@talismn/balances 0.0.0-pr2075-20250714011912 → 0.0.0-pr2075-20250714084622
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.
@@ -19,6 +19,9 @@ export declare class BalancesProvider {
|
|
19
19
|
getBalances$(addressesByTokenId: Record<TokenId, Address[]>): Observable<BalancesResult>;
|
20
20
|
fetchBalances(addressesByTokenId: Record<TokenId, Address[]>): Promise<IBalance[]>;
|
21
21
|
private getNetworkBalances$;
|
22
|
+
private getPolkadotNetworkModuleBalances$;
|
23
|
+
private getEthereumNetworkModuleBalances$;
|
24
|
+
private updateStorage$;
|
22
25
|
private getNetworkMiniMetadatas$;
|
23
26
|
private getNetworkSpecVersion$;
|
24
27
|
private getMiniMetadatas$;
|
@@ -6296,7 +6296,6 @@ class BalancesProvider {
|
|
6296
6296
|
})), rxjs.distinctUntilChanged(lodashEs.isEqual));
|
6297
6297
|
}
|
6298
6298
|
fetchBalances(addressesByTokenId) {
|
6299
|
-
// TODO: better
|
6300
6299
|
return rxjs.firstValueFrom(this.getBalances$(addressesByTokenId).pipe(rxjs.filter(({
|
6301
6300
|
status
|
6302
6301
|
}) => status === "live"), rxjs.map(({
|
@@ -6304,103 +6303,156 @@ class BalancesProvider {
|
|
6304
6303
|
}) => balances)));
|
6305
6304
|
}
|
6306
6305
|
getNetworkBalances$(networkId, addressesByTokenId) {
|
6307
|
-
|
6306
|
+
const network$ = this.#chaindataProvider.getNetworkById$(networkId);
|
6307
|
+
const tokensMapById$ = this.#chaindataProvider.getTokensMapById$();
|
6308
|
+
const networkBalances$ = rxjs.combineLatest([network$, tokensMapById$]).pipe(rxjs.switchMap(([network, tokensMapById]) => {
|
6309
|
+
const tokensAndAddresses = lodashEs.toPairs(addressesByTokenId).map(([tokenId, addresses]) => [tokensMapById[tokenId], addresses]);
|
6310
|
+
return rxjs.combineLatest(BALANCE_MODULES.filter(mod => mod.platform === network?.platform).map(mod => {
|
6311
|
+
const tokensWithAddresses = tokensAndAddresses.filter(([token]) => token.type === mod.type);
|
6312
|
+
switch (mod.platform) {
|
6313
|
+
case "ethereum":
|
6314
|
+
{
|
6315
|
+
return this.getEthereumNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6316
|
+
}
|
6317
|
+
case "polkadot":
|
6318
|
+
{
|
6319
|
+
return this.getPolkadotNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6320
|
+
}
|
6321
|
+
}
|
6322
|
+
}));
|
6323
|
+
}), rxjs.map(results => {
|
6324
|
+
return {
|
6325
|
+
status: results.some(({
|
6326
|
+
status
|
6327
|
+
}) => status === "initialising") ? "initialising" : "live",
|
6328
|
+
balances: results.flatMap(result => result.balances).sort(sortByBalanceId)
|
6329
|
+
};
|
6330
|
+
}), rxjs.distinctUntilChanged(lodashEs.isEqual));
|
6331
|
+
|
6332
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6333
|
+
return rxjs.defer(() => networkBalances$.pipe(rxjs.startWith({
|
6334
|
+
status: "initialising",
|
6335
|
+
balances: this.getStoredBalances(addressesByTokenId)
|
6336
|
+
})));
|
6337
|
+
}
|
6338
|
+
getPolkadotNetworkModuleBalances$(networkId, tokensWithAddresses, mod) {
|
6339
|
+
return util.getSharedObservable(`BalancesProvider.getPolkadotNetworkModuleBalances$`, {
|
6308
6340
|
networkId,
|
6309
|
-
|
6341
|
+
mod,
|
6342
|
+
tokensWithAddresses
|
6310
6343
|
}, () => {
|
6311
|
-
|
6312
|
-
|
6313
|
-
|
6314
|
-
|
6315
|
-
|
6316
|
-
return rxjs.combineLatest(BALANCE_MODULES.filter(mod => mod.platform === network?.platform).map(mod => {
|
6317
|
-
const tokensWithAddresses = tokensAndAddresses.filter(([token]) => token.type === mod.type);
|
6318
|
-
const moduleAddressesByTokenId = lodashEs.fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6319
|
-
const miniMetadata = miniMetadatas.find(m => m.source === mod.type);
|
6320
|
-
|
6321
|
-
// all balance ids expected in result set
|
6322
|
-
const balanceIds = lodashEs.toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6323
|
-
tokenId,
|
6324
|
-
address
|
6325
|
-
})));
|
6326
|
-
const initValue = {
|
6327
|
-
status: "initialising",
|
6328
|
-
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6329
|
-
};
|
6344
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
6345
|
+
status: "live",
|
6346
|
+
balances: []
|
6347
|
+
});
|
6348
|
+
const moduleAddressesByTokenId = lodashEs.fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6330
6349
|
|
6331
|
-
|
6332
|
-
|
6333
|
-
|
6334
|
-
|
6335
|
-
|
6336
|
-
|
6337
|
-
|
6338
|
-
|
6339
|
-
|
6340
|
-
|
6341
|
-
status: "cache"
|
6342
|
-
})), b => getBalanceId(b)));
|
6343
|
-
this.#storage.next(lodashEs.assign({}, storage, {
|
6344
|
-
balances
|
6345
|
-
}));
|
6346
|
-
};
|
6347
|
-
switch (mod.platform) {
|
6348
|
-
case "ethereum":
|
6349
|
-
{
|
6350
|
-
if (!this.#chainConnectors.evm) return rxjs.of(initValue);
|
6351
|
-
return mod.subscribeBalances({
|
6352
|
-
networkId,
|
6353
|
-
tokensWithAddresses,
|
6354
|
-
connector: this.#chainConnectors.evm
|
6355
|
-
}).pipe(rxjs.catchError(() => rxjs.EMPTY),
|
6356
|
-
// don't emit, let provider mark balances stale
|
6357
|
-
rxjs.map(results => ({
|
6358
|
-
status: "live",
|
6359
|
-
// exclude zero balances
|
6360
|
-
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6361
|
-
})), rxjs.tap(updateStorage), rxjs.startWith(initValue));
|
6362
|
-
}
|
6363
|
-
case "polkadot":
|
6364
|
-
if (!this.#chainConnectors.substrate || !miniMetadata) {
|
6365
|
-
log.debug("[balances] no substrate connector or miniMetadata for polkadot", mod.type);
|
6366
|
-
return rxjs.of(initValue);
|
6367
|
-
}
|
6368
|
-
return mod.subscribeBalances({
|
6369
|
-
networkId,
|
6370
|
-
tokensWithAddresses,
|
6371
|
-
connector: this.#chainConnectors.substrate,
|
6372
|
-
miniMetadata: miniMetadata
|
6373
|
-
}).pipe(rxjs.catchError(() => rxjs.EMPTY),
|
6374
|
-
// don't emit, let provider mark balances stale
|
6375
|
-
rxjs.map(results => ({
|
6376
|
-
status: "live",
|
6377
|
-
// exclude zero balances
|
6378
|
-
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6379
|
-
})), rxjs.tap(updateStorage), rxjs.startWith(initValue));
|
6380
|
-
}
|
6350
|
+
// all balance ids expected in result set
|
6351
|
+
const balanceIds = lodashEs.toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6352
|
+
tokenId,
|
6353
|
+
address
|
6354
|
+
})));
|
6355
|
+
if (!this.#chainConnectors.substrate) {
|
6356
|
+
log.debug("[balances] no substrate connector or miniMetadata for module", mod.type);
|
6357
|
+
return rxjs.defer(() => rxjs.of({
|
6358
|
+
status: "initialising",
|
6359
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6381
6360
|
}));
|
6382
|
-
}
|
6383
|
-
|
6384
|
-
|
6385
|
-
|
6386
|
-
|
6387
|
-
|
6388
|
-
|
6389
|
-
|
6361
|
+
}
|
6362
|
+
const moduleBalances$ = this.getNetworkMiniMetadatas$(networkId).pipe(rxjs.map(miniMetadatas => miniMetadatas.find(m => m.source === mod.type)), rxjs.switchMap(miniMetadata => mod.subscribeBalances({
|
6363
|
+
networkId,
|
6364
|
+
tokensWithAddresses,
|
6365
|
+
connector: this.#chainConnectors.substrate,
|
6366
|
+
miniMetadata: miniMetadata
|
6367
|
+
})), rxjs.catchError(() => rxjs.EMPTY),
|
6368
|
+
// don't emit, let provider mark balances stale
|
6369
|
+
rxjs.map(results => ({
|
6370
|
+
status: "live",
|
6371
|
+
// exclude zero balances
|
6372
|
+
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6373
|
+
})), rxjs.tap(results => {
|
6374
|
+
this.updateStorage$(balanceIds, results);
|
6375
|
+
}), rxjs.shareReplay({
|
6376
|
+
refCount: true,
|
6377
|
+
bufferSize: 1
|
6378
|
+
}), util.keepAlive(0));
|
6379
|
+
|
6380
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6381
|
+
return rxjs.defer(() => moduleBalances$.pipe(rxjs.startWith({
|
6390
6382
|
status: "initialising",
|
6391
|
-
balances: this.getStoredBalances(
|
6392
|
-
})
|
6393
|
-
|
6394
|
-
|
6395
|
-
|
6396
|
-
|
6383
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6384
|
+
})));
|
6385
|
+
});
|
6386
|
+
}
|
6387
|
+
getEthereumNetworkModuleBalances$(networkId, tokensWithAddresses, mod) {
|
6388
|
+
return util.getSharedObservable(`BalancesProvider.getEthereumNetworkModuleBalances$`, {
|
6389
|
+
networkId,
|
6390
|
+
mod,
|
6391
|
+
tokensWithAddresses
|
6392
|
+
}, () => {
|
6393
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
6394
|
+
status: "live",
|
6395
|
+
balances: []
|
6396
|
+
});
|
6397
|
+
const moduleAddressesByTokenId = lodashEs.fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6398
|
+
|
6399
|
+
// all balance ids expected in result set
|
6400
|
+
const balanceIds = lodashEs.toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6401
|
+
tokenId,
|
6402
|
+
address
|
6403
|
+
})));
|
6404
|
+
if (!this.#chainConnectors.evm) {
|
6405
|
+
log.debug("[balances] no ethereum connector for module", mod.type);
|
6406
|
+
return rxjs.defer(() => rxjs.of({
|
6407
|
+
status: "initialising",
|
6408
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6409
|
+
}));
|
6410
|
+
}
|
6411
|
+
const moduleBalances$ = mod.subscribeBalances({
|
6412
|
+
networkId,
|
6413
|
+
tokensWithAddresses,
|
6414
|
+
connector: this.#chainConnectors.evm
|
6415
|
+
}).pipe(rxjs.catchError(() => rxjs.EMPTY),
|
6416
|
+
// don't emit, let provider mark balances stale
|
6417
|
+
rxjs.map(results => ({
|
6418
|
+
status: "live",
|
6419
|
+
// exclude zero balances
|
6420
|
+
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6421
|
+
})), rxjs.tap(results => {
|
6422
|
+
this.updateStorage$(balanceIds, results);
|
6423
|
+
}), rxjs.shareReplay({
|
6397
6424
|
refCount: true,
|
6398
6425
|
bufferSize: 1
|
6399
6426
|
}), util.keepAlive(0));
|
6427
|
+
|
6428
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6429
|
+
return rxjs.defer(() => moduleBalances$.pipe(rxjs.startWith({
|
6430
|
+
status: "initialising",
|
6431
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6432
|
+
})));
|
6400
6433
|
});
|
6401
6434
|
}
|
6435
|
+
updateStorage$(balanceIds, balancesResult) {
|
6436
|
+
if (balancesResult.status !== "live") return;
|
6437
|
+
const storage = this.#storage.getValue();
|
6438
|
+
const balances = lodashEs.assign({}, storage.balances,
|
6439
|
+
// delete all balances expected in the result set. because if they are not present it means they are empty.
|
6440
|
+
lodashEs.fromPairs(balanceIds.map(balanceId => [balanceId, undefined])), lodashEs.keyBy(
|
6441
|
+
// storage balances must have status "cache", because they are used as start value when initialising subsequent subscriptions
|
6442
|
+
balancesResult.balances.map(b => ({
|
6443
|
+
...b,
|
6444
|
+
status: "cache"
|
6445
|
+
})), b => getBalanceId(b)));
|
6446
|
+
this.#storage.next(lodashEs.assign({}, storage, {
|
6447
|
+
balances
|
6448
|
+
}));
|
6449
|
+
}
|
6402
6450
|
getNetworkMiniMetadatas$(networkId) {
|
6403
|
-
return
|
6451
|
+
return util.getSharedObservable(`BalancesProvider.getNetworkMiniMetadatas$`, {
|
6452
|
+
networkId
|
6453
|
+
}, () => {
|
6454
|
+
return this.#chaindataProvider.getNetworkById$(networkId).pipe(rxjs.switchMap(network => chaindataProvider.isNetworkDot(network) ? this.getNetworkSpecVersion$(networkId).pipe(rxjs.switchMap(specVersion => specVersion === null ? rxjs.of([]) : this.getMiniMetadatas$(networkId, specVersion))) : rxjs.of([])), rxjs.distinctUntilChanged(lodashEs.isEqual));
|
6455
|
+
});
|
6404
6456
|
}
|
6405
6457
|
getNetworkSpecVersion$(networkId) {
|
6406
6458
|
return rxjs.from(viem.withRetry(() => getSpecVersion(this.#chainConnectors.substrate, networkId), {
|
@@ -6296,7 +6296,6 @@ class BalancesProvider {
|
|
6296
6296
|
})), rxjs.distinctUntilChanged(lodashEs.isEqual));
|
6297
6297
|
}
|
6298
6298
|
fetchBalances(addressesByTokenId) {
|
6299
|
-
// TODO: better
|
6300
6299
|
return rxjs.firstValueFrom(this.getBalances$(addressesByTokenId).pipe(rxjs.filter(({
|
6301
6300
|
status
|
6302
6301
|
}) => status === "live"), rxjs.map(({
|
@@ -6304,103 +6303,156 @@ class BalancesProvider {
|
|
6304
6303
|
}) => balances)));
|
6305
6304
|
}
|
6306
6305
|
getNetworkBalances$(networkId, addressesByTokenId) {
|
6307
|
-
|
6306
|
+
const network$ = this.#chaindataProvider.getNetworkById$(networkId);
|
6307
|
+
const tokensMapById$ = this.#chaindataProvider.getTokensMapById$();
|
6308
|
+
const networkBalances$ = rxjs.combineLatest([network$, tokensMapById$]).pipe(rxjs.switchMap(([network, tokensMapById]) => {
|
6309
|
+
const tokensAndAddresses = lodashEs.toPairs(addressesByTokenId).map(([tokenId, addresses]) => [tokensMapById[tokenId], addresses]);
|
6310
|
+
return rxjs.combineLatest(BALANCE_MODULES.filter(mod => mod.platform === network?.platform).map(mod => {
|
6311
|
+
const tokensWithAddresses = tokensAndAddresses.filter(([token]) => token.type === mod.type);
|
6312
|
+
switch (mod.platform) {
|
6313
|
+
case "ethereum":
|
6314
|
+
{
|
6315
|
+
return this.getEthereumNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6316
|
+
}
|
6317
|
+
case "polkadot":
|
6318
|
+
{
|
6319
|
+
return this.getPolkadotNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6320
|
+
}
|
6321
|
+
}
|
6322
|
+
}));
|
6323
|
+
}), rxjs.map(results => {
|
6324
|
+
return {
|
6325
|
+
status: results.some(({
|
6326
|
+
status
|
6327
|
+
}) => status === "initialising") ? "initialising" : "live",
|
6328
|
+
balances: results.flatMap(result => result.balances).sort(sortByBalanceId)
|
6329
|
+
};
|
6330
|
+
}), rxjs.distinctUntilChanged(lodashEs.isEqual));
|
6331
|
+
|
6332
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6333
|
+
return rxjs.defer(() => networkBalances$.pipe(rxjs.startWith({
|
6334
|
+
status: "initialising",
|
6335
|
+
balances: this.getStoredBalances(addressesByTokenId)
|
6336
|
+
})));
|
6337
|
+
}
|
6338
|
+
getPolkadotNetworkModuleBalances$(networkId, tokensWithAddresses, mod) {
|
6339
|
+
return util.getSharedObservable(`BalancesProvider.getPolkadotNetworkModuleBalances$`, {
|
6308
6340
|
networkId,
|
6309
|
-
|
6341
|
+
mod,
|
6342
|
+
tokensWithAddresses
|
6310
6343
|
}, () => {
|
6311
|
-
|
6312
|
-
|
6313
|
-
|
6314
|
-
|
6315
|
-
|
6316
|
-
return rxjs.combineLatest(BALANCE_MODULES.filter(mod => mod.platform === network?.platform).map(mod => {
|
6317
|
-
const tokensWithAddresses = tokensAndAddresses.filter(([token]) => token.type === mod.type);
|
6318
|
-
const moduleAddressesByTokenId = lodashEs.fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6319
|
-
const miniMetadata = miniMetadatas.find(m => m.source === mod.type);
|
6320
|
-
|
6321
|
-
// all balance ids expected in result set
|
6322
|
-
const balanceIds = lodashEs.toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6323
|
-
tokenId,
|
6324
|
-
address
|
6325
|
-
})));
|
6326
|
-
const initValue = {
|
6327
|
-
status: "initialising",
|
6328
|
-
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6329
|
-
};
|
6344
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
6345
|
+
status: "live",
|
6346
|
+
balances: []
|
6347
|
+
});
|
6348
|
+
const moduleAddressesByTokenId = lodashEs.fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6330
6349
|
|
6331
|
-
|
6332
|
-
|
6333
|
-
|
6334
|
-
|
6335
|
-
|
6336
|
-
|
6337
|
-
|
6338
|
-
|
6339
|
-
|
6340
|
-
|
6341
|
-
status: "cache"
|
6342
|
-
})), b => getBalanceId(b)));
|
6343
|
-
this.#storage.next(lodashEs.assign({}, storage, {
|
6344
|
-
balances
|
6345
|
-
}));
|
6346
|
-
};
|
6347
|
-
switch (mod.platform) {
|
6348
|
-
case "ethereum":
|
6349
|
-
{
|
6350
|
-
if (!this.#chainConnectors.evm) return rxjs.of(initValue);
|
6351
|
-
return mod.subscribeBalances({
|
6352
|
-
networkId,
|
6353
|
-
tokensWithAddresses,
|
6354
|
-
connector: this.#chainConnectors.evm
|
6355
|
-
}).pipe(rxjs.catchError(() => rxjs.EMPTY),
|
6356
|
-
// don't emit, let provider mark balances stale
|
6357
|
-
rxjs.map(results => ({
|
6358
|
-
status: "live",
|
6359
|
-
// exclude zero balances
|
6360
|
-
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6361
|
-
})), rxjs.tap(updateStorage), rxjs.startWith(initValue));
|
6362
|
-
}
|
6363
|
-
case "polkadot":
|
6364
|
-
if (!this.#chainConnectors.substrate || !miniMetadata) {
|
6365
|
-
log.debug("[balances] no substrate connector or miniMetadata for polkadot", mod.type);
|
6366
|
-
return rxjs.of(initValue);
|
6367
|
-
}
|
6368
|
-
return mod.subscribeBalances({
|
6369
|
-
networkId,
|
6370
|
-
tokensWithAddresses,
|
6371
|
-
connector: this.#chainConnectors.substrate,
|
6372
|
-
miniMetadata: miniMetadata
|
6373
|
-
}).pipe(rxjs.catchError(() => rxjs.EMPTY),
|
6374
|
-
// don't emit, let provider mark balances stale
|
6375
|
-
rxjs.map(results => ({
|
6376
|
-
status: "live",
|
6377
|
-
// exclude zero balances
|
6378
|
-
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6379
|
-
})), rxjs.tap(updateStorage), rxjs.startWith(initValue));
|
6380
|
-
}
|
6350
|
+
// all balance ids expected in result set
|
6351
|
+
const balanceIds = lodashEs.toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6352
|
+
tokenId,
|
6353
|
+
address
|
6354
|
+
})));
|
6355
|
+
if (!this.#chainConnectors.substrate) {
|
6356
|
+
log.debug("[balances] no substrate connector or miniMetadata for module", mod.type);
|
6357
|
+
return rxjs.defer(() => rxjs.of({
|
6358
|
+
status: "initialising",
|
6359
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6381
6360
|
}));
|
6382
|
-
}
|
6383
|
-
|
6384
|
-
|
6385
|
-
|
6386
|
-
|
6387
|
-
|
6388
|
-
|
6389
|
-
|
6361
|
+
}
|
6362
|
+
const moduleBalances$ = this.getNetworkMiniMetadatas$(networkId).pipe(rxjs.map(miniMetadatas => miniMetadatas.find(m => m.source === mod.type)), rxjs.switchMap(miniMetadata => mod.subscribeBalances({
|
6363
|
+
networkId,
|
6364
|
+
tokensWithAddresses,
|
6365
|
+
connector: this.#chainConnectors.substrate,
|
6366
|
+
miniMetadata: miniMetadata
|
6367
|
+
})), rxjs.catchError(() => rxjs.EMPTY),
|
6368
|
+
// don't emit, let provider mark balances stale
|
6369
|
+
rxjs.map(results => ({
|
6370
|
+
status: "live",
|
6371
|
+
// exclude zero balances
|
6372
|
+
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6373
|
+
})), rxjs.tap(results => {
|
6374
|
+
this.updateStorage$(balanceIds, results);
|
6375
|
+
}), rxjs.shareReplay({
|
6376
|
+
refCount: true,
|
6377
|
+
bufferSize: 1
|
6378
|
+
}), util.keepAlive(0));
|
6379
|
+
|
6380
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6381
|
+
return rxjs.defer(() => moduleBalances$.pipe(rxjs.startWith({
|
6390
6382
|
status: "initialising",
|
6391
|
-
balances: this.getStoredBalances(
|
6392
|
-
})
|
6393
|
-
|
6394
|
-
|
6395
|
-
|
6396
|
-
|
6383
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6384
|
+
})));
|
6385
|
+
});
|
6386
|
+
}
|
6387
|
+
getEthereumNetworkModuleBalances$(networkId, tokensWithAddresses, mod) {
|
6388
|
+
return util.getSharedObservable(`BalancesProvider.getEthereumNetworkModuleBalances$`, {
|
6389
|
+
networkId,
|
6390
|
+
mod,
|
6391
|
+
tokensWithAddresses
|
6392
|
+
}, () => {
|
6393
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
6394
|
+
status: "live",
|
6395
|
+
balances: []
|
6396
|
+
});
|
6397
|
+
const moduleAddressesByTokenId = lodashEs.fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6398
|
+
|
6399
|
+
// all balance ids expected in result set
|
6400
|
+
const balanceIds = lodashEs.toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6401
|
+
tokenId,
|
6402
|
+
address
|
6403
|
+
})));
|
6404
|
+
if (!this.#chainConnectors.evm) {
|
6405
|
+
log.debug("[balances] no ethereum connector for module", mod.type);
|
6406
|
+
return rxjs.defer(() => rxjs.of({
|
6407
|
+
status: "initialising",
|
6408
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6409
|
+
}));
|
6410
|
+
}
|
6411
|
+
const moduleBalances$ = mod.subscribeBalances({
|
6412
|
+
networkId,
|
6413
|
+
tokensWithAddresses,
|
6414
|
+
connector: this.#chainConnectors.evm
|
6415
|
+
}).pipe(rxjs.catchError(() => rxjs.EMPTY),
|
6416
|
+
// don't emit, let provider mark balances stale
|
6417
|
+
rxjs.map(results => ({
|
6418
|
+
status: "live",
|
6419
|
+
// exclude zero balances
|
6420
|
+
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6421
|
+
})), rxjs.tap(results => {
|
6422
|
+
this.updateStorage$(balanceIds, results);
|
6423
|
+
}), rxjs.shareReplay({
|
6397
6424
|
refCount: true,
|
6398
6425
|
bufferSize: 1
|
6399
6426
|
}), util.keepAlive(0));
|
6427
|
+
|
6428
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6429
|
+
return rxjs.defer(() => moduleBalances$.pipe(rxjs.startWith({
|
6430
|
+
status: "initialising",
|
6431
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6432
|
+
})));
|
6400
6433
|
});
|
6401
6434
|
}
|
6435
|
+
updateStorage$(balanceIds, balancesResult) {
|
6436
|
+
if (balancesResult.status !== "live") return;
|
6437
|
+
const storage = this.#storage.getValue();
|
6438
|
+
const balances = lodashEs.assign({}, storage.balances,
|
6439
|
+
// delete all balances expected in the result set. because if they are not present it means they are empty.
|
6440
|
+
lodashEs.fromPairs(balanceIds.map(balanceId => [balanceId, undefined])), lodashEs.keyBy(
|
6441
|
+
// storage balances must have status "cache", because they are used as start value when initialising subsequent subscriptions
|
6442
|
+
balancesResult.balances.map(b => ({
|
6443
|
+
...b,
|
6444
|
+
status: "cache"
|
6445
|
+
})), b => getBalanceId(b)));
|
6446
|
+
this.#storage.next(lodashEs.assign({}, storage, {
|
6447
|
+
balances
|
6448
|
+
}));
|
6449
|
+
}
|
6402
6450
|
getNetworkMiniMetadatas$(networkId) {
|
6403
|
-
return
|
6451
|
+
return util.getSharedObservable(`BalancesProvider.getNetworkMiniMetadatas$`, {
|
6452
|
+
networkId
|
6453
|
+
}, () => {
|
6454
|
+
return this.#chaindataProvider.getNetworkById$(networkId).pipe(rxjs.switchMap(network => chaindataProvider.isNetworkDot(network) ? this.getNetworkSpecVersion$(networkId).pipe(rxjs.switchMap(specVersion => specVersion === null ? rxjs.of([]) : this.getMiniMetadatas$(networkId, specVersion))) : rxjs.of([])), rxjs.distinctUntilChanged(lodashEs.isEqual));
|
6455
|
+
});
|
6404
6456
|
}
|
6405
6457
|
getNetworkSpecVersion$(networkId) {
|
6406
6458
|
return rxjs.from(viem.withRetry(() => getSpecVersion(this.#chainConnectors.substrate, networkId), {
|
@@ -5,7 +5,7 @@ import { parseAbi, erc20Abi, getContract, ContractFunctionExecutionError, hexToS
|
|
5
5
|
import { assign, omit, isEqual, uniq, keyBy, keys, fromPairs, values, toPairs } from 'lodash-es';
|
6
6
|
import z from 'zod/v4';
|
7
7
|
import anylogger from 'anylogger';
|
8
|
-
import { of, Observable, distinctUntilChanged, map, timer, switchMap, from, firstValueFrom, combineLatest, BehaviorSubject, shareReplay, startWith, filter, catchError, EMPTY, tap } from 'rxjs';
|
8
|
+
import { of, Observable, distinctUntilChanged, map, timer, switchMap, from, firstValueFrom, combineLatest, BehaviorSubject, shareReplay, startWith, filter, defer, catchError, EMPTY, tap } from 'rxjs';
|
9
9
|
import BigNumber from 'bignumber.js';
|
10
10
|
import { parseMetadataRpc, toHex, unifyMetadata, decAnyMetadata, getDynamicBuilder, getLookupFn, decodeScale, getStorageKeyPrefix, Twox128, compactMetadata, encodeMetadata, papiParse, papiStringify } from '@talismn/scale';
|
11
11
|
import { newTokenRates } from '@talismn/token-rates';
|
@@ -6287,7 +6287,6 @@ class BalancesProvider {
|
|
6287
6287
|
})), distinctUntilChanged(isEqual));
|
6288
6288
|
}
|
6289
6289
|
fetchBalances(addressesByTokenId) {
|
6290
|
-
// TODO: better
|
6291
6290
|
return firstValueFrom(this.getBalances$(addressesByTokenId).pipe(filter(({
|
6292
6291
|
status
|
6293
6292
|
}) => status === "live"), map(({
|
@@ -6295,103 +6294,156 @@ class BalancesProvider {
|
|
6295
6294
|
}) => balances)));
|
6296
6295
|
}
|
6297
6296
|
getNetworkBalances$(networkId, addressesByTokenId) {
|
6298
|
-
|
6297
|
+
const network$ = this.#chaindataProvider.getNetworkById$(networkId);
|
6298
|
+
const tokensMapById$ = this.#chaindataProvider.getTokensMapById$();
|
6299
|
+
const networkBalances$ = combineLatest([network$, tokensMapById$]).pipe(switchMap(([network, tokensMapById]) => {
|
6300
|
+
const tokensAndAddresses = toPairs(addressesByTokenId).map(([tokenId, addresses]) => [tokensMapById[tokenId], addresses]);
|
6301
|
+
return combineLatest(BALANCE_MODULES.filter(mod => mod.platform === network?.platform).map(mod => {
|
6302
|
+
const tokensWithAddresses = tokensAndAddresses.filter(([token]) => token.type === mod.type);
|
6303
|
+
switch (mod.platform) {
|
6304
|
+
case "ethereum":
|
6305
|
+
{
|
6306
|
+
return this.getEthereumNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6307
|
+
}
|
6308
|
+
case "polkadot":
|
6309
|
+
{
|
6310
|
+
return this.getPolkadotNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6311
|
+
}
|
6312
|
+
}
|
6313
|
+
}));
|
6314
|
+
}), map(results => {
|
6315
|
+
return {
|
6316
|
+
status: results.some(({
|
6317
|
+
status
|
6318
|
+
}) => status === "initialising") ? "initialising" : "live",
|
6319
|
+
balances: results.flatMap(result => result.balances).sort(sortByBalanceId)
|
6320
|
+
};
|
6321
|
+
}), distinctUntilChanged(isEqual));
|
6322
|
+
|
6323
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6324
|
+
return defer(() => networkBalances$.pipe(startWith({
|
6325
|
+
status: "initialising",
|
6326
|
+
balances: this.getStoredBalances(addressesByTokenId)
|
6327
|
+
})));
|
6328
|
+
}
|
6329
|
+
getPolkadotNetworkModuleBalances$(networkId, tokensWithAddresses, mod) {
|
6330
|
+
return getSharedObservable(`BalancesProvider.getPolkadotNetworkModuleBalances$`, {
|
6299
6331
|
networkId,
|
6300
|
-
|
6332
|
+
mod,
|
6333
|
+
tokensWithAddresses
|
6301
6334
|
}, () => {
|
6302
|
-
|
6303
|
-
|
6304
|
-
|
6305
|
-
|
6306
|
-
|
6307
|
-
return combineLatest(BALANCE_MODULES.filter(mod => mod.platform === network?.platform).map(mod => {
|
6308
|
-
const tokensWithAddresses = tokensAndAddresses.filter(([token]) => token.type === mod.type);
|
6309
|
-
const moduleAddressesByTokenId = fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6310
|
-
const miniMetadata = miniMetadatas.find(m => m.source === mod.type);
|
6311
|
-
|
6312
|
-
// all balance ids expected in result set
|
6313
|
-
const balanceIds = toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6314
|
-
tokenId,
|
6315
|
-
address
|
6316
|
-
})));
|
6317
|
-
const initValue = {
|
6318
|
-
status: "initialising",
|
6319
|
-
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6320
|
-
};
|
6335
|
+
if (!tokensWithAddresses.length) return of({
|
6336
|
+
status: "live",
|
6337
|
+
balances: []
|
6338
|
+
});
|
6339
|
+
const moduleAddressesByTokenId = fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6321
6340
|
|
6322
|
-
|
6323
|
-
|
6324
|
-
|
6325
|
-
|
6326
|
-
|
6327
|
-
|
6328
|
-
|
6329
|
-
|
6330
|
-
|
6331
|
-
|
6332
|
-
status: "cache"
|
6333
|
-
})), b => getBalanceId(b)));
|
6334
|
-
this.#storage.next(assign({}, storage, {
|
6335
|
-
balances
|
6336
|
-
}));
|
6337
|
-
};
|
6338
|
-
switch (mod.platform) {
|
6339
|
-
case "ethereum":
|
6340
|
-
{
|
6341
|
-
if (!this.#chainConnectors.evm) return of(initValue);
|
6342
|
-
return mod.subscribeBalances({
|
6343
|
-
networkId,
|
6344
|
-
tokensWithAddresses,
|
6345
|
-
connector: this.#chainConnectors.evm
|
6346
|
-
}).pipe(catchError(() => EMPTY),
|
6347
|
-
// don't emit, let provider mark balances stale
|
6348
|
-
map(results => ({
|
6349
|
-
status: "live",
|
6350
|
-
// exclude zero balances
|
6351
|
-
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6352
|
-
})), tap(updateStorage), startWith(initValue));
|
6353
|
-
}
|
6354
|
-
case "polkadot":
|
6355
|
-
if (!this.#chainConnectors.substrate || !miniMetadata) {
|
6356
|
-
log.debug("[balances] no substrate connector or miniMetadata for polkadot", mod.type);
|
6357
|
-
return of(initValue);
|
6358
|
-
}
|
6359
|
-
return mod.subscribeBalances({
|
6360
|
-
networkId,
|
6361
|
-
tokensWithAddresses,
|
6362
|
-
connector: this.#chainConnectors.substrate,
|
6363
|
-
miniMetadata: miniMetadata
|
6364
|
-
}).pipe(catchError(() => EMPTY),
|
6365
|
-
// don't emit, let provider mark balances stale
|
6366
|
-
map(results => ({
|
6367
|
-
status: "live",
|
6368
|
-
// exclude zero balances
|
6369
|
-
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6370
|
-
})), tap(updateStorage), startWith(initValue));
|
6371
|
-
}
|
6341
|
+
// all balance ids expected in result set
|
6342
|
+
const balanceIds = toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6343
|
+
tokenId,
|
6344
|
+
address
|
6345
|
+
})));
|
6346
|
+
if (!this.#chainConnectors.substrate) {
|
6347
|
+
log.debug("[balances] no substrate connector or miniMetadata for module", mod.type);
|
6348
|
+
return defer(() => of({
|
6349
|
+
status: "initialising",
|
6350
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6372
6351
|
}));
|
6373
|
-
}
|
6374
|
-
|
6375
|
-
|
6376
|
-
|
6377
|
-
|
6378
|
-
|
6379
|
-
|
6380
|
-
|
6352
|
+
}
|
6353
|
+
const moduleBalances$ = this.getNetworkMiniMetadatas$(networkId).pipe(map(miniMetadatas => miniMetadatas.find(m => m.source === mod.type)), switchMap(miniMetadata => mod.subscribeBalances({
|
6354
|
+
networkId,
|
6355
|
+
tokensWithAddresses,
|
6356
|
+
connector: this.#chainConnectors.substrate,
|
6357
|
+
miniMetadata: miniMetadata
|
6358
|
+
})), catchError(() => EMPTY),
|
6359
|
+
// don't emit, let provider mark balances stale
|
6360
|
+
map(results => ({
|
6361
|
+
status: "live",
|
6362
|
+
// exclude zero balances
|
6363
|
+
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6364
|
+
})), tap(results => {
|
6365
|
+
this.updateStorage$(balanceIds, results);
|
6366
|
+
}), shareReplay({
|
6367
|
+
refCount: true,
|
6368
|
+
bufferSize: 1
|
6369
|
+
}), keepAlive(0));
|
6370
|
+
|
6371
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6372
|
+
return defer(() => moduleBalances$.pipe(startWith({
|
6381
6373
|
status: "initialising",
|
6382
|
-
balances: this.getStoredBalances(
|
6383
|
-
})
|
6384
|
-
|
6385
|
-
|
6386
|
-
|
6387
|
-
|
6374
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6375
|
+
})));
|
6376
|
+
});
|
6377
|
+
}
|
6378
|
+
getEthereumNetworkModuleBalances$(networkId, tokensWithAddresses, mod) {
|
6379
|
+
return getSharedObservable(`BalancesProvider.getEthereumNetworkModuleBalances$`, {
|
6380
|
+
networkId,
|
6381
|
+
mod,
|
6382
|
+
tokensWithAddresses
|
6383
|
+
}, () => {
|
6384
|
+
if (!tokensWithAddresses.length) return of({
|
6385
|
+
status: "live",
|
6386
|
+
balances: []
|
6387
|
+
});
|
6388
|
+
const moduleAddressesByTokenId = fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6389
|
+
|
6390
|
+
// all balance ids expected in result set
|
6391
|
+
const balanceIds = toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6392
|
+
tokenId,
|
6393
|
+
address
|
6394
|
+
})));
|
6395
|
+
if (!this.#chainConnectors.evm) {
|
6396
|
+
log.debug("[balances] no ethereum connector for module", mod.type);
|
6397
|
+
return defer(() => of({
|
6398
|
+
status: "initialising",
|
6399
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6400
|
+
}));
|
6401
|
+
}
|
6402
|
+
const moduleBalances$ = mod.subscribeBalances({
|
6403
|
+
networkId,
|
6404
|
+
tokensWithAddresses,
|
6405
|
+
connector: this.#chainConnectors.evm
|
6406
|
+
}).pipe(catchError(() => EMPTY),
|
6407
|
+
// don't emit, let provider mark balances stale
|
6408
|
+
map(results => ({
|
6409
|
+
status: "live",
|
6410
|
+
// exclude zero balances
|
6411
|
+
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6412
|
+
})), tap(results => {
|
6413
|
+
this.updateStorage$(balanceIds, results);
|
6414
|
+
}), shareReplay({
|
6388
6415
|
refCount: true,
|
6389
6416
|
bufferSize: 1
|
6390
6417
|
}), keepAlive(0));
|
6418
|
+
|
6419
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6420
|
+
return defer(() => moduleBalances$.pipe(startWith({
|
6421
|
+
status: "initialising",
|
6422
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6423
|
+
})));
|
6391
6424
|
});
|
6392
6425
|
}
|
6426
|
+
updateStorage$(balanceIds, balancesResult) {
|
6427
|
+
if (balancesResult.status !== "live") return;
|
6428
|
+
const storage = this.#storage.getValue();
|
6429
|
+
const balances = assign({}, storage.balances,
|
6430
|
+
// delete all balances expected in the result set. because if they are not present it means they are empty.
|
6431
|
+
fromPairs(balanceIds.map(balanceId => [balanceId, undefined])), keyBy(
|
6432
|
+
// storage balances must have status "cache", because they are used as start value when initialising subsequent subscriptions
|
6433
|
+
balancesResult.balances.map(b => ({
|
6434
|
+
...b,
|
6435
|
+
status: "cache"
|
6436
|
+
})), b => getBalanceId(b)));
|
6437
|
+
this.#storage.next(assign({}, storage, {
|
6438
|
+
balances
|
6439
|
+
}));
|
6440
|
+
}
|
6393
6441
|
getNetworkMiniMetadatas$(networkId) {
|
6394
|
-
return
|
6442
|
+
return getSharedObservable(`BalancesProvider.getNetworkMiniMetadatas$`, {
|
6443
|
+
networkId
|
6444
|
+
}, () => {
|
6445
|
+
return this.#chaindataProvider.getNetworkById$(networkId).pipe(switchMap(network => isNetworkDot(network) ? this.getNetworkSpecVersion$(networkId).pipe(switchMap(specVersion => specVersion === null ? of([]) : this.getMiniMetadatas$(networkId, specVersion))) : of([])), distinctUntilChanged(isEqual));
|
6446
|
+
});
|
6395
6447
|
}
|
6396
6448
|
getNetworkSpecVersion$(networkId) {
|
6397
6449
|
return from(withRetry(() => getSpecVersion(this.#chainConnectors.substrate, networkId), {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@talismn/balances",
|
3
|
-
"version": "0.0.0-pr2075-
|
3
|
+
"version": "0.0.0-pr2075-20250714084622",
|
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-pr2075-
|
38
|
-
"@talismn/
|
39
|
-
"@talismn/chaindata-provider": "0.0.0-pr2075-
|
40
|
-
"@talismn/
|
41
|
-
"@talismn/
|
42
|
-
"@talismn/
|
43
|
-
"@talismn/
|
37
|
+
"@talismn/chain-connector": "0.0.0-pr2075-20250714084622",
|
38
|
+
"@talismn/token-rates": "0.0.0-pr2075-20250714084622",
|
39
|
+
"@talismn/chaindata-provider": "0.0.0-pr2075-20250714084622",
|
40
|
+
"@talismn/chain-connector-evm": "0.0.0-pr2075-20250714084622",
|
41
|
+
"@talismn/sapi": "0.0.0-pr2075-20250714084622",
|
42
|
+
"@talismn/util": "0.0.0-pr2075-20250714084622",
|
43
|
+
"@talismn/scale": "0.0.0-pr2075-20250714084622"
|
44
44
|
},
|
45
45
|
"devDependencies": {
|
46
46
|
"@polkadot/api-contract": "16.1.2",
|