Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
"devDependencies": {
"@actions/core": "^1.10.1",
"@emotion/react": "^11.11.3",
"@jest/test-sequencer": "^29.7.0",
"@nx/js": "^19.7.2",
"@swc-node/register": "^1.10.9",
"@swc/cli": "^0.4.0",
"@swc/core": "^1.3.36",
"@jest/test-sequencer": "^29.7.0",
"@types/chai": "^4.3.16",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
Expand All @@ -24,6 +24,7 @@
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0",
"eslint-plugin-react-refresh": "latest",
"events": "^3.3.0",
"http-server": "^14.1.1",
"husky": "^8.0.3",
"lint-staged": "^13.2.0",
Expand All @@ -33,8 +34,8 @@
"string_decoder": "^1.3.0",
"syncpack": "^13.0.0",
"tsup": "8.3.0",
"typescript": "^5.6.2",
"typedoc": "^0.26.5",
"typescript": "^5.6.2",
"wsrun": "^5.2.4"
},
"engines": {
Expand Down
1 change: 0 additions & 1 deletion packages/game-bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"@imtbl/metrics": "workspace:*",
"@imtbl/passport": "workspace:*",
"@imtbl/x-client": "workspace:*",
"@imtbl/x-provider": "workspace:*",
"ethers": "^6.13.4"
},
"devDependencies": {
Expand Down
245 changes: 0 additions & 245 deletions packages/game-bridge/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable no-console */
import * as passport from '@imtbl/passport';
import * as config from '@imtbl/config';
import * as provider from '@imtbl/x-provider';
import * as xClient from '@imtbl/x-client';
import {
track,
Expand Down Expand Up @@ -43,24 +42,15 @@ const sdkVersionSha = '__SDK_VERSION_SHA__';
const PASSPORT_FUNCTIONS = {
init: 'init',
relogin: 'relogin',
reconnect: 'reconnect',
getPKCEAuthUrl: 'getPKCEAuthUrl',
loginPKCE: 'loginPKCE',
connectPKCE: 'connectPKCE',
getAccessToken: 'getAccessToken',
getIdToken: 'getIdToken',
logout: 'logout',
getEmail: 'getEmail',
getPassportId: 'getPassportId',
getLinkedAddresses: 'getLinkedAddresses',
storeTokens: 'storeTokens',
imx: {
getAddress: 'getAddress',
isRegisteredOffchain: 'isRegisteredOffchain',
registerOffchain: 'registerOffchain',
transfer: 'imxTransfer',
batchNftTransfer: 'imxBatchNftTransfer',
},
zkEvm: {
connectEvm: 'connectEvm',
sendTransaction: 'zkEvmSendTransaction',
Expand All @@ -72,64 +62,12 @@ const PASSPORT_FUNCTIONS = {
},
};

function getHttpErrorSummary(err: unknown): {
status?: number;
fullUrl?: string;
traceId?: string;
requestId?: string;
cfRay?: string;
} {
const e: any = err as any;
const status: number | undefined = e?.response?.status;
const url: string | undefined = e?.config?.url;
const baseURL: string | undefined = e?.config?.baseURL;
let fullUrl = typeof url === 'string' && typeof baseURL === 'string' && !/^https?:\/\//i.test(url)
? `${baseURL}${url}`
: url;

// Remove query parameters to avoid exposing sensitive data (tokens, API keys, etc.)
if (typeof fullUrl === 'string') {
const queryIndex = fullUrl.indexOf('?');
if (queryIndex !== -1) {
fullUrl = fullUrl.substring(0, queryIndex);
}
}

const headers: Record<string, string> | undefined = e?.response?.headers;
const traceId = headers?.['x-amzn-trace-id'] ?? headers?.['x-trace-id'];
const requestId = headers?.['x-amzn-requestid']
?? headers?.['x-amzn-request-id']
?? headers?.['x-request-id'];
const cfRay = headers?.['cf-ray'];

return {
status,
fullUrl,
traceId,
requestId,
cfRay,
};
}

function parseHttpStatusSuffix(message: string): { status?: number; url?: string } {
// Matches our existing suffix formats like:
// "... [httpStatus=500 url=https://...]" or
// "... [httpStatus=500 url=https://... trace=... ...]"
const m = message.match(/\[httpStatus=([^\s\]]+)\s+url=([^\s\]]+)[^\]]*\]/);
if (!m) return {};
const statusStr = m[1];
const url = m[2];
const status = statusStr && /^[0-9]+$/.test(statusStr) ? Number(statusStr) : undefined;
return { status, url };
}

// To notify game engine that this file is loaded
const initRequest = 'init';
const initRequestId = '1';

let passportClient: passport.Passport | null;
let passportInitData: string | null = null;
let providerInstance: provider.IMXProvider | null;
let zkEvmProviderInstance: passport.Provider | null;
let versionInfo: VersionInfo | null;

Expand Down Expand Up @@ -212,25 +150,6 @@ const getPassportClient = (): passport.Passport => {
return passportClient;
};

const setProvider = (
passportProvider: provider.IMXProvider | null | undefined,
): boolean => {
if (passportProvider !== null && passportProvider !== undefined) {
providerInstance = passportProvider;
console.log('IMX provider set');
return true;
}
console.log('No IMX provider');
return false;
};

const getProvider = (): provider.IMXProvider => {
if (providerInstance == null) {
throw new Error('No IMX provider');
}
return providerInstance;
};

const setZkEvmProvider = (zkEvmProvider: passport.Provider | null | undefined): boolean => {
if (zkEvmProvider !== null && zkEvmProvider !== undefined) {
zkEvmProviderInstance = zkEvmProvider;
Expand Down Expand Up @@ -397,32 +316,6 @@ window.callFunction = async (jsonData: string) => {
});
break;
}
case PASSPORT_FUNCTIONS.reconnect: {
let providerSet = false;
const userInfo = await getPassportClient().login({
useCachedSession: true,
});
if (userInfo) {
const passportProvider = await getPassportClient().connectImx();
providerSet = setProvider(passportProvider);
identify({ passportId: userInfo?.sub });
}

if (!providerSet) {
throw new Error('Failed to reconnect');
}

trackDuration(moduleName, 'performedReconnect', mt(markStart), {
succeeded: userInfo !== null,
});
callbackToGame({
responseFor: fxName,
requestId,
success: providerSet,
error: null,
});
break;
}
case PASSPORT_FUNCTIONS.getPKCEAuthUrl: {
const request = data ? JSON.parse(data) : {};
const directLoginOptions: passport.DirectLoginOptions | undefined = request?.directLoginOptions;
Expand Down Expand Up @@ -454,34 +347,8 @@ window.callFunction = async (jsonData: string) => {
});
break;
}
case PASSPORT_FUNCTIONS.connectPKCE: {
const request = JSON.parse(data);
const profile = await getPassportClient().loginWithPKCEFlowCallback(
request.authorizationCode,
request.state,
);
const passportProvider = await getPassportClient().connectImx();
const providerSet = setProvider(passportProvider);

if (!providerSet) {
throw new Error('Failed to connect via PKCE');
}

identify({ passportId: profile.sub });
trackDuration(moduleName, 'performedConnectPkce', mt(markStart), {
succeeded: providerSet,
});
callbackToGame({
responseFor: fxName,
requestId,
success: providerSet,
error: null,
});
break;
}
case PASSPORT_FUNCTIONS.logout: {
const logoutUrl = await getPassportClient().getLogoutUrl();
providerInstance = null;
zkEvmProviderInstance = null;
trackDuration(moduleName, 'performedGetLogoutUrl', mt(markStart));
callbackToGame({
Expand Down Expand Up @@ -590,84 +457,6 @@ window.callFunction = async (jsonData: string) => {
});
break;
}
case PASSPORT_FUNCTIONS.imx.getAddress: {
const address = await getProvider().getAddress();
trackDuration(moduleName, 'performedImxGetAddress', mt(markStart));
callbackToGame({
responseFor: fxName,
requestId,
success: true,
error: null,
result: address,
});
break;
}
case PASSPORT_FUNCTIONS.imx.isRegisteredOffchain: {
const registered = await getProvider().isRegisteredOffchain();
trackDuration(moduleName, 'performedImxIsRegisteredOffchain', mt(markStart));
callbackToGame({
responseFor: fxName,
requestId,
success: true,
error: null,
result: registered,
});
break;
}
case PASSPORT_FUNCTIONS.imx.registerOffchain: {
const response = await getProvider().registerOffchain();
trackDuration(moduleName, 'performedImxRegisterOffchain', mt(markStart));
callbackToGame({
...{
responseFor: fxName,
requestId,
success: true,
error: null,
},
...response,
});
break;
}
case PASSPORT_FUNCTIONS.imx.transfer: {
const unsignedTransferRequest = JSON.parse(data);
const response = await getProvider().transfer(unsignedTransferRequest);
trackDuration(moduleName, 'performedImxTransfer', mt(markStart), {
requestId,
transferRequest: JSON.stringify(unsignedTransferRequest),
transferResponse: JSON.stringify(response),
});
callbackToGame({
...{
responseFor: fxName,
requestId,
success: true,
error: null,
},
...response,
});
break;
}
case PASSPORT_FUNCTIONS.imx.batchNftTransfer: {
const nftTransferDetails = JSON.parse(data);
const response = await getProvider().batchNftTransfer(
nftTransferDetails,
);
trackDuration(moduleName, 'performedImxBatchNftTransfer', mt(markStart), {
requestId,
transferRequest: JSON.stringify(nftTransferDetails),
transferResponse: JSON.stringify(response),
});
callbackToGame({
...{
responseFor: fxName,
requestId,
success: true,
error: null,
},
...response,
});
break;
}
case PASSPORT_FUNCTIONS.zkEvm.connectEvm: {
const zkEvmProvider = await getPassportClient().connectEvm();
const providerSet = setZkEvmProvider(zkEvmProvider);
Expand Down Expand Up @@ -869,40 +658,6 @@ window.callFunction = async (jsonData: string) => {
}

// Make endpoint visible in Unity Output for debugging (CI logs don't include JS console).
const {
status,
fullUrl,
traceId,
requestId: httpRequestId,
cfRay,
} = getHttpErrorSummary(error);
if (
fxName === PASSPORT_FUNCTIONS.imx.registerOffchain
&& wrappedError instanceof Error
) {
// Some upstream errors embed "[httpStatus=... url=...]" only in the message string
// without preserving axios-like fields. Parse what we can so we can still enrich.
const parsed = parseHttpStatusSuffix(wrappedError.message);
const effectiveStatus = status ?? parsed.status;
const effectiveUrl = fullUrl ?? parsed.url;
const suffix = ` [httpStatus=${effectiveStatus ?? 'unknown'}`
+ ` url=${effectiveUrl ?? 'unknown'}`
+ ` trace=${traceId ?? 'unknown'}`
+ ` reqId=${httpRequestId ?? 'unknown'}`
+ ` cfRay=${cfRay ?? 'unknown'}]`;
// If a previous layer already added a minimal suffix like:
// "... [httpStatus=500 url=...]"
// upgrade it to include trace/reqId/resp instead of skipping.
if (wrappedError.message.includes('[httpStatus=')) {
if (!wrappedError.message.includes('trace=')) {
const upgraded = wrappedError.message.replace(/\[httpStatus=[^\]]*\]/g, suffix.trim());
wrappedError = new Error(upgraded);
}
} else {
wrappedError = new Error(`${wrappedError.message}${suffix}`);
}
}

const errorType = error instanceof passport.PassportError
? error?.type
: undefined;
Expand Down
Loading
Loading