Skip to content

Commit 557f3dc

Browse files
authored
feat(wallet): set up sequence provider (#2772)
1 parent c262bd5 commit 557f3dc

File tree

8 files changed

+305
-67
lines changed

8 files changed

+305
-67
lines changed

packages/passport/sdk/src/Passport.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
import type { DirectLoginOptions } from '@imtbl/auth';
1616
import {
1717
connectWallet,
18-
ZkEvmProvider,
1918
WalletConfiguration,
2019
GuardianClient,
2120
MagicTEESigner,
@@ -24,7 +23,7 @@ import {
2423
EvmChain,
2524
getChainConfig,
2625
} from '@imtbl/wallet';
27-
import type { LinkWalletParams, LinkedWallet } from '@imtbl/wallet';
26+
import type { Provider, LinkWalletParams, LinkedWallet } from '@imtbl/wallet';
2827
import {
2928
PassportModuleConfiguration,
3029
ConnectEvmArguments,
@@ -205,7 +204,7 @@ export class Passport {
205204
* @param {EvmChain} options.chain - The EVM chain to connect to (defaults to ZKEVM)
206205
* @returns {Promise<Provider>} The EVM provider instance
207206
*/
208-
public async connectEvm(options: ConnectEvmArguments = { announceProvider: true }): Promise<ZkEvmProvider> {
207+
public async connectEvm(options: ConnectEvmArguments = { announceProvider: true }): Promise<Provider> {
209208
return withMetricsAsync(async () => {
210209
const chain = options?.chain ?? EvmChain.ZKEVM;
211210

packages/passport/sdk/src/types.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Environment, ModuleConfiguration } from '@imtbl/config';
22
import { IMXClient } from '@imtbl/x-client';
33
import { ImxApiClients } from '@imtbl/generated-clients';
44
import { Flow } from '@imtbl/metrics';
5+
import { EvmChain } from '@imtbl/wallet';
56

67
/**
78
* Direct login method identifier
@@ -13,6 +14,7 @@ export type DirectLoginMethod = string;
1314
// Re-export events from auth and wallet
1415
export { AuthEvents } from '@imtbl/auth';
1516
export { WalletEvents } from '@imtbl/wallet';
17+
export { EvmChain };
1618

1719
export type AccountsRequestedEvent = {
1820
environment: Environment;
@@ -121,16 +123,13 @@ export type PKCEData = {
121123
// Re-export wallet linking types from wallet package
122124
export type { LinkWalletParams, LinkedWallet } from '@imtbl/wallet';
123125

124-
// Re-export EvmChain enum for specifying which chain to connect to
125-
export { EvmChain } from '@imtbl/wallet';
126-
127126
export type ConnectEvmArguments = {
128127
announceProvider: boolean;
129128
/**
130129
* The EVM chain to connect to (defaults to ZKEVM)
131130
* @default EvmChain.ZKEVM
132131
*/
133-
chain?: import('@imtbl/wallet').EvmChain;
132+
chain?: EvmChain;
134133
};
135134

136135
// Export ZkEvmProvider for return type

packages/wallet/src/connectWallet.test.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ jest.mock('./zkEvm/zkEvmProvider', () => ({
4242
ZkEvmProvider: jest.fn(),
4343
}));
4444

45+
jest.mock('./sequence/sequenceProvider', () => ({
46+
SequenceProvider: jest.fn(),
47+
}));
48+
4549
jest.mock('./provider/eip6963', () => ({
4650
announceProvider: jest.fn(),
4751
passportProviderInfo: { name: 'passport', rdns: 'com.immutable.passport', icon: '' },
@@ -51,15 +55,24 @@ const { connectWallet } = require('./connectWallet');
5155

5256
const { announceProvider } = jest.requireMock('./provider/eip6963');
5357
const { ZkEvmProvider } = jest.requireMock('./zkEvm/zkEvmProvider');
58+
const { SequenceProvider } = jest.requireMock('./sequence/sequenceProvider');
5459

55-
const chain = {
60+
const zkEvmChain = {
5661
chainId: 13473,
5762
rpcUrl: 'https://rpc.sandbox.immutable.com',
5863
relayerUrl: 'https://relayer.sandbox.immutable.com',
5964
apiUrl: 'https://api.sandbox.immutable.com',
6065
name: 'Immutable zkEVM Testnet',
6166
};
6267

68+
const arbitrumChain = {
69+
chainId: 42161,
70+
rpcUrl: 'https://arb1.arbitrum.io/rpc',
71+
relayerUrl: 'https://next-arbitrum-one-relayer.sequence.app',
72+
apiUrl: 'https://api.immutable.com',
73+
name: 'Arbitrum One',
74+
};
75+
6376
const createAuthStub = () => ({
6477
getConfig: jest.fn().mockReturnValue({
6578
authenticationDomain: 'https://auth.immutable.com',
@@ -82,7 +95,7 @@ describe('connectWallet', () => {
8295
it('announces provider by default', async () => {
8396
const auth = createAuthStub();
8497

85-
const provider = await connectWallet({ auth, chains: [chain] });
98+
const provider = await connectWallet({ auth, chains: [zkEvmChain] });
8699

87100
expect(ZkEvmProvider).toHaveBeenCalled();
88101
expect(announceProvider).toHaveBeenCalledWith({
@@ -94,8 +107,46 @@ describe('connectWallet', () => {
94107
it('does not announce provider when disabled', async () => {
95108
const auth = createAuthStub();
96109

97-
await connectWallet({ auth, chains: [chain], announceProvider: false });
110+
await connectWallet({ auth, chains: [zkEvmChain], announceProvider: false });
98111

99112
expect(announceProvider).not.toHaveBeenCalled();
100113
});
114+
115+
describe('provider selection', () => {
116+
it('uses ZkEvmProvider for zkEVM chain (by chainId)', async () => {
117+
const auth = createAuthStub();
118+
119+
await connectWallet({ auth, chains: [zkEvmChain] });
120+
121+
expect(ZkEvmProvider).toHaveBeenCalled();
122+
expect(SequenceProvider).not.toHaveBeenCalled();
123+
});
124+
125+
it('uses ZkEvmProvider for chain with magic config', async () => {
126+
const auth = createAuthStub();
127+
const devChain = {
128+
chainId: 99999, // unknown chainId
129+
rpcUrl: 'https://rpc.dev.immutable.com',
130+
relayerUrl: 'https://relayer.dev.immutable.com',
131+
apiUrl: 'https://api.dev.immutable.com',
132+
name: 'Dev Chain',
133+
magicPublishableApiKey: 'pk_test_123',
134+
magicProviderId: 'provider-123',
135+
};
136+
137+
await connectWallet({ auth, chains: [devChain] });
138+
139+
expect(ZkEvmProvider).toHaveBeenCalled();
140+
expect(SequenceProvider).not.toHaveBeenCalled();
141+
});
142+
143+
it('uses SequenceProvider for non-zkEVM chain (Arbitrum)', async () => {
144+
const auth = createAuthStub();
145+
146+
await connectWallet({ auth, chains: [arbitrumChain] });
147+
148+
expect(SequenceProvider).toHaveBeenCalled();
149+
expect(ZkEvmProvider).not.toHaveBeenCalled();
150+
});
151+
});
101152
});

packages/wallet/src/connectWallet.ts

Lines changed: 65 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@ import {
1010
mr,
1111
} from '@imtbl/generated-clients';
1212
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';
1417
import { WalletConfiguration } from './config';
1518
import GuardianClient from './guardian';
1619
import MagicTEESigner from './magic/magicTEESigner';
1720
import { announceProvider, passportProviderInfo } from './provider/eip6963';
1821
import { DEFAULT_CHAINS } from './presets';
19-
import { MAGIC_CONFIG, IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID } from './constants';
22+
import {
23+
MAGIC_CONFIG,
24+
IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID,
25+
} from './constants';
2026

2127
/**
2228
* Type guard to check if chainId is a valid key for MAGIC_CONFIG
@@ -64,6 +70,11 @@ const DEFAULT_REDIRECT_FALLBACK = 'https://auth.immutable.com/im-logged-in';
6470
const DEFAULT_AUTHENTICATION_DOMAIN = 'https://auth.immutable.com';
6571
const SANDBOX_DOMAIN_REGEX = /(sandbox|testnet)/i;
6672

73+
function isZkEvmChain(chain: ChainConfig): boolean {
74+
// Only zkEVM chains use magic
75+
return !!chain.magicPublishableApiKey || isValidMagicChainId(chain.chainId);
76+
}
77+
6778
function isSandboxChain(chain: ChainConfig): boolean {
6879
if (chain.chainId === IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID) {
6980
return true;
@@ -156,7 +167,7 @@ function createDefaultAuth(initialChain: ChainConfig, options: ConnectWalletOpti
156167
*/
157168
export async function connectWallet(
158169
config: ConnectWalletOptions = {},
159-
): Promise<ZkEvmProvider> {
170+
): Promise<Provider> {
160171
// Use default chains if not provided (testnet + mainnet)
161172
const chains = config.chains && config.chains.length > 0
162173
? config.chains
@@ -216,7 +227,10 @@ export async function connectWallet(
216227
feeTokenSymbol: config.feeTokenSymbol,
217228
});
218229

219-
// 6. Create GuardianClient
230+
// 6. Create PassportEventEmitter
231+
const passportEventEmitter = config.passportEventEmitter || new TypedEventEmitter<PassportEventMap>();
232+
233+
// 7. Create GuardianClient
220234
const guardianApi = new mr.GuardianApi(apiConfig);
221235

222236
const guardianClient = new GuardianClient({
@@ -226,50 +240,57 @@ export async function connectWallet(
226240
authConfig,
227241
});
228242

229-
// 7. Get Magic config for initial chain (from chain config or hard-coded default)
230-
const magicConfig = getMagicConfigForChain(initialChain);
243+
// 8. Create provider based on chain type
244+
let provider: Provider;
231245

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-
});
246+
if (isZkEvmChain(initialChain)) {
247+
// 9. Get Magic config for initial chain (from chain config or hard-coded default)
248+
const magicConfig = getMagicConfigForChain(initialChain);
240249

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;
250+
// 10. Create MagicTEESigner with Magic TEE base path (separate from backend API)
251+
const magicTeeBasePath = initialChain.magicTeeBasePath || 'https://tee.express.magiclabs.com';
252+
const magicTeeApiClients = new MagicTeeApiClients({
253+
basePath: magicTeeBasePath,
254+
timeout: 10000,
255+
magicPublishableApiKey: magicConfig.magicPublishableApiKey,
256+
magicProviderId: magicConfig.magicProviderId,
257+
});
258+
const ethSigner = new MagicTEESigner(auth, magicTeeApiClients);
259+
260+
// 11. Determine session activity API URL (only for mainnet, testnet, devnet)
261+
let sessionActivityApiUrl: string | null = null;
262+
if (initialChain.chainId === 13371) {
263+
// Mainnet
264+
sessionActivityApiUrl = 'https://api.immutable.com';
265+
} else if (initialChain.chainId === 13473) {
266+
// Testnet
267+
sessionActivityApiUrl = 'https://api.sandbox.immutable.com';
268+
} else if (initialChain.apiUrl) {
269+
// Devnet - use the apiUrl from chain config
270+
sessionActivityApiUrl = initialChain.apiUrl;
271+
}
272+
// For any other chain, sessionActivityApiUrl remains null (no session activity tracking)
273+
274+
// 12. Create ZkEvmProvider
275+
provider = new ZkEvmProvider({
276+
auth,
277+
config: walletConfig,
278+
multiRollupApiClients,
279+
passportEventEmitter,
280+
guardianClient,
281+
ethSigner,
282+
user,
283+
sessionActivityApiUrl,
284+
});
285+
} else {
286+
// Non-zkEVM chain - use SequenceProvider
287+
provider = new SequenceProvider({
288+
chainConfig: initialChain,
289+
guardianClient,
290+
});
254291
}
255-
// For any other chain, sessionActivityApiUrl remains null (no session activity tracking)
256-
257-
// 10. Create PassportEventEmitter
258-
const passportEventEmitter = config.passportEventEmitter || new TypedEventEmitter<PassportEventMap>();
259-
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-
});
271292

272-
// 12. Announce provider via EIP-6963
293+
// 13. Announce provider via EIP-6963
273294
if (config.announceProvider !== false) {
274295
announceProvider({
275296
info: passportProviderInfo,

packages/wallet/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ export {
2828
// Export chain registry for looking up chain configs
2929
export { getChainConfig } from './chainRegistry';
3030

31-
// Export main wallet provider
31+
// Export main wallet providers
3232
export { ZkEvmProvider } from './zkEvm/zkEvmProvider';
33+
export { SequenceProvider } from './sequence/sequenceProvider';
3334

3435
// Export internal configuration (for Passport/advanced usage)
3536
export { WalletConfiguration } from './config';

0 commit comments

Comments
 (0)