@imtbl/wallet 2.12.6 → 2.12.7-alpha.1
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 +688 -0
- package/dist/browser/index.js +47 -47
- package/dist/node/index.cjs +65 -69
- package/dist/node/index.js +50 -50
- package/dist/types/connectWallet.d.ts +13 -19
- package/dist/types/guardian/index.d.ts +10 -5
- package/dist/types/magic/magicTEESigner.d.ts +3 -4
- package/dist/types/sequence/sequenceProvider.d.ts +4 -4
- package/dist/types/sequence/signer/identityInstrumentSigner.d.ts +2 -2
- package/dist/types/sequence/signer/index.d.ts +5 -4
- package/dist/types/sequence/signer/privateKeySigner.d.ts +2 -2
- package/dist/types/sequence/user/registerUser.d.ts +3 -3
- package/dist/types/types.d.ts +39 -9
- package/dist/types/zkEvm/relayerClient.d.ts +8 -4
- package/dist/types/zkEvm/user/registerZkEvmUser.d.ts +7 -4
- package/dist/types/zkEvm/zkEvmProvider.d.ts +13 -5
- package/package.json +7 -7
- package/src/connectWallet.test.ts +16 -22
- package/src/connectWallet.ts +79 -53
- package/src/guardian/index.ts +40 -25
- package/src/magic/magicTEESigner.ts +8 -6
- package/src/sequence/sequenceProvider.ts +17 -9
- package/src/sequence/signer/identityInstrumentSigner.ts +8 -6
- package/src/sequence/signer/index.ts +8 -12
- package/src/sequence/signer/privateKeySigner.ts +6 -5
- package/src/sequence/user/registerUser.ts +5 -4
- package/src/types.ts +44 -12
- package/src/zkEvm/relayerClient.ts +22 -6
- package/src/zkEvm/user/registerZkEvmUser.ts +16 -5
- package/src/zkEvm/zkEvmProvider.ts +56 -29
package/src/connectWallet.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Auth,
|
|
3
|
-
IAuthConfiguration,
|
|
4
3
|
TypedEventEmitter,
|
|
5
4
|
} from '@imtbl/auth';
|
|
6
5
|
import {
|
|
@@ -12,7 +11,7 @@ import {
|
|
|
12
11
|
import { ZkEvmProvider } from './zkEvm/zkEvmProvider';
|
|
13
12
|
import { SequenceProvider } from './sequence/sequenceProvider';
|
|
14
13
|
import {
|
|
15
|
-
ConnectWalletOptions, PassportEventMap, ChainConfig, Provider,
|
|
14
|
+
ConnectWalletOptions, PassportEventMap, ChainConfig, Provider, GetUserFunction,
|
|
16
15
|
} from './types';
|
|
17
16
|
import { WalletConfiguration } from './config';
|
|
18
17
|
import GuardianClient from './guardian';
|
|
@@ -121,13 +120,22 @@ function getDefaultClientId(chain: ChainConfig): string {
|
|
|
121
120
|
return isSandboxChain(chain) ? DEFAULT_SANDBOX_CLIENT_ID : DEFAULT_PRODUCTION_CLIENT_ID;
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Create a default getUser function using internal Auth instance.
|
|
125
|
+
* This is used when no external getUser is provided.
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
function createDefaultGetUser(initialChain: ChainConfig, options: ConnectWalletOptions): {
|
|
129
|
+
getUser: GetUserFunction;
|
|
130
|
+
clientId: string;
|
|
131
|
+
} {
|
|
125
132
|
const passportDomain = derivePassportDomain(initialChain);
|
|
126
133
|
const authenticationDomain = deriveAuthenticationDomain();
|
|
127
134
|
const redirectUri = deriveRedirectUri();
|
|
135
|
+
const clientId = options.clientId || getDefaultClientId(initialChain);
|
|
128
136
|
|
|
129
|
-
|
|
130
|
-
clientId
|
|
137
|
+
const auth = new Auth({
|
|
138
|
+
clientId,
|
|
131
139
|
redirectUri,
|
|
132
140
|
popupRedirectUri: redirectUri,
|
|
133
141
|
logoutRedirectUri: redirectUri,
|
|
@@ -138,6 +146,29 @@ function createDefaultAuth(initialChain: ChainConfig, options: ConnectWalletOpti
|
|
|
138
146
|
popupOverlayOptions: options.popupOverlayOptions,
|
|
139
147
|
crossSdkBridgeEnabled: options.crossSdkBridgeEnabled,
|
|
140
148
|
});
|
|
149
|
+
|
|
150
|
+
// Set up message listener for popup login callback
|
|
151
|
+
if (typeof window !== 'undefined') {
|
|
152
|
+
window.addEventListener('message', async (event) => {
|
|
153
|
+
if (event.data.code && event.data.state) {
|
|
154
|
+
const currentQueryString = window.location.search;
|
|
155
|
+
const newQueryString = new URLSearchParams(currentQueryString);
|
|
156
|
+
newQueryString.set('code', event.data.code);
|
|
157
|
+
newQueryString.set('state', event.data.state);
|
|
158
|
+
window.history.replaceState(null, '', `?${newQueryString.toString()}`);
|
|
159
|
+
await auth.loginCallback();
|
|
160
|
+
newQueryString.delete('code');
|
|
161
|
+
newQueryString.delete('state');
|
|
162
|
+
window.history.replaceState(null, '', `?${newQueryString.toString()}`);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Return getUser function that wraps Auth.getUserOrLogin
|
|
168
|
+
return {
|
|
169
|
+
getUser: async () => auth.getUserOrLogin(),
|
|
170
|
+
clientId,
|
|
171
|
+
};
|
|
141
172
|
}
|
|
142
173
|
|
|
143
174
|
/**
|
|
@@ -146,30 +177,24 @@ function createDefaultAuth(initialChain: ChainConfig, options: ConnectWalletOpti
|
|
|
146
177
|
* @param config - Wallet configuration
|
|
147
178
|
* @returns EIP-1193 compliant provider with multi-chain support
|
|
148
179
|
*
|
|
149
|
-
* If
|
|
180
|
+
* If getUser is not provided, a default implementation using @imtbl/auth will be created.
|
|
150
181
|
*
|
|
151
|
-
* @example
|
|
182
|
+
* @example Using external auth (e.g., NextAuth)
|
|
152
183
|
* ```typescript
|
|
153
|
-
* import {
|
|
154
|
-
* import {
|
|
184
|
+
* import { connectWallet } from '@imtbl/wallet';
|
|
185
|
+
* import { useImmutableSession } from '@imtbl/auth-next-client';
|
|
155
186
|
*
|
|
156
|
-
*
|
|
157
|
-
* const
|
|
158
|
-
*
|
|
159
|
-
* passportDomain: 'https://passport.immutable.com',
|
|
160
|
-
* clientId: 'your-client-id',
|
|
161
|
-
* redirectUri: 'https://your-app.com/callback',
|
|
162
|
-
* scope: 'openid profile email offline_access transact',
|
|
163
|
-
* });
|
|
187
|
+
* const { getUser } = useImmutableSession();
|
|
188
|
+
* const provider = await connectWallet({ getUser });
|
|
189
|
+
* ```
|
|
164
190
|
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
191
|
+
* @example Using default auth (simplest setup)
|
|
192
|
+
* ```typescript
|
|
193
|
+
* import { connectWallet } from '@imtbl/wallet';
|
|
167
194
|
*
|
|
168
|
-
* //
|
|
169
|
-
* const provider = await connectWallet(
|
|
170
|
-
*
|
|
171
|
-
* chains: [IMMUTABLE_ZKEVM_MAINNET_CHAIN],
|
|
172
|
-
* });
|
|
195
|
+
* // Uses default Immutable-hosted authentication
|
|
196
|
+
* const provider = await connectWallet();
|
|
197
|
+
* const accounts = await provider.request({ method: 'eth_requestAccounts' });
|
|
173
198
|
* ```
|
|
174
199
|
*/
|
|
175
200
|
export async function connectWallet(
|
|
@@ -198,33 +223,28 @@ export async function connectWallet(
|
|
|
198
223
|
passport: apiConfig,
|
|
199
224
|
});
|
|
200
225
|
|
|
201
|
-
// 3. Resolve
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
newQueryString.delete('code');
|
|
215
|
-
newQueryString.delete('state');
|
|
216
|
-
window.history.replaceState(null, '', `?${newQueryString.toString()}`);
|
|
217
|
-
}
|
|
218
|
-
});
|
|
226
|
+
// 3. Resolve getUser function
|
|
227
|
+
// If not provided, create a default implementation using internal Auth
|
|
228
|
+
let getUser: GetUserFunction;
|
|
229
|
+
let clientId: string;
|
|
230
|
+
|
|
231
|
+
if (config.getUser) {
|
|
232
|
+
getUser = config.getUser;
|
|
233
|
+
clientId = config.clientId || getDefaultClientId(initialChain);
|
|
234
|
+
} else {
|
|
235
|
+
// Create default getUser using internal Auth
|
|
236
|
+
const defaultAuth = createDefaultGetUser(initialChain, config);
|
|
237
|
+
getUser = defaultAuth.getUser;
|
|
238
|
+
clientId = defaultAuth.clientId;
|
|
219
239
|
}
|
|
220
240
|
|
|
221
|
-
// 4.
|
|
222
|
-
const
|
|
223
|
-
const user = await auth.getUser();
|
|
241
|
+
// 4. Get current user (may be null if not logged in)
|
|
242
|
+
const user = await getUser().catch(() => null);
|
|
224
243
|
|
|
225
244
|
// 5. Create wallet configuration with concrete URLs
|
|
245
|
+
const passportDomain = initialChain.passportDomain || initialChain.apiUrl.replace('api.', 'passport.');
|
|
226
246
|
const walletConfig = new WalletConfiguration({
|
|
227
|
-
passportDomain
|
|
247
|
+
passportDomain,
|
|
228
248
|
zkEvmRpcUrl: initialChain.rpcUrl,
|
|
229
249
|
relayerUrl: initialChain.relayerUrl,
|
|
230
250
|
indexerMrBasePath: initialChain.apiUrl,
|
|
@@ -242,9 +262,10 @@ export async function connectWallet(
|
|
|
242
262
|
|
|
243
263
|
const guardianClient = new GuardianClient({
|
|
244
264
|
config: walletConfig,
|
|
245
|
-
|
|
265
|
+
getUser,
|
|
246
266
|
guardianApi,
|
|
247
|
-
|
|
267
|
+
passportDomain,
|
|
268
|
+
clientId,
|
|
248
269
|
});
|
|
249
270
|
|
|
250
271
|
// 8. Create provider based on chain type
|
|
@@ -262,7 +283,7 @@ export async function connectWallet(
|
|
|
262
283
|
magicPublishableApiKey: magicConfig.magicPublishableApiKey,
|
|
263
284
|
magicProviderId: magicConfig.magicProviderId,
|
|
264
285
|
});
|
|
265
|
-
const ethSigner = new MagicTEESigner(
|
|
286
|
+
const ethSigner = new MagicTEESigner(getUser, magicTeeApiClients);
|
|
266
287
|
|
|
267
288
|
// 11. Determine session activity API URL (only for mainnet, testnet, devnet)
|
|
268
289
|
let sessionActivityApiUrl: string | null = null;
|
|
@@ -280,24 +301,29 @@ export async function connectWallet(
|
|
|
280
301
|
|
|
281
302
|
// 12. Create ZkEvmProvider
|
|
282
303
|
provider = new ZkEvmProvider({
|
|
283
|
-
|
|
304
|
+
getUser,
|
|
305
|
+
clientId,
|
|
284
306
|
config: walletConfig,
|
|
285
307
|
multiRollupApiClients,
|
|
286
|
-
passportEventEmitter,
|
|
308
|
+
walletEventEmitter: passportEventEmitter,
|
|
287
309
|
guardianClient,
|
|
288
310
|
ethSigner,
|
|
289
311
|
user,
|
|
290
312
|
sessionActivityApiUrl,
|
|
291
313
|
});
|
|
292
314
|
} else {
|
|
315
|
+
// Determine if this is a dev environment based on domain
|
|
316
|
+
const isDevEnvironment = passportDomain.includes('.dev.') || initialChain.apiUrl.includes('.dev.');
|
|
317
|
+
|
|
293
318
|
// Create Sequence signer based on environment
|
|
294
|
-
const sequenceSigner = createSequenceSigner(
|
|
319
|
+
const sequenceSigner = createSequenceSigner(getUser, {
|
|
295
320
|
identityInstrumentEndpoint: initialChain.sequenceIdentityInstrumentEndpoint,
|
|
321
|
+
isDevEnvironment,
|
|
296
322
|
});
|
|
297
323
|
|
|
298
324
|
// Non-zkEVM chain - use SequenceProvider
|
|
299
325
|
provider = new SequenceProvider({
|
|
300
|
-
|
|
326
|
+
getUser,
|
|
301
327
|
chainConfig: initialChain,
|
|
302
328
|
multiRollupApiClients,
|
|
303
329
|
guardianClient,
|
package/src/guardian/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as GeneratedClients from '@imtbl/generated-clients';
|
|
2
2
|
import { zeroAddress } from 'viem';
|
|
3
|
-
import {
|
|
3
|
+
import { isUserZkEvm, type UserZkEvm } from '@imtbl/auth';
|
|
4
4
|
import ConfirmationScreen from '../confirmation/confirmation';
|
|
5
5
|
import { JsonRpcError, ProviderErrorCode, RpcErrorCode } from '../zkEvm/JsonRpcError';
|
|
6
6
|
import { MetaTransaction, TypedDataPayload } from '../zkEvm/types';
|
|
@@ -8,12 +8,14 @@ import { WalletConfiguration } from '../config';
|
|
|
8
8
|
import { getEip155ChainId } from '../zkEvm/walletHelpers';
|
|
9
9
|
import { WalletError, WalletErrorType } from '../errors';
|
|
10
10
|
import { isAxiosError } from '../utils/http';
|
|
11
|
+
import type { GetUserFunction } from '../types';
|
|
11
12
|
|
|
12
13
|
export type GuardianClientParams = {
|
|
13
14
|
config: WalletConfiguration;
|
|
14
|
-
|
|
15
|
+
getUser: GetUserFunction;
|
|
15
16
|
guardianApi: GeneratedClients.mr.GuardianApi;
|
|
16
|
-
|
|
17
|
+
passportDomain: string;
|
|
18
|
+
clientId: string;
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
type GuardianEVMTxnEvaluationParams = {
|
|
@@ -68,15 +70,40 @@ export default class GuardianClient {
|
|
|
68
70
|
|
|
69
71
|
private readonly crossSdkBridgeEnabled: boolean;
|
|
70
72
|
|
|
71
|
-
private readonly
|
|
73
|
+
private readonly getUser: GetUserFunction;
|
|
72
74
|
|
|
73
75
|
constructor({
|
|
74
|
-
config,
|
|
76
|
+
config, getUser, guardianApi, passportDomain, clientId,
|
|
75
77
|
}: GuardianClientParams) {
|
|
76
|
-
|
|
78
|
+
// Create a minimal auth config for ConfirmationScreen
|
|
79
|
+
this.confirmationScreen = new ConfirmationScreen({
|
|
80
|
+
authenticationDomain: 'https://auth.immutable.com',
|
|
81
|
+
passportDomain,
|
|
82
|
+
oidcConfiguration: {
|
|
83
|
+
clientId,
|
|
84
|
+
redirectUri: 'https://auth.immutable.com/im-logged-in',
|
|
85
|
+
},
|
|
86
|
+
crossSdkBridgeEnabled: config.crossSdkBridgeEnabled,
|
|
87
|
+
});
|
|
77
88
|
this.crossSdkBridgeEnabled = config.crossSdkBridgeEnabled;
|
|
78
89
|
this.guardianApi = guardianApi;
|
|
79
|
-
this.
|
|
90
|
+
this.getUser = getUser;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get zkEvm user using getUser function.
|
|
95
|
+
*/
|
|
96
|
+
private async getUserZkEvm(): Promise<UserZkEvm> {
|
|
97
|
+
const user = await this.getUser();
|
|
98
|
+
|
|
99
|
+
if (!user || !isUserZkEvm(user)) {
|
|
100
|
+
throw new JsonRpcError(
|
|
101
|
+
ProviderErrorCode.UNAUTHORIZED,
|
|
102
|
+
'User not authenticated or missing zkEvm data',
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return user;
|
|
80
107
|
}
|
|
81
108
|
|
|
82
109
|
/**
|
|
@@ -120,7 +147,7 @@ export default class GuardianClient {
|
|
|
120
147
|
nonce,
|
|
121
148
|
metaTransactions,
|
|
122
149
|
}: GuardianEVMTxnEvaluationParams): Promise<GeneratedClients.mr.TransactionEvaluationResponse> {
|
|
123
|
-
const user = await this.
|
|
150
|
+
const user = await this.getUserZkEvm();
|
|
124
151
|
const headers = { Authorization: `Bearer ${user.accessToken}` };
|
|
125
152
|
const guardianTransactions = transformGuardianTransactions(metaTransactions);
|
|
126
153
|
try {
|
|
@@ -175,7 +202,7 @@ export default class GuardianClient {
|
|
|
175
202
|
}
|
|
176
203
|
|
|
177
204
|
if (confirmationRequired && !!transactionId) {
|
|
178
|
-
const user = await this.
|
|
205
|
+
const user = await this.getUserZkEvm();
|
|
179
206
|
const confirmationResult = await this.confirmationScreen.requestConfirmation(
|
|
180
207
|
transactionId,
|
|
181
208
|
user.zkEvm.ethAddress,
|
|
@@ -200,13 +227,7 @@ export default class GuardianClient {
|
|
|
200
227
|
{ chainID, payload }: GuardianEIP712MessageEvaluationParams,
|
|
201
228
|
): Promise<GeneratedClients.mr.MessageEvaluationResponse> {
|
|
202
229
|
try {
|
|
203
|
-
const user = await this.
|
|
204
|
-
if (user === null) {
|
|
205
|
-
throw new JsonRpcError(
|
|
206
|
-
ProviderErrorCode.UNAUTHORIZED,
|
|
207
|
-
'User not logged in. Please log in first.',
|
|
208
|
-
);
|
|
209
|
-
}
|
|
230
|
+
const user = await this.getUserZkEvm();
|
|
210
231
|
const messageEvalResponse = await this.guardianApi.evaluateMessage(
|
|
211
232
|
{ messageEvaluationRequest: { chainID, payload } },
|
|
212
233
|
{ headers: { Authorization: `Bearer ${user.accessToken}` } },
|
|
@@ -227,7 +248,7 @@ export default class GuardianClient {
|
|
|
227
248
|
throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, transactionRejectedCrossSdkBridgeError);
|
|
228
249
|
}
|
|
229
250
|
if (confirmationRequired && !!messageId) {
|
|
230
|
-
const user = await this.
|
|
251
|
+
const user = await this.getUserZkEvm();
|
|
231
252
|
const confirmationResult = await this.confirmationScreen.requestMessageConfirmation(
|
|
232
253
|
messageId,
|
|
233
254
|
user.zkEvm.ethAddress,
|
|
@@ -250,13 +271,7 @@ export default class GuardianClient {
|
|
|
250
271
|
{ chainID, payload }: GuardianERC191MessageEvaluationParams,
|
|
251
272
|
): Promise<GeneratedClients.mr.MessageEvaluationResponse> {
|
|
252
273
|
try {
|
|
253
|
-
const user = await this.
|
|
254
|
-
if (user === null) {
|
|
255
|
-
throw new JsonRpcError(
|
|
256
|
-
ProviderErrorCode.UNAUTHORIZED,
|
|
257
|
-
'User not logged in. Please log in first.',
|
|
258
|
-
);
|
|
259
|
-
}
|
|
274
|
+
const user = await this.getUserZkEvm();
|
|
260
275
|
const messageEvalResponse = await this.guardianApi.evaluateErc191Message(
|
|
261
276
|
{
|
|
262
277
|
eRC191MessageEvaluationRequest: {
|
|
@@ -282,7 +297,7 @@ export default class GuardianClient {
|
|
|
282
297
|
throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, transactionRejectedCrossSdkBridgeError);
|
|
283
298
|
}
|
|
284
299
|
if (confirmationRequired && !!messageId) {
|
|
285
|
-
const user = await this.
|
|
300
|
+
const user = await this.getUserZkEvm();
|
|
286
301
|
const confirmationResult = await this.confirmationScreen.requestMessageConfirmation(
|
|
287
302
|
messageId,
|
|
288
303
|
user.zkEvm.ethAddress,
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import { MagicTeeApiClients } from '@imtbl/generated-clients';
|
|
3
3
|
import { Flow, trackDuration } from '@imtbl/metrics';
|
|
4
4
|
import { WalletError, WalletErrorType } from '../errors';
|
|
5
|
-
import { Auth } from '@imtbl/auth';
|
|
6
5
|
import { withMetricsAsync } from '../utils/metrics';
|
|
7
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
isUserZkEvm, User, WalletSigner, GetUserFunction,
|
|
8
|
+
} from '../types';
|
|
8
9
|
import { isAxiosError } from '../utils/http';
|
|
9
10
|
|
|
10
11
|
const CHAIN_IDENTIFIER = 'ETH';
|
|
@@ -62,7 +63,7 @@ const toBase64 = (value: string): string => {
|
|
|
62
63
|
* This signer delegates cryptographic operations to the Magic TEE service.
|
|
63
64
|
*/
|
|
64
65
|
export default class MagicTEESigner implements WalletSigner {
|
|
65
|
-
private readonly
|
|
66
|
+
private readonly getUser: GetUserFunction;
|
|
66
67
|
|
|
67
68
|
private readonly magicTeeApiClient: MagicTeeApiClients;
|
|
68
69
|
|
|
@@ -70,8 +71,8 @@ export default class MagicTEESigner implements WalletSigner {
|
|
|
70
71
|
|
|
71
72
|
private createWalletPromise: Promise<UserWallet> | null = null;
|
|
72
73
|
|
|
73
|
-
constructor(
|
|
74
|
-
this.
|
|
74
|
+
constructor(getUser: GetUserFunction, magicTeeApiClient: MagicTeeApiClients) {
|
|
75
|
+
this.getUser = getUser;
|
|
75
76
|
this.magicTeeApiClient = magicTeeApiClient;
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -164,7 +165,8 @@ export default class MagicTEESigner implements WalletSigner {
|
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
private async getUserOrThrow(): Promise<User> {
|
|
167
|
-
const user = await this.
|
|
168
|
+
const user = await this.getUser();
|
|
169
|
+
|
|
168
170
|
if (!user) {
|
|
169
171
|
throw new WalletError(
|
|
170
172
|
'User has been logged out',
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
type PublicClient,
|
|
6
6
|
} from 'viem';
|
|
7
7
|
import { MultiRollupApiClients } from '@imtbl/generated-clients';
|
|
8
|
-
import {
|
|
8
|
+
import { TypedEventEmitter, User } from '@imtbl/auth';
|
|
9
9
|
import { trackFlow, trackError, identify } from '@imtbl/metrics';
|
|
10
10
|
import {
|
|
11
11
|
Provider,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
RequestArguments,
|
|
15
15
|
ChainConfig,
|
|
16
16
|
EvmChain,
|
|
17
|
+
GetUserFunction,
|
|
17
18
|
} from '../types';
|
|
18
19
|
import { JsonRpcError, ProviderErrorCode, RpcErrorCode } from '../zkEvm/JsonRpcError';
|
|
19
20
|
import GuardianClient from '../guardian';
|
|
@@ -22,7 +23,7 @@ import { registerUser } from './user';
|
|
|
22
23
|
import { getEvmChainFromChainId } from '../network/chainRegistry';
|
|
23
24
|
|
|
24
25
|
export type SequenceProviderInput = {
|
|
25
|
-
|
|
26
|
+
getUser: GetUserFunction;
|
|
26
27
|
chainConfig: ChainConfig;
|
|
27
28
|
multiRollupApiClients: MultiRollupApiClients;
|
|
28
29
|
guardianClient: GuardianClient;
|
|
@@ -50,7 +51,7 @@ function getUserChainAddress(user: User, chain: SequenceChain): string | undefin
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export class SequenceProvider implements Provider {
|
|
53
|
-
readonly #
|
|
54
|
+
readonly #getUser: GetUserFunction;
|
|
54
55
|
|
|
55
56
|
readonly #chainConfig: ChainConfig;
|
|
56
57
|
|
|
@@ -69,7 +70,7 @@ export class SequenceProvider implements Provider {
|
|
|
69
70
|
public readonly isPassport: boolean = true;
|
|
70
71
|
|
|
71
72
|
constructor({
|
|
72
|
-
|
|
73
|
+
getUser,
|
|
73
74
|
chainConfig,
|
|
74
75
|
multiRollupApiClients,
|
|
75
76
|
guardianClient,
|
|
@@ -83,7 +84,7 @@ export class SequenceProvider implements Provider {
|
|
|
83
84
|
}
|
|
84
85
|
this.#evmChain = evmChain;
|
|
85
86
|
|
|
86
|
-
this.#
|
|
87
|
+
this.#getUser = getUser;
|
|
87
88
|
this.#chainConfig = chainConfig;
|
|
88
89
|
this.#multiRollupApiClients = multiRollupApiClients;
|
|
89
90
|
this.#guardianClient = guardianClient;
|
|
@@ -100,7 +101,7 @@ export class SequenceProvider implements Provider {
|
|
|
100
101
|
* Get the user's address for this chain if already registered.
|
|
101
102
|
*/
|
|
102
103
|
async #getChainAddress(): Promise<string | undefined> {
|
|
103
|
-
const user = await this.#
|
|
104
|
+
const user = await this.#getUser();
|
|
104
105
|
if (user && isUserRegisteredForChain(user, this.#evmChain)) {
|
|
105
106
|
return getUserChainAddress(user, this.#evmChain);
|
|
106
107
|
}
|
|
@@ -117,8 +118,15 @@ export class SequenceProvider implements Provider {
|
|
|
117
118
|
const flow = trackFlow('passport', 'ethRequestAccounts');
|
|
118
119
|
|
|
119
120
|
try {
|
|
120
|
-
const user = await this.#
|
|
121
|
-
flow.addEvent('
|
|
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
|
+
}
|
|
122
130
|
|
|
123
131
|
let userEthAddress: string | undefined;
|
|
124
132
|
|
|
@@ -126,7 +134,7 @@ export class SequenceProvider implements Provider {
|
|
|
126
134
|
flow.addEvent('startUserRegistration');
|
|
127
135
|
|
|
128
136
|
userEthAddress = await registerUser({
|
|
129
|
-
|
|
137
|
+
getUser: this.#getUser,
|
|
130
138
|
ethSigner: this.#ethSigner,
|
|
131
139
|
multiRollupApiClients: this.#multiRollupApiClients,
|
|
132
140
|
accessToken: user.accessToken,
|
|
@@ -2,13 +2,14 @@ import { hashMessage, toHex, concat } from 'viem';
|
|
|
2
2
|
import { Identity } from '@0xsequence/wallet-wdk';
|
|
3
3
|
import { IdentityInstrument, IdTokenChallenge } from '@0xsequence/identity-instrument';
|
|
4
4
|
import { WalletError, WalletErrorType } from '../../errors';
|
|
5
|
-
import {
|
|
5
|
+
import { User, decodeJwtPayload } from '@imtbl/auth';
|
|
6
6
|
import { Hex, Address } from 'ox';
|
|
7
7
|
import {
|
|
8
8
|
Payload,
|
|
9
9
|
Signature as SequenceSignature,
|
|
10
10
|
} from '@0xsequence/wallet-primitives';
|
|
11
11
|
import { SequenceSigner } from './types';
|
|
12
|
+
import { GetUserFunction } from '../../types';
|
|
12
13
|
|
|
13
14
|
interface IdTokenPayload {
|
|
14
15
|
iss: string;
|
|
@@ -36,7 +37,7 @@ export interface IdentityInstrumentSignerConfig {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export class IdentityInstrumentSigner implements SequenceSigner {
|
|
39
|
-
readonly #
|
|
40
|
+
readonly #getUser: GetUserFunction;
|
|
40
41
|
|
|
41
42
|
readonly #config: IdentityInstrumentSignerConfig;
|
|
42
43
|
|
|
@@ -44,13 +45,13 @@ export class IdentityInstrumentSigner implements SequenceSigner {
|
|
|
44
45
|
|
|
45
46
|
#createWalletPromise: Promise<UserWallet> | null = null;
|
|
46
47
|
|
|
47
|
-
constructor(
|
|
48
|
-
this.#
|
|
48
|
+
constructor(getUser: GetUserFunction, config: IdentityInstrumentSignerConfig) {
|
|
49
|
+
this.#getUser = getUser;
|
|
49
50
|
this.#config = config;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
async #getUserOrThrow(): Promise<User> {
|
|
53
|
-
const user = await this.#
|
|
54
|
+
const user = await this.#getUser();
|
|
54
55
|
if (!user) {
|
|
55
56
|
throw new WalletError(
|
|
56
57
|
'User not authenticated',
|
|
@@ -80,7 +81,8 @@ export class IdentityInstrumentSigner implements SequenceSigner {
|
|
|
80
81
|
this.#createWalletPromise = (async () => {
|
|
81
82
|
try {
|
|
82
83
|
this.#userWallet = null;
|
|
83
|
-
|
|
84
|
+
// Force refresh to get latest user data including idToken
|
|
85
|
+
await this.#getUser(true);
|
|
84
86
|
|
|
85
87
|
const authenticatedUser = user || await this.#getUserOrThrow();
|
|
86
88
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { Auth, IAuthConfiguration } from '@imtbl/auth';
|
|
2
1
|
import { SequenceSigner } from './types';
|
|
3
2
|
import { IdentityInstrumentSigner } from './identityInstrumentSigner';
|
|
4
3
|
import { PrivateKeySigner } from './privateKeySigner';
|
|
4
|
+
import { GetUserFunction } from '../../types';
|
|
5
5
|
|
|
6
6
|
export type { SequenceSigner } from './types';
|
|
7
7
|
export { IdentityInstrumentSigner } from './identityInstrumentSigner';
|
|
8
8
|
export type { IdentityInstrumentSignerConfig } from './identityInstrumentSigner';
|
|
9
9
|
export { PrivateKeySigner } from './privateKeySigner';
|
|
10
10
|
|
|
11
|
-
const DEV_AUTH_DOMAIN = 'https://auth.dev.immutable.com';
|
|
12
|
-
|
|
13
11
|
export interface CreateSequenceSignerConfig {
|
|
14
12
|
/** Identity Instrument endpoint (required for prod/sandbox) */
|
|
15
13
|
identityInstrumentEndpoint?: string;
|
|
14
|
+
/** Whether this is a dev environment (uses PrivateKeySigner instead of IdentityInstrumentSigner) */
|
|
15
|
+
isDevEnvironment?: boolean;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -20,26 +20,22 @@ export interface CreateSequenceSignerConfig {
|
|
|
20
20
|
* - Dev environment (behind VPN): uses PrivateKeySigner
|
|
21
21
|
* - Prod/Sandbox: uses IdentityInstrumentSigner
|
|
22
22
|
*
|
|
23
|
-
* @param
|
|
24
|
-
* @param authConfig - Auth configuration (to determine environment)
|
|
23
|
+
* @param getUser - Function to get the current user
|
|
25
24
|
* @param config - Signer configuration
|
|
26
25
|
*/
|
|
27
26
|
export function createSequenceSigner(
|
|
28
|
-
|
|
29
|
-
authConfig: IAuthConfiguration,
|
|
27
|
+
getUser: GetUserFunction,
|
|
30
28
|
config: CreateSequenceSignerConfig = {},
|
|
31
29
|
): SequenceSigner {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (isDevEnvironment) {
|
|
35
|
-
return new PrivateKeySigner(auth);
|
|
30
|
+
if (config.isDevEnvironment) {
|
|
31
|
+
return new PrivateKeySigner(getUser);
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
if (!config.identityInstrumentEndpoint) {
|
|
39
35
|
throw new Error('identityInstrumentEndpoint is required for non-dev environments');
|
|
40
36
|
}
|
|
41
37
|
|
|
42
|
-
return new IdentityInstrumentSigner(
|
|
38
|
+
return new IdentityInstrumentSigner(getUser, {
|
|
43
39
|
identityInstrumentEndpoint: config.identityInstrumentEndpoint,
|
|
44
40
|
});
|
|
45
41
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { keccak256, toBytes } from 'viem';
|
|
2
2
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
3
3
|
import { WalletError, WalletErrorType } from '../../errors';
|
|
4
|
-
import {
|
|
4
|
+
import { User } from '@imtbl/auth';
|
|
5
5
|
import { Signers } from '@0xsequence/wallet-core';
|
|
6
6
|
import {
|
|
7
7
|
Payload,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from '@0xsequence/wallet-primitives';
|
|
10
10
|
import { SequenceSigner } from './types';
|
|
11
11
|
import { Address } from 'ox';
|
|
12
|
+
import { GetUserFunction } from '../../types';
|
|
12
13
|
|
|
13
14
|
interface PrivateKeyWallet {
|
|
14
15
|
userIdentifier: string;
|
|
@@ -22,18 +23,18 @@ interface PrivateKeyWallet {
|
|
|
22
23
|
* Uses a deterministic private key derived from the user's sub.
|
|
23
24
|
*/
|
|
24
25
|
export class PrivateKeySigner implements SequenceSigner {
|
|
25
|
-
readonly #
|
|
26
|
+
readonly #getUser: GetUserFunction;
|
|
26
27
|
|
|
27
28
|
#privateKeyWallet: PrivateKeyWallet | null = null;
|
|
28
29
|
|
|
29
30
|
#createWalletPromise: Promise<PrivateKeyWallet> | null = null;
|
|
30
31
|
|
|
31
|
-
constructor(
|
|
32
|
-
this.#
|
|
32
|
+
constructor(getUser: GetUserFunction) {
|
|
33
|
+
this.#getUser = getUser;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
async #getUserOrThrow(): Promise<User> {
|
|
36
|
-
const user = await this.#
|
|
37
|
+
const user = await this.#getUser();
|
|
37
38
|
if (!user) {
|
|
38
39
|
throw new WalletError('User not authenticated', WalletErrorType.NOT_LOGGED_IN_ERROR);
|
|
39
40
|
}
|
|
@@ -2,12 +2,12 @@ import { MultiRollupApiClients } from '@imtbl/generated-clients';
|
|
|
2
2
|
import { Flow } from '@imtbl/metrics';
|
|
3
3
|
import type { PublicClient } from 'viem';
|
|
4
4
|
import { getEip155ChainId } from '../../zkEvm/walletHelpers';
|
|
5
|
-
import { Auth } from '@imtbl/auth';
|
|
6
5
|
import { JsonRpcError, RpcErrorCode } from '../../zkEvm/JsonRpcError';
|
|
7
6
|
import { SequenceSigner } from '../signer';
|
|
7
|
+
import { GetUserFunction } from '../../types';
|
|
8
8
|
|
|
9
9
|
export type RegisterUserInput = {
|
|
10
|
-
|
|
10
|
+
getUser: GetUserFunction;
|
|
11
11
|
ethSigner: SequenceSigner;
|
|
12
12
|
multiRollupApiClients: MultiRollupApiClients;
|
|
13
13
|
accessToken: string;
|
|
@@ -39,7 +39,7 @@ function formatSignature(signature: string): string {
|
|
|
39
39
|
* Creates a counterfactual address for the user on the specified chain.
|
|
40
40
|
*/
|
|
41
41
|
export async function registerUser({
|
|
42
|
-
|
|
42
|
+
getUser,
|
|
43
43
|
ethSigner,
|
|
44
44
|
multiRollupApiClients,
|
|
45
45
|
accessToken,
|
|
@@ -87,7 +87,8 @@ export async function registerUser({
|
|
|
87
87
|
});
|
|
88
88
|
flow.addEvent('endCreateCounterfactualAddress');
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
// Trigger background refresh to get updated user data with the new chain registration
|
|
91
|
+
getUser(true).catch(() => {});
|
|
91
92
|
|
|
92
93
|
return registrationResponse.data.counterfactual_address;
|
|
93
94
|
} catch (error) {
|