@imtbl/wallet 2.12.7-alpha.1 → 2.12.7-alpha.11
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/README.md +1 -31
- package/dist/browser/index.js +30 -204
- package/dist/node/index.cjs +55 -244
- package/dist/node/index.js +29 -203
- package/dist/types/confirmation/confirmation.d.ts +1 -1
- package/dist/types/connectWallet.d.ts +3 -2
- package/dist/types/constants.d.ts +0 -9
- package/dist/types/index.d.ts +2 -4
- package/dist/types/{network/presets.d.ts → presets.d.ts} +1 -55
- package/dist/types/types.d.ts +0 -12
- package/dist/types/zkEvm/types.d.ts +10 -3
- package/package.json +4 -9
- package/src/confirmation/confirmation.ts +4 -14
- package/src/connectWallet.test.ts +464 -58
- package/src/connectWallet.ts +40 -81
- package/src/constants.ts +0 -13
- package/src/guardian/index.ts +0 -2
- package/src/index.ts +2 -18
- package/src/presets.ts +92 -0
- package/src/types.ts +0 -16
- package/src/zkEvm/types.ts +10 -4
- package/dist/types/network/chainRegistry.d.ts +0 -13
- package/dist/types/sequence/sequenceProvider.d.ts +0 -21
- package/dist/types/sequence/signer/identityInstrumentSigner.d.ts +0 -15
- package/dist/types/sequence/signer/index.d.ts +0 -21
- package/dist/types/sequence/signer/privateKeySigner.d.ts +0 -15
- package/dist/types/sequence/signer/types.d.ts +0 -14
- package/dist/types/sequence/user/index.d.ts +0 -2
- package/dist/types/sequence/user/registerUser.d.ts +0 -18
- package/src/network/chainRegistry.test.ts +0 -64
- package/src/network/chainRegistry.ts +0 -74
- package/src/network/presets.ts +0 -185
- package/src/sequence/sequenceProvider.ts +0 -284
- package/src/sequence/signer/identityInstrumentSigner.ts +0 -195
- package/src/sequence/signer/index.ts +0 -41
- package/src/sequence/signer/privateKeySigner.ts +0 -112
- package/src/sequence/signer/types.ts +0 -24
- package/src/sequence/user/index.ts +0 -2
- package/src/sequence/user/registerUser.ts +0 -101
package/src/network/presets.ts
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import { ChainConfig } from '../types';
|
|
2
|
-
import {
|
|
3
|
-
IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID,
|
|
4
|
-
IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID,
|
|
5
|
-
ARBITRUM_ONE_CHAIN_ID,
|
|
6
|
-
ARBITRUM_SEPOLIA_CHAIN_ID,
|
|
7
|
-
ETHEREUM_SEPOLIA_CHAIN_ID,
|
|
8
|
-
MAGIC_CONFIG,
|
|
9
|
-
} from '../constants';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Immutable zkEVM Mainnet chain configuration
|
|
13
|
-
*/
|
|
14
|
-
export const IMMUTABLE_ZKEVM_MAINNET_CHAIN: ChainConfig = {
|
|
15
|
-
chainId: IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID,
|
|
16
|
-
name: 'Immutable zkEVM',
|
|
17
|
-
rpcUrl: 'https://rpc.immutable.com',
|
|
18
|
-
relayerUrl: 'https://api.immutable.com/relayer-mr',
|
|
19
|
-
apiUrl: 'https://api.immutable.com',
|
|
20
|
-
passportDomain: 'https://passport.immutable.com',
|
|
21
|
-
magicPublishableApiKey: MAGIC_CONFIG[IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID].magicPublishableApiKey,
|
|
22
|
-
magicProviderId: MAGIC_CONFIG[IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID].magicProviderId,
|
|
23
|
-
magicTeeBasePath: 'https://tee.express.magiclabs.com',
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Immutable zkEVM Testnet chain configuration
|
|
28
|
-
*/
|
|
29
|
-
export const IMMUTABLE_ZKEVM_TESTNET_CHAIN: ChainConfig = {
|
|
30
|
-
chainId: IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID,
|
|
31
|
-
name: 'Immutable zkEVM Testnet',
|
|
32
|
-
rpcUrl: 'https://rpc.testnet.immutable.com',
|
|
33
|
-
relayerUrl: 'https://api.sandbox.immutable.com/relayer-mr',
|
|
34
|
-
apiUrl: 'https://api.sandbox.immutable.com',
|
|
35
|
-
passportDomain: 'https://passport.sandbox.immutable.com',
|
|
36
|
-
magicPublishableApiKey: MAGIC_CONFIG[IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID].magicPublishableApiKey,
|
|
37
|
-
magicProviderId: MAGIC_CONFIG[IMMUTABLE_ZKEVM_TESTNET_CHAIN_ID].magicProviderId,
|
|
38
|
-
magicTeeBasePath: 'https://tee.express.magiclabs.com',
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Arbitrum One Mainnet chain configuration
|
|
43
|
-
*/
|
|
44
|
-
export const ARBITRUM_ONE_CHAIN: ChainConfig = {
|
|
45
|
-
chainId: ARBITRUM_ONE_CHAIN_ID,
|
|
46
|
-
name: 'Arbitrum One',
|
|
47
|
-
rpcUrl: 'https://arb1.arbitrum.io/rpc',
|
|
48
|
-
relayerUrl: 'https://next-arbitrum-one-relayer.sequence.app',
|
|
49
|
-
nodeUrl: 'https://next-nodes.sequence.app/arbitrum-one',
|
|
50
|
-
apiUrl: 'https://api.immutable.com',
|
|
51
|
-
passportDomain: 'https://passport.immutable.com',
|
|
52
|
-
feeTokenSymbol: 'ETH',
|
|
53
|
-
sequenceIdentityInstrumentEndpoint: 'https://next-identity.sequence.app',
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Arbitrum Sepolia Testnet chain configuration
|
|
58
|
-
*/
|
|
59
|
-
export const ARBITRUM_SEPOLIA_CHAIN: ChainConfig = {
|
|
60
|
-
chainId: ARBITRUM_SEPOLIA_CHAIN_ID,
|
|
61
|
-
name: 'Arbitrum Sepolia',
|
|
62
|
-
rpcUrl: 'https://sepolia-rollup.arbitrum.io/rpc',
|
|
63
|
-
relayerUrl: 'https://next-arbitrum-sepolia-relayer.sequence.app',
|
|
64
|
-
nodeUrl: 'https://next-nodes.sequence.app/arbitrum-sepolia',
|
|
65
|
-
apiUrl: 'https://api.sandbox.immutable.com',
|
|
66
|
-
passportDomain: 'https://passport.sandbox.immutable.com',
|
|
67
|
-
feeTokenSymbol: 'ETH',
|
|
68
|
-
sequenceIdentityInstrumentEndpoint: 'https://next-identity.sequence-dev.app',
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Ethereum Sepolia Testnet chain configuration
|
|
73
|
-
*/
|
|
74
|
-
export const ETHEREUM_SEPOLIA_CHAIN: ChainConfig = {
|
|
75
|
-
chainId: ETHEREUM_SEPOLIA_CHAIN_ID,
|
|
76
|
-
name: 'Ethereum Sepolia',
|
|
77
|
-
rpcUrl: 'https://rpc.sepolia.org',
|
|
78
|
-
relayerUrl: 'https://next-sepolia-relayer.sequence.app',
|
|
79
|
-
nodeUrl: 'https://next-nodes.sequence.app/sepolia',
|
|
80
|
-
apiUrl: 'https://api.sandbox.immutable.com',
|
|
81
|
-
passportDomain: 'https://passport.sandbox.immutable.com',
|
|
82
|
-
feeTokenSymbol: 'ETH',
|
|
83
|
-
sequenceIdentityInstrumentEndpoint: 'https://next-identity.sequence-dev.app',
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Default chains (testnet + mainnet)
|
|
88
|
-
* Testnet is first (default initial chain)
|
|
89
|
-
*/
|
|
90
|
-
export const DEFAULT_CHAINS: ChainConfig[] = [
|
|
91
|
-
IMMUTABLE_ZKEVM_TESTNET_CHAIN,
|
|
92
|
-
IMMUTABLE_ZKEVM_MAINNET_CHAIN,
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Mainnet only preset
|
|
97
|
-
*
|
|
98
|
-
* @example
|
|
99
|
-
* ```typescript
|
|
100
|
-
* const provider = await connectWallet({
|
|
101
|
-
* ...IMMUTABLE_ZKEVM_MAINNET,
|
|
102
|
-
* auth,
|
|
103
|
-
* });
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
export const IMMUTABLE_ZKEVM_MAINNET = {
|
|
107
|
-
chains: [IMMUTABLE_ZKEVM_MAINNET_CHAIN],
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Testnet only preset
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* ```typescript
|
|
115
|
-
* const provider = await connectWallet({
|
|
116
|
-
* ...IMMUTABLE_ZKEVM_TESTNET,
|
|
117
|
-
* auth,
|
|
118
|
-
* });
|
|
119
|
-
* ```
|
|
120
|
-
*/
|
|
121
|
-
export const IMMUTABLE_ZKEVM_TESTNET = {
|
|
122
|
-
chains: [IMMUTABLE_ZKEVM_TESTNET_CHAIN],
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Multi-chain preset (testnet + mainnet)
|
|
127
|
-
* Defaults to testnet as initial chain
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```typescript
|
|
131
|
-
* const provider = await connectWallet({
|
|
132
|
-
* ...IMMUTABLE_ZKEVM_MULTICHAIN,
|
|
133
|
-
* auth,
|
|
134
|
-
* initialChainId: IMMUTABLE_ZKEVM_MAINNET_CHAIN_ID, // Optional: start on mainnet
|
|
135
|
-
* });
|
|
136
|
-
* ```
|
|
137
|
-
*/
|
|
138
|
-
export const IMMUTABLE_ZKEVM_MULTICHAIN = {
|
|
139
|
-
chains: DEFAULT_CHAINS,
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Arbitrum mainnet only preset
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* ```typescript
|
|
147
|
-
* const provider = await connectWallet({
|
|
148
|
-
* ...ARBITRUM_ONE_MAINNET,
|
|
149
|
-
* auth,
|
|
150
|
-
* });
|
|
151
|
-
* ```
|
|
152
|
-
*/
|
|
153
|
-
export const ARBITRUM_ONE = {
|
|
154
|
-
chains: [ARBITRUM_ONE_CHAIN],
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Arbitrum testnet only preset
|
|
159
|
-
*
|
|
160
|
-
* @example
|
|
161
|
-
* ```typescript
|
|
162
|
-
* const provider = await connectWallet({
|
|
163
|
-
* ...ARBITRUM_SEPOLIA,
|
|
164
|
-
* auth,
|
|
165
|
-
* });
|
|
166
|
-
* ```
|
|
167
|
-
*/
|
|
168
|
-
export const ARBITRUM_SEPOLIA = {
|
|
169
|
-
chains: [ARBITRUM_SEPOLIA_CHAIN],
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Ethereum Sepolia testnet only preset
|
|
174
|
-
*
|
|
175
|
-
* @example
|
|
176
|
-
* ```typescript
|
|
177
|
-
* const provider = await connectWallet({
|
|
178
|
-
* ...ETHEREUM_SEPOLIA,
|
|
179
|
-
* auth,
|
|
180
|
-
* });
|
|
181
|
-
* ```
|
|
182
|
-
*/
|
|
183
|
-
export const ETHEREUM_SEPOLIA = {
|
|
184
|
-
chains: [ETHEREUM_SEPOLIA_CHAIN],
|
|
185
|
-
};
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createPublicClient,
|
|
3
|
-
http,
|
|
4
|
-
toHex,
|
|
5
|
-
type PublicClient,
|
|
6
|
-
} from 'viem';
|
|
7
|
-
import { MultiRollupApiClients } from '@imtbl/generated-clients';
|
|
8
|
-
import { 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
|
-
GetUserFunction,
|
|
18
|
-
} from '../types';
|
|
19
|
-
import { JsonRpcError, ProviderErrorCode, RpcErrorCode } from '../zkEvm/JsonRpcError';
|
|
20
|
-
import GuardianClient from '../guardian';
|
|
21
|
-
import { SequenceSigner } from './signer';
|
|
22
|
-
import { registerUser } from './user';
|
|
23
|
-
import { getEvmChainFromChainId } from '../network/chainRegistry';
|
|
24
|
-
|
|
25
|
-
export type SequenceProviderInput = {
|
|
26
|
-
getUser: GetUserFunction;
|
|
27
|
-
chainConfig: ChainConfig;
|
|
28
|
-
multiRollupApiClients: MultiRollupApiClients;
|
|
29
|
-
guardianClient: GuardianClient;
|
|
30
|
-
ethSigner: SequenceSigner;
|
|
31
|
-
passportEventEmitter: TypedEventEmitter<ProviderEventMap>;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/** Non-zkEVM chain type */
|
|
35
|
-
type SequenceChain = Exclude<EvmChain, EvmChain.ZKEVM>;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Check if user is registered for a non-zkEVM chain.
|
|
39
|
-
* The chain data is stored as user[chainName] (e.g., user.arbitrum_one).
|
|
40
|
-
*/
|
|
41
|
-
function isUserRegisteredForChain(user: User, chain: SequenceChain): boolean {
|
|
42
|
-
return chain in user && !!(user as any)[chain]?.ethAddress;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get the user's eth address for a non-zkEVM chain.
|
|
47
|
-
*/
|
|
48
|
-
function getUserChainAddress(user: User, chain: SequenceChain): string | undefined {
|
|
49
|
-
const chainData = (user as any)[chain];
|
|
50
|
-
return chainData?.ethAddress;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export class SequenceProvider implements Provider {
|
|
54
|
-
readonly #getUser: GetUserFunction;
|
|
55
|
-
|
|
56
|
-
readonly #chainConfig: ChainConfig;
|
|
57
|
-
|
|
58
|
-
readonly #multiRollupApiClients: MultiRollupApiClients;
|
|
59
|
-
|
|
60
|
-
readonly #rpcProvider: PublicClient;
|
|
61
|
-
|
|
62
|
-
readonly #providerEventEmitter: TypedEventEmitter<ProviderEventMap>;
|
|
63
|
-
|
|
64
|
-
readonly #guardianClient: GuardianClient;
|
|
65
|
-
|
|
66
|
-
readonly #ethSigner: SequenceSigner;
|
|
67
|
-
|
|
68
|
-
readonly #evmChain: SequenceChain;
|
|
69
|
-
|
|
70
|
-
public readonly isPassport: boolean = true;
|
|
71
|
-
|
|
72
|
-
constructor({
|
|
73
|
-
getUser,
|
|
74
|
-
chainConfig,
|
|
75
|
-
multiRollupApiClients,
|
|
76
|
-
guardianClient,
|
|
77
|
-
ethSigner,
|
|
78
|
-
passportEventEmitter,
|
|
79
|
-
}: SequenceProviderInput) {
|
|
80
|
-
// Validate this is not a zkEVM chain
|
|
81
|
-
const evmChain = getEvmChainFromChainId(chainConfig.chainId);
|
|
82
|
-
if (evmChain === EvmChain.ZKEVM) {
|
|
83
|
-
throw new Error('SequenceProvider cannot be used for zkEVM chains. Use ZkEvmProvider instead.');
|
|
84
|
-
}
|
|
85
|
-
this.#evmChain = evmChain;
|
|
86
|
-
|
|
87
|
-
this.#getUser = getUser;
|
|
88
|
-
this.#chainConfig = chainConfig;
|
|
89
|
-
this.#multiRollupApiClients = multiRollupApiClients;
|
|
90
|
-
this.#guardianClient = guardianClient;
|
|
91
|
-
this.#ethSigner = ethSigner;
|
|
92
|
-
this.#providerEventEmitter = passportEventEmitter;
|
|
93
|
-
|
|
94
|
-
// Create PublicClient for reading from the chain using viem
|
|
95
|
-
this.#rpcProvider = createPublicClient({
|
|
96
|
-
transport: http(this.#chainConfig.rpcUrl),
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Get the user's address for this chain if already registered.
|
|
102
|
-
*/
|
|
103
|
-
async #getChainAddress(): Promise<string | undefined> {
|
|
104
|
-
const user = await this.#getUser();
|
|
105
|
-
if (user && isUserRegisteredForChain(user, this.#evmChain)) {
|
|
106
|
-
return getUserChainAddress(user, this.#evmChain);
|
|
107
|
-
}
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async #performRequest(request: RequestArguments): Promise<any> {
|
|
112
|
-
switch (request.method) {
|
|
113
|
-
case 'eth_requestAccounts': {
|
|
114
|
-
// Check if already registered
|
|
115
|
-
const existingAddress = await this.#getChainAddress();
|
|
116
|
-
if (existingAddress) return [existingAddress];
|
|
117
|
-
|
|
118
|
-
const flow = trackFlow('passport', 'ethRequestAccounts');
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
const user = await this.#getUser();
|
|
122
|
-
flow.addEvent('endGetUser');
|
|
123
|
-
|
|
124
|
-
if (!user) {
|
|
125
|
-
throw new JsonRpcError(
|
|
126
|
-
RpcErrorCode.INTERNAL_ERROR,
|
|
127
|
-
'User not authenticated',
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
let userEthAddress: string | undefined;
|
|
132
|
-
|
|
133
|
-
if (!isUserRegisteredForChain(user, this.#evmChain)) {
|
|
134
|
-
flow.addEvent('startUserRegistration');
|
|
135
|
-
|
|
136
|
-
userEthAddress = await registerUser({
|
|
137
|
-
getUser: this.#getUser,
|
|
138
|
-
ethSigner: this.#ethSigner,
|
|
139
|
-
multiRollupApiClients: this.#multiRollupApiClients,
|
|
140
|
-
accessToken: user.accessToken,
|
|
141
|
-
rpcProvider: this.#rpcProvider,
|
|
142
|
-
flow,
|
|
143
|
-
});
|
|
144
|
-
flow.addEvent('endUserRegistration');
|
|
145
|
-
} else {
|
|
146
|
-
userEthAddress = getUserChainAddress(user, this.#evmChain);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (!userEthAddress) {
|
|
150
|
-
throw new JsonRpcError(
|
|
151
|
-
RpcErrorCode.INTERNAL_ERROR,
|
|
152
|
-
'Failed to get user address after registration',
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
this.#providerEventEmitter.emit(ProviderEvent.ACCOUNTS_CHANGED, [
|
|
157
|
-
userEthAddress,
|
|
158
|
-
]);
|
|
159
|
-
identify({
|
|
160
|
-
passportId: user.profile.sub,
|
|
161
|
-
});
|
|
162
|
-
return [userEthAddress];
|
|
163
|
-
} catch (error) {
|
|
164
|
-
if (error instanceof Error) {
|
|
165
|
-
trackError('passport', 'ethRequestAccounts', error, { flowId: flow.details.flowId });
|
|
166
|
-
} else {
|
|
167
|
-
flow.addEvent('errored');
|
|
168
|
-
}
|
|
169
|
-
throw error;
|
|
170
|
-
} finally {
|
|
171
|
-
flow.addEvent('End');
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
case 'eth_accounts': {
|
|
176
|
-
const address = await this.#getChainAddress();
|
|
177
|
-
return address ? [address] : [];
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// TODO: Implement eth_sendTransaction
|
|
181
|
-
case 'eth_sendTransaction': {
|
|
182
|
-
throw new JsonRpcError(
|
|
183
|
-
ProviderErrorCode.UNSUPPORTED_METHOD,
|
|
184
|
-
'eth_sendTransaction not yet implemented for this chain',
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// TODO: Implement personal_sign
|
|
189
|
-
case 'personal_sign': {
|
|
190
|
-
throw new JsonRpcError(
|
|
191
|
-
ProviderErrorCode.UNSUPPORTED_METHOD,
|
|
192
|
-
'personal_sign not yet implemented for this chain',
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// TODO: Implement eth_signTypedData
|
|
197
|
-
case 'eth_signTypedData':
|
|
198
|
-
case 'eth_signTypedData_v4': {
|
|
199
|
-
throw new JsonRpcError(
|
|
200
|
-
ProviderErrorCode.UNSUPPORTED_METHOD,
|
|
201
|
-
'eth_signTypedData not yet implemented for this chain',
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
case 'eth_chainId': {
|
|
206
|
-
const chainId = await this.#rpcProvider.getChainId();
|
|
207
|
-
return toHex(chainId);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Pass through methods
|
|
211
|
-
case 'eth_getBalance':
|
|
212
|
-
case 'eth_getCode':
|
|
213
|
-
case 'eth_getTransactionCount': {
|
|
214
|
-
const [address, blockNumber] = request.params || [];
|
|
215
|
-
return this.#rpcProvider.request({
|
|
216
|
-
method: request.method as any,
|
|
217
|
-
params: [address, blockNumber || 'latest'],
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
case 'eth_getStorageAt': {
|
|
222
|
-
const [address, storageSlot, blockNumber] = request.params || [];
|
|
223
|
-
return this.#rpcProvider.request({
|
|
224
|
-
method: 'eth_getStorageAt',
|
|
225
|
-
params: [address, storageSlot, blockNumber || 'latest'],
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
case 'eth_call':
|
|
230
|
-
case 'eth_estimateGas': {
|
|
231
|
-
const [transaction, blockNumber] = request.params || [];
|
|
232
|
-
return this.#rpcProvider.request({
|
|
233
|
-
method: request.method as any,
|
|
234
|
-
params: [transaction, blockNumber || 'latest'],
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
case 'eth_gasPrice':
|
|
239
|
-
case 'eth_blockNumber':
|
|
240
|
-
case 'eth_getBlockByHash':
|
|
241
|
-
case 'eth_getBlockByNumber':
|
|
242
|
-
case 'eth_getTransactionByHash':
|
|
243
|
-
case 'eth_getTransactionReceipt': {
|
|
244
|
-
return this.#rpcProvider.request({
|
|
245
|
-
method: request.method as any,
|
|
246
|
-
params: (request.params || []) as any,
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
default: {
|
|
251
|
-
throw new JsonRpcError(
|
|
252
|
-
ProviderErrorCode.UNSUPPORTED_METHOD,
|
|
253
|
-
'Method not supported',
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
public async request(request: RequestArguments): Promise<any> {
|
|
260
|
-
try {
|
|
261
|
-
return this.#performRequest(request);
|
|
262
|
-
} catch (error: unknown) {
|
|
263
|
-
if (error instanceof JsonRpcError) {
|
|
264
|
-
throw error;
|
|
265
|
-
}
|
|
266
|
-
if (error instanceof Error) {
|
|
267
|
-
throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, error.message);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, 'Internal error');
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
public on(event: string, listener: (...args: any[]) => void): void {
|
|
275
|
-
this.#providerEventEmitter.on(event, listener);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
public removeListener(
|
|
279
|
-
event: string,
|
|
280
|
-
listener: (...args: any[]) => void,
|
|
281
|
-
): void {
|
|
282
|
-
this.#providerEventEmitter.removeListener(event, listener);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { hashMessage, toHex, concat } from 'viem';
|
|
2
|
-
import { Identity } from '@0xsequence/wallet-wdk';
|
|
3
|
-
import { IdentityInstrument, IdTokenChallenge } from '@0xsequence/identity-instrument';
|
|
4
|
-
import { WalletError, WalletErrorType } from '../../errors';
|
|
5
|
-
import { User, decodeJwtPayload } from '@imtbl/auth';
|
|
6
|
-
import { Hex, Address } from 'ox';
|
|
7
|
-
import {
|
|
8
|
-
Payload,
|
|
9
|
-
Signature as SequenceSignature,
|
|
10
|
-
} from '@0xsequence/wallet-primitives';
|
|
11
|
-
import { SequenceSigner } from './types';
|
|
12
|
-
import { GetUserFunction } from '../../types';
|
|
13
|
-
|
|
14
|
-
interface IdTokenPayload {
|
|
15
|
-
iss: string;
|
|
16
|
-
aud: string;
|
|
17
|
-
sub: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface AuthKey {
|
|
21
|
-
address: string;
|
|
22
|
-
privateKey: CryptoKey;
|
|
23
|
-
identitySigner: string;
|
|
24
|
-
expiresAt: Date;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface UserWallet {
|
|
28
|
-
userIdentifier: string;
|
|
29
|
-
signerAddress: string;
|
|
30
|
-
authKey: AuthKey;
|
|
31
|
-
identityInstrument: IdentityInstrument;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface IdentityInstrumentSignerConfig {
|
|
35
|
-
/** Sequence Identity Instrument endpoint URL */
|
|
36
|
-
identityInstrumentEndpoint: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class IdentityInstrumentSigner implements SequenceSigner {
|
|
40
|
-
readonly #getUser: GetUserFunction;
|
|
41
|
-
|
|
42
|
-
readonly #config: IdentityInstrumentSignerConfig;
|
|
43
|
-
|
|
44
|
-
#userWallet: UserWallet | null = null;
|
|
45
|
-
|
|
46
|
-
#createWalletPromise: Promise<UserWallet> | null = null;
|
|
47
|
-
|
|
48
|
-
constructor(getUser: GetUserFunction, config: IdentityInstrumentSignerConfig) {
|
|
49
|
-
this.#getUser = getUser;
|
|
50
|
-
this.#config = config;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async #getUserOrThrow(): Promise<User> {
|
|
54
|
-
const user = await this.#getUser();
|
|
55
|
-
if (!user) {
|
|
56
|
-
throw new WalletError(
|
|
57
|
-
'User not authenticated',
|
|
58
|
-
WalletErrorType.NOT_LOGGED_IN_ERROR,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
return user;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async #getUserWallet(): Promise<UserWallet> {
|
|
65
|
-
let userWallet = this.#userWallet;
|
|
66
|
-
if (!userWallet) {
|
|
67
|
-
userWallet = await this.#createWallet();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const user = await this.#getUserOrThrow();
|
|
71
|
-
if (user.profile.sub !== userWallet.userIdentifier) {
|
|
72
|
-
userWallet = await this.#createWallet(user);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return userWallet;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async #createWallet(user?: User): Promise<UserWallet> {
|
|
79
|
-
if (this.#createWalletPromise) return this.#createWalletPromise;
|
|
80
|
-
|
|
81
|
-
this.#createWalletPromise = (async () => {
|
|
82
|
-
try {
|
|
83
|
-
this.#userWallet = null;
|
|
84
|
-
// Force refresh to get latest user data including idToken
|
|
85
|
-
await this.#getUser(true);
|
|
86
|
-
|
|
87
|
-
const authenticatedUser = user || await this.#getUserOrThrow();
|
|
88
|
-
|
|
89
|
-
if (!authenticatedUser.idToken) {
|
|
90
|
-
throw new WalletError(
|
|
91
|
-
'User idToken not available',
|
|
92
|
-
WalletErrorType.NOT_LOGGED_IN_ERROR,
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const { idToken } = authenticatedUser;
|
|
97
|
-
const decoded = decodeJwtPayload<IdTokenPayload>(idToken);
|
|
98
|
-
const issuer = decoded.iss;
|
|
99
|
-
const audience = decoded.aud;
|
|
100
|
-
|
|
101
|
-
const keyPair = await window.crypto.subtle.generateKey(
|
|
102
|
-
{ name: 'ECDSA', namedCurve: 'P-256' },
|
|
103
|
-
false,
|
|
104
|
-
['sign', 'verify'],
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
const publicKey = await window.crypto.subtle.exportKey('raw', keyPair.publicKey);
|
|
108
|
-
const authKey: AuthKey = {
|
|
109
|
-
address: Hex.fromBytes(new Uint8Array(publicKey)),
|
|
110
|
-
privateKey: keyPair.privateKey,
|
|
111
|
-
identitySigner: '',
|
|
112
|
-
expiresAt: new Date(Date.now() + 3600000),
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const identityInstrument = new IdentityInstrument(
|
|
116
|
-
this.#config.identityInstrumentEndpoint,
|
|
117
|
-
'@14:test',
|
|
118
|
-
);
|
|
119
|
-
const challenge = new IdTokenChallenge(issuer, audience, idToken);
|
|
120
|
-
|
|
121
|
-
await identityInstrument.commitVerifier(
|
|
122
|
-
Identity.toIdentityAuthKey(authKey),
|
|
123
|
-
challenge,
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
const result = await identityInstrument.completeAuth(
|
|
127
|
-
Identity.toIdentityAuthKey(authKey),
|
|
128
|
-
challenge,
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
const signerAddress = result.signer.address;
|
|
132
|
-
authKey.identitySigner = signerAddress;
|
|
133
|
-
|
|
134
|
-
this.#userWallet = {
|
|
135
|
-
userIdentifier: authenticatedUser.profile.sub,
|
|
136
|
-
signerAddress,
|
|
137
|
-
authKey,
|
|
138
|
-
identityInstrument,
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
return this.#userWallet;
|
|
142
|
-
} catch (error) {
|
|
143
|
-
const errorMessage = `Identity Instrument: Failed to create signer: ${(error as Error).message}`;
|
|
144
|
-
throw new WalletError(errorMessage, WalletErrorType.WALLET_CONNECTION_ERROR);
|
|
145
|
-
} finally {
|
|
146
|
-
this.#createWalletPromise = null;
|
|
147
|
-
}
|
|
148
|
-
})();
|
|
149
|
-
|
|
150
|
-
return this.#createWalletPromise;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async getAddress(): Promise<string> {
|
|
154
|
-
const wallet = await this.#getUserWallet();
|
|
155
|
-
return wallet.signerAddress;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async signPayload(
|
|
159
|
-
walletAddress: Address.Address,
|
|
160
|
-
chainId: number,
|
|
161
|
-
payload: Payload.Parented,
|
|
162
|
-
): Promise<SequenceSignature.SignatureOfSignerLeaf> {
|
|
163
|
-
const wallet = await this.#getUserWallet();
|
|
164
|
-
|
|
165
|
-
const signer = new Identity.IdentitySigner(
|
|
166
|
-
wallet.identityInstrument,
|
|
167
|
-
wallet.authKey,
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
return signer.sign(walletAddress, chainId, payload);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async signMessage(message: string | Uint8Array): Promise<string> {
|
|
174
|
-
const wallet = await this.#getUserWallet();
|
|
175
|
-
|
|
176
|
-
const signer = new Identity.IdentitySigner(
|
|
177
|
-
wallet.identityInstrument,
|
|
178
|
-
wallet.authKey,
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
const messageBytes = typeof message === 'string'
|
|
182
|
-
? new TextEncoder().encode(message)
|
|
183
|
-
: message;
|
|
184
|
-
const digest = hashMessage({ raw: messageBytes });
|
|
185
|
-
|
|
186
|
-
const signature = await signer.signDigest(Hex.toBytes(digest));
|
|
187
|
-
|
|
188
|
-
// Format signature: r (32 bytes) + s (32 bytes) + v (1 byte)
|
|
189
|
-
const r = toHex(signature.r, { size: 32 });
|
|
190
|
-
const s = toHex(signature.s, { size: 32 });
|
|
191
|
-
const v = toHex(signature.yParity + 27, { size: 1 });
|
|
192
|
-
|
|
193
|
-
return concat([r, s, v]);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { SequenceSigner } from './types';
|
|
2
|
-
import { IdentityInstrumentSigner } from './identityInstrumentSigner';
|
|
3
|
-
import { PrivateKeySigner } from './privateKeySigner';
|
|
4
|
-
import { GetUserFunction } from '../../types';
|
|
5
|
-
|
|
6
|
-
export type { SequenceSigner } from './types';
|
|
7
|
-
export { IdentityInstrumentSigner } from './identityInstrumentSigner';
|
|
8
|
-
export type { IdentityInstrumentSignerConfig } from './identityInstrumentSigner';
|
|
9
|
-
export { PrivateKeySigner } from './privateKeySigner';
|
|
10
|
-
|
|
11
|
-
export interface CreateSequenceSignerConfig {
|
|
12
|
-
/** Identity Instrument endpoint (required for prod/sandbox) */
|
|
13
|
-
identityInstrumentEndpoint?: string;
|
|
14
|
-
/** Whether this is a dev environment (uses PrivateKeySigner instead of IdentityInstrumentSigner) */
|
|
15
|
-
isDevEnvironment?: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Create the appropriate signer based on environment.
|
|
20
|
-
* - Dev environment (behind VPN): uses PrivateKeySigner
|
|
21
|
-
* - Prod/Sandbox: uses IdentityInstrumentSigner
|
|
22
|
-
*
|
|
23
|
-
* @param getUser - Function to get the current user
|
|
24
|
-
* @param config - Signer configuration
|
|
25
|
-
*/
|
|
26
|
-
export function createSequenceSigner(
|
|
27
|
-
getUser: GetUserFunction,
|
|
28
|
-
config: CreateSequenceSignerConfig = {},
|
|
29
|
-
): SequenceSigner {
|
|
30
|
-
if (config.isDevEnvironment) {
|
|
31
|
-
return new PrivateKeySigner(getUser);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (!config.identityInstrumentEndpoint) {
|
|
35
|
-
throw new Error('identityInstrumentEndpoint is required for non-dev environments');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return new IdentityInstrumentSigner(getUser, {
|
|
39
|
-
identityInstrumentEndpoint: config.identityInstrumentEndpoint,
|
|
40
|
-
});
|
|
41
|
-
}
|