@0xsequence/provider 2.3.17 → 2.3.18
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.d.ts +2 -2
- package/dist/0xsequence-provider.cjs.js +15 -6
- package/package.json +10 -10
- package/dist/0xsequence-provider.cjs.dev.js +0 -4347
- package/dist/0xsequence-provider.cjs.prod.js +0 -4347
- package/dist/0xsequence-provider.esm.js +0 -4290
- package/dist/declarations/src/analytics.d.ts +0 -13
- package/dist/declarations/src/client.d.ts +0 -92
- package/dist/declarations/src/index.d.ts +0 -9
- package/dist/declarations/src/init.d.ts +0 -36
- package/dist/declarations/src/provider.d.ts +0 -141
- package/dist/declarations/src/signer.d.ts +0 -88
- package/dist/declarations/src/transactions.d.ts +0 -2
- package/dist/declarations/src/transports/base-provider-transport.d.ts +0 -49
- package/dist/declarations/src/transports/base-wallet-transport.d.ts +0 -43
- package/dist/declarations/src/transports/extension-transport/base-injected-transport.d.ts +0 -17
- package/dist/declarations/src/transports/extension-transport/extension-message-handler.d.ts +0 -12
- package/dist/declarations/src/transports/extension-transport/extension-message-provider.d.ts +0 -11
- package/dist/declarations/src/transports/extension-transport/index.d.ts +0 -3
- package/dist/declarations/src/transports/index.d.ts +0 -8
- package/dist/declarations/src/transports/mux-transport/index.d.ts +0 -1
- package/dist/declarations/src/transports/mux-transport/mux-message-provider.d.ts +0 -47
- package/dist/declarations/src/transports/proxy-transport/index.d.ts +0 -3
- package/dist/declarations/src/transports/proxy-transport/proxy-message-channel.d.ts +0 -15
- package/dist/declarations/src/transports/proxy-transport/proxy-message-handler.d.ts +0 -12
- package/dist/declarations/src/transports/proxy-transport/proxy-message-provider.d.ts +0 -12
- package/dist/declarations/src/transports/unreal-transport/index.d.ts +0 -2
- package/dist/declarations/src/transports/unreal-transport/unreal-message-handler.d.ts +0 -14
- package/dist/declarations/src/transports/unreal-transport/unreal-message-provider.d.ts +0 -15
- package/dist/declarations/src/transports/wallet-request-handler.d.ts +0 -64
- package/dist/declarations/src/transports/window-transport/index.d.ts +0 -2
- package/dist/declarations/src/transports/window-transport/window-message-handler.d.ts +0 -15
- package/dist/declarations/src/transports/window-transport/window-message-provider.d.ts +0 -13
- package/dist/declarations/src/types.d.ts +0 -262
- package/dist/declarations/src/utils/index.d.ts +0 -18
- package/dist/declarations/src/utils.d.ts +0 -37
|
@@ -1,4290 +0,0 @@
|
|
|
1
|
-
import { walletContracts } from '@0xsequence/abi';
|
|
2
|
-
import { commons, VERSION } from '@0xsequence/core';
|
|
3
|
-
import { ethers } from 'ethers';
|
|
4
|
-
import { EventEmitter2 } from 'eventemitter2';
|
|
5
|
-
import { logger, encodeMessageDigest, encodeTypedDataDigest, sanitizeNumberString, sanitizeHost, sanitizeAlphanumeric, bigintReplacer, base64EncodeObject, base64DecodeObject, bigintReviver, toHexString } from '@0xsequence/utils';
|
|
6
|
-
import { findSupportedNetwork, ChainId, findNetworkConfig, allNetworks, JsonRpcProvider, loggingProviderMiddleware, exceptionProviderMiddleware, CachedProvider } from '@0xsequence/network';
|
|
7
|
-
import { signAuthorization } from '@0xsequence/auth';
|
|
8
|
-
import { Databeat, isBrowser } from '@databeat/tracker';
|
|
9
|
-
|
|
10
|
-
const PROHIBITED_FUNCTIONS = new Map(['addHook(bytes4,address)', 'clearExtraImageHashes(bytes32[])', 'removeHook(bytes4)', 'setExtraImageHash(bytes32,uint256)', 'updateIPFSRoot(bytes32)', 'updateImageHash(bytes32)', 'updateImageHashAndIPFS(bytes32,bytes32)', 'updateImplementation(address)'].map(signature => [ethers.id(signature).slice(0, 10), signature]));
|
|
11
|
-
function validateTransactionRequest(wallet, transaction) {
|
|
12
|
-
const transactions = commons.transaction.fromTransactionish(wallet, transaction);
|
|
13
|
-
const unwound = commons.transaction.unwind(wallet, transactions);
|
|
14
|
-
unwound.forEach(transaction => validateTransaction(wallet, transaction));
|
|
15
|
-
}
|
|
16
|
-
function validateTransaction(wallet, transaction) {
|
|
17
|
-
if (transaction.to.toLowerCase() === wallet.toLowerCase()) {
|
|
18
|
-
if (transaction.data) {
|
|
19
|
-
const data = ethers.getBytes(transaction.data);
|
|
20
|
-
if (data.length >= 4 && !isCreateContractCall(data)) {
|
|
21
|
-
throw new Error('self calls are forbidden');
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
if (transaction.delegateCall) {
|
|
26
|
-
throw new Error('delegate calls are forbidden');
|
|
27
|
-
}
|
|
28
|
-
if (transaction.data) {
|
|
29
|
-
const data = ethers.hexlify(transaction.data);
|
|
30
|
-
const selector = data.slice(0, 10);
|
|
31
|
-
const signature = PROHIBITED_FUNCTIONS.get(selector);
|
|
32
|
-
if (signature) {
|
|
33
|
-
const name = signature.slice(0, signature.indexOf('('));
|
|
34
|
-
throw new Error(`${name} calls are forbidden`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
function isCreateContractCall(data) {
|
|
39
|
-
const walletInterface = new ethers.Interface(walletContracts.mainModule.abi);
|
|
40
|
-
try {
|
|
41
|
-
walletInterface.decodeFunctionData('createContract', data);
|
|
42
|
-
return true;
|
|
43
|
-
} catch (_unused) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function _extends() {
|
|
49
|
-
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
50
|
-
for (var e = 1; e < arguments.length; e++) {
|
|
51
|
-
var t = arguments[e];
|
|
52
|
-
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
53
|
-
}
|
|
54
|
-
return n;
|
|
55
|
-
}, _extends.apply(null, arguments);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function isProviderTransport(transport) {
|
|
59
|
-
return transport && typeof transport === 'object' && typeof transport.register === 'function' && typeof transport.unregister === 'function' && typeof transport.openWallet === 'function' && typeof transport.closeWallet === 'function' && typeof transport.isOpened === 'function' && typeof transport.isConnected === 'function' && typeof transport.on === 'function';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Older versions of sequence.js will require a JsonRpcResponse result type, but newer versions use raw EIP1193 results
|
|
63
|
-
|
|
64
|
-
// ProviderMessageCallback is used to respond to ProviderMessage requests. The error
|
|
65
|
-
// argument is for exceptions during the execution, and response is the response payload
|
|
66
|
-
// which may contain the result or an error payload from the wallet.
|
|
67
|
-
|
|
68
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
69
|
-
|
|
70
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
71
|
-
class WindowSessionParams extends URLSearchParams {
|
|
72
|
-
static new(init) {
|
|
73
|
-
return new URLSearchParams(init);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
let EventType = /*#__PURE__*/function (EventType) {
|
|
77
|
-
EventType["OPEN"] = "open";
|
|
78
|
-
EventType["CLOSE"] = "close";
|
|
79
|
-
EventType["MESSAGE"] = "message";
|
|
80
|
-
EventType["CONNECT"] = "connect";
|
|
81
|
-
EventType["DISCONNECT"] = "disconnect";
|
|
82
|
-
EventType["ACCOUNTS_CHANGED"] = "accountsChanged";
|
|
83
|
-
EventType["CHAIN_CHANGED"] = "chainChanged";
|
|
84
|
-
EventType["NETWORKS"] = "networks";
|
|
85
|
-
EventType["WALLET_CONTEXT"] = "walletContext";
|
|
86
|
-
EventType["INIT"] = "init";
|
|
87
|
-
EventType["DEBUG"] = "_debug";
|
|
88
|
-
return EventType;
|
|
89
|
-
}({});
|
|
90
|
-
let OpenState = /*#__PURE__*/function (OpenState) {
|
|
91
|
-
OpenState[OpenState["CLOSED"] = 0] = "CLOSED";
|
|
92
|
-
OpenState[OpenState["OPENING"] = 1] = "OPENING";
|
|
93
|
-
OpenState[OpenState["OPENED"] = 2] = "OPENED";
|
|
94
|
-
return OpenState;
|
|
95
|
-
}({});
|
|
96
|
-
let InitState = /*#__PURE__*/function (InitState) {
|
|
97
|
-
InitState[InitState["NIL"] = 0] = "NIL";
|
|
98
|
-
InitState[InitState["SENT_NONCE"] = 1] = "SENT_NONCE";
|
|
99
|
-
InitState[InitState["OK"] = 2] = "OK";
|
|
100
|
-
return InitState;
|
|
101
|
-
}({});
|
|
102
|
-
|
|
103
|
-
/** Options to further customize the wallet experience. */
|
|
104
|
-
|
|
105
|
-
/** light and dark are the main themes, to use other themes in wallet settings,
|
|
106
|
-
* you can use the camel case version of the name in the wallet settings.
|
|
107
|
-
* For example: "Blue Dark" on wallet UI can be passed as "blueDark" */
|
|
108
|
-
|
|
109
|
-
// | 'large'
|
|
110
|
-
|
|
111
|
-
class ProviderError extends Error {
|
|
112
|
-
constructor(message) {
|
|
113
|
-
super(message);
|
|
114
|
-
this.name = 'ProviderError';
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const ErrSignedInRequired = new ProviderError('Wallet is not signed in. Connect a wallet and try again.');
|
|
118
|
-
|
|
119
|
-
// TODO: lets build some nice error handling tools, prob in /utils ...
|
|
120
|
-
|
|
121
|
-
const PROVIDER_OPEN_TIMEOUT = 30000; // in ms
|
|
122
|
-
|
|
123
|
-
let _messageIdx = 0;
|
|
124
|
-
const nextMessageIdx = () => ++_messageIdx;
|
|
125
|
-
class BaseProviderTransport {
|
|
126
|
-
constructor(projectAccessKey) {
|
|
127
|
-
var _this = this;
|
|
128
|
-
this.pendingMessageRequests = [];
|
|
129
|
-
this.responseCallbacks = new Map();
|
|
130
|
-
this.state = void 0;
|
|
131
|
-
this.confirmationOnly = false;
|
|
132
|
-
this.events = new EventEmitter2();
|
|
133
|
-
this.openPayload = void 0;
|
|
134
|
-
this.connectPayload = void 0;
|
|
135
|
-
this.accountsChangedPayload = void 0;
|
|
136
|
-
this.networksPayload = void 0;
|
|
137
|
-
this.walletContextPayload = void 0;
|
|
138
|
-
this._sessionId = void 0;
|
|
139
|
-
this._init = void 0;
|
|
140
|
-
this._registered = void 0;
|
|
141
|
-
this.projectAccessKey = void 0;
|
|
142
|
-
// sendMessageRequest sends a ProviderMessageRequest over the wire to the wallet
|
|
143
|
-
this.sendMessageRequest = async function (message) {
|
|
144
|
-
return new Promise((resolve, reject) => {
|
|
145
|
-
if ((!message.idx || message.idx <= 0) && message.type !== 'init') {
|
|
146
|
-
reject(new Error('message idx not set'));
|
|
147
|
-
}
|
|
148
|
-
const responseCallback = (error, response) => {
|
|
149
|
-
if (error) {
|
|
150
|
-
reject(error);
|
|
151
|
-
} else if (response) {
|
|
152
|
-
resolve(response);
|
|
153
|
-
} else {
|
|
154
|
-
throw new Error('no valid response to return');
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
const idx = message.idx;
|
|
158
|
-
if (!_this.responseCallbacks.get(idx)) {
|
|
159
|
-
_this.responseCallbacks.set(idx, responseCallback);
|
|
160
|
-
} else {
|
|
161
|
-
reject(new Error('duplicate message idx, should never happen'));
|
|
162
|
-
}
|
|
163
|
-
if (!_this.isOpened()) {
|
|
164
|
-
logger.debug('pushing to pending requests', message);
|
|
165
|
-
_this.pendingMessageRequests.push(message);
|
|
166
|
-
} else {
|
|
167
|
-
_this.sendMessage(message);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
};
|
|
171
|
-
this.waitUntilOpened = async function (openTimeout = PROVIDER_OPEN_TIMEOUT) {
|
|
172
|
-
let opened = false;
|
|
173
|
-
return Promise.race([new Promise((_, reject) => {
|
|
174
|
-
const timeout = setTimeout(() => {
|
|
175
|
-
clearTimeout(timeout);
|
|
176
|
-
// only emit close if the timeout wins the race
|
|
177
|
-
if (!opened) {
|
|
178
|
-
_this.state = OpenState.CLOSED;
|
|
179
|
-
_this.events.emit('close', {
|
|
180
|
-
code: 1005,
|
|
181
|
-
message: 'opening wallet timed out'
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
reject(new Error('opening wallet timed out'));
|
|
185
|
-
}, openTimeout);
|
|
186
|
-
}), new Promise(resolve => {
|
|
187
|
-
if (_this.isOpened()) {
|
|
188
|
-
var _this$openPayload;
|
|
189
|
-
opened = true;
|
|
190
|
-
resolve((_this$openPayload = _this.openPayload) == null ? void 0 : _this$openPayload.session);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
_this.events.once('open', openInfo => {
|
|
194
|
-
_this.openPayload = openInfo;
|
|
195
|
-
opened = true;
|
|
196
|
-
resolve(openInfo.session);
|
|
197
|
-
});
|
|
198
|
-
})]);
|
|
199
|
-
};
|
|
200
|
-
this.waitUntilConnected = async function () {
|
|
201
|
-
await _this.waitUntilOpened();
|
|
202
|
-
const connect = new Promise(resolve => {
|
|
203
|
-
if (_this.connectPayload) {
|
|
204
|
-
resolve(_this.connectPayload);
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
_this.events.once('connect', connectDetails => {
|
|
208
|
-
_this.connectPayload = connectDetails;
|
|
209
|
-
resolve(connectDetails);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
const closeWallet = new Promise((_, reject) => {
|
|
213
|
-
_this.events.once('close', error => {
|
|
214
|
-
if (error) {
|
|
215
|
-
reject(new Error(`wallet closed due to ${JSON.stringify(error)}`));
|
|
216
|
-
} else {
|
|
217
|
-
reject(new Error(`user closed the wallet`));
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
return Promise.race([connect, closeWallet]);
|
|
222
|
-
};
|
|
223
|
-
this.state = OpenState.CLOSED;
|
|
224
|
-
this._registered = false;
|
|
225
|
-
this._init = InitState.NIL;
|
|
226
|
-
this.projectAccessKey = projectAccessKey;
|
|
227
|
-
}
|
|
228
|
-
get registered() {
|
|
229
|
-
return this._registered;
|
|
230
|
-
}
|
|
231
|
-
register() {
|
|
232
|
-
throw new Error('abstract method');
|
|
233
|
-
}
|
|
234
|
-
unregister() {
|
|
235
|
-
throw new Error('abstract method');
|
|
236
|
-
}
|
|
237
|
-
openWallet(path, intent, networkId) {
|
|
238
|
-
throw new Error('abstract method');
|
|
239
|
-
}
|
|
240
|
-
closeWallet() {
|
|
241
|
-
throw new Error('abstract method');
|
|
242
|
-
}
|
|
243
|
-
isOpened() {
|
|
244
|
-
return this.registered && this.state === OpenState.OPENED;
|
|
245
|
-
}
|
|
246
|
-
isConnected() {
|
|
247
|
-
var _this$openPayload2;
|
|
248
|
-
// if we're registered, and we have the account details, then we are connected
|
|
249
|
-
const session = (_this$openPayload2 = this.openPayload) == null ? void 0 : _this$openPayload2.session;
|
|
250
|
-
return this.registered && session !== undefined && !!session.accountAddress && session.accountAddress.length === 42 && !!session.networks && session.networks.length > 0;
|
|
251
|
-
}
|
|
252
|
-
async request(request) {
|
|
253
|
-
if (this.state === OpenState.CLOSED) {
|
|
254
|
-
// flag the wallet to auto-close once user submits input. ie.
|
|
255
|
-
// prompting to sign a message or transaction
|
|
256
|
-
this.confirmationOnly = true;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// open/focus the wallet.
|
|
260
|
-
// automatically open the wallet when a provider request makes it here.
|
|
261
|
-
//
|
|
262
|
-
// NOTE: if we're not signed in, then the provider will fail, users must first connect+sign in.
|
|
263
|
-
//
|
|
264
|
-
// TODO: how does this behave with a session has expired?
|
|
265
|
-
this.openWallet(undefined, {
|
|
266
|
-
type: 'jsonRpcRequest',
|
|
267
|
-
method: request.method
|
|
268
|
-
}, request.chainId);
|
|
269
|
-
if (!this.isOpened()) {
|
|
270
|
-
await this.waitUntilOpened(); // will throw on timeout
|
|
271
|
-
}
|
|
272
|
-
const response = await this.sendMessageRequest({
|
|
273
|
-
idx: nextMessageIdx(),
|
|
274
|
-
type: EventType.MESSAGE,
|
|
275
|
-
data: request,
|
|
276
|
-
chainId: request.chainId,
|
|
277
|
-
clientVersion: VERSION,
|
|
278
|
-
projectAccessKey: this.projectAccessKey
|
|
279
|
-
});
|
|
280
|
-
return response.data;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// handleMessage will handle message received from the remote wallet
|
|
284
|
-
handleMessage(message) {
|
|
285
|
-
var _this2 = this;
|
|
286
|
-
// init incoming for initial handshake with transport.
|
|
287
|
-
// always respond to INIT messages, e.g. on popup window reload
|
|
288
|
-
if (message.type === EventType.INIT) {
|
|
289
|
-
logger.debug('MessageProvider, received INIT message', message);
|
|
290
|
-
const {
|
|
291
|
-
nonce
|
|
292
|
-
} = message.data;
|
|
293
|
-
if (!nonce || nonce.length == 0) {
|
|
294
|
-
logger.error('invalid init nonce');
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
this._init = InitState.OK;
|
|
298
|
-
this.sendMessage({
|
|
299
|
-
idx: -1,
|
|
300
|
-
type: EventType.INIT,
|
|
301
|
-
data: {
|
|
302
|
-
sessionId: this._sessionId,
|
|
303
|
-
nonce: nonce
|
|
304
|
-
},
|
|
305
|
-
clientVersion: VERSION
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
if (this._init !== InitState.OK) {
|
|
309
|
-
// if provider is not init'd, then we drop any received messages. the only
|
|
310
|
-
// message we will process is of event type 'init', as our acknowledgement
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// message is either a notification, or its a ProviderMessageResponse
|
|
315
|
-
logger.debug('RECEIVED MESSAGE FROM WALLET', message.idx, message);
|
|
316
|
-
const requestIdx = message.idx;
|
|
317
|
-
const responseCallback = this.responseCallbacks.get(requestIdx);
|
|
318
|
-
if (requestIdx) {
|
|
319
|
-
this.responseCallbacks.delete(requestIdx);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// OPEN response
|
|
323
|
-
//
|
|
324
|
-
// Flip opened flag, and flush the pending queue
|
|
325
|
-
if (message.type === EventType.OPEN && !this.isOpened()) {
|
|
326
|
-
var _message$data, _message$data2;
|
|
327
|
-
if (this._sessionId && this._sessionId !== ((_message$data = message.data) == null ? void 0 : _message$data.sessionId)) {
|
|
328
|
-
logger.debug('open event received from wallet, but does not match sessionId', this._sessionId);
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// check if open error occured due to invalid defaultNetworkId
|
|
333
|
-
if ((_message$data2 = message.data) != null && _message$data2.error) {
|
|
334
|
-
var _message$data3;
|
|
335
|
-
const err = new Error(`opening wallet failed: received ${(_message$data3 = message.data) == null ? void 0 : _message$data3.error}`);
|
|
336
|
-
logger.error(err);
|
|
337
|
-
this.close();
|
|
338
|
-
throw err;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// success!
|
|
342
|
-
this.state = OpenState.OPENED;
|
|
343
|
-
this.openPayload = message.data;
|
|
344
|
-
this.events.emit('open', this.openPayload);
|
|
345
|
-
|
|
346
|
-
// flush pending requests when connected
|
|
347
|
-
if (this.pendingMessageRequests.length !== 0) {
|
|
348
|
-
const pendingMessageRequests = this.pendingMessageRequests.splice(0, this.pendingMessageRequests.length);
|
|
349
|
-
pendingMessageRequests.forEach(async function (pendingMessageRequest) {
|
|
350
|
-
_this2.sendMessage(pendingMessageRequest);
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// MESSAGE resposne
|
|
357
|
-
if (message.type === EventType.MESSAGE) {
|
|
358
|
-
// Require user confirmation, bring up wallet to prompt for input then close
|
|
359
|
-
// TODO: perhaps apply technique like in multicall to queue messages within
|
|
360
|
-
// a period of time, then close the window if responseCallbacks is empty, this is better.
|
|
361
|
-
if (this.confirmationOnly) {
|
|
362
|
-
setTimeout(() => {
|
|
363
|
-
if (this.responseCallbacks.size === 0) {
|
|
364
|
-
this.closeWallet();
|
|
365
|
-
}
|
|
366
|
-
}, 500); // TODO: be smarter about timer as we're processing the response callbacks..
|
|
367
|
-
}
|
|
368
|
-
if (!responseCallback) {
|
|
369
|
-
// NOTE: this would occur if 'idx' isn't set, which should never happen
|
|
370
|
-
// or when we register two handler, or duplicate messages with the same idx are sent,
|
|
371
|
-
// all of which should be prevented prior to getting to this point
|
|
372
|
-
throw new Error('impossible state');
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Callback to original caller
|
|
376
|
-
if (responseCallback) {
|
|
377
|
-
this.events.emit('message', message);
|
|
378
|
-
responseCallback(message.data.error, message);
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// ACCOUNTS_CHANGED -- when a user logs in or out
|
|
384
|
-
if (message.type === EventType.ACCOUNTS_CHANGED) {
|
|
385
|
-
this.accountsChangedPayload = {
|
|
386
|
-
accounts: []
|
|
387
|
-
};
|
|
388
|
-
if (message.data && message.data.length > 0) {
|
|
389
|
-
this.accountsChangedPayload = {
|
|
390
|
-
accounts: [ethers.getAddress(message.data[0])],
|
|
391
|
-
origin: message.origin
|
|
392
|
-
};
|
|
393
|
-
this.events.emit('accountsChanged', this.accountsChangedPayload.accounts, this.accountsChangedPayload.origin);
|
|
394
|
-
} else {
|
|
395
|
-
this.events.emit('accountsChanged', [], message.origin);
|
|
396
|
-
}
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// CHAIN_CHANGED -- when a user changes their default chain
|
|
401
|
-
if (message.type === EventType.CHAIN_CHANGED) {
|
|
402
|
-
this.events.emit('chainChanged', message.data, message.origin);
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// NOTIFY NETWORKS -- when a user connects or logs in
|
|
407
|
-
if (message.type === EventType.NETWORKS) {
|
|
408
|
-
this.networksPayload = message.data;
|
|
409
|
-
this.events.emit('networks', this.networksPayload);
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// NOTIFY WALLET_CONTEXT -- when a user connects or logs in
|
|
414
|
-
if (message.type === EventType.WALLET_CONTEXT) {
|
|
415
|
-
this.walletContextPayload = message.data;
|
|
416
|
-
this.events.emit('walletContext', this.walletContextPayload);
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// NOTIFY CLOSE -- when wallet instructs to close
|
|
421
|
-
if (message.type === EventType.CLOSE) {
|
|
422
|
-
if (this.state !== OpenState.CLOSED) {
|
|
423
|
-
this.close(message.data);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// NOTIFY CONNECT -- when wallet instructs we've connected
|
|
428
|
-
if (message.type === EventType.CONNECT) {
|
|
429
|
-
this.connectPayload = message.data;
|
|
430
|
-
this.events.emit('connect', this.connectPayload);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// NOTIFY DISCONNECT -- when wallet instructs to disconnect
|
|
434
|
-
if (message.type === EventType.DISCONNECT) {
|
|
435
|
-
if (this.isConnected()) {
|
|
436
|
-
this.events.emit('disconnect', message.data, message.origin);
|
|
437
|
-
this.close();
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
sendMessage(message) {
|
|
442
|
-
throw new Error('abstract method');
|
|
443
|
-
}
|
|
444
|
-
on(event, fn) {
|
|
445
|
-
this.events.on(event, fn);
|
|
446
|
-
}
|
|
447
|
-
once(event, fn) {
|
|
448
|
-
this.events.once(event, fn);
|
|
449
|
-
}
|
|
450
|
-
emit(event, ...args) {
|
|
451
|
-
return this.events.emit(event, ...args);
|
|
452
|
-
}
|
|
453
|
-
close(error) {
|
|
454
|
-
if (this.state === OpenState.CLOSED) return;
|
|
455
|
-
this.state = OpenState.CLOSED;
|
|
456
|
-
this.confirmationOnly = false;
|
|
457
|
-
this._sessionId = undefined;
|
|
458
|
-
logger.info('closing wallet and flushing!');
|
|
459
|
-
|
|
460
|
-
// flush pending requests and return error to all callbacks
|
|
461
|
-
this.pendingMessageRequests.length = 0;
|
|
462
|
-
this.responseCallbacks.forEach(responseCallback => {
|
|
463
|
-
responseCallback(_extends({}, new Error('wallet closed'), {
|
|
464
|
-
code: 4001
|
|
465
|
-
}));
|
|
466
|
-
});
|
|
467
|
-
this.responseCallbacks.clear();
|
|
468
|
-
this.connectPayload = undefined;
|
|
469
|
-
this.openPayload = undefined;
|
|
470
|
-
this.accountsChangedPayload = undefined;
|
|
471
|
-
this.networksPayload = undefined;
|
|
472
|
-
this.walletContextPayload = undefined;
|
|
473
|
-
this.events.emit('close', error);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function messageIsExemptFromEIP191Prefix(message) {
|
|
478
|
-
return EIP_191_PREFIX_EXCEPTIONS.some(e => e.predicate(message));
|
|
479
|
-
}
|
|
480
|
-
const EIP_191_PREFIX_EXCEPTIONS = [
|
|
481
|
-
// NOTE: Decentraland does not support 191 correctly.
|
|
482
|
-
{
|
|
483
|
-
name: 'Decentraland Exception',
|
|
484
|
-
predicate: isDecentralandLoginMessage
|
|
485
|
-
},
|
|
486
|
-
// NOTE: 0x v3 does not support 191 correctly.
|
|
487
|
-
// See https://gov.0x.org/t/zeip-proposal-fix-v3-eip-191-non-compliance-when-validating-eip-1271-signatures/3396 for more info.
|
|
488
|
-
{
|
|
489
|
-
name: '0x v3 Exception',
|
|
490
|
-
predicate: isZeroExV3Order
|
|
491
|
-
}];
|
|
492
|
-
const DCL_REGEX = /^Decentraland Login\nEphemeral address: 0x[a-fA-F0-9]{40}\nExpiration: (\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)((-(\d{2}):(\d{2})|Z)?)$/;
|
|
493
|
-
function isDecentralandLoginMessage(bytes) {
|
|
494
|
-
try {
|
|
495
|
-
const stringified = ethers.toUtf8String(bytes);
|
|
496
|
-
return DCL_REGEX.test(stringified);
|
|
497
|
-
} catch (_unused) {
|
|
498
|
-
return false;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// try to interpret bytes as abi-encoded 0x v3 OrderWithHash -
|
|
503
|
-
// see https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md
|
|
504
|
-
function isZeroExV3Order(bytes) {
|
|
505
|
-
const abi = new ethers.Interface(ZeroXV3EIP1271OrderWithHashAbi);
|
|
506
|
-
try {
|
|
507
|
-
abi.decodeFunctionData('OrderWithHash', bytes);
|
|
508
|
-
return true;
|
|
509
|
-
} catch (err) {
|
|
510
|
-
// failed to decode ABI, so it's not a v3 order.
|
|
511
|
-
return false;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
const ZeroXV3EIP1271OrderWithHashAbi = [{
|
|
515
|
-
inputs: [{
|
|
516
|
-
components: [{
|
|
517
|
-
internalType: 'address',
|
|
518
|
-
name: 'makerAddress',
|
|
519
|
-
type: 'address'
|
|
520
|
-
}, {
|
|
521
|
-
internalType: 'address',
|
|
522
|
-
name: 'takerAddress',
|
|
523
|
-
type: 'address'
|
|
524
|
-
}, {
|
|
525
|
-
internalType: 'address',
|
|
526
|
-
name: 'feeRecipientAddress',
|
|
527
|
-
type: 'address'
|
|
528
|
-
}, {
|
|
529
|
-
internalType: 'address',
|
|
530
|
-
name: 'senderAddress',
|
|
531
|
-
type: 'address'
|
|
532
|
-
}, {
|
|
533
|
-
internalType: 'uint256',
|
|
534
|
-
name: 'makerAssetAmount',
|
|
535
|
-
type: 'uint256'
|
|
536
|
-
}, {
|
|
537
|
-
internalType: 'uint256',
|
|
538
|
-
name: 'takerAssetAmount',
|
|
539
|
-
type: 'uint256'
|
|
540
|
-
}, {
|
|
541
|
-
internalType: 'uint256',
|
|
542
|
-
name: 'makerFee',
|
|
543
|
-
type: 'uint256'
|
|
544
|
-
}, {
|
|
545
|
-
internalType: 'uint256',
|
|
546
|
-
name: 'takerFee',
|
|
547
|
-
type: 'uint256'
|
|
548
|
-
}, {
|
|
549
|
-
internalType: 'uint256',
|
|
550
|
-
name: 'expirationTimeSeconds',
|
|
551
|
-
type: 'uint256'
|
|
552
|
-
}, {
|
|
553
|
-
internalType: 'uint256',
|
|
554
|
-
name: 'salt',
|
|
555
|
-
type: 'uint256'
|
|
556
|
-
}, {
|
|
557
|
-
internalType: 'bytes',
|
|
558
|
-
name: 'makerAssetData',
|
|
559
|
-
type: 'bytes'
|
|
560
|
-
}, {
|
|
561
|
-
internalType: 'bytes',
|
|
562
|
-
name: 'takerAssetData',
|
|
563
|
-
type: 'bytes'
|
|
564
|
-
}, {
|
|
565
|
-
internalType: 'bytes',
|
|
566
|
-
name: 'makerFeeAssetData',
|
|
567
|
-
type: 'bytes'
|
|
568
|
-
}, {
|
|
569
|
-
internalType: 'bytes',
|
|
570
|
-
name: 'takerFeeAssetData',
|
|
571
|
-
type: 'bytes'
|
|
572
|
-
}],
|
|
573
|
-
internalType: 'struct IEIP1271Data.Order',
|
|
574
|
-
name: 'order',
|
|
575
|
-
type: 'tuple'
|
|
576
|
-
}, {
|
|
577
|
-
internalType: 'bytes32',
|
|
578
|
-
name: 'orderHash',
|
|
579
|
-
type: 'bytes32'
|
|
580
|
-
}],
|
|
581
|
-
name: 'OrderWithHash',
|
|
582
|
-
outputs: [],
|
|
583
|
-
stateMutability: 'pure',
|
|
584
|
-
type: 'function'
|
|
585
|
-
}];
|
|
586
|
-
|
|
587
|
-
const eip191prefix = ethers.toUtf8Bytes('\x19Ethereum Signed Message:\n');
|
|
588
|
-
const messageToBytes = message => {
|
|
589
|
-
if (ethers.isBytesLike(message)) {
|
|
590
|
-
return ethers.getBytes(message);
|
|
591
|
-
}
|
|
592
|
-
return ethers.toUtf8Bytes(message);
|
|
593
|
-
};
|
|
594
|
-
const prefixEIP191Message = message => {
|
|
595
|
-
const messageBytes = messageToBytes(message);
|
|
596
|
-
if (messageIsExemptFromEIP191Prefix(messageBytes)) {
|
|
597
|
-
return messageBytes;
|
|
598
|
-
} else {
|
|
599
|
-
return ethers.getBytes(ethers.concat([eip191prefix, ethers.toUtf8Bytes(String(messageBytes.length)), messageBytes]));
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
const trimEIP191Prefix = prefixedMessage => {
|
|
603
|
-
// If the message is not prefixed, we return the message as is.
|
|
604
|
-
if (JSON.stringify(prefixedMessage.slice(0, eip191prefix.length)) !== JSON.stringify(eip191prefix)) {
|
|
605
|
-
return prefixedMessage;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// We have two parts to remove.
|
|
609
|
-
// First is the EIP-191 prefix.
|
|
610
|
-
const ethereumSignedMessagePartSlicedArray = prefixedMessage.slice(eip191prefix.length);
|
|
611
|
-
|
|
612
|
-
// Second is the digits added which represent length of the message without the prefix
|
|
613
|
-
// and we need to find the prefix that will match this.
|
|
614
|
-
// Here first we take the max prefix char length, and check if as a number it is bigger
|
|
615
|
-
// than the length of the message (since prefix is added to represent length of original message),
|
|
616
|
-
// if it is we remove 1 from char length, if not we keep the max prefix char length.
|
|
617
|
-
// As an example for the case where , if the message is 123456789, the expected prefix char is 9, with starting value 9123456789
|
|
618
|
-
// the char length of the total message with the prefix is 10, so the max prefix char length we start is 2 from [1,0], and as a number 10, it is longer
|
|
619
|
-
// than the length of the message after removing prefix (10 - 2 = 8), so we slice 1 char less, which is 9, and we get the correct prefix.
|
|
620
|
-
const maxPrefixCharLength = String(ethereumSignedMessagePartSlicedArray.length).length;
|
|
621
|
-
let prefixCharLenght;
|
|
622
|
-
let prefixAsNumber;
|
|
623
|
-
try {
|
|
624
|
-
prefixAsNumber = Number(ethers.toUtf8String(ethereumSignedMessagePartSlicedArray.slice(0, maxPrefixCharLength)));
|
|
625
|
-
} catch (_unused) {
|
|
626
|
-
prefixAsNumber = Number(ethers.hexlify(ethereumSignedMessagePartSlicedArray.slice(0, maxPrefixCharLength)));
|
|
627
|
-
}
|
|
628
|
-
if (prefixAsNumber > ethereumSignedMessagePartSlicedArray.length || !Number.isInteger(prefixAsNumber)) {
|
|
629
|
-
prefixCharLenght = maxPrefixCharLength - 1;
|
|
630
|
-
} else {
|
|
631
|
-
prefixCharLenght = maxPrefixCharLength;
|
|
632
|
-
}
|
|
633
|
-
const prefixRevertedMessage = ethereumSignedMessagePartSlicedArray.slice(prefixCharLenght);
|
|
634
|
-
return prefixRevertedMessage;
|
|
635
|
-
};
|
|
636
|
-
const isValidSignature = async (address, digest, sig, provider) => {
|
|
637
|
-
const reader = new commons.reader.OnChainReader(provider);
|
|
638
|
-
return reader.isValidSignature(address, digest, sig);
|
|
639
|
-
};
|
|
640
|
-
|
|
641
|
-
// Verify message signature
|
|
642
|
-
const isValidMessageSignature = async (address, message, signature, provider) => {
|
|
643
|
-
const prefixed = prefixEIP191Message(message);
|
|
644
|
-
const digest = encodeMessageDigest(prefixed);
|
|
645
|
-
return isValidSignature(address, digest, signature, provider);
|
|
646
|
-
};
|
|
647
|
-
|
|
648
|
-
// Verify typedData signature
|
|
649
|
-
const isValidTypedDataSignature = (address, typedData, signature, provider) => {
|
|
650
|
-
return isValidSignature(address, encodeTypedDataDigest(typedData), signature, provider);
|
|
651
|
-
};
|
|
652
|
-
const isBrowserExtension = () => window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:';
|
|
653
|
-
const isUnityPlugin = () => !!navigator.userAgent.match(/UnitySequence/i);
|
|
654
|
-
|
|
655
|
-
// /**
|
|
656
|
-
// * Returns the status of a signer's wallet on given chain by checking wallet deployment and config status
|
|
657
|
-
// *
|
|
658
|
-
// * @param {Status} of the wallet
|
|
659
|
-
// */
|
|
660
|
-
const isWalletUpToDate = status => {
|
|
661
|
-
return status.onChain.deployed && status.fullyMigrated;
|
|
662
|
-
};
|
|
663
|
-
class MemoryItemStore {
|
|
664
|
-
constructor() {
|
|
665
|
-
this.callbacks = [];
|
|
666
|
-
this.store = {};
|
|
667
|
-
}
|
|
668
|
-
getItem(key) {
|
|
669
|
-
return this.store[key] || null;
|
|
670
|
-
}
|
|
671
|
-
setItem(key, value) {
|
|
672
|
-
this.store[key] = value;
|
|
673
|
-
this.callbacks.filter(c => c.key === key).forEach(c => c.cb(value));
|
|
674
|
-
}
|
|
675
|
-
removeItem(key) {
|
|
676
|
-
delete this.store[key];
|
|
677
|
-
}
|
|
678
|
-
onItemChange(key, cb) {
|
|
679
|
-
this.callbacks.push({
|
|
680
|
-
key,
|
|
681
|
-
cb
|
|
682
|
-
});
|
|
683
|
-
return () => {
|
|
684
|
-
this.callbacks = this.callbacks.filter(c => c.cb !== cb);
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
class LocalStorage {
|
|
689
|
-
static isAvailable() {
|
|
690
|
-
return typeof window === 'object' && typeof window.localStorage === 'object';
|
|
691
|
-
}
|
|
692
|
-
constructor() {
|
|
693
|
-
this.callbacks = [];
|
|
694
|
-
if (!LocalStorage.isAvailable()) {
|
|
695
|
-
throw new Error('LocalStorage is not available');
|
|
696
|
-
}
|
|
697
|
-
window.addEventListener('storage', e => {
|
|
698
|
-
const {
|
|
699
|
-
key
|
|
700
|
-
} = e;
|
|
701
|
-
const cb = this.callbacks.filter(c => c.key === key);
|
|
702
|
-
cb.forEach(c => c.cb(this.getItem(key)));
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
getItem(key) {
|
|
706
|
-
return window.localStorage.getItem(key);
|
|
707
|
-
}
|
|
708
|
-
setItem(key, value) {
|
|
709
|
-
window.localStorage.setItem(key, value);
|
|
710
|
-
|
|
711
|
-
// Trigger callbacks
|
|
712
|
-
// NOTICE: the event is not triggered on the same window
|
|
713
|
-
this.callbacks.filter(c => c.key === key).forEach(c => c.cb(value));
|
|
714
|
-
}
|
|
715
|
-
removeItem(key) {
|
|
716
|
-
window.localStorage.removeItem(key);
|
|
717
|
-
|
|
718
|
-
// Trigger callbacks
|
|
719
|
-
// NOTICE: the event is not triggered on the same window
|
|
720
|
-
this.callbacks.filter(c => c.key === key).forEach(c => c.cb(null));
|
|
721
|
-
}
|
|
722
|
-
onItemChange(key, cb) {
|
|
723
|
-
this.callbacks.push({
|
|
724
|
-
key,
|
|
725
|
-
cb
|
|
726
|
-
});
|
|
727
|
-
return () => {
|
|
728
|
-
this.callbacks = this.callbacks.filter(c => c.cb !== cb);
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
function useBestStore() {
|
|
733
|
-
if (LocalStorage.isAvailable()) {
|
|
734
|
-
return new LocalStorage();
|
|
735
|
-
}
|
|
736
|
-
return new MemoryItemStore();
|
|
737
|
-
}
|
|
738
|
-
async function resolveArrayProperties(object) {
|
|
739
|
-
if (Array.isArray(object)) {
|
|
740
|
-
// T must include array type
|
|
741
|
-
return Promise.all(object.map(o => ethers.resolveProperties(o)));
|
|
742
|
-
}
|
|
743
|
-
return ethers.resolveProperties(object);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
const TRANSPORT_SESSION_LS_KEY = '@sequence.transportSession';
|
|
747
|
-
class BaseWalletTransport {
|
|
748
|
-
constructor(walletRequestHandler) {
|
|
749
|
-
var _this = this;
|
|
750
|
-
this.walletRequestHandler = void 0;
|
|
751
|
-
this._sessionId = void 0;
|
|
752
|
-
this._registered = void 0;
|
|
753
|
-
this._init = void 0;
|
|
754
|
-
this._initNonce = void 0;
|
|
755
|
-
this._initCallback = void 0;
|
|
756
|
-
// appOrigin identifies the dapp's origin which opened the app. A transport
|
|
757
|
-
// will auto-detect and set this value if it can. This is determined
|
|
758
|
-
// as the parent app/window which opened the wallet.
|
|
759
|
-
this.appOrigin = void 0;
|
|
760
|
-
this.handleMessage = async function (message) {
|
|
761
|
-
const request = message;
|
|
762
|
-
|
|
763
|
-
// ensure initial handshake is complete before accepting
|
|
764
|
-
// other kinds of messages.
|
|
765
|
-
if (_this._init !== InitState.OK) {
|
|
766
|
-
if (request.type === EventType.INIT) {
|
|
767
|
-
if (_this.isValidInitAck(message)) {
|
|
768
|
-
// successful init
|
|
769
|
-
if (_this._initCallback) _this._initCallback();
|
|
770
|
-
} else {
|
|
771
|
-
// failed init
|
|
772
|
-
if (_this._initCallback) _this._initCallback('invalid init');
|
|
773
|
-
return;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// ensure signer is ready to handle requests
|
|
780
|
-
// if (this.walletRequestHandler.getSigner() === undefined) {
|
|
781
|
-
// await this.walletRequestHandler.signerReady()
|
|
782
|
-
// }
|
|
783
|
-
|
|
784
|
-
// handle request
|
|
785
|
-
switch (request.type) {
|
|
786
|
-
case EventType.OPEN:
|
|
787
|
-
{
|
|
788
|
-
if (_this._init !== InitState.OK) return;
|
|
789
|
-
const session = {
|
|
790
|
-
sessionId: request.data.sessionId,
|
|
791
|
-
intent: request.data.intent,
|
|
792
|
-
networkId: request.data.networkId
|
|
793
|
-
};
|
|
794
|
-
await _this.open(session);
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
case EventType.CLOSE:
|
|
798
|
-
{
|
|
799
|
-
if (_this._init !== InitState.OK) return;
|
|
800
|
-
// noop. just here to capture the message so event emitters may be notified
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
case EventType.MESSAGE:
|
|
804
|
-
{
|
|
805
|
-
const response = await _this.walletRequestHandler.sendMessageRequest(request);
|
|
806
|
-
_this.sendMessage(response);
|
|
807
|
-
if (response.data.error) ;
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
default:
|
|
811
|
-
{
|
|
812
|
-
logger.error(`unexpected payload type ${request.type}`);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
};
|
|
816
|
-
// sendMessageRequest sends a ProviderMessageRequest to the wallet post-message transport
|
|
817
|
-
this.sendMessageRequest = async function (message) {
|
|
818
|
-
return _this.walletRequestHandler.sendMessageRequest(message);
|
|
819
|
-
};
|
|
820
|
-
this.open = async function ({
|
|
821
|
-
sessionId,
|
|
822
|
-
intent,
|
|
823
|
-
networkId
|
|
824
|
-
}) {
|
|
825
|
-
if (sessionId) {
|
|
826
|
-
_this._sessionId = sanitizeNumberString(sessionId);
|
|
827
|
-
// persist transport session in localstorage for restoring after redirect/reload
|
|
828
|
-
_this.saveTransportSession({
|
|
829
|
-
sessionId,
|
|
830
|
-
intent,
|
|
831
|
-
networkId
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
_this.walletRequestHandler.setOpenIntent(intent);
|
|
835
|
-
|
|
836
|
-
// init handshake for certain transports, before we can open the communication.
|
|
837
|
-
//
|
|
838
|
-
// for example, with the window-transport, we have to exchange messages to determine the
|
|
839
|
-
// origin host of the dapp.
|
|
840
|
-
await _this.init();
|
|
841
|
-
|
|
842
|
-
// determine chainId from networkId (string or number)
|
|
843
|
-
let chainId = undefined;
|
|
844
|
-
try {
|
|
845
|
-
if (networkId) {
|
|
846
|
-
const network = findSupportedNetwork(networkId);
|
|
847
|
-
if (network) {
|
|
848
|
-
chainId = network.chainId;
|
|
849
|
-
} else {
|
|
850
|
-
throw new Error(`unknown network ${networkId}`);
|
|
851
|
-
}
|
|
852
|
-
} else {
|
|
853
|
-
// if not provided, use defaultChainId
|
|
854
|
-
chainId = _this.walletRequestHandler.defaultChainId();
|
|
855
|
-
}
|
|
856
|
-
} catch (err) {
|
|
857
|
-
console.error(err);
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
// Prepare connect options from intent
|
|
861
|
-
if (intent && intent.type === 'connect' && intent.options) {
|
|
862
|
-
const connectOptions = intent.options;
|
|
863
|
-
const authorizeOptions = connectOptions; // overlapping types
|
|
864
|
-
|
|
865
|
-
// Sanity/integrity check the intent payload, and set authorization origin
|
|
866
|
-
// if its been determined as part of the init handshake from earlier.
|
|
867
|
-
if (_this.appOrigin && authorizeOptions != null && authorizeOptions.origin) {
|
|
868
|
-
if (!isBrowserExtension()) {
|
|
869
|
-
if (authorizeOptions.origin !== _this.appOrigin) {
|
|
870
|
-
throw new Error('origin is invalid');
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
} else if (!_this.appOrigin && authorizeOptions != null && authorizeOptions.origin) {
|
|
874
|
-
// ie. when we can't determine the origin in our transport, but dapp provides it to us.
|
|
875
|
-
// we just sanitize the origin host.
|
|
876
|
-
connectOptions.origin = sanitizeHost(authorizeOptions.origin);
|
|
877
|
-
} else if (_this.appOrigin) {
|
|
878
|
-
// ie. when we auto-determine the origin such as in window-transport
|
|
879
|
-
connectOptions.origin = _this.appOrigin;
|
|
880
|
-
}
|
|
881
|
-
if (connectOptions.app) {
|
|
882
|
-
connectOptions.app = sanitizeAlphanumeric(connectOptions.app);
|
|
883
|
-
}
|
|
884
|
-
if (connectOptions.networkId) {
|
|
885
|
-
networkId = connectOptions.networkId;
|
|
886
|
-
} else if (networkId) {
|
|
887
|
-
connectOptions.networkId = networkId;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
// Set connect options on the walletRequestHandler as our primary
|
|
891
|
-
// wallet controller, and fall back to networkId if necessary
|
|
892
|
-
_this.walletRequestHandler.setConnectOptions(connectOptions);
|
|
893
|
-
} else {
|
|
894
|
-
_this.walletRequestHandler.setConnectOptions(undefined);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// ensure signer is ready
|
|
898
|
-
await _this.walletRequestHandler.getAccount();
|
|
899
|
-
|
|
900
|
-
// Notify open and proceed to prompt for connection if intended
|
|
901
|
-
if (!(await _this.walletRequestHandler.isSignedIn())) {
|
|
902
|
-
// open wallet without a specific connected chainId, as the user is not signed in
|
|
903
|
-
_this.notifyOpen({
|
|
904
|
-
sessionId: _this._sessionId
|
|
905
|
-
});
|
|
906
|
-
return true;
|
|
907
|
-
} else {
|
|
908
|
-
// prompt user with a connect request. the options will be used as previously set above.
|
|
909
|
-
// upon success, the walletRequestHandler will notify the dapp with the ConnectDetails.
|
|
910
|
-
// upon cancellation by user, the walletRequestHandler will throw an error
|
|
911
|
-
|
|
912
|
-
if (intent && intent.type === 'connect') {
|
|
913
|
-
// Failed to set default network on open
|
|
914
|
-
// Fail silently here so we can continue with connect flow and ask
|
|
915
|
-
// user to connect on a different network if necessary
|
|
916
|
-
if (!chainId || chainId <= 0) {
|
|
917
|
-
console.log('Failed to set default network on open');
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
// notify wallet is opened, without session details
|
|
921
|
-
_this.notifyOpen({
|
|
922
|
-
sessionId: _this._sessionId
|
|
923
|
-
});
|
|
924
|
-
try {
|
|
925
|
-
const connectDetails = await _this.walletRequestHandler.promptConnect(intent.options);
|
|
926
|
-
if (connectDetails.connected) {
|
|
927
|
-
_this.walletRequestHandler.notifyConnect(connectDetails);
|
|
928
|
-
}
|
|
929
|
-
} catch (err) {
|
|
930
|
-
logger.warn('promptConnect not connected:', err);
|
|
931
|
-
} finally {
|
|
932
|
-
// auto-close by default, unless intent is to keep open
|
|
933
|
-
if (!intent.options || intent.options.keepWalletOpened !== true) {
|
|
934
|
-
_this.notifyClose();
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
} else {
|
|
938
|
-
// Using default network
|
|
939
|
-
|
|
940
|
-
// Failed to set default network on open -- quit + close
|
|
941
|
-
if (!chainId || chainId <= 0) {
|
|
942
|
-
_this.notifyOpen({
|
|
943
|
-
sessionId: _this._sessionId,
|
|
944
|
-
error: `failed to open wallet on network ${networkId}`
|
|
945
|
-
});
|
|
946
|
-
return false;
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
// user is already connected, notify session details.
|
|
950
|
-
// TODO: in future, keep list if 'connected' dapps / sessions in the session
|
|
951
|
-
// controller, and only sync with allowed apps
|
|
952
|
-
_this.notifyOpen({
|
|
953
|
-
sessionId: _this._sessionId,
|
|
954
|
-
chainId: `${chainId}`,
|
|
955
|
-
session: await _this.walletRequestHandler.walletSession(chainId)
|
|
956
|
-
});
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
return true;
|
|
960
|
-
};
|
|
961
|
-
this.saveTransportSession = session => {
|
|
962
|
-
useBestStore().setItem(TRANSPORT_SESSION_LS_KEY, JSON.stringify(session));
|
|
963
|
-
};
|
|
964
|
-
this.getCachedTransportSession = async function () {
|
|
965
|
-
const session = useBestStore().getItem(TRANSPORT_SESSION_LS_KEY);
|
|
966
|
-
try {
|
|
967
|
-
return session ? JSON.parse(session) : null;
|
|
968
|
-
} catch (err) {
|
|
969
|
-
console.error(`unable to parse transport session: ${session}`);
|
|
970
|
-
return null;
|
|
971
|
-
}
|
|
972
|
-
};
|
|
973
|
-
this.walletRequestHandler = walletRequestHandler;
|
|
974
|
-
this._init = InitState.NIL;
|
|
975
|
-
this.walletRequestHandler.on('connect', connectDetails => {
|
|
976
|
-
if (!this.registered) return;
|
|
977
|
-
// means user has logged in and wallet is connected to the app
|
|
978
|
-
this.notifyConnect(connectDetails);
|
|
979
|
-
});
|
|
980
|
-
this.walletRequestHandler.on('disconnect', (error, origin) => {
|
|
981
|
-
if (!this.registered) return;
|
|
982
|
-
// means user has logged out the app / disconnected wallet from the app
|
|
983
|
-
this.notifyDisconnect(error, origin);
|
|
984
|
-
});
|
|
985
|
-
this.walletRequestHandler.on('accountsChanged', (accounts, origin) => {
|
|
986
|
-
if (!this.registered) return;
|
|
987
|
-
this.notifyAccountsChanged(accounts, origin);
|
|
988
|
-
});
|
|
989
|
-
this.walletRequestHandler.on('networks', networks => {
|
|
990
|
-
if (!this.registered) return;
|
|
991
|
-
this.notifyNetworks(networks);
|
|
992
|
-
if (!networks || networks.length === 0) {
|
|
993
|
-
this.notifyChainChanged('0x0');
|
|
994
|
-
} else {
|
|
995
|
-
this.notifyChainChanged(ethers.toQuantity(networks.find(network => network.isDefaultChain).chainId));
|
|
996
|
-
}
|
|
997
|
-
});
|
|
998
|
-
this.walletRequestHandler.on('chainChanged', (chainIdHex, origin) => {
|
|
999
|
-
this.notifyChainChanged(chainIdHex, origin);
|
|
1000
|
-
});
|
|
1001
|
-
this.walletRequestHandler.on('walletContext', walletContext => {
|
|
1002
|
-
if (!this.registered || !walletContext) return;
|
|
1003
|
-
this.notifyWalletContext(walletContext);
|
|
1004
|
-
});
|
|
1005
|
-
this.walletRequestHandler.on('close', error => {
|
|
1006
|
-
if (!this.registered) return;
|
|
1007
|
-
this.notifyClose(error);
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
|
-
get registered() {
|
|
1011
|
-
return this._registered;
|
|
1012
|
-
}
|
|
1013
|
-
register() {
|
|
1014
|
-
throw new Error('abstract method');
|
|
1015
|
-
}
|
|
1016
|
-
unregister() {
|
|
1017
|
-
throw new Error('abstract method');
|
|
1018
|
-
}
|
|
1019
|
-
request(request) {
|
|
1020
|
-
throw new Error('abstract method');
|
|
1021
|
-
}
|
|
1022
|
-
sendMessage(message) {
|
|
1023
|
-
throw new Error('abstract method');
|
|
1024
|
-
}
|
|
1025
|
-
notifyOpen(openInfo) {
|
|
1026
|
-
const {
|
|
1027
|
-
chainId,
|
|
1028
|
-
sessionId,
|
|
1029
|
-
session,
|
|
1030
|
-
error
|
|
1031
|
-
} = openInfo;
|
|
1032
|
-
this.sendMessage({
|
|
1033
|
-
idx: -1,
|
|
1034
|
-
type: EventType.OPEN,
|
|
1035
|
-
data: {
|
|
1036
|
-
chainId,
|
|
1037
|
-
sessionId,
|
|
1038
|
-
session,
|
|
1039
|
-
error
|
|
1040
|
-
},
|
|
1041
|
-
clientVersion: VERSION
|
|
1042
|
-
});
|
|
1043
|
-
}
|
|
1044
|
-
notifyClose(error) {
|
|
1045
|
-
this.sendMessage({
|
|
1046
|
-
idx: -1,
|
|
1047
|
-
type: EventType.CLOSE,
|
|
1048
|
-
data: error ? {
|
|
1049
|
-
error
|
|
1050
|
-
} : null,
|
|
1051
|
-
clientVersion: VERSION
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
notifyConnect(connectDetails) {
|
|
1055
|
-
this.sendMessage({
|
|
1056
|
-
idx: -1,
|
|
1057
|
-
type: EventType.CONNECT,
|
|
1058
|
-
data: connectDetails,
|
|
1059
|
-
clientVersion: VERSION
|
|
1060
|
-
});
|
|
1061
|
-
}
|
|
1062
|
-
notifyDisconnect(error, origin) {
|
|
1063
|
-
this.sendMessage({
|
|
1064
|
-
idx: -1,
|
|
1065
|
-
type: EventType.DISCONNECT,
|
|
1066
|
-
data: error ? {
|
|
1067
|
-
error
|
|
1068
|
-
} : null,
|
|
1069
|
-
origin,
|
|
1070
|
-
clientVersion: VERSION
|
|
1071
|
-
});
|
|
1072
|
-
}
|
|
1073
|
-
notifyAccountsChanged(accounts, origin) {
|
|
1074
|
-
this.sendMessage({
|
|
1075
|
-
idx: -1,
|
|
1076
|
-
type: EventType.ACCOUNTS_CHANGED,
|
|
1077
|
-
data: accounts,
|
|
1078
|
-
origin,
|
|
1079
|
-
clientVersion: VERSION
|
|
1080
|
-
});
|
|
1081
|
-
}
|
|
1082
|
-
notifyChainChanged(chainIdHex, origin) {
|
|
1083
|
-
this.sendMessage({
|
|
1084
|
-
idx: -1,
|
|
1085
|
-
type: EventType.CHAIN_CHANGED,
|
|
1086
|
-
data: chainIdHex,
|
|
1087
|
-
origin,
|
|
1088
|
-
clientVersion: VERSION
|
|
1089
|
-
});
|
|
1090
|
-
}
|
|
1091
|
-
notifyNetworks(networks) {
|
|
1092
|
-
this.sendMessage({
|
|
1093
|
-
idx: -1,
|
|
1094
|
-
type: EventType.NETWORKS,
|
|
1095
|
-
data: networks,
|
|
1096
|
-
clientVersion: VERSION
|
|
1097
|
-
});
|
|
1098
|
-
}
|
|
1099
|
-
notifyWalletContext(walletContext) {
|
|
1100
|
-
this.sendMessage({
|
|
1101
|
-
idx: -1,
|
|
1102
|
-
type: EventType.WALLET_CONTEXT,
|
|
1103
|
-
data: walletContext,
|
|
1104
|
-
clientVersion: VERSION
|
|
1105
|
-
});
|
|
1106
|
-
}
|
|
1107
|
-
isValidInitAck(message) {
|
|
1108
|
-
if (this._init === InitState.OK) {
|
|
1109
|
-
// we're already in init state, we shouldn't handle this message
|
|
1110
|
-
logger.warn("isValidInitAck, already in init'd state, so inquiry is invalid.");
|
|
1111
|
-
return false;
|
|
1112
|
-
}
|
|
1113
|
-
if (message.type !== EventType.INIT) {
|
|
1114
|
-
logger.warn('isValidInitAck, invalid message type, expecting init');
|
|
1115
|
-
return false;
|
|
1116
|
-
}
|
|
1117
|
-
const {
|
|
1118
|
-
sessionId,
|
|
1119
|
-
nonce
|
|
1120
|
-
} = message.data;
|
|
1121
|
-
if (!sessionId || sessionId.length === 0 || !nonce || nonce.length === 0) {
|
|
1122
|
-
logger.error('invalid init ack');
|
|
1123
|
-
return false;
|
|
1124
|
-
}
|
|
1125
|
-
if (sessionId !== this._sessionId || nonce !== this._initNonce) {
|
|
1126
|
-
logger.error('invalid init ack match');
|
|
1127
|
-
return false;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// all checks pass, its true
|
|
1131
|
-
return true;
|
|
1132
|
-
}
|
|
1133
|
-
init() {
|
|
1134
|
-
return new Promise((resolve, reject) => {
|
|
1135
|
-
// avoid re-init`ing, or if there is a transport which doesn't require
|
|
1136
|
-
// it, then it may set this._init to OK in its constructor.
|
|
1137
|
-
if (this._init === InitState.OK) {
|
|
1138
|
-
resolve();
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
if (this._init !== InitState.NIL || this._initCallback) {
|
|
1142
|
-
reject('transport init is in progress');
|
|
1143
|
-
return;
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
// start init timeout, if we don't receive confirmation
|
|
1147
|
-
// from provider within this amount of time, then we timeout
|
|
1148
|
-
const initTimeout = setTimeout(() => {
|
|
1149
|
-
logger.warn('transport init timed out');
|
|
1150
|
-
if (this._initCallback) {
|
|
1151
|
-
this._initCallback('transport init timed out');
|
|
1152
|
-
}
|
|
1153
|
-
}, PROVIDER_OPEN_TIMEOUT / 2);
|
|
1154
|
-
|
|
1155
|
-
// setup callback as we receive the init message async in the handleMessage function
|
|
1156
|
-
this._initCallback = error => {
|
|
1157
|
-
this._initCallback = undefined; // reset
|
|
1158
|
-
clearTimeout(initTimeout);
|
|
1159
|
-
if (error) {
|
|
1160
|
-
reject(error);
|
|
1161
|
-
} else {
|
|
1162
|
-
this._init = InitState.OK;
|
|
1163
|
-
resolve();
|
|
1164
|
-
}
|
|
1165
|
-
};
|
|
1166
|
-
|
|
1167
|
-
// send init request with random nonce to the provider, where we expect
|
|
1168
|
-
// for the provider to echo it back to us as complete handshake
|
|
1169
|
-
this._initNonce = `${performance.now()}`;
|
|
1170
|
-
this.sendMessage({
|
|
1171
|
-
idx: -1,
|
|
1172
|
-
type: EventType.INIT,
|
|
1173
|
-
data: {
|
|
1174
|
-
nonce: this._initNonce
|
|
1175
|
-
},
|
|
1176
|
-
clientVersion: VERSION
|
|
1177
|
-
});
|
|
1178
|
-
this._init = InitState.SENT_NONCE;
|
|
1179
|
-
|
|
1180
|
-
// NOTE: the promise will resolve in the _initCallback method
|
|
1181
|
-
// which will be called from either handleMessage or the initTimeout
|
|
1182
|
-
});
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
class ProxyMessageChannel {
|
|
1187
|
-
constructor() {
|
|
1188
|
-
this.app = void 0;
|
|
1189
|
-
this.wallet = void 0;
|
|
1190
|
-
const port1 = new ProxyMessageChannelPort();
|
|
1191
|
-
const port2 = new ProxyMessageChannelPort();
|
|
1192
|
-
port1.conn = port2;
|
|
1193
|
-
port2.conn = port1;
|
|
1194
|
-
this.app = port1;
|
|
1195
|
-
this.wallet = port2;
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
class ProxyMessageChannelPort {
|
|
1199
|
-
constructor() {
|
|
1200
|
-
this.conn = void 0;
|
|
1201
|
-
this.events = new EventEmitter2();
|
|
1202
|
-
// handle messages which hit this port
|
|
1203
|
-
this.handleMessage = message => {
|
|
1204
|
-
throw new Error('ProxyMessageChannelPort is not registered');
|
|
1205
|
-
};
|
|
1206
|
-
// send messages to the connected port
|
|
1207
|
-
this.sendMessage = message => {
|
|
1208
|
-
this.conn.handleMessage(message);
|
|
1209
|
-
|
|
1210
|
-
// trigger events
|
|
1211
|
-
if (message.type === 'open') {
|
|
1212
|
-
this.events.emit('open', message);
|
|
1213
|
-
}
|
|
1214
|
-
if (message.type === 'close') {
|
|
1215
|
-
this.events.emit('close', message);
|
|
1216
|
-
}
|
|
1217
|
-
if (message.type === 'connect') {
|
|
1218
|
-
this.events.emit('connect', message);
|
|
1219
|
-
}
|
|
1220
|
-
if (message.type === 'disconnect') {
|
|
1221
|
-
this.events.emit('disconnect', message);
|
|
1222
|
-
}
|
|
1223
|
-
};
|
|
1224
|
-
}
|
|
1225
|
-
on(event, fn) {
|
|
1226
|
-
this.events.on(event, fn);
|
|
1227
|
-
}
|
|
1228
|
-
once(event, fn) {
|
|
1229
|
-
this.events.once(event, fn);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
class ProxyMessageProvider extends BaseProviderTransport {
|
|
1234
|
-
constructor(port) {
|
|
1235
|
-
super();
|
|
1236
|
-
this.port = void 0;
|
|
1237
|
-
this.register = () => {
|
|
1238
|
-
this.port.handleMessage = message => {
|
|
1239
|
-
this.handleMessage(message);
|
|
1240
|
-
};
|
|
1241
|
-
this.on('open', (...args) => {
|
|
1242
|
-
this.port.events.emit('open', ...args);
|
|
1243
|
-
});
|
|
1244
|
-
this.on('close', (...args) => {
|
|
1245
|
-
this.port.events.emit('close', ...args);
|
|
1246
|
-
});
|
|
1247
|
-
this.on('connect', (...args) => {
|
|
1248
|
-
this.port.events.emit('connect', ...args);
|
|
1249
|
-
});
|
|
1250
|
-
this.on('disconnect', (...args) => {
|
|
1251
|
-
this.port.events.emit('disconnect', ...args);
|
|
1252
|
-
});
|
|
1253
|
-
this._registered = true;
|
|
1254
|
-
};
|
|
1255
|
-
this.unregister = () => {
|
|
1256
|
-
this._registered = false;
|
|
1257
|
-
this.closeWallet();
|
|
1258
|
-
this.events.removeAllListeners();
|
|
1259
|
-
// @ts-ignore
|
|
1260
|
-
this.port.handleMessage = undefined;
|
|
1261
|
-
};
|
|
1262
|
-
this.openWallet = (path, intent, networkId) => {
|
|
1263
|
-
if (this.state === OpenState.CLOSED) {
|
|
1264
|
-
this.state = OpenState.OPENING;
|
|
1265
|
-
const sessionId = `${performance.now()}`;
|
|
1266
|
-
this._sessionId = sessionId;
|
|
1267
|
-
this.sendMessage({
|
|
1268
|
-
idx: -1,
|
|
1269
|
-
type: EventType.OPEN,
|
|
1270
|
-
data: {
|
|
1271
|
-
path,
|
|
1272
|
-
intent,
|
|
1273
|
-
networkId,
|
|
1274
|
-
sessionId
|
|
1275
|
-
},
|
|
1276
|
-
clientVersion: VERSION
|
|
1277
|
-
});
|
|
1278
|
-
}
|
|
1279
|
-
};
|
|
1280
|
-
this.state = OpenState.CLOSED;
|
|
1281
|
-
this.port = port;
|
|
1282
|
-
if (!port) {
|
|
1283
|
-
throw new Error('port argument cannot be empty');
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
// disable init handshake for proxy-transport, we set it to OK, to
|
|
1287
|
-
// consider it in completed state.
|
|
1288
|
-
this._init = InitState.OK;
|
|
1289
|
-
}
|
|
1290
|
-
closeWallet() {
|
|
1291
|
-
this.sendMessage({
|
|
1292
|
-
idx: -1,
|
|
1293
|
-
type: EventType.CLOSE,
|
|
1294
|
-
data: null,
|
|
1295
|
-
clientVersion: VERSION
|
|
1296
|
-
});
|
|
1297
|
-
this.close();
|
|
1298
|
-
}
|
|
1299
|
-
sendMessage(message) {
|
|
1300
|
-
if (!message.idx) {
|
|
1301
|
-
throw new Error('message idx is empty');
|
|
1302
|
-
}
|
|
1303
|
-
this.port.sendMessage(message);
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
class ProxyMessageHandler extends BaseWalletTransport {
|
|
1308
|
-
constructor(walletRequestHandler, port) {
|
|
1309
|
-
super(walletRequestHandler);
|
|
1310
|
-
this.port = void 0;
|
|
1311
|
-
this.port = port;
|
|
1312
|
-
this._init = InitState.OK;
|
|
1313
|
-
}
|
|
1314
|
-
register() {
|
|
1315
|
-
this.port.handleMessage = message => {
|
|
1316
|
-
this.handleMessage(message);
|
|
1317
|
-
};
|
|
1318
|
-
this._registered = true;
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
// note: we can't decide whether to restore the session within register(), because session info is
|
|
1322
|
-
// received asyncronously via EventType.OPEN after register() is executed.
|
|
1323
|
-
// And in the case of a redirect/reload, EventType.OPEN is not sent at all,
|
|
1324
|
-
// because the wallet is already open.
|
|
1325
|
-
//
|
|
1326
|
-
// call this method from wallet redirect hander when a session restore is needed
|
|
1327
|
-
async restoreSession() {
|
|
1328
|
-
const cachedSession = await this.getCachedTransportSession();
|
|
1329
|
-
if (cachedSession) {
|
|
1330
|
-
this.open(cachedSession);
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
unregister() {
|
|
1334
|
-
// @ts-ignore
|
|
1335
|
-
this.port.handleMessage = undefined;
|
|
1336
|
-
this._registered = false;
|
|
1337
|
-
}
|
|
1338
|
-
sendMessage(message) {
|
|
1339
|
-
this.port.sendMessage(message);
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
/**
|
|
1344
|
-
* This will redirect console logs from Sequence.js & the wallet to the Unreal console, for debugging purposes.
|
|
1345
|
-
*/
|
|
1346
|
-
function overrideLogs(side) {
|
|
1347
|
-
var _window$ue;
|
|
1348
|
-
if ((_window$ue = window.ue) != null && _window$ue.sequencewallettransport && !window.logsOverriddenForUnreal) {
|
|
1349
|
-
var _window$ue2;
|
|
1350
|
-
const t = (_window$ue2 = window.ue) == null ? void 0 : _window$ue2.sequencewallettransport;
|
|
1351
|
-
console.log = (...args) => {
|
|
1352
|
-
t.logfromjs(`${side}: ${stringify(args)}`);
|
|
1353
|
-
};
|
|
1354
|
-
console.warn = (...args) => {
|
|
1355
|
-
t.warnfromjs(`${side}: ${stringify(args)}`);
|
|
1356
|
-
};
|
|
1357
|
-
console.error = (...args) => {
|
|
1358
|
-
t.errorfromjs(`${side}: ${stringify(args)}`);
|
|
1359
|
-
};
|
|
1360
|
-
window.logsOverriddenForUnreal = true;
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
function stringify(things) {
|
|
1364
|
-
return things.map(a => typeof a === 'object' ? a instanceof Error ? a.message : JSON.stringify(a, bigintReplacer) : String(a)).join(' ');
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
let registeredUnrealMessageProvider;
|
|
1368
|
-
|
|
1369
|
-
// all lowercase is an annoying limitation of Unreal CEF BindUObject
|
|
1370
|
-
|
|
1371
|
-
/**
|
|
1372
|
-
* Initialized on dApp side
|
|
1373
|
-
*/
|
|
1374
|
-
class UnrealMessageProvider extends BaseProviderTransport {
|
|
1375
|
-
constructor(walletAppURL) {
|
|
1376
|
-
super();
|
|
1377
|
-
this.walletURL = void 0;
|
|
1378
|
-
this.register = () => {
|
|
1379
|
-
var _window$ue;
|
|
1380
|
-
overrideLogs('dapp');
|
|
1381
|
-
if (registeredUnrealMessageProvider) {
|
|
1382
|
-
// overriding the registered message provider
|
|
1383
|
-
registeredUnrealMessageProvider.unregister();
|
|
1384
|
-
registeredUnrealMessageProvider = this;
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
// listen for incoming messages from wallet
|
|
1388
|
-
if ((_window$ue = window.ue) != null && _window$ue.sequencewallettransport) {
|
|
1389
|
-
window.ue.sequencewallettransport.onmessagefromwallet = this.onUnrealCallback;
|
|
1390
|
-
}
|
|
1391
|
-
registeredUnrealMessageProvider = this;
|
|
1392
|
-
this._registered = true;
|
|
1393
|
-
console.log('registering transport!');
|
|
1394
|
-
};
|
|
1395
|
-
this.unregister = () => {
|
|
1396
|
-
var _window$ue2;
|
|
1397
|
-
this._registered = false;
|
|
1398
|
-
this.closeWallet();
|
|
1399
|
-
|
|
1400
|
-
// disable message listener
|
|
1401
|
-
if (registeredUnrealMessageProvider === this) {
|
|
1402
|
-
registeredUnrealMessageProvider = undefined;
|
|
1403
|
-
}
|
|
1404
|
-
if (((_window$ue2 = window.ue) == null || (_window$ue2 = _window$ue2.sequencewallettransport) == null ? void 0 : _window$ue2.onmessagefromwallet) === this.onUnrealCallback) {
|
|
1405
|
-
delete window.ue.sequencewallettransport.onmessagefromwallet;
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
// clear event listeners
|
|
1409
|
-
this.events.removeAllListeners();
|
|
1410
|
-
};
|
|
1411
|
-
this.openWallet = (path, intent, networkId) => {
|
|
1412
|
-
if (this.isOpened()) {
|
|
1413
|
-
// TODO focus wallet
|
|
1414
|
-
console.log('wallet already open!');
|
|
1415
|
-
return;
|
|
1416
|
-
}
|
|
1417
|
-
console.log('opening wallet!');
|
|
1418
|
-
// Instantiate new walletURL for this call
|
|
1419
|
-
const walletURL = new URL(this.walletURL.href);
|
|
1420
|
-
const windowSessionParams = new WindowSessionParams();
|
|
1421
|
-
if (path) {
|
|
1422
|
-
walletURL.pathname = path.toLowerCase();
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
// Set session, intent and network id on walletURL
|
|
1426
|
-
this._init = InitState.NIL;
|
|
1427
|
-
this._sessionId = `${performance.now()}`;
|
|
1428
|
-
windowSessionParams.set('sid', this._sessionId);
|
|
1429
|
-
if (intent) {
|
|
1430
|
-
// encode intent as base64 url-encoded param
|
|
1431
|
-
windowSessionParams.set('intent', base64EncodeObject(intent));
|
|
1432
|
-
}
|
|
1433
|
-
if (networkId) {
|
|
1434
|
-
windowSessionParams.set('net', `${networkId}`);
|
|
1435
|
-
}
|
|
1436
|
-
// serialize params
|
|
1437
|
-
walletURL.search = windowSessionParams.toString();
|
|
1438
|
-
console.log('opening wallet to', walletURL.href);
|
|
1439
|
-
window.open(walletURL.href);
|
|
1440
|
-
};
|
|
1441
|
-
// onmessage, receives ProviderMessageResponse from the wallet unreal transport
|
|
1442
|
-
this.onUnrealCallback = message => {
|
|
1443
|
-
if (!message) {
|
|
1444
|
-
throw new Error('ProviderMessage object is empty');
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
// handle message with base message provider
|
|
1448
|
-
this.handleMessage(message);
|
|
1449
|
-
};
|
|
1450
|
-
this.walletURL = new URL(walletAppURL);
|
|
1451
|
-
}
|
|
1452
|
-
closeWallet() {
|
|
1453
|
-
this.close();
|
|
1454
|
-
}
|
|
1455
|
-
// all lowercase is an annoying limitation of Unreal CEF BindUObject
|
|
1456
|
-
sendMessage(message) {
|
|
1457
|
-
var _window$ue3;
|
|
1458
|
-
const postedMessage = typeof message !== 'string' ? JSON.stringify(message, bigintReplacer) : message;
|
|
1459
|
-
console.log('Sending message to wallet:', postedMessage);
|
|
1460
|
-
(_window$ue3 = window.ue) == null || (_window$ue3 = _window$ue3.sequencewallettransport) == null || _window$ue3.sendmessagetowallet(postedMessage);
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
// all lowercase is an annoying limitation of Unreal CEF BindUObject
|
|
1465
|
-
|
|
1466
|
-
/**
|
|
1467
|
-
* Initialized on Wallet side
|
|
1468
|
-
*/
|
|
1469
|
-
class UnrealMessageHandler extends BaseWalletTransport {
|
|
1470
|
-
constructor(walletRequestHandler) {
|
|
1471
|
-
super(walletRequestHandler);
|
|
1472
|
-
// onmessage is called when (the wallet) receives request messages from the dapp
|
|
1473
|
-
// over the unreal json-messaging transport
|
|
1474
|
-
this.onMessageFromUnreal = request => {
|
|
1475
|
-
// Wallet always expects json-rpc request messages from a dapp
|
|
1476
|
-
|
|
1477
|
-
logger.debug('RECEIVED MESSAGE', request);
|
|
1478
|
-
|
|
1479
|
-
// Handle message via the base transport
|
|
1480
|
-
this.handleMessage(request);
|
|
1481
|
-
};
|
|
1482
|
-
this.getUnrealTransportSession = windowParams => {
|
|
1483
|
-
const params = new WindowSessionParams(windowParams);
|
|
1484
|
-
return {
|
|
1485
|
-
sessionId: params.get('sid'),
|
|
1486
|
-
networkId: params.get('net'),
|
|
1487
|
-
intent: base64DecodeObject(params.get('intent'))
|
|
1488
|
-
};
|
|
1489
|
-
};
|
|
1490
|
-
this._init = InitState.NIL;
|
|
1491
|
-
}
|
|
1492
|
-
async register(windowHref) {
|
|
1493
|
-
var _window$ue;
|
|
1494
|
-
if (((_window$ue = window.ue) == null ? void 0 : _window$ue.sequencewallettransport) === undefined) {
|
|
1495
|
-
return;
|
|
1496
|
-
}
|
|
1497
|
-
overrideLogs('wallet');
|
|
1498
|
-
|
|
1499
|
-
// record open details (sessionId + default network) from the window url
|
|
1500
|
-
const {
|
|
1501
|
-
search: rawParams
|
|
1502
|
-
} = new URL(windowHref || window.location.href);
|
|
1503
|
-
let session = this.getUnrealTransportSession(rawParams);
|
|
1504
|
-
|
|
1505
|
-
// provider should always include sid when opening a new window
|
|
1506
|
-
const isNewWindowSession = !!session.sessionId;
|
|
1507
|
-
|
|
1508
|
-
// attempt to restore previous session in the case of a redirect or window reload
|
|
1509
|
-
if (!isNewWindowSession) {
|
|
1510
|
-
session = await this.getCachedTransportSession();
|
|
1511
|
-
}
|
|
1512
|
-
if (!session) {
|
|
1513
|
-
logger.error('unreal session is undefined');
|
|
1514
|
-
return;
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
// listen for window-transport requests
|
|
1518
|
-
window.ue.sequencewallettransport.onmessagefromsequencejs = this.onMessageFromUnreal;
|
|
1519
|
-
this._registered = true;
|
|
1520
|
-
|
|
1521
|
-
// send open event to the app which opened us
|
|
1522
|
-
this.open(session).then(opened => {
|
|
1523
|
-
if (!opened) {
|
|
1524
|
-
var _session;
|
|
1525
|
-
const err = `failed to open to network ${(_session = session) == null ? void 0 : _session.networkId}`;
|
|
1526
|
-
logger.error(err);
|
|
1527
|
-
this.notifyClose({
|
|
1528
|
-
message: err
|
|
1529
|
-
});
|
|
1530
|
-
window.close();
|
|
1531
|
-
}
|
|
1532
|
-
}).catch(e => {
|
|
1533
|
-
var _session2;
|
|
1534
|
-
const err = `failed to open to network ${(_session2 = session) == null ? void 0 : _session2.networkId}, due to: ${e}`;
|
|
1535
|
-
logger.error(err);
|
|
1536
|
-
this.notifyClose({
|
|
1537
|
-
message: err
|
|
1538
|
-
});
|
|
1539
|
-
window.close();
|
|
1540
|
-
});
|
|
1541
|
-
}
|
|
1542
|
-
unregister() {
|
|
1543
|
-
var _window$ue2;
|
|
1544
|
-
if (((_window$ue2 = window.ue) == null || (_window$ue2 = _window$ue2.sequencewallettransport) == null ? void 0 : _window$ue2.onmessagefromsequencejs) === this.onMessageFromUnreal) {
|
|
1545
|
-
delete window.ue.sequencewallettransport.onmessagefromsequencejs;
|
|
1546
|
-
}
|
|
1547
|
-
this._registered = false;
|
|
1548
|
-
}
|
|
1549
|
-
// sendMessage sends message to the dapp window
|
|
1550
|
-
sendMessage(message) {
|
|
1551
|
-
var _window$ue3;
|
|
1552
|
-
if (message.type !== EventType.INIT && this._init !== InitState.OK) {
|
|
1553
|
-
logger.error('impossible state, should not be calling postMessage until inited');
|
|
1554
|
-
return;
|
|
1555
|
-
}
|
|
1556
|
-
// prepare payload
|
|
1557
|
-
const payload = JSON.stringify(message, bigintReplacer);
|
|
1558
|
-
|
|
1559
|
-
// post-message to app.
|
|
1560
|
-
(_window$ue3 = window.ue) == null || (_window$ue3 = _window$ue3.sequencewallettransport) == null || _window$ue3.sendmessagetosequencejs(payload);
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
const CHANNEL_ID = 'sequence-extension-message-handler';
|
|
1565
|
-
class ExtensionMessageHandler extends BaseWalletTransport {
|
|
1566
|
-
constructor(walletRequestHandler, runtime) {
|
|
1567
|
-
super(walletRequestHandler);
|
|
1568
|
-
this.runtime = runtime;
|
|
1569
|
-
this.port = void 0;
|
|
1570
|
-
this._init = InitState.OK;
|
|
1571
|
-
}
|
|
1572
|
-
register() {
|
|
1573
|
-
this._registered = true;
|
|
1574
|
-
this.port = this.runtime.connect({
|
|
1575
|
-
name: CHANNEL_ID
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
sendMessage(message) {
|
|
1579
|
-
logger.info('[ExtensionMessageHandler send]', message);
|
|
1580
|
-
this.port.postMessage(message);
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
class ExtensionMessageProvider extends BaseProviderTransport {
|
|
1585
|
-
constructor(runtime) {
|
|
1586
|
-
super();
|
|
1587
|
-
this.register = () => {
|
|
1588
|
-
this._registered = true;
|
|
1589
|
-
};
|
|
1590
|
-
runtime.onConnect.addListener(port => {
|
|
1591
|
-
if (port.name === CHANNEL_ID) {
|
|
1592
|
-
this._init = InitState.OK;
|
|
1593
|
-
port.onMessage.addListener(message => {
|
|
1594
|
-
this.handleMessage(message);
|
|
1595
|
-
});
|
|
1596
|
-
}
|
|
1597
|
-
});
|
|
1598
|
-
}
|
|
1599
|
-
sendMessage(message) {
|
|
1600
|
-
//noop
|
|
1601
|
-
}
|
|
1602
|
-
unregister() {
|
|
1603
|
-
//noop
|
|
1604
|
-
}
|
|
1605
|
-
openWallet(path, intent, networkId) {
|
|
1606
|
-
//noop
|
|
1607
|
-
}
|
|
1608
|
-
closeWallet() {
|
|
1609
|
-
//noop
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
|
|
1613
|
-
// to be used on injected window.ethereum EIP1193 proxy
|
|
1614
|
-
class BaseInjectedTransport extends EventEmitter2 {
|
|
1615
|
-
constructor(stream) {
|
|
1616
|
-
var _this;
|
|
1617
|
-
super();
|
|
1618
|
-
_this = this;
|
|
1619
|
-
this.stream = stream;
|
|
1620
|
-
this.responseCallbacks = new Map();
|
|
1621
|
-
this._messageIdx = 0;
|
|
1622
|
-
this.nextMessageIdx = () => ++this._messageIdx;
|
|
1623
|
-
this.handleMessage = message => {
|
|
1624
|
-
if (!message.type || !message.data) {
|
|
1625
|
-
return;
|
|
1626
|
-
}
|
|
1627
|
-
logger.info('[received message]', message);
|
|
1628
|
-
const requestIdx = message.idx;
|
|
1629
|
-
const responseCallback = this.responseCallbacks.get(requestIdx);
|
|
1630
|
-
if (requestIdx) {
|
|
1631
|
-
this.responseCallbacks.delete(requestIdx);
|
|
1632
|
-
}
|
|
1633
|
-
switch (message.type) {
|
|
1634
|
-
case EventType.MESSAGE:
|
|
1635
|
-
if (responseCallback) {
|
|
1636
|
-
this.emit(EventType.MESSAGE, message);
|
|
1637
|
-
responseCallback(message.data.error, message);
|
|
1638
|
-
} else {
|
|
1639
|
-
// NOTE: this would occur if 'idx' isn't set, which should never happen
|
|
1640
|
-
// or when we register two handler, or duplicate messages with the same idx are sent,
|
|
1641
|
-
// all of which should be prevented prior to getting to this point
|
|
1642
|
-
throw new Error('impossible state');
|
|
1643
|
-
}
|
|
1644
|
-
break;
|
|
1645
|
-
case EventType.DISCONNECT:
|
|
1646
|
-
case EventType.ACCOUNTS_CHANGED:
|
|
1647
|
-
case EventType.CHAIN_CHANGED:
|
|
1648
|
-
this.emit(message.type, message.data);
|
|
1649
|
-
break;
|
|
1650
|
-
default:
|
|
1651
|
-
console.error('unknown message type', message);
|
|
1652
|
-
break;
|
|
1653
|
-
}
|
|
1654
|
-
};
|
|
1655
|
-
this.sendMessageRequest = async function (message) {
|
|
1656
|
-
return new Promise((resolve, reject) => {
|
|
1657
|
-
if (!message.idx || message.idx <= 0) {
|
|
1658
|
-
reject(new Error('message idx not set'));
|
|
1659
|
-
}
|
|
1660
|
-
const responseCallback = (error, response) => {
|
|
1661
|
-
if (error) {
|
|
1662
|
-
reject(error);
|
|
1663
|
-
} else if (response) {
|
|
1664
|
-
resolve(response);
|
|
1665
|
-
} else {
|
|
1666
|
-
throw new Error('no valid response to return');
|
|
1667
|
-
}
|
|
1668
|
-
};
|
|
1669
|
-
const {
|
|
1670
|
-
idx
|
|
1671
|
-
} = message;
|
|
1672
|
-
if (!_this.responseCallbacks.get(idx)) {
|
|
1673
|
-
_this.responseCallbacks.set(idx, responseCallback);
|
|
1674
|
-
} else {
|
|
1675
|
-
reject(new Error('duplicate message idx, should never happen'));
|
|
1676
|
-
}
|
|
1677
|
-
_this.sendMessage(message);
|
|
1678
|
-
});
|
|
1679
|
-
};
|
|
1680
|
-
this.stream.on('data', this.handleMessage);
|
|
1681
|
-
}
|
|
1682
|
-
sendMessage(message) {
|
|
1683
|
-
if (!this.stream.writable) {
|
|
1684
|
-
console.error('window post message stream is not writable');
|
|
1685
|
-
}
|
|
1686
|
-
this.stream.write(message);
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
// ..
|
|
1691
|
-
let registeredWindowMessageProvider;
|
|
1692
|
-
class WindowMessageProvider extends BaseProviderTransport {
|
|
1693
|
-
constructor(walletAppURL, projectAccessKey) {
|
|
1694
|
-
super(projectAccessKey);
|
|
1695
|
-
this.walletURL = void 0;
|
|
1696
|
-
this.walletWindow = void 0;
|
|
1697
|
-
this.register = () => {
|
|
1698
|
-
if (registeredWindowMessageProvider) {
|
|
1699
|
-
// overriding the registered message provider
|
|
1700
|
-
registeredWindowMessageProvider.unregister();
|
|
1701
|
-
registeredWindowMessageProvider = this;
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
// listen for incoming messages from wallet
|
|
1705
|
-
window.addEventListener('message', this.onWindowEvent);
|
|
1706
|
-
registeredWindowMessageProvider = this;
|
|
1707
|
-
|
|
1708
|
-
// open heartbeat
|
|
1709
|
-
this.on('open', () => {
|
|
1710
|
-
// Heartbeat to track if window closed
|
|
1711
|
-
const popup = this.walletWindow;
|
|
1712
|
-
const interval = setInterval(() => {
|
|
1713
|
-
if (popup && popup.closed) {
|
|
1714
|
-
clearInterval(interval);
|
|
1715
|
-
this.close();
|
|
1716
|
-
}
|
|
1717
|
-
}, 500);
|
|
1718
|
-
});
|
|
1719
|
-
|
|
1720
|
-
// close clean up
|
|
1721
|
-
this.on('close', () => {
|
|
1722
|
-
if (this.walletWindow) {
|
|
1723
|
-
this.walletWindow.close();
|
|
1724
|
-
this.walletWindow = null;
|
|
1725
|
-
}
|
|
1726
|
-
});
|
|
1727
|
-
this._registered = true;
|
|
1728
|
-
};
|
|
1729
|
-
this.unregister = () => {
|
|
1730
|
-
this._registered = false;
|
|
1731
|
-
this.closeWallet();
|
|
1732
|
-
|
|
1733
|
-
// disable message listener
|
|
1734
|
-
if (registeredWindowMessageProvider === this) {
|
|
1735
|
-
registeredWindowMessageProvider = undefined;
|
|
1736
|
-
}
|
|
1737
|
-
window.removeEventListener('message', this.onWindowEvent);
|
|
1738
|
-
|
|
1739
|
-
// clear event listeners
|
|
1740
|
-
this.events.removeAllListeners();
|
|
1741
|
-
};
|
|
1742
|
-
this.openWallet = (path, intent, networkId) => {
|
|
1743
|
-
if (this.walletWindow && this.isOpened()) {
|
|
1744
|
-
// TODO: update the location of window to path
|
|
1745
|
-
this.walletWindow.focus();
|
|
1746
|
-
return;
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
|
-
// Instantiate new walletURL for this call
|
|
1750
|
-
const walletURL = new URL(this.walletURL.href);
|
|
1751
|
-
const windowSessionParams = new WindowSessionParams();
|
|
1752
|
-
if (path && path !== '') {
|
|
1753
|
-
walletURL.pathname = path.toLowerCase();
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
// Set session, intent and network id on walletURL
|
|
1757
|
-
this._init = InitState.NIL;
|
|
1758
|
-
this._sessionId = `${performance.now()}`;
|
|
1759
|
-
windowSessionParams.set('sid', this._sessionId);
|
|
1760
|
-
if (intent) {
|
|
1761
|
-
// for the window-transport, we eagerly/optimistically set the origin host
|
|
1762
|
-
// when connecting to the wallet, however, this will be verified and enforced
|
|
1763
|
-
// on the wallet-side, so if a dapp provides the wrong origin, it will be dropped.
|
|
1764
|
-
if (intent.type === 'connect') {
|
|
1765
|
-
if (!intent.options) intent.options = {
|
|
1766
|
-
app: window.location.origin
|
|
1767
|
-
};
|
|
1768
|
-
|
|
1769
|
-
// skip setting origin host if we're in an browser extension execution context
|
|
1770
|
-
// allow origin that is passed in
|
|
1771
|
-
if (!isBrowserExtension() && !isUnityPlugin() && intent.options) {
|
|
1772
|
-
intent.options.origin = window.location.origin;
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
// encode intent as base64 url-encoded param
|
|
1776
|
-
windowSessionParams.set('intent', base64EncodeObject(intent));
|
|
1777
|
-
}
|
|
1778
|
-
if (networkId) {
|
|
1779
|
-
windowSessionParams.set('net', `${networkId}`);
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
// Open popup window on center of the app window
|
|
1783
|
-
let windowSize;
|
|
1784
|
-
let windowPos;
|
|
1785
|
-
if (isBrowserExtension()) {
|
|
1786
|
-
windowSize = [450, 750];
|
|
1787
|
-
windowPos = [Math.abs(window.screen.width / 2 - windowSize[0] / 2), Math.abs(window.screen.height / 2 - windowSize[1] / 2)];
|
|
1788
|
-
} else {
|
|
1789
|
-
windowSize = [450, 750];
|
|
1790
|
-
windowPos = [Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2), Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)];
|
|
1791
|
-
}
|
|
1792
|
-
const windowFeatures = `toolbar=0,location=0,menubar=0,scrollbars=yes,status=yes` + `,width=${windowSize[0]},height=${windowSize[1]}` + `,left=${windowPos[0]},top=${windowPos[1]}`;
|
|
1793
|
-
|
|
1794
|
-
// serialize params
|
|
1795
|
-
walletURL.search = windowSessionParams.toString();
|
|
1796
|
-
this.walletWindow = window.open(walletURL.href, 'sequence.app', windowFeatures);
|
|
1797
|
-
|
|
1798
|
-
// TODO: move this somewhere else
|
|
1799
|
-
// TODO: perhaps we trigger a .on('openTimeout') event..? maybe.. could help.
|
|
1800
|
-
|
|
1801
|
-
// Popup blocking detection and notice
|
|
1802
|
-
// let warned = false
|
|
1803
|
-
// const warnPopupBlocked = () => {
|
|
1804
|
-
// if (warned) return
|
|
1805
|
-
// warned = true
|
|
1806
|
-
// // alert('popup is blocked! hey yo') // NOTE: for debug purposes only
|
|
1807
|
-
// throw new Error('popup is blocked')
|
|
1808
|
-
// }
|
|
1809
|
-
|
|
1810
|
-
// const popupCheck = setTimeout(() => {
|
|
1811
|
-
// if (!popup || popup.closed || typeof popup.closed === 'undefined') {
|
|
1812
|
-
// // popup is definitely blocked if we reach here.
|
|
1813
|
-
// warnPopupBlocked()
|
|
1814
|
-
// }
|
|
1815
|
-
// }, 1000)
|
|
1816
|
-
|
|
1817
|
-
// const popupBlocked = popup === null || popup === undefined
|
|
1818
|
-
// if (popupBlocked) {
|
|
1819
|
-
// warnPopupBlocked()
|
|
1820
|
-
// return
|
|
1821
|
-
// }
|
|
1822
|
-
};
|
|
1823
|
-
// onmessage, receives ProviderMessageResponse from the wallet post-message transport
|
|
1824
|
-
this.onWindowEvent = event => {
|
|
1825
|
-
// Security check, ensure message is coming from wallet origin url
|
|
1826
|
-
if (event.origin !== this.walletURL.origin) {
|
|
1827
|
-
// Safetly can skip events not from the wallet
|
|
1828
|
-
return;
|
|
1829
|
-
}
|
|
1830
|
-
let message;
|
|
1831
|
-
try {
|
|
1832
|
-
message = JSON.parse(event.data, bigintReviver);
|
|
1833
|
-
} catch (err) {
|
|
1834
|
-
// event is not a ProviderMessage JSON object, skip
|
|
1835
|
-
return;
|
|
1836
|
-
}
|
|
1837
|
-
if (!message) {
|
|
1838
|
-
throw new Error('ProviderMessage object is empty');
|
|
1839
|
-
}
|
|
1840
|
-
|
|
1841
|
-
// handle message with base message provider
|
|
1842
|
-
this.handleMessage(message);
|
|
1843
|
-
};
|
|
1844
|
-
this.walletURL = new URL(walletAppURL);
|
|
1845
|
-
}
|
|
1846
|
-
closeWallet() {
|
|
1847
|
-
var _this$walletWindow;
|
|
1848
|
-
this.close();
|
|
1849
|
-
(_this$walletWindow = this.walletWindow) == null || _this$walletWindow.close();
|
|
1850
|
-
}
|
|
1851
|
-
sendMessage(message) {
|
|
1852
|
-
if (!this.walletWindow) {
|
|
1853
|
-
logger.warn('WindowMessageProvider: sendMessage failed as walletWindow is unavailable');
|
|
1854
|
-
return;
|
|
1855
|
-
}
|
|
1856
|
-
const postedMessage = typeof message !== 'string' ? JSON.stringify(message, bigintReplacer) : message;
|
|
1857
|
-
this.walletWindow.postMessage(postedMessage, this.walletURL.origin);
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
class WindowMessageHandler extends BaseWalletTransport {
|
|
1862
|
-
constructor(walletRequestHandler) {
|
|
1863
|
-
var _this;
|
|
1864
|
-
super(walletRequestHandler);
|
|
1865
|
-
_this = this;
|
|
1866
|
-
this.parentWindow = void 0;
|
|
1867
|
-
this._isPopup = false;
|
|
1868
|
-
// onmessage is called when (the wallet) receives request messages from the dapp
|
|
1869
|
-
// over the window post-messaging transport
|
|
1870
|
-
this.onWindowEvent = async function (event) {
|
|
1871
|
-
var _request, _request$origin;
|
|
1872
|
-
if (!event.origin || event.origin === '') {
|
|
1873
|
-
// skip same-origin or when event.origin is empty/undefined
|
|
1874
|
-
return;
|
|
1875
|
-
}
|
|
1876
|
-
if (_this.appOrigin && event.origin !== _this.appOrigin) {
|
|
1877
|
-
// skip message as not from expected app origin
|
|
1878
|
-
return;
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
// Wallet always expects json-rpc request messages from a dapp
|
|
1882
|
-
let request;
|
|
1883
|
-
try {
|
|
1884
|
-
request = JSON.parse(event.data, bigintReviver);
|
|
1885
|
-
} catch (err) {
|
|
1886
|
-
// event is not a ProviderMessage JSON object, skip
|
|
1887
|
-
return;
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
// Set the origin on the request
|
|
1891
|
-
(_request$origin = (_request = request).origin) != null ? _request$origin : _request.origin = event.origin;
|
|
1892
|
-
logger.debug('RECEIVED MESSAGE', request);
|
|
1893
|
-
|
|
1894
|
-
// Record event origin for valid init ack
|
|
1895
|
-
if (_this._init !== InitState.OK && _this.isValidInitAck(request)) {
|
|
1896
|
-
_this.appOrigin = event.origin;
|
|
1897
|
-
}
|
|
1898
|
-
if (_this._init === InitState.OK && (!_this.appOrigin || _this.appOrigin.length < 8)) {
|
|
1899
|
-
// impossible state
|
|
1900
|
-
logger.error('impossible state, init.OK and appOrigin required');
|
|
1901
|
-
return;
|
|
1902
|
-
}
|
|
1903
|
-
|
|
1904
|
-
// Handle message via the base transport
|
|
1905
|
-
_this.handleMessage(request);
|
|
1906
|
-
};
|
|
1907
|
-
this.getWindowTransportSession = windowParams => {
|
|
1908
|
-
const params = new WindowSessionParams(windowParams);
|
|
1909
|
-
return {
|
|
1910
|
-
sessionId: params.get('sid'),
|
|
1911
|
-
networkId: params.get('net'),
|
|
1912
|
-
intent: base64DecodeObject(params.get('intent'))
|
|
1913
|
-
};
|
|
1914
|
-
};
|
|
1915
|
-
this._init = InitState.NIL;
|
|
1916
|
-
}
|
|
1917
|
-
async register(windowHref) {
|
|
1918
|
-
const isPopup = parent.window.opener !== null;
|
|
1919
|
-
this._isPopup = isPopup;
|
|
1920
|
-
if (isPopup !== true) {
|
|
1921
|
-
return;
|
|
1922
|
-
}
|
|
1923
|
-
|
|
1924
|
-
// record open details (sessionId + default network) from the window url
|
|
1925
|
-
const {
|
|
1926
|
-
search: rawParams
|
|
1927
|
-
} = new URL(windowHref || window.location.href);
|
|
1928
|
-
let session = this.getWindowTransportSession(rawParams);
|
|
1929
|
-
|
|
1930
|
-
// provider should always include sid when opening a new window
|
|
1931
|
-
const isNewWindowSession = !!session.sessionId;
|
|
1932
|
-
|
|
1933
|
-
// attempt to restore previous session in the case of a redirect or window reload
|
|
1934
|
-
if (!isNewWindowSession) {
|
|
1935
|
-
session = await this.getCachedTransportSession();
|
|
1936
|
-
}
|
|
1937
|
-
if (!session) {
|
|
1938
|
-
logger.error('window session is undefined');
|
|
1939
|
-
return;
|
|
1940
|
-
}
|
|
1941
|
-
|
|
1942
|
-
// record parent window instance for communication
|
|
1943
|
-
this.parentWindow = parent.window.opener;
|
|
1944
|
-
|
|
1945
|
-
// listen for window-transport requests
|
|
1946
|
-
window.addEventListener('message', this.onWindowEvent, false);
|
|
1947
|
-
this._registered = true;
|
|
1948
|
-
|
|
1949
|
-
// send open event to the app which opened us
|
|
1950
|
-
this.open(session).then(opened => {
|
|
1951
|
-
if (!opened) {
|
|
1952
|
-
var _session;
|
|
1953
|
-
const err = `failed to open to network ${(_session = session) == null ? void 0 : _session.networkId}`;
|
|
1954
|
-
logger.error(err);
|
|
1955
|
-
this.notifyClose({
|
|
1956
|
-
message: err
|
|
1957
|
-
});
|
|
1958
|
-
window.close();
|
|
1959
|
-
}
|
|
1960
|
-
}).catch(e => {
|
|
1961
|
-
var _session2;
|
|
1962
|
-
const err = `failed to open to network ${(_session2 = session) == null ? void 0 : _session2.networkId}, due to: ${e}`;
|
|
1963
|
-
logger.error(err);
|
|
1964
|
-
this.notifyClose({
|
|
1965
|
-
message: err
|
|
1966
|
-
});
|
|
1967
|
-
window.close();
|
|
1968
|
-
});
|
|
1969
|
-
}
|
|
1970
|
-
unregister() {
|
|
1971
|
-
window.removeEventListener('message', this.onWindowEvent);
|
|
1972
|
-
this._registered = false;
|
|
1973
|
-
}
|
|
1974
|
-
// postMessage sends message to the dapp window
|
|
1975
|
-
sendMessage(message) {
|
|
1976
|
-
// prepare payload
|
|
1977
|
-
const payload = JSON.stringify(message, bigintReplacer);
|
|
1978
|
-
|
|
1979
|
-
// post-message to app.
|
|
1980
|
-
// only for init requests, we send to '*' origin
|
|
1981
|
-
if (message.type === EventType.INIT) {
|
|
1982
|
-
this.postMessage(payload, true);
|
|
1983
|
-
} else {
|
|
1984
|
-
this.postMessage(payload);
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
get isPopup() {
|
|
1988
|
-
return this._isPopup;
|
|
1989
|
-
}
|
|
1990
|
-
postMessage(message, init = false) {
|
|
1991
|
-
if (init !== true && this._init !== InitState.OK) {
|
|
1992
|
-
logger.error('impossible state, should not be calling postMessage until inited');
|
|
1993
|
-
return;
|
|
1994
|
-
}
|
|
1995
|
-
if (init) {
|
|
1996
|
-
// init message transmission to global target -- for 'init' payloads only
|
|
1997
|
-
this.parentWindow.postMessage(message, '*');
|
|
1998
|
-
} else {
|
|
1999
|
-
// open message transmission
|
|
2000
|
-
if (this.appOrigin && this.appOrigin.length > 4) {
|
|
2001
|
-
// just above '.com'
|
|
2002
|
-
this.parentWindow.postMessage(message, this.appOrigin);
|
|
2003
|
-
} else {
|
|
2004
|
-
logger.error('unable to postMessage as parentOrigin is invalid');
|
|
2005
|
-
}
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
function isMuxTransportTemplate(obj) {
|
|
2011
|
-
return obj && typeof obj === 'object' && (obj.windowTransport && typeof obj.windowTransport === 'object' || obj.proxyTransport && typeof obj.proxyTransport === 'object' || obj.extensionTransport && typeof obj.extensionTransport === 'object' || obj.unrealTransport && typeof obj.unrealTransport === 'object') && (
|
|
2012
|
-
// One of the transports must be enabled
|
|
2013
|
-
obj.windowTransport && obj.windowTransport.enabled || obj.proxyTransport && obj.proxyTransport.enabled || obj.extensionTransport && obj.extensionTransport.enabled || obj.unrealTransport && obj.unrealTransport.enabled);
|
|
2014
|
-
}
|
|
2015
|
-
class MuxMessageProvider {
|
|
2016
|
-
constructor(messageProviders = []) {
|
|
2017
|
-
var _this = this;
|
|
2018
|
-
this.messageProviders = void 0;
|
|
2019
|
-
this.provider = void 0;
|
|
2020
|
-
this.register = () => {
|
|
2021
|
-
if (this.messageProviders.length === 1) {
|
|
2022
|
-
this.provider = this.messageProviders[0];
|
|
2023
|
-
this.provider.register();
|
|
2024
|
-
return;
|
|
2025
|
-
}
|
|
2026
|
-
|
|
2027
|
-
// REVIEW/NOTE: ........ this method does not work for the chrome-extension. The issue becomes
|
|
2028
|
-
// when the browser quits or restarts, the "open" event is never triggered. Perhaps the code here is fine,
|
|
2029
|
-
// or maybe its not. What should happen is when a dapp makes a request, it will call openWallet
|
|
2030
|
-
// below, in which case one of the events will register. So perhaps this is fine.
|
|
2031
|
-
this.messageProviders.forEach(m => {
|
|
2032
|
-
m.register();
|
|
2033
|
-
m.once('open', () => {
|
|
2034
|
-
// the first one to open is the winner, and others will be unregistered
|
|
2035
|
-
if (!this.provider) {
|
|
2036
|
-
this.provider = m;
|
|
2037
|
-
|
|
2038
|
-
// unregister other providers
|
|
2039
|
-
this.messageProviders.forEach(m => {
|
|
2040
|
-
if (this.provider !== m) {
|
|
2041
|
-
m.unregister();
|
|
2042
|
-
}
|
|
2043
|
-
});
|
|
2044
|
-
}
|
|
2045
|
-
});
|
|
2046
|
-
});
|
|
2047
|
-
};
|
|
2048
|
-
this.unregister = () => {
|
|
2049
|
-
this.messageProviders.forEach(m => m.unregister());
|
|
2050
|
-
this.provider = undefined;
|
|
2051
|
-
};
|
|
2052
|
-
this.openWallet = (path, intent, networkId) => {
|
|
2053
|
-
if (this.provider) {
|
|
2054
|
-
this.provider.openWallet(path, intent, networkId);
|
|
2055
|
-
return;
|
|
2056
|
-
}
|
|
2057
|
-
this.messageProviders.forEach(m => m.openWallet(path, intent, networkId));
|
|
2058
|
-
};
|
|
2059
|
-
this.sendMessageRequest = async function (message) {
|
|
2060
|
-
if (_this.provider) {
|
|
2061
|
-
return _this.provider.sendMessageRequest(message);
|
|
2062
|
-
}
|
|
2063
|
-
throw new Error('impossible state, wallet must be opened first');
|
|
2064
|
-
};
|
|
2065
|
-
this.waitUntilOpened = async function () {
|
|
2066
|
-
if (_this.provider) {
|
|
2067
|
-
return _this.provider.waitUntilOpened();
|
|
2068
|
-
}
|
|
2069
|
-
return Promise.race(_this.messageProviders.map(p => p.waitUntilOpened()));
|
|
2070
|
-
};
|
|
2071
|
-
this.waitUntilConnected = async function () {
|
|
2072
|
-
if (_this.provider) {
|
|
2073
|
-
return _this.provider.waitUntilConnected();
|
|
2074
|
-
}
|
|
2075
|
-
throw new Error('impossible state, wallet must be opened first');
|
|
2076
|
-
};
|
|
2077
|
-
this.messageProviders = messageProviders;
|
|
2078
|
-
this.provider = undefined;
|
|
2079
|
-
}
|
|
2080
|
-
static new(template, projectAccessKey) {
|
|
2081
|
-
var _template$windowTrans, _template$proxyTransp, _template$extensionTr, _template$unrealTrans;
|
|
2082
|
-
const muxMessageProvider = new MuxMessageProvider();
|
|
2083
|
-
if ((_template$windowTrans = template.windowTransport) != null && _template$windowTrans.enabled && typeof window === 'object' && template.walletAppURL) {
|
|
2084
|
-
const windowMessageProvider = new WindowMessageProvider(template.walletAppURL, projectAccessKey);
|
|
2085
|
-
muxMessageProvider.add(windowMessageProvider);
|
|
2086
|
-
}
|
|
2087
|
-
if ((_template$proxyTransp = template.proxyTransport) != null && _template$proxyTransp.enabled) {
|
|
2088
|
-
const proxyMessageProvider = new ProxyMessageProvider(template.proxyTransport.appPort);
|
|
2089
|
-
muxMessageProvider.add(proxyMessageProvider);
|
|
2090
|
-
}
|
|
2091
|
-
if ((_template$extensionTr = template.extensionTransport) != null && _template$extensionTr.enabled) {
|
|
2092
|
-
const extensionMessageProvider = new ExtensionMessageProvider(template.extensionTransport.runtime);
|
|
2093
|
-
muxMessageProvider.add(extensionMessageProvider);
|
|
2094
|
-
|
|
2095
|
-
// NOTE/REVIEW: see note in mux-message-provider
|
|
2096
|
-
//
|
|
2097
|
-
// We don't add the extensionMessageProvider here because we don't send requests to it anyways, we seem to
|
|
2098
|
-
// send all requests to the WindowMessageProvider anyways. By allowing it, if browser restarts, it will break
|
|
2099
|
-
// the entire extension because messageProvider.provider will be undefined. So this is a hack to fix it.
|
|
2100
|
-
}
|
|
2101
|
-
if ((_template$unrealTrans = template.unrealTransport) != null && _template$unrealTrans.enabled && template.windowTransport && template.walletAppURL) {
|
|
2102
|
-
const unrealMessageProvider = new UnrealMessageProvider(template.walletAppURL);
|
|
2103
|
-
muxMessageProvider.add(unrealMessageProvider);
|
|
2104
|
-
}
|
|
2105
|
-
muxMessageProvider.register();
|
|
2106
|
-
return muxMessageProvider;
|
|
2107
|
-
}
|
|
2108
|
-
add(...messageProviders) {
|
|
2109
|
-
this.messageProviders.push(...messageProviders);
|
|
2110
|
-
}
|
|
2111
|
-
closeWallet() {
|
|
2112
|
-
if (this.provider) {
|
|
2113
|
-
this.provider.closeWallet();
|
|
2114
|
-
}
|
|
2115
|
-
}
|
|
2116
|
-
isOpened() {
|
|
2117
|
-
if (this.provider) {
|
|
2118
|
-
return this.provider.isOpened();
|
|
2119
|
-
}
|
|
2120
|
-
return false;
|
|
2121
|
-
}
|
|
2122
|
-
isConnected() {
|
|
2123
|
-
if (this.provider) {
|
|
2124
|
-
return this.provider.isConnected();
|
|
2125
|
-
}
|
|
2126
|
-
return false;
|
|
2127
|
-
}
|
|
2128
|
-
on(event, fn) {
|
|
2129
|
-
if (this.provider) {
|
|
2130
|
-
this.provider.on(event, fn);
|
|
2131
|
-
return;
|
|
2132
|
-
}
|
|
2133
|
-
this.messageProviders.forEach(m => {
|
|
2134
|
-
m.on(event, fn);
|
|
2135
|
-
});
|
|
2136
|
-
}
|
|
2137
|
-
once(event, fn) {
|
|
2138
|
-
if (this.provider) {
|
|
2139
|
-
this.provider.once(event, fn);
|
|
2140
|
-
return;
|
|
2141
|
-
}
|
|
2142
|
-
this.messageProviders.forEach(m => {
|
|
2143
|
-
m.once(event, fn);
|
|
2144
|
-
});
|
|
2145
|
-
}
|
|
2146
|
-
emit(event, ...args) {
|
|
2147
|
-
if (this.provider) {
|
|
2148
|
-
return this.provider.emit(event, ...args);
|
|
2149
|
-
}
|
|
2150
|
-
for (let i = 0; i < this.messageProviders.length; i++) {
|
|
2151
|
-
this.messageProviders[i].emit(event, ...args);
|
|
2152
|
-
}
|
|
2153
|
-
return true;
|
|
2154
|
-
}
|
|
2155
|
-
request(request) {
|
|
2156
|
-
if (!this.provider) {
|
|
2157
|
-
throw new Error('impossible state, wallet must be opened first');
|
|
2158
|
-
}
|
|
2159
|
-
return this.provider.request(request);
|
|
2160
|
-
}
|
|
2161
|
-
sendMessage(message) {
|
|
2162
|
-
if (!message.idx || message.idx <= 0) {
|
|
2163
|
-
throw new Error('message idx is empty');
|
|
2164
|
-
}
|
|
2165
|
-
if (this.provider) {
|
|
2166
|
-
this.provider.sendMessage(message);
|
|
2167
|
-
} else {
|
|
2168
|
-
throw new Error('impossible state, wallet must be opened first');
|
|
2169
|
-
}
|
|
2170
|
-
}
|
|
2171
|
-
handleMessage(message) {
|
|
2172
|
-
if (this.provider) {
|
|
2173
|
-
this.provider.handleMessage(message);
|
|
2174
|
-
return;
|
|
2175
|
-
}
|
|
2176
|
-
throw new Error('impossible state, wallet must be opened first');
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
function toExtended(transactions) {
|
|
2181
|
-
if (transactions.length === 0) {
|
|
2182
|
-
throw new Error('No transaction provided');
|
|
2183
|
-
}
|
|
2184
|
-
const [first, ...rest] = transactions;
|
|
2185
|
-
return _extends({}, first, {
|
|
2186
|
-
auxiliary: rest
|
|
2187
|
-
});
|
|
2188
|
-
}
|
|
2189
|
-
function fromExtended(transaction) {
|
|
2190
|
-
return [transaction, ...(transaction.auxiliary || [])];
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2193
|
-
const SIGNER_READY_TIMEOUT = 10000;
|
|
2194
|
-
class WalletRequestHandler {
|
|
2195
|
-
constructor(account, prompter, networks) {
|
|
2196
|
-
var _this = this;
|
|
2197
|
-
// signer interface of the wallet. A null value means there is no signer (ie. user not signed in). An undefined
|
|
2198
|
-
// value means the signer state is unknown, usually meaning the wallet app is booting up and initializing. Of course
|
|
2199
|
-
// a Signer value is the actually interface to a signed-in account
|
|
2200
|
-
this.account = void 0;
|
|
2201
|
-
this.signerReadyCallbacks = [];
|
|
2202
|
-
this.prompter = void 0;
|
|
2203
|
-
this.networks = void 0;
|
|
2204
|
-
this._openIntent = void 0;
|
|
2205
|
-
this._connectOptions = void 0;
|
|
2206
|
-
this.events = new EventEmitter2();
|
|
2207
|
-
this.onConnectOptionsChange = undefined;
|
|
2208
|
-
this.promptConnect = async function (options) {
|
|
2209
|
-
if (!options && !_this._connectOptions) {
|
|
2210
|
-
// this is an unexpected state and should not happen
|
|
2211
|
-
throw new Error('prompter connect options are empty');
|
|
2212
|
-
}
|
|
2213
|
-
if (!_this.prompter) {
|
|
2214
|
-
// if prompter is null, we'll auto connect
|
|
2215
|
-
return _this.connect(options);
|
|
2216
|
-
}
|
|
2217
|
-
const promptConnectDetails = await _this.prompter.promptConnect(options || _this._connectOptions).catch(_ => {
|
|
2218
|
-
return {
|
|
2219
|
-
connected: false
|
|
2220
|
-
};
|
|
2221
|
-
});
|
|
2222
|
-
const connectDetails = promptConnectDetails;
|
|
2223
|
-
if (connectDetails.connected && !connectDetails.session) {
|
|
2224
|
-
connectDetails.session = await _this.walletSession(options == null ? void 0 : options.networkId);
|
|
2225
|
-
}
|
|
2226
|
-
return promptConnectDetails;
|
|
2227
|
-
};
|
|
2228
|
-
this.isSignedIn = async function () {
|
|
2229
|
-
await _this.signerReady();
|
|
2230
|
-
return !!_this.account;
|
|
2231
|
-
};
|
|
2232
|
-
this.getAccount = async function () {
|
|
2233
|
-
await _this.signerReady();
|
|
2234
|
-
if (_this.account === undefined) {
|
|
2235
|
-
throw new Error('signerReady failed resolve');
|
|
2236
|
-
}
|
|
2237
|
-
return _this.account;
|
|
2238
|
-
};
|
|
2239
|
-
this.account = account;
|
|
2240
|
-
this.prompter = prompter;
|
|
2241
|
-
this.networks = networks;
|
|
2242
|
-
}
|
|
2243
|
-
defaultChainId() {
|
|
2244
|
-
var _this$prompter$getDef, _this$prompter;
|
|
2245
|
-
return (_this$prompter$getDef = (_this$prompter = this.prompter) == null ? void 0 : _this$prompter.getDefaultChainId()) != null ? _this$prompter$getDef : this.networks[0].chainId;
|
|
2246
|
-
}
|
|
2247
|
-
async signIn(account, options = {}) {
|
|
2248
|
-
this.setAccount(account);
|
|
2249
|
-
const {
|
|
2250
|
-
connect,
|
|
2251
|
-
defaultNetworkId
|
|
2252
|
-
} = options;
|
|
2253
|
-
|
|
2254
|
-
// Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform
|
|
2255
|
-
// necessary auth request, and then notify the dapp of the 'connect' details.
|
|
2256
|
-
//
|
|
2257
|
-
// NOTE: if a user is signing into a dapp from a fresh state, and and auth request is made
|
|
2258
|
-
// we don't trigger the promptConnect flow, as we consider the user just authenticated
|
|
2259
|
-
// for this dapp, so its safe to authorize in the promptSignInConnect() which will directly
|
|
2260
|
-
// connect after signing in.
|
|
2261
|
-
//
|
|
2262
|
-
// NOTE: signIn can optionally connect and notify dapp at this time for new signIn flows
|
|
2263
|
-
if (connect) {
|
|
2264
|
-
const connectOptions = this._connectOptions;
|
|
2265
|
-
let connectDetails;
|
|
2266
|
-
if (this.prompter !== null) {
|
|
2267
|
-
var _this$prompter2;
|
|
2268
|
-
connectDetails = await ((_this$prompter2 = this.prompter) == null ? void 0 : _this$prompter2.promptSignInConnect(connectOptions));
|
|
2269
|
-
} else {
|
|
2270
|
-
connectDetails = await this.connect(connectOptions);
|
|
2271
|
-
}
|
|
2272
|
-
this.notifyConnect(connectDetails);
|
|
2273
|
-
if (!connectOptions || connectOptions.keepWalletOpened !== true) {
|
|
2274
|
-
this.notifyClose();
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
if (defaultNetworkId && this.defaultChainId() !== defaultNetworkId) {
|
|
2278
|
-
var _this$prompter3;
|
|
2279
|
-
await ((_this$prompter3 = this.prompter) == null ? void 0 : _this$prompter3.promptChangeNetwork(defaultNetworkId));
|
|
2280
|
-
}
|
|
2281
|
-
}
|
|
2282
|
-
signOut() {
|
|
2283
|
-
if (this.account) {
|
|
2284
|
-
this.notifyDisconnect();
|
|
2285
|
-
}
|
|
2286
|
-
|
|
2287
|
-
// signed out state
|
|
2288
|
-
this.setAccount(null);
|
|
2289
|
-
}
|
|
2290
|
-
signerReset() {
|
|
2291
|
-
// resetting signer puts the wallet in an uninitialized state, which requires the app to
|
|
2292
|
-
// re-initiatize and set the signer either as "null" (ie. no signer) or "Signer" (ie. signed in).
|
|
2293
|
-
this.account = undefined;
|
|
2294
|
-
}
|
|
2295
|
-
signerReady(timeout = SIGNER_READY_TIMEOUT) {
|
|
2296
|
-
return new Promise((resolve, reject) => {
|
|
2297
|
-
if (this.account !== undefined) {
|
|
2298
|
-
resolve();
|
|
2299
|
-
} else {
|
|
2300
|
-
setTimeout(() => {
|
|
2301
|
-
if (this.account === undefined) {
|
|
2302
|
-
this.signerReadyCallbacks = [];
|
|
2303
|
-
reject(`signerReady timed out`);
|
|
2304
|
-
}
|
|
2305
|
-
}, timeout);
|
|
2306
|
-
this.signerReadyCallbacks.push(resolve);
|
|
2307
|
-
}
|
|
2308
|
-
});
|
|
2309
|
-
}
|
|
2310
|
-
async connect(options) {
|
|
2311
|
-
var _ref, _options$networkId;
|
|
2312
|
-
if (!this.account) {
|
|
2313
|
-
return {
|
|
2314
|
-
connected: false,
|
|
2315
|
-
chainId: '0x0',
|
|
2316
|
-
error: 'unable to connect without signed in account'
|
|
2317
|
-
};
|
|
2318
|
-
}
|
|
2319
|
-
const networkId = (_ref = (_options$networkId = options == null ? void 0 : options.networkId) != null ? _options$networkId : this.defaultChainId()) != null ? _ref : ChainId.MAINNET;
|
|
2320
|
-
const chainId = findSupportedNetwork(networkId).chainId;
|
|
2321
|
-
const connectDetails = {
|
|
2322
|
-
connected: true,
|
|
2323
|
-
chainId: ethers.toQuantity(chainId)
|
|
2324
|
-
};
|
|
2325
|
-
if (options && options.authorize) {
|
|
2326
|
-
// Perform ethauth eip712 request and construct the ConnectDetails response
|
|
2327
|
-
// including the auth proof
|
|
2328
|
-
const authOptions = {
|
|
2329
|
-
app: options.app,
|
|
2330
|
-
origin: options.origin,
|
|
2331
|
-
expiry: options.expiry,
|
|
2332
|
-
nonce: options.authorizeNonce
|
|
2333
|
-
};
|
|
2334
|
-
// if (typeof(options.authorize) === 'object') {
|
|
2335
|
-
// authOptions = { ...authOptions, ...options.authorize }
|
|
2336
|
-
// }
|
|
2337
|
-
|
|
2338
|
-
try {
|
|
2339
|
-
// TODO: Either implement account as a signer, or change signAuthorization to accept an account
|
|
2340
|
-
connectDetails.proof = await signAuthorization(this.account, chainId, authOptions);
|
|
2341
|
-
} catch (err) {
|
|
2342
|
-
logger.warn(`connect, signAuthorization failed for options: ${JSON.stringify(options)}, due to: ${err.message}`);
|
|
2343
|
-
return {
|
|
2344
|
-
connected: false,
|
|
2345
|
-
chainId: '0x0',
|
|
2346
|
-
error: `signAuthorization failed: ${err.message}`
|
|
2347
|
-
};
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2350
|
-
|
|
2351
|
-
// Build session response for connect details
|
|
2352
|
-
connectDetails.session = this.walletSession(chainId);
|
|
2353
|
-
return connectDetails;
|
|
2354
|
-
}
|
|
2355
|
-
// sendMessageRequest will unwrap the ProviderMessageRequest and send it to the JsonRpcHandler
|
|
2356
|
-
// (aka, the signer in this instance) and then responds with a wrapped response of
|
|
2357
|
-
// ProviderMessageResponse to be sent over the transport
|
|
2358
|
-
async sendMessageRequest(message) {
|
|
2359
|
-
var _message$clientVersio;
|
|
2360
|
-
// Older versions of the client require the response to be jsonrpc wrapped
|
|
2361
|
-
const majorVersion = Number(((_message$clientVersio = message.clientVersion) == null ? void 0 : _message$clientVersio.split('.')[0]) || '0');
|
|
2362
|
-
const isJsonRpcResponse = majorVersion < 2;
|
|
2363
|
-
const jsonRpcResponse = {
|
|
2364
|
-
id: message.data.id,
|
|
2365
|
-
jsonrpc: '2.0',
|
|
2366
|
-
result: null,
|
|
2367
|
-
error: undefined
|
|
2368
|
-
};
|
|
2369
|
-
try {
|
|
2370
|
-
const result = await this.request({
|
|
2371
|
-
method: message.data.method,
|
|
2372
|
-
params: message.data.params,
|
|
2373
|
-
chainId: message.chainId,
|
|
2374
|
-
origin: message.origin,
|
|
2375
|
-
projectAccessKey: message.projectAccessKey
|
|
2376
|
-
});
|
|
2377
|
-
return _extends({}, message, {
|
|
2378
|
-
data: isJsonRpcResponse ? _extends({}, jsonRpcResponse, {
|
|
2379
|
-
result
|
|
2380
|
-
}) : result
|
|
2381
|
-
});
|
|
2382
|
-
} catch (error) {
|
|
2383
|
-
return _extends({}, message, {
|
|
2384
|
-
data: isJsonRpcResponse ? _extends({}, jsonRpcResponse, {
|
|
2385
|
-
error
|
|
2386
|
-
}) : {
|
|
2387
|
-
error
|
|
2388
|
-
}
|
|
2389
|
-
});
|
|
2390
|
-
}
|
|
2391
|
-
}
|
|
2392
|
-
async request(request) {
|
|
2393
|
-
await this.getAccount();
|
|
2394
|
-
try {
|
|
2395
|
-
var _this$account, _request$chainId;
|
|
2396
|
-
// only allow public json rpc method to the provider when user is not logged in, aka signer is not set
|
|
2397
|
-
if ((!this.account || this.account === null) && !permittedJsonRpcMethods.includes(request.method)) {
|
|
2398
|
-
// throw new Error(`not logged in. ${request.method} is unavailable`)
|
|
2399
|
-
throw ErrSignedInRequired;
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
|
-
// wallet account
|
|
2403
|
-
const account = this.account;
|
|
2404
|
-
if (!account) {
|
|
2405
|
-
throw new Error('WalletRequestHandler: wallet account is not configured');
|
|
2406
|
-
}
|
|
2407
|
-
|
|
2408
|
-
// fetch the provider for the specific chain, or undefined will select defaultChain
|
|
2409
|
-
const provider = (_this$account = this.account) == null ? void 0 : _this$account.providerFor((_request$chainId = request.chainId) != null ? _request$chainId : this.defaultChainId());
|
|
2410
|
-
if (!provider) {
|
|
2411
|
-
throw new Error(`WalletRequestHandler: wallet provider is not configured for chainId ${request.chainId}`);
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
// NOTE: we cannot use `instanceof ethers.JsonRpcProvider` because despite that we know
|
|
2415
|
-
// its the proper type, its not resolving properly at runtime during the pupeteer e2e tests.
|
|
2416
|
-
// Switching to the type guard function below is safer and more reliable.
|
|
2417
|
-
//
|
|
2418
|
-
// const jsonRpcProvider = provider instanceof ethers.JsonRpcProvider ? provider : undefined
|
|
2419
|
-
const jsonRpcProvider = isJsonRpcProvider(provider) ? provider : undefined;
|
|
2420
|
-
switch (request.method) {
|
|
2421
|
-
case 'net_version':
|
|
2422
|
-
{
|
|
2423
|
-
if (!jsonRpcProvider) {
|
|
2424
|
-
throw new Error(`Account provider doesn't support send method`);
|
|
2425
|
-
}
|
|
2426
|
-
return await jsonRpcProvider.send('net_version', []);
|
|
2427
|
-
}
|
|
2428
|
-
case 'eth_chainId':
|
|
2429
|
-
{
|
|
2430
|
-
if (!jsonRpcProvider) {
|
|
2431
|
-
throw new Error(`Account provider doesn't support send method`);
|
|
2432
|
-
}
|
|
2433
|
-
return await jsonRpcProvider.send('eth_chainId', []);
|
|
2434
|
-
}
|
|
2435
|
-
case 'eth_accounts':
|
|
2436
|
-
{
|
|
2437
|
-
return [account.address];
|
|
2438
|
-
}
|
|
2439
|
-
case 'eth_getBalance':
|
|
2440
|
-
{
|
|
2441
|
-
const [accountAddress, blockTag] = request.params;
|
|
2442
|
-
const walletBalance = await provider.getBalance(accountAddress, blockTag);
|
|
2443
|
-
return toHexString(walletBalance);
|
|
2444
|
-
}
|
|
2445
|
-
case 'sequence_sign':
|
|
2446
|
-
case 'personal_sign':
|
|
2447
|
-
case 'eth_sign':
|
|
2448
|
-
{
|
|
2449
|
-
// note: message from json-rpc input is in hex format
|
|
2450
|
-
let message;
|
|
2451
|
-
|
|
2452
|
-
// there is a difference in the order of the params:
|
|
2453
|
-
// sequence_sign, personal_sign: [data, address]
|
|
2454
|
-
// eth_sign: [address, data]
|
|
2455
|
-
switch (request.method) {
|
|
2456
|
-
case 'sequence_sign':
|
|
2457
|
-
case 'personal_sign':
|
|
2458
|
-
{
|
|
2459
|
-
const [data, _address] = request.params;
|
|
2460
|
-
message = data;
|
|
2461
|
-
break;
|
|
2462
|
-
}
|
|
2463
|
-
case 'eth_sign':
|
|
2464
|
-
{
|
|
2465
|
-
const [_address, data] = request.params;
|
|
2466
|
-
message = data;
|
|
2467
|
-
break;
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
let sig = '';
|
|
2471
|
-
|
|
2472
|
-
// Message must be prefixed with "\x19Ethereum Signed Message:\n"
|
|
2473
|
-
// as defined by EIP-191
|
|
2474
|
-
const prefixedMessage = prefixEIP191Message(message);
|
|
2475
|
-
|
|
2476
|
-
// TODO:
|
|
2477
|
-
// if (process.env.TEST_MODE === 'true' && this.prompter === null) {
|
|
2478
|
-
const sequenceVerified = request.method === 'sequence_sign';
|
|
2479
|
-
if (this.prompter === null) {
|
|
2480
|
-
var _request$chainId2;
|
|
2481
|
-
// prompter is null, so we'll sign from here
|
|
2482
|
-
sig = await account.signMessage(prefixedMessage, (_request$chainId2 = request.chainId) != null ? _request$chainId2 : this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore');
|
|
2483
|
-
} else {
|
|
2484
|
-
sig = await this.prompter.promptSignMessage({
|
|
2485
|
-
chainId: request.chainId,
|
|
2486
|
-
message: ethers.getBytes(prefixedMessage),
|
|
2487
|
-
eip6492: sequenceVerified
|
|
2488
|
-
}, request.origin, request.projectAccessKey);
|
|
2489
|
-
}
|
|
2490
|
-
if (sig && sig.length > 0) {
|
|
2491
|
-
return sig;
|
|
2492
|
-
} else {
|
|
2493
|
-
// The user has declined the request when value is null
|
|
2494
|
-
throw new Error('declined by user');
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
case 'sequence_signTypedData_v4':
|
|
2498
|
-
case 'eth_signTypedData':
|
|
2499
|
-
case 'eth_signTypedData_v4':
|
|
2500
|
-
{
|
|
2501
|
-
// note: signingAddress from json-rpc input is in hex format, and typedDataObject
|
|
2502
|
-
// should be an object, but in some instances may be double string encoded
|
|
2503
|
-
const [signingAddress, typedDataObject] = request.params;
|
|
2504
|
-
let typedData = undefined;
|
|
2505
|
-
if (typeof typedDataObject === 'string') {
|
|
2506
|
-
try {
|
|
2507
|
-
typedData = JSON.parse(typedDataObject);
|
|
2508
|
-
} catch (e) {
|
|
2509
|
-
console.warn('walletRequestHandler: error parsing typedData', e);
|
|
2510
|
-
}
|
|
2511
|
-
} else {
|
|
2512
|
-
typedData = typedDataObject;
|
|
2513
|
-
}
|
|
2514
|
-
if (!typedData || !typedData.domain || !typedData.types || !typedData.message) {
|
|
2515
|
-
throw new Error('invalid typedData object');
|
|
2516
|
-
}
|
|
2517
|
-
let sig = '';
|
|
2518
|
-
const sequenceVerified = request.method === 'sequence_signTypedData_v4';
|
|
2519
|
-
if (this.prompter === null) {
|
|
2520
|
-
var _request$chainId3;
|
|
2521
|
-
// prompter is null, so we'll sign from here
|
|
2522
|
-
sig = await account.signTypedData(typedData.domain, typedData.types, typedData.message, (_request$chainId3 = request.chainId) != null ? _request$chainId3 : this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore');
|
|
2523
|
-
} else {
|
|
2524
|
-
sig = await this.prompter.promptSignMessage({
|
|
2525
|
-
chainId: request.chainId,
|
|
2526
|
-
typedData: typedData,
|
|
2527
|
-
eip6492: sequenceVerified
|
|
2528
|
-
}, request.origin, request.projectAccessKey);
|
|
2529
|
-
}
|
|
2530
|
-
if (sig && sig.length > 0) {
|
|
2531
|
-
return sig;
|
|
2532
|
-
} else {
|
|
2533
|
-
// The user has declined the request when value is null
|
|
2534
|
-
throw new Error('declined by user');
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
case 'eth_sendTransaction':
|
|
2538
|
-
{
|
|
2539
|
-
// https://eth.wiki/json-rpc/API#eth_sendtransaction
|
|
2540
|
-
const transactionParams = fromExtended(request.params[0]).map(tx => {
|
|
2541
|
-
// eth_sendTransaction uses 'gas'
|
|
2542
|
-
// ethers and sequence use 'gasLimit'
|
|
2543
|
-
if ('gas' in tx && tx.gasLimit === undefined) {
|
|
2544
|
-
tx.gasLimit = tx.gas;
|
|
2545
|
-
delete tx.gas;
|
|
2546
|
-
}
|
|
2547
|
-
return tx;
|
|
2548
|
-
});
|
|
2549
|
-
validateTransactionRequest(account.address, transactionParams);
|
|
2550
|
-
let txnHash = '';
|
|
2551
|
-
if (this.prompter === null) {
|
|
2552
|
-
var _request$chainId4, _txnResponse$hash;
|
|
2553
|
-
// prompter is null, so we'll send from here
|
|
2554
|
-
const txnResponse = await account.sendTransaction(transactionParams, (_request$chainId4 = request.chainId) != null ? _request$chainId4 : this.defaultChainId());
|
|
2555
|
-
txnHash = (_txnResponse$hash = txnResponse == null ? void 0 : txnResponse.hash) != null ? _txnResponse$hash : '';
|
|
2556
|
-
} else {
|
|
2557
|
-
// prompt user to provide the response
|
|
2558
|
-
txnHash = await this.prompter.promptSendTransaction(transactionParams, request.chainId, request.origin, request.projectAccessKey);
|
|
2559
|
-
}
|
|
2560
|
-
if (txnHash) {
|
|
2561
|
-
return txnHash;
|
|
2562
|
-
} else {
|
|
2563
|
-
// The user has declined the request when value is null
|
|
2564
|
-
throw new Error('declined by user');
|
|
2565
|
-
}
|
|
2566
|
-
}
|
|
2567
|
-
case 'eth_signTransaction':
|
|
2568
|
-
{
|
|
2569
|
-
// https://eth.wiki/json-rpc/API#eth_signTransaction
|
|
2570
|
-
const [transaction] = request.params;
|
|
2571
|
-
const sender = ethers.getAddress(transaction.from);
|
|
2572
|
-
if (sender !== account.address) {
|
|
2573
|
-
throw new Error('sender address does not match wallet');
|
|
2574
|
-
}
|
|
2575
|
-
validateTransactionRequest(account.address, transaction);
|
|
2576
|
-
if (this.prompter === null) {
|
|
2577
|
-
var _request$chainId5;
|
|
2578
|
-
// The eth_signTransaction method expects a `string` return value we instead return a `SignedTransactions` object,
|
|
2579
|
-
// this can only be broadcasted using an RPC provider with support for signed Sequence transactions, like this one.
|
|
2580
|
-
//
|
|
2581
|
-
// TODO: verify serializing / transporting the SignedTransaction object works as expected, most likely however
|
|
2582
|
-
// we will want to resolveProperties the big number values to hex strings
|
|
2583
|
-
return await account.signTransactions(transaction, (_request$chainId5 = request.chainId) != null ? _request$chainId5 : this.defaultChainId());
|
|
2584
|
-
} else {
|
|
2585
|
-
return await this.prompter.promptSignTransaction(transaction, request.chainId, request.origin, request.projectAccessKey);
|
|
2586
|
-
}
|
|
2587
|
-
}
|
|
2588
|
-
case 'eth_sendRawTransaction':
|
|
2589
|
-
{
|
|
2590
|
-
// NOTE: we're not using a prompter here as the transaction is already signed
|
|
2591
|
-
// and would have prompted the user upon signing.
|
|
2592
|
-
|
|
2593
|
-
// https://eth.wiki/json-rpc/API#eth_sendRawTransaction
|
|
2594
|
-
if (commons.transaction.isSignedTransactionBundle(request.params[0])) {
|
|
2595
|
-
const txChainId = Number(request.params[0].chainId);
|
|
2596
|
-
const tx = await account.relayer(txChainId).relay(request.params[0]);
|
|
2597
|
-
return tx.hash;
|
|
2598
|
-
} else {
|
|
2599
|
-
const tx = await provider.broadcastTransaction(request.params[0]);
|
|
2600
|
-
return tx.hash;
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
2603
|
-
case 'eth_getTransactionCount':
|
|
2604
|
-
{
|
|
2605
|
-
const address = ethers.getAddress(request.params[0]);
|
|
2606
|
-
const tag = request.params[1];
|
|
2607
|
-
|
|
2608
|
-
// TODO: Maybe we should fetch this data from the relayer or from the reader
|
|
2609
|
-
// but for now we keep it simple and just use the provider
|
|
2610
|
-
|
|
2611
|
-
const count = await provider.getTransactionCount(address, tag);
|
|
2612
|
-
return toHexString(BigInt(count));
|
|
2613
|
-
}
|
|
2614
|
-
case 'eth_blockNumber':
|
|
2615
|
-
{
|
|
2616
|
-
return await provider.getBlockNumber();
|
|
2617
|
-
}
|
|
2618
|
-
case 'eth_getBlockByNumber':
|
|
2619
|
-
{
|
|
2620
|
-
return await provider.getBlock(request.params[0] /* , jsonRpcRequest.params[1] */);
|
|
2621
|
-
}
|
|
2622
|
-
case 'eth_getBlockByHash':
|
|
2623
|
-
{
|
|
2624
|
-
return await provider.getBlock(request.params[0] /* , jsonRpcRequest.params[1] */);
|
|
2625
|
-
}
|
|
2626
|
-
case 'eth_getTransactionByHash':
|
|
2627
|
-
{
|
|
2628
|
-
return await provider.getTransaction(request.params[0]);
|
|
2629
|
-
}
|
|
2630
|
-
case 'eth_call':
|
|
2631
|
-
{
|
|
2632
|
-
const [transactionObject, blockTag] = request.params;
|
|
2633
|
-
return await provider.call(_extends({}, transactionObject, {
|
|
2634
|
-
blockTag
|
|
2635
|
-
}));
|
|
2636
|
-
}
|
|
2637
|
-
case 'eth_getCode':
|
|
2638
|
-
{
|
|
2639
|
-
const [contractAddress, blockTag] = request.params;
|
|
2640
|
-
return await provider.getCode(contractAddress, blockTag);
|
|
2641
|
-
}
|
|
2642
|
-
case 'eth_estimateGas':
|
|
2643
|
-
{
|
|
2644
|
-
const [transactionObject] = request.params;
|
|
2645
|
-
return await provider.estimateGas(transactionObject);
|
|
2646
|
-
}
|
|
2647
|
-
case 'eth_gasPrice':
|
|
2648
|
-
{
|
|
2649
|
-
const feeData = await provider.getFeeData();
|
|
2650
|
-
return feeData.gasPrice === null ? null : toHexString(feeData.gasPrice);
|
|
2651
|
-
}
|
|
2652
|
-
case 'wallet_switchEthereumChain':
|
|
2653
|
-
{
|
|
2654
|
-
const [switchParams] = request.params;
|
|
2655
|
-
if (!switchParams.chainId || switchParams.chainId.length === 0) {
|
|
2656
|
-
throw new Error('invalid chainId');
|
|
2657
|
-
}
|
|
2658
|
-
const chainId = BigInt(switchParams.chainId);
|
|
2659
|
-
this.setDefaultChainId(Number(chainId));
|
|
2660
|
-
return null; // success
|
|
2661
|
-
}
|
|
2662
|
-
|
|
2663
|
-
// smart wallet method
|
|
2664
|
-
case 'sequence_getWalletContext':
|
|
2665
|
-
{
|
|
2666
|
-
return account.contexts;
|
|
2667
|
-
}
|
|
2668
|
-
|
|
2669
|
-
// smart wallet method
|
|
2670
|
-
case 'sequence_getWalletConfig':
|
|
2671
|
-
{
|
|
2672
|
-
const [chainId] = request.params;
|
|
2673
|
-
if (chainId) {
|
|
2674
|
-
return [(await account.status(chainId)).onChain.config];
|
|
2675
|
-
} else {
|
|
2676
|
-
return await Promise.all(account.networks.map(async function (network) {
|
|
2677
|
-
const status = await account.status(network.chainId);
|
|
2678
|
-
return status.onChain.config;
|
|
2679
|
-
}));
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
|
|
2683
|
-
// smart wallet method
|
|
2684
|
-
case 'sequence_getWalletState':
|
|
2685
|
-
{
|
|
2686
|
-
const [chainId] = request.params;
|
|
2687
|
-
// TODO: Add getWalletState to the Signer interface
|
|
2688
|
-
if (chainId) {
|
|
2689
|
-
return [getLegacyWalletState(chainId, await account.status(chainId))];
|
|
2690
|
-
} else {
|
|
2691
|
-
return await Promise.all(account.networks.map(async function (network) {
|
|
2692
|
-
const status = await account.status(network.chainId);
|
|
2693
|
-
return getLegacyWalletState(network.chainId, status);
|
|
2694
|
-
}));
|
|
2695
|
-
}
|
|
2696
|
-
}
|
|
2697
|
-
|
|
2698
|
-
// smart wallet method
|
|
2699
|
-
case 'sequence_getNetworks':
|
|
2700
|
-
{
|
|
2701
|
-
// NOTE: must ensure that the response result below returns clean serialized data, which is to omit
|
|
2702
|
-
// the provider and relayer objects and only return the urls so can be reinstantiated on dapp side.
|
|
2703
|
-
// This is handled by this.getNetworks() but noted here for future readers.
|
|
2704
|
-
return await this.getNetworks(true);
|
|
2705
|
-
}
|
|
2706
|
-
case 'sequence_isSequence':
|
|
2707
|
-
{
|
|
2708
|
-
return true;
|
|
2709
|
-
}
|
|
2710
|
-
|
|
2711
|
-
// smart wallet method
|
|
2712
|
-
case 'sequence_updateConfig':
|
|
2713
|
-
{
|
|
2714
|
-
throw new Error('sequence_updateConfig method is not allowed from a dapp');
|
|
2715
|
-
// NOTE: method is disabled as we don't need a dapp to request to update a config.
|
|
2716
|
-
// However, if we ever want this, we can enable it but must also use the prompter
|
|
2717
|
-
// for confirmation.
|
|
2718
|
-
//
|
|
2719
|
-
// const [newConfig] = request.params
|
|
2720
|
-
// return await signer.updateConfig(newConfig)
|
|
2721
|
-
}
|
|
2722
|
-
|
|
2723
|
-
// smart wallet method
|
|
2724
|
-
case 'sequence_publishConfig':
|
|
2725
|
-
{
|
|
2726
|
-
throw new Error('sequence_publishConfig method is not allowed from a dapp');
|
|
2727
|
-
}
|
|
2728
|
-
|
|
2729
|
-
// relayer method
|
|
2730
|
-
case 'sequence_gasRefundOptions':
|
|
2731
|
-
{
|
|
2732
|
-
// TODO
|
|
2733
|
-
break;
|
|
2734
|
-
}
|
|
2735
|
-
|
|
2736
|
-
// relayer method
|
|
2737
|
-
case 'sequence_getNonce':
|
|
2738
|
-
{
|
|
2739
|
-
// TODO
|
|
2740
|
-
break;
|
|
2741
|
-
}
|
|
2742
|
-
|
|
2743
|
-
// relayer method
|
|
2744
|
-
case 'sequence_relay':
|
|
2745
|
-
{
|
|
2746
|
-
// TODO
|
|
2747
|
-
break;
|
|
2748
|
-
}
|
|
2749
|
-
|
|
2750
|
-
// set default network of wallet
|
|
2751
|
-
case 'sequence_setDefaultNetwork':
|
|
2752
|
-
{
|
|
2753
|
-
const [defaultChainId] = request.params;
|
|
2754
|
-
if (!defaultChainId) {
|
|
2755
|
-
throw new Error('invalid request, method argument defaultChainId cannot be empty');
|
|
2756
|
-
}
|
|
2757
|
-
this.setDefaultChainId(defaultChainId);
|
|
2758
|
-
return await this.getNetworks(true);
|
|
2759
|
-
}
|
|
2760
|
-
default:
|
|
2761
|
-
{
|
|
2762
|
-
if (!jsonRpcProvider) {
|
|
2763
|
-
throw new Error(`Account provider doesn't support send method`);
|
|
2764
|
-
}
|
|
2765
|
-
|
|
2766
|
-
// NOTE: provider here will be chain-bound if chainId is provided
|
|
2767
|
-
return await jsonRpcProvider.send(request.method, request.params);
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2770
|
-
} catch (err) {
|
|
2771
|
-
logger.error(err);
|
|
2772
|
-
throw {
|
|
2773
|
-
message: typeof err == 'string' ? err : (err == null ? void 0 : err.message) || 'unkown error',
|
|
2774
|
-
code: 4001
|
|
2775
|
-
};
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2778
|
-
on(event, fn) {
|
|
2779
|
-
this.events.on(event, fn);
|
|
2780
|
-
}
|
|
2781
|
-
once(event, fn) {
|
|
2782
|
-
this.events.once(event, fn);
|
|
2783
|
-
}
|
|
2784
|
-
async getAddress() {
|
|
2785
|
-
var _this$account$address, _this$account2;
|
|
2786
|
-
return (_this$account$address = (_this$account2 = this.account) == null ? void 0 : _this$account2.address) != null ? _this$account$address : '';
|
|
2787
|
-
}
|
|
2788
|
-
get openIntent() {
|
|
2789
|
-
return this._openIntent;
|
|
2790
|
-
}
|
|
2791
|
-
setOpenIntent(intent) {
|
|
2792
|
-
this._openIntent = intent;
|
|
2793
|
-
}
|
|
2794
|
-
get connectOptions() {
|
|
2795
|
-
return this._connectOptions;
|
|
2796
|
-
}
|
|
2797
|
-
setConnectOptions(options) {
|
|
2798
|
-
var _this$onConnectOption;
|
|
2799
|
-
this._connectOptions = options;
|
|
2800
|
-
(_this$onConnectOption = this.onConnectOptionsChange) == null || _this$onConnectOption.call(this, options);
|
|
2801
|
-
}
|
|
2802
|
-
async setDefaultChainId(chainId) {
|
|
2803
|
-
var _this$prompter4;
|
|
2804
|
-
await ((_this$prompter4 = this.prompter) == null ? void 0 : _this$prompter4.promptChangeNetwork(chainId));
|
|
2805
|
-
return this.defaultChainId();
|
|
2806
|
-
}
|
|
2807
|
-
async getNetworks(jsonRpcResponse) {
|
|
2808
|
-
if (!this.account) {
|
|
2809
|
-
logger.warn('signer not set: getNetworks is returning an empty list');
|
|
2810
|
-
return [];
|
|
2811
|
-
}
|
|
2812
|
-
if (jsonRpcResponse) {
|
|
2813
|
-
// omit provider and relayer objects as they are not serializable
|
|
2814
|
-
return this.account.networks.map(n => {
|
|
2815
|
-
const network = _extends({}, n);
|
|
2816
|
-
network.provider = undefined;
|
|
2817
|
-
network.relayer = undefined;
|
|
2818
|
-
return network;
|
|
2819
|
-
});
|
|
2820
|
-
} else {
|
|
2821
|
-
return this.account.networks;
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
2824
|
-
walletSession(networkId) {
|
|
2825
|
-
if (!this.account) {
|
|
2826
|
-
return undefined;
|
|
2827
|
-
}
|
|
2828
|
-
const session = {
|
|
2829
|
-
walletContext: this.account.contexts,
|
|
2830
|
-
accountAddress: this.account.address,
|
|
2831
|
-
// The dapp shouldn't access the relayer directly, and the provider (as an object) is not serializable.
|
|
2832
|
-
networks: this.account.networks.map(n => _extends({}, n, {
|
|
2833
|
-
provider: undefined,
|
|
2834
|
-
relayer: undefined
|
|
2835
|
-
}))
|
|
2836
|
-
};
|
|
2837
|
-
if (networkId) {
|
|
2838
|
-
const network = findNetworkConfig(session.networks, networkId);
|
|
2839
|
-
if (network) {
|
|
2840
|
-
var _session$networks;
|
|
2841
|
-
// Delete the isDefaultChain property from the session network
|
|
2842
|
-
(_session$networks = session.networks) == null || _session$networks.forEach(n => delete n.isDefaultChain);
|
|
2843
|
-
|
|
2844
|
-
// Add the isDefaultChain property to the network with the given networkId
|
|
2845
|
-
network.isDefaultChain = true;
|
|
2846
|
-
}
|
|
2847
|
-
}
|
|
2848
|
-
return session;
|
|
2849
|
-
}
|
|
2850
|
-
notifyConnect(connectDetails, origin) {
|
|
2851
|
-
var _connectDetails$sessi;
|
|
2852
|
-
console.log('emit connect', connectDetails);
|
|
2853
|
-
this.events.emit('connect', connectDetails);
|
|
2854
|
-
if ((_connectDetails$sessi = connectDetails.session) != null && _connectDetails$sessi.accountAddress) {
|
|
2855
|
-
var _connectDetails$sessi2;
|
|
2856
|
-
this.events.emit('accountsChanged', [(_connectDetails$sessi2 = connectDetails.session) == null ? void 0 : _connectDetails$sessi2.accountAddress], origin);
|
|
2857
|
-
}
|
|
2858
|
-
}
|
|
2859
|
-
notifyDisconnect(origin) {
|
|
2860
|
-
this.events.emit('accountsChanged', [], origin);
|
|
2861
|
-
this.events.emit('disconnect', undefined, origin);
|
|
2862
|
-
}
|
|
2863
|
-
notifyChainChanged(chainId, origin) {
|
|
2864
|
-
this.events.emit('chainChanged', ethers.toQuantity(chainId), origin);
|
|
2865
|
-
}
|
|
2866
|
-
async notifyNetworks(networks) {
|
|
2867
|
-
const n = networks || (await this.getNetworks(true));
|
|
2868
|
-
this.events.emit('networks', n);
|
|
2869
|
-
if (n.length > 0) {
|
|
2870
|
-
const defaultNetwork = n.find(network => network.chainId === this.defaultChainId());
|
|
2871
|
-
if (defaultNetwork) {
|
|
2872
|
-
this.events.emit('chainChanged', ethers.toQuantity(defaultNetwork.chainId));
|
|
2873
|
-
}
|
|
2874
|
-
} else {
|
|
2875
|
-
this.events.emit('chainChanged', '0x0');
|
|
2876
|
-
}
|
|
2877
|
-
}
|
|
2878
|
-
async notifyWalletContext() {
|
|
2879
|
-
if (!this.account) {
|
|
2880
|
-
logger.warn('signer not set: skipping to notify wallet context');
|
|
2881
|
-
return;
|
|
2882
|
-
}
|
|
2883
|
-
const walletContext = this.account.contexts;
|
|
2884
|
-
this.events.emit('walletContext', walletContext);
|
|
2885
|
-
}
|
|
2886
|
-
notifyClose(error) {
|
|
2887
|
-
this.events.emit('close', error);
|
|
2888
|
-
}
|
|
2889
|
-
setAccount(account) {
|
|
2890
|
-
this.account = account;
|
|
2891
|
-
if (account !== undefined) {
|
|
2892
|
-
for (let i = 0; i < this.signerReadyCallbacks.length; i++) {
|
|
2893
|
-
this.signerReadyCallbacks[i]();
|
|
2894
|
-
}
|
|
2895
|
-
this.signerReadyCallbacks = [];
|
|
2896
|
-
}
|
|
2897
|
-
}
|
|
2898
|
-
async handleConfirmWalletDeployPrompt(prompter, account, sequenceVerified, chainId, origin) {
|
|
2899
|
-
// check if wallet is deployed and up to date, if not, prompt user to deploy
|
|
2900
|
-
// if no chainId is provided, we'll assume the wallet is auth chain wallet and is up to date
|
|
2901
|
-
if (!chainId) {
|
|
2902
|
-
return true;
|
|
2903
|
-
}
|
|
2904
|
-
const skipsDeploy = status => {
|
|
2905
|
-
return status.canOnchainValidate || status.original.version === 2 && sequenceVerified;
|
|
2906
|
-
};
|
|
2907
|
-
const status = await account.status(chainId);
|
|
2908
|
-
if (skipsDeploy(status)) {
|
|
2909
|
-
return true;
|
|
2910
|
-
}
|
|
2911
|
-
const promptResult = await prompter.promptConfirmWalletDeploy(chainId, origin);
|
|
2912
|
-
|
|
2913
|
-
// if client returned true, check again to make sure wallet is deployed and up to date
|
|
2914
|
-
if (promptResult) {
|
|
2915
|
-
const status2 = await account.status(chainId);
|
|
2916
|
-
if (skipsDeploy(status2)) {
|
|
2917
|
-
return true;
|
|
2918
|
-
} else {
|
|
2919
|
-
logger.error('WalletRequestHandler: result for promptConfirmWalletDeploy is not correct');
|
|
2920
|
-
return false;
|
|
2921
|
-
}
|
|
2922
|
-
}
|
|
2923
|
-
return false;
|
|
2924
|
-
}
|
|
2925
|
-
}
|
|
2926
|
-
function getLegacyWalletState(chainId, status) {
|
|
2927
|
-
return {
|
|
2928
|
-
context: status.original.context,
|
|
2929
|
-
config: status.onChain.config,
|
|
2930
|
-
address: commons.context.addressOf(status.original.context, status.original.imageHash),
|
|
2931
|
-
chainId,
|
|
2932
|
-
deployed: status.onChain.deployed,
|
|
2933
|
-
imageHash: status.imageHash,
|
|
2934
|
-
lastImageHash: status.onChain.imageHash,
|
|
2935
|
-
published: true,
|
|
2936
|
-
status
|
|
2937
|
-
};
|
|
2938
|
-
}
|
|
2939
|
-
const permittedJsonRpcMethods = ['net_version', 'eth_chainId', 'eth_getBalance', 'eth_getTransactionCount', 'eth_blockNumber', 'eth_getBlockByNumber', 'eth_getBlockByHash', 'eth_getTransactionByHash', 'eth_getCode', 'eth_estimateGas', 'eth_gasPrice', 'sequence_getWalletContext', 'sequence_getNetworks', 'sequence_setDefaultNetwork'];
|
|
2940
|
-
function isJsonRpcProvider(provider) {
|
|
2941
|
-
return provider && typeof provider === 'object' && typeof provider.send === 'function' && typeof provider.getNetwork === 'function';
|
|
2942
|
-
}
|
|
2943
|
-
|
|
2944
|
-
function _objectWithoutPropertiesLoose(r, e) {
|
|
2945
|
-
if (null == r) return {};
|
|
2946
|
-
var t = {};
|
|
2947
|
-
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
|
|
2948
|
-
if (e.includes(n)) continue;
|
|
2949
|
-
t[n] = r[n];
|
|
2950
|
-
}
|
|
2951
|
-
return t;
|
|
2952
|
-
}
|
|
2953
|
-
|
|
2954
|
-
class SequenceSigner {
|
|
2955
|
-
get utils() {
|
|
2956
|
-
return this.provider.utils;
|
|
2957
|
-
}
|
|
2958
|
-
constructor(client, provider) {
|
|
2959
|
-
this.client = client;
|
|
2960
|
-
this.provider = provider;
|
|
2961
|
-
this.singleNetworkSigners = {};
|
|
2962
|
-
this._isSequenceSigner = true;
|
|
2963
|
-
}
|
|
2964
|
-
async getAddress() {
|
|
2965
|
-
return this.client.getAddress();
|
|
2966
|
-
}
|
|
2967
|
-
|
|
2968
|
-
// This method shouldn't be used directly
|
|
2969
|
-
// it exists to maintain compatibility with ethers.Signer
|
|
2970
|
-
connect(provider) {
|
|
2971
|
-
if (!SequenceProvider.is(provider)) {
|
|
2972
|
-
throw new Error('SequenceSigner can only be connected to a SequenceProvider');
|
|
2973
|
-
}
|
|
2974
|
-
return new SequenceSigner(this.client, provider);
|
|
2975
|
-
}
|
|
2976
|
-
getSigner(chainId) {
|
|
2977
|
-
// The signer for the default network is this signer
|
|
2978
|
-
if (!chainId) {
|
|
2979
|
-
return this;
|
|
2980
|
-
}
|
|
2981
|
-
const useChainId = this.provider.toChainId(chainId);
|
|
2982
|
-
if (!this.singleNetworkSigners[useChainId]) {
|
|
2983
|
-
this.singleNetworkSigners[useChainId] = new SingleNetworkSequenceSigner(this.client, this.provider, useChainId);
|
|
2984
|
-
}
|
|
2985
|
-
return this.singleNetworkSigners[useChainId];
|
|
2986
|
-
}
|
|
2987
|
-
|
|
2988
|
-
/**
|
|
2989
|
-
* Resolves the chainId to use for the given request. If no chainId is provided,
|
|
2990
|
-
* it uses the chainId defined by the client (default chainId). This can be
|
|
2991
|
-
* overriden to build a single-network SequenceProvider.
|
|
2992
|
-
*/
|
|
2993
|
-
useChainId(chainId) {
|
|
2994
|
-
return this.provider.toChainId(chainId) || this.client.getChainId();
|
|
2995
|
-
}
|
|
2996
|
-
async signMessage(message, options) {
|
|
2997
|
-
const {
|
|
2998
|
-
eip6492 = true
|
|
2999
|
-
} = options || {};
|
|
3000
|
-
const chainId = this.useChainId(options == null ? void 0 : options.chainId);
|
|
3001
|
-
return this.client.signMessage(message, {
|
|
3002
|
-
eip6492,
|
|
3003
|
-
chainId
|
|
3004
|
-
});
|
|
3005
|
-
}
|
|
3006
|
-
async signTypedData(domain, types, message, options) {
|
|
3007
|
-
const {
|
|
3008
|
-
eip6492 = true
|
|
3009
|
-
} = options || {};
|
|
3010
|
-
const chainId = this.useChainId(options == null ? void 0 : options.chainId);
|
|
3011
|
-
return this.client.signTypedData({
|
|
3012
|
-
domain,
|
|
3013
|
-
types,
|
|
3014
|
-
message
|
|
3015
|
-
}, {
|
|
3016
|
-
eip6492,
|
|
3017
|
-
chainId
|
|
3018
|
-
});
|
|
3019
|
-
}
|
|
3020
|
-
getProvider(chainId) {
|
|
3021
|
-
return this.provider.getProvider(chainId);
|
|
3022
|
-
}
|
|
3023
|
-
async sendTransaction(transaction, options) {
|
|
3024
|
-
const chainId = this.useChainId(options == null ? void 0 : options.chainId);
|
|
3025
|
-
const resolved = await resolveArrayProperties(transaction);
|
|
3026
|
-
const txHash = await this.client.sendTransaction(resolved, {
|
|
3027
|
-
chainId
|
|
3028
|
-
});
|
|
3029
|
-
const provider = this.getProvider(chainId);
|
|
3030
|
-
try {
|
|
3031
|
-
const result = await new Promise(resolve => {
|
|
3032
|
-
const check = async function check() {
|
|
3033
|
-
const tx = await provider.getTransaction(txHash);
|
|
3034
|
-
if (tx !== null) {
|
|
3035
|
-
return resolve(tx);
|
|
3036
|
-
}
|
|
3037
|
-
await provider.once('block', check);
|
|
3038
|
-
};
|
|
3039
|
-
check();
|
|
3040
|
-
});
|
|
3041
|
-
return result;
|
|
3042
|
-
} catch (err) {
|
|
3043
|
-
err.transactionHash = txHash;
|
|
3044
|
-
throw err;
|
|
3045
|
-
}
|
|
3046
|
-
}
|
|
3047
|
-
async getWalletConfig(chainId) {
|
|
3048
|
-
const useChainId = this.useChainId(chainId);
|
|
3049
|
-
return this.client.getOnchainWalletConfig({
|
|
3050
|
-
chainId: useChainId
|
|
3051
|
-
});
|
|
3052
|
-
}
|
|
3053
|
-
getNetworks() {
|
|
3054
|
-
return this.client.getNetworks();
|
|
3055
|
-
}
|
|
3056
|
-
async getBalance(blockTag, optionals) {
|
|
3057
|
-
const provider = this.getProvider(optionals == null ? void 0 : optionals.chainId);
|
|
3058
|
-
return provider.getBalance(this.getAddress(), blockTag);
|
|
3059
|
-
}
|
|
3060
|
-
async estimateGas(transaction, optionals) {
|
|
3061
|
-
return this.getProvider(optionals == null ? void 0 : optionals.chainId).estimateGas(transaction);
|
|
3062
|
-
}
|
|
3063
|
-
async call(transaction, optionals) {
|
|
3064
|
-
return this.getProvider(optionals == null ? void 0 : optionals.chainId).call(transaction);
|
|
3065
|
-
}
|
|
3066
|
-
getChainId() {
|
|
3067
|
-
return Promise.resolve(this.client.getChainId());
|
|
3068
|
-
}
|
|
3069
|
-
async getFeeData(optionals) {
|
|
3070
|
-
return this.getProvider(optionals == null ? void 0 : optionals.chainId).getFeeData();
|
|
3071
|
-
}
|
|
3072
|
-
async resolveName(name) {
|
|
3073
|
-
const res = await this.provider.resolveName(name);
|
|
3074
|
-
|
|
3075
|
-
// For some reason ethers.Signer expects this to return `string`
|
|
3076
|
-
// but ethers.Provider expects this to return `string | null`.
|
|
3077
|
-
// The signer doesn't have any other source of information, so we'll
|
|
3078
|
-
// fail if the provider doesn't return a result.
|
|
3079
|
-
if (res === null) {
|
|
3080
|
-
throw new Error(`ENS name not found: ${name}`);
|
|
3081
|
-
}
|
|
3082
|
-
return res;
|
|
3083
|
-
}
|
|
3084
|
-
_checkProvider(_operation) {
|
|
3085
|
-
// We always have a provider, so this is a noop
|
|
3086
|
-
}
|
|
3087
|
-
getNonce(_blockTag) {
|
|
3088
|
-
throw new Error('SequenceSigner does not support getNonce');
|
|
3089
|
-
}
|
|
3090
|
-
populateCall(_transaction) {
|
|
3091
|
-
throw new Error('SequenceSigner does not support populateCall');
|
|
3092
|
-
}
|
|
3093
|
-
populateTransaction(_transaction) {
|
|
3094
|
-
throw new Error('SequenceSigner does not support populateTransaction');
|
|
3095
|
-
}
|
|
3096
|
-
checkTransaction(_transaction) {
|
|
3097
|
-
throw new Error('SequenceSigner does not support checkTransaction');
|
|
3098
|
-
}
|
|
3099
|
-
getTransactionCount(_blockTag) {
|
|
3100
|
-
// We could try returning the sequence nonce here
|
|
3101
|
-
// but we aren't sure how ethers will use this nonce
|
|
3102
|
-
throw new Error('SequenceSigner does not support getTransactionCount');
|
|
3103
|
-
}
|
|
3104
|
-
signTransaction(_transaction) {
|
|
3105
|
-
// We could implement signTransaction/sendTransaction here
|
|
3106
|
-
// but first we need a way of serializing these signed transactions
|
|
3107
|
-
// and it could lead to more trouble, because the dapp could try to send this transaction
|
|
3108
|
-
// using a different provider, which would fail.
|
|
3109
|
-
throw new Error('SequenceWallet does not support signTransaction, use sendTransaction instead.');
|
|
3110
|
-
}
|
|
3111
|
-
static is(cand) {
|
|
3112
|
-
return cand && typeof cand === 'object' && cand._isSequenceSigner === true;
|
|
3113
|
-
}
|
|
3114
|
-
}
|
|
3115
|
-
|
|
3116
|
-
/**
|
|
3117
|
-
* This is the same provider, but it only allows a single network at a time.
|
|
3118
|
-
* the network defined by the constructor is the only one that can be used.
|
|
3119
|
-
*
|
|
3120
|
-
* Attempting to call any method with a different network will throw an error.
|
|
3121
|
-
* Attempting to change the network of this provider will throw an error.
|
|
3122
|
-
*
|
|
3123
|
-
* NOTICE: These networks won't support ENS unless they are the mainnet.
|
|
3124
|
-
*/
|
|
3125
|
-
class SingleNetworkSequenceSigner extends SequenceSigner {
|
|
3126
|
-
constructor(client, provider, chainId) {
|
|
3127
|
-
super(client, provider.getProvider(chainId));
|
|
3128
|
-
this.chainId = chainId;
|
|
3129
|
-
this._isSingleNetworkSequenceSigner = true;
|
|
3130
|
-
}
|
|
3131
|
-
_useChainId(chainId) {
|
|
3132
|
-
const provided = this.provider.toChainId(chainId);
|
|
3133
|
-
if (provided && provided !== this.chainId) {
|
|
3134
|
-
throw new Error(`This signer only supports the network ${this.chainId}, but ${provided} was requested.`);
|
|
3135
|
-
}
|
|
3136
|
-
return provided || this.provider.toChainId(this.chainId);
|
|
3137
|
-
}
|
|
3138
|
-
useChainId(chainId) {
|
|
3139
|
-
return this._useChainId(chainId);
|
|
3140
|
-
}
|
|
3141
|
-
getChainId() {
|
|
3142
|
-
return Promise.resolve(this.provider.toChainId(this.chainId));
|
|
3143
|
-
}
|
|
3144
|
-
|
|
3145
|
-
/**
|
|
3146
|
-
* Override getProvider and getSigner so they always use `useChainId`
|
|
3147
|
-
* this way they can't return providers and signers that can switch networks,
|
|
3148
|
-
* or that don't match the chainId of this signer.
|
|
3149
|
-
*/
|
|
3150
|
-
getProvider(chainId) {
|
|
3151
|
-
return super.getProvider(this._useChainId(chainId));
|
|
3152
|
-
}
|
|
3153
|
-
getSigner(chainId) {
|
|
3154
|
-
if (this._useChainId(chainId) !== this.chainId) {
|
|
3155
|
-
throw new Error(`Unreachable code`);
|
|
3156
|
-
}
|
|
3157
|
-
return this;
|
|
3158
|
-
}
|
|
3159
|
-
static is(cand) {
|
|
3160
|
-
return cand && typeof cand === 'object' && cand._isSingleNetworkSequenceSigner === true;
|
|
3161
|
-
}
|
|
3162
|
-
}
|
|
3163
|
-
|
|
3164
|
-
/**
|
|
3165
|
-
* This class is redundant with the SequenceSigner class, but it is here for now to
|
|
3166
|
-
* maintain compatibility with the old wallet API. Eventually we should move these
|
|
3167
|
-
* methods to the SequenceSigner class and deprecate this class.
|
|
3168
|
-
*/
|
|
3169
|
-
class WalletUtils {
|
|
3170
|
-
constructor(signer) {
|
|
3171
|
-
this.signer = signer;
|
|
3172
|
-
if (SingleNetworkSequenceSigner.is(signer)) {
|
|
3173
|
-
throw new Error('WalletUtils does not support SingleNetworkSequenceSigner');
|
|
3174
|
-
}
|
|
3175
|
-
}
|
|
3176
|
-
|
|
3177
|
-
// Sign message on a specified chain, or DefaultChain by default
|
|
3178
|
-
signMessage(message, chainId, eip6492) {
|
|
3179
|
-
return this.signer.signMessage(message, {
|
|
3180
|
-
chainId,
|
|
3181
|
-
eip6492
|
|
3182
|
-
});
|
|
3183
|
-
}
|
|
3184
|
-
|
|
3185
|
-
// Sign EIP-712 TypedData on a specified chain, or DefaultChain by default
|
|
3186
|
-
signTypedData(domain, types, message, chainId, eip6492) {
|
|
3187
|
-
return this.signer.signTypedData(domain, types, message, {
|
|
3188
|
-
chainId,
|
|
3189
|
-
eip6492
|
|
3190
|
-
});
|
|
3191
|
-
}
|
|
3192
|
-
|
|
3193
|
-
// Verify signature of a digest, one of a message, typedData or other
|
|
3194
|
-
async isValidSignature(address, digest, signature, chainId) {
|
|
3195
|
-
return isValidSignature(address, digest, signature, this.signer.getProvider(chainId));
|
|
3196
|
-
}
|
|
3197
|
-
|
|
3198
|
-
// Verify message signature
|
|
3199
|
-
async isValidMessageSignature(address, message, signature, chainId) {
|
|
3200
|
-
const provider = this.signer.getProvider(chainId);
|
|
3201
|
-
const prefixed = prefixEIP191Message(message);
|
|
3202
|
-
const digest = encodeMessageDigest(prefixed);
|
|
3203
|
-
return isValidSignature(address, digest, signature, provider);
|
|
3204
|
-
}
|
|
3205
|
-
|
|
3206
|
-
// Verify typedData signature
|
|
3207
|
-
isValidTypedDataSignature(address, typedData, signature, chainId) {
|
|
3208
|
-
return this.isValidSignature(address, encodeTypedDataDigest(typedData), signature, chainId);
|
|
3209
|
-
}
|
|
3210
|
-
|
|
3211
|
-
// sendTransaction()
|
|
3212
|
-
// sendTransactions()
|
|
3213
|
-
|
|
3214
|
-
// sendETH()
|
|
3215
|
-
// sendToken()
|
|
3216
|
-
// sendCoin() -- sugar for sendToken()
|
|
3217
|
-
// sendCollectible() -- sugar for sendToken()
|
|
3218
|
-
// callContract()
|
|
3219
|
-
|
|
3220
|
-
// transactionHistory()
|
|
3221
|
-
// getReceipt()
|
|
3222
|
-
// getLogs()
|
|
3223
|
-
// // ..
|
|
3224
|
-
|
|
3225
|
-
// validateSignature()
|
|
3226
|
-
// recoverWalletConfig()
|
|
3227
|
-
// recoverAddress()
|
|
3228
|
-
}
|
|
3229
|
-
|
|
3230
|
-
const _excluded = ["method"];
|
|
3231
|
-
const EIP1193EventTypes = ['connect', 'disconnect', 'chainChanged', 'accountsChanged'];
|
|
3232
|
-
class SequenceProvider extends ethers.AbstractProvider {
|
|
3233
|
-
constructor(client, providerFor, networks = allNetworks, options) {
|
|
3234
|
-
// We support a lot of networks
|
|
3235
|
-
// but we start with the default one
|
|
3236
|
-
super(client.getChainId(), options);
|
|
3237
|
-
|
|
3238
|
-
// Emit events as defined by EIP-1193
|
|
3239
|
-
this.client = client;
|
|
3240
|
-
this.providerFor = providerFor;
|
|
3241
|
-
this.networks = networks;
|
|
3242
|
-
this.options = options;
|
|
3243
|
-
this.singleNetworkProviders = {};
|
|
3244
|
-
this._isSequenceProvider = true;
|
|
3245
|
-
this.utils = void 0;
|
|
3246
|
-
this.signer = void 0;
|
|
3247
|
-
this.eip1193EventListeners = new Map();
|
|
3248
|
-
client.onConnect(details => {
|
|
3249
|
-
var _this$eip1193EventLis;
|
|
3250
|
-
//this.emit('connect', details)
|
|
3251
|
-
(_this$eip1193EventLis = this.eip1193EventListeners.get('connect')) == null || _this$eip1193EventLis.forEach(listener => listener(details));
|
|
3252
|
-
});
|
|
3253
|
-
client.onDisconnect(error => {
|
|
3254
|
-
var _this$eip1193EventLis2;
|
|
3255
|
-
//this.emit('disconnect', error)
|
|
3256
|
-
(_this$eip1193EventLis2 = this.eip1193EventListeners.get('disconnect')) == null || _this$eip1193EventLis2.forEach(listener => listener(error));
|
|
3257
|
-
});
|
|
3258
|
-
client.onDefaultChainIdChanged(chainId => {
|
|
3259
|
-
var _this$eip1193EventLis3;
|
|
3260
|
-
//this.emit('chainChanged', chainId)
|
|
3261
|
-
(_this$eip1193EventLis3 = this.eip1193EventListeners.get('chainChanged')) == null || _this$eip1193EventLis3.forEach(listener => listener(chainId));
|
|
3262
|
-
});
|
|
3263
|
-
client.onAccountsChanged(accounts => {
|
|
3264
|
-
var _this$eip1193EventLis4;
|
|
3265
|
-
//this.emit('accountsChanged', accounts)
|
|
3266
|
-
(_this$eip1193EventLis4 = this.eip1193EventListeners.get('accountsChanged')) == null || _this$eip1193EventLis4.forEach(listener => listener(accounts));
|
|
3267
|
-
});
|
|
3268
|
-
|
|
3269
|
-
// NOTICE: We don't emit 'open' and 'close' events
|
|
3270
|
-
// because these are handled by the library, and they
|
|
3271
|
-
// are not part of EIP-1193
|
|
3272
|
-
|
|
3273
|
-
// devs can still access them using
|
|
3274
|
-
// client.onOpen()
|
|
3275
|
-
// client.onClose()
|
|
3276
|
-
|
|
3277
|
-
// Create a Sequence signer too
|
|
3278
|
-
this.signer = new SequenceSigner(this.client, this);
|
|
3279
|
-
|
|
3280
|
-
// Create a utils instance
|
|
3281
|
-
this.utils = new WalletUtils(this.signer);
|
|
3282
|
-
}
|
|
3283
|
-
async on(event, listener) {
|
|
3284
|
-
if (EIP1193EventTypes.includes(event)) {
|
|
3285
|
-
const listeners = this.eip1193EventListeners.get(event) || new Set();
|
|
3286
|
-
listeners.add(listener);
|
|
3287
|
-
this.eip1193EventListeners.set(event, listeners);
|
|
3288
|
-
return this;
|
|
3289
|
-
}
|
|
3290
|
-
return super.on(event, listener);
|
|
3291
|
-
}
|
|
3292
|
-
async off(event, listener) {
|
|
3293
|
-
if (EIP1193EventTypes.includes(event)) {
|
|
3294
|
-
const listeners = this.eip1193EventListeners.get(event);
|
|
3295
|
-
if (listeners) {
|
|
3296
|
-
if (listener) {
|
|
3297
|
-
listeners.delete(listener);
|
|
3298
|
-
} else {
|
|
3299
|
-
listeners.clear();
|
|
3300
|
-
}
|
|
3301
|
-
}
|
|
3302
|
-
return this;
|
|
3303
|
-
}
|
|
3304
|
-
return super.off(event, listener);
|
|
3305
|
-
}
|
|
3306
|
-
getSigner(chainId) {
|
|
3307
|
-
return this.signer.getSigner(chainId);
|
|
3308
|
-
}
|
|
3309
|
-
connect(options) {
|
|
3310
|
-
return this.client.connect(options);
|
|
3311
|
-
}
|
|
3312
|
-
disconnect() {
|
|
3313
|
-
return this.client.disconnect();
|
|
3314
|
-
}
|
|
3315
|
-
isConnected() {
|
|
3316
|
-
return this.client.isConnected();
|
|
3317
|
-
}
|
|
3318
|
-
getSession() {
|
|
3319
|
-
return this.client.getSession();
|
|
3320
|
-
}
|
|
3321
|
-
listAccounts() {
|
|
3322
|
-
return [this.client.getAddress()];
|
|
3323
|
-
}
|
|
3324
|
-
|
|
3325
|
-
// @deprecated use getSigner() instead
|
|
3326
|
-
getAddress() {
|
|
3327
|
-
return this.client.getAddress();
|
|
3328
|
-
}
|
|
3329
|
-
getNetworks() {
|
|
3330
|
-
return this.client.getNetworks();
|
|
3331
|
-
}
|
|
3332
|
-
getChainId() {
|
|
3333
|
-
return this.client.getChainId();
|
|
3334
|
-
}
|
|
3335
|
-
setDefaultChainId(chainId) {
|
|
3336
|
-
return this.client.setDefaultChainId(this.toChainId(chainId));
|
|
3337
|
-
}
|
|
3338
|
-
isOpened() {
|
|
3339
|
-
return this.client.isOpened();
|
|
3340
|
-
}
|
|
3341
|
-
closeWallet() {
|
|
3342
|
-
return this.client.closeWallet();
|
|
3343
|
-
}
|
|
3344
|
-
getWalletContext() {
|
|
3345
|
-
return this.client.getWalletContext();
|
|
3346
|
-
}
|
|
3347
|
-
|
|
3348
|
-
// @deprecated use getSigner() instead
|
|
3349
|
-
async getWalletConfig(chainId) {
|
|
3350
|
-
const useChainId = await this.useChainId(chainId);
|
|
3351
|
-
return this.client.getOnchainWalletConfig({
|
|
3352
|
-
chainId: useChainId
|
|
3353
|
-
});
|
|
3354
|
-
}
|
|
3355
|
-
authorize(options) {
|
|
3356
|
-
// Just an alias for connect with authorize: true
|
|
3357
|
-
return this.client.connect(_extends({}, options, {
|
|
3358
|
-
authorize: true
|
|
3359
|
-
}));
|
|
3360
|
-
}
|
|
3361
|
-
async openWallet(path, intent) {
|
|
3362
|
-
await this.client.openWallet(path, intent);
|
|
3363
|
-
return true;
|
|
3364
|
-
}
|
|
3365
|
-
toChainId(chainId) {
|
|
3366
|
-
if (chainId === undefined) {
|
|
3367
|
-
return undefined;
|
|
3368
|
-
}
|
|
3369
|
-
const resolved = findNetworkConfig(this.networks, chainId);
|
|
3370
|
-
if (!resolved) {
|
|
3371
|
-
throw new Error(`Unsupported network ${chainId}`);
|
|
3372
|
-
}
|
|
3373
|
-
return resolved.chainId;
|
|
3374
|
-
}
|
|
3375
|
-
|
|
3376
|
-
/**
|
|
3377
|
-
* Resolves the chainId to use for the given request. If no chainId is provided,
|
|
3378
|
-
* it uses the chainId defined by the client (default chainId). This can be
|
|
3379
|
-
* overriden to build a single-network SequenceProvider.
|
|
3380
|
-
*/
|
|
3381
|
-
async useChainId(chainId) {
|
|
3382
|
-
return this.toChainId(chainId) || this.client.getChainId();
|
|
3383
|
-
}
|
|
3384
|
-
|
|
3385
|
-
/**
|
|
3386
|
-
* This generates a provider that ONLY works for the given chainId.
|
|
3387
|
-
* the generated provider can't switch networks, and can't handle requests
|
|
3388
|
-
* for other networks.
|
|
3389
|
-
*/
|
|
3390
|
-
|
|
3391
|
-
getProvider(chainId) {
|
|
3392
|
-
// The provider without a chainId is... this one
|
|
3393
|
-
if (!chainId) {
|
|
3394
|
-
return this;
|
|
3395
|
-
}
|
|
3396
|
-
const useChainId = this.toChainId(chainId);
|
|
3397
|
-
if (!this.singleNetworkProviders[useChainId]) {
|
|
3398
|
-
this.singleNetworkProviders[useChainId] = new SingleNetworkSequenceProvider(this.client, this.providerFor, useChainId, this.options);
|
|
3399
|
-
}
|
|
3400
|
-
return this.singleNetworkProviders[useChainId];
|
|
3401
|
-
}
|
|
3402
|
-
|
|
3403
|
-
/**
|
|
3404
|
-
* This returns a subprovider, this is a regular non-sequence provider that
|
|
3405
|
-
* can be used to fulfill read only requests on a given network.
|
|
3406
|
-
*/
|
|
3407
|
-
async _getSubprovider(chainId) {
|
|
3408
|
-
const useChainId = await this.useChainId(chainId);
|
|
3409
|
-
|
|
3410
|
-
// Whoever implements providerFrom should memoize the generated provider
|
|
3411
|
-
// otherwise every instance of SequenceProvider will create a new subprovider
|
|
3412
|
-
const provider = this.providerFor(useChainId);
|
|
3413
|
-
if (!provider) {
|
|
3414
|
-
throw new Error(`Unsupported network ${useChainId}`);
|
|
3415
|
-
}
|
|
3416
|
-
return provider;
|
|
3417
|
-
}
|
|
3418
|
-
async _perform(req) {
|
|
3419
|
-
var _provider$getRpcReque;
|
|
3420
|
-
const {
|
|
3421
|
-
method
|
|
3422
|
-
} = req,
|
|
3423
|
-
args = _objectWithoutPropertiesLoose(req, _excluded);
|
|
3424
|
-
const provider = await this._getSubprovider();
|
|
3425
|
-
const prepared = (_provider$getRpcReque = provider.getRpcRequest(req)) != null ? _provider$getRpcReque : {
|
|
3426
|
-
method,
|
|
3427
|
-
args: Object.values(args)
|
|
3428
|
-
};
|
|
3429
|
-
if (!prepared) {
|
|
3430
|
-
throw new Error(`Unsupported method ${req.method}`);
|
|
3431
|
-
}
|
|
3432
|
-
return provider.send(prepared.method, prepared.args);
|
|
3433
|
-
}
|
|
3434
|
-
async perform(method, params) {
|
|
3435
|
-
// First we check if the method should be handled by the client
|
|
3436
|
-
if (method === 'eth_chainId') {
|
|
3437
|
-
return ethers.toQuantity(await this.useChainId());
|
|
3438
|
-
}
|
|
3439
|
-
if (method === 'eth_accounts') {
|
|
3440
|
-
return [this.client.getAddress()];
|
|
3441
|
-
}
|
|
3442
|
-
if (method === 'wallet_switchEthereumChain') {
|
|
3443
|
-
const args = params[0];
|
|
3444
|
-
const chainId = normalizeChainId(args);
|
|
3445
|
-
return this.setDefaultChainId(chainId);
|
|
3446
|
-
}
|
|
3447
|
-
|
|
3448
|
-
// Usually these methods aren't used by calling the provider
|
|
3449
|
-
// but to maximize compatibility we support them too.
|
|
3450
|
-
// The correct way of accessing these methods is by using .getSigner()
|
|
3451
|
-
if (method === 'eth_sendTransaction' || method === 'eth_sign' || method === 'eth_signTypedData' || method === 'eth_signTypedData_v4' || method === 'personal_sign' ||
|
|
3452
|
-
// These methods will use EIP-6492
|
|
3453
|
-
// but this is handled directly by the wallet
|
|
3454
|
-
method === 'sequence_sign' || method === 'sequence_signTypedData_v4') {
|
|
3455
|
-
// We pass the chainId to the client, if we don't pass one
|
|
3456
|
-
// the client will use its own default chainId
|
|
3457
|
-
return this.client.request({
|
|
3458
|
-
method,
|
|
3459
|
-
params,
|
|
3460
|
-
chainId: this.getChainId()
|
|
3461
|
-
});
|
|
3462
|
-
}
|
|
3463
|
-
return this._perform(_extends({
|
|
3464
|
-
method
|
|
3465
|
-
}, params));
|
|
3466
|
-
}
|
|
3467
|
-
send(method, params) {
|
|
3468
|
-
return this.perform(method, params);
|
|
3469
|
-
}
|
|
3470
|
-
request(request) {
|
|
3471
|
-
return this.perform(request.method, request.params);
|
|
3472
|
-
}
|
|
3473
|
-
async _detectNetwork() {
|
|
3474
|
-
const chainId = this.client.getChainId();
|
|
3475
|
-
const found = findNetworkConfig(this.networks, chainId);
|
|
3476
|
-
if (!found) {
|
|
3477
|
-
throw new Error(`Unknown network ${chainId}`);
|
|
3478
|
-
}
|
|
3479
|
-
const network = new ethers.Network(found.name, found.chainId);
|
|
3480
|
-
return network;
|
|
3481
|
-
}
|
|
3482
|
-
async detectNetwork() {
|
|
3483
|
-
return this._detectNetwork();
|
|
3484
|
-
}
|
|
3485
|
-
|
|
3486
|
-
// Override most of the methods, so we add support for an optional chainId
|
|
3487
|
-
// argument, which is used to select the provider to use.
|
|
3488
|
-
//
|
|
3489
|
-
// NOTICE: We could use generics to avoid repeating the same code
|
|
3490
|
-
// but this would make the code harder to read, and it's not worth it
|
|
3491
|
-
// since we only have a few methods to override.
|
|
3492
|
-
|
|
3493
|
-
async waitForTransaction(transactionHash, confirmations, timeout, optionals) {
|
|
3494
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3495
|
-
return provider.waitForTransaction(transactionHash, confirmations, timeout);
|
|
3496
|
-
}
|
|
3497
|
-
async getBlockNumber(optionals) {
|
|
3498
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3499
|
-
return provider.getBlockNumber();
|
|
3500
|
-
}
|
|
3501
|
-
async getFeeData(optionals) {
|
|
3502
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3503
|
-
return provider.getFeeData();
|
|
3504
|
-
}
|
|
3505
|
-
async getBalance(addressOrName, blockTag, optionals) {
|
|
3506
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3507
|
-
return provider.getBalance(addressOrName, blockTag);
|
|
3508
|
-
}
|
|
3509
|
-
async getTransactionCount(addressOrName, blockTag, optionals) {
|
|
3510
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3511
|
-
return provider.getTransactionCount(addressOrName, blockTag);
|
|
3512
|
-
}
|
|
3513
|
-
async getCode(addressOrName, blockTag, optionals) {
|
|
3514
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3515
|
-
return provider.getCode(addressOrName, blockTag);
|
|
3516
|
-
}
|
|
3517
|
-
async getStorage(addressOrName, position, blockTag, optionals) {
|
|
3518
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3519
|
-
return provider.getStorage(addressOrName, position, blockTag);
|
|
3520
|
-
}
|
|
3521
|
-
async call(transaction, optionals) {
|
|
3522
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3523
|
-
return provider.call(transaction);
|
|
3524
|
-
}
|
|
3525
|
-
async estimateGas(transaction, optionals) {
|
|
3526
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3527
|
-
return provider.estimateGas(transaction);
|
|
3528
|
-
}
|
|
3529
|
-
async getBlock(blockHashOrBlockTag, prefetchTxs, optionals) {
|
|
3530
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3531
|
-
return provider.getBlock(blockHashOrBlockTag, prefetchTxs);
|
|
3532
|
-
}
|
|
3533
|
-
async getTransaction(transactionHash, optionals) {
|
|
3534
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3535
|
-
return provider.getTransaction(transactionHash);
|
|
3536
|
-
}
|
|
3537
|
-
async getLogs(filter, optionals) {
|
|
3538
|
-
const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
|
|
3539
|
-
return provider.getLogs(filter);
|
|
3540
|
-
}
|
|
3541
|
-
|
|
3542
|
-
// ENS methods
|
|
3543
|
-
|
|
3544
|
-
async supportsENS() {
|
|
3545
|
-
const networks = await this.getNetworks();
|
|
3546
|
-
return networks.some(n => n.chainId === 1);
|
|
3547
|
-
}
|
|
3548
|
-
async getResolver(name) {
|
|
3549
|
-
if (!(await this.supportsENS())) {
|
|
3550
|
-
return null;
|
|
3551
|
-
}
|
|
3552
|
-
|
|
3553
|
-
// Resolver is always on the chainId 1
|
|
3554
|
-
const provider = await this._getSubprovider(1);
|
|
3555
|
-
return provider.getResolver(name);
|
|
3556
|
-
}
|
|
3557
|
-
async resolveName(name) {
|
|
3558
|
-
if (ethers.isAddress(name)) {
|
|
3559
|
-
return name;
|
|
3560
|
-
}
|
|
3561
|
-
if (!(await this.supportsENS())) {
|
|
3562
|
-
return null;
|
|
3563
|
-
}
|
|
3564
|
-
|
|
3565
|
-
// Resolver is always on the chainId 1
|
|
3566
|
-
const provider = await this._getSubprovider(1);
|
|
3567
|
-
return provider.resolveName(name);
|
|
3568
|
-
}
|
|
3569
|
-
async lookupAddress(address) {
|
|
3570
|
-
if (!(await this.supportsENS())) {
|
|
3571
|
-
return null;
|
|
3572
|
-
}
|
|
3573
|
-
|
|
3574
|
-
// Resolver is always on the chainId 1
|
|
3575
|
-
const provider = await this._getSubprovider(1);
|
|
3576
|
-
return provider.lookupAddress(address);
|
|
3577
|
-
}
|
|
3578
|
-
async getAvatar(nameOrAddress) {
|
|
3579
|
-
if (!(await this.supportsENS())) {
|
|
3580
|
-
return null;
|
|
3581
|
-
}
|
|
3582
|
-
const provider = await this._getSubprovider(1);
|
|
3583
|
-
return provider.getAvatar(nameOrAddress);
|
|
3584
|
-
}
|
|
3585
|
-
}
|
|
3586
|
-
SequenceProvider.is = provider => {
|
|
3587
|
-
return provider && typeof provider === 'object' && provider._isSequenceProvider === true;
|
|
3588
|
-
};
|
|
3589
|
-
function normalizeChainId(chainId) {
|
|
3590
|
-
if (typeof chainId === 'object') return normalizeChainId(chainId.chainId);
|
|
3591
|
-
return Number(chainId);
|
|
3592
|
-
}
|
|
3593
|
-
|
|
3594
|
-
/**
|
|
3595
|
-
* This is the same provider, but it only allows a single network at a time.
|
|
3596
|
-
* the network defined by the constructor is the only one that can be used.
|
|
3597
|
-
*
|
|
3598
|
-
* Attempting to call any method with a different network will throw an error.
|
|
3599
|
-
* Attempting to change the network of this provider will throw an error.
|
|
3600
|
-
*
|
|
3601
|
-
* NOTICE: These networks won't support ENS unless they are the mainnet.
|
|
3602
|
-
*/
|
|
3603
|
-
class SingleNetworkSequenceProvider extends SequenceProvider {
|
|
3604
|
-
constructor(client, providerFor, chainId, options) {
|
|
3605
|
-
super(client, providerFor, undefined, options);
|
|
3606
|
-
this.chainId = chainId;
|
|
3607
|
-
this._isSingleNetworkSequenceProvider = true;
|
|
3608
|
-
}
|
|
3609
|
-
_useChainId(chainId) {
|
|
3610
|
-
const provided = this.toChainId(chainId);
|
|
3611
|
-
if (provided && provided !== this.chainId) {
|
|
3612
|
-
throw new Error(`This provider only supports the network ${this.chainId}, but ${provided} was requested.`);
|
|
3613
|
-
}
|
|
3614
|
-
return provided || super.toChainId(this.chainId);
|
|
3615
|
-
}
|
|
3616
|
-
useChainId(chainId) {
|
|
3617
|
-
return Promise.resolve(this._useChainId(chainId));
|
|
3618
|
-
}
|
|
3619
|
-
getChainId() {
|
|
3620
|
-
return super.toChainId(this.chainId);
|
|
3621
|
-
}
|
|
3622
|
-
async getNetwork() {
|
|
3623
|
-
const networks = await this.client.getNetworks();
|
|
3624
|
-
const found = findNetworkConfig(networks, this.chainId);
|
|
3625
|
-
if (!found) {
|
|
3626
|
-
throw new Error(`Unsupported network ${this.chainId}`);
|
|
3627
|
-
}
|
|
3628
|
-
return new ethers.Network(found.name, found.chainId);
|
|
3629
|
-
}
|
|
3630
|
-
|
|
3631
|
-
/**
|
|
3632
|
-
* Override getProvider and getSigner so they always use `useChainId`
|
|
3633
|
-
* this way they can't return providers and signers that can switch networks,
|
|
3634
|
-
* or that don't match the chainId of this signer.
|
|
3635
|
-
*/
|
|
3636
|
-
getProvider(chainId) {
|
|
3637
|
-
if (this._useChainId(chainId) !== this.chainId) {
|
|
3638
|
-
throw new Error(`Unreachable code`);
|
|
3639
|
-
}
|
|
3640
|
-
return this;
|
|
3641
|
-
}
|
|
3642
|
-
getSigner(chainId) {
|
|
3643
|
-
return super.getSigner(this._useChainId(chainId));
|
|
3644
|
-
}
|
|
3645
|
-
setDefaultChainId(_chainId) {
|
|
3646
|
-
throw new Error(`This provider only supports the network ${this.chainId}; use the parent provider to switch networks.`);
|
|
3647
|
-
}
|
|
3648
|
-
static is(cand) {
|
|
3649
|
-
return cand && typeof cand === 'object' && cand._isSingleNetworkSequenceProvider === true;
|
|
3650
|
-
}
|
|
3651
|
-
}
|
|
3652
|
-
|
|
3653
|
-
let AnalyticsEventType = /*#__PURE__*/function (AnalyticsEventType) {
|
|
3654
|
-
AnalyticsEventType[AnalyticsEventType["INIT"] = 0] = "INIT";
|
|
3655
|
-
AnalyticsEventType[AnalyticsEventType["VIEW"] = 1] = "VIEW";
|
|
3656
|
-
AnalyticsEventType[AnalyticsEventType["SIGN_MESSAGE_REQUEST"] = 2] = "SIGN_MESSAGE_REQUEST";
|
|
3657
|
-
AnalyticsEventType[AnalyticsEventType["SIGN_TYPED_DATA_REQUEST"] = 3] = "SIGN_TYPED_DATA_REQUEST";
|
|
3658
|
-
AnalyticsEventType[AnalyticsEventType["SEND_TRANSACTION_REQUEST"] = 4] = "SEND_TRANSACTION_REQUEST";
|
|
3659
|
-
return AnalyticsEventType;
|
|
3660
|
-
}({});
|
|
3661
|
-
// Analytics sub-class to add some custom helper methods
|
|
3662
|
-
class Analytics extends Databeat {}
|
|
3663
|
-
|
|
3664
|
-
// Setup analytics tracker
|
|
3665
|
-
const setupAnalytics = (projectAccessKey, server) => {
|
|
3666
|
-
if (!server) {
|
|
3667
|
-
server = 'https://nodes.sequence.app';
|
|
3668
|
-
}
|
|
3669
|
-
|
|
3670
|
-
// disable tracking if projectAccessKey is not set
|
|
3671
|
-
const noop = !projectAccessKey;
|
|
3672
|
-
|
|
3673
|
-
// auth
|
|
3674
|
-
const auth = {};
|
|
3675
|
-
if (projectAccessKey) {
|
|
3676
|
-
auth.headers = {
|
|
3677
|
-
'X-Access-Key': projectAccessKey
|
|
3678
|
-
};
|
|
3679
|
-
}
|
|
3680
|
-
return new Analytics(server, auth, {
|
|
3681
|
-
noop: noop,
|
|
3682
|
-
defaultEnabled: true,
|
|
3683
|
-
privacy: {
|
|
3684
|
-
userIdHash: true,
|
|
3685
|
-
userAgentSalt: false
|
|
3686
|
-
},
|
|
3687
|
-
initProps: () => {
|
|
3688
|
-
if (!isBrowser()) {
|
|
3689
|
-
return {};
|
|
3690
|
-
} else {
|
|
3691
|
-
return {
|
|
3692
|
-
origin: window.location.origin
|
|
3693
|
-
};
|
|
3694
|
-
}
|
|
3695
|
-
}
|
|
3696
|
-
});
|
|
3697
|
-
};
|
|
3698
|
-
|
|
3699
|
-
/**
|
|
3700
|
-
* This session class is meant to persist the state of the wallet connection
|
|
3701
|
-
* whitin the dapp. This enables the client to retain the wallet address (and some more)
|
|
3702
|
-
* even if the user refreshes the page. Otherwise we would have to open the popup again.
|
|
3703
|
-
*/
|
|
3704
|
-
class SequenceClientSession {
|
|
3705
|
-
constructor(store) {
|
|
3706
|
-
this.store = store;
|
|
3707
|
-
}
|
|
3708
|
-
connectedSession() {
|
|
3709
|
-
const session = this.getSession();
|
|
3710
|
-
if (session && session.accountAddress && session.walletContext && session.networks) {
|
|
3711
|
-
return {
|
|
3712
|
-
accountAddress: session.accountAddress,
|
|
3713
|
-
walletContext: session.walletContext,
|
|
3714
|
-
networks: session.networks
|
|
3715
|
-
};
|
|
3716
|
-
}
|
|
3717
|
-
throw new Error('Sequence session not connected');
|
|
3718
|
-
}
|
|
3719
|
-
hasSession() {
|
|
3720
|
-
var _this$getSession;
|
|
3721
|
-
return ((_this$getSession = this.getSession()) == null ? void 0 : _this$getSession.accountAddress) !== undefined;
|
|
3722
|
-
}
|
|
3723
|
-
setSession(session) {
|
|
3724
|
-
return this.store.setItem(SequenceClientSession.SESSION_LOCALSTORE_KEY, JSON.stringify(session));
|
|
3725
|
-
}
|
|
3726
|
-
getSession() {
|
|
3727
|
-
try {
|
|
3728
|
-
const session = this.store.getItem(SequenceClientSession.SESSION_LOCALSTORE_KEY);
|
|
3729
|
-
if (session) {
|
|
3730
|
-
return JSON.parse(session);
|
|
3731
|
-
}
|
|
3732
|
-
} catch (err) {
|
|
3733
|
-
console.error('Error parsing session', err);
|
|
3734
|
-
}
|
|
3735
|
-
return undefined;
|
|
3736
|
-
}
|
|
3737
|
-
async clearSession() {
|
|
3738
|
-
return this.store.removeItem(SequenceClientSession.SESSION_LOCALSTORE_KEY);
|
|
3739
|
-
}
|
|
3740
|
-
}
|
|
3741
|
-
|
|
3742
|
-
/**
|
|
3743
|
-
* The wallet webapp doesn't really care what's the "default chain" for the user.
|
|
3744
|
-
* so we don't even bother to send this information to the wallet. Instead, we
|
|
3745
|
-
* track it locally using storage, that way the data stays always in sync.
|
|
3746
|
-
*/
|
|
3747
|
-
SequenceClientSession.SESSION_LOCALSTORE_KEY = '@sequence.session';
|
|
3748
|
-
class DefaultChainIdTracker {
|
|
3749
|
-
constructor(store, startingChainId = 1) {
|
|
3750
|
-
this.store = store;
|
|
3751
|
-
this.startingChainId = startingChainId;
|
|
3752
|
-
this.callbacks = [];
|
|
3753
|
-
store.onItemChange(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, value => {
|
|
3754
|
-
if (value) {
|
|
3755
|
-
const chainId = parseInt(value);
|
|
3756
|
-
this.callbacks.forEach(cb => cb(chainId));
|
|
3757
|
-
}
|
|
3758
|
-
});
|
|
3759
|
-
}
|
|
3760
|
-
onDefaultChainIdChanged(callback) {
|
|
3761
|
-
this.callbacks.push(callback);
|
|
3762
|
-
return () => {
|
|
3763
|
-
this.callbacks = this.callbacks.filter(cb => cb !== callback);
|
|
3764
|
-
};
|
|
3765
|
-
}
|
|
3766
|
-
setDefaultChainId(chainId) {
|
|
3767
|
-
if (chainId !== this.getDefaultChainId()) {
|
|
3768
|
-
this.store.setItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, chainId.toString());
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
|
-
getDefaultChainId() {
|
|
3772
|
-
const read = this.store.getItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY);
|
|
3773
|
-
if (!read || read.length === 0) {
|
|
3774
|
-
return this.startingChainId;
|
|
3775
|
-
}
|
|
3776
|
-
return parseInt(read);
|
|
3777
|
-
}
|
|
3778
|
-
}
|
|
3779
|
-
DefaultChainIdTracker.SESSION_CHAIN_ID_KEY = '@sequence.session.defaultChainId';
|
|
3780
|
-
/**
|
|
3781
|
-
* This is a wallet client for sequence wallet-webapp. It connects using *some* transport
|
|
3782
|
-
* and it allows to perform all sequence specific (or write) operations related to the wallet.
|
|
3783
|
-
*s
|
|
3784
|
-
* It doesn't implement a full ethereum Provider, it doesn't include read-only methods.
|
|
3785
|
-
*/
|
|
3786
|
-
|
|
3787
|
-
// TODO: rename Client to transport.. or something.. like SequenceTransport ..
|
|
3788
|
-
class SequenceClient {
|
|
3789
|
-
constructor(transport, store, options) {
|
|
3790
|
-
var _options$defaultEIP, _this$session$getSess;
|
|
3791
|
-
this.session = void 0;
|
|
3792
|
-
this.defaultChainId = void 0;
|
|
3793
|
-
this.callbacks = {};
|
|
3794
|
-
this.transport = void 0;
|
|
3795
|
-
this.defaultEIP6492 = void 0;
|
|
3796
|
-
this.projectAccessKey = void 0;
|
|
3797
|
-
this.analytics = void 0;
|
|
3798
|
-
if (isMuxTransportTemplate(transport)) {
|
|
3799
|
-
this.transport = MuxMessageProvider.new(transport, options == null ? void 0 : options.projectAccessKey);
|
|
3800
|
-
} else if (isProviderTransport(transport)) {
|
|
3801
|
-
this.transport = transport;
|
|
3802
|
-
} else {
|
|
3803
|
-
throw new Error('Invalid transport');
|
|
3804
|
-
}
|
|
3805
|
-
const defaultChainId = options == null ? void 0 : options.defaultChainId;
|
|
3806
|
-
this.defaultEIP6492 = (_options$defaultEIP = options == null ? void 0 : options.defaultEIP6492) != null ? _options$defaultEIP : false;
|
|
3807
|
-
this.session = new SequenceClientSession(store);
|
|
3808
|
-
this.defaultChainId = new DefaultChainIdTracker(store, defaultChainId);
|
|
3809
|
-
this.transport.on('accountsChanged', accounts => {
|
|
3810
|
-
var _this$callbacks$accou;
|
|
3811
|
-
if (accounts.length > 1) {
|
|
3812
|
-
console.warn('SequenceClient: wallet-webapp returned more than one account');
|
|
3813
|
-
}
|
|
3814
|
-
(_this$callbacks$accou = this.callbacks.accountsChanged) == null || _this$callbacks$accou.forEach(cb => cb(accounts));
|
|
3815
|
-
});
|
|
3816
|
-
this.transport.on('connect', response => {
|
|
3817
|
-
var _this$callbacks$conne;
|
|
3818
|
-
const chainIdHex = ethers.toQuantity(this.getChainId());
|
|
3819
|
-
(_this$callbacks$conne = this.callbacks.connect) == null || _this$callbacks$conne.forEach(cb => cb(_extends({}, response, {
|
|
3820
|
-
// Ignore the full connect response
|
|
3821
|
-
// use the chainId defined locally
|
|
3822
|
-
chainId: chainIdHex
|
|
3823
|
-
})));
|
|
3824
|
-
});
|
|
3825
|
-
this.transport.on('disconnect', (error, origin) => {
|
|
3826
|
-
var _this$callbacks$disco;
|
|
3827
|
-
(_this$callbacks$disco = this.callbacks.disconnect) == null || _this$callbacks$disco.forEach(cb => cb(error, origin));
|
|
3828
|
-
});
|
|
3829
|
-
this.transport.on('networks', networks => {
|
|
3830
|
-
var _this$callbacks$netwo;
|
|
3831
|
-
(_this$callbacks$netwo = this.callbacks.networks) == null || _this$callbacks$netwo.forEach(cb => cb(networks));
|
|
3832
|
-
});
|
|
3833
|
-
this.transport.on('walletContext', context => {
|
|
3834
|
-
var _this$callbacks$walle;
|
|
3835
|
-
(_this$callbacks$walle = this.callbacks.walletContext) == null || _this$callbacks$walle.forEach(cb => cb(context));
|
|
3836
|
-
});
|
|
3837
|
-
this.transport.on('open', info => {
|
|
3838
|
-
var _this$callbacks$open;
|
|
3839
|
-
(_this$callbacks$open = this.callbacks.open) == null || _this$callbacks$open.forEach(cb => cb(info));
|
|
3840
|
-
});
|
|
3841
|
-
this.transport.on('close', () => {
|
|
3842
|
-
var _this$callbacks$close;
|
|
3843
|
-
(_this$callbacks$close = this.callbacks.close) == null || _this$callbacks$close.forEach(cb => cb());
|
|
3844
|
-
});
|
|
3845
|
-
this.transport.on('chainChanged', (chainIdHex, origin) => {
|
|
3846
|
-
var _this$callbacks$chain;
|
|
3847
|
-
(_this$callbacks$chain = this.callbacks.chainChanged) == null || _this$callbacks$chain.forEach(cb => cb(chainIdHex, origin));
|
|
3848
|
-
});
|
|
3849
|
-
|
|
3850
|
-
// We don't listen for the transport chainChanged event
|
|
3851
|
-
// instead we handle it locally, so we listen for changes in the store
|
|
3852
|
-
this.defaultChainId.onDefaultChainIdChanged(chainId => {
|
|
3853
|
-
var _this$callbacks$chain2;
|
|
3854
|
-
const chainIdHex = ethers.toQuantity(chainId);
|
|
3855
|
-
(_this$callbacks$chain2 = this.callbacks.chainChanged) == null || _this$callbacks$chain2.forEach(cb => cb(chainIdHex));
|
|
3856
|
-
});
|
|
3857
|
-
if (options != null && options.projectAccessKey) {
|
|
3858
|
-
this.projectAccessKey = options.projectAccessKey;
|
|
3859
|
-
}
|
|
3860
|
-
if (this.projectAccessKey && options != null && options.analytics) {
|
|
3861
|
-
this.analytics = setupAnalytics(this.projectAccessKey);
|
|
3862
|
-
}
|
|
3863
|
-
if ((_this$session$getSess = this.session.getSession()) != null && _this$session$getSess.accountAddress) {
|
|
3864
|
-
var _this$analytics, _this$session$getSess2;
|
|
3865
|
-
(_this$analytics = this.analytics) == null || _this$analytics.identify((_this$session$getSess2 = this.session.getSession()) == null || (_this$session$getSess2 = _this$session$getSess2.accountAddress) == null ? void 0 : _this$session$getSess2.toLowerCase());
|
|
3866
|
-
}
|
|
3867
|
-
}
|
|
3868
|
-
|
|
3869
|
-
// Callbacks
|
|
3870
|
-
|
|
3871
|
-
registerCallback(eventName, callback) {
|
|
3872
|
-
if (!this.callbacks[eventName]) {
|
|
3873
|
-
this.callbacks[eventName] = [];
|
|
3874
|
-
}
|
|
3875
|
-
this.callbacks[eventName].push(callback);
|
|
3876
|
-
return () => {
|
|
3877
|
-
this.callbacks[eventName] = this.callbacks[eventName].filter(c => c !== callback);
|
|
3878
|
-
};
|
|
3879
|
-
}
|
|
3880
|
-
|
|
3881
|
-
// Individual callbacks lead to more idiomatic code
|
|
3882
|
-
|
|
3883
|
-
onOpen(callback) {
|
|
3884
|
-
return this.registerCallback('open', callback);
|
|
3885
|
-
}
|
|
3886
|
-
onClose(callback) {
|
|
3887
|
-
return this.registerCallback('close', callback);
|
|
3888
|
-
}
|
|
3889
|
-
onConnect(callback) {
|
|
3890
|
-
return this.registerCallback('connect', callback);
|
|
3891
|
-
}
|
|
3892
|
-
onDisconnect(callback) {
|
|
3893
|
-
return this.registerCallback('disconnect', callback);
|
|
3894
|
-
}
|
|
3895
|
-
onNetworks(callback) {
|
|
3896
|
-
return this.registerCallback('networks', callback);
|
|
3897
|
-
}
|
|
3898
|
-
onAccountsChanged(callback) {
|
|
3899
|
-
return this.registerCallback('accountsChanged', callback);
|
|
3900
|
-
}
|
|
3901
|
-
|
|
3902
|
-
// @deprecated
|
|
3903
|
-
onWalletContext(callback) {
|
|
3904
|
-
return this.registerCallback('walletContext', callback);
|
|
3905
|
-
}
|
|
3906
|
-
onChainChanged(callback) {
|
|
3907
|
-
return this.registerCallback('chainChanged', callback);
|
|
3908
|
-
}
|
|
3909
|
-
onDefaultChainIdChanged(callback) {
|
|
3910
|
-
return this.registerCallback('chainChanged', callback);
|
|
3911
|
-
}
|
|
3912
|
-
getChainId() {
|
|
3913
|
-
return this.defaultChainId.getDefaultChainId();
|
|
3914
|
-
}
|
|
3915
|
-
setDefaultChainId(chainId) {
|
|
3916
|
-
return this.defaultChainId.setDefaultChainId(chainId);
|
|
3917
|
-
}
|
|
3918
|
-
|
|
3919
|
-
// Proxy transport methods
|
|
3920
|
-
|
|
3921
|
-
async openWallet(path, intent) {
|
|
3922
|
-
this.transport.openWallet(path, intent, this.getChainId());
|
|
3923
|
-
await this.transport.waitUntilOpened();
|
|
3924
|
-
return this.isOpened();
|
|
3925
|
-
}
|
|
3926
|
-
closeWallet() {
|
|
3927
|
-
return this.transport.closeWallet();
|
|
3928
|
-
}
|
|
3929
|
-
isOpened() {
|
|
3930
|
-
return this.transport.isOpened();
|
|
3931
|
-
}
|
|
3932
|
-
isConnected() {
|
|
3933
|
-
return this.session.hasSession();
|
|
3934
|
-
}
|
|
3935
|
-
getSession() {
|
|
3936
|
-
return this.session.getSession();
|
|
3937
|
-
}
|
|
3938
|
-
|
|
3939
|
-
// Basic API
|
|
3940
|
-
getAddress() {
|
|
3941
|
-
const session = this.session.connectedSession();
|
|
3942
|
-
return session.accountAddress;
|
|
3943
|
-
}
|
|
3944
|
-
async connect(options) {
|
|
3945
|
-
if ((options == null ? void 0 : options.authorizeVersion) === undefined) {
|
|
3946
|
-
// Populate default authorize version if not provided
|
|
3947
|
-
options.authorizeVersion = 2;
|
|
3948
|
-
}
|
|
3949
|
-
if ((options == null ? void 0 : options.refresh) === true) {
|
|
3950
|
-
this.disconnect();
|
|
3951
|
-
}
|
|
3952
|
-
options.projectAccessKey = this.projectAccessKey;
|
|
3953
|
-
if (options) {
|
|
3954
|
-
if (options.authorize) {
|
|
3955
|
-
if (!options.app) {
|
|
3956
|
-
throw new Error(`connecting with 'authorize' option also requires 'app' to be set`);
|
|
3957
|
-
}
|
|
3958
|
-
if (options.authorizeVersion === undefined) {
|
|
3959
|
-
options.authorizeVersion = 2;
|
|
3960
|
-
}
|
|
3961
|
-
}
|
|
3962
|
-
}
|
|
3963
|
-
await this.openWallet(undefined, {
|
|
3964
|
-
type: 'connect',
|
|
3965
|
-
options: _extends({}, options, {
|
|
3966
|
-
networkId: this.getChainId(),
|
|
3967
|
-
clientVersion: VERSION
|
|
3968
|
-
})
|
|
3969
|
-
});
|
|
3970
|
-
const connectDetails = await this.transport.waitUntilConnected().catch(error => {
|
|
3971
|
-
if (error instanceof Error) {
|
|
3972
|
-
return {
|
|
3973
|
-
connected: false,
|
|
3974
|
-
error: error.message
|
|
3975
|
-
};
|
|
3976
|
-
} else {
|
|
3977
|
-
return {
|
|
3978
|
-
connected: false,
|
|
3979
|
-
error: JSON.stringify(error)
|
|
3980
|
-
};
|
|
3981
|
-
}
|
|
3982
|
-
});
|
|
3983
|
-
|
|
3984
|
-
// Normalize chainId into a decimal string
|
|
3985
|
-
// TODO: Remove this once wallet-webapp returns chainId as a string
|
|
3986
|
-
if (connectDetails.chainId) {
|
|
3987
|
-
connectDetails.chainId = BigInt(connectDetails.chainId).toString();
|
|
3988
|
-
}
|
|
3989
|
-
if (connectDetails.connected) {
|
|
3990
|
-
var _connectDetails$sessi;
|
|
3991
|
-
if (!connectDetails.session) {
|
|
3992
|
-
throw new Error('impossible state, connect response is missing session');
|
|
3993
|
-
}
|
|
3994
|
-
this.session.setSession(connectDetails.session);
|
|
3995
|
-
if ((_connectDetails$sessi = connectDetails.session) != null && _connectDetails$sessi.accountAddress) {
|
|
3996
|
-
var _this$analytics2;
|
|
3997
|
-
(_this$analytics2 = this.analytics) == null || _this$analytics2.identify(connectDetails.session.accountAddress.toLowerCase());
|
|
3998
|
-
}
|
|
3999
|
-
}
|
|
4000
|
-
return connectDetails;
|
|
4001
|
-
}
|
|
4002
|
-
disconnect() {
|
|
4003
|
-
var _this$analytics3;
|
|
4004
|
-
if (this.isOpened()) {
|
|
4005
|
-
this.closeWallet();
|
|
4006
|
-
}
|
|
4007
|
-
(_this$analytics3 = this.analytics) == null || _this$analytics3.reset();
|
|
4008
|
-
return this.session.clearSession();
|
|
4009
|
-
}
|
|
4010
|
-
|
|
4011
|
-
// Higher level API
|
|
4012
|
-
|
|
4013
|
-
async request(request) {
|
|
4014
|
-
// Internally when sending requests we use `legacy_sign`
|
|
4015
|
-
// to avoid the default EIP6492 behavior overriding an explicit
|
|
4016
|
-
// "legacy sign" request, so we map the method here.
|
|
4017
|
-
request.method = this.mapSignMethod(request.method);
|
|
4018
|
-
const result = await this.transport.request(request);
|
|
4019
|
-
|
|
4020
|
-
// We may need to unwrap the response if it's a JSON-RPC response. ie. older universal wallet versions
|
|
4021
|
-
return unwrapJsonRpcResponse(result);
|
|
4022
|
-
}
|
|
4023
|
-
async getNetworks(pull) {
|
|
4024
|
-
const connectedSession = this.session.connectedSession();
|
|
4025
|
-
if (pull) {
|
|
4026
|
-
connectedSession.networks = await this.request({
|
|
4027
|
-
method: 'sequence_getNetworks'
|
|
4028
|
-
});
|
|
4029
|
-
this.session.setSession(connectedSession);
|
|
4030
|
-
}
|
|
4031
|
-
return connectedSession.networks;
|
|
4032
|
-
}
|
|
4033
|
-
|
|
4034
|
-
// NOTICE: `legacy_sign` will get overriden by `send`
|
|
4035
|
-
// it is done this way to ensure that:
|
|
4036
|
-
// - `send` handles `personal_sign` as a request for the default sign method
|
|
4037
|
-
// - explicit `personal_sign` is not replaced by `sequence_sign` (if default is EI6492)
|
|
4038
|
-
signMethod(options) {
|
|
4039
|
-
if ((options == null ? void 0 : options.eip6492) === undefined) {
|
|
4040
|
-
return 'personal_sign';
|
|
4041
|
-
}
|
|
4042
|
-
return options.eip6492 ? 'sequence_sign' : 'legacy_sign';
|
|
4043
|
-
}
|
|
4044
|
-
signTypedDataMethod(options) {
|
|
4045
|
-
if ((options == null ? void 0 : options.eip6492) === undefined) {
|
|
4046
|
-
return 'eth_signTypedData_v4';
|
|
4047
|
-
}
|
|
4048
|
-
return options.eip6492 ? 'sequence_signTypedData_v4' : 'legacy_signTypedData_v4';
|
|
4049
|
-
}
|
|
4050
|
-
mapSignMethod(method) {
|
|
4051
|
-
if (method === 'personal_sign') {
|
|
4052
|
-
if (this.defaultEIP6492) {
|
|
4053
|
-
return 'sequence_sign';
|
|
4054
|
-
} else {
|
|
4055
|
-
return 'personal_sign';
|
|
4056
|
-
}
|
|
4057
|
-
}
|
|
4058
|
-
if (method === 'eth_signTypedData_v4') {
|
|
4059
|
-
if (this.defaultEIP6492) {
|
|
4060
|
-
return 'sequence_signTypedData_v4';
|
|
4061
|
-
} else {
|
|
4062
|
-
return 'eth_signTypedData_v4';
|
|
4063
|
-
}
|
|
4064
|
-
}
|
|
4065
|
-
if (method === 'legacy_sign') {
|
|
4066
|
-
return 'personal_sign';
|
|
4067
|
-
}
|
|
4068
|
-
if (method === 'legacy_signTypedData_v4') {
|
|
4069
|
-
return 'eth_signTypedData_v4';
|
|
4070
|
-
}
|
|
4071
|
-
return method;
|
|
4072
|
-
}
|
|
4073
|
-
async signMessage(message, options) {
|
|
4074
|
-
var _this$analytics4;
|
|
4075
|
-
const method = this.signMethod(options);
|
|
4076
|
-
(_this$analytics4 = this.analytics) == null || _this$analytics4.track({
|
|
4077
|
-
event: 'SIGN_MESSAGE_REQUEST',
|
|
4078
|
-
props: {
|
|
4079
|
-
chainId: `${(options == null ? void 0 : options.chainId) || this.getChainId()}`
|
|
4080
|
-
}
|
|
4081
|
-
});
|
|
4082
|
-
message = ethers.hexlify(messageToBytes(message));
|
|
4083
|
-
|
|
4084
|
-
// Address is ignored by the wallet webapp
|
|
4085
|
-
return this.request({
|
|
4086
|
-
method,
|
|
4087
|
-
params: [message, this.getAddress()],
|
|
4088
|
-
chainId: options == null ? void 0 : options.chainId
|
|
4089
|
-
});
|
|
4090
|
-
}
|
|
4091
|
-
async signTypedData(typedData, options) {
|
|
4092
|
-
var _this$analytics5;
|
|
4093
|
-
const method = this.signTypedDataMethod(options);
|
|
4094
|
-
|
|
4095
|
-
// TODO: Stop using ethers for this, this is the only place where we use it
|
|
4096
|
-
// and it makes the client depend on ethers.
|
|
4097
|
-
const encoded = ethers.TypedDataEncoder.getPayload(typedData.domain, typedData.types, typedData.message);
|
|
4098
|
-
|
|
4099
|
-
// The sign typed data will use one of the following chainIds, in order:
|
|
4100
|
-
// - The one provided in the options
|
|
4101
|
-
// - The one provided in the typedData.domain.chainId
|
|
4102
|
-
// - The default chainId
|
|
4103
|
-
|
|
4104
|
-
(_this$analytics5 = this.analytics) == null || _this$analytics5.track({
|
|
4105
|
-
event: 'SIGN_TYPED_DATA_REQUEST',
|
|
4106
|
-
props: {
|
|
4107
|
-
chainId: `${(options == null ? void 0 : options.chainId) || this.getChainId()}`
|
|
4108
|
-
}
|
|
4109
|
-
});
|
|
4110
|
-
return this.request({
|
|
4111
|
-
method,
|
|
4112
|
-
params: [this.getAddress(), encoded],
|
|
4113
|
-
chainId: (options == null ? void 0 : options.chainId) || typedData.domain.chainId && Number(typedData.domain.chainId) || this.getChainId()
|
|
4114
|
-
});
|
|
4115
|
-
}
|
|
4116
|
-
async sendTransaction(tx, options) {
|
|
4117
|
-
var _this$analytics6;
|
|
4118
|
-
const sequenceTxs = Array.isArray(tx) ? tx : [tx];
|
|
4119
|
-
const extendedTxs = toExtended(sequenceTxs);
|
|
4120
|
-
(_this$analytics6 = this.analytics) == null || _this$analytics6.track({
|
|
4121
|
-
event: 'SEND_TRANSACTION_REQUEST',
|
|
4122
|
-
props: {
|
|
4123
|
-
chainId: `${(options == null ? void 0 : options.chainId) || this.getChainId()}`
|
|
4124
|
-
}
|
|
4125
|
-
});
|
|
4126
|
-
return this.request({
|
|
4127
|
-
method: 'eth_sendTransaction',
|
|
4128
|
-
params: [extendedTxs],
|
|
4129
|
-
chainId: options == null ? void 0 : options.chainId
|
|
4130
|
-
});
|
|
4131
|
-
}
|
|
4132
|
-
async getWalletContext() {
|
|
4133
|
-
return this.request({
|
|
4134
|
-
method: 'sequence_getWalletContext'
|
|
4135
|
-
});
|
|
4136
|
-
}
|
|
4137
|
-
async getOnchainWalletConfig(options) {
|
|
4138
|
-
// NOTICE: sequence_getWalletConfig sends the chainId as a param
|
|
4139
|
-
const res = await this.request({
|
|
4140
|
-
method: 'sequence_getWalletConfig',
|
|
4141
|
-
params: [(options == null ? void 0 : options.chainId) || this.getChainId()],
|
|
4142
|
-
chainId: options == null ? void 0 : options.chainId
|
|
4143
|
-
});
|
|
4144
|
-
return Array.isArray(res) ? res[0] : res;
|
|
4145
|
-
}
|
|
4146
|
-
|
|
4147
|
-
// NOTICE: We are leaving out all the "regular" methods os a tipical
|
|
4148
|
-
// JSON RPC Provider (eth_getBlockByNumber, eth_call, etc)
|
|
4149
|
-
// wallet-webapp does implement them, but this client is meant to be
|
|
4150
|
-
// exclusively used for Sequence specific methods
|
|
4151
|
-
}
|
|
4152
|
-
|
|
4153
|
-
// Unwrap a JsonRpcResponse result
|
|
4154
|
-
const unwrapJsonRpcResponse = response => {
|
|
4155
|
-
if (response && typeof response === 'object' && 'jsonrpc' in response && 'result' in response) {
|
|
4156
|
-
return response.result;
|
|
4157
|
-
}
|
|
4158
|
-
return response;
|
|
4159
|
-
};
|
|
4160
|
-
|
|
4161
|
-
const DefaultProviderConfig = {
|
|
4162
|
-
transports: {
|
|
4163
|
-
walletAppURL: 'https://sequence.app',
|
|
4164
|
-
windowTransport: {
|
|
4165
|
-
enabled: true
|
|
4166
|
-
},
|
|
4167
|
-
proxyTransport: {
|
|
4168
|
-
enabled: false
|
|
4169
|
-
}
|
|
4170
|
-
},
|
|
4171
|
-
defaultNetwork: 1,
|
|
4172
|
-
analytics: true
|
|
4173
|
-
};
|
|
4174
|
-
let sequenceWalletProvider;
|
|
4175
|
-
|
|
4176
|
-
/**
|
|
4177
|
-
* Initializes a wallet with the provided project access key and optional configuration.
|
|
4178
|
-
*
|
|
4179
|
-
* @param projectAccessKey - Access key for the project that can be obtained from Sequence Builder on sequence.build
|
|
4180
|
-
* @param partialConfig - Optional partial configuration for the wallet.
|
|
4181
|
-
* @returns The initialized wallet provider.
|
|
4182
|
-
* @throws Error if projectAccessKey is not provided, empty string or is not string.
|
|
4183
|
-
*/
|
|
4184
|
-
const initWallet = (projectAccessKey, partialConfig) => {
|
|
4185
|
-
var _findNetworkConfig, _client$getSession;
|
|
4186
|
-
if (!projectAccessKey || typeof projectAccessKey !== 'string') {
|
|
4187
|
-
throw new Error('Please pass a projectAccessKey in initWallet.');
|
|
4188
|
-
}
|
|
4189
|
-
if (sequenceWalletProvider) {
|
|
4190
|
-
return sequenceWalletProvider;
|
|
4191
|
-
}
|
|
4192
|
-
|
|
4193
|
-
// Combine both the provided config and the default config
|
|
4194
|
-
const config = _extends({}, DefaultProviderConfig, partialConfig, {
|
|
4195
|
-
transports: _extends({}, DefaultProviderConfig.transports, partialConfig == null ? void 0 : partialConfig.transports)
|
|
4196
|
-
});
|
|
4197
|
-
let networks = [];
|
|
4198
|
-
const updateNetworks = (connectedNetworks = []) => {
|
|
4199
|
-
var _config$networks;
|
|
4200
|
-
networks = mergeNetworks(allNetworks, connectedNetworks, (_config$networks = config.networks) != null ? _config$networks : []);
|
|
4201
|
-
|
|
4202
|
-
// Append projectAccessKey to network rpcUrls
|
|
4203
|
-
networks = networks.map(network => {
|
|
4204
|
-
// Don't double-append in the case the user has already included their access key in the rpc URL
|
|
4205
|
-
if (network.rpcUrl.includes(projectAccessKey)) {
|
|
4206
|
-
return network;
|
|
4207
|
-
}
|
|
4208
|
-
|
|
4209
|
-
// XXX: This will probably break non-sequence RPC provider URLs.
|
|
4210
|
-
network.rpcUrl = network.rpcUrl + `/${projectAccessKey}`;
|
|
4211
|
-
return network;
|
|
4212
|
-
});
|
|
4213
|
-
};
|
|
4214
|
-
updateNetworks();
|
|
4215
|
-
|
|
4216
|
-
// This is the starting default network (as defined by the config)
|
|
4217
|
-
// it can be later be changed using `wallet_switchEthereumChain` or some
|
|
4218
|
-
// of the other methods on the provider.
|
|
4219
|
-
const defaultNetwork = config.defaultNetwork ? (_findNetworkConfig = findNetworkConfig(networks, config.defaultNetwork)) == null ? void 0 : _findNetworkConfig.chainId : undefined;
|
|
4220
|
-
if (!defaultNetwork && config.defaultNetwork) {
|
|
4221
|
-
throw new Error(`defaultNetwork not found for chainId: ${config.defaultNetwork}`);
|
|
4222
|
-
}
|
|
4223
|
-
|
|
4224
|
-
// Generate ItemStore
|
|
4225
|
-
const itemStore = config.localStorage || useBestStore();
|
|
4226
|
-
|
|
4227
|
-
// Create client, provider and return signer
|
|
4228
|
-
const client = new SequenceClient(config.transports, itemStore, {
|
|
4229
|
-
defaultChainId: defaultNetwork,
|
|
4230
|
-
defaultEIP6492: config.defaultEIP6492,
|
|
4231
|
-
projectAccessKey: projectAccessKey,
|
|
4232
|
-
analytics: config.analytics
|
|
4233
|
-
});
|
|
4234
|
-
updateNetworks((_client$getSession = client.getSession()) == null ? void 0 : _client$getSession.networks);
|
|
4235
|
-
client.onConnect(ev => {
|
|
4236
|
-
var _ev$session;
|
|
4237
|
-
updateNetworks((_ev$session = ev.session) == null ? void 0 : _ev$session.networks);
|
|
4238
|
-
});
|
|
4239
|
-
const rpcProviders = {};
|
|
4240
|
-
|
|
4241
|
-
// This builds a "public rpc" on demand, we build them on demand because we don't want to
|
|
4242
|
-
// generate a bunch of providers for networks that aren't used.
|
|
4243
|
-
const providerForChainId = chainId => {
|
|
4244
|
-
const network = findNetworkConfig(networks, chainId);
|
|
4245
|
-
if (!network) {
|
|
4246
|
-
throw new Error(`no network config found for chainId: ${chainId}`);
|
|
4247
|
-
}
|
|
4248
|
-
const {
|
|
4249
|
-
rpcUrl
|
|
4250
|
-
} = network;
|
|
4251
|
-
|
|
4252
|
-
// Cache providers by rpc url
|
|
4253
|
-
if (!rpcProviders[rpcUrl]) {
|
|
4254
|
-
rpcProviders[rpcUrl] = new JsonRpcProvider(rpcUrl, {
|
|
4255
|
-
middlewares: [loggingProviderMiddleware, exceptionProviderMiddleware, new CachedProvider()]
|
|
4256
|
-
}, {
|
|
4257
|
-
cacheTimeout: -1
|
|
4258
|
-
});
|
|
4259
|
-
}
|
|
4260
|
-
return rpcProviders[rpcUrl];
|
|
4261
|
-
};
|
|
4262
|
-
sequenceWalletProvider = new SequenceProvider(client, providerForChainId);
|
|
4263
|
-
return sequenceWalletProvider;
|
|
4264
|
-
};
|
|
4265
|
-
const unregisterWallet = () => {
|
|
4266
|
-
if (!sequenceWalletProvider) return;
|
|
4267
|
-
sequenceWalletProvider.client.closeWallet();
|
|
4268
|
-
sequenceWalletProvider.client.transport.unregister();
|
|
4269
|
-
sequenceWalletProvider = undefined;
|
|
4270
|
-
};
|
|
4271
|
-
const getWallet = () => {
|
|
4272
|
-
if (!sequenceWalletProvider) {
|
|
4273
|
-
throw new Error('Wallet has not been initialized, call sequence.initWallet(config) first.');
|
|
4274
|
-
}
|
|
4275
|
-
return sequenceWalletProvider;
|
|
4276
|
-
};
|
|
4277
|
-
|
|
4278
|
-
// allNetworks <- connectedNetworks <- config.networks
|
|
4279
|
-
const mergeNetworks = (...networks) => {
|
|
4280
|
-
const networkMap = new Map();
|
|
4281
|
-
for (const network of networks.flat()) {
|
|
4282
|
-
if (network.chainId && network.rpcUrl) {
|
|
4283
|
-
const existingNetwork = networkMap.get(network.chainId);
|
|
4284
|
-
networkMap.set(network.chainId, _extends({}, existingNetwork, network));
|
|
4285
|
-
}
|
|
4286
|
-
}
|
|
4287
|
-
return Array.from(networkMap.values());
|
|
4288
|
-
};
|
|
4289
|
-
|
|
4290
|
-
export { Analytics, AnalyticsEventType, BaseInjectedTransport, BaseProviderTransport, BaseWalletTransport, CHANNEL_ID, DefaultChainIdTracker, DefaultProviderConfig, ErrSignedInRequired, EventType, ExtensionMessageHandler, ExtensionMessageProvider, InitState, LocalStorage, MemoryItemStore, MuxMessageProvider, OpenState, PROVIDER_OPEN_TIMEOUT, ProviderError, ProxyMessageChannel, ProxyMessageChannelPort, ProxyMessageHandler, ProxyMessageProvider, SequenceClient, SequenceClientSession, SequenceProvider, SequenceSigner, SingleNetworkSequenceProvider, SingleNetworkSequenceSigner, UnrealMessageHandler, UnrealMessageProvider, WalletRequestHandler, WindowMessageHandler, WindowMessageProvider, WindowSessionParams, getWallet, initWallet, isBrowserExtension, isMuxTransportTemplate, isProviderTransport, isUnityPlugin, isValidMessageSignature, isValidSignature, isValidTypedDataSignature, isWalletUpToDate, messageToBytes, nextMessageIdx, prefixEIP191Message, resolveArrayProperties, setupAnalytics, trimEIP191Prefix, unregisterWallet, useBestStore, validateTransactionRequest };
|