@0xsequence/provider 1.0.5 → 1.1.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.
Files changed (31) hide show
  1. package/dist/0xsequence-provider.cjs.dev.js +1742 -1678
  2. package/dist/0xsequence-provider.cjs.prod.js +1742 -1678
  3. package/dist/0xsequence-provider.esm.js +1735 -1676
  4. package/dist/declarations/src/client.d.ts +74 -0
  5. package/dist/declarations/src/index.d.ts +3 -1
  6. package/dist/declarations/src/init.d.ts +25 -0
  7. package/dist/declarations/src/provider.d.ts +127 -63
  8. package/dist/declarations/src/signer.d.ts +87 -0
  9. package/dist/declarations/src/transports/mux-transport/mux-message-provider.d.ts +21 -0
  10. package/dist/declarations/src/transports/wallet-request-handler.d.ts +0 -1
  11. package/dist/declarations/src/types.d.ts +25 -9
  12. package/dist/declarations/src/utils/index.d.ts +10 -7
  13. package/dist/declarations/src/utils.d.ts +21 -15
  14. package/package.json +19 -12
  15. package/src/client.ts +423 -0
  16. package/src/extended.ts +4 -0
  17. package/src/index.ts +3 -1
  18. package/src/init.ts +134 -0
  19. package/src/provider.ts +447 -424
  20. package/src/signer.ts +316 -0
  21. package/src/transports/base-wallet-transport.ts +3 -8
  22. package/src/transports/mux-transport/mux-message-provider.ts +86 -2
  23. package/src/transports/proxy-transport/proxy-message-provider.ts +1 -1
  24. package/src/transports/wallet-request-handler.ts +3 -33
  25. package/src/transports/window-transport/window-message-provider.ts +4 -2
  26. package/src/types.ts +41 -10
  27. package/src/utils/index.ts +16 -76
  28. package/src/utils.ts +81 -51
  29. package/dist/declarations/src/extended.d.ts +0 -6
  30. package/dist/declarations/src/wallet.d.ts +0 -105
  31. package/src/wallet.ts +0 -770
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xsequence/provider",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "description": "provider sub-package for Sequence",
5
5
  "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/provider",
6
6
  "source": "src/index.ts",
@@ -11,21 +11,22 @@
11
11
  "dependencies": {
12
12
  "eventemitter2": "^6.4.5",
13
13
  "webextension-polyfill": "^0.10.0",
14
- "@0xsequence/abi": "1.0.5",
15
- "@0xsequence/account": "1.0.5",
16
- "@0xsequence/auth": "1.0.5",
17
- "@0xsequence/core": "1.0.5",
18
- "@0xsequence/migration": "1.0.5",
19
- "@0xsequence/relayer": "1.0.5",
20
- "@0xsequence/network": "1.0.5",
21
- "@0xsequence/utils": "1.0.5",
22
- "@0xsequence/wallet": "1.0.5"
14
+ "@0xsequence/abi": "1.1.0",
15
+ "@0xsequence/account": "1.1.0",
16
+ "@0xsequence/auth": "1.1.0",
17
+ "@0xsequence/relayer": "1.1.0",
18
+ "@0xsequence/migration": "1.1.0",
19
+ "@0xsequence/core": "1.1.0",
20
+ "@0xsequence/network": "1.1.0",
21
+ "@0xsequence/wallet": "1.1.0",
22
+ "@0xsequence/utils": "1.1.0"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "ethers": ">=5.5 < 6"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/webextension-polyfill": "^0.10.0",
29
+ "hardhat": "^2.12.2",
29
30
  "ethers": "^5.7.2"
30
31
  },
31
32
  "files": [
@@ -33,8 +34,14 @@
33
34
  "dist"
34
35
  ],
35
36
  "scripts": {
36
- "test": "pnpm test:file tests/**/*.spec.ts",
37
+ "test": "pnpm test:concurrently 'pnpm test:run'",
38
+ "test:run": "pnpm test:file tests/**/*.spec.ts",
37
39
  "test:file": "NODE_OPTIONS='--loader tsx' mocha --timeout 30000",
38
- "typecheck": "tsc --noEmit"
40
+ "typecheck": "tsc --noEmit",
41
+ "test:concurrently": "concurrently -k --success first 'pnpm start:hardhat1' 'pnpm start:hardhat2'",
42
+ "start:hardhat1": "pnpm start:hardhat1:verbose > /dev/null 2>&1",
43
+ "start:hardhat2": "pnpm start:hardhat2:verbose > /dev/null 2>&1",
44
+ "start:hardhat1:verbose": "hardhat node --config hardhat1.config.js --hostname 0.0.0.0 --port 9595",
45
+ "start:hardhat2:verbose": "hardhat node --config hardhat2.config.js --hostname 0.0.0.0 --port 8595"
39
46
  }
40
47
  }
package/src/client.ts ADDED
@@ -0,0 +1,423 @@
1
+ import { JsonRpcRequest, JsonRpcResponse, NetworkConfig } from "@0xsequence/network"
2
+ import { ConnectDetails, ConnectOptions, ItemStore, MuxMessageProvider, MuxTransportTemplate, OpenWalletIntent, OptionalChainId, OptionalEIP6492, ProviderTransport, WalletEventTypes, WalletSession, isMuxTransportTemplate, isProviderTransport } from "."
3
+ import { commons } from "@0xsequence/core"
4
+ import { TypedData } from "@0xsequence/utils"
5
+ import { toExtended } from "./extended"
6
+ import { ethers } from "ethers"
7
+
8
+
9
+ /**
10
+ * This session class is meant to persist the state of the wallet connection
11
+ * whitin the dapp. This enables the client to retain the wallet address (and some more)
12
+ * even if the user refreshes the page. Otherwise we would have to open the popup again.
13
+ */
14
+ export class SequenceClientSession {
15
+ static readonly SESSION_LOCALSTORE_KEY = '@sequence.session'
16
+
17
+ constructor (
18
+ private store: ItemStore
19
+ ) {}
20
+
21
+ connectedSession(): Required<WalletSession> {
22
+ const session = this.getSession()
23
+
24
+ if (
25
+ session &&
26
+ session.accountAddress &&
27
+ session.walletContext &&
28
+ session.networks
29
+ ) {
30
+ return {
31
+ accountAddress: session.accountAddress!,
32
+ walletContext: session.walletContext!,
33
+ networks: session.networks!
34
+ }
35
+ }
36
+
37
+ throw new Error('Sequence session not connected')
38
+ }
39
+
40
+ hasSession(): boolean {
41
+ return this.getSession()?.accountAddress !== undefined
42
+ }
43
+
44
+ setSession(session: WalletSession) {
45
+ return this.store.setItem(SequenceClientSession.SESSION_LOCALSTORE_KEY, JSON.stringify(session))
46
+ }
47
+
48
+ getSession(): WalletSession | undefined {
49
+ const session = this.store.getItem(SequenceClientSession.SESSION_LOCALSTORE_KEY)
50
+
51
+ if (session) {
52
+ return JSON.parse(session)
53
+ }
54
+
55
+ return undefined
56
+ }
57
+
58
+ async clearSession() {
59
+ return this.store.removeItem(SequenceClientSession.SESSION_LOCALSTORE_KEY)
60
+ }
61
+ }
62
+
63
+ /**
64
+ * The wallet webapp doesn't really care what's the "default chain" for the user.
65
+ * so we don't even bother to send this information to the wallet. Instead, we
66
+ * track it locally using storage, that way the data stays always in sync.
67
+ */
68
+ export class DefaultChainIdTracker {
69
+ static readonly SESSION_CHAIN_ID_KEY = '@sequence.session.defaultChainId'
70
+
71
+ callbacks: ((chainId: number) => void)[] = []
72
+
73
+ constructor(
74
+ private store: ItemStore,
75
+ private startingChainId: number = 1
76
+ ) {
77
+ store.onItemChange(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, (value: string | null) => {
78
+ if (value) {
79
+ const chainId = parseInt(value)
80
+ this.callbacks.forEach(cb => cb(chainId))
81
+ }
82
+ })
83
+ }
84
+
85
+ onDefaultChainIdChanged(callback: (chainId: number) => void) {
86
+ this.callbacks.push(callback)
87
+ return () => {
88
+ this.callbacks = this.callbacks.filter(cb => cb !== callback)
89
+ }
90
+ }
91
+
92
+ setDefaultChainId(chainId: number) {
93
+ if (chainId !== this.getDefaultChainId()) {
94
+ this.store.setItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, chainId.toString())
95
+ }
96
+ }
97
+
98
+ getDefaultChainId(): number {
99
+ const read = this.store.getItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY)
100
+
101
+ if (!read || read.length === 0) {
102
+ return this.startingChainId
103
+ }
104
+
105
+ return parseInt(read)
106
+ }
107
+ }
108
+
109
+ /**
110
+ * This is a wallet client for sequence wallet-webapp. It connects using *some* transport
111
+ * and it allows to perform all sequence specific (or write) operations related to the wallet.
112
+ *
113
+ * It doesn't implement a full ethereum Provider, it doesn't include read-only methods.
114
+ */
115
+ export class SequenceClient {
116
+ private readonly session: SequenceClientSession
117
+ private readonly defaultChainId: DefaultChainIdTracker
118
+ private readonly callbacks: { [K in keyof WalletEventTypes]?: WalletEventTypes[K][] } = {}
119
+
120
+ public readonly transport: ProviderTransport
121
+
122
+ constructor (
123
+ transport: ProviderTransport | MuxTransportTemplate,
124
+ store: ItemStore,
125
+ defaultChainId?: number
126
+ ) {
127
+ if (isMuxTransportTemplate(transport)) {
128
+ this.transport = MuxMessageProvider.new(transport)
129
+ } else if (isProviderTransport(transport)) {
130
+ this.transport = transport
131
+ } else {
132
+ throw new Error('Invalid transport')
133
+ }
134
+
135
+ this.session = new SequenceClientSession(store)
136
+ this.defaultChainId = new DefaultChainIdTracker(store, defaultChainId)
137
+
138
+ this.transport.on('accountsChanged', (accounts: string[]) => {
139
+ if (accounts.length > 1) {
140
+ console.warn('SequenceClient: wallet-webapp returned more than one account')
141
+ }
142
+
143
+ this.callbacks.accountsChanged?.forEach(cb => cb(accounts))
144
+ })
145
+
146
+ this.transport.on('connect', (response: ConnectDetails) => {
147
+ const chainIdHex = ethers.BigNumber.from(this.getChainId()).toHexString()
148
+ this.callbacks.connect?.forEach(cb => cb({
149
+ ...response,
150
+ // Ignore the full connect response
151
+ // use the chainId defined locally
152
+ chainId: chainIdHex
153
+ }))
154
+ })
155
+
156
+ this.transport.on('disconnect', (error) => {
157
+ this.callbacks.disconnect?.forEach(cb => cb(error))
158
+ })
159
+
160
+ this.transport.on('networks', (networks) => {
161
+ this.callbacks.networks?.forEach(cb => cb(networks))
162
+ })
163
+
164
+ this.transport.on('walletContext', (context) => {
165
+ this.callbacks.walletContext?.forEach(cb => cb(context))
166
+ })
167
+
168
+ this.transport.on('open', (info) => {
169
+ this.callbacks.open?.forEach(cb => cb(info))
170
+ })
171
+
172
+ this.transport.on('close', () => {
173
+ this.callbacks.close?.forEach(cb => cb())
174
+ })
175
+
176
+ // We don't listen for the transport chainChanged event
177
+ // instead we handle it locally, so we listen for changes in the store
178
+ this.defaultChainId.onDefaultChainIdChanged((chainId: number) => {
179
+ const chainIdHex = ethers.BigNumber.from(chainId).toHexString()
180
+ this.callbacks.chainChanged?.forEach(cb => cb(chainIdHex))
181
+ })
182
+ }
183
+
184
+ // Callbacks
185
+
186
+ registerCallback<K extends keyof WalletEventTypes>(eventName: K, callback: WalletEventTypes[K]) {
187
+ if (!this.callbacks[eventName]) {
188
+ this.callbacks[eventName] = []
189
+ }
190
+
191
+ this.callbacks[eventName]!.push(callback)
192
+
193
+ return () => {
194
+ this.callbacks[eventName] = this.callbacks[eventName]!.filter(c => c !== callback) as any
195
+ }
196
+ }
197
+
198
+ // Individual callbacks lead to more idiomatic code
199
+
200
+ onOpen(callback: WalletEventTypes['open']) {
201
+ return this.registerCallback('open', callback)
202
+ }
203
+
204
+ onClose(callback: WalletEventTypes['close']) {
205
+ return this.registerCallback('close', callback)
206
+ }
207
+
208
+ onConnect(callback: WalletEventTypes['connect']) {
209
+ return this.registerCallback('connect', callback)
210
+ }
211
+
212
+ onDisconnect(callback: WalletEventTypes['disconnect']) {
213
+ return this.registerCallback('disconnect', callback)
214
+ }
215
+
216
+ onNetworks(callback: WalletEventTypes['networks']) {
217
+ return this.registerCallback('networks', callback)
218
+ }
219
+
220
+ onAccountsChanged(callback: WalletEventTypes['accountsChanged']) {
221
+ return this.registerCallback('accountsChanged', callback)
222
+ }
223
+
224
+ // @deprecated
225
+ onWalletContext(callback: WalletEventTypes['walletContext']) {
226
+ return this.registerCallback('walletContext', callback)
227
+ }
228
+
229
+ onDefaultChainIdChanged(callback: WalletEventTypes['chainChanged']) {
230
+ return this.registerCallback('chainChanged', callback)
231
+ }
232
+
233
+ getChainId(): number {
234
+ return this.defaultChainId.getDefaultChainId()
235
+ }
236
+
237
+ setDefaultChainId(chainId: number) {
238
+ return this.defaultChainId.setDefaultChainId(chainId)
239
+ }
240
+
241
+ // Proxy transport methods
242
+
243
+ async openWallet(path?: string, intent?: OpenWalletIntent) {
244
+ this.transport.openWallet(path, intent, this.getChainId())
245
+ await this.transport.waitUntilOpened()
246
+ return this.isOpened()
247
+ }
248
+
249
+ closeWallet() {
250
+ return this.transport.closeWallet()
251
+ }
252
+
253
+ isOpened(): boolean {
254
+ return this.transport.isOpened()
255
+ }
256
+
257
+ isConnected(): boolean {
258
+ return this.session.hasSession()
259
+ }
260
+
261
+ getSession(): WalletSession | undefined {
262
+ return this.session.getSession()
263
+ }
264
+
265
+ // Basic API
266
+ getAddress(): string {
267
+ const session = this.session.connectedSession()
268
+ return session.accountAddress
269
+ }
270
+
271
+ async connect(options: ConnectOptions): Promise<ConnectDetails> {
272
+ if (options?.authorizeVersion === undefined) {
273
+ // Populate default authorize version if not provided
274
+ options.authorizeVersion = 2
275
+ }
276
+
277
+ if (options?.refresh === true) {
278
+ this.disconnect()
279
+ }
280
+
281
+ if (options) {
282
+ if (options.authorize) {
283
+ if (!options.app) {
284
+ throw new Error(`connecting with 'authorize' option also requires 'app' to be set`)
285
+ }
286
+
287
+ if (options.authorizeVersion === undefined) {
288
+ options.authorizeVersion = 2
289
+ }
290
+ }
291
+ }
292
+
293
+ await this.openWallet(undefined, { type: 'connect', options: { ...options, networkId: this.getChainId() } })
294
+
295
+ const connectDetails = await this.transport.waitUntilConnected().catch((error): ConnectDetails => {
296
+ if (error instanceof Error) {
297
+ return { connected: false, error: error.message }
298
+ } else {
299
+ return { connected: false, error: JSON.stringify(error) }
300
+ }
301
+ })
302
+
303
+ // Normalize chainId into a decimal string
304
+ // TODO: Remove this once wallet-webapp returns chainId as a string
305
+ if (connectDetails.chainId) {
306
+ connectDetails.chainId = ethers.BigNumber.from(connectDetails.chainId).toString()
307
+ }
308
+
309
+ if (connectDetails.connected) {
310
+ if (!connectDetails.session) {
311
+ throw new Error('impossible state, connect response is missing session')
312
+ }
313
+
314
+ this.session.setSession(connectDetails.session)
315
+ }
316
+
317
+ return connectDetails
318
+ }
319
+
320
+ disconnect() {
321
+ if (this.isOpened()) {
322
+ this.closeWallet()
323
+ }
324
+
325
+ return this.session.clearSession()
326
+ }
327
+
328
+ // Higher level API
329
+
330
+ // Working with sendAsync is less idiomatic
331
+ // but transport uses it instead of send, so we wrap it
332
+ send(request: JsonRpcRequest, chainId?: number): Promise<any> {
333
+ return new Promise((resolve, reject) => {
334
+ this.transport.sendAsync(request, (error, response) => {
335
+ if (error) {
336
+ reject(error)
337
+ } else if (response === undefined) {
338
+ reject(new Error(`Got undefined response for request: ${request}`))
339
+ } else if (typeof response === 'object' && response.error) {
340
+ reject(response.error)
341
+ } else if (typeof response === 'object' && response.result) {
342
+ resolve(response.result)
343
+ } else {
344
+ reject(new Error(`Got invalid response for request: ${request}`))
345
+ }
346
+ }, chainId || this.getChainId())
347
+ })
348
+ }
349
+
350
+ async getNetworks(pull?: boolean): Promise<NetworkConfig[]> {
351
+ const connectedSession = this.session.connectedSession()
352
+
353
+ if (pull) {
354
+ connectedSession.networks = await this.send({ method: 'sequence_getNetworks' })
355
+ this.session.setSession(connectedSession)
356
+ }
357
+
358
+ return connectedSession.networks
359
+ }
360
+
361
+ async signMessage(
362
+ message: ethers.BytesLike,
363
+ options?: OptionalEIP6492 & OptionalChainId
364
+ ): Promise<string> {
365
+ const method = options?.eip6492 ? 'sequence_sign' : 'personal_sign'
366
+
367
+ // Address is ignored by the wallet webapp
368
+ return this.send({ method, params: [message, this.getAddress()] }, options?.chainId)
369
+ }
370
+
371
+ async signTypedData(
372
+ typedData: TypedData,
373
+ options?: OptionalEIP6492 & OptionalChainId
374
+ ): Promise<string> {
375
+ const method = options?.eip6492 ? 'sequence_signTypedData_v4' : 'eth_signTypedData_v4'
376
+
377
+ // TODO: Stop using ethers for this, this is the only place where we use it
378
+ // and it makes the client depend on ethers.
379
+ const encoded = ethers.utils._TypedDataEncoder.getPayload(typedData.domain, typedData.types, typedData.message)
380
+
381
+ // The sign typed data will use one of the following chainIds, in order:
382
+ // - The one provided in the options
383
+ // - The one provided in the typedData.domain.chainId
384
+ // - The default chainId
385
+
386
+ return this.send({ method, params: [this.getAddress(), encoded] }, (
387
+ options?.chainId ||
388
+ (
389
+ typedData.domain.chainId &&
390
+ ethers.BigNumber.from(typedData.domain.chainId).toNumber()
391
+ ) ||
392
+ this.getChainId()
393
+ ))
394
+ }
395
+
396
+ async sendTransaction(
397
+ tx: (
398
+ ethers.providers.TransactionRequest[] |
399
+ ethers.providers.TransactionRequest
400
+ ),
401
+ options?: OptionalChainId
402
+ ): Promise<string> {
403
+ const sequenceTxs = Array.isArray(tx) ? tx : [tx]
404
+ const extendedTxs = toExtended(sequenceTxs)
405
+
406
+ return this.send({ method: 'eth_sendTransaction', params: [extendedTxs] }, options?.chainId )
407
+ }
408
+
409
+ async getWalletContext(): Promise<commons.context.VersionedContext> {
410
+ return this.send({ method: 'sequence_getWalletContext' })
411
+ }
412
+
413
+ async getOnchainWalletConfig(options?: OptionalChainId): Promise<commons.config.Config> {
414
+ // NOTICE: sequence_getWalletConfig sends the chainId as a param
415
+ const res = await this.send({ method: 'sequence_getWalletConfig', params: [options?.chainId || this.getChainId()] }, options?.chainId)
416
+ return Array.isArray(res) ? res[0] : res
417
+ }
418
+
419
+ // NOTICE: We are leaving out all the "regular" methods os a tipical
420
+ // JSON RPC Provider (eth_getBlockByNumber, eth_call, etc)
421
+ // wallet-webapp does implement them, but this client is meant to be
422
+ // exclusively used for Sequence specific methods
423
+ }
package/src/extended.ts CHANGED
@@ -21,3 +21,7 @@ export function toExtended(transactions: ethers.providers.TransactionRequest[]):
21
21
  export function fromExtended(transaction: ExtendedTransactionRequest): ethers.providers.TransactionRequest[] {
22
22
  return [transaction, ...(transaction.auxiliary || [])]
23
23
  }
24
+
25
+ export function isExtended(transaction: ethers.providers.TransactionRequest): transaction is ExtendedTransactionRequest {
26
+ return (transaction as any).auxiliary !== undefined
27
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from './transports'
2
2
  export * from './types'
3
- export * from './wallet'
4
3
  export * from './provider'
5
4
  export * from './utils'
5
+ export * from './client'
6
+ export * from './signer'
7
+ export * from './init'
package/src/init.ts ADDED
@@ -0,0 +1,134 @@
1
+ import { CachedProvider, ChainIdLike, JsonRpcRouter, JsonRpcSender, NetworkConfig, allNetworks, exceptionProviderMiddleware, findNetworkConfig, loggingProviderMiddleware } from "@0xsequence/network"
2
+ import { MuxTransportTemplate } from "./transports"
3
+ import { ItemStore, useBestStore } from "./utils"
4
+ import { ethers } from "ethers"
5
+ import { SequenceClient } from "./client"
6
+ import { SequenceProvider } from "./provider"
7
+
8
+
9
+ export interface ProviderConfig {
10
+ // The local storage dependency for the wallet provider, defaults to window.localStorage.
11
+ // For example, this option should be used when using React Native since window.localStorage is not available.
12
+ localStorage?: ItemStore
13
+
14
+ // defaultNetwork is the primary network of a dapp and the default network a
15
+ // provider will communicate. Note: this setting is also configurable from the
16
+ // Wallet constructor's first argument. If both are specified, then they
17
+ // need to match
18
+ defaultNetwork?: ChainIdLike
19
+
20
+ // networks is a configuration list of networks used by the wallet. This list
21
+ // is combined with the network list specified by sequence.js.
22
+ // notice that this can only replace the rpc urls on the dapp side,
23
+ // the networks on wallet-webapp side remain the same.
24
+ //
25
+ // NOTICE: It's not possible to define networks that aren't already
26
+ // defined in sequence.js.
27
+ networks?: Partial<NetworkConfig>[]
28
+
29
+ // transports for dapp to wallet jron-rpc communication
30
+ transports?: MuxTransportTemplate
31
+ }
32
+
33
+ export const DefaultProviderConfig = {
34
+ transports: {
35
+ walletAppURL: 'https://sequence.app',
36
+ windowTransport: { enabled: true },
37
+ proxyTransport: { enabled: false }
38
+ },
39
+
40
+ defaultNetwork: 1,
41
+ }
42
+
43
+ let sequenceWalletProvider: SequenceProvider | undefined
44
+
45
+ export const initWallet = (
46
+ partialConfig?: Partial<ProviderConfig>
47
+ ) => {
48
+ if (sequenceWalletProvider) {
49
+ return sequenceWalletProvider
50
+ }
51
+
52
+ // Combine both the provided config and the default config
53
+ const config = {
54
+ ...DefaultProviderConfig,
55
+ ...partialConfig,
56
+ transports: {
57
+ ...DefaultProviderConfig.transports,
58
+ ...partialConfig?.transports,
59
+ }
60
+ }
61
+
62
+ const rpcProviders: Record<number, ethers.providers.JsonRpcProvider> = {}
63
+
64
+ // Find any new networks that aren't already defined in sequence.js
65
+ // and add them to the list of networks, (they must have a rpcUrl and chainId)
66
+ const newNetworks = (config.networks?.filter((n) => {
67
+ n.rpcUrl !== undefined &&
68
+ n.chainId !== undefined &&
69
+ !allNetworks.find((an) => an.chainId === n.chainId)
70
+ }) ?? []) as NetworkConfig[]
71
+
72
+ // Override any information about the networks using the config
73
+ const combinedNetworks = allNetworks.map((n) => {
74
+ const network = config.networks?.find((cn) => cn.chainId === n.chainId)
75
+ return network ? { ...n, ...network } : n
76
+ }).concat(newNetworks)
77
+
78
+ // This build a "public rpc" on demand, we build them on demand because we don't want to
79
+ // generate a bunch of providers for networks that aren't used.
80
+ const providerForChainId = (chainId: number) => {
81
+ if (!rpcProviders[chainId]) {
82
+ const rpcUrl = combinedNetworks.find((n) => n.chainId === chainId)?.rpcUrl
83
+ if (!rpcUrl) {
84
+ throw new Error(`no rpcUrl found for chainId: ${chainId}`)
85
+ }
86
+
87
+ const baseProvider = new ethers.providers.JsonRpcProvider(rpcUrl)
88
+ const router = new JsonRpcRouter(
89
+ [
90
+ loggingProviderMiddleware,
91
+ exceptionProviderMiddleware,
92
+ new CachedProvider()
93
+ ],
94
+ new JsonRpcSender(baseProvider)
95
+ )
96
+
97
+ rpcProviders[chainId] = new ethers.providers.Web3Provider(router, chainId)
98
+ }
99
+
100
+ return rpcProviders[chainId]
101
+ }
102
+
103
+ // This is the starting default network (as defined by the config)
104
+ // it can be later be changed using `wallet_switchEthereumChain` or some
105
+ // of the other methods on the provider.
106
+ const defaultNetwork = config.defaultNetwork ? findNetworkConfig(combinedNetworks, config.defaultNetwork)?.chainId : undefined
107
+ if (!defaultNetwork && config.defaultNetwork) {
108
+ throw new Error(`defaultNetwork not found for chainId: ${config.defaultNetwork}`)
109
+ }
110
+
111
+ // Generate ItemStore
112
+ const itemStore = config.localStorage || useBestStore()
113
+
114
+ // Create client, provider and return signer
115
+ const client = new SequenceClient(config.transports, itemStore, defaultNetwork)
116
+ sequenceWalletProvider = new SequenceProvider(client, providerForChainId)
117
+
118
+ return sequenceWalletProvider
119
+ }
120
+
121
+ export const unregisterWallet = () => {
122
+ if (!sequenceWalletProvider) return
123
+ sequenceWalletProvider.client.closeWallet()
124
+ sequenceWalletProvider.client.transport.unregister()
125
+ sequenceWalletProvider = undefined
126
+ }
127
+
128
+ export const getWallet = () => {
129
+ if (!sequenceWalletProvider) {
130
+ throw new Error('Wallet has not been initialized, call sequence.initWallet(config) first.')
131
+ }
132
+
133
+ return sequenceWalletProvider
134
+ }