@0xsequence/provider 0.43.34 → 1.0.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.
@@ -1,5 +1,12 @@
1
+ import { Account, AccountStatus } from '@0xsequence/account'
2
+ import { signAuthorization, AuthorizationOptions } from '@0xsequence/auth'
3
+ import { commons } from '@0xsequence/core'
4
+ import { NetworkConfig, JsonRpcHandler, JsonRpcRequest, JsonRpcResponseCallback, JsonRpcResponse, getChainId } from '@0xsequence/network'
5
+ import { logger, TypedData } from '@0xsequence/utils'
6
+ import { BigNumber, ethers, providers } from 'ethers'
1
7
  import { EventEmitter2 as EventEmitter } from 'eventemitter2'
2
8
 
9
+ import { fromExtended } from '../extended'
3
10
  import {
4
11
  ProviderMessageRequest,
5
12
  ProviderMessageResponse,
@@ -15,15 +22,6 @@ import {
15
22
  ProviderEventTypes,
16
23
  TypedEventEmitter
17
24
  } from '../types'
18
-
19
- import { BigNumber, ethers, providers } from 'ethers'
20
-
21
- import { NetworkConfig, JsonRpcHandler, JsonRpcRequest, JsonRpcResponseCallback, JsonRpcResponse } from '@0xsequence/network'
22
- import { Signer } from '@0xsequence/wallet'
23
- import { isSignedTransactions, TransactionRequest } from '@0xsequence/transactions'
24
- import { signAuthorization, AuthorizationOptions } from '@0xsequence/auth'
25
- import { logger, TypedData } from '@0xsequence/utils'
26
-
27
25
  import { isWalletUpToDate, prefixEIP191Message } from '../utils'
28
26
 
29
27
  type ExternalProvider = providers.ExternalProvider
@@ -32,67 +30,57 @@ const SIGNER_READY_TIMEOUT = 10000
32
30
 
33
31
  export interface WalletSignInOptions {
34
32
  connect?: boolean
35
- mainnetNetworks?: NetworkConfig[]
36
- testnetNetworks?: NetworkConfig[]
37
- defaultNetworkId?: string | number
33
+ defaultNetworkId?: number
38
34
  }
39
35
 
40
36
  export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, ProviderMessageRequestHandler {
41
37
  // signer interface of the wallet. A null value means there is no signer (ie. user not signed in). An undefined
42
38
  // value means the signer state is unknown, usually meaning the wallet app is booting up and initializing. Of course
43
39
  // a Signer value is the actually interface to a signed-in account
44
- private signer: Signer | null | undefined
40
+ private account: Account | null | undefined
45
41
  private signerReadyCallbacks: Array<() => void> = []
46
42
 
47
43
  private prompter: WalletUserPrompter | null
48
- private mainnetNetworks: NetworkConfig[]
49
- private testnetNetworks: NetworkConfig[]
44
+ private networks: NetworkConfig[]
50
45
 
51
46
  private _openIntent?: OpenWalletIntent
52
47
  private _connectOptions?: ConnectOptions
53
- private _defaultNetworkId?: string | number
54
- private _chainId?: number
55
48
 
56
49
  private events: TypedEventEmitter<ProviderEventTypes> = new EventEmitter() as TypedEventEmitter<ProviderEventTypes>
57
50
 
58
51
  onConnectOptionsChange: ((connectOptions: ConnectOptions | undefined) => void) | undefined = undefined
59
52
 
60
53
  constructor(
61
- signer: Signer | null | undefined,
54
+ account: Account | null | undefined,
62
55
  prompter: WalletUserPrompter | null,
63
- mainnetNetworks: NetworkConfig[],
64
- testnetNetworks: NetworkConfig[] = []
56
+ networks: NetworkConfig[]
65
57
  ) {
66
- this.signer = signer
58
+ this.account = account
67
59
  this.prompter = prompter
68
- this.mainnetNetworks = mainnetNetworks
69
- this.testnetNetworks = testnetNetworks
60
+ this.networks = networks
70
61
  }
71
62
 
72
- async signIn(signer: Signer | null, options: WalletSignInOptions = {}) {
73
- this.setSigner(signer)
63
+ defaultChainId(): number {
64
+ return this.prompter?.getDefaultChainId() ?? this.networks[0].chainId
65
+ }
74
66
 
75
- const { connect, mainnetNetworks, testnetNetworks, defaultNetworkId } = options
67
+ private findNetworkID(network: string | number) {
68
+ const lnet = typeof network === 'string' ? network.toLowerCase() : network
69
+ const networkId = this.networks.find(n => {
70
+ return n.name.toLowerCase() === lnet || n.chainId.toString() === lnet || n.chainId === lnet
71
+ })
76
72
 
77
- if (mainnetNetworks && mainnetNetworks.length > 0) {
78
- this.mainnetNetworks = mainnetNetworks
79
- }
80
- if (testnetNetworks && testnetNetworks.length > 0) {
81
- this.testnetNetworks = testnetNetworks
82
- }
83
- if (
84
- (!this.mainnetNetworks || this.mainnetNetworks.length === 0) &&
85
- (!this.testnetNetworks || this.testnetNetworks.length === 0)
86
- ) {
87
- throw new Error('signIn failed as network configuration is empty')
73
+ if (!networkId) {
74
+ throw new Error(`Network ${network} not found`)
88
75
  }
89
76
 
90
- const networkId = defaultNetworkId || this._defaultNetworkId
91
- if (networkId) {
92
- if (!(await this.setDefaultNetwork(networkId, false))) {
93
- throw new Error(`WalletRequestHandler setup unable to set defaultNetworkId ${networkId}`)
94
- }
95
- }
77
+ return networkId.chainId
78
+ }
79
+
80
+ async signIn(account: Account | null, options: WalletSignInOptions = {}) {
81
+ this.setAccount(account)
82
+
83
+ const { connect, defaultNetworkId } = options
96
84
 
97
85
  // Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform
98
86
  // necessary auth request, and then notify the dapp of the 'connect' details.
@@ -120,26 +108,30 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
120
108
  this.notifyClose()
121
109
  }
122
110
  }
111
+
112
+ if (defaultNetworkId && this.defaultChainId() !== defaultNetworkId) {
113
+ await this.prompter?.promptChangeNetwork(defaultNetworkId)
114
+ }
123
115
  }
124
116
 
125
117
  signOut() {
126
118
  // signed out state
127
- this.setSigner(null)
119
+ this.setAccount(null)
128
120
  }
129
121
 
130
122
  signerReset() {
131
123
  // resetting signer puts the wallet in an uninitialized state, which requires the app to
132
124
  // re-initiatize and set the signer either as "null" (ie. no signer) or "Signer" (ie. signed in).
133
- this.signer = undefined
125
+ this.account = undefined
134
126
  }
135
127
 
136
128
  signerReady(timeout: number = SIGNER_READY_TIMEOUT): Promise<void> {
137
129
  return new Promise((resolve, reject) => {
138
- if (this.signer !== undefined) {
130
+ if (this.account !== undefined) {
139
131
  resolve()
140
132
  } else {
141
133
  setTimeout(() => {
142
- if (this.signer === undefined) {
134
+ if (this.account === undefined) {
143
135
  this.signerReadyCallbacks = []
144
136
  reject(`signerReady timed out`)
145
137
  }
@@ -150,7 +142,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
150
142
  }
151
143
 
152
144
  async connect(options?: ConnectOptions): Promise<ConnectDetails> {
153
- if (!this.signer) {
145
+ if (!this.account) {
154
146
  return {
155
147
  connected: false,
156
148
  chainId: '0x0',
@@ -158,9 +150,29 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
158
150
  }
159
151
  }
160
152
 
153
+ let chainId: number | string
154
+ switch (typeof options?.networkId) {
155
+ case 'string':
156
+ // First see if it matches the name of a network
157
+ try {
158
+ chainId = this.findNetworkID(options.networkId)
159
+ } catch (err) {
160
+ // If not, try to parse it as a big number
161
+ chainId = getChainId(options.networkId)
162
+ }
163
+
164
+ break
165
+ case 'number':
166
+ chainId = options.networkId
167
+ break
168
+ default:
169
+ chainId = this.prompter?.getDefaultChainId() ?? 1
170
+ break
171
+ }
172
+
161
173
  const connectDetails: ConnectDetails = {
162
174
  connected: true,
163
- chainId: ethers.utils.hexlify(await this.getChainId())
175
+ chainId: ethers.BigNumber.from(chainId).toHexString()
164
176
  }
165
177
 
166
178
  if (options && options.authorize) {
@@ -176,7 +188,8 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
176
188
  // }
177
189
 
178
190
  try {
179
- connectDetails.proof = await signAuthorization(this.signer, authOptions)
191
+ // TODO: Either implement account as a signer, or change signAuthorization to accept an account
192
+ connectDetails.proof = await signAuthorization(this.account, chainId, authOptions)
180
193
  } catch (err) {
181
194
  logger.warn(`connect, signAuthorization failed for options: ${JSON.stringify(options)}, due to: ${err.message}`)
182
195
  return {
@@ -248,38 +261,47 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
248
261
  result: null
249
262
  }
250
263
 
251
- await this.getSigner()
264
+ await this.getAccount()
252
265
 
253
266
  try {
254
267
  // only allow public json rpc method to the provider when user is not logged in, aka signer is not set
255
- if ((!this.signer || this.signer === null) && !permittedJsonRpcMethods.includes(request.method)) {
268
+ if ((!this.account || this.account === null) && !permittedJsonRpcMethods.includes(request.method)) {
256
269
  // throw new Error(`not logged in. ${request.method} is unavailable`)
257
270
  throw ErrSignedInRequired
258
271
  }
259
272
 
260
- // wallet signer
261
- const signer = this.signer
262
- if (!signer) throw new Error('WalletRequestHandler: wallet signer is not configured')
273
+ // wallet account
274
+ const account = this.account
275
+ if (!account) throw new Error('WalletRequestHandler: wallet account is not configured')
263
276
 
264
277
  // fetch the provider for the specific chain, or undefined will select defaultChain
265
- const provider = await signer.getProvider(chainId)
278
+ const provider = this.account?.provider(chainId ?? this.defaultChainId())
266
279
  if (!provider) throw new Error(`WalletRequestHandler: wallet provider is not configured for chainId ${chainId}`)
280
+ const jsonRpcProvider = provider instanceof ethers.providers.JsonRpcProvider ? provider : undefined
267
281
 
268
282
  switch (request.method) {
269
283
  case 'net_version': {
270
- const result = await provider.send('net_version', [])
284
+ if (!jsonRpcProvider) {
285
+ throw new Error(`Account provider doesn't support send method`)
286
+ }
287
+
288
+ const result = await jsonRpcProvider.send('net_version', [])
271
289
  response.result = result
272
290
  break
273
291
  }
274
292
 
275
293
  case 'eth_chainId': {
276
- const result = await provider.send('eth_chainId', [])
294
+ if (!jsonRpcProvider) {
295
+ throw new Error(`Account provider doesn't support send method`)
296
+ }
297
+
298
+ const result = await jsonRpcProvider.send('eth_chainId', [])
277
299
  response.result = result
278
300
  break
279
301
  }
280
302
 
281
303
  case 'eth_accounts': {
282
- const walletAddress = await signer.getAddress()
304
+ const walletAddress = account.address
283
305
  response.result = [walletAddress]
284
306
  break
285
307
  }
@@ -291,20 +313,27 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
291
313
  break
292
314
  }
293
315
 
316
+ case 'sequence_sign':
294
317
  case 'personal_sign':
295
318
  case 'eth_sign': {
296
319
  // note: message from json-rpc input is in hex format
297
320
  let message: any
298
321
 
299
322
  // there is a difference in the order of the params:
300
- // personal_sign: [data, address]
323
+ // sequence_sign, personal_sign: [data, address]
301
324
  // eth_sign: [address, data]
302
- if (request.method === 'personal_sign') {
303
- const [data, address] = request.params!
304
- message = data
305
- } else {
306
- const [address, data] = request.params!
307
- message = data
325
+ switch (request.method) {
326
+ case 'sequence_sign':
327
+ case 'personal_sign': {
328
+ const [data, _address] = request.params!
329
+ message = data
330
+ break
331
+ }
332
+ case 'eth_sign': {
333
+ const [_address, data] = request.params!
334
+ message = data
335
+ break
336
+ }
308
337
  }
309
338
 
310
339
  let sig = ''
@@ -315,14 +344,16 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
315
344
 
316
345
  // TODO:
317
346
  // if (process.env.TEST_MODE === 'true' && this.prompter === null) {
347
+ const sequenceVerified = request.method === 'sequence_sign'
318
348
  if (this.prompter === null) {
319
349
  // prompter is null, so we'll sign from here
320
- sig = await signer.signMessage(prefixedMessage, chainId)
350
+ sig = await account.signMessage(prefixedMessage, chainId ?? this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore')
321
351
  } else {
322
- const promptResultForDeployment = await this.handleConfirmWalletDeployPrompt(this.prompter, signer, chainId)
323
- if (promptResultForDeployment) {
324
- sig = await this.prompter.promptSignMessage({ chainId: chainId, message: prefixedMessage }, this.connectOptions)
325
- }
352
+ sig = await this.prompter.promptSignMessage({
353
+ chainId: chainId,
354
+ message: prefixedMessage,
355
+ eip6492: sequenceVerified
356
+ }, this.connectOptions)
326
357
  }
327
358
 
328
359
  if (sig && sig.length > 0) {
@@ -334,6 +365,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
334
365
  break
335
366
  }
336
367
 
368
+ case 'sequence_signTypedData_v4':
337
369
  case 'eth_signTypedData':
338
370
  case 'eth_signTypedData_v4': {
339
371
  // note: signingAddress from json-rpc input is in hex format, and typedDataObject
@@ -344,7 +376,9 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
344
376
  if (typeof typedDataObject === 'string') {
345
377
  try {
346
378
  typedData = JSON.parse(typedDataObject)
347
- } catch (e) {}
379
+ } catch (e) {
380
+ console.warn('walletRequestHandler: error parsing typedData', e)
381
+ }
348
382
  } else {
349
383
  typedData = typedDataObject
350
384
  }
@@ -355,14 +389,16 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
355
389
 
356
390
  let sig = ''
357
391
 
392
+ const sequenceVerified = request.method === 'sequence_signTypedData_v4'
358
393
  if (this.prompter === null) {
359
394
  // prompter is null, so we'll sign from here
360
- sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId)
395
+ sig = await account.signTypedData(typedData.domain, typedData.types, typedData.message, chainId ?? this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore')
361
396
  } else {
362
- const promptResultForDeployment = await this.handleConfirmWalletDeployPrompt(this.prompter, signer, chainId)
363
- if (promptResultForDeployment) {
364
- sig = await this.prompter.promptSignMessage({ chainId: chainId, typedData: typedData }, this.connectOptions)
365
- }
397
+ sig = await this.prompter.promptSignMessage({
398
+ chainId: chainId,
399
+ typedData: typedData,
400
+ eip6492: sequenceVerified
401
+ }, this.connectOptions)
366
402
  }
367
403
 
368
404
  if (sig && sig.length > 0) {
@@ -376,19 +412,22 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
376
412
 
377
413
  case 'eth_sendTransaction': {
378
414
  // https://eth.wiki/json-rpc/API#eth_sendtransaction
379
- const [transactionParams] = request.params!
380
-
381
- // eth_sendTransaction uses 'gas'
382
- // ethers and sequence use 'gasLimit'
383
- if ('gas' in transactionParams && transactionParams.gasLimit === undefined) {
384
- transactionParams.gasLimit = transactionParams.gas
385
- delete transactionParams.gas
386
- }
415
+ const transactionParams = fromExtended(request.params![0])
416
+ .map((tx) => {
417
+ // eth_sendTransaction uses 'gas'
418
+ // ethers and sequence use 'gasLimit'
419
+ if ('gas' in tx && tx.gasLimit === undefined) {
420
+ tx.gasLimit = tx.gas as any
421
+ delete tx.gas
422
+ }
423
+
424
+ return tx
425
+ })
387
426
 
388
427
  let txnHash = ''
389
428
  if (this.prompter === null) {
390
429
  // prompter is null, so we'll send from here
391
- const txnResponse = await signer.sendTransaction(transactionParams, chainId)
430
+ const txnResponse = await account.sendTransaction(transactionParams, chainId ?? this.defaultChainId())
392
431
  txnHash = txnResponse.hash
393
432
  } else {
394
433
  // prompt user to provide the response
@@ -409,7 +448,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
409
448
  const [transaction] = request.params!
410
449
  const sender = ethers.utils.getAddress(transaction.from)
411
450
 
412
- if (sender !== (await signer.getAddress())) {
451
+ if (sender !== account.address) {
413
452
  throw new Error('sender address does not match wallet')
414
453
  }
415
454
 
@@ -418,8 +457,8 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
418
457
  // this can only be broadcasted using an RPC provider with support for signed Sequence transactions, like this one.
419
458
  //
420
459
  // TODO: verify serializing / transporting the SignedTransaction object works as expected, most likely however
421
- // we will want to resolveProperties the bignumber values to hex strings
422
- response.result = await signer.signTransactions(transaction, chainId)
460
+ // we will want to resolveProperties the big number values to hex strings
461
+ response.result = await account.signTransactions(transaction, chainId ?? this.defaultChainId())
423
462
  } else {
424
463
  response.result = await this.prompter.promptSignTransaction(transaction, chainId, this.connectOptions)
425
464
  }
@@ -432,10 +471,10 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
432
471
  // and would have prompted the user upon signing.
433
472
 
434
473
  // https://eth.wiki/json-rpc/API#eth_sendRawTransaction
435
- if (isSignedTransactions(request.params![0])) {
474
+ if (commons.transaction.isSignedTransactionBundle(request.params![0])) {
436
475
  const txChainId = BigNumber.from(request.params![0].chainId).toNumber()
437
- const tx = await (await signer.getRelayer(txChainId))!.relay(request.params![0])
438
- response.result = (await tx).hash
476
+ const tx = await account.relayer(txChainId)!.relay(request.params![0])
477
+ response.result = tx.hash
439
478
  } else {
440
479
  const tx = await provider.sendTransaction(request.params![0])
441
480
  response.result = tx.hash
@@ -447,15 +486,12 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
447
486
  const address = ethers.utils.getAddress(request.params![0] as string)
448
487
  const tag = request.params![1]
449
488
 
450
- const walletAddress = ethers.utils.getAddress(await signer.getAddress())
489
+ // TODO: Maybe we should fetch this data from the relayer or from the reader
490
+ // but for now we keep it simple and just use the provider
491
+
492
+ const count = await provider.getTransactionCount(address, tag)
493
+ response.result = ethers.BigNumber.from(count).toHexString()
451
494
 
452
- if (address === walletAddress) {
453
- const count = await signer.getTransactionCount(tag)
454
- response.result = ethers.BigNumber.from(count).toHexString()
455
- } else {
456
- const count = await provider.getTransactionCount(address, tag)
457
- response.result = ethers.BigNumber.from(count).toHexString()
458
- }
459
495
  break
460
496
  }
461
497
 
@@ -511,10 +547,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
511
547
 
512
548
  const chainId = ethers.BigNumber.from(switchParams.chainId)
513
549
 
514
- const ok = await this.setDefaultNetwork(chainId.toString(), true)
515
- if (!ok) {
516
- throw new Error(`unable to set chainId ${chainId}`)
517
- }
550
+ this.setDefaultChainId(chainId.toNumber())
518
551
 
519
552
  response.result = null // success
520
553
  break
@@ -522,21 +555,36 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
522
555
 
523
556
  // smart wallet method
524
557
  case 'sequence_getWalletContext': {
525
- response.result = await signer.getWalletContext()
558
+ response.result = account.contexts
526
559
  break
527
560
  }
528
561
 
529
562
  // smart wallet method
530
563
  case 'sequence_getWalletConfig': {
531
564
  const [chainId] = request.params!
532
- response.result = await signer.getWalletConfig(chainId)
565
+ if (chainId) {
566
+ response.result = [(await account.status(chainId)).onChain.config]
567
+ } else {
568
+ response.result = await Promise.all(account.networks.map(async network => {
569
+ const status = await account.status(network.chainId)
570
+ return status.onChain.config
571
+ }))
572
+ }
533
573
  break
534
574
  }
535
575
 
536
576
  // smart wallet method
537
577
  case 'sequence_getWalletState': {
538
578
  const [chainId] = request.params!
539
- response.result = await signer.getWalletState(chainId)
579
+ // TODO: Add getWalletState to the Signer interface
580
+ if (chainId) {
581
+ response.result = [getLegacyWalletState(chainId, await account.status(chainId))]
582
+ } else {
583
+ response.result = await Promise.all(account.networks.map(async network => {
584
+ const status = await account.status(network.chainId)
585
+ return getLegacyWalletState(network.chainId, status)
586
+ }))
587
+ }
540
588
  break
541
589
  }
542
590
 
@@ -592,23 +640,24 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
592
640
 
593
641
  // set default network of wallet
594
642
  case 'sequence_setDefaultNetwork': {
595
- const [defaultNetworkId] = request.params!
643
+ const [defaultChainId] = request.params!
596
644
 
597
- if (!defaultNetworkId) {
598
- throw new Error('invalid request, method argument defaultNetworkId cannot be empty')
599
- }
600
- const ok = await this.setDefaultNetwork(defaultNetworkId)
601
- if (!ok) {
602
- throw new Error(`unable to set default network ${defaultNetworkId}`)
645
+ if (!defaultChainId) {
646
+ throw new Error('invalid request, method argument defaultChainId cannot be empty')
603
647
  }
604
648
 
649
+ this.setDefaultChainId(defaultChainId)
605
650
  response.result = await this.getNetworks(true)
606
651
  break
607
652
  }
608
653
 
609
654
  default: {
655
+ if (!jsonRpcProvider) {
656
+ throw new Error(`Account provider doesn't support send method`)
657
+ }
658
+
610
659
  // NOTE: provider here will be chain-bound if chainId is provided
611
- const providerResponse = await provider.send(request.method, request.params!)
660
+ const providerResponse = await jsonRpcProvider.send(request.method, request.params!)
612
661
  response.result = providerResponse
613
662
  }
614
663
  }
@@ -635,21 +684,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
635
684
  }
636
685
 
637
686
  async getAddress(): Promise<string> {
638
- if (!this.signer) {
639
- return ''
640
- } else {
641
- return this.signer.getAddress()
642
- }
643
- }
644
-
645
- async getChainId(): Promise<number> {
646
- if (!this.signer) {
647
- return 0
648
- } else {
649
- if (this._chainId) return this._chainId // memoized
650
- this._chainId = await this.signer.getChainId()
651
- return this._chainId
652
- }
687
+ return this.account?.address ?? ''
653
688
  }
654
689
 
655
690
  get openIntent(): OpenWalletIntent | undefined {
@@ -670,55 +705,38 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
670
705
  this.onConnectOptionsChange?.(options)
671
706
  }
672
707
 
673
- get defaultNetworkId(): string | number | undefined {
674
- return this._defaultNetworkId
675
- }
676
-
677
- async setDefaultNetwork(chainId: string | number, notifyNetworks: boolean = true): Promise<number | undefined> {
678
- if (!chainId) return undefined
679
- this._defaultNetworkId = chainId
680
- this._chainId = undefined
681
-
682
- if (this.signer && (<any>this.signer).setNetworks) {
683
- const defaultChainId: number = (<any>this.signer).setNetworks(this.mainnetNetworks, this.testnetNetworks, chainId)
684
- if (defaultChainId && notifyNetworks) {
685
- await this.notifyNetworks()
686
- }
687
- return defaultChainId
688
- } else {
689
- return undefined
690
- }
708
+ async setDefaultChainId(chainId: number): Promise<number> {
709
+ await this.prompter?.promptChangeNetwork(chainId)
710
+ return this.defaultChainId()
691
711
  }
692
712
 
693
713
  async getNetworks(jsonRpcResponse?: boolean): Promise<NetworkConfig[]> {
694
- if (!this.signer) {
714
+ if (!this.account) {
695
715
  logger.warn('signer not set: getNetworks is returning an empty list')
696
716
  return []
697
717
  }
698
718
 
699
- const networks = await this.signer.getNetworks()
700
-
701
719
  if (jsonRpcResponse) {
702
720
  // omit provider and relayer objects as they are not serializable
703
- return networks.map(n => {
721
+ return this.account.networks.map(n => {
704
722
  const network: NetworkConfig = { ...n }
705
723
  network.provider = undefined
706
724
  network.relayer = undefined
707
725
  return network
708
726
  })
709
727
  } else {
710
- return networks
728
+ return this.account.networks
711
729
  }
712
730
  }
713
731
 
714
- async walletSession(): Promise<WalletSession | undefined> {
715
- return !this.signer
716
- ? undefined
717
- : {
718
- walletContext: await this.signer.getWalletContext(),
719
- accountAddress: await this.signer.getAddress(),
720
- networks: await this.getNetworks(true)
721
- }
732
+ walletSession(): WalletSession | undefined {
733
+ if (!this.account) return undefined
734
+ return {
735
+ walletContext: this.account.contexts,
736
+ accountAddress: this.account.address,
737
+ // The dapp shouldn't access the relayer directly, and the provider (as an object) is not serializable.
738
+ networks: this.account.networks.map((n) => ({ ...n, provider: undefined, relayer: undefined }))
739
+ }
722
740
  }
723
741
 
724
742
  notifyConnect(connectDetails: ConnectDetails, origin?: string) {
@@ -737,7 +755,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
737
755
  const n = networks || (await this.getNetworks(true))
738
756
  this.events.emit('networks', n)
739
757
  if (n.length > 0) {
740
- const defaultNetwork = n.find(network => network.isDefaultChain)
758
+ const defaultNetwork = n.find(network => network.chainId === this.defaultChainId())
741
759
  if (defaultNetwork) {
742
760
  this.events.emit('chainChanged', ethers.utils.hexlify(defaultNetwork.chainId))
743
761
  }
@@ -747,11 +765,11 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
747
765
  }
748
766
 
749
767
  async notifyWalletContext() {
750
- if (!this.signer) {
768
+ if (!this.account) {
751
769
  logger.warn('signer not set: skipping to notify wallet context')
752
770
  return
753
771
  }
754
- const walletContext = await this.signer.getWalletContext()
772
+ const walletContext = this.account.contexts
755
773
  this.events.emit('walletContext', walletContext)
756
774
  }
757
775
 
@@ -761,21 +779,21 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
761
779
 
762
780
  isSignedIn = async (): Promise<boolean> => {
763
781
  await this.signerReady()
764
- return !!this.signer
782
+ return !!this.account
765
783
  }
766
784
 
767
- getSigner = async (): Promise<Signer | null> => {
785
+ getAccount = async (): Promise<Account | null> => {
768
786
  await this.signerReady()
769
- if (this.signer === undefined) {
787
+ if (this.account === undefined) {
770
788
  throw new Error('signerReady failed resolve')
771
789
  }
772
- return this.signer
790
+ return this.account
773
791
  }
774
792
 
775
- setSigner(signer: Signer | null | undefined) {
776
- this.signer = signer
793
+ setAccount(account: Account | null | undefined) {
794
+ this.account = account
777
795
 
778
- if (signer !== undefined) {
796
+ if (account !== undefined) {
779
797
  for (let i = 0; i < this.signerReadyCallbacks.length; i++) {
780
798
  this.signerReadyCallbacks[i]()
781
799
  }
@@ -785,7 +803,8 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
785
803
 
786
804
  private async handleConfirmWalletDeployPrompt(
787
805
  prompter: WalletUserPrompter,
788
- signer: Signer,
806
+ account: Account,
807
+ sequenceVerified: boolean,
789
808
  chainId?: number
790
809
  ): Promise<boolean> {
791
810
  // check if wallet is deployed and up to date, if not, prompt user to deploy
@@ -793,33 +812,85 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
793
812
  if (!chainId) {
794
813
  return true
795
814
  }
796
- const isUpToDate = await isWalletUpToDate(signer, chainId)
797
- if (isUpToDate) {
815
+
816
+ const skipsDeploy = (status: AccountStatus) => {
817
+ return status.canOnchainValidate || (status.original.version === 2 && sequenceVerified)
818
+ }
819
+
820
+ const status = await account.status(chainId)
821
+ if (skipsDeploy(status)) {
798
822
  return true
799
823
  }
824
+
800
825
  const promptResult = await prompter.promptConfirmWalletDeploy(chainId, this.connectOptions)
826
+
801
827
  // if client returned true, check again to make sure wallet is deployed and up to date
802
828
  if (promptResult) {
803
- const isPromptResultCorrect = await isWalletUpToDate(signer, chainId)
804
- if (!isPromptResultCorrect) {
829
+ const status2 = await account.status(chainId)
830
+
831
+ if (skipsDeploy(status2)) {
832
+ return true
833
+ } else {
805
834
  logger.error('WalletRequestHandler: result for promptConfirmWalletDeploy is not correct')
806
835
  return false
807
- } else {
808
- return true
809
836
  }
810
837
  }
838
+
811
839
  return false
812
840
  }
813
841
  }
814
842
 
815
843
  export interface WalletUserPrompter {
844
+ getDefaultChainId(): number
845
+
816
846
  promptConnect(options?: ConnectOptions): Promise<PromptConnectDetails>
817
847
  promptSignInConnect(options?: ConnectOptions): Promise<PromptConnectDetails>
818
848
 
819
849
  promptSignMessage(message: MessageToSign, options?: ConnectOptions): Promise<string>
820
- promptSignTransaction(txn: TransactionRequest, chaindId?: number, options?: ConnectOptions): Promise<string>
821
- promptSendTransaction(txn: TransactionRequest, chaindId?: number, options?: ConnectOptions): Promise<string>
850
+ promptSignTransaction(txn: commons.transaction.Transactionish, chainId?: number, options?: ConnectOptions): Promise<string>
851
+ promptSendTransaction(txn: commons.transaction.Transactionish, chainId?: number, options?: ConnectOptions): Promise<string>
822
852
  promptConfirmWalletDeploy(chainId: number, options?: ConnectOptions): Promise<boolean>
853
+
854
+ promptChangeNetwork(chainId: number): Promise<boolean>
855
+ }
856
+
857
+ interface LegacyWalletState {
858
+ context: commons.context.WalletContext
859
+ config?: commons.config.Config
860
+
861
+ // the wallet address
862
+ address: string
863
+
864
+ // the chainId of the network
865
+ chainId: number
866
+
867
+ // whether the wallet has been ever deployed
868
+ deployed: boolean
869
+
870
+ // the imageHash of the `config` WalletConfig
871
+ imageHash: string
872
+
873
+ // the last imageHash of a WalletConfig, stored on-chain
874
+ lastImageHash?: string
875
+
876
+ // whether the WalletConfig object itself has been published to logs
877
+ published?: boolean
878
+
879
+ status: AccountStatus
880
+ }
881
+
882
+ function getLegacyWalletState(chainId: number, status: AccountStatus): LegacyWalletState {
883
+ return {
884
+ context: status.original.context,
885
+ config: status.onChain.config,
886
+ address: commons.context.addressOf(status.original.context, status.original.imageHash),
887
+ chainId,
888
+ deployed: status.onChain.deployed,
889
+ imageHash: status.imageHash,
890
+ lastImageHash: status.onChain.imageHash,
891
+ published: true,
892
+ status
893
+ }
823
894
  }
824
895
 
825
896
  const permittedJsonRpcMethods = [