@0xsequence/provider 1.0.5 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/0xsequence-provider.cjs.dev.js +1763 -1678
  2. package/dist/0xsequence-provider.cjs.prod.js +1763 -1678
  3. package/dist/0xsequence-provider.esm.js +1756 -1676
  4. package/dist/declarations/src/client.d.ts +74 -0
  5. package/dist/declarations/src/index.d.ts +3 -1
  6. package/dist/declarations/src/init.d.ts +25 -0
  7. package/dist/declarations/src/provider.d.ts +127 -63
  8. package/dist/declarations/src/signer.d.ts +87 -0
  9. package/dist/declarations/src/transports/mux-transport/mux-message-provider.d.ts +21 -0
  10. package/dist/declarations/src/transports/wallet-request-handler.d.ts +0 -1
  11. package/dist/declarations/src/types.d.ts +25 -9
  12. package/dist/declarations/src/utils/index.d.ts +10 -7
  13. package/dist/declarations/src/utils.d.ts +21 -15
  14. package/package.json +19 -12
  15. package/src/client.ts +423 -0
  16. package/src/extended.ts +4 -0
  17. package/src/index.ts +3 -1
  18. package/src/init.ts +134 -0
  19. package/src/provider.ts +447 -424
  20. package/src/signer.ts +316 -0
  21. package/src/transactions.ts +23 -0
  22. package/src/transports/base-wallet-transport.ts +3 -8
  23. package/src/transports/mux-transport/mux-message-provider.ts +86 -2
  24. package/src/transports/proxy-transport/proxy-message-provider.ts +1 -1
  25. package/src/transports/wallet-request-handler.ts +8 -33
  26. package/src/transports/window-transport/window-message-provider.ts +4 -2
  27. package/src/types.ts +41 -10
  28. package/src/utils/index.ts +16 -76
  29. package/src/utils.ts +81 -51
  30. package/dist/declarations/src/extended.d.ts +0 -6
  31. package/dist/declarations/src/wallet.d.ts +0 -105
  32. package/src/wallet.ts +0 -770
@@ -1,10 +1,9 @@
1
1
  import { EventEmitter2 } from 'eventemitter2';
2
- import { logger, encodeMessageDigest, encodeTypedDataDigest, sanitizeNumberString, sanitizeHost, sanitizeAlphanumeric, base64EncodeObject, base64DecodeObject, shallowCopy, resolveProperties, getDefaultConnectionInfo } from '@0xsequence/utils';
3
- import { ethers, BigNumber, providers } from 'ethers';
4
- import { findSupportedNetwork, getChainId, JsonRpcSender, maybeChainId, allowProviderMiddleware, networkProviderMiddleware, findNetworkConfig, CachedProvider, JsonRpcRouter, loggingProviderMiddleware, exceptionProviderMiddleware, sortNetworks, EagerProvider, SigningProvider, updateNetworkConfig, ensureValidNetworks } from '@0xsequence/network';
5
- import { commons, universal } from '@0xsequence/core';
2
+ import { logger, encodeMessageDigest, encodeTypedDataDigest, sanitizeNumberString, sanitizeHost, sanitizeAlphanumeric, base64EncodeObject, base64DecodeObject } from '@0xsequence/utils';
3
+ import { ethers, BigNumber } from 'ethers';
4
+ import { findSupportedNetwork, allNetworks, findNetworkConfig, JsonRpcRouter, loggingProviderMiddleware, exceptionProviderMiddleware, CachedProvider, JsonRpcSender } from '@0xsequence/network';
5
+ import { commons } from '@0xsequence/core';
6
6
  import { signAuthorization } from '@0xsequence/auth';
7
- import { Signer, resolveArrayProperties } from '@0xsequence/wallet';
8
7
 
9
8
  function _extends() {
10
9
  _extends = Object.assign ? Object.assign.bind() : function (target) {
@@ -21,6 +20,10 @@ function _extends() {
21
20
  return _extends.apply(this, arguments);
22
21
  }
23
22
 
23
+ function isProviderTransport(transport) {
24
+ 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';
25
+ }
26
+
24
27
  // ProviderMessageCallback is used to respond to ProviderMessage requests. The error
25
28
  // argument is for exceptions during the execution, and response is the response payload
26
29
  // which may contain the result or an error payload from the wallet.
@@ -72,6 +75,8 @@ const ErrSignedInRequired = new ProviderError('Wallet is not signed in. Connect
72
75
 
73
76
  // TODO: lets build some nice error handling tools, prob in /utils ...
74
77
 
78
+ // This is required by viem, it expects a provider to have an EIP-1193 compliant `request` attribute.
79
+
75
80
  const PROVIDER_OPEN_TIMEOUT = 30000; // in ms
76
81
 
77
82
  let _messageIdx = 0;
@@ -609,59 +614,87 @@ const isUnityPlugin = () => !!navigator.userAgent.match(/UnitySequence/i);
609
614
  const isWalletUpToDate = status => {
610
615
  return status.onChain.deployed && status.fullyMigrated;
611
616
  };
617
+ class MemoryItemStore {
618
+ constructor() {
619
+ this.callbacks = [];
620
+ this.store = {};
621
+ }
622
+ getItem(key) {
623
+ return this.store[key] || null;
624
+ }
625
+ setItem(key, value) {
626
+ this.store[key] = value;
627
+ this.callbacks.filter(c => c.key === key).forEach(c => c.cb(value));
628
+ }
629
+ removeItem(key) {
630
+ delete this.store[key];
631
+ }
632
+ onItemChange(key, cb) {
633
+ this.callbacks.push({
634
+ key,
635
+ cb
636
+ });
637
+ return () => {
638
+ this.callbacks = this.callbacks.filter(c => c.cb !== cb);
639
+ };
640
+ }
641
+ }
612
642
  class LocalStorage {
613
- constructor() {}
614
- static getInstance() {
615
- if (typeof window === 'object') {
616
- if (!LocalStorage._instance) {
617
- LocalStorage._instance = {
618
- getItem: key => Promise.resolve(window.localStorage.getItem(key)),
619
- setItem: (key, value) => Promise.resolve(window.localStorage.setItem(key, value)),
620
- removeItem: key => Promise.resolve(window.localStorage.removeItem(key))
621
- };
622
- }
623
- } else {
624
- // noop local storage if window is not defined
625
- // TODO: perhaps add an in-memory local storage if we need?
626
- if (!LocalStorage._instance) {
627
- LocalStorage._instance = {
628
- getItem: key => Promise.resolve(null),
629
- setItem: (key, value) => Promise.resolve(),
630
- removeItem: key => Promise.resolve()
631
- };
632
- }
643
+ static isAvailable() {
644
+ return typeof window === 'object' && typeof window.localStorage === 'object';
645
+ }
646
+ constructor() {
647
+ this.callbacks = [];
648
+ if (!LocalStorage.isAvailable()) {
649
+ throw new Error('LocalStorage is not available');
633
650
  }
634
- return this._instance;
651
+ window.addEventListener('storage', e => {
652
+ const {
653
+ key
654
+ } = e;
655
+ const cb = this.callbacks.filter(c => c.key === key);
656
+ cb.forEach(c => c.cb(this.getItem(key)));
657
+ });
635
658
  }
636
- static use(instance) {
637
- LocalStorage._instance = instance;
659
+ getItem(key) {
660
+ return window.localStorage.getItem(key);
638
661
  }
639
- }
662
+ setItem(key, value) {
663
+ window.localStorage.setItem(key, value);
640
664
 
641
- // window.localstorage helper
642
- class LocalStore {
643
- constructor(key, def) {
644
- this.def = def;
645
- this.key = key;
665
+ // Trigger callbacks
666
+ // NOTICE: the event is not triggered on the same window
667
+ this.callbacks.filter(c => c.key === key).forEach(c => c.cb(value));
646
668
  }
647
- async get() {
648
- const val = await LocalStorage.getInstance().getItem(this.key);
649
- if (val === null) {
650
- return this.def;
651
- }
652
- try {
653
- return JSON.parse(val);
654
- } catch (err) {
655
- console.error(err);
656
- }
657
- return;
669
+ removeItem(key) {
670
+ window.localStorage.removeItem(key);
671
+
672
+ // Trigger callbacks
673
+ // NOTICE: the event is not triggered on the same window
674
+ this.callbacks.filter(c => c.key === key).forEach(c => c.cb(null));
675
+ }
676
+ onItemChange(key, cb) {
677
+ this.callbacks.push({
678
+ key,
679
+ cb
680
+ });
681
+ return () => {
682
+ this.callbacks = this.callbacks.filter(c => c.cb !== cb);
683
+ };
658
684
  }
659
- set(val) {
660
- val ? LocalStorage.getInstance().setItem(this.key, JSON.stringify(val)) : LocalStorage.getInstance().removeItem(this.key);
685
+ }
686
+ function useBestStore() {
687
+ if (LocalStorage.isAvailable()) {
688
+ return new LocalStorage();
661
689
  }
662
- del() {
663
- LocalStorage.getInstance().removeItem(this.key);
690
+ return new MemoryItemStore();
691
+ }
692
+ async function resolveArrayProperties(object) {
693
+ if (Array.isArray(object)) {
694
+ // T must include array type
695
+ return Promise.all(object.map(o => ethers.utils.resolveProperties(o)));
664
696
  }
697
+ return ethers.utils.resolveProperties(object);
665
698
  }
666
699
 
667
700
  const TRANSPORT_SESSION_LS_KEY = '@sequence.transportSession';
@@ -802,11 +835,6 @@ class BaseWalletTransport {
802
835
  // Set connect options on the walletRequestHandler as our primary
803
836
  // wallet controller, and fall back to networkId if necessary
804
837
  _this.walletRequestHandler.setConnectOptions(connectOptions);
805
- if (connectOptions.networkId) {
806
- networkId = connectOptions.networkId;
807
- } else if (networkId) {
808
- connectOptions.networkId = networkId;
809
- }
810
838
  } else {
811
839
  _this.walletRequestHandler.setConnectOptions(undefined);
812
840
  }
@@ -876,10 +904,10 @@ class BaseWalletTransport {
876
904
  return true;
877
905
  };
878
906
  this.saveTransportSession = session => {
879
- LocalStorage.getInstance().setItem(TRANSPORT_SESSION_LS_KEY, JSON.stringify(session));
907
+ useBestStore().setItem(TRANSPORT_SESSION_LS_KEY, JSON.stringify(session));
880
908
  };
881
909
  this.getCachedTransportSession = async function () {
882
- const session = await LocalStorage.getInstance().getItem(TRANSPORT_SESSION_LS_KEY);
910
+ const session = useBestStore().getItem(TRANSPORT_SESSION_LS_KEY);
883
911
  try {
884
912
  return session ? JSON.parse(session) : null;
885
913
  } catch (err) {
@@ -1233,203 +1261,84 @@ class ProxyMessageHandler extends BaseWalletTransport {
1233
1261
  }
1234
1262
  }
1235
1263
 
1236
- class MuxMessageProvider {
1237
- constructor(...messageProviders) {
1238
- var _this = this;
1239
- this.register = () => {
1240
- if (this.messageProviders.length === 1) {
1241
- this.provider = this.messageProviders[0];
1242
- this.provider.register();
1243
- return;
1244
- }
1245
-
1246
- // REVIEW/NOTE: ........ this method does not work for the chrome-extension. The issue becomes
1247
- // when the browser quits or restarts, the "open" event is never triggered. Perhaps the code here is fine,
1248
- // or maybe its not. What should happen is when a dapp makes a request, it will call openWallet
1249
- // below, in which case one of the events will register. So perhaps this is fine.
1250
- this.messageProviders.forEach(m => {
1251
- m.register();
1252
- m.once('open', () => {
1253
- // the first one to open is the winner, and others will be unregistered
1254
- if (!this.provider) {
1255
- this.provider = m;
1256
-
1257
- // unregister other providers
1258
- this.messageProviders.forEach(m => {
1259
- if (this.provider !== m) {
1260
- m.unregister();
1261
- }
1262
- });
1263
- }
1264
- });
1265
- });
1266
- };
1267
- this.unregister = () => {
1268
- this.messageProviders.forEach(m => m.unregister());
1269
- this.provider = undefined;
1270
- };
1271
- this.openWallet = (path, intent, networkId) => {
1272
- if (this.provider) {
1273
- this.provider.openWallet(path, intent, networkId);
1274
- return;
1275
- }
1276
- this.messageProviders.forEach(m => m.openWallet(path, intent, networkId));
1277
- };
1278
- this.sendAsync = async function (request, callback, chainId) {
1279
- if (_this.provider) {
1280
- _this.provider.sendAsync(request, callback, chainId);
1281
- return;
1282
- }
1283
- throw new Error('impossible state, wallet must be opened first');
1284
- };
1285
- this.sendMessageRequest = async function (message) {
1286
- if (_this.provider) {
1287
- return _this.provider.sendMessageRequest(message);
1288
- }
1289
- throw new Error('impossible state, wallet must be opened first');
1264
+ /**
1265
+ * This will redirect console logs from Sequence.js & the wallet to the Unreal console, for debugging purposes.
1266
+ */
1267
+ function overrideLogs(side) {
1268
+ var _window$ue;
1269
+ if ((_window$ue = window.ue) != null && _window$ue.sequencewallettransport && !window.logsOverriddenForUnreal) {
1270
+ var _window$ue2;
1271
+ const t = (_window$ue2 = window.ue) == null ? void 0 : _window$ue2.sequencewallettransport;
1272
+ console.log = (...args) => {
1273
+ t.logfromjs(`${side}: ${stringify(args)}`);
1290
1274
  };
1291
- this.waitUntilOpened = async function () {
1292
- if (_this.provider) {
1293
- return _this.provider.waitUntilOpened();
1294
- }
1295
- return Promise.race(_this.messageProviders.map(p => p.waitUntilOpened()));
1275
+ console.warn = (...args) => {
1276
+ t.warnfromjs(`${side}: ${stringify(args)}`);
1296
1277
  };
1297
- this.waitUntilConnected = async function () {
1298
- if (_this.provider) {
1299
- return _this.provider.waitUntilConnected();
1300
- }
1301
- throw new Error('impossible state, wallet must be opened first');
1278
+ console.error = (...args) => {
1279
+ t.errorfromjs(`${side}: ${stringify(args)}`);
1302
1280
  };
1303
- this.messageProviders = messageProviders;
1304
- this.provider = undefined;
1305
- }
1306
- add(...messageProviders) {
1307
- this.messageProviders.push(...messageProviders);
1308
- }
1309
- closeWallet() {
1310
- if (this.provider) {
1311
- this.provider.closeWallet();
1312
- }
1313
- }
1314
- isOpened() {
1315
- if (this.provider) {
1316
- return this.provider.isOpened();
1317
- }
1318
- return false;
1319
- }
1320
- isConnected() {
1321
- if (this.provider) {
1322
- return this.provider.isConnected();
1323
- }
1324
- return false;
1325
- }
1326
- on(event, fn) {
1327
- if (this.provider) {
1328
- this.provider.on(event, fn);
1329
- return;
1330
- }
1331
- this.messageProviders.forEach(m => {
1332
- m.on(event, fn);
1333
- });
1334
- }
1335
- once(event, fn) {
1336
- if (this.provider) {
1337
- this.provider.once(event, fn);
1338
- return;
1339
- }
1340
- this.messageProviders.forEach(m => {
1341
- m.once(event, fn);
1342
- });
1343
- }
1344
- emit(event, ...args) {
1345
- if (this.provider) {
1346
- return this.provider.emit(event, ...args);
1347
- }
1348
- for (let i = 0; i < this.messageProviders.length; i++) {
1349
- this.messageProviders[i].emit(event, ...args);
1350
- }
1351
- return true;
1352
- }
1353
- sendMessage(message) {
1354
- if (!message.idx || message.idx <= 0) {
1355
- throw new Error('message idx is empty');
1356
- }
1357
- if (this.provider) {
1358
- this.provider.sendMessage(message);
1359
- } else {
1360
- throw new Error('impossible state, wallet must be opened first');
1361
- }
1362
- }
1363
- handleMessage(message) {
1364
- if (this.provider) {
1365
- this.provider.handleMessage(message);
1366
- return;
1367
- }
1368
- throw new Error('impossible state, wallet must be opened first');
1281
+ window.logsOverriddenForUnreal = true;
1369
1282
  }
1370
1283
  }
1284
+ function stringify(things) {
1285
+ return things.map(a => typeof a === 'object' ? a instanceof Error ? a.message : JSON.stringify(a) : String(a)).join(' ');
1286
+ }
1371
1287
 
1372
- // ..
1373
- let registeredWindowMessageProvider;
1374
- class WindowMessageProvider extends BaseProviderTransport {
1288
+ let registeredUnrealMessageProvider;
1289
+
1290
+ // all lowercase is an annoying limitation of Unreal CEF BindUObject
1291
+
1292
+ /**
1293
+ * Initialized on dApp side
1294
+ */
1295
+ class UnrealMessageProvider extends BaseProviderTransport {
1375
1296
  constructor(walletAppURL) {
1376
1297
  super();
1377
1298
  this.register = () => {
1378
- if (registeredWindowMessageProvider) {
1299
+ var _window$ue;
1300
+ overrideLogs('dapp');
1301
+ if (registeredUnrealMessageProvider) {
1379
1302
  // overriding the registered message provider
1380
- registeredWindowMessageProvider.unregister();
1381
- registeredWindowMessageProvider = this;
1303
+ registeredUnrealMessageProvider.unregister();
1304
+ registeredUnrealMessageProvider = this;
1382
1305
  }
1383
1306
 
1384
1307
  // listen for incoming messages from wallet
1385
- window.addEventListener('message', this.onWindowEvent);
1386
- registeredWindowMessageProvider = this;
1387
-
1388
- // open heartbeat
1389
- this.on('open', () => {
1390
- // Heartbeat to track if window closed
1391
- const popup = this.walletWindow;
1392
- const interval = setInterval(() => {
1393
- if (popup && popup.closed) {
1394
- clearInterval(interval);
1395
- this.close();
1396
- }
1397
- }, 500);
1398
- });
1399
-
1400
- // close clean up
1401
- this.on('close', () => {
1402
- if (this.walletWindow) {
1403
- this.walletWindow.close();
1404
- this.walletWindow = null;
1405
- }
1406
- });
1308
+ if ((_window$ue = window.ue) != null && _window$ue.sequencewallettransport) {
1309
+ window.ue.sequencewallettransport.onmessagefromwallet = this.onUnrealCallback;
1310
+ }
1311
+ registeredUnrealMessageProvider = this;
1407
1312
  this._registered = true;
1313
+ console.log('registering transport!');
1408
1314
  };
1409
1315
  this.unregister = () => {
1316
+ var _window$ue2, _window$ue2$sequencew;
1410
1317
  this._registered = false;
1411
1318
  this.closeWallet();
1412
1319
 
1413
1320
  // disable message listener
1414
- if (registeredWindowMessageProvider === this) {
1415
- registeredWindowMessageProvider = undefined;
1321
+ if (registeredUnrealMessageProvider === this) {
1322
+ registeredUnrealMessageProvider = undefined;
1323
+ }
1324
+ if (((_window$ue2 = window.ue) == null ? void 0 : (_window$ue2$sequencew = _window$ue2.sequencewallettransport) == null ? void 0 : _window$ue2$sequencew.onmessagefromwallet) === this.onUnrealCallback) {
1325
+ delete window.ue.sequencewallettransport.onmessagefromwallet;
1416
1326
  }
1417
- window.removeEventListener('message', this.onWindowEvent);
1418
1327
 
1419
1328
  // clear event listeners
1420
1329
  this.events.removeAllListeners();
1421
1330
  };
1422
1331
  this.openWallet = (path, intent, networkId) => {
1423
- if (this.walletWindow && this.isOpened()) {
1424
- // TODO: update the location of window to path
1425
- this.walletWindow.focus();
1332
+ if (this.isOpened()) {
1333
+ // TODO focus wallet
1334
+ console.log('wallet already open!');
1426
1335
  return;
1427
1336
  }
1428
-
1337
+ console.log('opening wallet!');
1429
1338
  // Instantiate new walletURL for this call
1430
1339
  const walletURL = new URL(this.walletURL.href);
1431
1340
  const windowSessionParams = new WindowSessionParams();
1432
- if (path && path !== '') {
1341
+ if (path) {
1433
1342
  walletURL.pathname = path.toLowerCase();
1434
1343
  }
1435
1344
 
@@ -1438,80 +1347,19 @@ class WindowMessageProvider extends BaseProviderTransport {
1438
1347
  this._sessionId = `${performance.now()}`;
1439
1348
  windowSessionParams.set('sid', this._sessionId);
1440
1349
  if (intent) {
1441
- // for the window-transport, we eagerly/optimistically set the origin host
1442
- // when connecting to the wallet, however, this will be verified and enforced
1443
- // on the wallet-side, so if a dapp provides the wrong origin, it will be dropped.
1444
- if (intent.type === 'connect') {
1445
- if (!intent.options) intent.options = {};
1446
-
1447
- // skip setting origin host if we're in an browser extension execution context
1448
- // allow origin that is passed in
1449
- if (!isBrowserExtension() && !isUnityPlugin()) {
1450
- intent.options.origin = window.location.origin;
1451
- }
1452
- }
1453
1350
  // encode intent as base64 url-encoded param
1454
1351
  windowSessionParams.set('intent', base64EncodeObject(intent));
1455
1352
  }
1456
1353
  if (networkId) {
1457
1354
  windowSessionParams.set('net', `${networkId}`);
1458
1355
  }
1459
-
1460
- // Open popup window on center of the app window
1461
- let windowSize;
1462
- let windowPos;
1463
- if (isBrowserExtension()) {
1464
- windowSize = [450, 750];
1465
- windowPos = [Math.abs(window.screen.width / 2 - windowSize[0] / 2), Math.abs(window.screen.height / 2 - windowSize[1] / 2)];
1466
- } else {
1467
- windowSize = [450, 750];
1468
- windowPos = [Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2), Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)];
1469
- }
1470
- const windowFeatures = `toolbar=0,location=0,menubar=0,scrollbars=yes,status=yes` + `,width=${windowSize[0]},height=${windowSize[1]}` + `,left=${windowPos[0]},top=${windowPos[1]}`;
1471
-
1472
1356
  // serialize params
1473
1357
  walletURL.search = windowSessionParams.toString();
1474
- this.walletWindow = window.open(walletURL.href, 'sequence.app', windowFeatures);
1475
-
1476
- // TODO: move this somewhere else
1477
- // TODO: perhaps we trigger a .on('openTimeout') event..? maybe.. could help.
1478
-
1479
- // Popup blocking detection and notice
1480
- // let warned = false
1481
- // const warnPopupBlocked = () => {
1482
- // if (warned) return
1483
- // warned = true
1484
- // // alert('popup is blocked! hey yo') // NOTE: for debug purposes only
1485
- // throw new Error('popup is blocked')
1486
- // }
1487
-
1488
- // const popupCheck = setTimeout(() => {
1489
- // if (!popup || popup.closed || typeof popup.closed === 'undefined') {
1490
- // // popup is definitely blocked if we reach here.
1491
- // warnPopupBlocked()
1492
- // }
1493
- // }, 1000)
1494
-
1495
- // const popupBlocked = popup === null || popup === undefined
1496
- // if (popupBlocked) {
1497
- // warnPopupBlocked()
1498
- // return
1499
- // }
1358
+ console.log('opening wallet to', walletURL.href);
1359
+ window.open(walletURL.href);
1500
1360
  };
1501
- // onmessage, receives ProviderMessageResponse from the wallet post-message transport
1502
- this.onWindowEvent = event => {
1503
- // Security check, ensure message is coming from wallet origin url
1504
- if (event.origin !== this.walletURL.origin) {
1505
- // Safetly can skip events not from the wallet
1506
- return;
1507
- }
1508
- let message;
1509
- try {
1510
- message = JSON.parse(event.data);
1511
- } catch (err) {
1512
- // event is not a ProviderMessage JSON object, skip
1513
- return;
1514
- }
1361
+ // onmessage, receives ProviderMessageResponse from the wallet unreal transport
1362
+ this.onUnrealCallback = message => {
1515
1363
  if (!message) {
1516
1364
  throw new Error('ProviderMessage object is empty');
1517
1365
  }
@@ -1522,62 +1370,36 @@ class WindowMessageProvider extends BaseProviderTransport {
1522
1370
  this.walletURL = new URL(walletAppURL);
1523
1371
  }
1524
1372
  closeWallet() {
1525
- var _this$walletWindow;
1526
1373
  this.close();
1527
- (_this$walletWindow = this.walletWindow) == null ? void 0 : _this$walletWindow.close();
1528
1374
  }
1375
+ // all lowercase is an annoying limitation of Unreal CEF BindUObject
1529
1376
  sendMessage(message) {
1530
- if (!this.walletWindow) {
1531
- logger.warn('WindowMessageProvider: sendMessage failed as walletWindow is unavailable');
1532
- return;
1533
- }
1377
+ var _window$ue3, _window$ue3$sequencew;
1534
1378
  const postedMessage = typeof message !== 'string' ? JSON.stringify(message) : message;
1535
- this.walletWindow.postMessage(postedMessage, this.walletURL.origin);
1379
+ console.log('Sending message to wallet:', postedMessage);
1380
+ (_window$ue3 = window.ue) == null ? void 0 : (_window$ue3$sequencew = _window$ue3.sequencewallettransport) == null ? void 0 : _window$ue3$sequencew.sendmessagetowallet(postedMessage);
1536
1381
  }
1537
1382
  }
1538
1383
 
1539
- class WindowMessageHandler extends BaseWalletTransport {
1384
+ // all lowercase is an annoying limitation of Unreal CEF BindUObject
1385
+
1386
+ /**
1387
+ * Initialized on Wallet side
1388
+ */
1389
+ class UnrealMessageHandler extends BaseWalletTransport {
1540
1390
  constructor(walletRequestHandler) {
1541
- var _this;
1542
1391
  super(walletRequestHandler);
1543
- _this = this;
1544
- this._isPopup = false;
1545
1392
  // onmessage is called when (the wallet) receives request messages from the dapp
1546
- // over the window post-messaging transport
1547
- this.onWindowEvent = async function (event) {
1548
- if (!event.origin || event.origin === '') {
1549
- // skip same-origin or when event.origin is empty/undefined
1550
- return;
1551
- }
1552
- if (_this.appOrigin && event.origin !== _this.appOrigin) {
1553
- // skip message as not from expected app origin
1554
- return;
1555
- }
1556
-
1393
+ // over the unreal json-messaging transport
1394
+ this.onMessageFromUnreal = request => {
1557
1395
  // Wallet always expects json-rpc request messages from a dapp
1558
- let request;
1559
- try {
1560
- request = JSON.parse(event.data);
1561
- } catch (err) {
1562
- // event is not a ProviderMessage JSON object, skip
1563
- return;
1564
- }
1565
- logger.debug('RECEIVED MESSAGE', request);
1566
1396
 
1567
- // Record event origin for valid init ack
1568
- if (_this._init !== InitState.OK && _this.isValidInitAck(request)) {
1569
- _this.appOrigin = event.origin;
1570
- }
1571
- if (_this._init === InitState.OK && (!_this.appOrigin || _this.appOrigin.length < 8)) {
1572
- // impossible state
1573
- logger.error('impossible state, init.OK and appOrigin required');
1574
- return;
1575
- }
1397
+ logger.debug('RECEIVED MESSAGE', request);
1576
1398
 
1577
1399
  // Handle message via the base transport
1578
- _this.handleMessage(request);
1400
+ this.handleMessage(request);
1579
1401
  };
1580
- this.getWindowTransportSession = windowParams => {
1402
+ this.getUnrealTransportSession = windowParams => {
1581
1403
  const params = new WindowSessionParams(windowParams);
1582
1404
  return {
1583
1405
  sessionId: params.get('sid'),
@@ -1588,18 +1410,17 @@ class WindowMessageHandler extends BaseWalletTransport {
1588
1410
  this._init = InitState.NIL;
1589
1411
  }
1590
1412
  async register(windowHref) {
1591
- const isPopup = parent.window.opener !== null;
1592
- this._isPopup = isPopup;
1593
- if (isPopup !== true) {
1413
+ var _window$ue;
1414
+ if (((_window$ue = window.ue) == null ? void 0 : _window$ue.sequencewallettransport) === undefined) {
1594
1415
  return;
1595
1416
  }
1417
+ overrideLogs('wallet');
1596
1418
 
1597
1419
  // record open details (sessionId + default network) from the window url
1598
1420
  const {
1599
- pathname,
1600
1421
  search: rawParams
1601
1422
  } = new URL(windowHref || window.location.href);
1602
- let session = this.getWindowTransportSession(rawParams);
1423
+ let session = this.getUnrealTransportSession(rawParams);
1603
1424
 
1604
1425
  // provider should always include sid when opening a new window
1605
1426
  const isNewWindowSession = !!session.sessionId;
@@ -1609,15 +1430,12 @@ class WindowMessageHandler extends BaseWalletTransport {
1609
1430
  session = await this.getCachedTransportSession();
1610
1431
  }
1611
1432
  if (!session) {
1612
- logger.error('window session is undefined');
1433
+ logger.error('unreal session is undefined');
1613
1434
  return;
1614
1435
  }
1615
1436
 
1616
- // record parent window instance for communication
1617
- this.parentWindow = parent.window.opener;
1618
-
1619
1437
  // listen for window-transport requests
1620
- window.addEventListener('message', this.onWindowEvent, false);
1438
+ window.ue.sequencewallettransport.onmessagefromsequencejs = this.onMessageFromUnreal;
1621
1439
  this._registered = true;
1622
1440
 
1623
1441
  // send open event to the app which opened us
@@ -1642,79 +1460,689 @@ class WindowMessageHandler extends BaseWalletTransport {
1642
1460
  });
1643
1461
  }
1644
1462
  unregister() {
1645
- window.removeEventListener('message', this.onWindowEvent);
1463
+ var _window$ue2, _window$ue2$sequencew;
1464
+ if (((_window$ue2 = window.ue) == null ? void 0 : (_window$ue2$sequencew = _window$ue2.sequencewallettransport) == null ? void 0 : _window$ue2$sequencew.onmessagefromsequencejs) === this.onMessageFromUnreal) {
1465
+ delete window.ue.sequencewallettransport.onmessagefromsequencejs;
1466
+ }
1646
1467
  this._registered = false;
1647
1468
  }
1648
- // postMessage sends message to the dapp window
1469
+ // sendMessage sends message to the dapp window
1649
1470
  sendMessage(message) {
1471
+ var _window$ue3, _window$ue3$sequencew;
1472
+ if (message.type !== EventType.INIT && this._init !== InitState.OK) {
1473
+ logger.error('impossible state, should not be calling postMessage until inited');
1474
+ return;
1475
+ }
1650
1476
  // prepare payload
1651
1477
  const payload = JSON.stringify(message);
1652
1478
 
1653
1479
  // post-message to app.
1654
- // only for init requests, we send to '*' origin
1655
- if (message.type === EventType.INIT) {
1656
- this.postMessage(payload, true);
1657
- } else {
1658
- this.postMessage(payload);
1659
- }
1480
+ (_window$ue3 = window.ue) == null ? void 0 : (_window$ue3$sequencew = _window$ue3.sequencewallettransport) == null ? void 0 : _window$ue3$sequencew.sendmessagetosequencejs(payload);
1660
1481
  }
1661
- get isPopup() {
1662
- return this._isPopup;
1482
+ }
1483
+
1484
+ const CHANNEL_ID = 'sequence-extension-message-handler';
1485
+ class ExtensionMessageHandler extends BaseWalletTransport {
1486
+ constructor(walletRequestHandler, runtime) {
1487
+ super(walletRequestHandler);
1488
+ this.runtime = runtime;
1489
+ this._init = InitState.OK;
1663
1490
  }
1664
- postMessage(message, init = false) {
1665
- if (init !== true && this._init !== InitState.OK) {
1666
- logger.error('impossible state, should not be calling postMessage until inited');
1667
- return;
1668
- }
1669
- if (init) {
1670
- // init message transmission to global target -- for 'init' payloads only
1671
- this.parentWindow.postMessage(message, '*');
1672
- } else {
1673
- // open message transmission
1674
- if (this.appOrigin && this.appOrigin.length > 4) {
1675
- // just above '.com'
1676
- this.parentWindow.postMessage(message, this.appOrigin);
1677
- } else {
1678
- logger.error('unable to postMessage as parentOrigin is invalid');
1679
- }
1680
- }
1491
+ register() {
1492
+ this._registered = true;
1493
+ this.port = this.runtime.connect({
1494
+ name: CHANNEL_ID
1495
+ });
1496
+ }
1497
+ sendMessage(message) {
1498
+ logger.info('[ExtensionMessageHandler send]', message);
1499
+ this.port.postMessage(message);
1681
1500
  }
1682
1501
  }
1683
1502
 
1684
- function toExtended(transactions) {
1685
- if (transactions.length === 0) {
1686
- throw new Error("No transaction provided");
1503
+ class ExtensionMessageProvider extends BaseProviderTransport {
1504
+ constructor(runtime) {
1505
+ super();
1506
+ this.register = () => {
1507
+ this._registered = true;
1508
+ };
1509
+ runtime.onConnect.addListener(port => {
1510
+ if (port.name === CHANNEL_ID) {
1511
+ this._init = InitState.OK;
1512
+ port.onMessage.addListener(message => {
1513
+ this.handleMessage(message);
1514
+ });
1515
+ }
1516
+ });
1517
+ }
1518
+ sendMessage(message) {
1519
+ //noop
1520
+ }
1521
+ unregister() {
1522
+ //noop
1523
+ }
1524
+ openWallet(path, intent, networkId) {
1525
+ //noop
1526
+ }
1527
+ closeWallet() {
1528
+ //noop
1687
1529
  }
1688
- const [first, ...rest] = transactions;
1689
- return _extends({}, first, {
1690
- auxiliary: rest
1691
- });
1692
- }
1693
- function fromExtended(transaction) {
1694
- return [transaction, ...(transaction.auxiliary || [])];
1695
1530
  }
1696
1531
 
1697
- const SIGNER_READY_TIMEOUT = 10000;
1698
- class WalletRequestHandler {
1699
- constructor(_account, prompter, networks) {
1700
- var _this = this;
1701
- // signer interface of the wallet. A null value means there is no signer (ie. user not signed in). An undefined
1702
- // value means the signer state is unknown, usually meaning the wallet app is booting up and initializing. Of course
1703
- // a Signer value is the actually interface to a signed-in account
1704
- this.signerReadyCallbacks = [];
1705
- this.events = new EventEmitter2();
1706
- this.onConnectOptionsChange = undefined;
1707
- this.promptConnect = async function (options) {
1708
- if (!options && !_this._connectOptions) {
1709
- // this is an unexpected state and should not happen
1710
- throw new Error('prompter connect options are empty');
1532
+ // to be used on injected window.ethereum EIP1193 proxy
1533
+ class BaseInjectedTransport extends EventEmitter2 {
1534
+ constructor(stream) {
1535
+ var _this;
1536
+ super();
1537
+ _this = this;
1538
+ this.responseCallbacks = new Map();
1539
+ this._messageIdx = 0;
1540
+ this.nextMessageIdx = () => ++this._messageIdx;
1541
+ this.handleMessage = message => {
1542
+ if (!message.type || !message.data) {
1543
+ return;
1711
1544
  }
1712
- if (!_this.prompter) {
1713
- // if prompter is null, we'll auto connect
1714
- return _this.connect(options);
1545
+ logger.info('[received message]', message);
1546
+ const requestIdx = message.idx;
1547
+ const responseCallback = this.responseCallbacks.get(requestIdx);
1548
+ if (requestIdx) {
1549
+ this.responseCallbacks.delete(requestIdx);
1715
1550
  }
1716
- const promptConnectDetails = await _this.prompter.promptConnect(options || _this._connectOptions).catch(_ => {
1717
- return {
1551
+ switch (message.type) {
1552
+ case EventType.MESSAGE:
1553
+ if (responseCallback) {
1554
+ this.emit(EventType.MESSAGE, message);
1555
+ responseCallback(message.data.error, message);
1556
+ } else {
1557
+ // NOTE: this would occur if 'idx' isn't set, which should never happen
1558
+ // or when we register two handler, or duplicate messages with the same idx are sent,
1559
+ // all of which should be prevented prior to getting to this point
1560
+ throw new Error('impossible state');
1561
+ }
1562
+ break;
1563
+ case EventType.DISCONNECT:
1564
+ case EventType.ACCOUNTS_CHANGED:
1565
+ case EventType.CHAIN_CHANGED:
1566
+ this.emit(message.type, message.data);
1567
+ break;
1568
+ default:
1569
+ console.error('unknown message type', message);
1570
+ break;
1571
+ }
1572
+ };
1573
+ this.sendMessageRequest = async function (message) {
1574
+ return new Promise((resolve, reject) => {
1575
+ if (!message.idx || message.idx <= 0) {
1576
+ reject(new Error('message idx not set'));
1577
+ }
1578
+ const responseCallback = (error, response) => {
1579
+ if (error) {
1580
+ reject(error);
1581
+ } else if (response) {
1582
+ resolve(response);
1583
+ } else {
1584
+ throw new Error('no valid response to return');
1585
+ }
1586
+ };
1587
+ const {
1588
+ idx
1589
+ } = message;
1590
+ if (!_this.responseCallbacks.get(idx)) {
1591
+ _this.responseCallbacks.set(idx, responseCallback);
1592
+ } else {
1593
+ reject(new Error('duplicate message idx, should never happen'));
1594
+ }
1595
+ _this.sendMessage(message);
1596
+ });
1597
+ };
1598
+ this.stream = stream;
1599
+ this.stream.on('data', this.handleMessage);
1600
+ }
1601
+ sendMessage(message) {
1602
+ if (!this.stream.writable) {
1603
+ console.error('window post message stream is not writable');
1604
+ }
1605
+ this.stream.write(message);
1606
+ }
1607
+ }
1608
+
1609
+ // ..
1610
+ let registeredWindowMessageProvider;
1611
+ class WindowMessageProvider extends BaseProviderTransport {
1612
+ constructor(walletAppURL) {
1613
+ super();
1614
+ this.register = () => {
1615
+ if (registeredWindowMessageProvider) {
1616
+ // overriding the registered message provider
1617
+ registeredWindowMessageProvider.unregister();
1618
+ registeredWindowMessageProvider = this;
1619
+ }
1620
+
1621
+ // listen for incoming messages from wallet
1622
+ window.addEventListener('message', this.onWindowEvent);
1623
+ registeredWindowMessageProvider = this;
1624
+
1625
+ // open heartbeat
1626
+ this.on('open', () => {
1627
+ // Heartbeat to track if window closed
1628
+ const popup = this.walletWindow;
1629
+ const interval = setInterval(() => {
1630
+ if (popup && popup.closed) {
1631
+ clearInterval(interval);
1632
+ this.close();
1633
+ }
1634
+ }, 500);
1635
+ });
1636
+
1637
+ // close clean up
1638
+ this.on('close', () => {
1639
+ if (this.walletWindow) {
1640
+ this.walletWindow.close();
1641
+ this.walletWindow = null;
1642
+ }
1643
+ });
1644
+ this._registered = true;
1645
+ };
1646
+ this.unregister = () => {
1647
+ this._registered = false;
1648
+ this.closeWallet();
1649
+
1650
+ // disable message listener
1651
+ if (registeredWindowMessageProvider === this) {
1652
+ registeredWindowMessageProvider = undefined;
1653
+ }
1654
+ window.removeEventListener('message', this.onWindowEvent);
1655
+
1656
+ // clear event listeners
1657
+ this.events.removeAllListeners();
1658
+ };
1659
+ this.openWallet = (path, intent, networkId) => {
1660
+ if (this.walletWindow && this.isOpened()) {
1661
+ // TODO: update the location of window to path
1662
+ this.walletWindow.focus();
1663
+ return;
1664
+ }
1665
+
1666
+ // Instantiate new walletURL for this call
1667
+ const walletURL = new URL(this.walletURL.href);
1668
+ const windowSessionParams = new WindowSessionParams();
1669
+ if (path && path !== '') {
1670
+ walletURL.pathname = path.toLowerCase();
1671
+ }
1672
+
1673
+ // Set session, intent and network id on walletURL
1674
+ this._init = InitState.NIL;
1675
+ this._sessionId = `${performance.now()}`;
1676
+ windowSessionParams.set('sid', this._sessionId);
1677
+ if (intent) {
1678
+ // for the window-transport, we eagerly/optimistically set the origin host
1679
+ // when connecting to the wallet, however, this will be verified and enforced
1680
+ // on the wallet-side, so if a dapp provides the wrong origin, it will be dropped.
1681
+ if (intent.type === 'connect') {
1682
+ if (!intent.options) intent.options = {
1683
+ app: window.location.origin
1684
+ };
1685
+
1686
+ // skip setting origin host if we're in an browser extension execution context
1687
+ // allow origin that is passed in
1688
+ if (!isBrowserExtension() && !isUnityPlugin() && intent.options) {
1689
+ intent.options.origin = window.location.origin;
1690
+ }
1691
+ }
1692
+ // encode intent as base64 url-encoded param
1693
+ windowSessionParams.set('intent', base64EncodeObject(intent));
1694
+ }
1695
+ if (networkId) {
1696
+ windowSessionParams.set('net', `${networkId}`);
1697
+ }
1698
+
1699
+ // Open popup window on center of the app window
1700
+ let windowSize;
1701
+ let windowPos;
1702
+ if (isBrowserExtension()) {
1703
+ windowSize = [450, 750];
1704
+ windowPos = [Math.abs(window.screen.width / 2 - windowSize[0] / 2), Math.abs(window.screen.height / 2 - windowSize[1] / 2)];
1705
+ } else {
1706
+ windowSize = [450, 750];
1707
+ windowPos = [Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2), Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)];
1708
+ }
1709
+ const windowFeatures = `toolbar=0,location=0,menubar=0,scrollbars=yes,status=yes` + `,width=${windowSize[0]},height=${windowSize[1]}` + `,left=${windowPos[0]},top=${windowPos[1]}`;
1710
+
1711
+ // serialize params
1712
+ walletURL.search = windowSessionParams.toString();
1713
+ this.walletWindow = window.open(walletURL.href, 'sequence.app', windowFeatures);
1714
+
1715
+ // TODO: move this somewhere else
1716
+ // TODO: perhaps we trigger a .on('openTimeout') event..? maybe.. could help.
1717
+
1718
+ // Popup blocking detection and notice
1719
+ // let warned = false
1720
+ // const warnPopupBlocked = () => {
1721
+ // if (warned) return
1722
+ // warned = true
1723
+ // // alert('popup is blocked! hey yo') // NOTE: for debug purposes only
1724
+ // throw new Error('popup is blocked')
1725
+ // }
1726
+
1727
+ // const popupCheck = setTimeout(() => {
1728
+ // if (!popup || popup.closed || typeof popup.closed === 'undefined') {
1729
+ // // popup is definitely blocked if we reach here.
1730
+ // warnPopupBlocked()
1731
+ // }
1732
+ // }, 1000)
1733
+
1734
+ // const popupBlocked = popup === null || popup === undefined
1735
+ // if (popupBlocked) {
1736
+ // warnPopupBlocked()
1737
+ // return
1738
+ // }
1739
+ };
1740
+ // onmessage, receives ProviderMessageResponse from the wallet post-message transport
1741
+ this.onWindowEvent = event => {
1742
+ // Security check, ensure message is coming from wallet origin url
1743
+ if (event.origin !== this.walletURL.origin) {
1744
+ // Safetly can skip events not from the wallet
1745
+ return;
1746
+ }
1747
+ let message;
1748
+ try {
1749
+ message = JSON.parse(event.data);
1750
+ } catch (err) {
1751
+ // event is not a ProviderMessage JSON object, skip
1752
+ return;
1753
+ }
1754
+ if (!message) {
1755
+ throw new Error('ProviderMessage object is empty');
1756
+ }
1757
+
1758
+ // handle message with base message provider
1759
+ this.handleMessage(message);
1760
+ };
1761
+ this.walletURL = new URL(walletAppURL);
1762
+ }
1763
+ closeWallet() {
1764
+ var _this$walletWindow;
1765
+ this.close();
1766
+ (_this$walletWindow = this.walletWindow) == null ? void 0 : _this$walletWindow.close();
1767
+ }
1768
+ sendMessage(message) {
1769
+ if (!this.walletWindow) {
1770
+ logger.warn('WindowMessageProvider: sendMessage failed as walletWindow is unavailable');
1771
+ return;
1772
+ }
1773
+ const postedMessage = typeof message !== 'string' ? JSON.stringify(message) : message;
1774
+ this.walletWindow.postMessage(postedMessage, this.walletURL.origin);
1775
+ }
1776
+ }
1777
+
1778
+ class WindowMessageHandler extends BaseWalletTransport {
1779
+ constructor(walletRequestHandler) {
1780
+ var _this;
1781
+ super(walletRequestHandler);
1782
+ _this = this;
1783
+ this._isPopup = false;
1784
+ // onmessage is called when (the wallet) receives request messages from the dapp
1785
+ // over the window post-messaging transport
1786
+ this.onWindowEvent = async function (event) {
1787
+ if (!event.origin || event.origin === '') {
1788
+ // skip same-origin or when event.origin is empty/undefined
1789
+ return;
1790
+ }
1791
+ if (_this.appOrigin && event.origin !== _this.appOrigin) {
1792
+ // skip message as not from expected app origin
1793
+ return;
1794
+ }
1795
+
1796
+ // Wallet always expects json-rpc request messages from a dapp
1797
+ let request;
1798
+ try {
1799
+ request = JSON.parse(event.data);
1800
+ } catch (err) {
1801
+ // event is not a ProviderMessage JSON object, skip
1802
+ return;
1803
+ }
1804
+ logger.debug('RECEIVED MESSAGE', request);
1805
+
1806
+ // Record event origin for valid init ack
1807
+ if (_this._init !== InitState.OK && _this.isValidInitAck(request)) {
1808
+ _this.appOrigin = event.origin;
1809
+ }
1810
+ if (_this._init === InitState.OK && (!_this.appOrigin || _this.appOrigin.length < 8)) {
1811
+ // impossible state
1812
+ logger.error('impossible state, init.OK and appOrigin required');
1813
+ return;
1814
+ }
1815
+
1816
+ // Handle message via the base transport
1817
+ _this.handleMessage(request);
1818
+ };
1819
+ this.getWindowTransportSession = windowParams => {
1820
+ const params = new WindowSessionParams(windowParams);
1821
+ return {
1822
+ sessionId: params.get('sid'),
1823
+ networkId: params.get('net'),
1824
+ intent: base64DecodeObject(params.get('intent'))
1825
+ };
1826
+ };
1827
+ this._init = InitState.NIL;
1828
+ }
1829
+ async register(windowHref) {
1830
+ const isPopup = parent.window.opener !== null;
1831
+ this._isPopup = isPopup;
1832
+ if (isPopup !== true) {
1833
+ return;
1834
+ }
1835
+
1836
+ // record open details (sessionId + default network) from the window url
1837
+ const {
1838
+ pathname,
1839
+ search: rawParams
1840
+ } = new URL(windowHref || window.location.href);
1841
+ let session = this.getWindowTransportSession(rawParams);
1842
+
1843
+ // provider should always include sid when opening a new window
1844
+ const isNewWindowSession = !!session.sessionId;
1845
+
1846
+ // attempt to restore previous session in the case of a redirect or window reload
1847
+ if (!isNewWindowSession) {
1848
+ session = await this.getCachedTransportSession();
1849
+ }
1850
+ if (!session) {
1851
+ logger.error('window session is undefined');
1852
+ return;
1853
+ }
1854
+
1855
+ // record parent window instance for communication
1856
+ this.parentWindow = parent.window.opener;
1857
+
1858
+ // listen for window-transport requests
1859
+ window.addEventListener('message', this.onWindowEvent, false);
1860
+ this._registered = true;
1861
+
1862
+ // send open event to the app which opened us
1863
+ this.open(session).then(opened => {
1864
+ if (!opened) {
1865
+ var _session;
1866
+ const err = `failed to open to network ${(_session = session) == null ? void 0 : _session.networkId}`;
1867
+ logger.error(err);
1868
+ this.notifyClose({
1869
+ message: err
1870
+ });
1871
+ window.close();
1872
+ }
1873
+ }).catch(e => {
1874
+ var _session2;
1875
+ const err = `failed to open to network ${(_session2 = session) == null ? void 0 : _session2.networkId}, due to: ${e}`;
1876
+ logger.error(err);
1877
+ this.notifyClose({
1878
+ message: err
1879
+ });
1880
+ window.close();
1881
+ });
1882
+ }
1883
+ unregister() {
1884
+ window.removeEventListener('message', this.onWindowEvent);
1885
+ this._registered = false;
1886
+ }
1887
+ // postMessage sends message to the dapp window
1888
+ sendMessage(message) {
1889
+ // prepare payload
1890
+ const payload = JSON.stringify(message);
1891
+
1892
+ // post-message to app.
1893
+ // only for init requests, we send to '*' origin
1894
+ if (message.type === EventType.INIT) {
1895
+ this.postMessage(payload, true);
1896
+ } else {
1897
+ this.postMessage(payload);
1898
+ }
1899
+ }
1900
+ get isPopup() {
1901
+ return this._isPopup;
1902
+ }
1903
+ postMessage(message, init = false) {
1904
+ if (init !== true && this._init !== InitState.OK) {
1905
+ logger.error('impossible state, should not be calling postMessage until inited');
1906
+ return;
1907
+ }
1908
+ if (init) {
1909
+ // init message transmission to global target -- for 'init' payloads only
1910
+ this.parentWindow.postMessage(message, '*');
1911
+ } else {
1912
+ // open message transmission
1913
+ if (this.appOrigin && this.appOrigin.length > 4) {
1914
+ // just above '.com'
1915
+ this.parentWindow.postMessage(message, this.appOrigin);
1916
+ } else {
1917
+ logger.error('unable to postMessage as parentOrigin is invalid');
1918
+ }
1919
+ }
1920
+ }
1921
+ }
1922
+
1923
+ function isMuxTransportTemplate(obj) {
1924
+ 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') && (
1925
+ // One of the transports must be enabled
1926
+ obj.windowTransport && obj.windowTransport.enabled || obj.proxyTransport && obj.proxyTransport.enabled || obj.extensionTransport && obj.extensionTransport.enabled || obj.unrealTransport && obj.unrealTransport.enabled);
1927
+ }
1928
+ class MuxMessageProvider {
1929
+ constructor(...messageProviders) {
1930
+ var _this = this;
1931
+ this.register = () => {
1932
+ if (this.messageProviders.length === 1) {
1933
+ this.provider = this.messageProviders[0];
1934
+ this.provider.register();
1935
+ return;
1936
+ }
1937
+
1938
+ // REVIEW/NOTE: ........ this method does not work for the chrome-extension. The issue becomes
1939
+ // when the browser quits or restarts, the "open" event is never triggered. Perhaps the code here is fine,
1940
+ // or maybe its not. What should happen is when a dapp makes a request, it will call openWallet
1941
+ // below, in which case one of the events will register. So perhaps this is fine.
1942
+ this.messageProviders.forEach(m => {
1943
+ m.register();
1944
+ m.once('open', () => {
1945
+ // the first one to open is the winner, and others will be unregistered
1946
+ if (!this.provider) {
1947
+ this.provider = m;
1948
+
1949
+ // unregister other providers
1950
+ this.messageProviders.forEach(m => {
1951
+ if (this.provider !== m) {
1952
+ m.unregister();
1953
+ }
1954
+ });
1955
+ }
1956
+ });
1957
+ });
1958
+ };
1959
+ this.unregister = () => {
1960
+ this.messageProviders.forEach(m => m.unregister());
1961
+ this.provider = undefined;
1962
+ };
1963
+ this.openWallet = (path, intent, networkId) => {
1964
+ if (this.provider) {
1965
+ this.provider.openWallet(path, intent, networkId);
1966
+ return;
1967
+ }
1968
+ this.messageProviders.forEach(m => m.openWallet(path, intent, networkId));
1969
+ };
1970
+ this.sendAsync = async function (request, callback, chainId) {
1971
+ if (_this.provider) {
1972
+ _this.provider.sendAsync(request, callback, chainId);
1973
+ return;
1974
+ }
1975
+ throw new Error('impossible state, wallet must be opened first');
1976
+ };
1977
+ this.sendMessageRequest = async function (message) {
1978
+ if (_this.provider) {
1979
+ return _this.provider.sendMessageRequest(message);
1980
+ }
1981
+ throw new Error('impossible state, wallet must be opened first');
1982
+ };
1983
+ this.waitUntilOpened = async function () {
1984
+ if (_this.provider) {
1985
+ return _this.provider.waitUntilOpened();
1986
+ }
1987
+ return Promise.race(_this.messageProviders.map(p => p.waitUntilOpened()));
1988
+ };
1989
+ this.waitUntilConnected = async function () {
1990
+ if (_this.provider) {
1991
+ return _this.provider.waitUntilConnected();
1992
+ }
1993
+ throw new Error('impossible state, wallet must be opened first');
1994
+ };
1995
+ this.messageProviders = messageProviders;
1996
+ this.provider = undefined;
1997
+ }
1998
+ static new(template) {
1999
+ var _template$windowTrans, _template$proxyTransp, _template$extensionTr, _template$unrealTrans;
2000
+ const muxMessageProvider = new MuxMessageProvider();
2001
+ if ((_template$windowTrans = template.windowTransport) != null && _template$windowTrans.enabled && typeof window === 'object' && template.walletAppURL) {
2002
+ const windowMessageProvider = new WindowMessageProvider(template.walletAppURL);
2003
+ muxMessageProvider.add(windowMessageProvider);
2004
+ }
2005
+ if ((_template$proxyTransp = template.proxyTransport) != null && _template$proxyTransp.enabled) {
2006
+ const proxyMessageProvider = new ProxyMessageProvider(template.proxyTransport.appPort);
2007
+ muxMessageProvider.add(proxyMessageProvider);
2008
+ }
2009
+ if ((_template$extensionTr = template.extensionTransport) != null && _template$extensionTr.enabled) {
2010
+ const extensionMessageProvider = new ExtensionMessageProvider(template.extensionTransport.runtime);
2011
+ muxMessageProvider.add(extensionMessageProvider);
2012
+
2013
+ // NOTE/REVIEW: see note in mux-message-provider
2014
+ //
2015
+ // We don't add the extensionMessageProvider here because we don't send requests to it anyways, we seem to
2016
+ // send all requests to the WindowMessageProvider anyways. By allowing it, if browser restarts, it will break
2017
+ // the entire extension because messageProvider.provider will be undefined. So this is a hack to fix it.
2018
+ }
2019
+
2020
+ if ((_template$unrealTrans = template.unrealTransport) != null && _template$unrealTrans.enabled && template.windowTransport && template.walletAppURL) {
2021
+ const unrealMessageProvider = new UnrealMessageProvider(template.walletAppURL);
2022
+ muxMessageProvider.add(unrealMessageProvider);
2023
+ }
2024
+ muxMessageProvider.register();
2025
+ return muxMessageProvider;
2026
+ }
2027
+ add(...messageProviders) {
2028
+ this.messageProviders.push(...messageProviders);
2029
+ }
2030
+ closeWallet() {
2031
+ if (this.provider) {
2032
+ this.provider.closeWallet();
2033
+ }
2034
+ }
2035
+ isOpened() {
2036
+ if (this.provider) {
2037
+ return this.provider.isOpened();
2038
+ }
2039
+ return false;
2040
+ }
2041
+ isConnected() {
2042
+ if (this.provider) {
2043
+ return this.provider.isConnected();
2044
+ }
2045
+ return false;
2046
+ }
2047
+ on(event, fn) {
2048
+ if (this.provider) {
2049
+ this.provider.on(event, fn);
2050
+ return;
2051
+ }
2052
+ this.messageProviders.forEach(m => {
2053
+ m.on(event, fn);
2054
+ });
2055
+ }
2056
+ once(event, fn) {
2057
+ if (this.provider) {
2058
+ this.provider.once(event, fn);
2059
+ return;
2060
+ }
2061
+ this.messageProviders.forEach(m => {
2062
+ m.once(event, fn);
2063
+ });
2064
+ }
2065
+ emit(event, ...args) {
2066
+ if (this.provider) {
2067
+ return this.provider.emit(event, ...args);
2068
+ }
2069
+ for (let i = 0; i < this.messageProviders.length; i++) {
2070
+ this.messageProviders[i].emit(event, ...args);
2071
+ }
2072
+ return true;
2073
+ }
2074
+ sendMessage(message) {
2075
+ if (!message.idx || message.idx <= 0) {
2076
+ throw new Error('message idx is empty');
2077
+ }
2078
+ if (this.provider) {
2079
+ this.provider.sendMessage(message);
2080
+ } else {
2081
+ throw new Error('impossible state, wallet must be opened first');
2082
+ }
2083
+ }
2084
+ handleMessage(message) {
2085
+ if (this.provider) {
2086
+ this.provider.handleMessage(message);
2087
+ return;
2088
+ }
2089
+ throw new Error('impossible state, wallet must be opened first');
2090
+ }
2091
+ }
2092
+
2093
+ function toExtended(transactions) {
2094
+ if (transactions.length === 0) {
2095
+ throw new Error("No transaction provided");
2096
+ }
2097
+ const [first, ...rest] = transactions;
2098
+ return _extends({}, first, {
2099
+ auxiliary: rest
2100
+ });
2101
+ }
2102
+ function fromExtended(transaction) {
2103
+ return [transaction, ...(transaction.auxiliary || [])];
2104
+ }
2105
+
2106
+ function validateTransactionRequest(wallet, transaction) {
2107
+ const transactions = commons.transaction.fromTransactionish(wallet, transaction);
2108
+ const unwound = commons.transaction.unwind(wallet, transactions);
2109
+ unwound.forEach(transaction => validateTransaction(wallet, transaction));
2110
+ }
2111
+ function validateTransaction(wallet, transaction) {
2112
+ if (transaction.to.toLowerCase() === wallet.toLowerCase()) {
2113
+ if (transaction.data) {
2114
+ const data = ethers.utils.arrayify(transaction.data);
2115
+ if (data.length >= 4) {
2116
+ throw new Error('self calls are forbidden');
2117
+ }
2118
+ }
2119
+ }
2120
+ if (transaction.delegateCall) {
2121
+ throw new Error('delegate calls are forbidden');
2122
+ }
2123
+ }
2124
+
2125
+ const SIGNER_READY_TIMEOUT = 10000;
2126
+ class WalletRequestHandler {
2127
+ constructor(_account, prompter, networks) {
2128
+ var _this = this;
2129
+ // signer interface of the wallet. A null value means there is no signer (ie. user not signed in). An undefined
2130
+ // value means the signer state is unknown, usually meaning the wallet app is booting up and initializing. Of course
2131
+ // a Signer value is the actually interface to a signed-in account
2132
+ this.signerReadyCallbacks = [];
2133
+ this.events = new EventEmitter2();
2134
+ this.onConnectOptionsChange = undefined;
2135
+ this.promptConnect = async function (options) {
2136
+ if (!options && !_this._connectOptions) {
2137
+ // this is an unexpected state and should not happen
2138
+ throw new Error('prompter connect options are empty');
2139
+ }
2140
+ if (!_this.prompter) {
2141
+ // if prompter is null, we'll auto connect
2142
+ return _this.connect(options);
2143
+ }
2144
+ const promptConnectDetails = await _this.prompter.promptConnect(options || _this._connectOptions).catch(_ => {
2145
+ return {
1718
2146
  connected: false
1719
2147
  };
1720
2148
  });
@@ -1884,6 +2312,7 @@ class WalletRequestHandler {
1884
2312
  }
1885
2313
  return tx;
1886
2314
  });
2315
+ validateTransactionRequest(account.address, transactionParams);
1887
2316
  let txnHash = '';
1888
2317
  if (_this.prompter === null) {
1889
2318
  // prompter is null, so we'll send from here
@@ -1909,6 +2338,7 @@ class WalletRequestHandler {
1909
2338
  if (sender !== account.address) {
1910
2339
  throw new Error('sender address does not match wallet');
1911
2340
  }
2341
+ validateTransactionRequest(account.address, transaction);
1912
2342
  if (_this.prompter === null) {
1913
2343
  // The eth_signTransaction method expects a `string` return value we instead return a `SignedTransactions` object,
1914
2344
  // this can only be broadcasted using an RPC provider with support for signed Sequence transactions, like this one.
@@ -2151,16 +2581,6 @@ class WalletRequestHandler {
2151
2581
  var _this$prompter$getDef, _this$prompter;
2152
2582
  return (_this$prompter$getDef = (_this$prompter = this.prompter) == null ? void 0 : _this$prompter.getDefaultChainId()) != null ? _this$prompter$getDef : this.networks[0].chainId;
2153
2583
  }
2154
- findNetworkID(network) {
2155
- const lnet = typeof network === 'string' ? network.toLowerCase() : network;
2156
- const networkId = this.networks.find(n => {
2157
- return n.name.toLowerCase() === lnet || n.chainId.toString() === lnet || n.chainId === lnet;
2158
- });
2159
- if (!networkId) {
2160
- throw new Error(`Network ${network} not found`);
2161
- }
2162
- return networkId.chainId;
2163
- }
2164
2584
  async signIn(account, options = {}) {
2165
2585
  this.setAccount(account);
2166
2586
  const {
@@ -2229,24 +2649,7 @@ class WalletRequestHandler {
2229
2649
  error: 'unable to connect without signed in account'
2230
2650
  };
2231
2651
  }
2232
- let chainId;
2233
- switch (typeof (options == null ? void 0 : options.networkId)) {
2234
- case 'string':
2235
- // First see if it matches the name of a network
2236
- try {
2237
- chainId = this.findNetworkID(options.networkId);
2238
- } catch (err) {
2239
- // If not, try to parse it as a big number
2240
- chainId = getChainId(options.networkId);
2241
- }
2242
- break;
2243
- case 'number':
2244
- chainId = options.networkId;
2245
- break;
2246
- default:
2247
- chainId = (_this$prompter$getDef2 = (_this$prompter4 = this.prompter) == null ? void 0 : _this$prompter4.getDefaultChainId()) != null ? _this$prompter$getDef2 : 1;
2248
- break;
2249
- }
2652
+ const chainId = (_this$prompter$getDef2 = (_this$prompter4 = this.prompter) == null ? void 0 : _this$prompter4.getDefaultChainId()) != null ? _this$prompter$getDef2 : 1;
2250
2653
  const connectDetails = {
2251
2654
  connected: true,
2252
2655
  chainId: ethers.BigNumber.from(chainId).toHexString()
@@ -2358,6 +2761,7 @@ class WalletRequestHandler {
2358
2761
  }
2359
2762
  notifyConnect(connectDetails, origin) {
2360
2763
  var _connectDetails$sessi;
2764
+ console.log('emit connect', connectDetails);
2361
2765
  this.events.emit('connect', connectDetails);
2362
2766
  if ((_connectDetails$sessi = connectDetails.session) != null && _connectDetails$sessi.accountAddress) {
2363
2767
  var _connectDetails$sessi2;
@@ -2443,1406 +2847,1082 @@ function getLegacyWalletState(chainId, status) {
2443
2847
  }
2444
2848
  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'];
2445
2849
 
2446
- const CHANNEL_ID = 'sequence-extension-message-handler';
2447
- class ExtensionMessageHandler extends BaseWalletTransport {
2448
- constructor(walletRequestHandler, runtime) {
2449
- super(walletRequestHandler);
2450
- this.runtime = runtime;
2451
- this._init = InitState.OK;
2850
+ class SequenceSigner {
2851
+ get utils() {
2852
+ return this.provider.utils;
2853
+ }
2854
+ constructor(client, provider) {
2855
+ this.singleNetworkSigners = {};
2856
+ this._isSigner = true;
2857
+ this._isSequenceSigner = true;
2858
+ this.client = client;
2859
+ this.provider = provider;
2860
+ }
2861
+ async getAddress() {
2862
+ return this.client.getAddress();
2863
+ }
2864
+
2865
+ // This method shouldn't be used directly
2866
+ // it exists to maintain compatibility with ethers.Signer
2867
+ connect(provider) {
2868
+ if (!SequenceProvider.is(provider)) {
2869
+ throw new Error("SequenceSigner can only be connected to a SequenceProvider");
2870
+ }
2871
+ return new SequenceSigner(this.client, provider);
2872
+ }
2873
+ getSigner(chainId) {
2874
+ // The signer for the default network is this signer
2875
+ if (!chainId) {
2876
+ return this;
2877
+ }
2878
+ const useChainId = this.provider.toChainId(chainId);
2879
+ if (!this.singleNetworkSigners[useChainId]) {
2880
+ this.singleNetworkSigners[useChainId] = new SingleNetworkSequenceSigner(this.client, this.provider, useChainId);
2881
+ }
2882
+ return this.singleNetworkSigners[useChainId];
2883
+ }
2884
+
2885
+ /**
2886
+ * Resolves the chainId to use for the given request. If no chainId is provided,
2887
+ * it uses the chainId defined by the client (default chainId). This can be
2888
+ * overriden to build a single-network SequenceProvider.
2889
+ */
2890
+ useChainId(chainId) {
2891
+ return this.provider.toChainId(chainId) || this.client.getChainId();
2892
+ }
2893
+ async signMessage(message, options) {
2894
+ const {
2895
+ eip6492 = true
2896
+ } = options || {};
2897
+ const chainId = this.useChainId(options == null ? void 0 : options.chainId);
2898
+ return this.client.signMessage(message, {
2899
+ eip6492,
2900
+ chainId
2901
+ });
2902
+ }
2903
+ async signTypedData(domain, types, message, options) {
2904
+ const {
2905
+ eip6492 = true
2906
+ } = options || {};
2907
+ const chainId = this.useChainId(options == null ? void 0 : options.chainId);
2908
+ return this.client.signTypedData({
2909
+ domain,
2910
+ types,
2911
+ message
2912
+ }, {
2913
+ eip6492,
2914
+ chainId
2915
+ });
2916
+ }
2917
+ getProvider(chainId) {
2918
+ return this.provider.getProvider(chainId);
2919
+ }
2920
+ async sendTransaction(transaction, options) {
2921
+ const chainId = this.useChainId(options == null ? void 0 : options.chainId);
2922
+ const resolved = await resolveArrayProperties(transaction);
2923
+ const txHash = await this.client.sendTransaction(resolved, {
2924
+ chainId
2925
+ });
2926
+ const provider = this.getProvider(chainId);
2927
+ try {
2928
+ return await ethers.utils.poll(async function () {
2929
+ const tx = await provider.getTransaction(txHash);
2930
+ return tx ? provider._wrapTransaction(tx, txHash) : undefined;
2931
+ }, {
2932
+ onceBlock: provider
2933
+ });
2934
+ } catch (err) {
2935
+ err.transactionHash = txHash;
2936
+ throw err;
2937
+ }
2452
2938
  }
2453
- register() {
2454
- this._registered = true;
2455
- this.port = this.runtime.connect({
2456
- name: CHANNEL_ID
2939
+ async getWalletConfig(chainId) {
2940
+ const useChainId = this.useChainId(chainId);
2941
+ return this.client.getOnchainWalletConfig({
2942
+ chainId: useChainId
2457
2943
  });
2458
2944
  }
2459
- sendMessage(message) {
2460
- logger.info('[ExtensionMessageHandler send]', message);
2461
- this.port.postMessage(message);
2945
+ getNetworks() {
2946
+ return this.client.getNetworks();
2462
2947
  }
2463
- }
2464
-
2465
- class ExtensionMessageProvider extends BaseProviderTransport {
2466
- constructor(runtime) {
2467
- super();
2468
- this.register = () => {
2469
- this._registered = true;
2470
- };
2471
- runtime.onConnect.addListener(port => {
2472
- if (port.name === CHANNEL_ID) {
2473
- this._init = InitState.OK;
2474
- port.onMessage.addListener(message => {
2475
- this.handleMessage(message);
2476
- });
2477
- }
2478
- });
2948
+ async getBalance(blockTag, optionals) {
2949
+ const provider = this.getProvider(optionals == null ? void 0 : optionals.chainId);
2950
+ return provider.getBalance(this.getAddress(), blockTag);
2479
2951
  }
2480
- sendMessage(message) {
2481
- //noop
2952
+ async estimateGas(transaction, optionals) {
2953
+ return this.getProvider(optionals == null ? void 0 : optionals.chainId).estimateGas(transaction);
2482
2954
  }
2483
- unregister() {
2484
- //noop
2955
+ async call(transaction, blockTag, optionals) {
2956
+ return this.getProvider(optionals == null ? void 0 : optionals.chainId).call(transaction, blockTag);
2485
2957
  }
2486
- openWallet(path, intent, networkId) {
2487
- //noop
2958
+ getChainId() {
2959
+ return Promise.resolve(this.client.getChainId());
2488
2960
  }
2489
- closeWallet() {
2490
- //noop
2961
+ async getGasPrice(optionals) {
2962
+ return this.getProvider(optionals == null ? void 0 : optionals.chainId).getGasPrice();
2491
2963
  }
2492
- }
2493
-
2494
- // to be used on injected window.ethereum EIP1193 proxy
2495
- class BaseInjectedTransport extends EventEmitter2 {
2496
- constructor(stream) {
2497
- var _this;
2498
- super();
2499
- _this = this;
2500
- this.responseCallbacks = new Map();
2501
- this._messageIdx = 0;
2502
- this.nextMessageIdx = () => ++this._messageIdx;
2503
- this.handleMessage = message => {
2504
- if (!message.type || !message.data) {
2505
- return;
2506
- }
2507
- logger.info('[received message]', message);
2508
- const requestIdx = message.idx;
2509
- const responseCallback = this.responseCallbacks.get(requestIdx);
2510
- if (requestIdx) {
2511
- this.responseCallbacks.delete(requestIdx);
2512
- }
2513
- switch (message.type) {
2514
- case EventType.MESSAGE:
2515
- if (responseCallback) {
2516
- this.emit(EventType.MESSAGE, message);
2517
- responseCallback(message.data.error, message);
2518
- } else {
2519
- // NOTE: this would occur if 'idx' isn't set, which should never happen
2520
- // or when we register two handler, or duplicate messages with the same idx are sent,
2521
- // all of which should be prevented prior to getting to this point
2522
- throw new Error('impossible state');
2523
- }
2524
- break;
2525
- case EventType.DISCONNECT:
2526
- case EventType.ACCOUNTS_CHANGED:
2527
- case EventType.CHAIN_CHANGED:
2528
- this.emit(message.type, message.data);
2529
- break;
2530
- default:
2531
- console.error('unknown message type', message);
2532
- break;
2533
- }
2534
- };
2535
- this.sendMessageRequest = async function (message) {
2536
- return new Promise((resolve, reject) => {
2537
- if (!message.idx || message.idx <= 0) {
2538
- reject(new Error('message idx not set'));
2539
- }
2540
- const responseCallback = (error, response) => {
2541
- if (error) {
2542
- reject(error);
2543
- } else if (response) {
2544
- resolve(response);
2545
- } else {
2546
- throw new Error('no valid response to return');
2547
- }
2548
- };
2549
- const {
2550
- idx
2551
- } = message;
2552
- if (!_this.responseCallbacks.get(idx)) {
2553
- _this.responseCallbacks.set(idx, responseCallback);
2554
- } else {
2555
- reject(new Error('duplicate message idx, should never happen'));
2556
- }
2557
- _this.sendMessage(message);
2558
- });
2559
- };
2560
- this.stream = stream;
2561
- this.stream.on('data', this.handleMessage);
2964
+ async getFeeData(optionals) {
2965
+ return this.getProvider(optionals == null ? void 0 : optionals.chainId).getFeeData();
2562
2966
  }
2563
- sendMessage(message) {
2564
- if (!this.stream.writable) {
2565
- console.error('window post message stream is not writable');
2967
+ async resolveName(name) {
2968
+ const res = await this.provider.resolveName(name);
2969
+
2970
+ // For some reason ethers.Signer expects this to return `string`
2971
+ // but ethers.providers.Provider expects this to return `string | null`.
2972
+ // The signer doesn't have any other source of information, so we'll
2973
+ // fail if the provider doesn't return a result.
2974
+ if (res === null) {
2975
+ throw new Error(`ENS name not found: ${name}`);
2566
2976
  }
2567
- this.stream.write(message);
2977
+ return res;
2568
2978
  }
2569
- }
2570
-
2571
- /**
2572
- * This will redirect console logs from Sequence.js & the wallet to the Unreal console, for debugging purposes.
2573
- */
2574
- function overrideLogs(side) {
2575
- var _window$ue;
2576
- if ((_window$ue = window.ue) != null && _window$ue.sequencewallettransport && !window.logsOverriddenForUnreal) {
2577
- var _window$ue2;
2578
- const t = (_window$ue2 = window.ue) == null ? void 0 : _window$ue2.sequencewallettransport;
2579
- console.log = (...args) => {
2580
- t.logfromjs(`${side}: ${stringify(args)}`);
2581
- };
2582
- console.warn = (...args) => {
2583
- t.warnfromjs(`${side}: ${stringify(args)}`);
2584
- };
2585
- console.error = (...args) => {
2586
- t.errorfromjs(`${side}: ${stringify(args)}`);
2587
- };
2588
- window.logsOverriddenForUnreal = true;
2979
+ _checkProvider(_operation) {
2980
+ // We always have a provider, so this is a noop
2589
2981
  }
2590
- }
2591
- function stringify(things) {
2592
- return things.map(a => typeof a === 'object' ? a instanceof Error ? a.message : JSON.stringify(a) : String(a)).join(' ');
2593
- }
2594
-
2595
- let registeredUnrealMessageProvider;
2596
-
2597
- // all lowercase is an annoying limitation of Unreal CEF BindUObject
2598
-
2599
- /**
2600
- * Initialized on dApp side
2601
- */
2602
- class UnrealMessageProvider extends BaseProviderTransport {
2603
- constructor(walletAppURL) {
2604
- super();
2605
- this.register = () => {
2606
- var _window$ue;
2607
- overrideLogs('dapp');
2608
- if (registeredUnrealMessageProvider) {
2609
- // overriding the registered message provider
2610
- registeredUnrealMessageProvider.unregister();
2611
- registeredUnrealMessageProvider = this;
2612
- }
2613
-
2614
- // listen for incoming messages from wallet
2615
- if ((_window$ue = window.ue) != null && _window$ue.sequencewallettransport) {
2616
- window.ue.sequencewallettransport.onmessagefromwallet = this.onUnrealCallback;
2617
- }
2618
- registeredUnrealMessageProvider = this;
2619
- this._registered = true;
2620
- console.log('registering transport!');
2621
- };
2622
- this.unregister = () => {
2623
- var _window$ue2, _window$ue2$sequencew;
2624
- this._registered = false;
2625
- this.closeWallet();
2626
-
2627
- // disable message listener
2628
- if (registeredUnrealMessageProvider === this) {
2629
- registeredUnrealMessageProvider = undefined;
2630
- }
2631
- if (((_window$ue2 = window.ue) == null ? void 0 : (_window$ue2$sequencew = _window$ue2.sequencewallettransport) == null ? void 0 : _window$ue2$sequencew.onmessagefromwallet) === this.onUnrealCallback) {
2632
- delete window.ue.sequencewallettransport.onmessagefromwallet;
2633
- }
2634
-
2635
- // clear event listeners
2636
- this.events.removeAllListeners();
2637
- };
2638
- this.openWallet = (path, intent, networkId) => {
2639
- if (this.isOpened()) {
2640
- // TODO focus wallet
2641
- console.log('wallet already open!');
2642
- return;
2643
- }
2644
- console.log('opening wallet!');
2645
- // Instantiate new walletURL for this call
2646
- const walletURL = new URL(this.walletURL.href);
2647
- const windowSessionParams = new WindowSessionParams();
2648
- if (path) {
2649
- walletURL.pathname = path.toLowerCase();
2650
- }
2651
-
2652
- // Set session, intent and network id on walletURL
2653
- this._init = InitState.NIL;
2654
- this._sessionId = `${performance.now()}`;
2655
- windowSessionParams.set('sid', this._sessionId);
2656
- if (intent) {
2657
- // encode intent as base64 url-encoded param
2658
- windowSessionParams.set('intent', base64EncodeObject(intent));
2659
- }
2660
- if (networkId) {
2661
- windowSessionParams.set('net', `${networkId}`);
2662
- }
2663
- // serialize params
2664
- walletURL.search = windowSessionParams.toString();
2665
- console.log('opening wallet to', walletURL.href);
2666
- window.open(walletURL.href);
2667
- };
2668
- // onmessage, receives ProviderMessageResponse from the wallet unreal transport
2669
- this.onUnrealCallback = message => {
2670
- if (!message) {
2671
- throw new Error('ProviderMessage object is empty');
2672
- }
2673
-
2674
- // handle message with base message provider
2675
- this.handleMessage(message);
2676
- };
2677
- this.walletURL = new URL(walletAppURL);
2982
+ populateTransaction(_transaction) {
2983
+ throw new Error("SequenceSigner does not support populateTransaction");
2678
2984
  }
2679
- closeWallet() {
2680
- this.close();
2985
+ checkTransaction(_transaction) {
2986
+ throw new Error("SequenceSigner does not support checkTransaction");
2681
2987
  }
2682
- // all lowercase is an annoying limitation of Unreal CEF BindUObject
2683
- sendMessage(message) {
2684
- var _window$ue3, _window$ue3$sequencew;
2685
- const postedMessage = typeof message !== 'string' ? JSON.stringify(message) : message;
2686
- console.log('Sending message to wallet:', postedMessage);
2687
- (_window$ue3 = window.ue) == null ? void 0 : (_window$ue3$sequencew = _window$ue3.sequencewallettransport) == null ? void 0 : _window$ue3$sequencew.sendmessagetowallet(postedMessage);
2988
+ getTransactionCount(_blockTag) {
2989
+ // We could try returning the sequence nonce here
2990
+ // but we aren't sure how ethers will use this nonce
2991
+ throw new Error("SequenceSigner does not support getTransactionCount");
2688
2992
  }
2689
- }
2690
-
2691
- // all lowercase is an annoying limitation of Unreal CEF BindUObject
2692
-
2693
- /**
2694
- * Initialized on Wallet side
2695
- */
2696
- class UnrealMessageHandler extends BaseWalletTransport {
2697
- constructor(walletRequestHandler) {
2698
- super(walletRequestHandler);
2699
- // onmessage is called when (the wallet) receives request messages from the dapp
2700
- // over the unreal json-messaging transport
2701
- this.onMessageFromUnreal = request => {
2702
- // Wallet always expects json-rpc request messages from a dapp
2703
-
2704
- logger.debug('RECEIVED MESSAGE', request);
2705
-
2706
- // Handle message via the base transport
2707
- this.handleMessage(request);
2708
- };
2709
- this.getUnrealTransportSession = windowParams => {
2710
- const params = new WindowSessionParams(windowParams);
2711
- return {
2712
- sessionId: params.get('sid'),
2713
- networkId: params.get('net'),
2714
- intent: base64DecodeObject(params.get('intent'))
2715
- };
2716
- };
2717
- this._init = InitState.NIL;
2993
+ signTransaction(_transaction) {
2994
+ // We could implement signTransaction/sendTransaction here
2995
+ // but first we need a way of serializing these signed transactions
2996
+ // and it could lead to more trouble, because the dapp could try to send this transaction
2997
+ // using a different provider, which would fail.
2998
+ throw new Error("SequenceWallet does not support signTransaction, use sendTransaction instead.");
2999
+ }
3000
+ static is(cand) {
3001
+ return cand && typeof cand === "object" && cand._isSequenceSigner === true;
2718
3002
  }
2719
- async register(windowHref) {
2720
- var _window$ue;
2721
- if (((_window$ue = window.ue) == null ? void 0 : _window$ue.sequencewallettransport) === undefined) {
2722
- return;
2723
- }
2724
- overrideLogs('wallet');
2725
-
2726
- // record open details (sessionId + default network) from the window url
2727
- const {
2728
- search: rawParams
2729
- } = new URL(windowHref || window.location.href);
2730
- let session = this.getUnrealTransportSession(rawParams);
3003
+ }
2731
3004
 
2732
- // provider should always include sid when opening a new window
2733
- const isNewWindowSession = !!session.sessionId;
3005
+ /**
3006
+ * This is the same provider, but it only allows a single network at a time.
3007
+ * the network defined by the constructor is the only one that can be used.
3008
+ *
3009
+ * Attempting to call any method with a different network will throw an error.
3010
+ * Attempting to change the network of this provider will throw an error.
3011
+ *
3012
+ * NOTICE: These networks won't support ENS unless they are the mainnet.
3013
+ */
3014
+ class SingleNetworkSequenceSigner extends SequenceSigner {
3015
+ constructor(client, provider, chainId) {
3016
+ super(client, provider.getProvider(chainId));
3017
+ this._isSingleNetworkSequenceSigner = true;
3018
+ this.chainId = chainId;
3019
+ }
3020
+ _useChainId(chainId) {
3021
+ const provided = this.provider.toChainId(chainId);
3022
+ if (provided && provided !== this.chainId) {
3023
+ throw new Error(`This signer only supports the network ${this.chainId}, but ${provided} was requested.`);
3024
+ }
3025
+ return provided || this.provider.toChainId(this.chainId);
3026
+ }
3027
+ useChainId(chainId) {
3028
+ return this._useChainId(chainId);
3029
+ }
3030
+ getChainId() {
3031
+ return Promise.resolve(this.provider.toChainId(this.chainId));
3032
+ }
2734
3033
 
2735
- // attempt to restore previous session in the case of a redirect or window reload
2736
- if (!isNewWindowSession) {
2737
- session = await this.getCachedTransportSession();
3034
+ /**
3035
+ * Override getProvider and getSigner so they always use `useChainId`
3036
+ * this way they can't return providers and signers that can switch networks,
3037
+ * or that don't match the chainId of this signer.
3038
+ */
3039
+ getProvider(chainId) {
3040
+ return super.getProvider(this._useChainId(chainId));
3041
+ }
3042
+ getSigner(chainId) {
3043
+ if (this._useChainId(chainId) !== this.chainId) {
3044
+ throw new Error(`Unreachable code`);
2738
3045
  }
2739
- if (!session) {
2740
- logger.error('unreal session is undefined');
2741
- return;
3046
+ return this;
3047
+ }
3048
+ static is(cand) {
3049
+ return cand && typeof cand === "object" && cand._isSingleNetworkSequenceSigner === true;
3050
+ }
3051
+ }
3052
+
3053
+ /**
3054
+ * This class is redundant with the SequenceSigner class, but it is here for now to
3055
+ * maintain compatibility with the old wallet API. Eventually we should move these
3056
+ * methods to the SequenceSigner class and deprecate this class.
3057
+ */
3058
+ class WalletUtils {
3059
+ constructor(signer) {
3060
+ this.signer = signer;
3061
+ if (SingleNetworkSequenceSigner.is(signer)) {
3062
+ throw new Error('WalletUtils does not support SingleNetworkSequenceSigner');
2742
3063
  }
3064
+ }
2743
3065
 
2744
- // listen for window-transport requests
2745
- window.ue.sequencewallettransport.onmessagefromsequencejs = this.onMessageFromUnreal;
2746
- this._registered = true;
3066
+ // Sign message on a specified chain, or DefaultChain by default
3067
+ signMessage(message, chainId, eip6492) {
3068
+ return this.signer.signMessage(message, {
3069
+ chainId,
3070
+ eip6492
3071
+ });
3072
+ }
2747
3073
 
2748
- // send open event to the app which opened us
2749
- this.open(session).then(opened => {
2750
- if (!opened) {
2751
- var _session;
2752
- const err = `failed to open to network ${(_session = session) == null ? void 0 : _session.networkId}`;
2753
- logger.error(err);
2754
- this.notifyClose({
2755
- message: err
2756
- });
2757
- window.close();
2758
- }
2759
- }).catch(e => {
2760
- var _session2;
2761
- const err = `failed to open to network ${(_session2 = session) == null ? void 0 : _session2.networkId}, due to: ${e}`;
2762
- logger.error(err);
2763
- this.notifyClose({
2764
- message: err
2765
- });
2766
- window.close();
3074
+ // Sign EIP-712 TypedData on a specified chain, or DefaultChain by default
3075
+ signTypedData(domain, types, message, chainId, eip6492) {
3076
+ return this.signer.signTypedData(domain, types, message, {
3077
+ chainId,
3078
+ eip6492
2767
3079
  });
2768
3080
  }
2769
- unregister() {
2770
- var _window$ue2, _window$ue2$sequencew;
2771
- if (((_window$ue2 = window.ue) == null ? void 0 : (_window$ue2$sequencew = _window$ue2.sequencewallettransport) == null ? void 0 : _window$ue2$sequencew.onmessagefromsequencejs) === this.onMessageFromUnreal) {
2772
- delete window.ue.sequencewallettransport.onmessagefromsequencejs;
2773
- }
2774
- this._registered = false;
3081
+
3082
+ // Verify signature of a digest, one of a message, typedData or other
3083
+ async isValidSignature(address, digest, signature, chainId) {
3084
+ return isValidSignature(address, digest, signature, this.signer.getProvider(chainId));
2775
3085
  }
2776
- // sendMessage sends message to the dapp window
2777
- sendMessage(message) {
2778
- var _window$ue3, _window$ue3$sequencew;
2779
- if (message.type !== EventType.INIT && this._init !== InitState.OK) {
2780
- logger.error('impossible state, should not be calling postMessage until inited');
2781
- return;
2782
- }
2783
- // prepare payload
2784
- const payload = JSON.stringify(message);
2785
3086
 
2786
- // post-message to app.
2787
- (_window$ue3 = window.ue) == null ? void 0 : (_window$ue3$sequencew = _window$ue3.sequencewallettransport) == null ? void 0 : _window$ue3$sequencew.sendmessagetosequencejs(payload);
3087
+ // Verify message signature
3088
+ async isValidMessageSignature(address, message, signature, chainId) {
3089
+ const provider = this.signer.getProvider(chainId);
3090
+ const prefixed = prefixEIP191Message(message);
3091
+ const digest = encodeMessageDigest(prefixed);
3092
+ return isValidSignature(address, digest, signature, provider);
2788
3093
  }
2789
- }
2790
3094
 
2791
- class Web3Provider extends providers.Web3Provider {
2792
- static isSequenceProvider(cand) {
2793
- return isSequenceProvider(cand);
3095
+ // Verify typedData signature
3096
+ isValidTypedDataSignature(address, typedData, signature, chainId) {
3097
+ return this.isValidSignature(address, encodeTypedDataDigest(typedData), signature, chainId);
2794
3098
  }
2795
3099
 
2796
- // defaultChainId is the default chainId to use with requests, but may be
2797
- // overridden by passing chainId argument to a specific request
2798
- constructor(provider, defaultChainId) {
2799
- const sender = new JsonRpcSender(provider, maybeChainId(defaultChainId));
2800
- provider = sender;
2801
- super(provider, 'any');
2802
- this._sender = sender;
3100
+ // sendTransaction()
3101
+ // sendTransactions()
3102
+
3103
+ // sendETH()
3104
+ // sendToken()
3105
+ // sendCoin() -- sugar for sendToken()
3106
+ // sendCollectible() -- sugar for sendToken()
3107
+ // callContract()
3108
+
3109
+ // transactionHistory()
3110
+ // getReceipt()
3111
+ // getLogs()
3112
+ // // ..
3113
+
3114
+ // validateSignature()
3115
+ // recoverWalletConfig()
3116
+ // recoverAddress()
3117
+ }
3118
+
3119
+ class SequenceProvider extends ethers.providers.BaseProvider {
3120
+ constructor(client, providerFor, networks = allNetworks) {
3121
+ // We support a lot of networks
3122
+ // but we start with the default one
3123
+ super(client.getChainId());
3124
+
3125
+ // Emit events as defined by EIP-1193
3126
+ this.singleNetworkProviders = {};
2803
3127
  this._isSequenceProvider = true;
2804
- this._defaultChainId = maybeChainId(defaultChainId);
3128
+ this.client = client;
3129
+ this.providerFor = providerFor;
3130
+ this.networks = networks;
3131
+ client.onConnect(details => {
3132
+ this.emit('connect', details);
3133
+ });
3134
+ client.onDisconnect(error => {
3135
+ this.emit('disconnect', error);
3136
+ });
3137
+ client.onDefaultChainIdChanged(chainId => {
3138
+ this.emit('chainChanged', chainId);
3139
+ });
3140
+ client.onAccountsChanged(accounts => {
3141
+ this.emit('accountsChanged', accounts);
3142
+ });
3143
+
3144
+ // NOTICE: We don't emit 'open' and 'close' events
3145
+ // because these are handled by the library, and they
3146
+ // are not part of EIP-1193
3147
+
3148
+ // devs can still access them using
3149
+ // client.onOpen()
3150
+ // client.onClose()
3151
+
3152
+ // Create a Sequence signer too
3153
+ this.signer = new SequenceSigner(this.client, this);
3154
+
3155
+ // Create a utils instance
3156
+ this.utils = new WalletUtils(this.signer);
2805
3157
  }
2806
- sendAsync(request, callback, chainId) {
2807
- this._sender.sendAsync(request, callback, chainId);
3158
+ getSigner(chainId) {
3159
+ return this.signer.getSigner(chainId);
2808
3160
  }
2809
- send(method, params, chainId) {
2810
- return this._sender.send(method, params, chainId);
3161
+ connect(options) {
3162
+ return this.client.connect(options);
2811
3163
  }
2812
- request(request) {
2813
- return this.send(request.method, request.params || [], request.chainId);
3164
+ disconnect() {
3165
+ return this.client.disconnect();
2814
3166
  }
2815
- getSigner() {
2816
- return new Web3Signer(this, this._defaultChainId);
3167
+ isConnected() {
3168
+ return this.client.isConnected();
3169
+ }
3170
+ getSession() {
3171
+ return this.client.getSession();
3172
+ }
3173
+ listAccounts() {
3174
+ return [this.client.getAddress()];
2817
3175
  }
2818
- async getChainId() {
2819
- // If we already have a default chainId, then we can just return it
2820
- const defaultChainId = this._defaultChainId;
2821
- if (defaultChainId) return defaultChainId;
2822
3176
 
2823
- // If there is no default chain, then we can just return the chainId of the provider
2824
- return this.send('eth_chainId', []);
3177
+ // @deprecated use getSigner() instead
3178
+ getAddress() {
3179
+ return this.client.getAddress();
2825
3180
  }
2826
3181
  getNetworks() {
2827
- return this.send('sequence_getNetworks', []);
3182
+ return this.client.getNetworks();
2828
3183
  }
2829
- }
2830
- function isSequenceProvider(provider) {
2831
- const cand = provider;
2832
- return cand && cand.send !== undefined && cand._isSequenceProvider === true;
2833
- }
2834
- class LocalWeb3Provider extends Web3Provider {
2835
- constructor(account, networks) {
2836
- const walletRequestHandler = new WalletRequestHandler(account, null, networks || []);
2837
- super(walletRequestHandler);
3184
+ getChainId() {
3185
+ return this.client.getChainId();
2838
3186
  }
2839
- }
2840
-
2841
- // TODO: in the future with ethers v6 we can remove/change this type name
2842
-
2843
- class Web3Signer extends Signer {
2844
- constructor(provider, defaultChainId) {
2845
- super();
2846
- this._providers = {};
2847
- this.provider = provider;
2848
- this.defaultChainId = defaultChainId;
3187
+ setDefaultChainId(chainId) {
3188
+ return this.client.setDefaultChainId(this.toChainId(chainId));
3189
+ }
3190
+ isOpened() {
3191
+ return this.client.isOpened();
3192
+ }
3193
+ closeWallet() {
3194
+ return this.client.closeWallet();
3195
+ }
3196
+ getWalletContext() {
3197
+ return this.client.getWalletContext();
2849
3198
  }
2850
3199
 
2851
- // memoized
2852
-
2853
- //
2854
- // ethers AbstractSigner methods
2855
- //
2856
- async getAddress() {
2857
- if (this._address) return this._address;
2858
- const accounts = await this.provider.send('eth_accounts', []);
2859
- this._address = accounts[0];
2860
- this._index = 0;
2861
- return ethers.utils.getAddress(this._address);
3200
+ // @deprecated use getSigner() instead
3201
+ async getWalletConfig(chainId) {
3202
+ const useChainId = await this.useChainId(chainId);
3203
+ return this.client.getOnchainWalletConfig({
3204
+ chainId: useChainId
3205
+ });
2862
3206
  }
2863
- signTransaction(transaction) {
2864
- // TODO .. since ethers isn't using this method, perhaps we will?
2865
- throw new Error('signTransaction is unsupported, use signTransactions instead');
3207
+ authorize(options) {
3208
+ // Just an alias for connect with authorize: true
3209
+ return this.client.connect(_extends({}, options, {
3210
+ authorize: true
3211
+ }));
2866
3212
  }
2867
- connect(provider) {
2868
- throw new Error('unsupported: cannot alter JSON-RPC Signer connection');
3213
+ async openWallet(path, intent) {
3214
+ await this.client.openWallet(path, intent);
3215
+ return true;
3216
+ }
3217
+ toChainId(chainId) {
3218
+ if (chainId === undefined) {
3219
+ return undefined;
3220
+ }
3221
+ const resolved = findNetworkConfig(this.networks, chainId);
3222
+ if (!resolved) {
3223
+ throw new Error(`Unsupported network ${chainId}`);
3224
+ }
3225
+ return resolved.chainId;
2869
3226
  }
2870
3227
 
2871
- //
2872
- // Sequence Signer methods
2873
- //
3228
+ /**
3229
+ * Resolves the chainId to use for the given request. If no chainId is provided,
3230
+ * it uses the chainId defined by the client (default chainId). This can be
3231
+ * overriden to build a single-network SequenceProvider.
3232
+ */
3233
+ async useChainId(chainId) {
3234
+ return this.toChainId(chainId) || this.client.getChainId();
3235
+ }
2874
3236
 
2875
- // getProvider returns a Web3Provider instance for the current chain. Note that this method
2876
- // and signer is bound to a particular chain to prevent misuse. If you'd like a provider
2877
- // for a specific chain, try getSender(chainId), or wallet.getProvider(chainId).
2878
- async getProvider(chainId) {
2879
- if (chainId) {
2880
- const currentChainId = await this.getChainId();
2881
- if (currentChainId !== chainId) {
2882
- throw new Error(`signer is attempting to access chain ${chainId}, but is already bound to chain ${currentChainId}`);
2883
- }
3237
+ /**
3238
+ * This generates a provider that ONLY works for the given chainId.
3239
+ * the generated provider can't switch networks, and can't handle requests
3240
+ * for other networks.
3241
+ */
3242
+
3243
+ getProvider(chainId) {
3244
+ // The provider without a chainId is... this one
3245
+ if (!chainId) {
3246
+ return this;
2884
3247
  }
2885
- return this.provider;
3248
+ const useChainId = this.toChainId(chainId);
3249
+ if (!this.singleNetworkProviders[useChainId]) {
3250
+ this.singleNetworkProviders[useChainId] = new SingleNetworkSequenceProvider(this.client, this.providerFor, useChainId);
3251
+ }
3252
+ return this.singleNetworkProviders[useChainId];
2886
3253
  }
2887
3254
 
2888
- // getSender returns a Web3Provider instance via the signer transport. Note: for our case
2889
- // the of sequence wallet, this will bring up the wallet window whenever using it, as the json-rpc
2890
- // requests are sent to the window transport. Therefore, for anything non-signing related
2891
- // you can write a higher-order JsonRpcRouter sender to route to the public provider endpoints
2892
- // as we do in the WalletProvider.
2893
- //
2894
- // This method is primarily utilized internally when routing requests to a particular chainId.
2895
- async getSender(chainId) {
2896
- if (!chainId || chainId && chainId === this.defaultChainId) {
2897
- return this.provider;
2898
- }
2899
- if (!this._providers[chainId]) {
2900
- this._providers[chainId] = new Web3Provider(new JsonRpcSender(this.provider, chainId), chainId);
3255
+ /**
3256
+ * This returns a subprovider, this is a regular non-sequence provider that
3257
+ * can be used to fulfill read only requests on a given network.
3258
+ */
3259
+ async _getSubprovider(chainId) {
3260
+ const useChainId = await this.useChainId(chainId);
3261
+
3262
+ // Whoever implements providerFrom should memoize the generated provider
3263
+ // otherwise every instance of SequenceProvider will create a new subprovider
3264
+ const provider = this.providerFor(useChainId);
3265
+ if (!provider) {
3266
+ throw new Error(`Unsupported network ${useChainId}`);
2901
3267
  }
2902
- return this._providers[chainId];
3268
+ return provider;
2903
3269
  }
2904
- getRelayer(chainId) {
2905
- // TODO: JsonRpcRelayer ......? or, Web3Relayer.. or SequenceRelayer?
2906
- // sequence_gasRefundOptions
2907
- // sequence_getNonce
2908
- // sequence_relay
2909
- throw new Error('TODO');
3270
+ async perform(method, params) {
3271
+ var _provider$prepareRequ;
3272
+ // First we check if the method should be handled by the client
3273
+ if (method === 'eth_chainId') {
3274
+ return this.useChainId();
3275
+ }
3276
+ if (method === 'eth_accounts') {
3277
+ return [this.client.getAddress()];
3278
+ }
3279
+ if (method === 'wallet_switchEthereumChain') {
3280
+ const args = params[0];
3281
+ const chainId = normalizeChainId(args);
3282
+ return this.setDefaultChainId(chainId);
3283
+ }
3284
+
3285
+ // Usually these methods aren't used by calling the provider
3286
+ // but to maximize compatibility we support them too.
3287
+ // The correct way of accessing these methods is by using .getSigner()
3288
+ if (method === 'eth_sendTransaction' || method === 'eth_sign' || method === 'eth_signTypedData' || method === 'eth_signTypedData_v4' || method === 'personal_sign' ||
3289
+ // These methods will use EIP-6492
3290
+ // but this is handled directly by the wallet
3291
+ method === 'sequence_sign' || method === 'sequence_signTypedData_v4') {
3292
+ // We pass the chainId to the client, if we don't pass one
3293
+ // the client will use its own default chainId
3294
+ return this.client.send({
3295
+ method,
3296
+ params
3297
+ }, this.getChainId());
3298
+ }
3299
+
3300
+ // Forward call to the corresponding provider
3301
+ // we use the provided chainId, or the default one provided by the client
3302
+ const provider = await this._getSubprovider();
3303
+ const prepared = (_provider$prepareRequ = provider.prepareRequest(method, params)) != null ? _provider$prepareRequ : [method, params];
3304
+ return provider.send(prepared[0], prepared[1]);
3305
+ }
3306
+ send(method, params) {
3307
+ return this.perform(method, params);
2910
3308
  }
2911
- async getWalletContext() {
2912
- if (!this._context) {
2913
- this._context = await this.provider.send('sequence_getWalletContext', []);
3309
+ request(request) {
3310
+ return this.perform(request.method, request.params);
3311
+ }
3312
+ async detectNetwork() {
3313
+ const chainId = this.client.getChainId();
3314
+ const network = findNetworkConfig(this.networks, chainId);
3315
+ if (!network) {
3316
+ throw new Error(`Unknown network ${chainId}`);
2914
3317
  }
2915
- return this._context;
3318
+ return network;
2916
3319
  }
2917
- async getWalletConfig(chainId) {
2918
- const reqChainId = maybeChainId(chainId) || this.defaultChainId;
2919
- if (!reqChainId) throw new Error('chainId is required');
2920
- return (await this.provider.send('sequence_getWalletConfig', [reqChainId], reqChainId))[0];
3320
+
3321
+ // Override most of the methods, so we add support for an optional chainId
3322
+ // argument, which is used to select the provider to use.
3323
+ //
3324
+ // NOTICE: We could use generics to avoid repeating the same code
3325
+ // but this would make the code harder to read, and it's not worth it
3326
+ // since we only have a few methods to override.
3327
+
3328
+ async waitForTransaction(transactionHash, confirmations, timeout, optionals) {
3329
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3330
+ return provider.waitForTransaction(transactionHash, confirmations, timeout);
2921
3331
  }
2922
- async getWalletState(chainId) {
2923
- const reqChainId = maybeChainId(chainId) || this.defaultChainId;
2924
- if (!reqChainId) throw new Error('chainId is required');
2925
- return (await this.provider.send('sequence_getWalletState', [reqChainId], reqChainId))[0].status;
3332
+ async getBlockNumber(optionals) {
3333
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3334
+ return provider.getBlockNumber();
2926
3335
  }
2927
- async getNetworks() {
2928
- if (!this._networks) this._networks = await this.provider.getNetworks();
2929
- return this._networks;
3336
+ async getGasPrice(optionals) {
3337
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3338
+ return provider.getGasPrice();
2930
3339
  }
2931
- async getSigners() {
2932
- const networks = await this.getNetworks();
2933
-
2934
- // TODO: Replace this with a method that aggregates signer addresses from all chains
2935
- const config = await this.getWalletConfig(networks[0].chainId);
2936
- if (!config) {
2937
- throw new Error(`walletConfig returned zero results for authChainId {authChainId}`);
2938
- }
2939
- return universal.genericCoderFor(config.version).config.signersOf(config).map(s => s.address);
3340
+ async getBalance(addressOrName, blockTag, optionals) {
3341
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3342
+ return provider.getBalance(addressOrName, blockTag);
3343
+ }
3344
+ async getTransactionCount(addressOrName, blockTag, optionals) {
3345
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3346
+ return provider.getTransactionCount(addressOrName, blockTag);
3347
+ }
3348
+ async getCode(addressOrName, blockTag, optionals) {
3349
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3350
+ return provider.getCode(addressOrName, blockTag);
3351
+ }
3352
+ async getStorageAt(addressOrName, position, blockTag, optionals) {
3353
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3354
+ return provider.getStorageAt(addressOrName, position, blockTag);
3355
+ }
3356
+ async call(transaction, blockTag, optionals) {
3357
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3358
+ return provider.call(transaction, blockTag);
3359
+ }
3360
+ async estimateGas(transaction, optionals) {
3361
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3362
+ return provider.estimateGas(transaction);
3363
+ }
3364
+ async getBlock(blockHashOrBlockTag, optionals) {
3365
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3366
+ return provider.getBlock(blockHashOrBlockTag);
3367
+ }
3368
+ async getTransaction(transactionHash, optionals) {
3369
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3370
+ return provider.getTransaction(transactionHash);
3371
+ }
3372
+ async getLogs(filter, optionals) {
3373
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3374
+ return provider.getLogs(filter);
2940
3375
  }
2941
3376
 
2942
- // signMessage matches implementation from ethers JsonRpcSigner for compatibility, but with
2943
- // multi-chain support.
2944
- async signMessage(message, chainId, sequenceVerified = true) {
2945
- const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId);
2946
- const data = typeof message === 'string' ? ethers.utils.toUtf8Bytes(message) : message;
2947
- const address = await this.getAddress();
3377
+ // ENS methods
2948
3378
 
2949
- // NOTE: as of ethers v5.5, it switched to using personal_sign, see
2950
- // https://github.com/ethers-io/ethers.js/pull/1542 and see
2951
- // https://github.com/WalletConnect/walletconnect-docs/issues/32 for additional info.
2952
- return provider.send(sequenceVerified ? 'sequence_sign' : 'personal_sign', [ethers.utils.hexlify(data), address]);
3379
+ async supportsENS() {
3380
+ const networks = await this.getNetworks();
3381
+ return networks.some(n => n.chainId === 1);
2953
3382
  }
3383
+ async getResolver(name) {
3384
+ if (!(await this.supportsENS())) {
3385
+ return null;
3386
+ }
2954
3387
 
2955
- // signTypedData matches implementation from ethers JsonRpcSigner for compatibility, but with
2956
- // multi-chain support.
2957
- async signTypedData(domain, types, message, chainId, sequenceVerified = true) {
2958
- // Populate any ENS names (in-place)
2959
- // const populated = await ethers.utils._TypedDataEncoder.resolveNames(domain, types, message, (name: string) => {
2960
- // return this.provider.resolveName(name)
2961
- // })
3388
+ // Resolver is always on the chainId 1
3389
+ const provider = await this._getSubprovider(1);
3390
+ return provider.getResolver(name);
3391
+ }
3392
+ async resolveName(name) {
3393
+ if (ethers.utils.isAddress(await name)) {
3394
+ return name;
3395
+ }
3396
+ if (!(await this.supportsENS())) {
3397
+ return null;
3398
+ }
2962
3399
 
2963
- return this.provider.send(sequenceVerified ? 'sequence_signTypedData_v4' : 'eth_signTypedData_v4', [await this.getAddress(), ethers.utils._TypedDataEncoder.getPayload(domain, types, message)], maybeChainId(chainId) || this.defaultChainId);
3400
+ // Resolver is always on the chainId 1
3401
+ const provider = await this._getSubprovider(1);
3402
+ return provider.resolveName(name);
2964
3403
  }
3404
+ async lookupAddress(address) {
3405
+ if (!(await this.supportsENS())) {
3406
+ return null;
3407
+ }
2965
3408
 
2966
- // sendTransaction matches implementation from ethers JsonRpcSigner for compatibility, but with
2967
- // multi-chain support.
2968
- async sendTransaction(transaction, chainId) {
2969
- const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId);
2970
- const tx = this.sendUncheckedTransaction(transaction, chainId).then(hash => {
2971
- return ethers.utils.poll(() => {
2972
- return provider.getTransaction(hash).then(tx => {
2973
- if (tx === null) {
2974
- return undefined;
2975
- }
2976
- return provider._wrapTransaction(tx, hash);
2977
- });
2978
- }, {
2979
- onceBlock: this.provider
2980
- }).catch(error => {
2981
- error.transactionHash = hash;
2982
- throw error;
2983
- });
2984
- });
3409
+ // Resolver is always on the chainId 1
3410
+ const provider = await this._getSubprovider(1);
3411
+ return provider.lookupAddress(address);
3412
+ }
3413
+ async getAvatar(nameOrAddress) {
3414
+ if (!(await this.supportsENS())) {
3415
+ return null;
3416
+ }
3417
+ const provider = await this._getSubprovider(1);
3418
+ return provider.getAvatar(nameOrAddress);
3419
+ }
3420
+ }
3421
+ SequenceProvider.is = provider => {
3422
+ return provider && typeof provider === 'object' && provider._isSequenceProvider === true;
3423
+ };
3424
+ function normalizeChainId(chainId) {
3425
+ if (typeof chainId === 'object') return normalizeChainId(chainId.chainId);
3426
+ return ethers.BigNumber.from(chainId).toNumber();
3427
+ }
2985
3428
 
2986
- // @ts-ignore
2987
- return tx;
3429
+ /**
3430
+ * This is the same provider, but it only allows a single network at a time.
3431
+ * the network defined by the constructor is the only one that can be used.
3432
+ *
3433
+ * Attempting to call any method with a different network will throw an error.
3434
+ * Attempting to change the network of this provider will throw an error.
3435
+ *
3436
+ * NOTICE: These networks won't support ENS unless they are the mainnet.
3437
+ */
3438
+ class SingleNetworkSequenceProvider extends SequenceProvider {
3439
+ constructor(client, providerFor, chainId) {
3440
+ super(client, providerFor);
3441
+ this._isSingleNetworkSequenceProvider = true;
3442
+ this.chainId = chainId;
3443
+ }
3444
+ _useChainId(chainId) {
3445
+ const provided = this.toChainId(chainId);
3446
+ if (provided && provided !== this.chainId) {
3447
+ throw new Error(`This provider only supports the network ${this.chainId}, but ${provided} was requested.`);
3448
+ }
3449
+ return provided || super.toChainId(this.chainId);
3450
+ }
3451
+ useChainId(chainId) {
3452
+ return Promise.resolve(this._useChainId(chainId));
3453
+ }
3454
+ getChainId() {
3455
+ return super.toChainId(this.chainId);
3456
+ }
3457
+ async getNetwork() {
3458
+ const networks = await this.client.getNetworks();
3459
+ const res = findNetworkConfig(networks, this.chainId);
3460
+ if (!res) {
3461
+ throw new Error(`Unsupported network ${this.chainId}`);
3462
+ }
3463
+ return res;
2988
3464
  }
2989
3465
 
2990
- // sendTransactionBatch is a convenience method to call sendTransaction in a batch format, allowing you to
2991
- // send multiple transaction as a single payload and just one on-chain transaction.
2992
- async sendTransactionBatch(transactions, chainId) {
2993
- const batch = await resolveArrayProperties(transactions);
2994
- if (!batch || batch.length === 0) {
2995
- throw new Error('cannot send empty batch');
3466
+ /**
3467
+ * Override getProvider and getSigner so they always use `useChainId`
3468
+ * this way they can't return providers and signers that can switch networks,
3469
+ * or that don't match the chainId of this signer.
3470
+ */
3471
+ getProvider(chainId) {
3472
+ if (this._useChainId(chainId) !== this.chainId) {
3473
+ throw new Error(`Unreachable code`);
2996
3474
  }
3475
+ return this;
3476
+ }
3477
+ getSigner(chainId) {
3478
+ return super.getSigner(this._useChainId(chainId));
3479
+ }
3480
+ setDefaultChainId(_chainId) {
3481
+ throw new Error(`This provider only supports the network ${this.chainId}; use the parent provider to switch networks.`);
3482
+ }
3483
+ static is(cand) {
3484
+ return cand && typeof cand === 'object' && cand._isSingleNetworkSequenceProvider === true;
3485
+ }
3486
+ }
2997
3487
 
2998
- // sendTransactionBatch only accepts TransactionRequest, not TransactionResponses
2999
- if (batch.find(v => v.wait !== undefined && v.wait !== null)) {
3000
- throw new Error('transaction request expected for sendTransactionBatch, transaction response found');
3488
+ /**
3489
+ * This session class is meant to persist the state of the wallet connection
3490
+ * whitin the dapp. This enables the client to retain the wallet address (and some more)
3491
+ * even if the user refreshes the page. Otherwise we would have to open the popup again.
3492
+ */
3493
+ class SequenceClientSession {
3494
+ constructor(store) {
3495
+ this.store = store;
3496
+ }
3497
+ connectedSession() {
3498
+ const session = this.getSession();
3499
+ if (session && session.accountAddress && session.walletContext && session.networks) {
3500
+ return {
3501
+ accountAddress: session.accountAddress,
3502
+ walletContext: session.walletContext,
3503
+ networks: session.networks
3504
+ };
3505
+ }
3506
+ throw new Error('Sequence session not connected');
3507
+ }
3508
+ hasSession() {
3509
+ var _this$getSession;
3510
+ return ((_this$getSession = this.getSession()) == null ? void 0 : _this$getSession.accountAddress) !== undefined;
3511
+ }
3512
+ setSession(session) {
3513
+ return this.store.setItem(SequenceClientSession.SESSION_LOCALSTORE_KEY, JSON.stringify(session));
3514
+ }
3515
+ getSession() {
3516
+ const session = this.store.getItem(SequenceClientSession.SESSION_LOCALSTORE_KEY);
3517
+ if (session) {
3518
+ return JSON.parse(session);
3001
3519
  }
3002
- const asExtended = toExtended(batch);
3003
- return this.sendTransaction(asExtended, chainId);
3520
+ return undefined;
3521
+ }
3522
+ async clearSession() {
3523
+ return this.store.removeItem(SequenceClientSession.SESSION_LOCALSTORE_KEY);
3524
+ }
3525
+ }
3526
+
3527
+ /**
3528
+ * The wallet webapp doesn't really care what's the "default chain" for the user.
3529
+ * so we don't even bother to send this information to the wallet. Instead, we
3530
+ * track it locally using storage, that way the data stays always in sync.
3531
+ */
3532
+ SequenceClientSession.SESSION_LOCALSTORE_KEY = '@sequence.session';
3533
+ class DefaultChainIdTracker {
3534
+ constructor(store, startingChainId = 1) {
3535
+ this.callbacks = [];
3536
+ this.store = store;
3537
+ this.startingChainId = startingChainId;
3538
+ store.onItemChange(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, value => {
3539
+ if (value) {
3540
+ const chainId = parseInt(value);
3541
+ this.callbacks.forEach(cb => cb(chainId));
3542
+ }
3543
+ });
3544
+ }
3545
+ onDefaultChainIdChanged(callback) {
3546
+ this.callbacks.push(callback);
3547
+ return () => {
3548
+ this.callbacks = this.callbacks.filter(cb => cb !== callback);
3549
+ };
3004
3550
  }
3005
- signTransactions(transaction, chainId) {
3006
- transaction = shallowCopy(transaction);
3007
- // TODO: transaction argument..? make sure to resolve any properties and serialize property before sending over
3008
- // the wire.. see sendUncheckedTransaction and resolveProperties
3009
- return this.provider.send('eth_signTransaction', [transaction], maybeChainId(chainId) || this.defaultChainId);
3551
+ setDefaultChainId(chainId) {
3552
+ if (chainId !== this.getDefaultChainId()) {
3553
+ this.store.setItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, chainId.toString());
3554
+ }
3010
3555
  }
3011
- sendSignedTransactions(signedTxs, chainId) {
3012
- // sequence_relay
3013
- throw new Error('TODO');
3556
+ getDefaultChainId() {
3557
+ const read = this.store.getItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY);
3558
+ if (!read || read.length === 0) {
3559
+ return this.startingChainId;
3560
+ }
3561
+ return parseInt(read);
3014
3562
  }
3563
+ }
3564
+
3565
+ /**
3566
+ * This is a wallet client for sequence wallet-webapp. It connects using *some* transport
3567
+ * and it allows to perform all sequence specific (or write) operations related to the wallet.
3568
+ *
3569
+ * It doesn't implement a full ethereum Provider, it doesn't include read-only methods.
3570
+ */
3571
+ DefaultChainIdTracker.SESSION_CHAIN_ID_KEY = '@sequence.session.defaultChainId';
3572
+ class SequenceClient {
3573
+ constructor(transport, store, defaultChainId) {
3574
+ this.callbacks = {};
3575
+ if (isMuxTransportTemplate(transport)) {
3576
+ this.transport = MuxMessageProvider.new(transport);
3577
+ } else if (isProviderTransport(transport)) {
3578
+ this.transport = transport;
3579
+ } else {
3580
+ throw new Error('Invalid transport');
3581
+ }
3582
+ this.session = new SequenceClientSession(store);
3583
+ this.defaultChainId = new DefaultChainIdTracker(store, defaultChainId);
3584
+ this.transport.on('accountsChanged', accounts => {
3585
+ var _this$callbacks$accou;
3586
+ if (accounts.length > 1) {
3587
+ console.warn('SequenceClient: wallet-webapp returned more than one account');
3588
+ }
3589
+ (_this$callbacks$accou = this.callbacks.accountsChanged) == null ? void 0 : _this$callbacks$accou.forEach(cb => cb(accounts));
3590
+ });
3591
+ this.transport.on('connect', response => {
3592
+ var _this$callbacks$conne;
3593
+ const chainIdHex = ethers.BigNumber.from(this.getChainId()).toHexString();
3594
+ (_this$callbacks$conne = this.callbacks.connect) == null ? void 0 : _this$callbacks$conne.forEach(cb => cb(_extends({}, response, {
3595
+ // Ignore the full connect response
3596
+ // use the chainId defined locally
3597
+ chainId: chainIdHex
3598
+ })));
3599
+ });
3600
+ this.transport.on('disconnect', error => {
3601
+ var _this$callbacks$disco;
3602
+ (_this$callbacks$disco = this.callbacks.disconnect) == null ? void 0 : _this$callbacks$disco.forEach(cb => cb(error));
3603
+ });
3604
+ this.transport.on('networks', networks => {
3605
+ var _this$callbacks$netwo;
3606
+ (_this$callbacks$netwo = this.callbacks.networks) == null ? void 0 : _this$callbacks$netwo.forEach(cb => cb(networks));
3607
+ });
3608
+ this.transport.on('walletContext', context => {
3609
+ var _this$callbacks$walle;
3610
+ (_this$callbacks$walle = this.callbacks.walletContext) == null ? void 0 : _this$callbacks$walle.forEach(cb => cb(context));
3611
+ });
3612
+ this.transport.on('open', info => {
3613
+ var _this$callbacks$open;
3614
+ (_this$callbacks$open = this.callbacks.open) == null ? void 0 : _this$callbacks$open.forEach(cb => cb(info));
3615
+ });
3616
+ this.transport.on('close', () => {
3617
+ var _this$callbacks$close;
3618
+ (_this$callbacks$close = this.callbacks.close) == null ? void 0 : _this$callbacks$close.forEach(cb => cb());
3619
+ });
3015
3620
 
3016
- // updateConfig..
3017
- // NOTE: this is not supported by the remote wallet by default.
3018
- async updateConfig(newConfig) {
3019
- // sequence_updateConfig
3020
- const [config, tx] = await this.provider.send('sequence_updateConfig', [newConfig], this.defaultChainId);
3021
- if (tx === null) {
3022
- return [config, undefined];
3023
- }
3024
- const provider = await this.getSender(this.defaultChainId);
3025
- return [config, provider._wrapTransaction(tx, tx.hash)];
3621
+ // We don't listen for the transport chainChanged event
3622
+ // instead we handle it locally, so we listen for changes in the store
3623
+ this.defaultChainId.onDefaultChainIdChanged(chainId => {
3624
+ var _this$callbacks$chain;
3625
+ const chainIdHex = ethers.BigNumber.from(chainId).toHexString();
3626
+ (_this$callbacks$chain = this.callbacks.chainChanged) == null ? void 0 : _this$callbacks$chain.forEach(cb => cb(chainIdHex));
3627
+ });
3026
3628
  }
3027
3629
 
3028
- // publishConfig..
3029
- // NOTE: this is not supported by the remote wallet by default.
3030
- async publishConfig() {
3031
- const provider = await this.getSender(this.defaultChainId);
3032
- const tx = await provider.send('sequence_publishConfig', []);
3033
- if (tx === null) {
3034
- return undefined;
3630
+ // Callbacks
3631
+
3632
+ registerCallback(eventName, callback) {
3633
+ if (!this.callbacks[eventName]) {
3634
+ this.callbacks[eventName] = [];
3035
3635
  }
3036
- return provider._wrapTransaction(tx, tx.hash);
3037
- }
3038
- async isDeployed(chainId) {
3039
- const provider = await this.getSender(maybeChainId(chainId));
3040
- const walletCode = await provider.getCode(await this.getAddress());
3041
- return !!walletCode && walletCode !== '0x';
3636
+ this.callbacks[eventName].push(callback);
3637
+ return () => {
3638
+ this.callbacks[eventName] = this.callbacks[eventName].filter(c => c !== callback);
3639
+ };
3042
3640
  }
3043
3641
 
3044
- //
3045
- // ethers JsonRpcSigner methods
3046
- //
3047
-
3048
- async _legacySignMessage(message, chainId) {
3049
- const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId);
3050
- const data = typeof message === 'string' ? ethers.utils.toUtf8Bytes(message) : message;
3051
- const address = await this.getAddress();
3642
+ // Individual callbacks lead to more idiomatic code
3052
3643
 
3053
- // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
3054
- // NOTE: ethers since 5.5 has switched to using personal_sign, we should review, etc.
3055
- return await provider.send('eth_sign', [address, ethers.utils.hexlify(data)]);
3644
+ onOpen(callback) {
3645
+ return this.registerCallback('open', callback);
3056
3646
  }
3057
- async _signTypedData(domain, types, message, chainId) {
3058
- return this.signTypedData(domain, types, message, chainId);
3647
+ onClose(callback) {
3648
+ return this.registerCallback('close', callback);
3059
3649
  }
3060
- async sendUncheckedTransaction(transaction, chainId) {
3061
- transaction = shallowCopy(transaction);
3062
- const fromAddress = this.getAddress();
3063
-
3064
- // NOTE: we do not use provider estimation, and instead rely on our relayer to determine the gasLimit and gasPrice
3065
- //
3066
- // TODO: alternatively/one day, we could write a provider middleware to eth_estimateGas
3067
- // and send it to our relayer url instead for estimation..
3068
- //
3069
- // if (!transaction.gasLimit) {
3070
- // const estimate = shallowCopy(transaction)
3071
- // estimate.from = fromAddress
3072
- // transaction.gasLimit = this.provider.estimateGas(estimate)
3073
- // }
3074
-
3075
- const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId);
3076
- return resolveProperties({
3077
- tx: resolveProperties(transaction),
3078
- sender: await fromAddress
3079
- }).then(({
3080
- tx,
3081
- sender
3082
- }) => {
3083
- if (tx.from != null) {
3084
- if (ethers.utils.getAddress(tx.from) !== sender) {
3085
- // logger.throwArgumentError("from address mismatch", "transaction", transaction)
3086
- throw new Error(`from address mismatch for transaction ${transaction}`);
3087
- }
3088
- } else {
3089
- tx.from = sender;
3090
- }
3091
- const hexTx = hexlifyTransaction(tx);
3092
- return provider.send('eth_sendTransaction', [hexTx]).then(hash => {
3093
- return hash;
3094
- }, error => {
3095
- // return checkError("sendTransaction", error, hexTx)
3096
- throw error;
3097
- });
3098
- });
3650
+ onConnect(callback) {
3651
+ return this.registerCallback('connect', callback);
3099
3652
  }
3100
- connectUnchecked() {
3101
- return new UncheckedJsonRpcSigner(this.provider, this.defaultChainId);
3653
+ onDisconnect(callback) {
3654
+ return this.registerCallback('disconnect', callback);
3102
3655
  }
3103
- async unlock(password) {
3104
- const address = await this.getAddress();
3105
- return this.provider.send('personal_unlockAccount', [address, password, null]);
3656
+ onNetworks(callback) {
3657
+ return this.registerCallback('networks', callback);
3106
3658
  }
3107
- }
3108
-
3109
- // NOTE: method has been copied + modified from ethers.js JsonRpcProvider
3110
- // Convert an ethers.js transaction into a JSON-RPC transaction
3111
-
3112
- const allowedTransactionKeys = {
3113
- chainId: true,
3114
- data: true,
3115
- gasLimit: true,
3116
- gasPrice: true,
3117
- nonce: true,
3118
- to: true,
3119
- value: true,
3120
- from: true,
3121
- auxiliary: true,
3122
- expiration: true,
3123
- afterNonce: true,
3124
- delegateCall: true,
3125
- revertOnError: true
3126
- };
3127
- const hexlifyTransaction = (transaction, allowExtra) => {
3128
- // Check only allowed properties are given
3129
- const allowed = shallowCopy(allowedTransactionKeys);
3130
- if (allowExtra) {
3131
- for (const key in allowExtra) {
3132
- if (allowExtra[key]) {
3133
- allowed[key] = true;
3134
- }
3135
- }
3659
+ onAccountsChanged(callback) {
3660
+ return this.registerCallback('accountsChanged', callback);
3136
3661
  }
3137
- ethers.utils.checkProperties(transaction, allowed);
3138
- const result = {}
3139
3662
 
3140
- // Some nodes (INFURA ropsten; INFURA mainnet is fine) do not like leading zeros.
3141
- ;
3142
- ['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(key => {
3143
- const value = transaction[key];
3144
- if (value === null || value === undefined) {
3145
- return;
3146
- }
3147
- const hexValue = ethers.utils.hexValue(value);
3148
- if (key === 'gasLimit') {
3149
- key = 'gas';
3150
- }
3151
- result[key] = hexValue;
3152
- });
3153
- ['from', 'to', 'data'].forEach(key => {
3154
- if (!transaction[key]) {
3155
- return;
3156
- }
3157
- result[key] = ethers.utils.hexlify(transaction[key]);
3158
- });
3159
- ['delegateCall', 'revertOnError'].forEach(key => {
3160
- const value = transaction[key];
3161
- if (value !== undefined && value !== null) {
3162
- result[key] = value;
3163
- }
3164
- });
3165
- const auxiliary = transaction['auxiliary'];
3166
- if (auxiliary && auxiliary.length > 0) {
3167
- result['auxiliary'] = [];
3168
- auxiliary.forEach(a => {
3169
- result['auxiliary'].push(hexlifyTransaction(a));
3170
- });
3663
+ // @deprecated
3664
+ onWalletContext(callback) {
3665
+ return this.registerCallback('walletContext', callback);
3171
3666
  }
3172
- return result;
3173
- };
3174
- class UncheckedJsonRpcSigner extends Web3Signer {
3175
- sendTransaction(transaction) {
3176
- return this.sendUncheckedTransaction(transaction).then(hash => {
3177
- return {
3178
- chainId: 0,
3179
- confirmations: 0,
3180
- data: '',
3181
- from: '',
3182
- gasLimit: ethers.constants.Zero,
3183
- gasPrice: ethers.constants.Zero,
3184
- hash,
3185
- nonce: 0,
3186
- value: ethers.constants.Zero,
3187
- wait: confirmations => this.provider.waitForTransaction(hash, confirmations)
3188
- };
3189
- });
3667
+ onDefaultChainIdChanged(callback) {
3668
+ return this.registerCallback('chainChanged', callback);
3190
3669
  }
3191
- }
3192
-
3193
- class WalletUtils {
3194
- constructor(walletProvider) {
3195
- this.wallet = walletProvider;
3670
+ getChainId() {
3671
+ return this.defaultChainId.getDefaultChainId();
3196
3672
  }
3197
-
3198
- // Sign message on a specified chain, or DefaultChain by default
3199
- signMessage(message, chainId, allSigners) {
3200
- const signer = this.wallet.getSigner();
3201
- if (!signer) throw new Error('unable to get signer');
3202
- return signer.signMessage(message, chainId, allSigners);
3673
+ setDefaultChainId(chainId) {
3674
+ return this.defaultChainId.setDefaultChainId(chainId);
3203
3675
  }
3204
3676
 
3205
- // Sign message on the AuthChain
3206
- async signAuthMessage(message, allSigners) {
3207
- const signer = this.wallet.getSigner();
3208
- if (!signer) throw new Error('unable to get AuthChain signer');
3209
- return signer.signMessage(message, await signer.getChainId(), allSigners);
3210
- }
3677
+ // Proxy transport methods
3211
3678
 
3212
- // Sign EIP-712 TypedData on a specified chain, or DefaultChain by default
3213
- signTypedData(domain, types, message, chainId, allSigners) {
3214
- const signer = this.wallet.getSigner();
3215
- if (!signer) throw new Error('unable to get signer');
3216
- return signer.signTypedData(domain, types, message, chainId, allSigners);
3679
+ async openWallet(path, intent) {
3680
+ this.transport.openWallet(path, intent, this.getChainId());
3681
+ await this.transport.waitUntilOpened();
3682
+ return this.isOpened();
3217
3683
  }
3218
-
3219
- // Sign EIP-712 TypedData on the AuthChain
3220
- async signAuthTypedData(domain, types, message, allSigners) {
3221
- const signer = this.wallet.getSigner();
3222
- if (!signer) throw new Error('unable to get AuthChain signer');
3223
- return signer.signTypedData(domain, types, message, await signer.getChainId(), allSigners);
3684
+ closeWallet() {
3685
+ return this.transport.closeWallet();
3224
3686
  }
3225
-
3226
- // Verify signature of a digest, one of a message, typedData or other
3227
- async isValidSignature(address, digest, signature, chainId) {
3228
- const provider = this.wallet.getProvider(chainId);
3229
- if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`);
3230
- return isValidSignature(address, digest, signature, provider);
3687
+ isOpened() {
3688
+ return this.transport.isOpened();
3231
3689
  }
3232
-
3233
- // Verify message signature
3234
- async isValidMessageSignature(address, message, signature, chainId) {
3235
- const provider = this.wallet.getProvider(chainId);
3236
- if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`);
3237
- const prefixed = prefixEIP191Message(message);
3238
- const digest = encodeMessageDigest(prefixed);
3239
- return isValidSignature(address, digest, signature, provider);
3690
+ isConnected() {
3691
+ return this.session.hasSession();
3240
3692
  }
3241
-
3242
- // Verify typedData signature
3243
- isValidTypedDataSignature(address, typedData, signature, chainId) {
3244
- return this.isValidSignature(address, encodeTypedDataDigest(typedData), signature, chainId);
3693
+ getSession() {
3694
+ return this.session.getSession();
3245
3695
  }
3246
3696
 
3247
- // // Recover the WalletConfig from a signature + digest combo
3248
- // recoverWalletConfig = async (
3249
- // address: string,
3250
- // digest: BytesLike,
3251
- // signature: string | DecodedSignature,
3252
- // chainId: BigNumberish,
3253
- // walletContext?: WalletContext
3254
- // ): Promise<WalletConfig> => {
3255
- // walletContext = walletContext || (await this.wallet.getWalletContext())
3256
- // return recoverWalletConfig(address, digest, signature, chainId, walletContext)
3257
- // }
3258
-
3259
- // // Recover the WalletConfig from a signature of a message
3260
- // recoverWalletConfigFromMessage = async (
3261
- // address: string,
3262
- // message: string | Uint8Array,
3263
- // signature: string | DecodedSignature,
3264
- // chainId: BigNumberish,
3265
- // walletContext?: WalletContext
3266
- // ): Promise<WalletConfig> => {
3267
- // walletContext = walletContext || (await this.wallet.getWalletContext())
3268
- // return recoverWalletConfig(address, encodeMessageDigest(prefixEIP191Message(message)), signature, chainId, walletContext)
3269
- // }
3270
-
3271
- // // Recover the WalletConfig from a signature of a typedData object
3272
- // recoverWalletConfigFromTypedData = async (
3273
- // address: string,
3274
- // typedData: TypedData,
3275
- // signature: string | DecodedSignature,
3276
- // chainId: BigNumberish,
3277
- // walletContext?: WalletContext
3278
- // ): Promise<WalletConfig> => {
3279
- // walletContext = walletContext || (await this.wallet.getWalletContext())
3280
- // return recoverWalletConfig(address, encodeTypedDataDigest(typedData), signature, chainId, walletContext)
3281
- // }
3282
-
3283
- // sendTransaction()
3284
- // sendTransactions()
3285
-
3286
- // sendETH()
3287
- // sendToken()
3288
- // sendCoin() -- sugar for sendToken()
3289
- // sendCollectible() -- sugar for sendToken()
3290
- // callContract()
3291
-
3292
- // transactionHistory()
3293
- // getReceipt()
3294
- // getLogs()
3295
- // // ..
3296
-
3297
- // isWalletDeployed()
3298
- // deployWallet()
3299
-
3300
- // validateSignature()
3301
- // recoverWalletConfig()
3302
- // recoverAddress()
3303
- }
3304
-
3305
- const SESSION_LOCALSTORE_KEY = '@sequence.session';
3306
- class Wallet {
3307
- constructor(_network, config) {
3308
- var _this = this;
3309
- this.init = () => {
3310
- var _this$config$transpor, _this$config$transpor2, _this$config$transpor3, _this$config$transpor4, _this$config$transpor5, _this$config$transpor6, _this$config$transpor7, _this$config$transpor8, _findSupportedNetwork;
3311
- if (this.transport.provider) {
3312
- // init must have already been called
3313
- return;
3314
- }
3315
-
3316
- // Setup provider
3317
-
3318
- this.transport.messageProvider = new MuxMessageProvider();
3319
-
3320
- // multiple message provider setup, first one to connect will be the main transport
3321
- if ((_this$config$transpor = this.config.transports) != null && (_this$config$transpor2 = _this$config$transpor.windowTransport) != null && _this$config$transpor2.enabled && typeof window === 'object') {
3322
- this.transport.windowMessageProvider = new WindowMessageProvider(this.config.walletAppURL);
3323
- this.transport.messageProvider.add(this.transport.windowMessageProvider);
3324
- }
3325
- if ((_this$config$transpor3 = this.config.transports) != null && (_this$config$transpor4 = _this$config$transpor3.proxyTransport) != null && _this$config$transpor4.enabled) {
3326
- this.transport.proxyMessageProvider = new ProxyMessageProvider(this.config.transports.proxyTransport.appPort);
3327
- this.transport.messageProvider.add(this.transport.proxyMessageProvider);
3328
- }
3329
- if ((_this$config$transpor5 = this.config.transports) != null && (_this$config$transpor6 = _this$config$transpor5.extensionTransport) != null && _this$config$transpor6.enabled) {
3330
- this.transport.extensionMessageProvider = new ExtensionMessageProvider(this.config.transports.extensionTransport.runtime);
3331
- // this.transport.extensionMessageProvider.register()
3332
- this.transport.messageProvider.add(this.transport.extensionMessageProvider);
3333
-
3334
- // NOTE/REVIEW: see note in mux-message-provider
3335
- //
3336
- // We don't add the extensionMessageProvider here because we don't send requests to it anyways, we seem to
3337
- // send all requests to the WindowMessageProvider anyways. By allowing it, if browser restarts, it will break
3338
- // the entire extension because messageProvider.provider will be undefined. So this is a hack to fix it.
3339
- //
3340
- // this.transport.messageProvider.add(this.transport.extensionMessageProvider)
3341
- }
3342
-
3343
- if ((_this$config$transpor7 = this.config.transports) != null && (_this$config$transpor8 = _this$config$transpor7.unrealTransport) != null && _this$config$transpor8.enabled) {
3344
- this.transport.unrealMessageProvider = new UnrealMessageProvider(this.config.walletAppURL);
3345
- this.transport.messageProvider.add(this.transport.unrealMessageProvider);
3346
- }
3347
- this.transport.messageProvider.register();
3348
-
3349
- // .....
3350
- this.transport.allowProvider = allowProviderMiddleware(request => {
3351
- if (request.method === 'sequence_setDefaultNetwork') return true;
3352
- const isConnected = this.isConnected();
3353
- if (!isConnected) {
3354
- throw new Error('Sequence: wallet not connected');
3355
- }
3356
- return isConnected;
3357
- });
3358
-
3359
- // ...
3360
- this.transport.networkProvider = networkProviderMiddleware(request => {
3361
- // return stub chainId of 0 when not connected to any
3362
- if (!this.networks || this.networks.length === 0) return 0;
3363
-
3364
- // return the default chainId as we're connected
3365
- return findNetworkConfig(this.networks, this.config.defaultNetworkId).chainId;
3366
- });
3367
-
3368
- // Provider proxy to support middleware stack of logging, caching and read-only rpc calls
3369
- this.transport.cachedProvider = new CachedProvider();
3370
- this.transport.cachedProvider.onUpdate(() => {
3371
- if (!this.session) this.session = {
3372
- providerCache: {}
3373
- };
3374
- this.session.providerCache = this.transport.cachedProvider.getCache();
3375
- this.saveSession(this.session);
3376
- });
3377
-
3378
- // ..
3379
- this.transport.router = new JsonRpcRouter([loggingProviderMiddleware, this.transport.networkProvider, this.transport.allowProvider, exceptionProviderMiddleware, this.transport.cachedProvider], this.transport.messageProvider);
3380
-
3381
- // TODO: Move finding this config to upper in the stack
3382
- this.transport.provider = new Web3Provider(this.transport.router, (_findSupportedNetwork = findSupportedNetwork(this.config.defaultNetworkId)) == null ? void 0 : _findSupportedNetwork.chainId);
3383
-
3384
- // NOTE: we don't listen on 'connect' even here as we handle it within connect() method
3385
- // in more synchronous flow.
3386
-
3387
- // below will update the wallet session object and persist it. In case the session
3388
- // is undefined, we consider the session to have been removed by the user, so we clear it.
3389
- this.transport.messageProvider.on('open', openInfo => {
3390
- const {
3391
- session
3392
- } = openInfo;
3393
- if (!session) {
3394
- if (this.session && this.session.accountAddress) {
3395
- // emit disconnect even if previously we had a session, and now we don't.
3396
- this.transport.messageProvider.emit('disconnect');
3397
- }
3398
- this.clearSession();
3399
- } else {
3400
- this.useSession(session, true);
3401
- }
3402
- });
3403
-
3404
- // below will update the account upon wallet connect/disconnect - aka, login/logout.
3405
- // if an origin is provided, this operation should be performed only on that origin
3406
- // and shouldn't affect the session of the wallet.
3407
- this.transport.messageProvider.on('accountsChanged', (accounts, origin) => {
3408
- if (origin) {
3409
- if (accounts.length > 0) {
3410
- this.useSession({
3411
- accountAddress: accounts[0]
3412
- }, true);
3413
- }
3414
- return;
3415
- }
3416
- if (!accounts || accounts.length === 0 || accounts[0] === '') {
3417
- this.clearSession();
3418
- } else {
3419
- this.useSession({
3420
- accountAddress: accounts[0]
3421
- }, true);
3697
+ // Basic API
3698
+ getAddress() {
3699
+ const session = this.session.connectedSession();
3700
+ return session.accountAddress;
3701
+ }
3702
+ async connect(options) {
3703
+ if ((options == null ? void 0 : options.authorizeVersion) === undefined) {
3704
+ // Populate default authorize version if not provided
3705
+ options.authorizeVersion = 2;
3706
+ }
3707
+ if ((options == null ? void 0 : options.refresh) === true) {
3708
+ this.disconnect();
3709
+ }
3710
+ if (options) {
3711
+ if (options.authorize) {
3712
+ if (!options.app) {
3713
+ throw new Error(`connecting with 'authorize' option also requires 'app' to be set`);
3422
3714
  }
3423
- });
3424
-
3425
- // below will update the networks automatically when the wallet networks change
3426
- this.transport.messageProvider.on('networks', networks => {
3427
- this.useSession({
3428
- networks: networks
3429
- }, true);
3430
- });
3431
-
3432
- // below will update the wallet context automatically
3433
- this.transport.messageProvider.on('walletContext', walletContext => {
3434
- this.useSession({
3435
- walletContext: walletContext
3436
- }, true);
3437
- });
3438
- };
3439
- this.loadSession = async function (preferredNetwork) {
3440
- const data = await LocalStorage.getInstance().getItem(SESSION_LOCALSTORE_KEY);
3441
- if (!data || data === '') {
3442
- return undefined;
3443
- }
3444
- try {
3445
- const session = JSON.parse(data);
3446
- if (session) {
3447
- // Setting preferredNetwork as default network if it's in session.networks
3448
- if (preferredNetwork !== undefined) {
3449
- var _session$networks;
3450
- const preferredNetworkIdNum = typeof preferredNetwork === 'string' ? parseInt(preferredNetwork) : preferredNetwork;
3451
- const isPreferredNetwork = n => n.name === preferredNetwork || n.chainId === preferredNetworkIdNum;
3452
- const preferredNetworkInConfig = (_session$networks = session.networks) == null ? void 0 : _session$networks.find(isPreferredNetwork);
3453
- const isAlreadyDefaultChain = preferredNetworkInConfig == null ? void 0 : preferredNetworkInConfig.isDefaultChain;
3454
- if (session.networks && preferredNetworkInConfig && !isAlreadyDefaultChain) {
3455
- const updatedNetworks = session.networks.map(n => _extends({}, n, {
3456
- isDefaultChain: isPreferredNetwork(n)
3457
- }));
3458
- session.networks = sortNetworks(updatedNetworks);
3459
- session.providerCache = undefined;
3460
- }
3461
- }
3462
- _this.useSession(session, true);
3715
+ if (options.authorizeVersion === undefined) {
3716
+ options.authorizeVersion = 2;
3463
3717
  }
3464
- return session;
3465
- } catch (err) {
3466
- logger.warn('loadSession failed, unable to parse session payload from storage.');
3467
- return undefined;
3468
3718
  }
3469
- };
3470
- this.connect = async function (options) {
3471
- if (options && (options == null ? void 0 : options.authorizeVersion) === undefined) {
3472
- // Populate default authorize version if not provided
3473
- options.authorizeVersion = 2;
3474
- }
3475
- if ((options == null ? void 0 : options.refresh) === true) {
3476
- _this.disconnect();
3477
- }
3478
- if (_this.isConnected() && (await _this.isSiteConnected(options == null ? void 0 : options.origin)) && !!_this.session && !(options != null && options.authorize) && !(options != null && options.askForEmail)) {
3719
+ }
3720
+ await this.openWallet(undefined, {
3721
+ type: 'connect',
3722
+ options: _extends({}, options, {
3723
+ networkId: this.getChainId()
3724
+ })
3725
+ });
3726
+ const connectDetails = await this.transport.waitUntilConnected().catch(error => {
3727
+ if (error instanceof Error) {
3479
3728
  return {
3480
- connected: true,
3481
- session: _this.session,
3482
- chainId: ethers.utils.hexlify(await _this.getChainId())
3729
+ connected: false,
3730
+ error: error.message
3483
3731
  };
3484
- }
3485
- if (options) {
3486
- if (options.authorize) {
3487
- if (!options.app) {
3488
- throw new Error(`connecting with 'authorize' option also requires 'app' to be set`);
3489
- }
3490
- if (options.authorizeVersion === undefined) {
3491
- options.authorizeVersion = 2;
3492
- }
3493
- }
3494
- }
3495
- await _this.openWallet(undefined, {
3496
- type: 'connect',
3497
- options
3498
- });
3499
- const connectDetails = await _this.transport.messageProvider.waitUntilConnected().catch(error => {
3500
- if (error instanceof Error) {
3501
- return {
3502
- connected: false,
3503
- error: error.message
3504
- };
3505
- } else {
3506
- return {
3507
- connected: false,
3508
- error: JSON.stringify(error)
3509
- };
3510
- }
3511
- });
3512
- if (connectDetails.connected) {
3513
- if (!!connectDetails.session) {
3514
- _this.useSession(connectDetails.session, true);
3515
- _this.addConnectedSite(options == null ? void 0 : options.origin);
3516
- } else {
3517
- throw new Error('impossible state, connect response is missing session');
3518
- }
3519
- }
3520
- return connectDetails;
3521
- };
3522
- this.authorize = async function (options) {
3523
- return _this.connect(_extends({}, options, {
3524
- authorize: true
3525
- }));
3526
- };
3527
- this.getSession = () => {
3528
- if (!this.isConnected()) {
3529
- return undefined;
3530
- }
3531
- return this.session;
3532
- };
3533
- this.getAddress = async function () {
3534
- if (!_this.isConnected()) {
3535
- throw new Error('connect first');
3536
- }
3537
- const session = _this.getSession();
3538
- return session.accountAddress;
3539
- };
3540
- this.getNetworks = async function (chainId) {
3541
- if (!_this.isConnected() || !_this.networks) {
3542
- throw new Error('connect first');
3543
- }
3544
- if (chainId) {
3545
- // filter list to just the specific chain requested
3546
- const network = findNetworkConfig(_this.networks, chainId);
3547
- return network ? [network] : [];
3548
- }
3549
- return _this.networks;
3550
- };
3551
- // getChainId returns the default chain id
3552
- this.getChainId = async function () {
3553
- if (!_this.networks || _this.networks.length < 1) {
3554
- throw new Error('networks have not been set by session. connect first.');
3555
- }
3556
- const network = findNetworkConfig(_this.networks, _this.config.defaultNetworkId);
3557
- if (!network) {
3558
- throw new Error('networks must have a default chain specified');
3559
- }
3560
- return network.chainId;
3561
- };
3562
- this.openWallet = async function (path, intent, networkId) {
3563
- if ((intent == null ? void 0 : intent.type) !== 'connect' && !_this.isConnected()) {
3564
- throw new Error('connect first');
3565
- }
3566
- let currentNetworkId;
3567
- if (!_this.networks || _this.networks.length < 1) {
3568
- currentNetworkId = _this.config.defaultNetworkId;
3569
- } else {
3570
- currentNetworkId = await _this.getChainId();
3571
- }
3572
- _this.transport.messageProvider.openWallet(path, intent, networkId || currentNetworkId);
3573
- await _this.transport.messageProvider.waitUntilOpened();
3574
- return true;
3575
- };
3576
- this.closeWallet = () => {
3577
- this.transport.messageProvider.closeWallet();
3578
- };
3579
- this.unregister = () => {
3580
- var _this$transport$messa;
3581
- this.disconnect();
3582
- (_this$transport$messa = this.transport.messageProvider) == null ? void 0 : _this$transport$messa.unregister();
3583
- };
3584
- this.saveSession = async function (session) {
3585
- logger.debug('wallet provider: saving session');
3586
- const data = JSON.stringify(session);
3587
- await LocalStorage.getInstance().setItem(SESSION_LOCALSTORE_KEY, data);
3588
- };
3589
- this.useSession = async function (session, autoSave = true) {
3590
- if (!_this.session) _this.session = {};
3591
-
3592
- // setup wallet context
3593
- if (_this.config.walletContext) {
3594
- _this.session.walletContext = _this.config.walletContext;
3595
- } else if (session.walletContext) {
3596
- _this.session.walletContext = session.walletContext;
3597
- }
3598
-
3599
- // setup account
3600
- if (session.accountAddress) {
3601
- _this.useAccountAddress(session.accountAddress);
3602
- }
3603
-
3604
- // setup networks
3605
- if (session.networks) {
3606
- _this.useNetworks(session.networks);
3607
- }
3608
-
3609
- // setup provider cache
3610
- if (session.providerCache) {
3611
- _this.transport.cachedProvider.setCache(session.providerCache);
3612
3732
  } else {
3613
- _this.transport.cachedProvider.clearCache();
3733
+ return {
3734
+ connected: false,
3735
+ error: JSON.stringify(error)
3736
+ };
3614
3737
  }
3738
+ });
3615
3739
 
3616
- // persist
3617
- if (autoSave) {
3618
- _this.saveSession(_this.session);
3619
- }
3620
- };
3621
- // config is a Partial, so that we may intersect it with the DefaultProviderConfig,
3622
- // which allows easy overriding and control of the config.
3623
- this.config = _extends({}, DefaultProviderConfig);
3624
- if (config) {
3625
- this.config = _extends({}, this.config, config);
3626
- }
3627
- if (_network) {
3628
- this.config.defaultNetworkId = _network;
3629
- } else if (!this.config.defaultNetworkId) {
3630
- this.config.defaultNetworkId = 'mainnet';
3631
- }
3632
- if (config != null && config.localStorage) {
3633
- LocalStorage.use(config.localStorage);
3634
- }
3635
- this.transport = {};
3636
- this.networks = [];
3637
- this.providers = {};
3638
- this.connectedSites = new LocalStore('@sequence.connectedSites', []);
3639
- this.utils = new WalletUtils(this);
3640
- this.init();
3641
- }
3642
- async addConnectedSite(origin) {
3643
- origin = origin || window.location.origin;
3644
- const connectedSites = await this.connectedSites.get();
3645
- if (connectedSites) {
3646
- if (connectedSites.includes(origin)) {
3647
- return;
3648
- }
3649
- this.connectedSites.set([...connectedSites, origin]);
3650
- } else {
3651
- this.connectedSites.set([origin]);
3740
+ // Normalize chainId into a decimal string
3741
+ // TODO: Remove this once wallet-webapp returns chainId as a string
3742
+ if (connectDetails.chainId) {
3743
+ connectDetails.chainId = ethers.BigNumber.from(connectDetails.chainId).toString();
3652
3744
  }
3653
- }
3654
- async removeConnectedSite(origin) {
3655
- const authorized = await this.connectedSites.get();
3656
- if (authorized) {
3657
- this.connectedSites.set(authorized.filter(domain => domain !== origin));
3745
+ if (connectDetails.connected) {
3746
+ if (!connectDetails.session) {
3747
+ throw new Error('impossible state, connect response is missing session');
3748
+ }
3749
+ this.session.setSession(connectDetails.session);
3658
3750
  }
3659
- }
3660
- getConnectedSites() {
3661
- return this.connectedSites.get();
3662
- }
3663
- async isSiteConnected(origin) {
3664
- const authorized = await this.connectedSites.get();
3665
- return !!authorized && authorized.includes(origin || window.location.origin);
3751
+ return connectDetails;
3666
3752
  }
3667
3753
  disconnect() {
3668
3754
  if (this.isOpened()) {
3669
3755
  this.closeWallet();
3670
3756
  }
3671
- return this.clearSession();
3757
+ return this.session.clearSession();
3672
3758
  }
3673
3759
 
3674
- // TODO: add switchNetwork(network: string | number) which will call wallet_switchEthereumChain
3675
- // and on successful response, will update the provider info here, etc.
3760
+ // Higher level API
3676
3761
 
3677
- getProviderConfig() {
3678
- return this.config;
3762
+ // Working with sendAsync is less idiomatic
3763
+ // but transport uses it instead of send, so we wrap it
3764
+ send(request, chainId) {
3765
+ return new Promise((resolve, reject) => {
3766
+ this.transport.sendAsync(request, (error, response) => {
3767
+ if (error) {
3768
+ reject(error);
3769
+ } else if (response === undefined) {
3770
+ reject(new Error(`Got undefined response for request: ${request}`));
3771
+ } else if (typeof response === 'object' && response.error) {
3772
+ reject(response.error);
3773
+ } else if (typeof response === 'object' && response.result) {
3774
+ resolve(response.result);
3775
+ } else {
3776
+ reject(new Error(`Got invalid response for request: ${request}`));
3777
+ }
3778
+ }, chainId || this.getChainId());
3779
+ });
3679
3780
  }
3680
- isOpened() {
3681
- return this.transport.messageProvider.isOpened();
3781
+ async getNetworks(pull) {
3782
+ const connectedSession = this.session.connectedSession();
3783
+ if (pull) {
3784
+ connectedSession.networks = await this.send({
3785
+ method: 'sequence_getNetworks'
3786
+ });
3787
+ this.session.setSession(connectedSession);
3788
+ }
3789
+ return connectedSession.networks;
3682
3790
  }
3683
- isConnected() {
3684
- return this.session !== undefined && this.session.networks !== undefined && this.session.networks.length > 0 && this.networks !== undefined && this.networks.length > 0 && !!this.session.accountAddress && this.session.accountAddress.startsWith('0x');
3791
+ async signMessage(message, options) {
3792
+ const method = options != null && options.eip6492 ? 'sequence_sign' : 'personal_sign';
3793
+
3794
+ // Address is ignored by the wallet webapp
3795
+ return this.send({
3796
+ method,
3797
+ params: [message, this.getAddress()]
3798
+ }, options == null ? void 0 : options.chainId);
3685
3799
  }
3686
- getProvider(chainId) {
3687
- // return the top-level provider message transport when chainId is unspecified
3688
- // and user has not logged in
3689
- if (!this.isConnected()) {
3690
- if (chainId) {
3691
- throw new Error(`session is empty. connect and try again.`);
3692
- } else {
3693
- return this.transport.provider;
3694
- }
3695
- }
3696
- let network = findNetworkConfig(this.networks, this.config.defaultNetworkId);
3697
- if (chainId) {
3698
- network = findNetworkConfig(this.networks, chainId);
3699
- if (!network) {
3700
- throw new Error(`network ${chainId} is not in the network list`);
3701
- }
3702
- }
3800
+ async signTypedData(typedData, options) {
3801
+ const method = options != null && options.eip6492 ? 'sequence_signTypedData_v4' : 'eth_signTypedData_v4';
3703
3802
 
3704
- // return memoized network provider
3705
- if (this.providers[network.chainId]) {
3706
- return this.providers[network.chainId];
3707
- }
3803
+ // TODO: Stop using ethers for this, this is the only place where we use it
3804
+ // and it makes the client depend on ethers.
3805
+ const encoded = ethers.utils._TypedDataEncoder.getPayload(typedData.domain, typedData.types, typedData.message);
3708
3806
 
3709
- // builder web3 provider stack
3710
- let provider;
3807
+ // The sign typed data will use one of the following chainIds, in order:
3808
+ // - The one provided in the options
3809
+ // - The one provided in the typedData.domain.chainId
3810
+ // - The default chainId
3711
3811
 
3712
- // network.provider may be set by the ProviderConfig override
3713
- const rpcProvider = new providers.JsonRpcProvider(getDefaultConnectionInfo(network.rpcUrl), network.chainId);
3714
- if (network.isDefaultChain) {
3715
- // communicating with defaultChain will prioritize the wallet message transport
3716
- const router = new JsonRpcRouter([loggingProviderMiddleware, exceptionProviderMiddleware, new EagerProvider({
3717
- accountAddress: this.session.accountAddress,
3718
- walletContext: this.session.walletContext
3719
- }), new SigningProvider(this.transport.provider), this.transport.cachedProvider], new JsonRpcSender(rpcProvider));
3720
- provider = new Web3Provider(router, network.chainId);
3721
- } else {
3722
- // communicating with another chain will bind to that network, but will forward
3723
- // any signing-related requests to the wallet message transport
3724
- const router = new JsonRpcRouter([loggingProviderMiddleware, exceptionProviderMiddleware, new EagerProvider({
3725
- accountAddress: this.session.accountAddress,
3726
- walletContext: this.session.walletContext,
3727
- chainId: network.chainId
3728
- }), new SigningProvider(this.transport.provider), new CachedProvider({
3729
- defaultChainId: network.chainId
3730
- })], new JsonRpcSender(rpcProvider));
3731
- provider = new Web3Provider(router, network.chainId);
3732
- }
3733
- this.providers[network.chainId] = provider;
3734
- return provider;
3735
- }
3736
- getAllProviders() {
3737
- return this.providers;
3738
- }
3739
- getSigner(chainId) {
3740
- return this.getProvider(chainId).getSigner();
3741
- }
3742
- getWalletConfig(chainId) {
3743
- return this.getSigner().getWalletConfig(chainId);
3744
- }
3745
- getWalletState(chainId) {
3746
- return this.getSigner().getWalletState(chainId);
3747
- }
3748
- getWalletContext() {
3749
- return this.getSigner().getWalletContext();
3750
- }
3751
- isDeployed(chainId) {
3752
- return this.getSigner(chainId).isDeployed();
3812
+ return this.send({
3813
+ method,
3814
+ params: [this.getAddress(), encoded]
3815
+ }, (options == null ? void 0 : options.chainId) || typedData.domain.chainId && ethers.BigNumber.from(typedData.domain.chainId).toNumber() || this.getChainId());
3753
3816
  }
3754
- on(event, fn) {
3755
- this.transport.messageProvider.on(event, fn);
3817
+ async sendTransaction(tx, options) {
3818
+ const sequenceTxs = Array.isArray(tx) ? tx : [tx];
3819
+ const extendedTxs = toExtended(sequenceTxs);
3820
+ return this.send({
3821
+ method: 'eth_sendTransaction',
3822
+ params: [extendedTxs]
3823
+ }, options == null ? void 0 : options.chainId);
3756
3824
  }
3757
- once(event, fn) {
3758
- this.transport.messageProvider.once(event, fn);
3825
+ async getWalletContext() {
3826
+ return this.send({
3827
+ method: 'sequence_getWalletContext'
3828
+ });
3759
3829
  }
3760
- useAccountAddress(accountAddress) {
3761
- if (!this.session) this.session = {};
3762
- this.session.accountAddress = ethers.utils.getAddress(accountAddress);
3830
+ async getOnchainWalletConfig(options) {
3831
+ // NOTICE: sequence_getWalletConfig sends the chainId as a param
3832
+ const res = await this.send({
3833
+ method: 'sequence_getWalletConfig',
3834
+ params: [(options == null ? void 0 : options.chainId) || this.getChainId()]
3835
+ }, options == null ? void 0 : options.chainId);
3836
+ return Array.isArray(res) ? res[0] : res;
3763
3837
  }
3764
- useNetworks(networks) {
3765
- // set networks in the session
3766
- if (!this.session) this.session = {};
3767
-
3768
- // set networks on session object
3769
- this.session.networks = networks;
3770
-
3771
- // short-circuit if setting empty network list (aka logged out state)
3772
- if (!this.session.networks || this.session.networks.length === 0) {
3773
- return;
3774
- }
3775
-
3776
- // check if any custom network settings, otherwise return early
3777
- if (!this.config.networks && !this.config.networkRpcUrl) {
3778
- this.networks = networks;
3779
- return;
3780
- }
3781
-
3782
- // init networks
3783
- this.networks = networks;
3784
-
3785
- // combine custom network config with networks in the session
3786
- if (this.config.networks) {
3787
- this.networks = networks.map(n => _extends({}, n)); // copy
3788
- this.config.networks.forEach(n => {
3789
- const network = findNetworkConfig(this.networks, n.chainId || n.name);
3790
- if (!network) return;
3791
- updateNetworkConfig(n, network);
3792
- });
3793
- ensureValidNetworks(this.networks, true);
3794
- }
3795
3838
 
3796
- // an extra override for convenience
3797
- if (this.config.networkRpcUrl) {
3798
- const network = this.networks.find(network => network.isDefaultChain);
3799
- if (network) {
3800
- network.rpcUrl = this.config.networkRpcUrl;
3801
- }
3802
- }
3803
- }
3804
- async clearSession() {
3805
- var _this$transport$cache;
3806
- logger.debug('wallet provider: clearing session');
3807
- await LocalStorage.getInstance().removeItem(SESSION_LOCALSTORE_KEY);
3808
- this.session = undefined;
3809
- this.networks = [];
3810
- this.providers = {};
3811
- (_this$transport$cache = this.transport.cachedProvider) == null ? void 0 : _this$transport$cache.clearCache();
3812
- }
3839
+ // NOTICE: We are leaving out all the "regular" methods os a tipical
3840
+ // JSON RPC Provider (eth_getBlockByNumber, eth_call, etc)
3841
+ // wallet-webapp does implement them, but this client is meant to be
3842
+ // exclusively used for Sequence specific methods
3813
3843
  }
3814
- const DefaultProviderConfig = {
3815
- walletAppURL: 'https://sequence.app',
3816
- // walletSessionURL: 'https://session.sequence.app',
3817
3844
 
3845
+ const DefaultProviderConfig = {
3818
3846
  transports: {
3847
+ walletAppURL: 'https://sequence.app',
3819
3848
  windowTransport: {
3820
3849
  enabled: true
3821
3850
  },
3822
3851
  proxyTransport: {
3823
3852
  enabled: false
3824
3853
  }
3825
- }
3854
+ },
3855
+ defaultNetwork: 1
3826
3856
  };
3827
- let walletInstance;
3828
- const initWallet = async (network, config) => {
3829
- if (walletInstance) {
3830
- return walletInstance;
3831
- }
3832
- walletInstance = new Wallet(network, config);
3833
- await walletInstance.loadSession(network);
3834
- return walletInstance;
3857
+ let sequenceWalletProvider;
3858
+ const initWallet = partialConfig => {
3859
+ var _config$networks$filt, _config$networks, _findNetworkConfig;
3860
+ if (sequenceWalletProvider) {
3861
+ return sequenceWalletProvider;
3862
+ }
3863
+
3864
+ // Combine both the provided config and the default config
3865
+ const config = _extends({}, DefaultProviderConfig, partialConfig, {
3866
+ transports: _extends({}, DefaultProviderConfig.transports, partialConfig == null ? void 0 : partialConfig.transports)
3867
+ });
3868
+ const rpcProviders = {};
3869
+
3870
+ // Find any new networks that aren't already defined in sequence.js
3871
+ // and add them to the list of networks, (they must have a rpcUrl and chainId)
3872
+ const newNetworks = (_config$networks$filt = (_config$networks = config.networks) == null ? void 0 : _config$networks.filter(n => {
3873
+ n.rpcUrl !== undefined && n.chainId !== undefined && !allNetworks.find(an => an.chainId === n.chainId);
3874
+ })) != null ? _config$networks$filt : [];
3875
+
3876
+ // Override any information about the networks using the config
3877
+ const combinedNetworks = allNetworks.map(n => {
3878
+ var _config$networks2;
3879
+ const network = (_config$networks2 = config.networks) == null ? void 0 : _config$networks2.find(cn => cn.chainId === n.chainId);
3880
+ return network ? _extends({}, n, network) : n;
3881
+ }).concat(newNetworks);
3882
+
3883
+ // This build a "public rpc" on demand, we build them on demand because we don't want to
3884
+ // generate a bunch of providers for networks that aren't used.
3885
+ const providerForChainId = chainId => {
3886
+ if (!rpcProviders[chainId]) {
3887
+ var _combinedNetworks$fin;
3888
+ const rpcUrl = (_combinedNetworks$fin = combinedNetworks.find(n => n.chainId === chainId)) == null ? void 0 : _combinedNetworks$fin.rpcUrl;
3889
+ if (!rpcUrl) {
3890
+ throw new Error(`no rpcUrl found for chainId: ${chainId}`);
3891
+ }
3892
+ const baseProvider = new ethers.providers.JsonRpcProvider(rpcUrl);
3893
+ const router = new JsonRpcRouter([loggingProviderMiddleware, exceptionProviderMiddleware, new CachedProvider()], new JsonRpcSender(baseProvider));
3894
+ rpcProviders[chainId] = new ethers.providers.Web3Provider(router, chainId);
3895
+ }
3896
+ return rpcProviders[chainId];
3897
+ };
3898
+
3899
+ // This is the starting default network (as defined by the config)
3900
+ // it can be later be changed using `wallet_switchEthereumChain` or some
3901
+ // of the other methods on the provider.
3902
+ const defaultNetwork = config.defaultNetwork ? (_findNetworkConfig = findNetworkConfig(combinedNetworks, config.defaultNetwork)) == null ? void 0 : _findNetworkConfig.chainId : undefined;
3903
+ if (!defaultNetwork && config.defaultNetwork) {
3904
+ throw new Error(`defaultNetwork not found for chainId: ${config.defaultNetwork}`);
3905
+ }
3906
+
3907
+ // Generate ItemStore
3908
+ const itemStore = config.localStorage || useBestStore();
3909
+
3910
+ // Create client, provider and return signer
3911
+ const client = new SequenceClient(config.transports, itemStore, defaultNetwork);
3912
+ sequenceWalletProvider = new SequenceProvider(client, providerForChainId);
3913
+ return sequenceWalletProvider;
3835
3914
  };
3836
3915
  const unregisterWallet = () => {
3837
- if (!walletInstance) return;
3838
- walletInstance.closeWallet();
3839
- walletInstance.unregister();
3916
+ if (!sequenceWalletProvider) return;
3917
+ sequenceWalletProvider.client.closeWallet();
3918
+ sequenceWalletProvider.client.transport.unregister();
3919
+ sequenceWalletProvider = undefined;
3840
3920
  };
3841
3921
  const getWallet = () => {
3842
- if (!walletInstance) {
3843
- throw new Error('Wallet has not been initialized, call sequence.initWallet(network, config) first.');
3922
+ if (!sequenceWalletProvider) {
3923
+ throw new Error('Wallet has not been initialized, call sequence.initWallet(config) first.');
3844
3924
  }
3845
- return walletInstance;
3925
+ return sequenceWalletProvider;
3846
3926
  };
3847
3927
 
3848
- export { BaseInjectedTransport, BaseProviderTransport, BaseWalletTransport, CHANNEL_ID, DefaultProviderConfig, ErrSignedInRequired, EventType, ExtensionMessageHandler, ExtensionMessageProvider, InitState, LocalStorage, LocalStore, LocalWeb3Provider, MuxMessageProvider, OpenState, PROVIDER_OPEN_TIMEOUT, ProviderError, ProxyMessageChannel, ProxyMessageChannelPort, ProxyMessageHandler, ProxyMessageProvider, SESSION_LOCALSTORE_KEY, UnrealMessageHandler, UnrealMessageProvider, Wallet, WalletRequestHandler, Web3Provider, Web3Signer, WindowMessageHandler, WindowMessageProvider, WindowSessionParams, getWallet, initWallet, isBrowserExtension, isSequenceProvider, isUnityPlugin, isValidMessageSignature, isValidSignature, isValidTypedDataSignature, isWalletUpToDate, messageToBytes, nextMessageIdx, prefixEIP191Message, trimEIP191Prefix, unregisterWallet };
3928
+ export { 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, trimEIP191Prefix, unregisterWallet, useBestStore };