@talismn/balances 0.0.0-pr2092-20250715131755 → 0.0.0-pr2093-20250715143904

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.
@@ -21,7 +21,9 @@ export declare class BalancesProvider {
21
21
  private getNetworkBalances$;
22
22
  private getPolkadotNetworkModuleBalances$;
23
23
  private getEthereumNetworkModuleBalances$;
24
- private updateStorage$;
24
+ private updateStoredBalances;
25
+ private updateStoredMiniMetadatas;
26
+ private processStorageUpdatesQueue;
25
27
  private getNetworkMiniMetadatas$;
26
28
  private getNetworkSpecVersion$;
27
29
  private getMiniMetadatas$;
@@ -6238,6 +6238,10 @@ class BalancesProvider {
6238
6238
  #chaindataProvider;
6239
6239
  #chainConnectors;
6240
6240
  #storage;
6241
+
6242
+ // storage is updated at high frequency, use a queue to avoid race conditions
6243
+ #updateStorageQueue = [];
6244
+ #isProcessingStorageUpdates = false;
6241
6245
  constructor(chaindataProvider, chainConnectors, storage = DEFAULT_STORAGE) {
6242
6246
  this.#chaindataProvider = chaindataProvider;
6243
6247
  this.#chainConnectors = chainConnectors;
@@ -6364,9 +6368,7 @@ class BalancesProvider {
6364
6368
  status: "live",
6365
6369
  // exclude zero balances
6366
6370
  balances: results.success.filter(b => new Balance(b).total.planck > 0n)
6367
- })), rxjs.tap(results => {
6368
- this.updateStorage$(balanceIds, results);
6369
- }),
6371
+ })), rxjs.tap(results => this.updateStoredBalances(balanceIds, results)),
6370
6372
  // shareReplay + keepAlive(0) keep the subscription alive while root observable is being unsubscribed+resubscribed, in case any input change
6371
6373
  rxjs.shareReplay({
6372
6374
  refCount: true,
@@ -6414,9 +6416,7 @@ class BalancesProvider {
6414
6416
  status: "live",
6415
6417
  // exclude zero balances
6416
6418
  balances: results.success.filter(b => new Balance(b).total.planck > 0n)
6417
- })), rxjs.tap(results => {
6418
- this.updateStorage$(balanceIds, results);
6419
- }),
6419
+ })), rxjs.tap(results => this.updateStoredBalances(balanceIds, results)),
6420
6420
  // shareReplay + keepAlive(0) keep the subscription alive while root observable is being unsubscribed+resubscribed, in case any input change
6421
6421
  rxjs.shareReplay({
6422
6422
  refCount: true,
@@ -6430,20 +6430,72 @@ class BalancesProvider {
6430
6430
  })));
6431
6431
  });
6432
6432
  }
6433
- updateStorage$(balanceIds, balancesResult) {
6433
+ updateStoredBalances(balanceIds, balancesResult) {
6434
6434
  if (balancesResult.status !== "live") return;
6435
+ this.#updateStorageQueue.push({
6436
+ type: "balances",
6437
+ balanceIds,
6438
+ balances: balancesResult.balances
6439
+ });
6440
+ this.processStorageUpdatesQueue();
6441
+ }
6442
+ updateStoredMiniMetadatas(networkId, miniMetadatas) {
6443
+ this.#updateStorageQueue.push({
6444
+ type: "miniMetadatas",
6445
+ networkId,
6446
+ miniMetadatas
6447
+ });
6448
+ this.processStorageUpdatesQueue();
6449
+ }
6450
+ processStorageUpdatesQueue() {
6451
+ if (this.#updateStorageQueue.length > 1) log.warn("[balances] this message is the proof that there are sometimes racing conditions, and that we avoided them :)");
6452
+ if (this.#isProcessingStorageUpdates || !this.#updateStorageQueue.length) return;
6453
+ this.#isProcessingStorageUpdates = true;
6435
6454
  const storage = this.#storage.getValue();
6436
- const balances = lodashEs.assign({}, storage.balances,
6437
- // delete all balances expected in the result set. because if they are not present it means they are empty.
6438
- lodashEs.fromPairs(balanceIds.map(balanceId => [balanceId, undefined])), lodashEs.keyBy(
6439
- // storage balances must have status "cache", because they are used as start value when initialising subsequent subscriptions
6440
- balancesResult.balances.map(b => ({
6441
- ...b,
6442
- status: "cache"
6443
- })), b => getBalanceId(b)));
6444
- this.#storage.next(lodashEs.assign({}, storage, {
6445
- balances
6446
- }));
6455
+ const balances = {
6456
+ ...storage.balances
6457
+ };
6458
+ const miniMetadatas = {
6459
+ ...storage.miniMetadatas
6460
+ };
6461
+ while (this.#updateStorageQueue.length) {
6462
+ const queueItem = this.#updateStorageQueue.shift();
6463
+ switch (queueItem?.type) {
6464
+ case "miniMetadatas":
6465
+ {
6466
+ const {
6467
+ networkId,
6468
+ miniMetadatas: newMiniMetadatas
6469
+ } = queueItem;
6470
+
6471
+ // remove old miniMetadatas for the network
6472
+ for (const miniMetadata of lodashEs.values(miniMetadatas)) if (miniMetadata.chainId === networkId) delete miniMetadatas[miniMetadata.id];
6473
+
6474
+ // add new ones
6475
+ for (const miniMetadata of newMiniMetadatas) miniMetadatas[miniMetadata.id] = miniMetadata;
6476
+ break;
6477
+ }
6478
+ case "balances":
6479
+ {
6480
+ const {
6481
+ balanceIds,
6482
+ balances: newBalances
6483
+ } = queueItem;
6484
+
6485
+ // remove all balances expected in the result set
6486
+ for (const balanceId of balanceIds) delete balances[balanceId];
6487
+
6488
+ // add new ones
6489
+ for (const balance of newBalances) balances[getBalanceId(balance)] = balance;
6490
+ break;
6491
+ }
6492
+ }
6493
+ this.#storage.next({
6494
+ balances,
6495
+ miniMetadatas
6496
+ });
6497
+ this.#isProcessingStorageUpdates = false;
6498
+ }
6447
6499
  }
6448
6500
  getNetworkMiniMetadatas$(networkId) {
6449
6501
  return util.getSharedObservable(`BalancesProvider.getNetworkMiniMetadatas$`, {
@@ -6495,15 +6547,7 @@ class BalancesProvider {
6495
6547
  // and persist in storage for later reuse
6496
6548
  rxjs.tap(newMiniMetadatas => {
6497
6549
  if (!newMiniMetadatas.length) return;
6498
- const storage = this.#storage.getValue();
6499
- const miniMetadatas = lodashEs.assign(
6500
- // keep minimetadatas of other networks
6501
- lodashEs.keyBy(lodashEs.values(storage.miniMetadatas).filter(m => m.chainId !== networkId), m => m.id),
6502
- // add the ones for our network
6503
- lodashEs.keyBy(newMiniMetadatas, m => m.id));
6504
- this.#storage.next(lodashEs.assign({}, storage, {
6505
- miniMetadatas
6506
- }));
6550
+ this.updateStoredMiniMetadatas(networkId, newMiniMetadatas);
6507
6551
  }));
6508
6552
  }),
6509
6553
  // emit only when mini metadata changes, as a change here would restart all subscriptions for the network
@@ -6238,6 +6238,10 @@ class BalancesProvider {
6238
6238
  #chaindataProvider;
6239
6239
  #chainConnectors;
6240
6240
  #storage;
6241
+
6242
+ // storage is updated at high frequency, use a queue to avoid race conditions
6243
+ #updateStorageQueue = [];
6244
+ #isProcessingStorageUpdates = false;
6241
6245
  constructor(chaindataProvider, chainConnectors, storage = DEFAULT_STORAGE) {
6242
6246
  this.#chaindataProvider = chaindataProvider;
6243
6247
  this.#chainConnectors = chainConnectors;
@@ -6364,9 +6368,7 @@ class BalancesProvider {
6364
6368
  status: "live",
6365
6369
  // exclude zero balances
6366
6370
  balances: results.success.filter(b => new Balance(b).total.planck > 0n)
6367
- })), rxjs.tap(results => {
6368
- this.updateStorage$(balanceIds, results);
6369
- }),
6371
+ })), rxjs.tap(results => this.updateStoredBalances(balanceIds, results)),
6370
6372
  // shareReplay + keepAlive(0) keep the subscription alive while root observable is being unsubscribed+resubscribed, in case any input change
6371
6373
  rxjs.shareReplay({
6372
6374
  refCount: true,
@@ -6414,9 +6416,7 @@ class BalancesProvider {
6414
6416
  status: "live",
6415
6417
  // exclude zero balances
6416
6418
  balances: results.success.filter(b => new Balance(b).total.planck > 0n)
6417
- })), rxjs.tap(results => {
6418
- this.updateStorage$(balanceIds, results);
6419
- }),
6419
+ })), rxjs.tap(results => this.updateStoredBalances(balanceIds, results)),
6420
6420
  // shareReplay + keepAlive(0) keep the subscription alive while root observable is being unsubscribed+resubscribed, in case any input change
6421
6421
  rxjs.shareReplay({
6422
6422
  refCount: true,
@@ -6430,20 +6430,72 @@ class BalancesProvider {
6430
6430
  })));
6431
6431
  });
6432
6432
  }
6433
- updateStorage$(balanceIds, balancesResult) {
6433
+ updateStoredBalances(balanceIds, balancesResult) {
6434
6434
  if (balancesResult.status !== "live") return;
6435
+ this.#updateStorageQueue.push({
6436
+ type: "balances",
6437
+ balanceIds,
6438
+ balances: balancesResult.balances
6439
+ });
6440
+ this.processStorageUpdatesQueue();
6441
+ }
6442
+ updateStoredMiniMetadatas(networkId, miniMetadatas) {
6443
+ this.#updateStorageQueue.push({
6444
+ type: "miniMetadatas",
6445
+ networkId,
6446
+ miniMetadatas
6447
+ });
6448
+ this.processStorageUpdatesQueue();
6449
+ }
6450
+ processStorageUpdatesQueue() {
6451
+ if (this.#updateStorageQueue.length > 1) log.warn("[balances] this message is the proof that there are sometimes racing conditions, and that we avoided them :)");
6452
+ if (this.#isProcessingStorageUpdates || !this.#updateStorageQueue.length) return;
6453
+ this.#isProcessingStorageUpdates = true;
6435
6454
  const storage = this.#storage.getValue();
6436
- const balances = lodashEs.assign({}, storage.balances,
6437
- // delete all balances expected in the result set. because if they are not present it means they are empty.
6438
- lodashEs.fromPairs(balanceIds.map(balanceId => [balanceId, undefined])), lodashEs.keyBy(
6439
- // storage balances must have status "cache", because they are used as start value when initialising subsequent subscriptions
6440
- balancesResult.balances.map(b => ({
6441
- ...b,
6442
- status: "cache"
6443
- })), b => getBalanceId(b)));
6444
- this.#storage.next(lodashEs.assign({}, storage, {
6445
- balances
6446
- }));
6455
+ const balances = {
6456
+ ...storage.balances
6457
+ };
6458
+ const miniMetadatas = {
6459
+ ...storage.miniMetadatas
6460
+ };
6461
+ while (this.#updateStorageQueue.length) {
6462
+ const queueItem = this.#updateStorageQueue.shift();
6463
+ switch (queueItem?.type) {
6464
+ case "miniMetadatas":
6465
+ {
6466
+ const {
6467
+ networkId,
6468
+ miniMetadatas: newMiniMetadatas
6469
+ } = queueItem;
6470
+
6471
+ // remove old miniMetadatas for the network
6472
+ for (const miniMetadata of lodashEs.values(miniMetadatas)) if (miniMetadata.chainId === networkId) delete miniMetadatas[miniMetadata.id];
6473
+
6474
+ // add new ones
6475
+ for (const miniMetadata of newMiniMetadatas) miniMetadatas[miniMetadata.id] = miniMetadata;
6476
+ break;
6477
+ }
6478
+ case "balances":
6479
+ {
6480
+ const {
6481
+ balanceIds,
6482
+ balances: newBalances
6483
+ } = queueItem;
6484
+
6485
+ // remove all balances expected in the result set
6486
+ for (const balanceId of balanceIds) delete balances[balanceId];
6487
+
6488
+ // add new ones
6489
+ for (const balance of newBalances) balances[getBalanceId(balance)] = balance;
6490
+ break;
6491
+ }
6492
+ }
6493
+ this.#storage.next({
6494
+ balances,
6495
+ miniMetadatas
6496
+ });
6497
+ this.#isProcessingStorageUpdates = false;
6498
+ }
6447
6499
  }
6448
6500
  getNetworkMiniMetadatas$(networkId) {
6449
6501
  return util.getSharedObservable(`BalancesProvider.getNetworkMiniMetadatas$`, {
@@ -6495,15 +6547,7 @@ class BalancesProvider {
6495
6547
  // and persist in storage for later reuse
6496
6548
  rxjs.tap(newMiniMetadatas => {
6497
6549
  if (!newMiniMetadatas.length) return;
6498
- const storage = this.#storage.getValue();
6499
- const miniMetadatas = lodashEs.assign(
6500
- // keep minimetadatas of other networks
6501
- lodashEs.keyBy(lodashEs.values(storage.miniMetadatas).filter(m => m.chainId !== networkId), m => m.id),
6502
- // add the ones for our network
6503
- lodashEs.keyBy(newMiniMetadatas, m => m.id));
6504
- this.#storage.next(lodashEs.assign({}, storage, {
6505
- miniMetadatas
6506
- }));
6550
+ this.updateStoredMiniMetadatas(networkId, newMiniMetadatas);
6507
6551
  }));
6508
6552
  }),
6509
6553
  // emit only when mini metadata changes, as a change here would restart all subscriptions for the network
@@ -6229,6 +6229,10 @@ class BalancesProvider {
6229
6229
  #chaindataProvider;
6230
6230
  #chainConnectors;
6231
6231
  #storage;
6232
+
6233
+ // storage is updated at high frequency, use a queue to avoid race conditions
6234
+ #updateStorageQueue = [];
6235
+ #isProcessingStorageUpdates = false;
6232
6236
  constructor(chaindataProvider, chainConnectors, storage = DEFAULT_STORAGE) {
6233
6237
  this.#chaindataProvider = chaindataProvider;
6234
6238
  this.#chainConnectors = chainConnectors;
@@ -6355,9 +6359,7 @@ class BalancesProvider {
6355
6359
  status: "live",
6356
6360
  // exclude zero balances
6357
6361
  balances: results.success.filter(b => new Balance(b).total.planck > 0n)
6358
- })), tap(results => {
6359
- this.updateStorage$(balanceIds, results);
6360
- }),
6362
+ })), tap(results => this.updateStoredBalances(balanceIds, results)),
6361
6363
  // shareReplay + keepAlive(0) keep the subscription alive while root observable is being unsubscribed+resubscribed, in case any input change
6362
6364
  shareReplay({
6363
6365
  refCount: true,
@@ -6405,9 +6407,7 @@ class BalancesProvider {
6405
6407
  status: "live",
6406
6408
  // exclude zero balances
6407
6409
  balances: results.success.filter(b => new Balance(b).total.planck > 0n)
6408
- })), tap(results => {
6409
- this.updateStorage$(balanceIds, results);
6410
- }),
6410
+ })), tap(results => this.updateStoredBalances(balanceIds, results)),
6411
6411
  // shareReplay + keepAlive(0) keep the subscription alive while root observable is being unsubscribed+resubscribed, in case any input change
6412
6412
  shareReplay({
6413
6413
  refCount: true,
@@ -6421,20 +6421,72 @@ class BalancesProvider {
6421
6421
  })));
6422
6422
  });
6423
6423
  }
6424
- updateStorage$(balanceIds, balancesResult) {
6424
+ updateStoredBalances(balanceIds, balancesResult) {
6425
6425
  if (balancesResult.status !== "live") return;
6426
+ this.#updateStorageQueue.push({
6427
+ type: "balances",
6428
+ balanceIds,
6429
+ balances: balancesResult.balances
6430
+ });
6431
+ this.processStorageUpdatesQueue();
6432
+ }
6433
+ updateStoredMiniMetadatas(networkId, miniMetadatas) {
6434
+ this.#updateStorageQueue.push({
6435
+ type: "miniMetadatas",
6436
+ networkId,
6437
+ miniMetadatas
6438
+ });
6439
+ this.processStorageUpdatesQueue();
6440
+ }
6441
+ processStorageUpdatesQueue() {
6442
+ if (this.#updateStorageQueue.length > 1) log.warn("[balances] this message is the proof that there are sometimes racing conditions, and that we avoided them :)");
6443
+ if (this.#isProcessingStorageUpdates || !this.#updateStorageQueue.length) return;
6444
+ this.#isProcessingStorageUpdates = true;
6426
6445
  const storage = this.#storage.getValue();
6427
- const balances = assign({}, storage.balances,
6428
- // delete all balances expected in the result set. because if they are not present it means they are empty.
6429
- fromPairs(balanceIds.map(balanceId => [balanceId, undefined])), keyBy(
6430
- // storage balances must have status "cache", because they are used as start value when initialising subsequent subscriptions
6431
- balancesResult.balances.map(b => ({
6432
- ...b,
6433
- status: "cache"
6434
- })), b => getBalanceId(b)));
6435
- this.#storage.next(assign({}, storage, {
6436
- balances
6437
- }));
6446
+ const balances = {
6447
+ ...storage.balances
6448
+ };
6449
+ const miniMetadatas = {
6450
+ ...storage.miniMetadatas
6451
+ };
6452
+ while (this.#updateStorageQueue.length) {
6453
+ const queueItem = this.#updateStorageQueue.shift();
6454
+ switch (queueItem?.type) {
6455
+ case "miniMetadatas":
6456
+ {
6457
+ const {
6458
+ networkId,
6459
+ miniMetadatas: newMiniMetadatas
6460
+ } = queueItem;
6461
+
6462
+ // remove old miniMetadatas for the network
6463
+ for (const miniMetadata of values(miniMetadatas)) if (miniMetadata.chainId === networkId) delete miniMetadatas[miniMetadata.id];
6464
+
6465
+ // add new ones
6466
+ for (const miniMetadata of newMiniMetadatas) miniMetadatas[miniMetadata.id] = miniMetadata;
6467
+ break;
6468
+ }
6469
+ case "balances":
6470
+ {
6471
+ const {
6472
+ balanceIds,
6473
+ balances: newBalances
6474
+ } = queueItem;
6475
+
6476
+ // remove all balances expected in the result set
6477
+ for (const balanceId of balanceIds) delete balances[balanceId];
6478
+
6479
+ // add new ones
6480
+ for (const balance of newBalances) balances[getBalanceId(balance)] = balance;
6481
+ break;
6482
+ }
6483
+ }
6484
+ this.#storage.next({
6485
+ balances,
6486
+ miniMetadatas
6487
+ });
6488
+ this.#isProcessingStorageUpdates = false;
6489
+ }
6438
6490
  }
6439
6491
  getNetworkMiniMetadatas$(networkId) {
6440
6492
  return getSharedObservable(`BalancesProvider.getNetworkMiniMetadatas$`, {
@@ -6486,15 +6538,7 @@ class BalancesProvider {
6486
6538
  // and persist in storage for later reuse
6487
6539
  tap(newMiniMetadatas => {
6488
6540
  if (!newMiniMetadatas.length) return;
6489
- const storage = this.#storage.getValue();
6490
- const miniMetadatas = assign(
6491
- // keep minimetadatas of other networks
6492
- keyBy(values(storage.miniMetadatas).filter(m => m.chainId !== networkId), m => m.id),
6493
- // add the ones for our network
6494
- keyBy(newMiniMetadatas, m => m.id));
6495
- this.#storage.next(assign({}, storage, {
6496
- miniMetadatas
6497
- }));
6541
+ this.updateStoredMiniMetadatas(networkId, newMiniMetadatas);
6498
6542
  }));
6499
6543
  }),
6500
6544
  // emit only when mini metadata changes, as a change here would restart all subscriptions for the network
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/balances",
3
- "version": "0.0.0-pr2092-20250715131755",
3
+ "version": "0.0.0-pr2093-20250715143904",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "GPL-3.0-or-later",
@@ -34,13 +34,13 @@
34
34
  "scale-ts": "^1.6.1",
35
35
  "viem": "^2.27.3",
36
36
  "zod": "^3.25.62",
37
- "@talismn/chain-connector": "0.0.0-pr2092-20250715131755",
38
- "@talismn/chain-connector-evm": "0.0.0-pr2092-20250715131755",
39
- "@talismn/chaindata-provider": "0.0.0-pr2092-20250715131755",
40
- "@talismn/sapi": "0.0.0-pr2092-20250715131755",
41
- "@talismn/token-rates": "0.0.0-pr2092-20250715131755",
42
- "@talismn/util": "0.0.0-pr2092-20250715131755",
43
- "@talismn/scale": "0.0.0-pr2092-20250715131755"
37
+ "@talismn/chain-connector": "0.0.0-pr2093-20250715143904",
38
+ "@talismn/chain-connector-evm": "0.0.0-pr2093-20250715143904",
39
+ "@talismn/chaindata-provider": "0.0.0-pr2093-20250715143904",
40
+ "@talismn/sapi": "0.0.0-pr2093-20250715143904",
41
+ "@talismn/scale": "0.0.0-pr2093-20250715143904",
42
+ "@talismn/token-rates": "0.0.0-pr2093-20250715143904",
43
+ "@talismn/util": "0.0.0-pr2093-20250715143904"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@polkadot/api-contract": "16.1.2",
@@ -55,8 +55,8 @@
55
55
  "jest": "^29.7.0",
56
56
  "ts-jest": "^29.2.5",
57
57
  "typescript": "^5.6.3",
58
- "@talismn/tsconfig": "0.0.2",
59
- "@talismn/eslint-config": "0.0.3"
58
+ "@talismn/eslint-config": "0.0.3",
59
+ "@talismn/tsconfig": "0.0.2"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "@polkadot/api-contract": "*",