@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.
- package/dist/browser/index.js +202 -29
- package/dist/node/index.cjs +241 -56
- package/dist/node/index.js +202 -29
- package/dist/types/confirmation/confirmation.d.ts +1 -1
- package/dist/types/connectWallet.d.ts +2 -3
- package/dist/types/constants.d.ts +7 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/network/chainRegistry.d.ts +13 -0
- package/dist/types/{presets.d.ts → network/presets.d.ts} +37 -1
- package/dist/types/sequence/sequenceProvider.d.ts +21 -0
- package/dist/types/sequence/signer/identityInstrumentSigner.d.ts +15 -0
- package/dist/types/sequence/signer/index.d.ts +20 -0
- package/dist/types/sequence/signer/privateKeySigner.d.ts +15 -0
- package/dist/types/sequence/signer/types.d.ts +14 -0
- package/dist/types/sequence/user/index.d.ts +2 -0
- package/dist/types/sequence/user/registerUser.d.ts +18 -0
- package/dist/types/types.d.ts +22 -1
- package/dist/types/zkEvm/types.d.ts +3 -10
- package/package.json +9 -4
- package/src/confirmation/confirmation.ts +14 -4
- package/src/connectWallet.test.ts +62 -3
- package/src/connectWallet.ts +82 -45
- package/src/constants.ts +10 -0
- package/src/guardian/index.ts +2 -0
- package/src/index.ts +14 -2
- package/src/network/chainRegistry.test.ts +64 -0
- package/src/network/chainRegistry.ts +74 -0
- package/src/{presets.ts → network/presets.ts} +64 -2
- package/src/sequence/sequenceProvider.ts +276 -0
- package/src/sequence/signer/identityInstrumentSigner.ts +193 -0
- package/src/sequence/signer/index.ts +45 -0
- package/src/sequence/signer/privateKeySigner.ts +111 -0
- package/src/sequence/signer/types.ts +24 -0
- package/src/sequence/user/index.ts +2 -0
- package/src/sequence/user/registerUser.ts +100 -0
- package/src/types.ts +26 -2
- package/src/zkEvm/types.ts +4 -10
package/src/constants.ts
CHANGED
|
@@ -8,6 +8,16 @@ export const IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID = 13371;
|
|
|
8
8
|
/** Immutable zkEVM Testnet chain ID */
|
|
9
9
|
export const IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID = 13473;
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Chain ID constants for Arbitrum networks
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Arbitrum One Mainnet chain ID */
|
|
16
|
+
export const ARBITRUM_ONE_CHAIN_ID = 42161;
|
|
17
|
+
|
|
18
|
+
/** Arbitrum Sepolia Testnet chain ID */
|
|
19
|
+
export const ARBITRUM_SEPOLIA_CHAIN_ID = 421614;
|
|
20
|
+
|
|
11
21
|
/**
|
|
12
22
|
* Magic configuration for Immutable networks
|
|
13
23
|
* @internal
|
package/src/guardian/index.ts
CHANGED
|
@@ -232,6 +232,7 @@ export default class GuardianClient {
|
|
|
232
232
|
messageId,
|
|
233
233
|
user.zkEvm.ethAddress,
|
|
234
234
|
'eip712',
|
|
235
|
+
chainID,
|
|
235
236
|
);
|
|
236
237
|
|
|
237
238
|
if (!confirmationResult.confirmed) {
|
|
@@ -286,6 +287,7 @@ export default class GuardianClient {
|
|
|
286
287
|
messageId,
|
|
287
288
|
user.zkEvm.ethAddress,
|
|
288
289
|
'erc191',
|
|
290
|
+
String(chainID),
|
|
289
291
|
);
|
|
290
292
|
|
|
291
293
|
if (!confirmationResult.confirmed) {
|
package/src/index.ts
CHANGED
|
@@ -5,20 +5,32 @@ export { connectWallet } from './connectWallet';
|
|
|
5
5
|
export {
|
|
6
6
|
IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID,
|
|
7
7
|
IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID,
|
|
8
|
+
ARBITRUM_ONE_CHAIN_ID,
|
|
9
|
+
ARBITRUM_SEPOLIA_CHAIN_ID,
|
|
8
10
|
} from './constants';
|
|
9
11
|
|
|
10
12
|
// Export presets (public API)
|
|
11
13
|
export {
|
|
14
|
+
// zkEVM chains
|
|
12
15
|
IMMUTABLE_ZKEVM_MAINNET,
|
|
13
16
|
IMMUTABLE_ZKEVM_TESTNET,
|
|
14
17
|
IMMUTABLE_ZKEVM_MULTICHAIN,
|
|
15
18
|
IMMUTABLE_ZKEVM_MAINNET_CHAIN,
|
|
16
19
|
IMMUTABLE_ZKEVM_TESTNET_CHAIN,
|
|
17
20
|
DEFAULT_CHAINS,
|
|
18
|
-
|
|
21
|
+
// Arbitrum chains
|
|
22
|
+
ARBITRUM_ONE,
|
|
23
|
+
ARBITRUM_SEPOLIA,
|
|
24
|
+
ARBITRUM_ONE_CHAIN,
|
|
25
|
+
ARBITRUM_SEPOLIA_CHAIN,
|
|
26
|
+
} from './network/presets';
|
|
19
27
|
|
|
20
|
-
// Export
|
|
28
|
+
// Export chain registry for looking up chain configs
|
|
29
|
+
export { getChainConfig, getEvmChainFromChainId } from './network/chainRegistry';
|
|
30
|
+
|
|
31
|
+
// Export main wallet providers
|
|
21
32
|
export { ZkEvmProvider } from './zkEvm/zkEvmProvider';
|
|
33
|
+
export { SequenceProvider } from './sequence/sequenceProvider';
|
|
22
34
|
|
|
23
35
|
// Export internal configuration (for Passport/advanced usage)
|
|
24
36
|
export { WalletConfiguration } from './config';
|
|
@@ -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
|
+
});
|
|
@@ -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
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { ChainConfig } from '
|
|
1
|
+
import { ChainConfig } from '../types';
|
|
2
2
|
import {
|
|
3
3
|
IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID,
|
|
4
4
|
IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID,
|
|
5
|
+
ARBITRUM_ONE_CHAIN_ID,
|
|
6
|
+
ARBITRUM_SEPOLIA_CHAIN_ID,
|
|
5
7
|
MAGIC_CONFIG,
|
|
6
|
-
} from '
|
|
8
|
+
} from '../constants';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Immutable zkEVM Mainnet chain configuration
|
|
@@ -35,6 +37,36 @@ export const IMMUTABLE_ZKEVM_TESTNET_CHAIN: ChainConfig = {
|
|
|
35
37
|
magicTeeBasePath: 'https://tee.express.magiclabs.com',
|
|
36
38
|
};
|
|
37
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Arbitrum One Mainnet chain configuration
|
|
42
|
+
*/
|
|
43
|
+
export const ARBITRUM_ONE_CHAIN: ChainConfig = {
|
|
44
|
+
chainId: ARBITRUM_ONE_CHAIN_ID,
|
|
45
|
+
name: 'Arbitrum One',
|
|
46
|
+
rpcUrl: 'https://arb1.arbitrum.io/rpc',
|
|
47
|
+
relayerUrl: 'https://next-arbitrum-one-relayer.sequence.app',
|
|
48
|
+
nodeUrl: 'https://next-nodes.sequence.app/arbitrum-one',
|
|
49
|
+
apiUrl: 'https://api.immutable.com',
|
|
50
|
+
passportDomain: 'https://passport.immutable.com',
|
|
51
|
+
feeTokenSymbol: 'ETH',
|
|
52
|
+
sequenceIdentityInstrumentEndpoint: 'https://next-identity.sequence.app',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Arbitrum Sepolia Testnet chain configuration
|
|
57
|
+
*/
|
|
58
|
+
export const ARBITRUM_SEPOLIA_CHAIN: ChainConfig = {
|
|
59
|
+
chainId: ARBITRUM_SEPOLIA_CHAIN_ID,
|
|
60
|
+
name: 'Arbitrum Sepolia',
|
|
61
|
+
rpcUrl: 'https://sepolia-rollup.arbitrum.io/rpc',
|
|
62
|
+
relayerUrl: 'https://next-arbitrum-sepolia-relayer.sequence.app',
|
|
63
|
+
nodeUrl: 'https://next-nodes.sequence.app/arbitrum-sepolia',
|
|
64
|
+
apiUrl: 'https://api.sandbox.immutable.com',
|
|
65
|
+
passportDomain: 'https://passport.sandbox.immutable.com',
|
|
66
|
+
feeTokenSymbol: 'ETH',
|
|
67
|
+
sequenceIdentityInstrumentEndpoint: 'https://next-identity.sequence-dev.app',
|
|
68
|
+
};
|
|
69
|
+
|
|
38
70
|
/**
|
|
39
71
|
* Default chains (testnet + mainnet)
|
|
40
72
|
* Testnet is first (default initial chain)
|
|
@@ -90,3 +122,33 @@ export const IMMUTABLE_ZKEVM_TESTNET = {
|
|
|
90
122
|
export const IMMUTABLE_ZKEVM_MULTICHAIN = {
|
|
91
123
|
chains: DEFAULT_CHAINS,
|
|
92
124
|
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Arbitrum mainnet only preset
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* const provider = await connectWallet({
|
|
132
|
+
* ...ARBITRUM_ONE_MAINNET,
|
|
133
|
+
* auth,
|
|
134
|
+
* });
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export const ARBITRUM_ONE = {
|
|
138
|
+
chains: [ARBITRUM_ONE_CHAIN],
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Arbitrum testnet only preset
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const provider = await connectWallet({
|
|
147
|
+
* ...ARBITRUM_SEPOLIA,
|
|
148
|
+
* auth,
|
|
149
|
+
* });
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export const ARBITRUM_SEPOLIA = {
|
|
153
|
+
chains: [ARBITRUM_SEPOLIA_CHAIN],
|
|
154
|
+
};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPublicClient,
|
|
3
|
+
http,
|
|
4
|
+
toHex,
|
|
5
|
+
type PublicClient,
|
|
6
|
+
} from 'viem';
|
|
7
|
+
import { MultiRollupApiClients } from '@imtbl/generated-clients';
|
|
8
|
+
import { Auth, TypedEventEmitter, User } from '@imtbl/auth';
|
|
9
|
+
import { trackFlow, trackError, identify } from '@imtbl/metrics';
|
|
10
|
+
import {
|
|
11
|
+
Provider,
|
|
12
|
+
ProviderEvent,
|
|
13
|
+
ProviderEventMap,
|
|
14
|
+
RequestArguments,
|
|
15
|
+
ChainConfig,
|
|
16
|
+
EvmChain,
|
|
17
|
+
} from '../types';
|
|
18
|
+
import { JsonRpcError, ProviderErrorCode, RpcErrorCode } from '../zkEvm/JsonRpcError';
|
|
19
|
+
import GuardianClient from '../guardian';
|
|
20
|
+
import { SequenceSigner } from './signer';
|
|
21
|
+
import { registerUser } from './user';
|
|
22
|
+
import { getEvmChainFromChainId } from '../network/chainRegistry';
|
|
23
|
+
|
|
24
|
+
export type SequenceProviderInput = {
|
|
25
|
+
auth: Auth;
|
|
26
|
+
chainConfig: ChainConfig;
|
|
27
|
+
multiRollupApiClients: MultiRollupApiClients;
|
|
28
|
+
guardianClient: GuardianClient;
|
|
29
|
+
ethSigner: SequenceSigner;
|
|
30
|
+
passportEventEmitter: TypedEventEmitter<ProviderEventMap>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** Non-zkEVM chain type */
|
|
34
|
+
type SequenceChain = Exclude<EvmChain, EvmChain.ZKEVM>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if user is registered for a non-zkEVM chain.
|
|
38
|
+
* The chain data is stored as user[chainName] (e.g., user.arbitrum_one).
|
|
39
|
+
*/
|
|
40
|
+
function isUserRegisteredForChain(user: User, chain: SequenceChain): boolean {
|
|
41
|
+
return chain in user && !!(user as any)[chain]?.ethAddress;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the user's eth address for a non-zkEVM chain.
|
|
46
|
+
*/
|
|
47
|
+
function getUserChainAddress(user: User, chain: SequenceChain): string | undefined {
|
|
48
|
+
const chainData = (user as any)[chain];
|
|
49
|
+
return chainData?.ethAddress;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class SequenceProvider implements Provider {
|
|
53
|
+
readonly #auth: Auth;
|
|
54
|
+
|
|
55
|
+
readonly #chainConfig: ChainConfig;
|
|
56
|
+
|
|
57
|
+
readonly #multiRollupApiClients: MultiRollupApiClients;
|
|
58
|
+
|
|
59
|
+
readonly #rpcProvider: PublicClient;
|
|
60
|
+
|
|
61
|
+
readonly #providerEventEmitter: TypedEventEmitter<ProviderEventMap>;
|
|
62
|
+
|
|
63
|
+
readonly #guardianClient: GuardianClient;
|
|
64
|
+
|
|
65
|
+
readonly #ethSigner: SequenceSigner;
|
|
66
|
+
|
|
67
|
+
readonly #evmChain: SequenceChain;
|
|
68
|
+
|
|
69
|
+
public readonly isPassport: boolean = true;
|
|
70
|
+
|
|
71
|
+
constructor({
|
|
72
|
+
auth,
|
|
73
|
+
chainConfig,
|
|
74
|
+
multiRollupApiClients,
|
|
75
|
+
guardianClient,
|
|
76
|
+
ethSigner,
|
|
77
|
+
passportEventEmitter,
|
|
78
|
+
}: SequenceProviderInput) {
|
|
79
|
+
// Validate this is not a zkEVM chain
|
|
80
|
+
const evmChain = getEvmChainFromChainId(chainConfig.chainId);
|
|
81
|
+
if (evmChain === EvmChain.ZKEVM) {
|
|
82
|
+
throw new Error('SequenceProvider cannot be used for zkEVM chains. Use ZkEvmProvider instead.');
|
|
83
|
+
}
|
|
84
|
+
this.#evmChain = evmChain;
|
|
85
|
+
|
|
86
|
+
this.#auth = auth;
|
|
87
|
+
this.#chainConfig = chainConfig;
|
|
88
|
+
this.#multiRollupApiClients = multiRollupApiClients;
|
|
89
|
+
this.#guardianClient = guardianClient;
|
|
90
|
+
this.#ethSigner = ethSigner;
|
|
91
|
+
this.#providerEventEmitter = passportEventEmitter;
|
|
92
|
+
|
|
93
|
+
// Create PublicClient for reading from the chain using viem
|
|
94
|
+
this.#rpcProvider = createPublicClient({
|
|
95
|
+
transport: http(this.#chainConfig.rpcUrl),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get the user's address for this chain if already registered.
|
|
101
|
+
*/
|
|
102
|
+
async #getChainAddress(): Promise<string | undefined> {
|
|
103
|
+
const user = await this.#auth.getUser();
|
|
104
|
+
if (user && isUserRegisteredForChain(user, this.#evmChain)) {
|
|
105
|
+
return getUserChainAddress(user, this.#evmChain);
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async #performRequest(request: RequestArguments): Promise<any> {
|
|
111
|
+
switch (request.method) {
|
|
112
|
+
case 'eth_requestAccounts': {
|
|
113
|
+
// Check if already registered
|
|
114
|
+
const existingAddress = await this.#getChainAddress();
|
|
115
|
+
if (existingAddress) return [existingAddress];
|
|
116
|
+
|
|
117
|
+
const flow = trackFlow('passport', 'ethRequestAccounts');
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const user = await this.#auth.getUserOrLogin();
|
|
121
|
+
flow.addEvent('endGetUserOrLogin');
|
|
122
|
+
|
|
123
|
+
let userEthAddress: string | undefined;
|
|
124
|
+
|
|
125
|
+
if (!isUserRegisteredForChain(user, this.#evmChain)) {
|
|
126
|
+
flow.addEvent('startUserRegistration');
|
|
127
|
+
|
|
128
|
+
userEthAddress = await registerUser({
|
|
129
|
+
auth: this.#auth,
|
|
130
|
+
ethSigner: this.#ethSigner,
|
|
131
|
+
multiRollupApiClients: this.#multiRollupApiClients,
|
|
132
|
+
accessToken: user.accessToken,
|
|
133
|
+
rpcProvider: this.#rpcProvider,
|
|
134
|
+
flow,
|
|
135
|
+
});
|
|
136
|
+
flow.addEvent('endUserRegistration');
|
|
137
|
+
} else {
|
|
138
|
+
userEthAddress = getUserChainAddress(user, this.#evmChain);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!userEthAddress) {
|
|
142
|
+
throw new JsonRpcError(
|
|
143
|
+
RpcErrorCode.INTERNAL_ERROR,
|
|
144
|
+
'Failed to get user address after registration',
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.#providerEventEmitter.emit(ProviderEvent.ACCOUNTS_CHANGED, [
|
|
149
|
+
userEthAddress,
|
|
150
|
+
]);
|
|
151
|
+
identify({
|
|
152
|
+
passportId: user.profile.sub,
|
|
153
|
+
});
|
|
154
|
+
return [userEthAddress];
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error instanceof Error) {
|
|
157
|
+
trackError('passport', 'ethRequestAccounts', error, { flowId: flow.details.flowId });
|
|
158
|
+
} else {
|
|
159
|
+
flow.addEvent('errored');
|
|
160
|
+
}
|
|
161
|
+
throw error;
|
|
162
|
+
} finally {
|
|
163
|
+
flow.addEvent('End');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
case 'eth_accounts': {
|
|
168
|
+
const address = await this.#getChainAddress();
|
|
169
|
+
return address ? [address] : [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// TODO: Implement eth_sendTransaction
|
|
173
|
+
case 'eth_sendTransaction': {
|
|
174
|
+
throw new JsonRpcError(
|
|
175
|
+
ProviderErrorCode.UNSUPPORTED_METHOD,
|
|
176
|
+
'eth_sendTransaction not yet implemented for this chain',
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// TODO: Implement personal_sign
|
|
181
|
+
case 'personal_sign': {
|
|
182
|
+
throw new JsonRpcError(
|
|
183
|
+
ProviderErrorCode.UNSUPPORTED_METHOD,
|
|
184
|
+
'personal_sign not yet implemented for this chain',
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// TODO: Implement eth_signTypedData
|
|
189
|
+
case 'eth_signTypedData':
|
|
190
|
+
case 'eth_signTypedData_v4': {
|
|
191
|
+
throw new JsonRpcError(
|
|
192
|
+
ProviderErrorCode.UNSUPPORTED_METHOD,
|
|
193
|
+
'eth_signTypedData not yet implemented for this chain',
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
case 'eth_chainId': {
|
|
198
|
+
const chainId = await this.#rpcProvider.getChainId();
|
|
199
|
+
return toHex(chainId);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Pass through methods
|
|
203
|
+
case 'eth_getBalance':
|
|
204
|
+
case 'eth_getCode':
|
|
205
|
+
case 'eth_getTransactionCount': {
|
|
206
|
+
const [address, blockNumber] = request.params || [];
|
|
207
|
+
return this.#rpcProvider.request({
|
|
208
|
+
method: request.method as any,
|
|
209
|
+
params: [address, blockNumber || 'latest'],
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
case 'eth_getStorageAt': {
|
|
214
|
+
const [address, storageSlot, blockNumber] = request.params || [];
|
|
215
|
+
return this.#rpcProvider.request({
|
|
216
|
+
method: 'eth_getStorageAt',
|
|
217
|
+
params: [address, storageSlot, blockNumber || 'latest'],
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
case 'eth_call':
|
|
222
|
+
case 'eth_estimateGas': {
|
|
223
|
+
const [transaction, blockNumber] = request.params || [];
|
|
224
|
+
return this.#rpcProvider.request({
|
|
225
|
+
method: request.method as any,
|
|
226
|
+
params: [transaction, blockNumber || 'latest'],
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
case 'eth_gasPrice':
|
|
231
|
+
case 'eth_blockNumber':
|
|
232
|
+
case 'eth_getBlockByHash':
|
|
233
|
+
case 'eth_getBlockByNumber':
|
|
234
|
+
case 'eth_getTransactionByHash':
|
|
235
|
+
case 'eth_getTransactionReceipt': {
|
|
236
|
+
return this.#rpcProvider.request({
|
|
237
|
+
method: request.method as any,
|
|
238
|
+
params: (request.params || []) as any,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
default: {
|
|
243
|
+
throw new JsonRpcError(
|
|
244
|
+
ProviderErrorCode.UNSUPPORTED_METHOD,
|
|
245
|
+
'Method not supported',
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public async request(request: RequestArguments): Promise<any> {
|
|
252
|
+
try {
|
|
253
|
+
return this.#performRequest(request);
|
|
254
|
+
} catch (error: unknown) {
|
|
255
|
+
if (error instanceof JsonRpcError) {
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
if (error instanceof Error) {
|
|
259
|
+
throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, error.message);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, 'Internal error');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
public on(event: string, listener: (...args: any[]) => void): void {
|
|
267
|
+
this.#providerEventEmitter.on(event, listener);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
public removeListener(
|
|
271
|
+
event: string,
|
|
272
|
+
listener: (...args: any[]) => void,
|
|
273
|
+
): void {
|
|
274
|
+
this.#providerEventEmitter.removeListener(event, listener);
|
|
275
|
+
}
|
|
276
|
+
}
|