@0xsequence/provider 1.0.5 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/0xsequence-provider.cjs.dev.js +1763 -1678
- package/dist/0xsequence-provider.cjs.prod.js +1763 -1678
- package/dist/0xsequence-provider.esm.js +1756 -1676
- package/dist/declarations/src/client.d.ts +74 -0
- package/dist/declarations/src/index.d.ts +3 -1
- package/dist/declarations/src/init.d.ts +25 -0
- package/dist/declarations/src/provider.d.ts +127 -63
- package/dist/declarations/src/signer.d.ts +87 -0
- package/dist/declarations/src/transports/mux-transport/mux-message-provider.d.ts +21 -0
- package/dist/declarations/src/transports/wallet-request-handler.d.ts +0 -1
- package/dist/declarations/src/types.d.ts +25 -9
- package/dist/declarations/src/utils/index.d.ts +10 -7
- package/dist/declarations/src/utils.d.ts +21 -15
- package/package.json +19 -12
- package/src/client.ts +423 -0
- package/src/extended.ts +4 -0
- package/src/index.ts +3 -1
- package/src/init.ts +134 -0
- package/src/provider.ts +447 -424
- package/src/signer.ts +316 -0
- package/src/transactions.ts +23 -0
- package/src/transports/base-wallet-transport.ts +3 -8
- package/src/transports/mux-transport/mux-message-provider.ts +86 -2
- package/src/transports/proxy-transport/proxy-message-provider.ts +1 -1
- package/src/transports/wallet-request-handler.ts +8 -33
- package/src/transports/window-transport/window-message-provider.ts +4 -2
- package/src/types.ts +41 -10
- package/src/utils/index.ts +16 -76
- package/src/utils.ts +81 -51
- package/dist/declarations/src/extended.d.ts +0 -6
- package/dist/declarations/src/wallet.d.ts +0 -105
- package/src/wallet.ts +0 -770
package/src/signer.ts
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { ethers } from "ethers"
|
|
2
|
+
|
|
3
|
+
import { SequenceProvider, SingleNetworkSequenceProvider } from "./provider"
|
|
4
|
+
import { SequenceClient } from "./client"
|
|
5
|
+
import { commons } from "@0xsequence/core"
|
|
6
|
+
import { ChainIdLike, NetworkConfig } from "@0xsequence/network"
|
|
7
|
+
import { resolveArrayProperties } from "./utils"
|
|
8
|
+
import { WalletUtils } from "./utils/index"
|
|
9
|
+
import { OptionalChainIdLike, OptionalEIP6492 } from "./types"
|
|
10
|
+
|
|
11
|
+
export interface ISequenceSigner extends ethers.Signer {
|
|
12
|
+
getProvider(): SequenceProvider
|
|
13
|
+
getProvider(chainId: ChainIdLike): SingleNetworkSequenceProvider
|
|
14
|
+
getProvider(chainId?: ChainIdLike): SequenceProvider | SingleNetworkSequenceProvider
|
|
15
|
+
|
|
16
|
+
getSigner(): SequenceSigner
|
|
17
|
+
getSigner(chainId: ChainIdLike): SingleNetworkSequenceSigner
|
|
18
|
+
getSigner(chainId?: ChainIdLike): SequenceSigner | SingleNetworkSequenceSigner
|
|
19
|
+
|
|
20
|
+
getWalletConfig(chainId?: ChainIdLike): Promise<commons.config.Config>
|
|
21
|
+
getNetworks(): Promise<NetworkConfig[]>
|
|
22
|
+
|
|
23
|
+
signMessage(
|
|
24
|
+
message: ethers.BytesLike,
|
|
25
|
+
options?: OptionalChainIdLike & OptionalEIP6492
|
|
26
|
+
): Promise<string>
|
|
27
|
+
|
|
28
|
+
signTypedData(
|
|
29
|
+
domain: ethers.TypedDataDomain,
|
|
30
|
+
types: Record<string, Array<ethers.TypedDataField>>,
|
|
31
|
+
message: Record<string, any>,
|
|
32
|
+
options?: OptionalChainIdLike & OptionalEIP6492
|
|
33
|
+
): Promise<string>
|
|
34
|
+
|
|
35
|
+
// sendTransaction takes an unsigned transaction, or list of unsigned transactions, and then has it signed by
|
|
36
|
+
// the signer, and finally sends it to the relayer for submission to an Ethereum network.
|
|
37
|
+
// It supports any kind of transaction, including regular ethers transactions, and Sequence transactions.
|
|
38
|
+
sendTransaction(
|
|
39
|
+
transaction: (
|
|
40
|
+
ethers.utils.Deferrable<ethers.providers.TransactionRequest>[] |
|
|
41
|
+
ethers.utils.Deferrable<ethers.providers.TransactionRequest>
|
|
42
|
+
),
|
|
43
|
+
options?: OptionalChainIdLike
|
|
44
|
+
): Promise<commons.transaction.TransactionResponse>
|
|
45
|
+
|
|
46
|
+
utils: WalletUtils
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class SequenceSigner implements ISequenceSigner {
|
|
50
|
+
private readonly singleNetworkSigners: { [chainId: number]: SingleNetworkSequenceSigner } = {}
|
|
51
|
+
|
|
52
|
+
readonly _isSigner: boolean = true
|
|
53
|
+
readonly _isSequenceSigner: boolean = true
|
|
54
|
+
|
|
55
|
+
get utils(): WalletUtils {
|
|
56
|
+
return this.provider.utils
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
constructor (
|
|
60
|
+
public client: SequenceClient,
|
|
61
|
+
public provider: SequenceProvider
|
|
62
|
+
) { }
|
|
63
|
+
|
|
64
|
+
async getAddress(): Promise<string> {
|
|
65
|
+
return this.client.getAddress()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// This method shouldn't be used directly
|
|
69
|
+
// it exists to maintain compatibility with ethers.Signer
|
|
70
|
+
connect(provider: ethers.providers.Provider): SequenceSigner {
|
|
71
|
+
if (!SequenceProvider.is(provider)) {
|
|
72
|
+
throw new Error("SequenceSigner can only be connected to a SequenceProvider")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return new SequenceSigner(this.client, provider)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getSigner(): SequenceSigner
|
|
79
|
+
getSigner(chainId: ChainIdLike): SingleNetworkSequenceSigner
|
|
80
|
+
getSigner(chainId?: ChainIdLike): SingleNetworkSequenceSigner | SequenceSigner
|
|
81
|
+
|
|
82
|
+
getSigner(chainId?: ChainIdLike): SingleNetworkSequenceSigner | SequenceSigner {
|
|
83
|
+
// The signer for the default network is this signer
|
|
84
|
+
if (!chainId) {
|
|
85
|
+
return this
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const useChainId = this.provider.toChainId(chainId)
|
|
89
|
+
|
|
90
|
+
if (!this.singleNetworkSigners[useChainId]) {
|
|
91
|
+
this.singleNetworkSigners[useChainId] = new SingleNetworkSequenceSigner(this.client, this.provider, useChainId)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return this.singleNetworkSigners[useChainId]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Resolves the chainId to use for the given request. If no chainId is provided,
|
|
99
|
+
* it uses the chainId defined by the client (default chainId). This can be
|
|
100
|
+
* overriden to build a single-network SequenceProvider.
|
|
101
|
+
*/
|
|
102
|
+
protected useChainId(chainId?: ChainIdLike): number {
|
|
103
|
+
return this.provider.toChainId(chainId) || this.client.getChainId()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async signMessage(
|
|
107
|
+
message: ethers.BytesLike,
|
|
108
|
+
options?: OptionalChainIdLike & OptionalEIP6492
|
|
109
|
+
): Promise<string> {
|
|
110
|
+
const { eip6492 = true } = options || {}
|
|
111
|
+
const chainId = this.useChainId(options?.chainId)
|
|
112
|
+
return this.client.signMessage(message, { eip6492, chainId })
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async signTypedData(
|
|
116
|
+
domain: ethers.TypedDataDomain,
|
|
117
|
+
types: Record<string, Array<ethers.TypedDataField>>,
|
|
118
|
+
message: Record<string, any>,
|
|
119
|
+
options?: OptionalChainIdLike & OptionalEIP6492
|
|
120
|
+
): Promise<string> {
|
|
121
|
+
const { eip6492 = true } = options || {}
|
|
122
|
+
const chainId = this.useChainId(options?.chainId)
|
|
123
|
+
return this.client.signTypedData({ domain, types, message }, { eip6492, chainId })
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
getProvider(): SequenceProvider
|
|
127
|
+
getProvider(chainId: ChainIdLike): SingleNetworkSequenceProvider
|
|
128
|
+
getProvider(chainId?: ChainIdLike): SingleNetworkSequenceProvider | SequenceProvider
|
|
129
|
+
|
|
130
|
+
getProvider(chainId?: ChainIdLike): SingleNetworkSequenceProvider | SequenceProvider {
|
|
131
|
+
return this.provider.getProvider(chainId)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async sendTransaction(
|
|
135
|
+
transaction: (
|
|
136
|
+
ethers.utils.Deferrable<ethers.providers.TransactionRequest>[] |
|
|
137
|
+
ethers.utils.Deferrable<ethers.providers.TransactionRequest>
|
|
138
|
+
),
|
|
139
|
+
options?: OptionalChainIdLike
|
|
140
|
+
) {
|
|
141
|
+
const chainId = this.useChainId(options?.chainId)
|
|
142
|
+
const resolved = await resolveArrayProperties(transaction)
|
|
143
|
+
const txHash = await this.client.sendTransaction(resolved, { chainId })
|
|
144
|
+
const provider = this.getProvider(chainId)
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
return await ethers.utils.poll(async () => {
|
|
148
|
+
const tx = await provider.getTransaction(txHash)
|
|
149
|
+
return tx ? provider._wrapTransaction(tx, txHash) : undefined
|
|
150
|
+
}, { onceBlock: provider }) as ethers.providers.TransactionResponse
|
|
151
|
+
} catch (err) {
|
|
152
|
+
err.transactionHash = txHash
|
|
153
|
+
throw err
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getWalletConfig(chainId?: ChainIdLike | undefined): Promise<commons.config.Config> {
|
|
158
|
+
const useChainId = this.useChainId(chainId)
|
|
159
|
+
return this.client.getOnchainWalletConfig({ chainId: useChainId })
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
getNetworks(): Promise<NetworkConfig[]> {
|
|
163
|
+
return this.client.getNetworks()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async getBalance(
|
|
167
|
+
blockTag?: ethers.providers.BlockTag | undefined,
|
|
168
|
+
optionals?: OptionalChainIdLike
|
|
169
|
+
): Promise<ethers.BigNumber> {
|
|
170
|
+
const provider = this.getProvider(optionals?.chainId)
|
|
171
|
+
return provider.getBalance(this.getAddress(), blockTag)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async estimateGas(
|
|
175
|
+
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
|
|
176
|
+
optionals?: OptionalChainIdLike
|
|
177
|
+
): Promise<ethers.BigNumber> {
|
|
178
|
+
return this.getProvider(optionals?.chainId).estimateGas(transaction)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async call(
|
|
182
|
+
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
|
|
183
|
+
blockTag?: ethers.providers.BlockTag | undefined,
|
|
184
|
+
optionals?: OptionalChainIdLike
|
|
185
|
+
): Promise<string> {
|
|
186
|
+
return this.getProvider(optionals?.chainId).call(transaction, blockTag)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
getChainId(): Promise<number> {
|
|
190
|
+
return Promise.resolve(this.client.getChainId())
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async getGasPrice(optionals?: OptionalChainIdLike): Promise<ethers.BigNumber> {
|
|
194
|
+
return this.getProvider(optionals?.chainId).getGasPrice()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async getFeeData(optionals?: OptionalChainIdLike): Promise<ethers.providers.FeeData> {
|
|
198
|
+
return this.getProvider(optionals?.chainId).getFeeData()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async resolveName(name: string): Promise<string> {
|
|
202
|
+
const res = await this.provider.resolveName(name)
|
|
203
|
+
|
|
204
|
+
// For some reason ethers.Signer expects this to return `string`
|
|
205
|
+
// but ethers.providers.Provider expects this to return `string | null`.
|
|
206
|
+
// The signer doesn't have any other source of information, so we'll
|
|
207
|
+
// fail if the provider doesn't return a result.
|
|
208
|
+
if (res === null) {
|
|
209
|
+
throw new Error(`ENS name not found: ${name}`)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return res
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
_checkProvider(_operation?: string | undefined): void {
|
|
216
|
+
// We always have a provider, so this is a noop
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
populateTransaction(
|
|
220
|
+
_transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>
|
|
221
|
+
): Promise<ethers.providers.TransactionRequest> {
|
|
222
|
+
throw new Error("SequenceSigner does not support populateTransaction")
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
checkTransaction(
|
|
226
|
+
_transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>
|
|
227
|
+
): ethers.utils.Deferrable<ethers.providers.TransactionRequest> {
|
|
228
|
+
throw new Error("SequenceSigner does not support checkTransaction")
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
getTransactionCount(_blockTag?: ethers.providers.BlockTag | undefined): Promise<number> {
|
|
232
|
+
// We could try returning the sequence nonce here
|
|
233
|
+
// but we aren't sure how ethers will use this nonce
|
|
234
|
+
throw new Error("SequenceSigner does not support getTransactionCount")
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
signTransaction(_transaction: ethers.utils.Deferrable<commons.transaction.Transactionish>): Promise<string> {
|
|
238
|
+
// We could implement signTransaction/sendTransaction here
|
|
239
|
+
// but first we need a way of serializing these signed transactions
|
|
240
|
+
// and it could lead to more trouble, because the dapp could try to send this transaction
|
|
241
|
+
// using a different provider, which would fail.
|
|
242
|
+
throw new Error("SequenceWallet does not support signTransaction, use sendTransaction instead.")
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
static is(cand: any): cand is SequenceSigner {
|
|
246
|
+
return (
|
|
247
|
+
cand &&
|
|
248
|
+
typeof cand === "object" &&
|
|
249
|
+
cand._isSequenceSigner === true
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* This is the same provider, but it only allows a single network at a time.
|
|
256
|
+
* the network defined by the constructor is the only one that can be used.
|
|
257
|
+
*
|
|
258
|
+
* Attempting to call any method with a different network will throw an error.
|
|
259
|
+
* Attempting to change the network of this provider will throw an error.
|
|
260
|
+
*
|
|
261
|
+
* NOTICE: These networks won't support ENS unless they are the mainnet.
|
|
262
|
+
*/
|
|
263
|
+
export class SingleNetworkSequenceSigner extends SequenceSigner {
|
|
264
|
+
readonly _isSingleNetworkSequenceSigner = true
|
|
265
|
+
|
|
266
|
+
constructor (
|
|
267
|
+
client: SequenceClient,
|
|
268
|
+
provider: SequenceProvider,
|
|
269
|
+
public readonly chainId: ChainIdLike
|
|
270
|
+
) {
|
|
271
|
+
super(client, provider.getProvider(chainId))
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private _useChainId(chainId?: ChainIdLike): number {
|
|
275
|
+
const provided = this.provider.toChainId(chainId)
|
|
276
|
+
|
|
277
|
+
if (provided && provided !== this.chainId) {
|
|
278
|
+
throw new Error(`This signer only supports the network ${this.chainId}, but ${provided} was requested.`)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return provided || this.provider.toChainId(this.chainId)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
protected useChainId(chainId?: ChainIdLike): number {
|
|
285
|
+
return this._useChainId(chainId)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
getChainId(): Promise<number> {
|
|
289
|
+
return Promise.resolve(this.provider.toChainId(this.chainId))
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Override getProvider and getSigner so they always use `useChainId`
|
|
294
|
+
* this way they can't return providers and signers that can switch networks,
|
|
295
|
+
* or that don't match the chainId of this signer.
|
|
296
|
+
*/
|
|
297
|
+
getProvider(chainId?: ChainIdLike): SingleNetworkSequenceProvider {
|
|
298
|
+
return super.getProvider(this._useChainId(chainId))
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
getSigner(chainId?: ChainIdLike | undefined): SingleNetworkSequenceSigner {
|
|
302
|
+
if (this._useChainId(chainId) !== this.chainId) {
|
|
303
|
+
throw new Error(`Unreachable code`)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return this
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
static is(cand: any): cand is SingleNetworkSequenceSigner {
|
|
310
|
+
return (
|
|
311
|
+
cand &&
|
|
312
|
+
typeof cand === "object" &&
|
|
313
|
+
cand._isSingleNetworkSequenceSigner === true
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { commons } from '@0xsequence/core'
|
|
2
|
+
import { ethers } from 'ethers'
|
|
3
|
+
|
|
4
|
+
export function validateTransactionRequest(wallet: string, transaction: commons.transaction.Transactionish) {
|
|
5
|
+
const transactions = commons.transaction.fromTransactionish(wallet, transaction)
|
|
6
|
+
const unwound = commons.transaction.unwind(wallet, transactions)
|
|
7
|
+
unwound.forEach(transaction => validateTransaction(wallet, transaction))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function validateTransaction(wallet: string, transaction: commons.transaction.Transaction) {
|
|
11
|
+
if (transaction.to.toLowerCase() === wallet.toLowerCase()) {
|
|
12
|
+
if (transaction.data) {
|
|
13
|
+
const data = ethers.utils.arrayify(transaction.data)
|
|
14
|
+
if (data.length >= 4) {
|
|
15
|
+
throw new Error('self calls are forbidden')
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (transaction.delegateCall) {
|
|
21
|
+
throw new Error('delegate calls are forbidden')
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -19,7 +19,7 @@ import { logger, sanitizeAlphanumeric, sanitizeHost, sanitizeNumberString } from
|
|
|
19
19
|
import { AuthorizationOptions } from '@0xsequence/auth'
|
|
20
20
|
|
|
21
21
|
import { PROVIDER_OPEN_TIMEOUT } from './base-provider-transport'
|
|
22
|
-
import { isBrowserExtension,
|
|
22
|
+
import { isBrowserExtension, useBestStore } from '../utils'
|
|
23
23
|
import { commons } from '@0xsequence/core'
|
|
24
24
|
|
|
25
25
|
const TRANSPORT_SESSION_LS_KEY = '@sequence.transportSession'
|
|
@@ -375,11 +375,6 @@ export abstract class BaseWalletTransport implements WalletTransport {
|
|
|
375
375
|
// Set connect options on the walletRequestHandler as our primary
|
|
376
376
|
// wallet controller, and fall back to networkId if necessary
|
|
377
377
|
this.walletRequestHandler.setConnectOptions(connectOptions)
|
|
378
|
-
if (connectOptions.networkId) {
|
|
379
|
-
networkId = connectOptions.networkId
|
|
380
|
-
} else if (networkId) {
|
|
381
|
-
connectOptions.networkId = networkId
|
|
382
|
-
}
|
|
383
378
|
} else {
|
|
384
379
|
this.walletRequestHandler.setConnectOptions(undefined)
|
|
385
380
|
}
|
|
@@ -452,11 +447,11 @@ export abstract class BaseWalletTransport implements WalletTransport {
|
|
|
452
447
|
}
|
|
453
448
|
|
|
454
449
|
private saveTransportSession = (session: TransportSession) => {
|
|
455
|
-
|
|
450
|
+
useBestStore().setItem(TRANSPORT_SESSION_LS_KEY, JSON.stringify(session))
|
|
456
451
|
}
|
|
457
452
|
|
|
458
453
|
protected getCachedTransportSession = async (): Promise<TransportSession | null> => {
|
|
459
|
-
const session =
|
|
454
|
+
const session = useBestStore().getItem(TRANSPORT_SESSION_LS_KEY)
|
|
460
455
|
|
|
461
456
|
try {
|
|
462
457
|
return session ? (JSON.parse(session) as TransportSession) : null
|
|
@@ -1,12 +1,62 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ProviderMessage,
|
|
2
|
+
ProviderMessage, ProviderTransport,
|
|
3
3
|
ProviderEventTypes, ProviderMessageRequest, ProviderMessageResponse, WalletSession, OpenWalletIntent, ConnectDetails
|
|
4
4
|
} from '../../types'
|
|
5
5
|
|
|
6
6
|
import { JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network'
|
|
7
|
+
import { ProxyMessageChannelPort, ProxyMessageProvider } from '../proxy-transport'
|
|
8
|
+
import { Runtime } from 'webextension-polyfill'
|
|
9
|
+
import { UnrealMessageProvider } from '../unreal-transport'
|
|
10
|
+
import { ExtensionMessageProvider } from '../extension-transport'
|
|
11
|
+
import { WindowMessageProvider } from '../window-transport'
|
|
7
12
|
|
|
8
|
-
export
|
|
13
|
+
export type MuxTransportTemplate = {
|
|
14
|
+
walletAppURL?: string
|
|
15
|
+
|
|
16
|
+
// WindowMessage transport (optional)
|
|
17
|
+
windowTransport?: {
|
|
18
|
+
enabled: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ProxyMessage transport (optional)
|
|
22
|
+
proxyTransport?: {
|
|
23
|
+
enabled: boolean
|
|
24
|
+
appPort?: ProxyMessageChannelPort
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Extension transport (optional)
|
|
28
|
+
extensionTransport?: {
|
|
29
|
+
enabled: boolean
|
|
30
|
+
runtime: Runtime.Static
|
|
31
|
+
}
|
|
9
32
|
|
|
33
|
+
// Unreal Engine transport (optional)
|
|
34
|
+
unrealTransport?: {
|
|
35
|
+
enabled: boolean
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function isMuxTransportTemplate(obj: any): obj is MuxTransportTemplate {
|
|
40
|
+
return (
|
|
41
|
+
obj &&
|
|
42
|
+
typeof obj === 'object' &&
|
|
43
|
+
(
|
|
44
|
+
(obj.windowTransport && typeof obj.windowTransport === 'object') ||
|
|
45
|
+
(obj.proxyTransport && typeof obj.proxyTransport === 'object') ||
|
|
46
|
+
(obj.extensionTransport && typeof obj.extensionTransport === 'object') ||
|
|
47
|
+
(obj.unrealTransport && typeof obj.unrealTransport === 'object')
|
|
48
|
+
) &&
|
|
49
|
+
(
|
|
50
|
+
// One of the transports must be enabled
|
|
51
|
+
(obj.windowTransport && obj.windowTransport.enabled) ||
|
|
52
|
+
(obj.proxyTransport && obj.proxyTransport.enabled) ||
|
|
53
|
+
(obj.extensionTransport && obj.extensionTransport.enabled) ||
|
|
54
|
+
(obj.unrealTransport && obj.unrealTransport.enabled)
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class MuxMessageProvider implements ProviderTransport {
|
|
10
60
|
private messageProviders: ProviderTransport[]
|
|
11
61
|
private provider: ProviderTransport | undefined
|
|
12
62
|
|
|
@@ -15,6 +65,40 @@ export class MuxMessageProvider implements ProviderTransport {
|
|
|
15
65
|
this.provider = undefined
|
|
16
66
|
}
|
|
17
67
|
|
|
68
|
+
static new(template: MuxTransportTemplate): MuxMessageProvider {
|
|
69
|
+
const muxMessageProvider = new MuxMessageProvider()
|
|
70
|
+
|
|
71
|
+
if (template.windowTransport?.enabled && typeof window === 'object' && template.walletAppURL) {
|
|
72
|
+
const windowMessageProvider = new WindowMessageProvider(template.walletAppURL)
|
|
73
|
+
muxMessageProvider.add(windowMessageProvider)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (template.proxyTransport?.enabled) {
|
|
77
|
+
const proxyMessageProvider = new ProxyMessageProvider(template.proxyTransport.appPort!)
|
|
78
|
+
muxMessageProvider.add(proxyMessageProvider)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (template.extensionTransport?.enabled) {
|
|
82
|
+
const extensionMessageProvider = new ExtensionMessageProvider(template.extensionTransport.runtime)
|
|
83
|
+
muxMessageProvider.add(extensionMessageProvider)
|
|
84
|
+
|
|
85
|
+
// NOTE/REVIEW: see note in mux-message-provider
|
|
86
|
+
//
|
|
87
|
+
// We don't add the extensionMessageProvider here because we don't send requests to it anyways, we seem to
|
|
88
|
+
// send all requests to the WindowMessageProvider anyways. By allowing it, if browser restarts, it will break
|
|
89
|
+
// the entire extension because messageProvider.provider will be undefined. So this is a hack to fix it.
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (template.unrealTransport?.enabled && template.windowTransport && template.walletAppURL) {
|
|
93
|
+
const unrealMessageProvider = new UnrealMessageProvider(template.walletAppURL)
|
|
94
|
+
muxMessageProvider.add(unrealMessageProvider)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
muxMessageProvider.register()
|
|
98
|
+
|
|
99
|
+
return muxMessageProvider
|
|
100
|
+
}
|
|
101
|
+
|
|
18
102
|
add(...messageProviders: ProviderTransport[]) {
|
|
19
103
|
this.messageProviders.push(...messageProviders)
|
|
20
104
|
}
|
|
@@ -13,7 +13,7 @@ export class ProxyMessageProvider extends BaseProviderTransport {
|
|
|
13
13
|
constructor(port: ProxyMessageChannelPort) {
|
|
14
14
|
super()
|
|
15
15
|
this.state = OpenState.CLOSED
|
|
16
|
-
this.port = port
|
|
16
|
+
this.port = port
|
|
17
17
|
if (!port) {
|
|
18
18
|
throw new Error('port argument cannot be empty')
|
|
19
19
|
}
|
|
@@ -7,6 +7,7 @@ import { BigNumber, ethers, providers } from 'ethers'
|
|
|
7
7
|
import { EventEmitter2 as EventEmitter } from 'eventemitter2'
|
|
8
8
|
|
|
9
9
|
import { fromExtended } from '../extended'
|
|
10
|
+
import { validateTransactionRequest } from '../transactions'
|
|
10
11
|
import {
|
|
11
12
|
ProviderMessageRequest,
|
|
12
13
|
ProviderMessageResponse,
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
ProviderEventTypes,
|
|
23
24
|
TypedEventEmitter
|
|
24
25
|
} from '../types'
|
|
25
|
-
import {
|
|
26
|
+
import { prefixEIP191Message } from '../utils'
|
|
26
27
|
|
|
27
28
|
type ExternalProvider = providers.ExternalProvider
|
|
28
29
|
|
|
@@ -64,19 +65,6 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
|
|
|
64
65
|
return this.prompter?.getDefaultChainId() ?? this.networks[0].chainId
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
private findNetworkID(network: string | number) {
|
|
68
|
-
const lnet = typeof network === 'string' ? network.toLowerCase() : network
|
|
69
|
-
const networkId = this.networks.find(n => {
|
|
70
|
-
return n.name.toLowerCase() === lnet || n.chainId.toString() === lnet || n.chainId === lnet
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
if (!networkId) {
|
|
74
|
-
throw new Error(`Network ${network} not found`)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return networkId.chainId
|
|
78
|
-
}
|
|
79
|
-
|
|
80
68
|
async signIn(account: Account | null, options: WalletSignInOptions = {}) {
|
|
81
69
|
this.setAccount(account)
|
|
82
70
|
|
|
@@ -150,25 +138,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
|
|
|
150
138
|
}
|
|
151
139
|
}
|
|
152
140
|
|
|
153
|
-
|
|
154
|
-
switch (typeof options?.networkId) {
|
|
155
|
-
case 'string':
|
|
156
|
-
// First see if it matches the name of a network
|
|
157
|
-
try {
|
|
158
|
-
chainId = this.findNetworkID(options.networkId)
|
|
159
|
-
} catch (err) {
|
|
160
|
-
// If not, try to parse it as a big number
|
|
161
|
-
chainId = getChainId(options.networkId)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
break
|
|
165
|
-
case 'number':
|
|
166
|
-
chainId = options.networkId
|
|
167
|
-
break
|
|
168
|
-
default:
|
|
169
|
-
chainId = this.prompter?.getDefaultChainId() ?? 1
|
|
170
|
-
break
|
|
171
|
-
}
|
|
141
|
+
const chainId: number | string = this.prompter?.getDefaultChainId() ?? 1
|
|
172
142
|
|
|
173
143
|
const connectDetails: ConnectDetails = {
|
|
174
144
|
connected: true,
|
|
@@ -424,6 +394,8 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
|
|
|
424
394
|
return tx
|
|
425
395
|
})
|
|
426
396
|
|
|
397
|
+
validateTransactionRequest(account.address, transactionParams)
|
|
398
|
+
|
|
427
399
|
let txnHash = ''
|
|
428
400
|
if (this.prompter === null) {
|
|
429
401
|
// prompter is null, so we'll send from here
|
|
@@ -452,6 +424,8 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
|
|
|
452
424
|
throw new Error('sender address does not match wallet')
|
|
453
425
|
}
|
|
454
426
|
|
|
427
|
+
validateTransactionRequest(account.address, transaction)
|
|
428
|
+
|
|
455
429
|
if (this.prompter === null) {
|
|
456
430
|
// The eth_signTransaction method expects a `string` return value we instead return a `SignedTransactions` object,
|
|
457
431
|
// this can only be broadcasted using an RPC provider with support for signed Sequence transactions, like this one.
|
|
@@ -740,6 +714,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
|
|
|
740
714
|
}
|
|
741
715
|
|
|
742
716
|
notifyConnect(connectDetails: ConnectDetails, origin?: string) {
|
|
717
|
+
console.log('emit connect', connectDetails)
|
|
743
718
|
this.events.emit('connect', connectDetails)
|
|
744
719
|
if (connectDetails.session?.accountAddress) {
|
|
745
720
|
this.events.emit('accountsChanged', [connectDetails.session?.accountAddress], origin)
|
|
@@ -88,11 +88,13 @@ export class WindowMessageProvider extends BaseProviderTransport {
|
|
|
88
88
|
// when connecting to the wallet, however, this will be verified and enforced
|
|
89
89
|
// on the wallet-side, so if a dapp provides the wrong origin, it will be dropped.
|
|
90
90
|
if (intent.type === 'connect') {
|
|
91
|
-
if (!intent.options) intent.options = {
|
|
91
|
+
if (!intent.options) intent.options = {
|
|
92
|
+
app: window.location.origin
|
|
93
|
+
}
|
|
92
94
|
|
|
93
95
|
// skip setting origin host if we're in an browser extension execution context
|
|
94
96
|
// allow origin that is passed in
|
|
95
|
-
if (!isBrowserExtension() && !isUnityPlugin()) {
|
|
97
|
+
if (!isBrowserExtension() && !isUnityPlugin() && intent.options) {
|
|
96
98
|
intent.options.origin = window.location.origin
|
|
97
99
|
}
|
|
98
100
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ETHAuthProof as AuthETHAuthProof } from '@0xsequence/auth'
|
|
2
2
|
import { commons } from '@0xsequence/core'
|
|
3
|
-
import { JsonRpcHandler, JsonRpcRequest, JsonRpcResponse, NetworkConfig, ProviderRpcError as NetworkProviderRpcError } from '@0xsequence/network'
|
|
3
|
+
import { ChainIdLike, JsonRpcHandler, JsonRpcRequest, JsonRpcResponse, NetworkConfig, ProviderRpcError as NetworkProviderRpcError } from '@0xsequence/network'
|
|
4
4
|
import { TypedData } from '@0xsequence/utils'
|
|
5
5
|
|
|
6
6
|
export interface ProviderTransport extends JsonRpcHandler, ProviderMessageTransport, ProviderMessageRequestHandler {
|
|
@@ -21,6 +21,20 @@ export interface ProviderTransport extends JsonRpcHandler, ProviderMessageTransp
|
|
|
21
21
|
waitUntilConnected(): Promise<ConnectDetails>
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export function isProviderTransport(transport: any): transport is ProviderTransport {
|
|
25
|
+
return (
|
|
26
|
+
transport &&
|
|
27
|
+
typeof transport === 'object' &&
|
|
28
|
+
typeof transport.register === 'function' &&
|
|
29
|
+
typeof transport.unregister === 'function' &&
|
|
30
|
+
typeof transport.openWallet === 'function' &&
|
|
31
|
+
typeof transport.closeWallet === 'function' &&
|
|
32
|
+
typeof transport.isOpened === 'function' &&
|
|
33
|
+
typeof transport.isConnected === 'function' &&
|
|
34
|
+
typeof transport.on === 'function'
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
export interface WalletTransport extends JsonRpcHandler, ProviderMessageTransport, ProviderMessageRequestHandler {
|
|
25
39
|
register(): void
|
|
26
40
|
unregister(): void
|
|
@@ -134,12 +148,8 @@ export enum InitState {
|
|
|
134
148
|
}
|
|
135
149
|
|
|
136
150
|
export interface ConnectOptions {
|
|
137
|
-
/** Specifies the default network a dapp would like to connect to. This field
|
|
138
|
-
* is optional as it can be provided a number of different ways. */
|
|
139
|
-
networkId?: string | number
|
|
140
|
-
|
|
141
151
|
/** app name of the dapp which will be announced to user on connect screen */
|
|
142
|
-
app
|
|
152
|
+
app: string
|
|
143
153
|
|
|
144
154
|
/** custom protocol for auth redirect (unity/unreal) */
|
|
145
155
|
appProtocol?: string
|
|
@@ -171,6 +181,13 @@ export interface ConnectOptions {
|
|
|
171
181
|
settings?: Settings
|
|
172
182
|
}
|
|
173
183
|
|
|
184
|
+
export interface NetworkedConnectOptions extends ConnectOptions {
|
|
185
|
+
/** chainId is the chainId to connect to. If not specified, the default chainId
|
|
186
|
+
* will be used. This does not define a default chain id, it is only used for the connect
|
|
187
|
+
* authorization signature. */
|
|
188
|
+
networkId?: string | number;
|
|
189
|
+
}
|
|
190
|
+
|
|
174
191
|
/** Options to further customize the wallet experience. */
|
|
175
192
|
export interface Settings {
|
|
176
193
|
/** Specify a wallet theme. `light` and `dark` are the main themes, to use other available
|
|
@@ -256,7 +273,7 @@ export interface ConnectDetails {
|
|
|
256
273
|
export type PromptConnectDetails = Pick<ConnectDetails, 'chainId' | 'error' | 'connected' | 'proof' | 'email'>
|
|
257
274
|
|
|
258
275
|
export type OpenWalletIntent =
|
|
259
|
-
| { type: 'connect'; options?:
|
|
276
|
+
| { type: 'connect'; options?: NetworkedConnectOptions }
|
|
260
277
|
| { type: 'openWithOptions'; options?: ConnectOptions }
|
|
261
278
|
| { type: 'jsonRpcRequest'; method: string }
|
|
262
279
|
|
|
@@ -280,9 +297,6 @@ export interface WalletSession {
|
|
|
280
297
|
// Networks in use for the session. The default/dapp network will show
|
|
281
298
|
// up as the first one in the list as the "main chain"
|
|
282
299
|
networks?: NetworkConfig[]
|
|
283
|
-
|
|
284
|
-
// Caching provider responses for things such as account and chainId
|
|
285
|
-
providerCache?: { [key: string]: any }
|
|
286
300
|
}
|
|
287
301
|
|
|
288
302
|
export class ProviderError extends Error {
|
|
@@ -314,3 +328,20 @@ export interface TypedEventEmitter<Events> {
|
|
|
314
328
|
}
|
|
315
329
|
|
|
316
330
|
type Arguments<T> = [T] extends [(...args: infer U) => any] ? U : [T] extends [void] ? [] : [T]
|
|
331
|
+
|
|
332
|
+
export type OptionalChainIdLike = {
|
|
333
|
+
chainId?: ChainIdLike
|
|
334
|
+
} | undefined
|
|
335
|
+
|
|
336
|
+
export type OptionalChainId = {
|
|
337
|
+
chainId?: number
|
|
338
|
+
} | undefined
|
|
339
|
+
|
|
340
|
+
export type OptionalEIP6492 = {
|
|
341
|
+
eip6492?: boolean
|
|
342
|
+
} | undefined
|
|
343
|
+
|
|
344
|
+
// This is required by viem, it expects a provider to have an EIP-1193 compliant `request` attribute.
|
|
345
|
+
export interface EIP1193Provider {
|
|
346
|
+
request: (request: { method: string, params?: Array<any> }) => Promise<any>
|
|
347
|
+
}
|