@0xsequence/provider 0.43.33 → 1.0.0
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/0xsequence-provider.cjs.dev.js +2265 -2219
- package/dist/0xsequence-provider.cjs.prod.js +2265 -2219
- package/dist/0xsequence-provider.esm.js +2521 -2477
- package/dist/declarations/src/extended.d.ts +6 -0
- package/dist/declarations/src/provider.d.ts +23 -21
- package/dist/declarations/src/transports/base-provider-transport.d.ts +3 -2
- package/dist/declarations/src/transports/base-wallet-transport.d.ts +3 -2
- package/dist/declarations/src/transports/wallet-request-handler.d.ts +19 -22
- package/dist/declarations/src/types.d.ts +10 -14
- package/dist/declarations/src/utils/index.d.ts +5 -9
- package/dist/declarations/src/utils.d.ts +9 -17
- package/dist/declarations/src/wallet.d.ts +12 -15
- package/package.json +11 -10
- package/src/extended.ts +23 -0
- package/src/provider.ts +74 -75
- package/src/transports/base-provider-transport.ts +3 -2
- package/src/transports/base-wallet-transport.ts +11 -10
- package/src/transports/wallet-request-handler.ts +251 -180
- package/src/types.ts +12 -15
- package/src/utils/index.ts +46 -51
- package/src/utils.ts +97 -96
- package/src/wallet.ts +42 -52
|
@@ -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
|
-
|
|
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
|
|
40
|
+
private account: Account | null | undefined
|
|
45
41
|
private signerReadyCallbacks: Array<() => void> = []
|
|
46
42
|
|
|
47
43
|
private prompter: WalletUserPrompter | null
|
|
48
|
-
private
|
|
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
|
-
|
|
54
|
+
account: Account | null | undefined,
|
|
62
55
|
prompter: WalletUserPrompter | null,
|
|
63
|
-
|
|
64
|
-
testnetNetworks: NetworkConfig[] = []
|
|
56
|
+
networks: NetworkConfig[]
|
|
65
57
|
) {
|
|
66
|
-
this.
|
|
58
|
+
this.account = account
|
|
67
59
|
this.prompter = prompter
|
|
68
|
-
this.
|
|
69
|
-
this.testnetNetworks = testnetNetworks
|
|
60
|
+
this.networks = networks
|
|
70
61
|
}
|
|
71
62
|
|
|
72
|
-
|
|
73
|
-
this.
|
|
63
|
+
defaultChainId(): number {
|
|
64
|
+
return this.prompter?.getDefaultChainId() ?? this.networks[0].chainId
|
|
65
|
+
}
|
|
74
66
|
|
|
75
|
-
|
|
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 (
|
|
78
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
130
|
+
if (this.account !== undefined) {
|
|
139
131
|
resolve()
|
|
140
132
|
} else {
|
|
141
133
|
setTimeout(() => {
|
|
142
|
-
if (this.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
261
|
-
const
|
|
262
|
-
if (!
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
|
350
|
+
sig = await account.signMessage(prefixedMessage, chainId ?? this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore')
|
|
321
351
|
} else {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
|
395
|
+
sig = await account.signTypedData(typedData.domain, typedData.types, typedData.message, chainId ?? this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore')
|
|
361
396
|
} else {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
|
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 !==
|
|
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
|
|
422
|
-
response.result = await
|
|
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 (
|
|
474
|
+
if (commons.transaction.isSignedTransactionBundle(request.params![0])) {
|
|
436
475
|
const txChainId = BigNumber.from(request.params![0].chainId).toNumber()
|
|
437
|
-
const tx = await
|
|
438
|
-
response.result =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 [
|
|
643
|
+
const [defaultChainId] = request.params!
|
|
596
644
|
|
|
597
|
-
if (!
|
|
598
|
-
throw new Error('invalid request, method argument
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
674
|
-
|
|
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.
|
|
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
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
:
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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.
|
|
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.
|
|
768
|
+
if (!this.account) {
|
|
751
769
|
logger.warn('signer not set: skipping to notify wallet context')
|
|
752
770
|
return
|
|
753
771
|
}
|
|
754
|
-
const walletContext =
|
|
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.
|
|
782
|
+
return !!this.account
|
|
765
783
|
}
|
|
766
784
|
|
|
767
|
-
|
|
785
|
+
getAccount = async (): Promise<Account | null> => {
|
|
768
786
|
await this.signerReady()
|
|
769
|
-
if (this.
|
|
787
|
+
if (this.account === undefined) {
|
|
770
788
|
throw new Error('signerReady failed resolve')
|
|
771
789
|
}
|
|
772
|
-
return this.
|
|
790
|
+
return this.account
|
|
773
791
|
}
|
|
774
792
|
|
|
775
|
-
|
|
776
|
-
this.
|
|
793
|
+
setAccount(account: Account | null | undefined) {
|
|
794
|
+
this.account = account
|
|
777
795
|
|
|
778
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
797
|
-
|
|
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
|
|
804
|
-
|
|
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:
|
|
821
|
-
promptSendTransaction(txn:
|
|
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 = [
|