@0xsequence/provider 1.0.4 → 1.1.0

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