@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
package/src/wallet.ts ADDED
@@ -0,0 +1,692 @@
1
+ import {
2
+ Networks,
3
+ NetworkConfig,
4
+ WalletContext,
5
+ sequenceContext,
6
+ ChainIdLike,
7
+ getChainId,
8
+ JsonRpcSender,
9
+ JsonRpcRouter,
10
+ JsonRpcMiddleware,
11
+ allowProviderMiddleware,
12
+ CachedProvider,
13
+ PublicProvider,
14
+ loggingProviderMiddleware,
15
+ SigningProvider,
16
+ EagerProvider,
17
+ exceptionProviderMiddleware,
18
+ networkProviderMiddleware,
19
+ JsonRpcExternalProvider,
20
+ JsonRpcHandlerFunc,
21
+ JsonRpcRequest,
22
+ JsonRpcResponse,
23
+ JsonRpcResponseCallback,
24
+ findNetworkConfig,
25
+ updateNetworkConfig,
26
+ ensureValidNetworks
27
+ } from '@0xsequence/network'
28
+ import { WalletConfig, WalletState } from '@0xsequence/config'
29
+ import { logger } from '@0xsequence/utils'
30
+ import { JsonRpcProvider, JsonRpcSigner, ExternalProvider } from '@ethersproject/providers'
31
+ import { Web3Provider, Web3Signer } from './provider'
32
+ import { MuxMessageProvider, WindowMessageProvider, ProxyMessageProvider, ProxyMessageChannelPort } from './transports'
33
+ import { WalletSession, ProviderEventTypes, ConnectOptions, OpenWalletIntent, ConnectDetails } from './types'
34
+ import { WalletCommands } from './commands'
35
+ import { ethers } from 'ethers'
36
+ import { ExtensionMessageProvider } from './transports/extension-transport/extension-message-provider'
37
+ import { LocalStore } from './utils'
38
+
39
+ import { Runtime } from 'webextension-polyfill-ts'
40
+
41
+ export interface WalletProvider {
42
+ connect(options?: ConnectOptions): Promise<ConnectDetails>
43
+ // authorize(options?: ConnectOptions): Promise<ConnectDetails>
44
+ disconnect(): void
45
+
46
+ isOpened(): boolean
47
+ isConnected(): boolean
48
+ getSession(): WalletSession | undefined
49
+
50
+ getAddress(): Promise<string>
51
+ getNetworks(chainId?: ChainIdLike): Promise<NetworkConfig[]>
52
+ getChainId(): Promise<number>
53
+ getAuthChainId(): Promise<number>
54
+
55
+ openWallet(path?: string, intent?: OpenWalletIntent, networkId?: string | number): Promise<boolean>
56
+ closeWallet(): void
57
+
58
+ getProvider(chainId?: ChainIdLike): Web3Provider | undefined
59
+ getSigner(chainId?: ChainIdLike): Web3Signer
60
+
61
+ getWalletContext(): Promise<WalletContext>
62
+ getWalletConfig(chainId?: ChainIdLike): Promise<WalletConfig[]>
63
+ getWalletState(chainId?: ChainIdLike): Promise<WalletState[]>
64
+ isDeployed(chainId?: ChainIdLike): Promise<boolean>
65
+
66
+ getProviderConfig(): ProviderConfig
67
+
68
+ on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void
69
+ once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void
70
+
71
+ commands: WalletCommands
72
+ }
73
+
74
+ export class Wallet implements WalletProvider {
75
+ public commands: WalletCommands
76
+
77
+ private config: ProviderConfig
78
+ private session?: WalletSession
79
+
80
+ private connectedSites: LocalStore<string[]>
81
+
82
+ private transport: {
83
+ // top-level provider which connects all transport layers
84
+ provider?: Web3Provider
85
+
86
+ // middleware stack for provider
87
+ router?: JsonRpcRouter
88
+ networkProvider?: JsonRpcMiddleware
89
+ allowProvider?: JsonRpcMiddleware
90
+ cachedProvider?: CachedProvider
91
+
92
+ // message communication
93
+ messageProvider?: MuxMessageProvider
94
+ windowMessageProvider?: WindowMessageProvider
95
+ proxyMessageProvider?: ProxyMessageProvider
96
+ extensionMessageProvider?: ExtensionMessageProvider
97
+ }
98
+
99
+ private networks: NetworkConfig[]
100
+ private providers: { [chainId: number]: Web3Provider }
101
+
102
+ constructor(defaultNetworkId?: string | number, config?: Partial<ProviderConfig>) {
103
+ // config is a Partial, so that we may intersect it with the DefaultProviderConfig,
104
+ // which allows easy overriding and control of the config.
105
+ this.config = { ...DefaultProviderConfig }
106
+ if (config) {
107
+ this.config = { ...this.config, ...config }
108
+ }
109
+ if (defaultNetworkId) {
110
+ this.config.defaultNetworkId = defaultNetworkId
111
+ } else if (!this.config.defaultNetworkId) {
112
+ this.config.defaultNetworkId = 'mainnet'
113
+ }
114
+
115
+ this.transport = {}
116
+ this.networks = []
117
+ this.providers = {}
118
+ this.connectedSites = new LocalStore('@sequence.connectedSites', [])
119
+ this.commands = new WalletCommands(this)
120
+ this.init()
121
+ }
122
+
123
+ private init = () => {
124
+ if (this.transport.provider) {
125
+ // init must have already been called
126
+ return
127
+ }
128
+
129
+ // Setup provider
130
+
131
+ this.transport.messageProvider = new MuxMessageProvider()
132
+
133
+ // multiple message provider setup, first one to connect will be the main transport
134
+ if (this.config.transports?.windowTransport?.enabled) {
135
+ this.transport.windowMessageProvider = new WindowMessageProvider(this.config.walletAppURL)
136
+ this.transport.messageProvider.add(this.transport.windowMessageProvider)
137
+ }
138
+ if (this.config.transports?.proxyTransport?.enabled) {
139
+ this.transport.proxyMessageProvider = new ProxyMessageProvider(this.config.transports.proxyTransport.appPort!)
140
+ this.transport.messageProvider.add(this.transport.proxyMessageProvider)
141
+ }
142
+ if (this.config.transports?.extensionTransport?.enabled) {
143
+ this.transport.extensionMessageProvider = new ExtensionMessageProvider(this.config.transports.extensionTransport.runtime)
144
+ this.transport.messageProvider.add(this.transport.extensionMessageProvider)
145
+ }
146
+ this.transport.messageProvider.register()
147
+
148
+ // .....
149
+ this.transport.allowProvider = allowProviderMiddleware((request: JsonRpcRequest): boolean => {
150
+ if (request.method === 'sequence_setDefaultNetwork') return true
151
+
152
+ const isConnected = this.isConnected()
153
+ if (!isConnected) {
154
+ throw new Error('Sequence: wallet not connected')
155
+ }
156
+ return isConnected
157
+ })
158
+
159
+ // ...
160
+ this.transport.networkProvider = networkProviderMiddleware((request: JsonRpcRequest): number => {
161
+ // return stub chainId of 0 when not connected to any
162
+ if (!this.networks || this.networks.length === 0) return 0
163
+
164
+ // return the default chainId as we're connected
165
+ return this.networks[0].chainId
166
+ })
167
+
168
+ // Provider proxy to support middleware stack of logging, caching and read-only rpc calls
169
+ this.transport.cachedProvider = new CachedProvider()
170
+ this.transport.cachedProvider.onUpdate(() => {
171
+ if (!this.session) this.session = { providerCache: {} }
172
+ this.session.providerCache = this.transport.cachedProvider!.getCache()
173
+ this.saveSession(this.session)
174
+ })
175
+
176
+ // ..
177
+ this.transport.router = new JsonRpcRouter(
178
+ [
179
+ loggingProviderMiddleware,
180
+ this.transport.networkProvider,
181
+ this.transport.allowProvider,
182
+ exceptionProviderMiddleware,
183
+ this.transport.cachedProvider
184
+ ],
185
+ this.transport.messageProvider
186
+ )
187
+
188
+ this.transport.provider = new Web3Provider(this.transport.router)
189
+
190
+ // NOTE: we don't listen on 'connect' even here as we handle it within connect() method
191
+ // in more synchronous flow.
192
+
193
+ // below will update the wallet session object and persist it. In case the session
194
+ // is undefined, we consider the session to have been removed by the user, so we clear it.
195
+ this.transport.messageProvider.on('open', (openInfo: { session?: WalletSession }) => {
196
+ const { session } = openInfo
197
+ if (!session) {
198
+ if (this.session && this.session.accountAddress) {
199
+ // emit disconnect even if previously we had a session, and now we don't.
200
+ this.transport.messageProvider!.emit('disconnect')
201
+ }
202
+ this.clearSession()
203
+ } else {
204
+ this.useSession(session, true)
205
+ }
206
+ })
207
+
208
+ // below will update the account upon wallet connect/disconnect (aka, login/logout)
209
+ this.transport.messageProvider.on('accountsChanged', (accounts: string[]) => {
210
+ if (!accounts || accounts.length === 0 || accounts[0] === '') {
211
+ this.clearSession()
212
+ } else {
213
+ this.useSession({ accountAddress: accounts[0] }, true)
214
+ }
215
+ })
216
+
217
+ // below will update the networks automatically when the wallet networks change
218
+ this.transport.messageProvider.on('networks', (networks: NetworkConfig[]) => {
219
+ this.useSession({ networks: networks }, true)
220
+ })
221
+
222
+ // below will update the wallet context automatically
223
+ this.transport.messageProvider.on('walletContext', (walletContext: WalletContext) => {
224
+ this.useSession({ walletContext: walletContext }, true)
225
+ })
226
+
227
+ // Load existing session from localStorage
228
+ const session = this.loadSession()
229
+ if (session) {
230
+ this.useSession(session, false)
231
+ }
232
+ }
233
+
234
+ connect = async (options?: ConnectOptions): Promise<ConnectDetails> => {
235
+ if (options?.refresh === true) {
236
+ this.disconnect()
237
+ }
238
+
239
+ if (
240
+ this.isConnected() &&
241
+ this.isSiteConnected(options?.origin) &&
242
+ !!this.session &&
243
+ !options?.authorize &&
244
+ !options?.askForEmail
245
+ ) {
246
+ return {
247
+ connected: true,
248
+ session: this.session,
249
+ chainId: ethers.utils.hexlify(await this.getChainId())
250
+ }
251
+ }
252
+
253
+ if (options) {
254
+ if (options.authorize && (!options.app || options.app === '')) {
255
+ throw new Error(`connecting with 'authorize' option also requires 'app' to be set`)
256
+ }
257
+ }
258
+
259
+ await this.openWallet(undefined, { type: 'connect', options })
260
+ const connectDetails = await this.transport.messageProvider!.waitUntilConnected().catch(_ => {
261
+ return { connected: false } as ConnectDetails
262
+ })
263
+
264
+ if (connectDetails.connected) {
265
+ if (!!connectDetails.session) {
266
+ this.useSession(connectDetails.session, true)
267
+
268
+ this.addConnectedSite(options?.origin)
269
+ } else {
270
+ throw new Error('impossible state, connect response is missing session')
271
+ }
272
+ }
273
+
274
+ return connectDetails
275
+ }
276
+
277
+ addConnectedSite(origin: string | undefined) {
278
+ origin = origin || window.location.origin
279
+
280
+ const connectedSites = this.connectedSites.get()
281
+
282
+ if (connectedSites) {
283
+ if (connectedSites.includes(origin)) {
284
+ return
285
+ }
286
+ this.connectedSites.set([...connectedSites, origin])
287
+ } else {
288
+ this.connectedSites.set([origin])
289
+ }
290
+ }
291
+
292
+ removeConnectedSite(origin: string) {
293
+ const authorized = this.connectedSites.get()
294
+
295
+ if (authorized) {
296
+ this.connectedSites.set(authorized.filter(domain => domain !== origin))
297
+ }
298
+ }
299
+
300
+ getConnectedSites() {
301
+ return this.connectedSites.get()
302
+ }
303
+
304
+ private isSiteConnected(origin: string | undefined): boolean {
305
+ const authorized = this.connectedSites.get()
306
+
307
+ return !!authorized && authorized.includes(origin || window.location.origin)
308
+ }
309
+
310
+ authorize = async (options?: ConnectOptions): Promise<ConnectDetails> => {
311
+ return this.connect({ ...options, authorize: true })
312
+ }
313
+
314
+ disconnect(): void {
315
+ if (this.isOpened()) {
316
+ this.closeWallet()
317
+ }
318
+ this.clearSession()
319
+ }
320
+
321
+ getProviderConfig(): ProviderConfig {
322
+ return this.config
323
+ }
324
+
325
+ isOpened(): boolean {
326
+ return this.transport.messageProvider!.isOpened()
327
+ }
328
+
329
+ isConnected(): boolean {
330
+ return (
331
+ this.session !== undefined &&
332
+ this.session.networks !== undefined &&
333
+ this.session.networks.length > 0 &&
334
+ this.networks !== undefined &&
335
+ this.networks.length > 0 &&
336
+ !!this.session.accountAddress &&
337
+ this.session.accountAddress.startsWith('0x')
338
+ )
339
+ }
340
+
341
+ getSession = (): WalletSession | undefined => {
342
+ if (!this.isConnected()) {
343
+ return undefined
344
+ }
345
+ return this.session
346
+ }
347
+
348
+ getAddress = async (): Promise<string> => {
349
+ if (!this.isConnected()) {
350
+ throw new Error('connect first')
351
+ }
352
+ const session = this.getSession()
353
+ return session!.accountAddress!
354
+ }
355
+
356
+ getNetworks = async (chainId?: ChainIdLike): Promise<NetworkConfig[]> => {
357
+ if (!this.isConnected() || !this.networks) {
358
+ throw new Error('connect first')
359
+ }
360
+ if (chainId) {
361
+ // filter list to just the specific chain requested
362
+ const network = findNetworkConfig(this.networks, chainId)
363
+ return network ? [network] : []
364
+ }
365
+ return this.networks
366
+ }
367
+
368
+ // getChainId returns the default chain id
369
+ getChainId = async (): Promise<number> => {
370
+ if (!this.networks || this.networks.length < 1) {
371
+ throw new Error('networks have not been set by session. connect first.')
372
+ }
373
+ // default chain id is first one in the list, by design
374
+ const network = this.networks[0]
375
+ if (!network.isDefaultChain) {
376
+ throw new Error('expecting first network in list to be default chain')
377
+ }
378
+ return network.chainId
379
+ }
380
+
381
+ getAuthChainId = async (): Promise<number> => {
382
+ if (!this.networks || this.networks.length < 1) {
383
+ throw new Error('networks have not been set by session. connect first.')
384
+ }
385
+ // auth chain is first or second one in the list, by design
386
+ const network0 = this.networks[0]
387
+ if (network0.isAuthChain) {
388
+ return network0.chainId
389
+ }
390
+ if (this.networks.length > 1) {
391
+ const network1 = this.networks[1]
392
+ if (network1.isAuthChain) {
393
+ return network1.chainId
394
+ }
395
+ }
396
+ throw new Error('expecting first or second network in list to be the auth chain')
397
+ }
398
+
399
+ openWallet = async (path?: string, intent?: OpenWalletIntent, networkId?: string | number): Promise<boolean> => {
400
+ if (intent?.type !== 'connect' && !this.isConnected()) {
401
+ throw new Error('connect first')
402
+ }
403
+
404
+ let currentNetworkId
405
+
406
+ if (!this.networks || this.networks.length < 1) {
407
+ currentNetworkId = this.config.defaultNetworkId
408
+ } else {
409
+ currentNetworkId = await this.getChainId()
410
+ }
411
+
412
+ this.transport.messageProvider!.openWallet(path, intent, networkId || currentNetworkId)
413
+ await this.transport.messageProvider!.waitUntilOpened()
414
+
415
+ return true
416
+ }
417
+
418
+ closeWallet = (): void => {
419
+ this.transport.messageProvider!.closeWallet()
420
+ }
421
+
422
+ getProvider(chainId?: ChainIdLike): Web3Provider | undefined {
423
+ // return the top-level provider message transport when chainId is unspecified
424
+ // and user has not logged in
425
+ if (!this.isConnected()) {
426
+ if (chainId) {
427
+ throw new Error(`session is empty. connect and try again.`)
428
+ } else {
429
+ return this.transport.provider
430
+ }
431
+ }
432
+
433
+ let network: NetworkConfig | undefined = this.networks[0]
434
+ if (chainId) {
435
+ network = findNetworkConfig(this.networks, chainId)
436
+ if (!network) {
437
+ throw new Error(`network ${chainId} is not in the network list`)
438
+ }
439
+ }
440
+
441
+ // return memoized network provider
442
+ if (this.providers[network.chainId]) {
443
+ return this.providers[network.chainId]
444
+ }
445
+
446
+ // builder web3 provider stack
447
+ let provider: Web3Provider
448
+
449
+ // network.provider may be set by the ProviderConfig override
450
+ const rpcProvider = network.provider ? network.provider : new JsonRpcProvider(network.rpcUrl, network.chainId)
451
+
452
+ if (network.isDefaultChain) {
453
+ // communicating with defaultChain will prioritize the wallet message transport
454
+ const router = new JsonRpcRouter(
455
+ [
456
+ loggingProviderMiddleware,
457
+ exceptionProviderMiddleware,
458
+ new EagerProvider({ accountAddress: this.session!.accountAddress, walletContext: this.session!.walletContext }),
459
+ new SigningProvider(this.transport!.provider!),
460
+ this.transport.cachedProvider!
461
+ ],
462
+ new JsonRpcSender(rpcProvider)
463
+ )
464
+
465
+ provider = new Web3Provider(router, network.chainId)
466
+ } else {
467
+ // communicating with another chain will bind to that network, but will forward
468
+ // any signing-related requests to the wallet message transport
469
+ const router = new JsonRpcRouter(
470
+ [
471
+ loggingProviderMiddleware,
472
+ exceptionProviderMiddleware,
473
+ new EagerProvider({
474
+ accountAddress: this.session!.accountAddress,
475
+ walletContext: this.session!.walletContext,
476
+ chainId: network.chainId
477
+ }),
478
+ new SigningProvider(this.transport.provider!),
479
+ new CachedProvider(network.chainId)
480
+ ],
481
+ new JsonRpcSender(rpcProvider)
482
+ )
483
+
484
+ provider = new Web3Provider(router, network.chainId)
485
+ }
486
+
487
+ this.providers[network.chainId] = provider
488
+ return provider
489
+ }
490
+
491
+ async getAuthProvider(): Promise<Web3Provider> {
492
+ return this.getProvider((await this.getAuthNetwork()).chainId)!
493
+ }
494
+
495
+ async getAuthNetwork(): Promise<NetworkConfig> {
496
+ return (await this.getNetworks()).find(n => n.isAuthChain)!
497
+ }
498
+
499
+ getAllProviders(): { [chainId: number]: Web3Provider } {
500
+ return this.providers
501
+ }
502
+
503
+ getSigner(chainId?: ChainIdLike): Web3Signer {
504
+ return this.getProvider(chainId)!.getSigner()
505
+ }
506
+
507
+ async getAuthSigner(): Promise<Web3Signer> {
508
+ return (await this.getAuthProvider()).getSigner()
509
+ }
510
+
511
+ getWalletConfig(chainId?: ChainIdLike): Promise<WalletConfig[]> {
512
+ return this.getSigner().getWalletConfig(chainId)
513
+ }
514
+
515
+ getWalletState(chainId?: ChainIdLike): Promise<WalletState[]> {
516
+ return this.getSigner().getWalletState(chainId)
517
+ }
518
+
519
+ getWalletContext(): Promise<WalletContext> {
520
+ return this.getSigner().getWalletContext()
521
+ }
522
+
523
+ isDeployed(chainId?: ChainIdLike): Promise<boolean> {
524
+ return this.getSigner(chainId).isDeployed()
525
+ }
526
+
527
+ on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) {
528
+ this.transport.messageProvider!.on(event, fn)
529
+ }
530
+
531
+ once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) {
532
+ this.transport.messageProvider!.once(event, fn)
533
+ }
534
+
535
+ private loadSession = (): WalletSession | undefined => {
536
+ const data = window.localStorage.getItem('@sequence.session')
537
+ if (!data || data === '') {
538
+ return undefined
539
+ }
540
+ try {
541
+ const session = JSON.parse(data) as WalletSession
542
+ return session
543
+ } catch (err) {
544
+ logger.warn('loadSession failed, unable to parse session payload from localStorage.')
545
+ return undefined
546
+ }
547
+ }
548
+
549
+ private saveSession = (session: WalletSession) => {
550
+ logger.debug('wallet provider: saving session')
551
+ const data = JSON.stringify(session)
552
+ window.localStorage.setItem('@sequence.session', data)
553
+ }
554
+
555
+ private useSession = async (session: WalletSession, autoSave: boolean = true) => {
556
+ if (!this.session) this.session = {}
557
+
558
+ // setup wallet context
559
+ if (this.config.walletContext) {
560
+ this.session.walletContext = this.config.walletContext
561
+ } else if (session.walletContext) {
562
+ this.session.walletContext = session.walletContext
563
+ }
564
+
565
+ // setup account
566
+ if (session.accountAddress) {
567
+ this.useAccountAddress(session.accountAddress)
568
+ }
569
+
570
+ // setup networks
571
+ if (session.networks) {
572
+ this.useNetworks(session.networks)
573
+ }
574
+
575
+ // setup provider cache
576
+ if (session.providerCache) {
577
+ this.transport.cachedProvider!.setCache(session.providerCache)
578
+ }
579
+
580
+ // persist
581
+ if (autoSave) {
582
+ this.saveSession(this.session)
583
+ }
584
+ }
585
+
586
+ private useAccountAddress(accountAddress: string) {
587
+ if (!this.session) this.session = {}
588
+ this.session.accountAddress = ethers.utils.getAddress(accountAddress)
589
+ }
590
+
591
+ private useNetworks(networks: NetworkConfig[]) {
592
+ // set networks in the session
593
+ if (!this.session) this.session = {}
594
+
595
+ // set networks on session object
596
+ this.session.networks = networks
597
+
598
+ // short-circuit if setting empty network list (aka logged out state)
599
+ if (!this.session.networks || this.session.networks.length === 0) {
600
+ return
601
+ }
602
+
603
+ // check if any custom network settings, otherwise return early
604
+ if (!this.config.networks && !this.config.networkRpcUrl) {
605
+ this.networks = networks
606
+ return
607
+ }
608
+
609
+ // combine custom network config with networks in the session
610
+ if (this.config.networks) {
611
+ this.networks = networks.map(n => ({ ...n })) // copy
612
+ this.config.networks.forEach(n => {
613
+ const network = findNetworkConfig(this.networks, n.chainId || n.name!)
614
+ if (!network) return
615
+ updateNetworkConfig(n, network)
616
+ })
617
+ ensureValidNetworks(this.networks, true)
618
+ }
619
+
620
+ // an extra override for convenience
621
+ if (this.config.networkRpcUrl) {
622
+ this.networks[0].rpcUrl = this.config.networkRpcUrl
623
+ }
624
+ }
625
+
626
+ private clearSession(): void {
627
+ logger.debug('wallet provider: clearing session')
628
+ window.localStorage.removeItem('@sequence.session')
629
+ this.session = undefined
630
+ this.networks = []
631
+ this.providers = {}
632
+ this.transport.cachedProvider?.clearCache()
633
+ }
634
+ }
635
+
636
+ export interface ProviderConfig {
637
+ // Sequence Wallet App URL, default: https://sequence.app
638
+ walletAppURL: string
639
+
640
+ // Sequence Wallet Session URL, default: https://session.sequence.app
641
+ // walletSessionURL: string
642
+
643
+ // networks is a configuration list of networks used by the wallet. This list
644
+ // is combined with the network list supplied from the wallet upon login,
645
+ // and settings here take precedence such as overriding a relayer setting, or rpcUrl.
646
+ networks?: Partial<NetworkConfig>[]
647
+
648
+ // networkRpcUrl will set the provider rpcUrl of the default network
649
+ networkRpcUrl?: string
650
+
651
+ // defaultNetworkId is the primary network of a dapp and the default network a
652
+ // provider will communicate. Note: this setting is also configurable from the
653
+ // Wallet constructor's first argument.
654
+ defaultNetworkId?: string | number
655
+
656
+ // transports for dapp to wallet jron-rpc communication
657
+ transports?: {
658
+ // WindowMessage transport (optional)
659
+ windowTransport?: {
660
+ enabled: boolean
661
+ }
662
+
663
+ // ProxyMessage transport (optional)
664
+ proxyTransport?: {
665
+ enabled: boolean
666
+ appPort?: ProxyMessageChannelPort
667
+ }
668
+
669
+ // Extension transport (optional)
670
+ extensionTransport?: {
671
+ enabled: boolean
672
+ runtime: Runtime.Static
673
+ }
674
+ }
675
+
676
+ // Sequence Wallet Modules Context override. By default (and recommended), the
677
+ // WalletContext used the one returned by the wallet app upon login.
678
+ //
679
+ // NOTE: do not use this option unless you know what you're doing
680
+ walletContext?: WalletContext
681
+ }
682
+
683
+ export const DefaultProviderConfig: ProviderConfig = {
684
+ walletAppURL: 'https://sequence.app',
685
+
686
+ // walletSessionURL: 'https://session.sequence.app',
687
+
688
+ transports: {
689
+ windowTransport: { enabled: true },
690
+ proxyTransport: { enabled: false }
691
+ }
692
+ }