@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.
- package/CHANGELOG.md +909 -0
- package/README.md +4 -0
- package/dist/0xsequence-provider.cjs.d.ts +1 -0
- package/dist/0xsequence-provider.cjs.dev.js +3540 -0
- package/dist/0xsequence-provider.cjs.js +7 -0
- package/dist/0xsequence-provider.cjs.prod.js +3540 -0
- package/dist/0xsequence-provider.esm.js +3508 -0
- package/dist/declarations/src/commands/index.d.ts +20 -0
- package/dist/declarations/src/index.d.ts +5 -0
- package/dist/declarations/src/provider.d.ts +59 -0
- package/dist/declarations/src/transports/base-provider-transport.d.ts +40 -0
- package/dist/declarations/src/transports/base-wallet-transport.d.ts +38 -0
- package/dist/declarations/src/transports/extension-transport/base-injected-transport.d.ts +18 -0
- package/dist/declarations/src/transports/extension-transport/extension-message-handler.d.ts +12 -0
- package/dist/declarations/src/transports/extension-transport/extension-message-provider.d.ts +11 -0
- package/dist/declarations/src/transports/extension-transport/index.d.ts +3 -0
- package/dist/declarations/src/transports/index.d.ts +7 -0
- package/dist/declarations/src/transports/mux-transport/index.d.ts +1 -0
- package/dist/declarations/src/transports/mux-transport/mux-message-provider.d.ts +22 -0
- package/dist/declarations/src/transports/proxy-transport/index.d.ts +3 -0
- package/dist/declarations/src/transports/proxy-transport/proxy-message-channel.d.ts +15 -0
- package/dist/declarations/src/transports/proxy-transport/proxy-message-handler.d.ts +12 -0
- package/dist/declarations/src/transports/proxy-transport/proxy-message-provider.d.ts +12 -0
- package/dist/declarations/src/transports/wallet-request-handler.d.ts +51 -0
- package/dist/declarations/src/transports/window-transport/index.d.ts +2 -0
- package/dist/declarations/src/transports/window-transport/window-message-handler.d.ts +15 -0
- package/dist/declarations/src/transports/window-transport/window-message-provider.d.ts +13 -0
- package/dist/declarations/src/types.d.ts +170 -0
- package/dist/declarations/src/utils.d.ts +14 -0
- package/dist/declarations/src/wallet.d.ts +99 -0
- package/package.json +37 -0
- package/src/commands/index.ts +146 -0
- package/src/index.ts +5 -0
- package/src/provider.ts +494 -0
- package/src/transports/base-provider-transport.ts +411 -0
- package/src/transports/base-wallet-transport.ts +441 -0
- package/src/transports/extension-transport/base-injected-transport.ts +100 -0
- package/src/transports/extension-transport/extension-message-handler.ts +25 -0
- package/src/transports/extension-transport/extension-message-provider.ts +41 -0
- package/src/transports/extension-transport/index.ts +3 -0
- package/src/transports/index.ts +7 -0
- package/src/transports/mux-transport/index.ts +1 -0
- package/src/transports/mux-transport/mux-message-provider.ts +160 -0
- package/src/transports/proxy-transport/index.ts +3 -0
- package/src/transports/proxy-transport/proxy-message-channel.ts +58 -0
- package/src/transports/proxy-transport/proxy-message-handler.ts +46 -0
- package/src/transports/proxy-transport/proxy-message-provider.ts +81 -0
- package/src/transports/wallet-request-handler.ts +702 -0
- package/src/transports/window-transport/index.ts +2 -0
- package/src/transports/window-transport/window-message-handler.ts +166 -0
- package/src/transports/window-transport/window-message-provider.ts +183 -0
- package/src/types.ts +252 -0
- package/src/utils.ts +76 -0
- 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
|
+
}
|