Skip to content
Draft
77 changes: 77 additions & 0 deletions packages/game-bridge/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const esbuild = require('esbuild');
const fs = require('fs');
const path = require('path');

async function build() {
// Clean dist folder
if (fs.existsSync('dist')) {
fs.rmSync('dist', { recursive: true });
}
fs.mkdirSync('dist/unity', { recursive: true });
fs.mkdirSync('dist/unreal', { recursive: true });

// Build Unity target (IIFE with inline HTML)
console.log('Building Unity target...');
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'browser',
target: 'chrome90',
format: 'iife',
globalName: 'ImmutableGameBridge',
outfile: 'dist/unity/bundle.js',
minify: true,
sourcemap: false,
keepNames: true,
define: {
'process.env.NODE_ENV': '"production"'
},
logLevel: 'info'
});

// Create HTML wrapper for Unity with inlined JS
let htmlTemplate = fs.readFileSync('src/index.html', 'utf-8');
const bundleJs = fs.readFileSync('dist/unity/bundle.js', 'utf-8');

// Find and replace the script section - be explicit about what we're replacing
const scriptStart = htmlTemplate.indexOf('<script');
const scriptEnd = htmlTemplate.indexOf('</script>') + '</script>'.length;

if (scriptStart === -1 || scriptEnd === -1) {
throw new Error('Could not find script tags in HTML template');
}

const beforeScript = htmlTemplate.substring(0, scriptStart);
const afterScript = htmlTemplate.substring(scriptEnd);

const inlineHtml = beforeScript + `<script>\n${bundleJs}\n </script>` + afterScript;

fs.writeFileSync('dist/unity/index.html', inlineHtml);
fs.unlinkSync('dist/unity/bundle.js');

console.log(`Unity HTML created with ${(bundleJs.length / 1024 / 1024).toFixed(2)}MB of inlined JS`);

// Build Unreal target (global format JS only)
console.log('Building Unreal target...');
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'browser',
target: 'chrome90',
format: 'iife',
globalName: 'ImmutableGameBridge',
outfile: 'dist/unreal/index.js',
minify: true,
sourcemap: false,
define: {
'process.env.NODE_ENV': '"production"'
}
});

console.log('✨ Build complete!');
}

build().catch((err) => {
console.error('Build failed:', err);
process.exit(1);
});
7 changes: 5 additions & 2 deletions packages/game-bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
"ethers": "^6.13.4"
},
"devDependencies": {
"esbuild": "^0.23.1",
"eslint": "^8.40.0",
"html-inline": "^1.2.0",
"parcel": "^2.8.3"
},
"scripts": {
"build": "parcel build --no-cache --no-scope-hoist",
"build:local": "parcel build --no-cache --no-scope-hoist && pnpm updateSdkVersion",
"build": "node build.js",
"build:parcel": "parcel build --no-cache --no-scope-hoist",
"build:local": "node build.js && pnpm updateSdkVersion",
"lint": "eslint ./src --ext .ts,.jsx,.tsx --max-warnings=0",
"start": "parcel",
"updateSdkVersion": "./scripts/updateSdkVersion.sh"
Expand Down
69 changes: 52 additions & 17 deletions packages/game-bridge/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
/* eslint-disable no-console */

// Catch any errors during module initialization
window.addEventListener('error', (event) => {
console.error('Global error caught:', event.error || event.message);
});

window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason);
});

console.log('game-bridge loading...');

import * as passport from '@imtbl/passport';
import * as config from '@imtbl/config';
import * as provider from '@imtbl/x-provider';
Expand All @@ -14,12 +26,29 @@ import * as ethers from 'ethers';
// eslint-disable-next-line import/no-duplicates
import { BrowserProvider, getAddress } from 'ethers';

console.log('game-bridge imports loaded');

// This patches a bundler issue where @0xsequence/core expects
// `ethers.getAddress` to exist on the `ethers` namespace, but Parcel
// fails to attach it in a global build. This ensures the function is
// `ethers.getAddress` to exist on the `ethers` namespace, but some bundlers
// fail to attach it in a global build. This ensures the function is
// available before any Passport code executes.
if (typeof ethers === 'object' && !ethers.getAddress) {
(ethers as any).getAddress = getAddress;
try {
if (typeof ethers === 'object' && !ethers.getAddress) {
try {
Object.defineProperty(ethers, 'getAddress', {
value: getAddress,
writable: true,
configurable: true
});
console.log('ethers.getAddress patched successfully');
} catch (e) {
// If we can't define the property, create a new object with the patched value
(window as any).ethers = { ...ethers, getAddress };
console.log('ethers.getAddress patched via window.ethers');
}
}
} catch (error) {
console.error('Error during ethers patching:', error);
}

/* eslint-disable no-undef */
Expand Down Expand Up @@ -176,7 +205,7 @@ const getProvider = (): provider.IMXProvider => {
return providerInstance;
};

const setZkEvmProvider = (zkEvmProvider: passport.Provider | null | undefined): boolean => {
const setEvmProvider = (zkEvmProvider: passport.Provider | null | undefined): boolean => {
if (zkEvmProvider !== null && zkEvmProvider !== undefined) {
zkEvmProviderInstance = zkEvmProvider;
console.log('zkEvm provider set');
Expand All @@ -186,7 +215,7 @@ const setZkEvmProvider = (zkEvmProvider: passport.Provider | null | undefined):
return false;
};

const getZkEvmProvider = (): passport.Provider => {
const getEvmProvider = (): passport.Provider => {
if (zkEvmProviderInstance == null) {
throw new Error('No zkEvm provider');
}
Expand Down Expand Up @@ -263,10 +292,13 @@ window.callFunction = async (jsonData: string) => {
},
}),
zkEvmRpcUrl: 'https://rpc.dev.immutable.com',
relayerUrl: 'https://api.dev.immutable.com/relayer-mr',
relayerUrl: 'http://localhost:8073/relayer-mr',
indexerMrBasePath: 'https://api.dev.immutable.com',
orderBookMrBasePath: 'https://api.dev.immutable.com',
passportMrBasePath: 'https://api.dev.immutable.com',
passportMrBasePath: 'http://localhost:8072',
sequenceIdentityInstrumentEndpoint: 'https://next-identity.sequence-dev.app/',
sequenceProjectAccessKey: 'AQAAAAAAAAB5QznGqk9paa4EQjom09ERpJs',
sequenceGuardEndpoint: 'https://guard.sequence.app',
},
};
} else {
Expand Down Expand Up @@ -606,8 +638,11 @@ window.callFunction = async (jsonData: string) => {
break;
}
case PASSPORT_FUNCTIONS.zkEvm.connectEvm: {
const zkEvmProvider = await getPassportClient().connectEvm();
const providerSet = setZkEvmProvider(zkEvmProvider);
const request = JSON.parse(data);
console.log(`Connecting to EVM chain: ${request.chain}`);

const zkEvmProvider = await getPassportClient().connectEvm({chain: request.chain});
const providerSet = setEvmProvider(zkEvmProvider);

if (!providerSet) {
throw new Error('Failed to connect to EVM');
Expand All @@ -626,7 +661,7 @@ window.callFunction = async (jsonData: string) => {
}
case PASSPORT_FUNCTIONS.zkEvm.sendTransaction: {
const transaction = JSON.parse(data);
const transactionHash = await getZkEvmProvider().request({
const transactionHash = await getEvmProvider().request({
method: 'eth_sendTransaction',
params: [transaction],
});
Expand All @@ -652,7 +687,7 @@ window.callFunction = async (jsonData: string) => {
}
case PASSPORT_FUNCTIONS.zkEvm.sendTransactionWithConfirmation: {
const transaction = JSON.parse(data);
const zkEvmProvider = getZkEvmProvider();
const zkEvmProvider = getEvmProvider();
const browserProvider = new BrowserProvider(zkEvmProvider);
const signer = await browserProvider.getSigner();

Expand All @@ -676,10 +711,10 @@ window.callFunction = async (jsonData: string) => {
}
case PASSPORT_FUNCTIONS.zkEvm.signTypedDataV4: {
const payload = JSON.parse(data);
const [address] = await getZkEvmProvider().request({
const [address] = await getEvmProvider().request({
method: 'eth_requestAccounts',
});
const signature = await getZkEvmProvider().request({
const signature = await getEvmProvider().request({
method: 'eth_signTypedData_v4',
params: [address, payload],
});
Expand All @@ -703,7 +738,7 @@ window.callFunction = async (jsonData: string) => {
break;
}
case PASSPORT_FUNCTIONS.zkEvm.requestAccounts: {
const result = await getZkEvmProvider().request({
const result = await getEvmProvider().request({
method: 'eth_requestAccounts',
});
const success = result !== null && result !== undefined;
Expand All @@ -724,7 +759,7 @@ window.callFunction = async (jsonData: string) => {
}
case PASSPORT_FUNCTIONS.zkEvm.getBalance: {
const request = JSON.parse(data);
const result = await getZkEvmProvider().request({
const result = await getEvmProvider().request({
method: 'eth_getBalance',
params: [request.address, request.blockNumberOrTag],
});
Expand All @@ -746,7 +781,7 @@ window.callFunction = async (jsonData: string) => {
}
case PASSPORT_FUNCTIONS.zkEvm.getTransactionReceipt: {
const request = JSON.parse(data);
const response = await getZkEvmProvider().request({
const response = await getEvmProvider().request({
method: 'eth_getTransactionReceipt',
params: [request.txHash],
});
Expand Down
4 changes: 2 additions & 2 deletions packages/internal/generated-clients/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from '../imx';

// eslint-disable-next-line @typescript-eslint/naming-convention
const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-__SDK_VERSION__' };
const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-1.0.0' };

/**
* Configuration for generated clients
Expand Down Expand Up @@ -78,7 +78,7 @@ export const multiRollupConfig = {
basePath: 'https://api.sandbox.immutable.com',
}),
passport: createConfig({
basePath: 'https://api.sandbox.immutable.com',
basePath: 'http://localhost:8072',
}),
}),
};
7 changes: 5 additions & 2 deletions packages/internal/generated-clients/src/mr-api-clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
PassportProfileApi,
GuardianApi,
} from './multi-rollup';
import { MultiRollupAPIConfiguration } from './config';
import { createConfig, MultiRollupAPIConfiguration } from './config';

export class MultiRollupApiClients {
public config: MultiRollupAPIConfiguration;
Expand Down Expand Up @@ -42,6 +42,9 @@ export class MultiRollupApiClients {
this.ordersApi = new OrdersApi(config.orderBook);
this.passportApi = new PassportApi(config.passport);
this.passportProfileApi = new PassportProfileApi(config.passport);
this.guardianApi = new GuardianApi(config.passport);
// this.guardianApi = new GuardianApi(config.passport);
this.guardianApi = new GuardianApi(createConfig({
basePath: 'http://localhost:8074',
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export interface TransactionApprovalRequest {

export const TransactionApprovalRequestChainTypeEnum = {
Starkex: 'starkex',
Evm: 'evm'
Evm: 'evm',
ArbitrumOne: 'arbitrum_one'
} as const;

export type TransactionApprovalRequestChainTypeEnum = typeof TransactionApprovalRequestChainTypeEnum[keyof typeof TransactionApprovalRequestChainTypeEnum];
Expand Down
Loading