@imtbl/wallet 2.12.5-alpha.8 → 2.12.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/browser/index.js +202 -29
  2. package/dist/node/index.cjs +241 -56
  3. package/dist/node/index.js +202 -29
  4. package/dist/types/confirmation/confirmation.d.ts +1 -1
  5. package/dist/types/connectWallet.d.ts +2 -3
  6. package/dist/types/constants.d.ts +7 -0
  7. package/dist/types/index.d.ts +4 -2
  8. package/dist/types/network/chainRegistry.d.ts +13 -0
  9. package/dist/types/{presets.d.ts → network/presets.d.ts} +37 -1
  10. package/dist/types/sequence/sequenceProvider.d.ts +21 -0
  11. package/dist/types/sequence/signer/identityInstrumentSigner.d.ts +15 -0
  12. package/dist/types/sequence/signer/index.d.ts +20 -0
  13. package/dist/types/sequence/signer/privateKeySigner.d.ts +15 -0
  14. package/dist/types/sequence/signer/types.d.ts +14 -0
  15. package/dist/types/sequence/user/index.d.ts +2 -0
  16. package/dist/types/sequence/user/registerUser.d.ts +18 -0
  17. package/dist/types/types.d.ts +22 -1
  18. package/dist/types/zkEvm/types.d.ts +3 -10
  19. package/package.json +9 -4
  20. package/src/confirmation/confirmation.ts +14 -4
  21. package/src/connectWallet.test.ts +62 -3
  22. package/src/connectWallet.ts +82 -45
  23. package/src/constants.ts +10 -0
  24. package/src/guardian/index.ts +2 -0
  25. package/src/index.ts +14 -2
  26. package/src/network/chainRegistry.test.ts +64 -0
  27. package/src/network/chainRegistry.ts +74 -0
  28. package/src/{presets.ts → network/presets.ts} +64 -2
  29. package/src/sequence/sequenceProvider.ts +276 -0
  30. package/src/sequence/signer/identityInstrumentSigner.ts +193 -0
  31. package/src/sequence/signer/index.ts +45 -0
  32. package/src/sequence/signer/privateKeySigner.ts +111 -0
  33. package/src/sequence/signer/types.ts +24 -0
  34. package/src/sequence/user/index.ts +2 -0
  35. package/src/sequence/user/registerUser.ts +100 -0
  36. package/src/types.ts +26 -2
  37. package/src/zkEvm/types.ts +4 -10
@@ -15,7 +15,7 @@ export default class ConfirmationScreen {
15
15
  constructor(config: IAuthConfiguration);
16
16
  private getHref;
17
17
  requestConfirmation(transactionId: string, etherAddress: string, chainType: GeneratedClients.mr.TransactionApprovalRequestChainTypeEnum, chainId?: string): Promise<ConfirmationResult>;
18
- requestMessageConfirmation(messageID: string, etherAddress: string, messageType?: MessageType): Promise<ConfirmationResult>;
18
+ requestMessageConfirmation(messageID: string, etherAddress: string, messageType: MessageType, chainId: string): Promise<ConfirmationResult>;
19
19
  showServiceUnavailable(): Promise<void>;
20
20
  loading(popupOptions?: {
21
21
  width: number;
@@ -1,5 +1,4 @@
1
- import { ZkEvmProvider } from './zkEvm/zkEvmProvider';
2
- import { ConnectWalletOptions } from './types';
1
+ import { ConnectWalletOptions, Provider } from './types';
3
2
  /**
4
3
  * Connect wallet with the provided configuration
5
4
  *
@@ -32,4 +31,4 @@ import { ConnectWalletOptions } from './types';
32
31
  * });
33
32
  * ```
34
33
  */
35
- export declare function connectWallet(config?: ConnectWalletOptions): Promise<ZkEvmProvider>;
34
+ export declare function connectWallet(config?: ConnectWalletOptions): Promise<Provider>;
@@ -5,6 +5,13 @@
5
5
  export declare const IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID = 13371;
6
6
  /** Immutable zkEVM Testnet chain ID */
7
7
  export declare const IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID = 13473;
8
+ /**
9
+ * Chain ID constants for Arbitrum networks
10
+ */
11
+ /** Arbitrum One Mainnet chain ID */
12
+ export declare const ARBITRUM_ONE_CHAIN_ID = 42161;
13
+ /** Arbitrum Sepolia Testnet chain ID */
14
+ export declare const ARBITRUM_SEPOLIA_CHAIN_ID = 421614;
8
15
  /**
9
16
  * Magic configuration for Immutable networks
10
17
  * @internal
@@ -1,7 +1,9 @@
1
1
  export { connectWallet } from './connectWallet';
2
- export { IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID, IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID, } from './constants';
3
- export { IMMUTABLE_ZKEVM_MAINNET, IMMUTABLE_ZKEVM_TESTNET, IMMUTABLE_ZKEVM_MULTICHAIN, IMMUTABLE_ZKEVM_MAINNET_CHAIN, IMMUTABLE_ZKEVM_TESTNET_CHAIN, DEFAULT_CHAINS, } from './presets';
2
+ export { IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID, IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID, ARBITRUM_ONE_CHAIN_ID, ARBITRUM_SEPOLIA_CHAIN_ID, } from './constants';
3
+ export { IMMUTABLE_ZKEVM_MAINNET, IMMUTABLE_ZKEVM_TESTNET, IMMUTABLE_ZKEVM_MULTICHAIN, IMMUTABLE_ZKEVM_MAINNET_CHAIN, IMMUTABLE_ZKEVM_TESTNET_CHAIN, DEFAULT_CHAINS, ARBITRUM_ONE, ARBITRUM_SEPOLIA, ARBITRUM_ONE_CHAIN, ARBITRUM_SEPOLIA_CHAIN, } from './network/presets';
4
+ export { getChainConfig, getEvmChainFromChainId } from './network/chainRegistry';
4
5
  export { ZkEvmProvider } from './zkEvm/zkEvmProvider';
6
+ export { SequenceProvider } from './sequence/sequenceProvider';
5
7
  export { WalletConfiguration } from './config';
6
8
  export * from './types';
7
9
  export { WalletError, WalletErrorType } from './errors';
@@ -0,0 +1,13 @@
1
+ import { Environment } from '@imtbl/config';
2
+ import { ChainConfig, EvmChain } from '../types';
3
+ /**
4
+ * Get chain config for non-zkEVM chains
5
+ * @throws Error if chain is not in registry
6
+ */
7
+ export declare function getChainConfig(chain: Exclude<EvmChain, EvmChain.ZKEVM>, environment: Environment): ChainConfig;
8
+ /**
9
+ * Get EvmChain from chainId
10
+ * @param chainId - Chain ID (can be number or string like "eip155:42161")
11
+ * @returns EvmChain enum value, defaults to ZKEVM if not found
12
+ */
13
+ export declare function getEvmChainFromChainId(chainId: string | number): EvmChain;
@@ -1,4 +1,4 @@
1
- import { ChainConfig } from './types';
1
+ import { ChainConfig } from '../types';
2
2
  /**
3
3
  * Immutable zkEVM Mainnet chain configuration
4
4
  */
@@ -7,6 +7,14 @@ export declare const IMMUTABLE_ZKEVM_MAINNET_CHAIN: ChainConfig;
7
7
  * Immutable zkEVM Testnet chain configuration
8
8
  */
9
9
  export declare const IMMUTABLE_ZKEVM_TESTNET_CHAIN: ChainConfig;
10
+ /**
11
+ * Arbitrum One Mainnet chain configuration
12
+ */
13
+ export declare const ARBITRUM_ONE_CHAIN: ChainConfig;
14
+ /**
15
+ * Arbitrum Sepolia Testnet chain configuration
16
+ */
17
+ export declare const ARBITRUM_SEPOLIA_CHAIN: ChainConfig;
10
18
  /**
11
19
  * Default chains (testnet + mainnet)
12
20
  * Testnet is first (default initial chain)
@@ -56,3 +64,31 @@ export declare const IMMUTABLE_ZKEVM_TESTNET: {
56
64
  export declare const IMMUTABLE_ZKEVM_MULTICHAIN: {
57
65
  chains: ChainConfig[];
58
66
  };
67
+ /**
68
+ * Arbitrum mainnet only preset
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const provider = await connectWallet({
73
+ * ...ARBITRUM_ONE_MAINNET,
74
+ * auth,
75
+ * });
76
+ * ```
77
+ */
78
+ export declare const ARBITRUM_ONE: {
79
+ chains: ChainConfig[];
80
+ };
81
+ /**
82
+ * Arbitrum testnet only preset
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const provider = await connectWallet({
87
+ * ...ARBITRUM_SEPOLIA,
88
+ * auth,
89
+ * });
90
+ * ```
91
+ */
92
+ export declare const ARBITRUM_SEPOLIA: {
93
+ chains: ChainConfig[];
94
+ };
@@ -0,0 +1,21 @@
1
+ import { MultiRollupApiClients } from '@imtbl/generated-clients';
2
+ import { Auth, TypedEventEmitter } from '@imtbl/auth';
3
+ import { Provider, ProviderEventMap, RequestArguments, ChainConfig } from '../types';
4
+ import GuardianClient from '../guardian';
5
+ import { SequenceSigner } from './signer';
6
+ export type SequenceProviderInput = {
7
+ auth: Auth;
8
+ chainConfig: ChainConfig;
9
+ multiRollupApiClients: MultiRollupApiClients;
10
+ guardianClient: GuardianClient;
11
+ ethSigner: SequenceSigner;
12
+ passportEventEmitter: TypedEventEmitter<ProviderEventMap>;
13
+ };
14
+ export declare class SequenceProvider implements Provider {
15
+ #private;
16
+ readonly isPassport: boolean;
17
+ constructor({ auth, chainConfig, multiRollupApiClients, guardianClient, ethSigner, passportEventEmitter, }: SequenceProviderInput);
18
+ request(request: RequestArguments): Promise<any>;
19
+ on(event: string, listener: (...args: any[]) => void): void;
20
+ removeListener(event: string, listener: (...args: any[]) => void): void;
21
+ }
@@ -0,0 +1,15 @@
1
+ import { Auth } from '@imtbl/auth';
2
+ import { Address } from 'ox';
3
+ import { Payload, Signature as SequenceSignature } from '@0xsequence/wallet-primitives';
4
+ import { SequenceSigner } from './types';
5
+ export interface IdentityInstrumentSignerConfig {
6
+ /** Sequence Identity Instrument endpoint URL */
7
+ identityInstrumentEndpoint: string;
8
+ }
9
+ export declare class IdentityInstrumentSigner implements SequenceSigner {
10
+ #private;
11
+ constructor(auth: Auth, config: IdentityInstrumentSignerConfig);
12
+ getAddress(): Promise<string>;
13
+ signPayload(walletAddress: Address.Address, chainId: number, payload: Payload.Parented): Promise<SequenceSignature.SignatureOfSignerLeaf>;
14
+ signMessage(message: string | Uint8Array): Promise<string>;
15
+ }
@@ -0,0 +1,20 @@
1
+ import { Auth, IAuthConfiguration } from '@imtbl/auth';
2
+ import { SequenceSigner } from './types';
3
+ export type { SequenceSigner } from './types';
4
+ export { IdentityInstrumentSigner } from './identityInstrumentSigner';
5
+ export type { IdentityInstrumentSignerConfig } from './identityInstrumentSigner';
6
+ export { PrivateKeySigner } from './privateKeySigner';
7
+ export interface CreateSequenceSignerConfig {
8
+ /** Identity Instrument endpoint (required for prod/sandbox) */
9
+ identityInstrumentEndpoint?: string;
10
+ }
11
+ /**
12
+ * Create the appropriate signer based on environment.
13
+ * - Dev environment (behind VPN): uses PrivateKeySigner
14
+ * - Prod/Sandbox: uses IdentityInstrumentSigner
15
+ *
16
+ * @param auth - Auth instance
17
+ * @param authConfig - Auth configuration (to determine environment)
18
+ * @param config - Signer configuration
19
+ */
20
+ export declare function createSequenceSigner(auth: Auth, authConfig: IAuthConfiguration, config?: CreateSequenceSignerConfig): SequenceSigner;
@@ -0,0 +1,15 @@
1
+ import { Auth } from '@imtbl/auth';
2
+ import { Payload, Signature as SequenceSignature } from '@0xsequence/wallet-primitives';
3
+ import { SequenceSigner } from './types';
4
+ import { Address } from 'ox';
5
+ /**
6
+ * Private key signer for dev environments (behind VPN).
7
+ * Uses a deterministic private key derived from the user's sub.
8
+ */
9
+ export declare class PrivateKeySigner implements SequenceSigner {
10
+ #private;
11
+ constructor(auth: Auth);
12
+ getAddress(): Promise<string>;
13
+ signPayload(walletAddress: Address.Address, chainId: number, payload: Payload.Parented): Promise<SequenceSignature.SignatureOfSignerLeaf>;
14
+ signMessage(message: string | Uint8Array): Promise<string>;
15
+ }
@@ -0,0 +1,14 @@
1
+ import { Address } from 'ox';
2
+ import { Payload, Signature as SequenceSignature } from '@0xsequence/wallet-primitives';
3
+ /**
4
+ * Signer interface for Sequence wallet operations.
5
+ * Used by non-zkEVM chains (e.g., Arbitrum).
6
+ */
7
+ export interface SequenceSigner {
8
+ /** Get the signer's address */
9
+ getAddress(): Promise<string>;
10
+ /** Sign a Sequence payload (for transactions) */
11
+ signPayload(walletAddress: Address.Address, chainId: number, payload: Payload.Parented): Promise<SequenceSignature.SignatureOfSignerLeaf>;
12
+ /** Sign a message (EIP-191 personal_sign) */
13
+ signMessage(message: string | Uint8Array): Promise<string>;
14
+ }
@@ -0,0 +1,2 @@
1
+ export { registerUser } from './registerUser';
2
+ export type { RegisterUserInput } from './registerUser';
@@ -0,0 +1,18 @@
1
+ import { MultiRollupApiClients } from '@imtbl/generated-clients';
2
+ import { Flow } from '@imtbl/metrics';
3
+ import type { PublicClient } from 'viem';
4
+ import { Auth } from '@imtbl/auth';
5
+ import { SequenceSigner } from '../signer';
6
+ export type RegisterUserInput = {
7
+ auth: Auth;
8
+ ethSigner: SequenceSigner;
9
+ multiRollupApiClients: MultiRollupApiClients;
10
+ accessToken: string;
11
+ rpcProvider: PublicClient;
12
+ flow: Flow;
13
+ };
14
+ /**
15
+ * Register a user for a non-zkEVM chain (e.g., Arbitrum).
16
+ * Creates a counterfactual address for the user on the specified chain.
17
+ */
18
+ export declare function registerUser({ auth, ethSigner, multiRollupApiClients, accessToken, rpcProvider, flow, }: RegisterUserInput): Promise<string>;
@@ -1,6 +1,10 @@
1
1
  import { Flow } from '@imtbl/metrics';
2
2
  import { Auth, TypedEventEmitter, type AuthEventMap } from '@imtbl/auth';
3
3
  import { JsonRpcError } from './zkEvm/JsonRpcError';
4
+ export declare enum EvmChain {
5
+ ZKEVM = "zkevm",
6
+ ARBITRUM_ONE = "arbitrum_one"
7
+ }
4
8
  /**
5
9
  * A viem-compatible signer interface for wallet operations.
6
10
  * This replaces ethers' AbstractSigner/Signer.
@@ -28,7 +32,16 @@ export type AccountsRequestedEvent = {
28
32
  export interface PassportEventMap extends AuthEventMap {
29
33
  [WalletEvents.ACCOUNTS_REQUESTED]: [AccountsRequestedEvent];
30
34
  }
31
- export type { Provider } from './zkEvm/types';
35
+ /**
36
+ * EIP-1193 Provider Interface
37
+ * Standard Ethereum provider interface for all chain types
38
+ */
39
+ export type Provider = {
40
+ request: (request: RequestArguments) => Promise<any>;
41
+ on: (event: string, listener: (...args: any[]) => void) => void;
42
+ removeListener: (event: string, listener: (...args: any[]) => void) => void;
43
+ isPassport: boolean;
44
+ };
32
45
  export interface RequestArguments {
33
46
  method: string;
34
47
  params?: Array<any>;
@@ -151,6 +164,14 @@ export interface ChainConfig {
151
164
  * Defaults to 'https://tee.express.magiclabs.com'
152
165
  */
153
166
  magicTeeBasePath?: string;
167
+ /** Preferred token symbol for relayer fees (default: 'IMX') */
168
+ feeTokenSymbol?: string;
169
+ /** Sequence RPC node URL TODO: check if this can be removed and only use rpcUrl */
170
+ nodeUrl?: string;
171
+ /**
172
+ * Sequence Identity Instrument endpoint (for non-zkEVM chains in prod/sandbox)
173
+ */
174
+ sequenceIdentityInstrumentEndpoint?: string;
154
175
  }
155
176
  /**
156
177
  * Popup overlay options for wallet UI
@@ -1,4 +1,5 @@
1
1
  import { JsonRpcError } from './JsonRpcError';
2
+ import type { Provider as ProviderType } from '../types';
2
3
  export declare enum RelayerTransactionStatus {
3
4
  PENDING = "PENDING",
4
5
  SUBMITTED = "SUBMITTED",
@@ -82,16 +83,8 @@ export interface JsonRpcResponsePayload {
82
83
  jsonrpc?: string;
83
84
  id?: string | number;
84
85
  }
85
- /**
86
- * EIP-1193 Provider Interface
87
- * Standard Ethereum provider interface
88
- */
89
- export type Provider = {
90
- request: (request: RequestArguments) => Promise<any>;
91
- on: (event: string, listener: (...args: any[]) => void) => void;
92
- removeListener: (event: string, listener: (...args: any[]) => void) => void;
93
- isPassport: boolean;
94
- };
86
+ export type { Provider } from '../types';
87
+ type Provider = ProviderType;
95
88
  export declare enum ProviderEvent {
96
89
  ACCOUNTS_CHANGED = "accountsChanged"
97
90
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imtbl/wallet",
3
- "version": "2.12.5-alpha.8",
3
+ "version": "2.12.5",
4
4
  "description": "Wallet SDK for Immutable",
5
5
  "author": "Immutable",
6
6
  "bugs": "https://github.com/immutable/ts-immutable-sdk/issues",
@@ -25,12 +25,16 @@
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
- "@imtbl/auth": "2.12.5-alpha.8",
29
- "@imtbl/generated-clients": "2.12.5-alpha.8",
30
- "@imtbl/metrics": "2.12.5-alpha.8",
28
+ "@imtbl/auth": "2.12.5",
29
+ "@imtbl/generated-clients": "2.12.5",
30
+ "@imtbl/metrics": "2.12.5",
31
31
  "viem": "~2.18.0"
32
32
  },
33
33
  "devDependencies": {
34
+ "@0xsequence/identity-instrument": "3.0.0-beta.10",
35
+ "@0xsequence/wallet-wdk": "3.0.0-beta.10",
36
+ "@0xsequence/wallet-core": "3.0.0-beta.10",
37
+ "@0xsequence/wallet-primitives": "3.0.0-beta.10",
34
38
  "@swc/core": "^1.3.36",
35
39
  "@swc/jest": "^0.2.37",
36
40
  "@types/jest": "^29.5.12",
@@ -38,6 +42,7 @@
38
42
  "@jest/test-sequencer": "^29.7.0",
39
43
  "jest": "^29.4.3",
40
44
  "jest-environment-jsdom": "^29.4.3",
45
+ "ox": "^0.9.17",
41
46
  "ts-node": "^10.9.1",
42
47
  "tsup": "^8.3.0",
43
48
  "typescript": "^5.6.2"
@@ -10,6 +10,8 @@ import {
10
10
  import { openPopupCenter } from './popup';
11
11
  import { IAuthConfiguration } from '@imtbl/auth';
12
12
  import ConfirmationOverlay from '../overlay/confirmationOverlay';
13
+ import { getEvmChainFromChainId } from '../network/chainRegistry';
14
+ import { EvmChain } from '../types';
13
15
 
14
16
  const CONFIRMATION_WINDOW_TITLE = 'Confirm this transaction';
15
17
  const CONFIRMATION_WINDOW_HEIGHT = 720;
@@ -105,7 +107,10 @@ export default class ConfirmationScreen {
105
107
  if (chainType === GeneratedClients.mr.TransactionApprovalRequestChainTypeEnum.Starkex) {
106
108
  href = this.getHref('transaction', { transactionId, etherAddress, chainType });
107
109
  } 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`, {
109
114
  transactionID: transactionId, etherAddress, chainType, chainID: chainId,
110
115
  });
111
116
  }
@@ -117,7 +122,8 @@ export default class ConfirmationScreen {
117
122
  requestMessageConfirmation(
118
123
  messageID: string,
119
124
  etherAddress: string,
120
- messageType?: MessageType,
125
+ messageType: MessageType,
126
+ chainId: string,
121
127
  ): Promise<ConfirmationResult> {
122
128
  return new Promise((resolve, reject) => {
123
129
  const messageHandler = ({ data, origin }: MessageEvent) => {
@@ -157,10 +163,14 @@ export default class ConfirmationScreen {
157
163
  };
158
164
 
159
165
  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`, {
161
170
  messageID,
162
171
  etherAddress,
163
- ...(messageType ? { messageType } : {}),
172
+ chainID: chainId,
173
+ messageType,
164
174
  });
165
175
  this.showConfirmationScreen(href, messageHandler, resolve);
166
176
  });
@@ -42,6 +42,18 @@ jest.mock('./zkEvm/zkEvmProvider', () => ({
42
42
  ZkEvmProvider: jest.fn(),
43
43
  }));
44
44
 
45
+ jest.mock('./sequence/sequenceProvider', () => ({
46
+ SequenceProvider: jest.fn(),
47
+ }));
48
+
49
+ jest.mock('./sequence/signer', () => ({
50
+ createSequenceSigner: jest.fn().mockReturnValue({
51
+ getAddress: jest.fn().mockResolvedValue('0x1234'),
52
+ signPayload: jest.fn(),
53
+ signMessage: jest.fn(),
54
+ }),
55
+ }));
56
+
45
57
  jest.mock('./provider/eip6963', () => ({
46
58
  announceProvider: jest.fn(),
47
59
  passportProviderInfo: { name: 'passport', rdns: 'com.immutable.passport', icon: '' },
@@ -51,8 +63,9 @@ const { connectWallet } = require('./connectWallet');
51
63
 
52
64
  const { announceProvider } = jest.requireMock('./provider/eip6963');
53
65
  const { ZkEvmProvider } = jest.requireMock('./zkEvm/zkEvmProvider');
66
+ const { SequenceProvider } = jest.requireMock('./sequence/sequenceProvider');
54
67
 
55
- const chain = {
68
+ const zkEvmChain = {
56
69
  chainId: 13473,
57
70
  rpcUrl: 'https://rpc.sandbox.immutable.com',
58
71
  relayerUrl: 'https://relayer.sandbox.immutable.com',
@@ -60,6 +73,14 @@ const chain = {
60
73
  name: 'Immutable zkEVM Testnet',
61
74
  };
62
75
 
76
+ const arbitrumChain = {
77
+ chainId: 42161,
78
+ rpcUrl: 'https://arb1.arbitrum.io/rpc',
79
+ relayerUrl: 'https://next-arbitrum-one-relayer.sequence.app',
80
+ apiUrl: 'https://api.immutable.com',
81
+ name: 'Arbitrum One',
82
+ };
83
+
63
84
  const createAuthStub = () => ({
64
85
  getConfig: jest.fn().mockReturnValue({
65
86
  authenticationDomain: 'https://auth.immutable.com',
@@ -82,7 +103,7 @@ describe('connectWallet', () => {
82
103
  it('announces provider by default', async () => {
83
104
  const auth = createAuthStub();
84
105
 
85
- const provider = await connectWallet({ auth, chains: [chain] });
106
+ const provider = await connectWallet({ auth, chains: [zkEvmChain] });
86
107
 
87
108
  expect(ZkEvmProvider).toHaveBeenCalled();
88
109
  expect(announceProvider).toHaveBeenCalledWith({
@@ -94,8 +115,46 @@ describe('connectWallet', () => {
94
115
  it('does not announce provider when disabled', async () => {
95
116
  const auth = createAuthStub();
96
117
 
97
- await connectWallet({ auth, chains: [chain], announceProvider: false });
118
+ await connectWallet({ auth, chains: [zkEvmChain], announceProvider: false });
98
119
 
99
120
  expect(announceProvider).not.toHaveBeenCalled();
100
121
  });
122
+
123
+ describe('provider selection', () => {
124
+ it('uses ZkEvmProvider for zkEVM chain (by chainId)', async () => {
125
+ const auth = createAuthStub();
126
+
127
+ await connectWallet({ auth, chains: [zkEvmChain] });
128
+
129
+ expect(ZkEvmProvider).toHaveBeenCalled();
130
+ expect(SequenceProvider).not.toHaveBeenCalled();
131
+ });
132
+
133
+ it('uses ZkEvmProvider for zkEVM devnet chain', async () => {
134
+ const auth = createAuthStub();
135
+ const devChain = {
136
+ chainId: 15003, // zkEVM devnet chainId
137
+ rpcUrl: 'https://rpc.dev.immutable.com',
138
+ relayerUrl: 'https://relayer.dev.immutable.com',
139
+ apiUrl: 'https://api.dev.immutable.com',
140
+ name: 'Dev Chain',
141
+ magicPublishableApiKey: 'pk_test_123',
142
+ magicProviderId: 'provider-123',
143
+ };
144
+
145
+ await connectWallet({ auth, chains: [devChain] });
146
+
147
+ expect(ZkEvmProvider).toHaveBeenCalled();
148
+ expect(SequenceProvider).not.toHaveBeenCalled();
149
+ });
150
+
151
+ it('uses SequenceProvider for non-zkEVM chain (Arbitrum)', async () => {
152
+ const auth = createAuthStub();
153
+
154
+ await connectWallet({ auth, chains: [arbitrumChain] });
155
+
156
+ expect(SequenceProvider).toHaveBeenCalled();
157
+ expect(ZkEvmProvider).not.toHaveBeenCalled();
158
+ });
159
+ });
101
160
  });
@@ -10,13 +10,21 @@ import {
10
10
  mr,
11
11
  } from '@imtbl/generated-clients';
12
12
  import { ZkEvmProvider } from './zkEvm/zkEvmProvider';
13
- import { ConnectWalletOptions, PassportEventMap, ChainConfig } from './types';
13
+ import { SequenceProvider } from './sequence/sequenceProvider';
14
+ import {
15
+ ConnectWalletOptions, PassportEventMap, ChainConfig, Provider,
16
+ } from './types';
14
17
  import { WalletConfiguration } from './config';
15
18
  import GuardianClient from './guardian';
16
19
  import MagicTEESigner from './magic/magicTEESigner';
17
20
  import { announceProvider, passportProviderInfo } from './provider/eip6963';
18
- import { DEFAULT_CHAINS } from './presets';
19
- import { MAGIC_CONFIG, IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID } from './constants';
21
+ import { DEFAULT_CHAINS } from './network/presets';
22
+ import {
23
+ MAGIC_CONFIG,
24
+ IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID,
25
+ } from './constants';
26
+ import { ChainId } from './network/chains';
27
+ import { createSequenceSigner } from './sequence/signer';
20
28
 
21
29
  /**
22
30
  * Type guard to check if chainId is a valid key for MAGIC_CONFIG
@@ -64,6 +72,16 @@ const DEFAULT_REDIRECT_FALLBACK = 'https://auth.immutable.com/im-logged-in';
64
72
  const DEFAULT_AUTHENTICATION_DOMAIN = 'https://auth.immutable.com';
65
73
  const SANDBOX_DOMAIN_REGEX = /(sandbox|testnet)/i;
66
74
 
75
+ const ZKEVM_CHAIN_IDS = [
76
+ ChainId.IMTBL_ZKEVM_MAINNET,
77
+ ChainId.IMTBL_ZKEVM_TESTNET,
78
+ ChainId.IMTBL_ZKEVM_DEVNET,
79
+ ];
80
+
81
+ function isZkEvmChain(chain: ChainConfig): boolean {
82
+ return ZKEVM_CHAIN_IDS.includes(chain.chainId);
83
+ }
84
+
67
85
  function isSandboxChain(chain: ChainConfig): boolean {
68
86
  if (chain.chainId === IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID) {
69
87
  return true;
@@ -156,7 +174,7 @@ function createDefaultAuth(initialChain: ChainConfig, options: ConnectWalletOpti
156
174
  */
157
175
  export async function connectWallet(
158
176
  config: ConnectWalletOptions = {},
159
- ): Promise<ZkEvmProvider> {
177
+ ): Promise<Provider> {
160
178
  // Use default chains if not provided (testnet + mainnet)
161
179
  const chains = config.chains && config.chains.length > 0
162
180
  ? config.chains
@@ -216,7 +234,10 @@ export async function connectWallet(
216
234
  feeTokenSymbol: config.feeTokenSymbol,
217
235
  });
218
236
 
219
- // 6. Create GuardianClient
237
+ // 6. Create PassportEventEmitter
238
+ const passportEventEmitter = config.passportEventEmitter || new TypedEventEmitter<PassportEventMap>();
239
+
240
+ // 7. Create GuardianClient
220
241
  const guardianApi = new mr.GuardianApi(apiConfig);
221
242
 
222
243
  const guardianClient = new GuardianClient({
@@ -226,50 +247,66 @@ export async function connectWallet(
226
247
  authConfig,
227
248
  });
228
249
 
229
- // 7. Get Magic config for initial chain (from chain config or hard-coded default)
230
- const magicConfig = getMagicConfigForChain(initialChain);
250
+ // 8. Create provider based on chain type
251
+ let provider: Provider;
231
252
 
232
- // 8. Create MagicTEESigner with Magic TEE base path (separate from backend API)
233
- const magicTeeBasePath = initialChain.magicTeeBasePath || 'https://tee.express.magiclabs.com';
234
- const magicTeeApiClients = new MagicTeeApiClients({
235
- basePath: magicTeeBasePath,
236
- timeout: 10000,
237
- magicPublishableApiKey: magicConfig.magicPublishableApiKey,
238
- magicProviderId: magicConfig.magicProviderId,
239
- });
240
-
241
- const ethSigner = new MagicTEESigner(auth, magicTeeApiClients);
242
-
243
- // 9. Determine session activity API URL (only for mainnet, testnet, devnet)
244
- let sessionActivityApiUrl: string | null = null;
245
- if (initialChain.chainId === 13371) {
246
- // Mainnet
247
- sessionActivityApiUrl = 'https://api.immutable.com';
248
- } else if (initialChain.chainId === 13473) {
249
- // Testnet
250
- sessionActivityApiUrl = 'https://api.sandbox.immutable.com';
251
- } else if (initialChain.apiUrl) {
252
- // Devnet - use the apiUrl from chain config
253
- sessionActivityApiUrl = initialChain.apiUrl;
254
- }
255
- // For any other chain, sessionActivityApiUrl remains null (no session activity tracking)
253
+ if (isZkEvmChain(initialChain)) {
254
+ // 9. Get Magic config for initial chain (from chain config or hard-coded default)
255
+ const magicConfig = getMagicConfigForChain(initialChain);
256
256
 
257
- // 10. Create PassportEventEmitter
258
- const passportEventEmitter = config.passportEventEmitter || new TypedEventEmitter<PassportEventMap>();
257
+ // 10. Create MagicTEESigner with Magic TEE base path (separate from backend API)
258
+ const magicTeeBasePath = initialChain.magicTeeBasePath || 'https://tee.express.magiclabs.com';
259
+ const magicTeeApiClients = new MagicTeeApiClients({
260
+ basePath: magicTeeBasePath,
261
+ timeout: 10000,
262
+ magicPublishableApiKey: magicConfig.magicPublishableApiKey,
263
+ magicProviderId: magicConfig.magicProviderId,
264
+ });
265
+ const ethSigner = new MagicTEESigner(auth, magicTeeApiClients);
266
+
267
+ // 11. Determine session activity API URL (only for mainnet, testnet, devnet)
268
+ let sessionActivityApiUrl: string | null = null;
269
+ if (initialChain.chainId === 13371) {
270
+ // Mainnet
271
+ sessionActivityApiUrl = 'https://api.immutable.com';
272
+ } else if (initialChain.chainId === 13473) {
273
+ // Testnet
274
+ sessionActivityApiUrl = 'https://api.sandbox.immutable.com';
275
+ } else if (initialChain.apiUrl) {
276
+ // Devnet - use the apiUrl from chain config
277
+ sessionActivityApiUrl = initialChain.apiUrl;
278
+ }
279
+ // For any other chain, sessionActivityApiUrl remains null (no session activity tracking)
280
+
281
+ // 12. Create ZkEvmProvider
282
+ provider = new ZkEvmProvider({
283
+ auth,
284
+ config: walletConfig,
285
+ multiRollupApiClients,
286
+ passportEventEmitter,
287
+ guardianClient,
288
+ ethSigner,
289
+ user,
290
+ sessionActivityApiUrl,
291
+ });
292
+ } else {
293
+ // Create Sequence signer based on environment
294
+ const sequenceSigner = createSequenceSigner(auth, authConfig, {
295
+ identityInstrumentEndpoint: initialChain.sequenceIdentityInstrumentEndpoint,
296
+ });
259
297
 
260
- // 11. Create ZkEvmProvider
261
- const provider = new ZkEvmProvider({
262
- auth,
263
- config: walletConfig,
264
- multiRollupApiClients,
265
- passportEventEmitter,
266
- guardianClient,
267
- ethSigner,
268
- user,
269
- sessionActivityApiUrl,
270
- });
298
+ // Non-zkEVM chain - use SequenceProvider
299
+ provider = new SequenceProvider({
300
+ auth,
301
+ chainConfig: initialChain,
302
+ multiRollupApiClients,
303
+ guardianClient,
304
+ ethSigner: sequenceSigner,
305
+ passportEventEmitter,
306
+ });
307
+ }
271
308
 
272
- // 12. Announce provider via EIP-6963
309
+ // 13. Announce provider via EIP-6963
273
310
  if (config.announceProvider !== false) {
274
311
  announceProvider({
275
312
  info: passportProviderInfo,