@0xsequence/provider 0.0.0-2021823181734

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +909 -0
  2. package/README.md +4 -0
  3. package/dist/0xsequence-provider.cjs.d.ts +1 -0
  4. package/dist/0xsequence-provider.cjs.dev.js +3540 -0
  5. package/dist/0xsequence-provider.cjs.js +7 -0
  6. package/dist/0xsequence-provider.cjs.prod.js +3540 -0
  7. package/dist/0xsequence-provider.esm.js +3508 -0
  8. package/dist/declarations/src/commands/index.d.ts +20 -0
  9. package/dist/declarations/src/index.d.ts +5 -0
  10. package/dist/declarations/src/provider.d.ts +59 -0
  11. package/dist/declarations/src/transports/base-provider-transport.d.ts +40 -0
  12. package/dist/declarations/src/transports/base-wallet-transport.d.ts +38 -0
  13. package/dist/declarations/src/transports/extension-transport/base-injected-transport.d.ts +18 -0
  14. package/dist/declarations/src/transports/extension-transport/extension-message-handler.d.ts +12 -0
  15. package/dist/declarations/src/transports/extension-transport/extension-message-provider.d.ts +11 -0
  16. package/dist/declarations/src/transports/extension-transport/index.d.ts +3 -0
  17. package/dist/declarations/src/transports/index.d.ts +7 -0
  18. package/dist/declarations/src/transports/mux-transport/index.d.ts +1 -0
  19. package/dist/declarations/src/transports/mux-transport/mux-message-provider.d.ts +22 -0
  20. package/dist/declarations/src/transports/proxy-transport/index.d.ts +3 -0
  21. package/dist/declarations/src/transports/proxy-transport/proxy-message-channel.d.ts +15 -0
  22. package/dist/declarations/src/transports/proxy-transport/proxy-message-handler.d.ts +12 -0
  23. package/dist/declarations/src/transports/proxy-transport/proxy-message-provider.d.ts +12 -0
  24. package/dist/declarations/src/transports/wallet-request-handler.d.ts +51 -0
  25. package/dist/declarations/src/transports/window-transport/index.d.ts +2 -0
  26. package/dist/declarations/src/transports/window-transport/window-message-handler.d.ts +15 -0
  27. package/dist/declarations/src/transports/window-transport/window-message-provider.d.ts +13 -0
  28. package/dist/declarations/src/types.d.ts +170 -0
  29. package/dist/declarations/src/utils.d.ts +14 -0
  30. package/dist/declarations/src/wallet.d.ts +99 -0
  31. package/package.json +37 -0
  32. package/src/commands/index.ts +146 -0
  33. package/src/index.ts +5 -0
  34. package/src/provider.ts +494 -0
  35. package/src/transports/base-provider-transport.ts +411 -0
  36. package/src/transports/base-wallet-transport.ts +441 -0
  37. package/src/transports/extension-transport/base-injected-transport.ts +100 -0
  38. package/src/transports/extension-transport/extension-message-handler.ts +25 -0
  39. package/src/transports/extension-transport/extension-message-provider.ts +41 -0
  40. package/src/transports/extension-transport/index.ts +3 -0
  41. package/src/transports/index.ts +7 -0
  42. package/src/transports/mux-transport/index.ts +1 -0
  43. package/src/transports/mux-transport/mux-message-provider.ts +160 -0
  44. package/src/transports/proxy-transport/index.ts +3 -0
  45. package/src/transports/proxy-transport/proxy-message-channel.ts +58 -0
  46. package/src/transports/proxy-transport/proxy-message-handler.ts +46 -0
  47. package/src/transports/proxy-transport/proxy-message-provider.ts +81 -0
  48. package/src/transports/wallet-request-handler.ts +702 -0
  49. package/src/transports/window-transport/index.ts +2 -0
  50. package/src/transports/window-transport/window-message-handler.ts +166 -0
  51. package/src/transports/window-transport/window-message-provider.ts +183 -0
  52. package/src/types.ts +252 -0
  53. package/src/utils.ts +76 -0
  54. package/src/wallet.ts +692 -0
@@ -0,0 +1,702 @@
1
+ import { EventEmitter2 as EventEmitter } from 'eventemitter2'
2
+
3
+ import {
4
+ ProviderMessage,
5
+ ProviderMessageRequest,
6
+ ProviderMessageResponse,
7
+ ProviderMessageResponseCallback,
8
+ ProviderMessageRequestHandler,
9
+ MessageToSign,
10
+ ProviderRpcError,
11
+ ConnectOptions,
12
+ ConnectDetails,
13
+ PromptConnectDetails,
14
+ WalletSession,
15
+ ErrSignedInRequired,
16
+ ProviderEventTypes,
17
+ TypedEventEmitter
18
+ } from '../types'
19
+
20
+ import { BigNumber, ethers } from 'ethers'
21
+ import { JsonRpcProvider, ExternalProvider } from '@ethersproject/providers'
22
+
23
+ import {
24
+ Networks,
25
+ NetworkConfig,
26
+ JsonRpcHandler,
27
+ JsonRpcRequest,
28
+ JsonRpcResponseCallback,
29
+ JsonRpcResponse
30
+ } from '@0xsequence/network'
31
+
32
+ import { Signer, Account } from '@0xsequence/wallet'
33
+ import { isSignedTransactions, SignedTransactions, TransactionRequest } from '@0xsequence/transactions'
34
+
35
+ import { signAuthorization, AuthorizationOptions } from '@0xsequence/auth'
36
+
37
+ import { logger, TypedData } from '@0xsequence/utils'
38
+
39
+ export interface WalletSignInOptions {
40
+ connect?: boolean
41
+ mainnetNetworks?: Networks
42
+ testnetNetworks?: Networks
43
+ defaultNetworkId?: string | number
44
+ }
45
+
46
+ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, ProviderMessageRequestHandler {
47
+ private signer: Signer | null
48
+ private prompter: WalletUserPrompter | null
49
+ private mainnetNetworks: NetworkConfig[]
50
+ private testnetNetworks: NetworkConfig[]
51
+
52
+ private _connectOptions?: ConnectOptions
53
+ private _defaultNetworkId?: string | number
54
+ private _chainId?: number
55
+
56
+ private events: TypedEventEmitter<ProviderEventTypes> = new EventEmitter() as TypedEventEmitter<ProviderEventTypes>
57
+
58
+ constructor(
59
+ signer: Signer | null,
60
+ prompter: WalletUserPrompter | null,
61
+ mainnetNetworks: Networks,
62
+ testnetNetworks: Networks = []
63
+ ) {
64
+ this.signer = signer
65
+ this.prompter = prompter
66
+ this.mainnetNetworks = mainnetNetworks
67
+ this.testnetNetworks = testnetNetworks
68
+ }
69
+
70
+ async signIn(signer: Signer | null, options: WalletSignInOptions = {}) {
71
+ this.signer = signer
72
+
73
+ const { connect, mainnetNetworks, testnetNetworks, defaultNetworkId } = options
74
+
75
+ if (mainnetNetworks && mainnetNetworks.length > 0) {
76
+ this.mainnetNetworks = mainnetNetworks
77
+ }
78
+ if (testnetNetworks && testnetNetworks.length > 0) {
79
+ this.testnetNetworks = testnetNetworks
80
+ }
81
+ if (
82
+ (!this.mainnetNetworks || this.mainnetNetworks.length === 0) &&
83
+ (!this.testnetNetworks || this.testnetNetworks.length === 0)
84
+ ) {
85
+ throw new Error('signIn failed as network configuration is empty')
86
+ }
87
+
88
+ const networkId = defaultNetworkId || this._defaultNetworkId
89
+ if (networkId) {
90
+ if (!(await this.setDefaultNetwork(networkId, false))) {
91
+ throw new Error(`WalletRequestHandler setup unable to set defaultNetworkId ${networkId}`)
92
+ }
93
+ }
94
+
95
+ // Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform
96
+ // necessary auth request, and then notify the dapp of the 'connect' details.
97
+ //
98
+ // NOTE: if a user is signing into a dapp from a fresh state, and and auth request is made
99
+ // we don't trigger the promptConnect flow, as we consider the user just authenticated
100
+ // for this dapp, so its safe to authorize in the connect() method without the prompt.
101
+ //
102
+ // NOTE: signIn can optionally connect and notify dapp at this time for new signIn flows
103
+ if (connect) {
104
+ const connectOptions = this._connectOptions
105
+
106
+ const connectDetails = await this.connect(connectOptions)
107
+ this.notifyConnect(connectDetails)
108
+
109
+ if (!connectOptions || connectOptions.keepWalletOpened !== true) {
110
+ this.notifyClose()
111
+ }
112
+ }
113
+ }
114
+
115
+ async connect(options?: ConnectOptions): Promise<ConnectDetails> {
116
+ if (!this.signer) {
117
+ return {
118
+ connected: false,
119
+ chainId: '0x0',
120
+ error: 'unable to connect without signed in account'
121
+ }
122
+ }
123
+
124
+ const connectDetails: ConnectDetails = {
125
+ connected: true,
126
+ chainId: ethers.utils.hexlify(await this.getChainId())
127
+ }
128
+
129
+ if (options && options.authorize) {
130
+ // Perform ethauth eip712 request and construct the ConnectDetails response
131
+ // including the auth proof
132
+ const authOptions: AuthorizationOptions = {
133
+ app: options.app,
134
+ origin: options.origin,
135
+ expiry: options.expiry
136
+ }
137
+ // if (typeof(options.authorize) === 'object') {
138
+ // authOptions = { ...authOptions, ...options.authorize }
139
+ // }
140
+
141
+ try {
142
+ connectDetails.proof = await signAuthorization(this.signer, authOptions)
143
+ } catch (err) {
144
+ logger.warn(`connect, signAuthorization failed for options: ${JSON.stringify(options)}, due to: ${err.message}`)
145
+ return {
146
+ connected: false,
147
+ chainId: '0x0',
148
+ error: `signAuthorization failed: ${err.message}`
149
+ }
150
+ }
151
+ }
152
+
153
+ // Build session response for connect details
154
+ connectDetails.session = await this.walletSession()
155
+
156
+ return connectDetails
157
+ }
158
+
159
+ promptConnect = async (options?: ConnectOptions): Promise<ConnectDetails> => {
160
+ if (!options && !this._connectOptions) {
161
+ // this is an unexpected state and should not happen
162
+ throw new Error('prompter connect options are empty')
163
+ }
164
+
165
+ if (!this.prompter) {
166
+ // if prompter is null, we'll auto connect
167
+ return this.connect(options)
168
+ }
169
+
170
+ const promptConnectDetails = await this.prompter.promptConnect(options || this._connectOptions).catch(_ => {
171
+ return { connected: false } as ConnectDetails
172
+ })
173
+
174
+ const connectDetails: ConnectDetails = promptConnectDetails
175
+ if (connectDetails.connected && !connectDetails.session) {
176
+ connectDetails.session = await this.walletSession()
177
+ }
178
+
179
+ return promptConnectDetails
180
+ }
181
+
182
+ // sendMessageRequest will unwrap the ProviderMessageRequest and send it to the JsonRpcHandler
183
+ // (aka, the signer in this instance) and then responds with a wrapped response of
184
+ // ProviderMessageResponse to be sent over the transport
185
+ sendMessageRequest(message: ProviderMessageRequest): Promise<ProviderMessageResponse> {
186
+ return new Promise(resolve => {
187
+ this.sendAsync(
188
+ message.data,
189
+ (error: any, response?: JsonRpcResponse) => {
190
+ // TODO: if response includes data.error, why do we need a separate error argument here?
191
+
192
+ const responseMessage: ProviderMessageResponse = {
193
+ ...message,
194
+ data: response!
195
+ }
196
+
197
+ // NOTE: we always resolve here, are the sendAsync call will wrap any exceptions
198
+ // in the error field of the response to ensure we send back to the user
199
+ resolve(responseMessage)
200
+ },
201
+ message.chainId
202
+ )
203
+ })
204
+ }
205
+
206
+ // sendAsync implements the JsonRpcHandler interface for sending JsonRpcRequests to the wallet
207
+ sendAsync = async (request: JsonRpcRequest, callback: JsonRpcResponseCallback, chainId?: number) => {
208
+ const response: JsonRpcResponse = {
209
+ jsonrpc: '2.0',
210
+ id: request.id!,
211
+ result: null
212
+ }
213
+
214
+ try {
215
+ // only allow public json rpc method to the provider when user is not logged in, aka signer is not set
216
+ if ((!this.signer || this.signer === null) && !permittedJsonRpcMethods.includes(request.method)) {
217
+ // throw new Error(`not logged in. ${request.method} is unavailable`)
218
+ throw ErrSignedInRequired
219
+ }
220
+
221
+ // wallet signer
222
+ const signer = this.signer
223
+ if (!signer) throw new Error('WalletRequestHandler: wallet signer is not configured')
224
+
225
+ // fetch the provider for the specific chain, or undefined will select defaultChain
226
+ const provider = await signer.getProvider(chainId)
227
+ if (!provider) throw new Error(`WalletRequestHandler: wallet provider is not configured for chainId ${chainId}`)
228
+
229
+ switch (request.method) {
230
+ case 'net_version': {
231
+ const result = await provider.send('net_version', [])
232
+ response.result = result
233
+ break
234
+ }
235
+
236
+ case 'eth_chainId': {
237
+ const result = await provider.send('eth_chainId', [])
238
+ response.result = result
239
+ break
240
+ }
241
+
242
+ case 'eth_accounts': {
243
+ const walletAddress = await signer.getAddress()
244
+ response.result = [walletAddress]
245
+ break
246
+ }
247
+
248
+ case 'eth_getBalance': {
249
+ const [accountAddress, blockTag] = request.params!
250
+ const walletBalance = await provider.getBalance(accountAddress, blockTag)
251
+ response.result = walletBalance.toHexString()
252
+ break
253
+ }
254
+
255
+ case 'eth_sign': {
256
+ // note: message from json-rpc input is in hex format
257
+ const [signingAddress, message] = request.params!
258
+
259
+ let sig = ''
260
+ // TODO:
261
+ // if (process.env.TEST_MODE === 'true' && this.prompter === null) {
262
+ if (this.prompter === null) {
263
+ // prompter is null, so we'll sign from here
264
+ sig = await signer.signMessage(ethers.utils.arrayify(message), chainId)
265
+ } else {
266
+ // prompt user to provide the response
267
+ sig = await this.prompter.promptSignMessage({ chainId: chainId, message: message })
268
+ }
269
+
270
+ if (sig && sig.length > 0) {
271
+ response.result = sig
272
+ } else {
273
+ // The user has declined the request when value is null
274
+ throw new Error('declined by user')
275
+ }
276
+ break
277
+ }
278
+
279
+ case 'eth_signTypedData':
280
+ case 'eth_signTypedData_v4': {
281
+ // note: signingAddress from json-rpc input is in hex format, and typedDataObject
282
+ // should be an object, but in some instances may be double string encoded
283
+ const [signingAddress, typedDataObject] = request.params!
284
+
285
+ let typedData: TypedData | undefined = undefined
286
+ if (typeof typedDataObject === 'string') {
287
+ try {
288
+ typedData = JSON.parse(typedDataObject)
289
+ } catch (e) {}
290
+ } else {
291
+ typedData = typedDataObject
292
+ }
293
+
294
+ if (!typedData || !typedData.domain || !typedData.types || !typedData.message) {
295
+ throw new Error('invalid typedData object')
296
+ }
297
+
298
+ let sig = ''
299
+ if (this.prompter === null) {
300
+ // prompter is null, so we'll sign from here
301
+ sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId)
302
+ } else {
303
+ // prompt user to provide the response
304
+ sig = await this.prompter.promptSignMessage({ chainId: chainId, typedData: typedData })
305
+ }
306
+
307
+ if (sig && sig.length > 0) {
308
+ response.result = sig
309
+ } else {
310
+ // The user has declined the request when value is null
311
+ throw new Error('declined by user')
312
+ }
313
+ break
314
+ }
315
+
316
+ case 'eth_sendTransaction': {
317
+ // https://eth.wiki/json-rpc/API#eth_sendtransaction
318
+ const [transactionParams] = request.params!
319
+
320
+ let txnHash = ''
321
+ if (this.prompter === null) {
322
+ // prompter is null, so we'll send from here
323
+ const txnResponse = await signer.sendTransaction(transactionParams, chainId)
324
+ txnHash = txnResponse.hash
325
+ } else {
326
+ // prompt user to provide the response
327
+ txnHash = await this.prompter.promptSendTransaction(transactionParams, chainId)
328
+ }
329
+
330
+ if (txnHash) {
331
+ response.result = txnHash
332
+ } else {
333
+ // The user has declined the request when value is null
334
+ throw new Error('declined by user')
335
+ }
336
+ break
337
+ }
338
+
339
+ case 'eth_signTransaction': {
340
+ // https://eth.wiki/json-rpc/API#eth_signTransaction
341
+ const [transaction] = request.params!
342
+ const sender = ethers.utils.getAddress(transaction.from)
343
+
344
+ if (sender !== (await signer.getAddress())) {
345
+ throw new Error('sender address does not match wallet')
346
+ }
347
+
348
+ if (this.prompter === null) {
349
+ // The eth_signTransaction method expects a `string` return value we instead return a `SignedTransactions` object,
350
+ // this can only be broadcasted using an RPC provider with support for signed Sequence transactions, like this one.
351
+ //
352
+ // TODO: verify serializing / transporting the SignedTransaction object works as expected, most likely however
353
+ // we will want to resolveProperties the bignumber values to hex strings
354
+ response.result = await signer.signTransactions(transaction, chainId)
355
+ } else {
356
+ response.result = await this.prompter.promptSignTransaction(transaction, chainId)
357
+ }
358
+
359
+ break
360
+ }
361
+
362
+ case 'eth_sendRawTransaction': {
363
+ // NOTE: we're not using a prompter here as the transaction is already signed
364
+ // and would have prompted the user upon signing.
365
+
366
+ // https://eth.wiki/json-rpc/API#eth_sendRawTransaction
367
+ if (isSignedTransactions(request.params![0])) {
368
+ const txChainId = BigNumber.from(request.params![0].chainId).toNumber()
369
+ const tx = await (await signer.getRelayer(txChainId))!.relay(request.params![0])
370
+ response.result = (await tx).hash
371
+ } else {
372
+ const tx = await provider.sendTransaction(request.params![0])
373
+ response.result = tx.hash
374
+ }
375
+ break
376
+ }
377
+
378
+ case 'eth_getTransactionCount': {
379
+ const address = ethers.utils.getAddress(request.params![0] as string)
380
+ const tag = request.params![1]
381
+
382
+ const walletAddress = ethers.utils.getAddress(await signer.getAddress())
383
+
384
+ if (address === walletAddress) {
385
+ const count = await signer.getTransactionCount(tag)
386
+ response.result = ethers.BigNumber.from(count).toHexString()
387
+ } else {
388
+ const count = await provider.getTransactionCount(address, tag)
389
+ response.result = ethers.BigNumber.from(count).toHexString()
390
+ }
391
+ break
392
+ }
393
+
394
+ case 'eth_blockNumber': {
395
+ response.result = await provider.getBlockNumber()
396
+ break
397
+ }
398
+
399
+ case 'eth_getBlockByNumber': {
400
+ response.result = await provider.getBlock(request.params![0] /* , jsonRpcRequest.params[1] */)
401
+ break
402
+ }
403
+
404
+ case 'eth_getBlockByHash': {
405
+ response.result = await provider.getBlock(request.params![0] /* , jsonRpcRequest.params[1] */)
406
+ break
407
+ }
408
+
409
+ case 'eth_getTransactionByHash': {
410
+ response.result = await provider.getTransaction(request.params![0])
411
+ break
412
+ }
413
+
414
+ case 'eth_call': {
415
+ const [transactionObject, blockTag] = request.params!
416
+ response.result = await provider.call(transactionObject, blockTag)
417
+ break
418
+ }
419
+
420
+ case 'eth_getCode': {
421
+ const [contractAddress, blockTag] = request.params!
422
+ response.result = await provider.getCode(contractAddress, blockTag)
423
+ break
424
+ }
425
+
426
+ case 'eth_estimateGas': {
427
+ const [transactionObject] = request.params!
428
+ response.result = await provider.estimateGas(transactionObject)
429
+ break
430
+ }
431
+
432
+ case 'eth_gasPrice': {
433
+ const gasPrice = await provider.getGasPrice()
434
+ response.result = gasPrice.toHexString()
435
+ break
436
+ }
437
+
438
+ // smart wallet method
439
+ case 'sequence_getWalletContext': {
440
+ response.result = await signer.getWalletContext()
441
+ break
442
+ }
443
+
444
+ // smart wallet method
445
+ case 'sequence_getWalletConfig': {
446
+ const [chainId] = request.params!
447
+ response.result = await signer.getWalletConfig(chainId)
448
+ break
449
+ }
450
+
451
+ // smart wallet method
452
+ case 'sequence_getWalletState': {
453
+ const [chainId] = request.params!
454
+ response.result = await signer.getWalletState(chainId)
455
+ break
456
+ }
457
+
458
+ // smart wallet method
459
+ case 'sequence_getNetworks': {
460
+ // NOTE: must ensure that the response result below returns clean serialized data, which is to omit
461
+ // the provider and relayer objects and only return the urls so can be reinstantiated on dapp side.
462
+ // This is handled by this.getNetworks() but noted here for future readers.
463
+ response.result = await this.getNetworks(true)
464
+ break
465
+ }
466
+
467
+ // smart wallet method
468
+ case 'sequence_updateConfig': {
469
+ throw new Error('sequence_updateConfig method is not allowed from a dapp')
470
+ // NOTE: method is disabled as we don't need a dapp to request to update a config.
471
+ // However, if we ever want this, we can enable it but must also use the prompter
472
+ // for confirmation.
473
+ //
474
+ // const [newConfig] = request.params
475
+ // response.result = await signer.updateConfig(newConfig)
476
+ break
477
+ }
478
+
479
+ // smart wallet method
480
+ case 'sequence_publishConfig': {
481
+ throw new Error('sequence_publishConfig method is not allowed from a dapp')
482
+ break
483
+ }
484
+
485
+ // relayer method
486
+ case 'sequence_estimateGasLimits': {
487
+ // TODO
488
+ break
489
+ }
490
+
491
+ // relayer method
492
+ case 'sequence_gasRefundOptions': {
493
+ // TODO
494
+ break
495
+ }
496
+
497
+ // relayer method
498
+ case 'sequence_getNonce': {
499
+ // TODO
500
+ break
501
+ }
502
+
503
+ // relayer method
504
+ case 'sequence_relay': {
505
+ // TODO
506
+ break
507
+ }
508
+
509
+ // set default network of wallet
510
+ case 'sequence_setDefaultNetwork': {
511
+ const [defaultNetworkId] = request.params!
512
+
513
+ if (!defaultNetworkId) {
514
+ throw new Error('invalid request, method argument defaultNetworkId cannot be empty')
515
+ }
516
+ const ok = await this.setDefaultNetwork(defaultNetworkId)
517
+ if (!ok) {
518
+ throw new Error(`unable to set default network ${defaultNetworkId}`)
519
+ }
520
+
521
+ response.result = await this.getNetworks(true)
522
+ break
523
+ }
524
+
525
+ default: {
526
+ // NOTE: provider here will be chain-bound if chainId is provided
527
+ const providerResponse = await provider.send(request.method, request.params!)
528
+ response.result = providerResponse
529
+ }
530
+ }
531
+ } catch (err) {
532
+ logger.error(err)
533
+
534
+ // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md#rpc-errors
535
+ response.result = null
536
+ response.error = {
537
+ ...new Error(err),
538
+ code: 4001
539
+ }
540
+ }
541
+
542
+ callback(undefined, response)
543
+ }
544
+
545
+ on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) {
546
+ this.events.on(event, fn as any)
547
+ }
548
+
549
+ once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) {
550
+ this.events.once(event, fn as any)
551
+ }
552
+
553
+ async getAddress(): Promise<string> {
554
+ if (!this.signer) {
555
+ return ''
556
+ } else {
557
+ return this.signer.getAddress()
558
+ }
559
+ }
560
+
561
+ async getChainId(): Promise<number> {
562
+ if (!this.signer) {
563
+ return 0
564
+ } else {
565
+ if (this._chainId) return this._chainId // memoized
566
+ this._chainId = await this.signer.getChainId()
567
+ return this._chainId
568
+ }
569
+ }
570
+
571
+ get connectOptions(): ConnectOptions | undefined {
572
+ return this._connectOptions
573
+ }
574
+
575
+ setConnectOptions(options: ConnectOptions | undefined) {
576
+ this._connectOptions = options
577
+ }
578
+
579
+ get defaultNetworkId(): string | number | undefined {
580
+ return this._defaultNetworkId
581
+ }
582
+
583
+ async setDefaultNetwork(chainId: string | number, notifyNetworks: boolean = true): Promise<number | undefined> {
584
+ if (!chainId) return undefined
585
+ this._defaultNetworkId = chainId
586
+ this._chainId = undefined
587
+
588
+ if (this.signer && (<any>this.signer).setNetworks) {
589
+ const defaultChainId: number = (<any>this.signer).setNetworks(this.mainnetNetworks, this.testnetNetworks, chainId)
590
+ if (defaultChainId && notifyNetworks) {
591
+ await this.notifyNetworks()
592
+ }
593
+ return defaultChainId
594
+ } else {
595
+ return undefined
596
+ }
597
+ }
598
+
599
+ async getNetworks(jsonRpcResponse?: boolean): Promise<NetworkConfig[]> {
600
+ if (!this.signer) {
601
+ logger.warn('signer not set: getNetworks is returning an empty list')
602
+ return []
603
+ }
604
+
605
+ const networks = await this.signer.getNetworks()
606
+
607
+ if (jsonRpcResponse) {
608
+ // omit provider and relayer objects as they are not serializable
609
+ return networks.map(n => {
610
+ const network: NetworkConfig = { ...n }
611
+ network.provider = undefined
612
+ network.relayer = undefined
613
+ return network
614
+ })
615
+ } else {
616
+ return networks
617
+ }
618
+ }
619
+
620
+ async walletSession(): Promise<WalletSession | undefined> {
621
+ return !this.signer
622
+ ? undefined
623
+ : {
624
+ walletContext: await this.signer.getWalletContext(),
625
+ accountAddress: await this.signer.getAddress(),
626
+ networks: await this.getNetworks(true)
627
+ }
628
+ }
629
+
630
+ notifyConnect(connectDetails: ConnectDetails) {
631
+ this.events.emit('connect', connectDetails)
632
+ if (connectDetails.session?.accountAddress) {
633
+ this.events.emit('accountsChanged', [connectDetails.session?.accountAddress])
634
+ }
635
+ }
636
+
637
+ notifyDisconnect() {
638
+ this.events.emit('accountsChanged', [])
639
+ this.events.emit('networks', [])
640
+ this.events.emit('disconnect')
641
+ }
642
+
643
+ async notifyNetworks(networks?: NetworkConfig[]) {
644
+ const n = networks || (await this.getNetworks(true))
645
+ this.events.emit('networks', n)
646
+ if (n && n.length > 0) {
647
+ this.events.emit('chainChanged', ethers.utils.hexlify(n[0].chainId))
648
+ } else {
649
+ this.events.emit('chainChanged', '0x0')
650
+ }
651
+ }
652
+
653
+ async notifyWalletContext() {
654
+ if (!this.signer) {
655
+ logger.warn('signer not set: skipping to notify wallet context')
656
+ return
657
+ }
658
+ const walletContext = await this.signer.getWalletContext()
659
+ this.events.emit('walletContext', walletContext)
660
+ }
661
+
662
+ notifyClose(error?: ProviderRpcError) {
663
+ this.events.emit('close', error)
664
+ }
665
+
666
+ isSignedIn(): boolean {
667
+ return !!this.signer
668
+ }
669
+
670
+ getSigner(): Signer | null {
671
+ return this.signer
672
+ }
673
+
674
+ setSigner(signer: Signer | null) {
675
+ this.signer = signer
676
+ }
677
+ }
678
+
679
+ export interface WalletUserPrompter {
680
+ promptConnect(options?: ConnectOptions): Promise<PromptConnectDetails>
681
+ promptSignMessage(message: MessageToSign): Promise<string>
682
+ promptSignTransaction(txn: TransactionRequest, chaindId?: number): Promise<string>
683
+ promptSendTransaction(txn: TransactionRequest, chaindId?: number): Promise<string>
684
+ }
685
+
686
+ const permittedJsonRpcMethods = [
687
+ 'net_version',
688
+ 'eth_chainId',
689
+ 'eth_getBalance',
690
+ 'eth_getTransactionCount',
691
+ 'eth_blockNumber',
692
+ 'eth_getBlockByNumber',
693
+ 'eth_getBlockByHash',
694
+ 'eth_getTransactionByHash',
695
+ 'eth_getCode',
696
+ 'eth_estimateGas',
697
+ 'eth_gasPrice',
698
+
699
+ 'sequence_getWalletContext',
700
+ 'sequence_getNetworks',
701
+ 'sequence_setDefaultNetwork'
702
+ ]
@@ -0,0 +1,2 @@
1
+ export * from './window-message-provider'
2
+ export * from './window-message-handler'