Skip to content

Commit 800c295

Browse files
authored
feat(wallet): pass chain correct chain to transaction confirmation app (#2773)
1 parent 557f3dc commit 800c295

File tree

10 files changed

+169
-81
lines changed

10 files changed

+169
-81
lines changed

packages/wallet/src/chainRegistry.test.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

packages/wallet/src/chainRegistry.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

packages/wallet/src/confirmation/confirmation.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
import { openPopupCenter } from './popup';
1111
import { IAuthConfiguration } from '@imtbl/auth';
1212
import ConfirmationOverlay from '../overlay/confirmationOverlay';
13+
import { getEvmChainFromChainId } from '../network/chainRegistry';
14+
import { EvmChain } from '../types';
1315

1416
const CONFIRMATION_WINDOW_TITLE = 'Confirm this transaction';
1517
const CONFIRMATION_WINDOW_HEIGHT = 720;
@@ -105,7 +107,10 @@ export default class ConfirmationScreen {
105107
if (chainType === GeneratedClients.mr.TransactionApprovalRequestChainTypeEnum.Starkex) {
106108
href = this.getHref('transaction', { transactionId, etherAddress, chainType });
107109
} else {
108-
href = this.getHref('zkevm/transaction', {
110+
// Get chain path from chainId (e.g., 'zkevm', 'arbitrum-one')
111+
const chain = chainId ? getEvmChainFromChainId(chainId) : EvmChain.ZKEVM;
112+
const chainPath = chain.replace('_', '-');
113+
href = this.getHref(`${chainPath}/transaction`, {
109114
transactionID: transactionId, etherAddress, chainType, chainID: chainId,
110115
});
111116
}
@@ -117,7 +122,8 @@ export default class ConfirmationScreen {
117122
requestMessageConfirmation(
118123
messageID: string,
119124
etherAddress: string,
120-
messageType?: MessageType,
125+
messageType: MessageType,
126+
chainId: string,
121127
): Promise<ConfirmationResult> {
122128
return new Promise((resolve, reject) => {
123129
const messageHandler = ({ data, origin }: MessageEvent) => {
@@ -157,10 +163,14 @@ export default class ConfirmationScreen {
157163
};
158164

159165
window.addEventListener('message', messageHandler);
160-
const href = this.getHref('zkevm/message', {
166+
// Get chain path from chainId (e.g., 'zkevm', 'arbitrum-one')
167+
const chain = getEvmChainFromChainId(chainId);
168+
const chainPath = chain.replace('_', '-');
169+
const href = this.getHref(`${chainPath}/message`, {
161170
messageID,
162171
etherAddress,
163-
...(messageType ? { messageType } : {}),
172+
chainID: chainId,
173+
messageType,
164174
});
165175
this.showConfirmationScreen(href, messageHandler, resolve);
166176
});

packages/wallet/src/connectWallet.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,10 @@ describe('connectWallet', () => {
122122
expect(SequenceProvider).not.toHaveBeenCalled();
123123
});
124124

125-
it('uses ZkEvmProvider for chain with magic config', async () => {
125+
it('uses ZkEvmProvider for zkEVM devnet chain', async () => {
126126
const auth = createAuthStub();
127127
const devChain = {
128-
chainId: 99999, // unknown chainId
128+
chainId: 15003, // zkEVM devnet chainId
129129
rpcUrl: 'https://rpc.dev.immutable.com',
130130
relayerUrl: 'https://relayer.dev.immutable.com',
131131
apiUrl: 'https://api.dev.immutable.com',

packages/wallet/src/connectWallet.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ import { WalletConfiguration } from './config';
1818
import GuardianClient from './guardian';
1919
import MagicTEESigner from './magic/magicTEESigner';
2020
import { announceProvider, passportProviderInfo } from './provider/eip6963';
21-
import { DEFAULT_CHAINS } from './presets';
21+
import { DEFAULT_CHAINS } from './network/presets';
2222
import {
2323
MAGIC_CONFIG,
2424
IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID,
2525
} from './constants';
26+
import { ChainId } from './network/chains';
2627

2728
/**
2829
* Type guard to check if chainId is a valid key for MAGIC_CONFIG
@@ -70,9 +71,14 @@ const DEFAULT_REDIRECT_FALLBACK = 'https://auth.immutable.com/im-logged-in';
7071
const DEFAULT_AUTHENTICATION_DOMAIN = 'https://auth.immutable.com';
7172
const SANDBOX_DOMAIN_REGEX = /(sandbox|testnet)/i;
7273

74+
const ZKEVM_CHAIN_IDS = [
75+
ChainId.IMTBL_ZKEVM_MAINNET,
76+
ChainId.IMTBL_ZKEVM_TESTNET,
77+
ChainId.IMTBL_ZKEVM_DEVNET,
78+
];
79+
7380
function isZkEvmChain(chain: ChainConfig): boolean {
74-
// Only zkEVM chains use magic
75-
return !!chain.magicPublishableApiKey || isValidMagicChainId(chain.chainId);
81+
return ZKEVM_CHAIN_IDS.includes(chain.chainId);
7682
}
7783

7884
function isSandboxChain(chain: ChainConfig): boolean {

packages/wallet/src/guardian/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ export default class GuardianClient {
232232
messageId,
233233
user.zkEvm.ethAddress,
234234
'eip712',
235+
chainID,
235236
);
236237

237238
if (!confirmationResult.confirmed) {
@@ -286,6 +287,7 @@ export default class GuardianClient {
286287
messageId,
287288
user.zkEvm.ethAddress,
288289
'erc191',
290+
String(chainID),
289291
);
290292

291293
if (!confirmationResult.confirmed) {

packages/wallet/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ export {
2323
ARBITRUM_SEPOLIA,
2424
ARBITRUM_ONE_CHAIN,
2525
ARBITRUM_SEPOLIA_CHAIN,
26-
} from './presets';
26+
} from './network/presets';
2727

2828
// Export chain registry for looking up chain configs
29-
export { getChainConfig } from './chainRegistry';
29+
export { getChainConfig, getEvmChainFromChainId } from './network/chainRegistry';
3030

3131
// Export main wallet providers
3232
export { ZkEvmProvider } from './zkEvm/zkEvmProvider';
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Environment } from '@imtbl/config';
2+
import { getChainConfig, getEvmChainFromChainId } from './chainRegistry';
3+
import { EvmChain } from '../types';
4+
import { ARBITRUM_ONE_CHAIN, ARBITRUM_SEPOLIA_CHAIN } from './presets';
5+
6+
describe('chainRegistry', () => {
7+
describe('getChainConfig', () => {
8+
it('returns Arbitrum One mainnet config for PRODUCTION', () => {
9+
const config = getChainConfig(EvmChain.ARBITRUM_ONE, Environment.PRODUCTION);
10+
11+
expect(config).toEqual(ARBITRUM_ONE_CHAIN);
12+
expect(config.chainId).toBe(42161);
13+
expect(config.name).toBe('Arbitrum One');
14+
});
15+
16+
it('returns Arbitrum Sepolia config for SANDBOX', () => {
17+
const config = getChainConfig(EvmChain.ARBITRUM_ONE, Environment.SANDBOX);
18+
19+
expect(config).toEqual(ARBITRUM_SEPOLIA_CHAIN);
20+
expect(config.chainId).toBe(421614);
21+
expect(config.name).toBe('Arbitrum Sepolia');
22+
});
23+
24+
it('throws error for unsupported chain', () => {
25+
expect(() => {
26+
getChainConfig('unsupported_chain' as any, Environment.PRODUCTION);
27+
}).toThrow('Chain unsupported_chain is not supported');
28+
});
29+
});
30+
31+
describe('getEvmChainFromChainId', () => {
32+
it('returns ZKEVM for mainnet chainId', () => {
33+
expect(getEvmChainFromChainId(13371)).toBe(EvmChain.ZKEVM);
34+
});
35+
36+
it('returns ZKEVM for testnet chainId', () => {
37+
expect(getEvmChainFromChainId(13473)).toBe(EvmChain.ZKEVM);
38+
});
39+
40+
it('returns ZKEVM for devnet chainId', () => {
41+
expect(getEvmChainFromChainId(15003)).toBe(EvmChain.ZKEVM);
42+
});
43+
44+
it('returns ARBITRUM_ONE for Arbitrum mainnet chainId', () => {
45+
expect(getEvmChainFromChainId(42161)).toBe(EvmChain.ARBITRUM_ONE);
46+
});
47+
48+
it('returns ARBITRUM_ONE for Arbitrum Sepolia chainId', () => {
49+
expect(getEvmChainFromChainId(421614)).toBe(EvmChain.ARBITRUM_ONE);
50+
});
51+
52+
it('handles string chainId', () => {
53+
expect(getEvmChainFromChainId('42161')).toBe(EvmChain.ARBITRUM_ONE);
54+
});
55+
56+
it('handles eip155 format chainId', () => {
57+
expect(getEvmChainFromChainId('eip155:42161')).toBe(EvmChain.ARBITRUM_ONE);
58+
});
59+
60+
it('defaults to ZKEVM for unknown chainId', () => {
61+
expect(getEvmChainFromChainId(99999)).toBe(EvmChain.ZKEVM);
62+
});
63+
});
64+
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Environment } from '@imtbl/config';
2+
import { ChainConfig, EvmChain } from '../types';
3+
import {
4+
IMMUTABLE_ZKEVM_MAINNET_CHAIN,
5+
IMMUTABLE_ZKEVM_TESTNET_CHAIN,
6+
ARBITRUM_ONE_CHAIN,
7+
ARBITRUM_SEPOLIA_CHAIN,
8+
} from './presets';
9+
import { ChainId } from './chains';
10+
11+
/**
12+
* Registry mapping (EvmChain, Environment) to ChainConfig
13+
*/
14+
const CHAIN_REGISTRY: Record<EvmChain, Record<Environment, ChainConfig>> = {
15+
[EvmChain.ZKEVM]: {
16+
[Environment.PRODUCTION]: IMMUTABLE_ZKEVM_MAINNET_CHAIN,
17+
[Environment.SANDBOX]: IMMUTABLE_ZKEVM_TESTNET_CHAIN,
18+
},
19+
[EvmChain.ARBITRUM_ONE]: {
20+
[Environment.PRODUCTION]: ARBITRUM_ONE_CHAIN,
21+
[Environment.SANDBOX]: ARBITRUM_SEPOLIA_CHAIN,
22+
},
23+
};
24+
25+
/**
26+
* Build chainId → EvmChain mapping from CHAIN_REGISTRY (derived, not manual)
27+
*/
28+
function buildChainIdToEvmChainMap(): Record<number, EvmChain> {
29+
const map: Record<number, EvmChain> = {};
30+
for (const [evmChain, envConfigs] of Object.entries(CHAIN_REGISTRY)) {
31+
for (const config of Object.values(envConfigs)) {
32+
map[config.chainId] = evmChain as EvmChain;
33+
}
34+
}
35+
// Devnet doesn't have a preset
36+
map[ChainId.IMTBL_ZKEVM_DEVNET] = EvmChain.ZKEVM;
37+
return map;
38+
}
39+
40+
const CHAIN_ID_TO_EVM_CHAIN = buildChainIdToEvmChainMap();
41+
42+
/**
43+
* Get chain config for non-zkEVM chains
44+
* @throws Error if chain is not in registry
45+
*/
46+
export function getChainConfig(
47+
chain: Exclude<EvmChain, EvmChain.ZKEVM>,
48+
environment: Environment,
49+
): ChainConfig {
50+
const envConfigs = CHAIN_REGISTRY[chain];
51+
if (!envConfigs) {
52+
throw new Error(`Chain ${chain} is not supported`);
53+
}
54+
55+
const config = envConfigs[environment];
56+
if (!config) {
57+
throw new Error(`Chain ${chain} is not configured for environment ${environment}`);
58+
}
59+
60+
return config;
61+
}
62+
63+
/**
64+
* Get EvmChain from chainId
65+
* @param chainId - Chain ID (can be number or string like "eip155:42161")
66+
* @returns EvmChain enum value, defaults to ZKEVM if not found
67+
*/
68+
export function getEvmChainFromChainId(chainId: string | number): EvmChain {
69+
const numericChainId = typeof chainId === 'string'
70+
? parseInt(chainId.includes(':') ? chainId.split(':')[1] : chainId, 10)
71+
: chainId;
72+
73+
return CHAIN_ID_TO_EVM_CHAIN[numericChainId] ?? EvmChain.ZKEVM;
74+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { ChainConfig } from './types';
1+
import { ChainConfig } from '../types';
22
import {
33
IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID,
44
IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID,
55
ARBITRUM_ONE_CHAIN_ID,
66
ARBITRUM_SEPOLIA_CHAIN_ID,
77
MAGIC_CONFIG,
8-
} from './constants';
8+
} from '../constants';
99

1010
/**
1111
* Immutable zkEVM Mainnet chain configuration

0 commit comments

Comments
 (0)