edge-core-js 2.2.0 → 2.3.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.
package/lib/node/index.js CHANGED
@@ -20,7 +20,7 @@ var elliptic = require('elliptic');
20
20
  var yavent = require('yavent');
21
21
  var reduxKeto = require('redux-keto');
22
22
  var crypto = require('crypto');
23
- var fetch = require('node-fetch');
23
+ var fetch$1 = require('node-fetch');
24
24
 
25
25
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
26
26
 
@@ -30,7 +30,7 @@ var aesjs__default = /*#__PURE__*/_interopDefaultLegacy(aesjs);
30
30
  var hashjs__default = /*#__PURE__*/_interopDefaultLegacy(hashjs);
31
31
  var elliptic__default = /*#__PURE__*/_interopDefaultLegacy(elliptic);
32
32
  var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
33
- var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
33
+ var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch$1);
34
34
 
35
35
  function scrypt$2(data, salt, n, r, p, dklen) {
36
36
  return new Promise((resolve, reject) => {
@@ -1843,7 +1843,7 @@ async function waitForPlugins(ai) {
1843
1843
  swap
1844
1844
  } = props.state.plugins;
1845
1845
  const missingPlugins = [];
1846
- for (const pluginId in init) {
1846
+ for (const pluginId of Object.keys(init)) {
1847
1847
  const shouldLoad = init[pluginId] !== false && init[pluginId] != null;
1848
1848
  if (shouldLoad && currency[pluginId] == null && swap[pluginId] == null) {
1849
1849
  missingPlugins.push(pluginId);
@@ -3258,6 +3258,13 @@ const clientFile = makeJsonFile(cleaners.asObject({
3258
3258
  clientId: asBase64
3259
3259
  }));
3260
3260
 
3261
+ const INFO_CACHE_FILE_NAME = 'infoCache.json';
3262
+ const asInfoCacheFile = cleaners.asObject({
3263
+ corePlugins: cleaners.asObject(asJsonObject),
3264
+ syncServers: cleaners.asArray(cleaners.asString)
3265
+ });
3266
+ const infoCacheFile = makeJsonFile(asInfoCacheFile);
3267
+
3261
3268
  const allPlugins = {};
3262
3269
  let allPluginsLocked = false;
3263
3270
  const onPluginsAdded = [];
@@ -3272,7 +3279,7 @@ function addEdgeCorePlugins(plugins) {
3272
3279
  }
3273
3280
 
3274
3281
  // Save the new plugins:
3275
- for (const pluginId in plugins) {
3282
+ for (const pluginId of Object.keys(plugins)) {
3276
3283
  allPlugins[pluginId] = plugins[pluginId];
3277
3284
  }
3278
3285
 
@@ -3291,7 +3298,7 @@ function lockEdgeCorePlugins() {
3291
3298
  /**
3292
3299
  * Subscribes a context object to the core plugin list.
3293
3300
  */
3294
- function watchPlugins(ios, logBackend, pluginsInit, dispatch) {
3301
+ function watchPlugins(ios, infoCache, logBackend, pluginsInit, dispatch) {
3295
3302
  const {
3296
3303
  io,
3297
3304
  nativeIo
@@ -3302,7 +3309,7 @@ function watchPlugins(ios, logBackend, pluginsInit, dispatch) {
3302
3309
  };
3303
3310
  function pluginsAdded(plugins) {
3304
3311
  const out = {};
3305
- for (const pluginId in plugins) {
3312
+ for (const pluginId of Object.keys(plugins)) {
3306
3313
  const plugin = plugins[pluginId];
3307
3314
  const log = makeLog(logBackend, pluginId);
3308
3315
  const initOptions = pluginsInit[pluginId];
@@ -3312,6 +3319,7 @@ function watchPlugins(ios, logBackend, pluginsInit, dispatch) {
3312
3319
  try {
3313
3320
  if (typeof plugin === 'function') {
3314
3321
  const opts = {
3322
+ infoPayload: infoCache.corePlugins?.[pluginId] ?? {},
3315
3323
  initOptions: typeof initOptions === 'object' ? initOptions : {},
3316
3324
  io: legacyIo,
3317
3325
  log,
@@ -3563,7 +3571,7 @@ async function addStorageWallet(ai, walletInfo) {
3563
3571
  const {
3564
3572
  lastHash
3565
3573
  } = status;
3566
- ai.props.log.error(`Could not sync ${syncKey} with last hash ${String(lastHash)}: ${String(error)}`);
3574
+ ai.props.log.error(`Could not sync ${String(syncKey)} with last hash ${String(lastHash)}: ${String(error)}`);
3567
3575
  onError(error);
3568
3576
  });
3569
3577
  } else await syncPromise;
@@ -4049,8 +4057,9 @@ const asEdgeMetadata = raw => {
4049
4057
  } = clean;
4050
4058
 
4051
4059
  // Delete corrupt amounts that exceed the Javascript number range:
4052
- for (const fiat of Object.keys(clean)) {
4060
+ for (const fiat of Object.keys(exchangeAmount)) {
4053
4061
  if (String(exchangeAmount[fiat]).includes('e')) {
4062
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
4054
4063
  delete exchangeAmount[fiat];
4055
4064
  }
4056
4065
  }
@@ -5469,7 +5478,7 @@ const asAppIdInfo = cleaners.asObject({
5469
5478
  */
5470
5479
  async function fetchAppIdInfo(ai, appId) {
5471
5480
  try {
5472
- const infoServerUri = shuffle(ai.props.state.contextConfig.edgeServers.infoServers)[0];
5481
+ const [infoServerUri] = shuffle(ai.props.state.infoServers);
5473
5482
  const url = `${infoServerUri}/v1/appIdInfo/${appId}`;
5474
5483
  const response = await ai.props.io.fetch(url);
5475
5484
  if (response.status === 404) {
@@ -5820,21 +5829,23 @@ async function loadTokensFile(input) {
5820
5829
  return;
5821
5830
  }
5822
5831
  const legacyCurrencyCodes = await legacyTokensFile.load(disklet, LEGACY_TOKENS_FILE);
5823
- const {
5824
- accountId,
5825
- currencyInfo,
5826
- pluginId
5827
- } = input.props.walletState;
5828
- const accountState = input.props.state.accounts[accountId];
5829
- const tokenIds = currencyCodesToTokenIds(accountState.builtinTokens[pluginId], accountState.customTokens[pluginId], currencyInfo, legacyCurrencyCodes ?? []);
5830
- dispatch({
5831
- type: 'CURRENCY_WALLET_LOADED_TOKEN_FILE',
5832
- payload: {
5833
- walletId: input.props.walletId,
5834
- detectedTokenIds: [],
5835
- enabledTokenIds: tokenIds
5836
- }
5837
- });
5832
+ if (legacyCurrencyCodes != null) {
5833
+ const {
5834
+ accountId,
5835
+ currencyInfo,
5836
+ pluginId
5837
+ } = input.props.walletState;
5838
+ const accountState = input.props.state.accounts[accountId];
5839
+ const tokenIds = currencyCodesToTokenIds(accountState.builtinTokens[pluginId], accountState.customTokens[pluginId], currencyInfo, legacyCurrencyCodes);
5840
+ dispatch({
5841
+ type: 'CURRENCY_WALLET_LOADED_TOKEN_FILE',
5842
+ payload: {
5843
+ walletId: input.props.walletId,
5844
+ detectedTokenIds: [],
5845
+ enabledTokenIds: tokenIds
5846
+ }
5847
+ });
5848
+ }
5838
5849
  }
5839
5850
 
5840
5851
  /**
@@ -6653,7 +6664,7 @@ function makeCurrencyWalletCallbacks(input) {
6653
6664
  return;
6654
6665
  }
6655
6666
  pushUpdate({
6656
- id: `${walletId}==${tokenId}`,
6667
+ id: `${walletId}==${String(tokenId)}`,
6657
6668
  action: 'onTokenBalanceChanged',
6658
6669
  updateFunc: () => {
6659
6670
  input.props.dispatch({
@@ -8254,14 +8265,15 @@ async function fetchSwapQuotes(ai, accountId, request, opts = {}) {
8254
8265
  slowResponseMs = 20000
8255
8266
  } = opts;
8256
8267
  const {
8257
- log
8268
+ log,
8269
+ state
8258
8270
  } = ai.props;
8259
- const account = ai.props.state.accounts[accountId];
8271
+ const account = state.accounts[accountId];
8260
8272
  const {
8261
8273
  swapSettings,
8262
8274
  userSettings
8263
8275
  } = account;
8264
- const swapPlugins = ai.props.state.plugins.swap;
8276
+ const swapPlugins = state.plugins.swap;
8265
8277
  log.warn('Requesting swap quotes for: ', {
8266
8278
  ...request,
8267
8279
  fromWallet: request.fromWallet.id,
@@ -8283,6 +8295,7 @@ async function fetchSwapQuotes(ai, accountId, request, opts = {}) {
8283
8295
  // Start request:
8284
8296
  pendingIds.add(pluginId);
8285
8297
  promises.push(swapPlugins[pluginId].fetchSwapQuote(request, userSettings[pluginId], {
8298
+ infoPayload: state.infoCache.corePlugins?.[pluginId] ?? {},
8286
8299
  promoCode: promoCodes[pluginId]
8287
8300
  }).then(quote => {
8288
8301
  const {
@@ -9241,8 +9254,8 @@ function makeAccountApi(ai, accountId) {
9241
9254
  } = walletOutput;
9242
9255
  if (engine == null) throw new Error(`Invalid wallet: ${activateWalletId} not found`);
9243
9256
  if (engine.engineActivateWallet == null) throw new Error(`activateWallet unsupported by walletId ${activateWalletId}`);
9244
- const walletId = paymentInfo?.walletId;
9245
- const wallet = currencyWallets[walletId ?? ''];
9257
+ const walletId = paymentInfo?.walletId ?? '';
9258
+ const wallet = currencyWallets[walletId];
9246
9259
  if (wallet == null) {
9247
9260
  throw new Error(`No wallet for walletId ${walletId}`);
9248
9261
  }
@@ -9702,14 +9715,7 @@ const accountPixie = reduxPixies.combinePixies({
9702
9715
  });
9703
9716
  return {
9704
9717
  update() {
9705
- const {
9706
- accountOutput
9707
- } = input.props;
9708
- if (accountOutput == null) return;
9709
- const {
9710
- accountApi
9711
- } = accountOutput;
9712
- if (accountApi == null) return;
9718
+ if (input.props.accountOutput?.accountApi == null) return;
9713
9719
 
9714
9720
  // Start once the EdgeAccount API exists:
9715
9721
  dataTask.start({
@@ -10118,16 +10124,110 @@ const context = reduxPixies.combinePixies({
10118
10124
  }
10119
10125
  }
10120
10126
  };
10121
- }
10127
+ },
10128
+ infoFetcher: reduxPixies.filterPixie(input => {
10129
+ async function doInfoSync() {
10130
+ const {
10131
+ dispatch,
10132
+ io
10133
+ } = input.props;
10134
+ const [infoServerUri] = shuffle(input.props.state.infoServers);
10135
+ const response = await fetch(`${infoServerUri}/v1/coreRollup`, {
10136
+ headers: {
10137
+ accept: 'application/json'
10138
+ }
10139
+ });
10140
+ if (!response.ok) return;
10141
+ const json = await response.json();
10142
+ const infoCache = asInfoCacheFile(json);
10143
+ dispatch({
10144
+ type: 'INFO_CACHE_FETCHED',
10145
+ payload: infoCache
10146
+ });
10147
+ await infoCacheFile.save(io.disklet, INFO_CACHE_FILE_NAME, infoCache);
10148
+ }
10149
+ const infoTask = makePeriodicTask(doInfoSync, 10 * 60 * 1000);
10150
+ infoTask.start();
10151
+ return {
10152
+ update() {},
10153
+ destroy() {
10154
+ infoTask.stop();
10155
+ }
10156
+ };
10157
+ }, props => props.state.paused ? undefined : props)
10122
10158
  });
10123
10159
 
10160
+ /**
10161
+ * Compares two JSON-like objects, returning false if they differ.
10162
+ */
10163
+ function matchJson(a, b) {
10164
+ // Use simple equality, unless a and b are proper objects:
10165
+ if (typeof a !== 'object' || typeof b !== 'object' || a == null || b == null) {
10166
+ return a === b;
10167
+ }
10168
+
10169
+ // These must either be both arrays or both objects:
10170
+ const aIsArray = Array.isArray(a);
10171
+ const bIsArray = Array.isArray(b);
10172
+ if (aIsArray !== bIsArray) return false;
10173
+
10174
+ // Compare arrays in order:
10175
+ if (aIsArray) {
10176
+ if (a.length !== b.length) return false;
10177
+ for (let i = 0; i < a.length; ++i) {
10178
+ if (!matchJson(a[i], b[i])) return false;
10179
+ }
10180
+ return true;
10181
+ }
10182
+
10183
+ // These are both regular objects, so grab the keys,
10184
+ // ignoring entries where the value is `undefined`:
10185
+ const aKeys = Object.getOwnPropertyNames(a).filter(key => a[key] !== undefined);
10186
+ const bKeys = Object.getOwnPropertyNames(b).filter(key => b[key] !== undefined);
10187
+ if (aKeys.length !== bKeys.length) return false;
10188
+
10189
+ // We know that both objects have the same number of properties,
10190
+ // so if every property in `a` has a matching property in `b`,
10191
+ // the objects must be identical, regardless of key order.
10192
+ for (const key of aKeys) {
10193
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
10194
+ if (!matchJson(a[key], b[key])) return false;
10195
+ }
10196
+ return true;
10197
+ }
10198
+
10124
10199
  const currency$1 = reduxPixies.combinePixies({
10125
10200
  wallets: reduxPixies.mapPixie(walletPixie, props => props.state.currency.currencyWalletIds, (props, walletId) => ({
10126
10201
  ...props,
10127
10202
  walletId,
10128
10203
  walletState: props.state.currency.wallets[walletId],
10129
10204
  walletOutput: props.output.currency.wallets[walletId]
10130
- }))
10205
+ })),
10206
+ pluginUpdater(input) {
10207
+ let lastInfo;
10208
+ return async () => {
10209
+ const {
10210
+ infoCache,
10211
+ plugins
10212
+ } = input.props.state;
10213
+
10214
+ // Bail out quickly if nothing has changed:
10215
+ if (lastInfo === infoCache) return;
10216
+
10217
+ // Update plugins after the first run:
10218
+ if (lastInfo != null) {
10219
+ for (const pluginId of Object.keys(plugins.currency)) {
10220
+ const plugin = plugins.currency[pluginId];
10221
+ const newPayload = infoCache.corePlugins?.[pluginId] ?? {};
10222
+ const oldPayload = lastInfo.corePlugins?.[pluginId] ?? {};
10223
+ if (plugin.updateInfoPayload != null && !matchJson(oldPayload, newPayload)) {
10224
+ await plugin.updateInfoPayload(newPayload);
10225
+ }
10226
+ }
10227
+ }
10228
+ lastInfo = infoCache;
10229
+ };
10230
+ }
10131
10231
  });
10132
10232
 
10133
10233
  /**
@@ -10280,26 +10380,6 @@ const rootPixie = reduxPixies.combinePixies({
10280
10380
  scrypt
10281
10381
  });
10282
10382
 
10283
- const defaultEdgeServers = {
10284
- infoServers: [],
10285
- syncServers: []
10286
- };
10287
- const contextConfig = reduxKeto.buildReducer({
10288
- edgeServers(state = defaultEdgeServers, action) {
10289
- if (action.type === 'INIT') {
10290
- const {
10291
- infoServers,
10292
- syncServers
10293
- } = action.payload;
10294
- return {
10295
- infoServers,
10296
- syncServers
10297
- };
10298
- }
10299
- return state;
10300
- }
10301
- });
10302
-
10303
10383
  const currency = reduxKeto.buildReducer({
10304
10384
  currencyWalletIds(state, action, next) {
10305
10385
  // Optimize the common case:
@@ -10554,6 +10634,18 @@ const reducer = reduxKeto.buildReducer({
10554
10634
  hideKeys(state = true, action) {
10555
10635
  return action.type === 'INIT' ? action.payload.hideKeys : state;
10556
10636
  },
10637
+ infoCache(state = {}, action) {
10638
+ switch (action.type) {
10639
+ case 'INIT':
10640
+ return action.payload.infoCache;
10641
+ case 'INFO_CACHE_FETCHED':
10642
+ return action.payload;
10643
+ }
10644
+ return state;
10645
+ },
10646
+ infoServers(state = [], action) {
10647
+ return action.type === 'INIT' ? action.payload.infoServers : state;
10648
+ },
10557
10649
  lastAccountId(state, action, next) {
10558
10650
  return `login${next.accountCount}`;
10559
10651
  },
@@ -10575,7 +10667,9 @@ const reducer = reduxKeto.buildReducer({
10575
10667
  skipBlockHeight(state = false, action) {
10576
10668
  return action.type === 'INIT' ? action.payload.skipBlockHeight : state;
10577
10669
  },
10578
- contextConfig,
10670
+ syncServers(state = [], action) {
10671
+ return action.type === 'INIT' ? action.payload.syncServers : state;
10672
+ },
10579
10673
  currency,
10580
10674
  login,
10581
10675
  plugins,
@@ -10600,15 +10694,15 @@ async function makeContext(ios, logBackend, opts) {
10600
10694
  apiKey,
10601
10695
  appId = '',
10602
10696
  authServer = 'https://login.edge.app/api',
10603
- infoServer = ['https://info-eu1.edge.app', 'https://info-us1.edge.app'],
10604
- syncServer = ['https://sync-us1.edge.app', 'https://sync-us2.edge.app', 'https://sync-us3.edge.app', 'https://sync-us4.edge.app', 'https://sync-us5.edge.app', 'https://sync-us6.edge.app', 'https://sync-eu.edge.app'],
10697
+ infoServer,
10698
+ syncServer,
10605
10699
  deviceDescription = null,
10606
10700
  hideKeys = false,
10607
10701
  plugins: pluginsInit = {},
10608
10702
  skipBlockHeight = false
10609
10703
  } = opts;
10610
- const infoServers = Array.isArray(infoServer) ? infoServer : [infoServer];
10611
- const syncServers = Array.isArray(syncServer) ? syncServer : [syncServer];
10704
+ const infoServers = typeof infoServer === 'string' ? [infoServer] : infoServer != null && infoServer.length > 0 ? infoServer : ['https://info-eu1.edge.app', 'https://info-us1.edge.app'];
10705
+ const syncServers = typeof syncServer === 'string' ? [syncServer] : syncServer != null && syncServer.length > 0 ? syncServer : ['https://sync-us1.edge.app', 'https://sync-us2.edge.app', 'https://sync-us3.edge.app', 'https://sync-us4.edge.app', 'https://sync-us5.edge.app', 'https://sync-us6.edge.app', 'https://sync-eu.edge.app'];
10612
10706
  const logSettings = {
10613
10707
  ...defaultLogSettings,
10614
10708
  ...opts.logSettings
@@ -10629,9 +10723,9 @@ async function makeContext(ios, logBackend, opts) {
10629
10723
  return state.ready ? state.logSettings : logSettings;
10630
10724
  });
10631
10725
  const log = makeLog(logBackend, 'edge-core');
10726
+ let [clientInfo, infoCache = {}, stashes] = await Promise.all([clientFile.load(io.disklet, CLIENT_FILE_NAME), infoCacheFile.load(io.disklet, INFO_CACHE_FILE_NAME), loadStashes(io.disklet, log)]);
10632
10727
 
10633
- // Load the clientId from disk:
10634
- let clientInfo = await clientFile.load(io.disklet, CLIENT_FILE_NAME);
10728
+ // Save the clientId if we don't have one:
10635
10729
  if (clientInfo == null) {
10636
10730
  clientInfo = {
10637
10731
  clientId: io.random(16)
@@ -10640,13 +10734,13 @@ async function makeContext(ios, logBackend, opts) {
10640
10734
  }
10641
10735
 
10642
10736
  // Load the login stashes from disk:
10643
- const stashes = await loadStashes(io.disklet, log);
10644
10737
  redux$1.dispatch({
10645
10738
  type: 'INIT',
10646
10739
  payload: {
10647
10740
  apiKey,
10648
10741
  appId,
10649
10742
  authServer,
10743
+ infoCache,
10650
10744
  infoServers,
10651
10745
  syncServers,
10652
10746
  clientId: clientInfo.clientId,
@@ -10660,10 +10754,10 @@ async function makeContext(ios, logBackend, opts) {
10660
10754
  });
10661
10755
 
10662
10756
  // Subscribe to new plugins:
10663
- watchPlugins(ios, logBackend, pluginsInit, redux$1.dispatch);
10757
+ watchPlugins(ios, infoCache, logBackend, pluginsInit, redux$1.dispatch);
10664
10758
 
10665
10759
  // Create sync client:
10666
- const syncClient = await edgeSyncClient.makeSyncClient({
10760
+ const syncClient = edgeSyncClient.makeSyncClient({
10667
10761
  log,
10668
10762
  fetch: io.fetch,
10669
10763
  edgeServers: {
@@ -10720,9 +10814,9 @@ function closeEdge() {
10720
10814
  */
10721
10815
  class FakeDb {
10722
10816
  constructor() {
10723
- this.lobbies = {};
10817
+ this.lobbies = new Map();
10724
10818
  this.logins = [];
10725
- this.repos = {};
10819
+ this.repos = new Map();
10726
10820
  }
10727
10821
  getLoginById(loginId) {
10728
10822
  return this.logins.find(login => verifyData(login.loginId, loginId));
@@ -10757,7 +10851,7 @@ class FakeDb {
10757
10851
  }
10758
10852
  }
10759
10853
  setupRepo(syncKey, repo) {
10760
- this.repos[syncKey] = repo;
10854
+ this.repos.set(syncKey, repo);
10761
10855
  }
10762
10856
  dumpLogin(login) {
10763
10857
  const makeTree = login => ({
@@ -11224,7 +11318,7 @@ function createLogin(request, login) {
11224
11318
  keyBoxes: []
11225
11319
  }))(body.data);
11226
11320
  for (const syncKey of keys.newSyncKeys) {
11227
- db.repos[syncKey] = {};
11321
+ db.repos.set(syncKey, {});
11228
11322
  }
11229
11323
 
11230
11324
  // Start building the new database row:
@@ -11267,7 +11361,7 @@ const addKeysRoute = withLogin2(request => {
11267
11361
 
11268
11362
  // Set up repos:
11269
11363
  for (const syncKey of clean.newSyncKeys) {
11270
- db.repos[syncKey] = {};
11364
+ db.repos.set(syncKey, {});
11271
11365
  }
11272
11366
  login.keyBoxes = softCat(login.keyBoxes, clean.keyBoxes);
11273
11367
  return statusResponse();
@@ -11526,7 +11620,7 @@ const withLobby = (server, fallback = handleMissingLobby) => request => {
11526
11620
  path
11527
11621
  } = request;
11528
11622
  const lobbyId = path.split('/')[4];
11529
- const lobby = db.lobbies[lobbyId];
11623
+ const lobby = db.lobbies.get(lobbyId);
11530
11624
  return lobby != null ? server({
11531
11625
  ...request,
11532
11626
  lobby,
@@ -11550,11 +11644,11 @@ const createLobbyRoute = withLobby(request => statusResponse(statusCodes.account
11550
11644
  timeout = 600
11551
11645
  } = clean;
11552
11646
  const expires = new Date(Date.now() + 1000 * timeout).toISOString();
11553
- db.lobbies[lobbyId] = {
11647
+ db.lobbies.set(lobbyId, {
11554
11648
  request: clean,
11555
11649
  replies: [],
11556
11650
  expires
11557
- };
11651
+ });
11558
11652
  return statusResponse();
11559
11653
  });
11560
11654
  const updateLobbyRoute = withLobby(request => {
@@ -11580,7 +11674,7 @@ const deleteLobbyRoute = withLobby(request => {
11580
11674
  db,
11581
11675
  lobbyId
11582
11676
  } = request;
11583
- delete db.lobbies[lobbyId];
11677
+ db.lobbies.delete(lobbyId);
11584
11678
  return statusResponse();
11585
11679
  });
11586
11680
 
@@ -11623,7 +11717,7 @@ const withRepo = server => request => {
11623
11717
  const syncKey = elements[4];
11624
11718
  // const hash = elements[5]
11625
11719
 
11626
- const repo = db.repos[syncKey];
11720
+ const repo = db.repos.get(syncKey);
11627
11721
  if (repo == null) {
11628
11722
  // This is not the auth server, so we have a different format:
11629
11723
  return jsonResponse({
@@ -11867,7 +11961,9 @@ function makeFakeWorld(ios, logBackend, users) {
11867
11961
 
11868
11962
  // Find the data on the server:
11869
11963
  const login = fakeDb.getLoginById(loginId);
11870
- if (login == null) throw new Error(`Cannot find user ${account.username}`);
11964
+ if (login == null) {
11965
+ throw new Error(`Cannot find user ${account.rootLoginId}`);
11966
+ }
11871
11967
 
11872
11968
  // Figure out which repos to use:
11873
11969
  const syncKeys = [];
@@ -11878,7 +11974,8 @@ function makeFakeWorld(ios, logBackend, users) {
11878
11974
  }
11879
11975
  const repos = {};
11880
11976
  for (const syncKey of syncKeys) {
11881
- repos[syncKey] = wasEdgeRepoDump(fakeDb.repos[syncKey]);
11977
+ const repo = fakeDb.repos.get(syncKey);
11978
+ if (repo != null) repos[syncKey] = wasEdgeRepoDump(repo);
11882
11979
  }
11883
11980
  return {
11884
11981
  lastLogin: account.lastLogin,
@@ -1894,6 +1894,10 @@ export * from './server-types'
1894
1894
 
1895
1895
 
1896
1896
 
1897
+
1898
+
1899
+
1900
+
1897
1901
 
1898
1902
 
1899
1903
 
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Compares two JSON-like objects, returning false if they differ.
3
+ */
4
+ export function matchJson(a, b) {
5
+ // Use simple equality, unless a and b are proper objects:
6
+ if (
7
+ typeof a !== 'object' ||
8
+ typeof b !== 'object' ||
9
+ a == null ||
10
+ b == null
11
+ ) {
12
+ return a === b
13
+ }
14
+
15
+ // These must either be both arrays or both objects:
16
+ const aIsArray = Array.isArray(a)
17
+ const bIsArray = Array.isArray(b)
18
+ if (aIsArray !== bIsArray) return false
19
+
20
+ // Compare arrays in order:
21
+ if (aIsArray) {
22
+ if (a.length !== b.length) return false
23
+ for (let i = 0; i < a.length; ++i) {
24
+ if (!matchJson(a[i], b[i])) return false
25
+ }
26
+ return true
27
+ }
28
+
29
+ // These are both regular objects, so grab the keys,
30
+ // ignoring entries where the value is `undefined`:
31
+ const aKeys = Object.getOwnPropertyNames(a).filter(
32
+ key => a[key] !== undefined
33
+ )
34
+ const bKeys = Object.getOwnPropertyNames(b).filter(
35
+ key => b[key] !== undefined
36
+ )
37
+ if (aKeys.length !== bKeys.length) return false
38
+
39
+ // We know that both objects have the same number of properties,
40
+ // so if every property in `a` has a matching property in `b`,
41
+ // the objects must be identical, regardless of key order.
42
+ for (const key of aKeys) {
43
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false
44
+ if (!matchJson(a[key], b[key])) return false
45
+ }
46
+ return true
47
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edge-core-js",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Edge account & wallet management library",
5
5
  "keywords": [
6
6
  "bitcoin",
@@ -163,9 +163,12 @@ export interface EdgeNativeIo {
163
163
  * All core plugins receive these options at creation time.
164
164
  */
165
165
  export interface EdgeCorePluginOptions {
166
- // Load-time options (like API keys) passed into the context:
166
+ /** Load-time options (like API keys) passed into the context */
167
167
  initOptions: JsonObject
168
168
 
169
+ /** Data provided by the info server */
170
+ infoPayload: JsonObject
171
+
169
172
  // Access to the world outside the plugin:
170
173
  io: EdgeIo
171
174
  log: EdgeLog // Plugin-scoped logging
@@ -1034,6 +1037,7 @@ export interface EdgeCurrencyPlugin {
1034
1037
  walletInfo: EdgeWalletInfo,
1035
1038
  opts: EdgeCurrencyEngineOptions
1036
1039
  ) => Promise<EdgeCurrencyEngine>
1040
+ readonly updateInfoPayload?: (infoPayload: JsonObject) => Promise<void>
1037
1041
 
1038
1042
  // Escape hatch:
1039
1043
  readonly otherMethods?: EdgeOtherMethods
@@ -1311,7 +1315,7 @@ export interface EdgeSwapPlugin {
1311
1315
  readonly fetchSwapQuote: (
1312
1316
  request: EdgeSwapRequest,
1313
1317
  userSettings: JsonObject | undefined,
1314
- opts: { promoCode?: string }
1318
+ opts: { infoPayload: JsonObject; promoCode?: string }
1315
1319
  ) => Promise<EdgeSwapQuote>
1316
1320
  }
1317
1321
 
@@ -1,31 +0,0 @@
1
- import { buildReducer } from 'redux-keto'
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
- const defaultEdgeServers = {
15
- infoServers: [],
16
- syncServers: []
17
- }
18
-
19
- export const contextConfig = buildReducer
20
-
21
-
22
-
23
- ({
24
- edgeServers(state = defaultEdgeServers, action) {
25
- if (action.type === 'INIT') {
26
- const { infoServers, syncServers } = action.payload
27
- return { infoServers, syncServers }
28
- }
29
- return state
30
- }
31
- })