@talismn/balances 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +48 -48
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +19 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +76 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +77 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1694,18 +1694,31 @@ const fetchRpcQueryPack = async (connector, networkId, queries, at) => {
|
|
|
1694
1694
|
return decodeRpcQueryPackChunked(queries, new Map(result.changes), { label: `rpcQueryPack decode ${networkId}` });
|
|
1695
1695
|
};
|
|
1696
1696
|
/**
|
|
1697
|
+
* The connector reports a chain that has been unreachable for a while with a StaleRpcError,
|
|
1698
|
+
* but keeps the subscription registered and re-issues it when a socket reconnects (the
|
|
1699
|
+
* first notification after that is a full snapshot again). Matched by its `type` rather
|
|
1700
|
+
* than `instanceof`, so a second copy of @talismn/chain-connectors still matches.
|
|
1701
|
+
*/
|
|
1702
|
+
const isStaleRpcError = (error) => typeof error === "object" && error !== null && error.type === "STALE_RPC_ERROR";
|
|
1703
|
+
/**
|
|
1697
1704
|
* Wraps the raw state_subscribeStorage subscription and emits, for every storage change
|
|
1698
1705
|
* callback, a SNAPSHOT of the accumulated changes keyed by state key.
|
|
1699
1706
|
*
|
|
1700
1707
|
* A snapshot (shallow copy of string refs, cheap) is required because decoding is chunked
|
|
1701
1708
|
* and asynchronous: a later websocket callback must not mutate the map that an in-flight
|
|
1702
1709
|
* decode is reading.
|
|
1710
|
+
*
|
|
1711
|
+
* A StaleRpcError does not end the stream. Erroring would unsubscribe, and with no
|
|
1712
|
+
* subscription holding the chain's socket, every later request (eg the dtao poll) would
|
|
1713
|
+
* open and close a socket of its own, starting from the network's first RPC each time.
|
|
1703
1714
|
*/
|
|
1704
1715
|
const getRawStorageUpdates$ = (connector, networkId, allStateKeys, timeout) => new rxjs.Observable((subscriber) => {
|
|
1705
1716
|
const changesCache = /* @__PURE__ */ new Map();
|
|
1706
1717
|
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
|
1707
|
-
if (error)
|
|
1708
|
-
|
|
1718
|
+
if (error) {
|
|
1719
|
+
if (isStaleRpcError(error)) log_default.warn(`Storage subscription stale on ${networkId}`);
|
|
1720
|
+
else subscriber.error(error);
|
|
1721
|
+
} else if (result) {
|
|
1709
1722
|
for (const [stateKey, encodedResult] of result.changes) changesCache.set(stateKey, encodedResult);
|
|
1710
1723
|
subscriber.next(new Map(changesCache));
|
|
1711
1724
|
}
|
|
@@ -2734,7 +2747,7 @@ const fetchOnChainTokenData$1 = async (connector, tokenId) => {
|
|
|
2734
2747
|
//#region src/modules/sol-spl/fetchBalances.ts
|
|
2735
2748
|
const SPL_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
|
2736
2749
|
const dynamicTokenMetadataCache$1 = new _talismn_util.LruMap(1024);
|
|
2737
|
-
const associatedTokenAccountCache = new _talismn_util.LruMap(1024);
|
|
2750
|
+
const associatedTokenAccountCache$1 = new _talismn_util.LruMap(1024);
|
|
2738
2751
|
const fetchBalances$8 = async ({ networkId, tokensWithAddresses, connector }) => {
|
|
2739
2752
|
if (!tokensWithAddresses.length) return {
|
|
2740
2753
|
success: [],
|
|
@@ -2754,7 +2767,7 @@ const fetchBalances$8 = async ({ networkId, tokensWithAddresses, connector }) =>
|
|
|
2754
2767
|
return {
|
|
2755
2768
|
mintAddress,
|
|
2756
2769
|
value,
|
|
2757
|
-
isSpendable: value !== 0n && state !== "frozen" && d.pubkey === await getAssociatedTokenAccount(address, mintAddress)
|
|
2770
|
+
isSpendable: value !== 0n && state !== "frozen" && d.pubkey === await getAssociatedTokenAccount$1(address, mintAddress)
|
|
2758
2771
|
};
|
|
2759
2772
|
} catch {
|
|
2760
2773
|
log_default.warn("Failed to parse token amount", {
|
|
@@ -2847,16 +2860,16 @@ const fetchBalances$8 = async ({ networkId, tokensWithAddresses, connector }) =>
|
|
|
2847
2860
|
};
|
|
2848
2861
|
};
|
|
2849
2862
|
const getBalanceKey$1 = (tokenId, address) => `${tokenId}:${address}`;
|
|
2850
|
-
const getAssociatedTokenAccount = async (owner, mint) => {
|
|
2863
|
+
const getAssociatedTokenAccount$1 = async (owner, mint) => {
|
|
2851
2864
|
const key = `${owner}:${mint}`;
|
|
2852
|
-
const cached = associatedTokenAccountCache.get(key);
|
|
2865
|
+
const cached = associatedTokenAccountCache$1.get(key);
|
|
2853
2866
|
if (cached) return cached;
|
|
2854
2867
|
const [associatedTokenAccount] = await (0, _solana_program_token.findAssociatedTokenPda)({
|
|
2855
2868
|
mint: (0, _solana_kit.address)(mint),
|
|
2856
2869
|
owner: (0, _solana_kit.address)(owner),
|
|
2857
2870
|
tokenProgram: _solana_program_token.TOKEN_PROGRAM_ADDRESS
|
|
2858
2871
|
});
|
|
2859
|
-
associatedTokenAccountCache.set(key, associatedTokenAccount);
|
|
2872
|
+
associatedTokenAccountCache$1.set(key, associatedTokenAccount);
|
|
2860
2873
|
return associatedTokenAccount;
|
|
2861
2874
|
};
|
|
2862
2875
|
//#endregion
|
|
@@ -3306,6 +3319,7 @@ const fetchOnChainTokenData = async (connector, tokenId) => {
|
|
|
3306
3319
|
//#region src/modules/sol-token2022/fetchBalances.ts
|
|
3307
3320
|
const TOKEN_2022_PROGRAM_ID = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb";
|
|
3308
3321
|
const dynamicTokenMetadataCache = /* @__PURE__ */ new Map();
|
|
3322
|
+
const associatedTokenAccountCache = new _talismn_util.LruMap(1024);
|
|
3309
3323
|
const fetchBalances$7 = async ({ networkId, tokensWithAddresses, connector }) => {
|
|
3310
3324
|
if (!tokensWithAddresses.length) return {
|
|
3311
3325
|
success: [],
|
|
@@ -3317,19 +3331,15 @@ const fetchBalances$7 = async ({ networkId, tokensWithAddresses, connector }) =>
|
|
|
3317
3331
|
const knownTokenIds = new Set(tokensWithAddresses.map(([token]) => token.id));
|
|
3318
3332
|
const unknownMints = /* @__PURE__ */ new Set();
|
|
3319
3333
|
const balancesPerAddress = await Promise.all(accountAddresses.map(async (address) => {
|
|
3320
|
-
const
|
|
3334
|
+
const tokenAccounts = await rpc.getTokenAccountsByOwner((0, _solana_kit.address)(address), { programId: (0, _solana_kit.address)(TOKEN_2022_PROGRAM_ID) }, { encoding: "jsonParsed" }).send();
|
|
3335
|
+
const tokenAccountHoldings = await Promise.all(tokenAccounts.value.map(async (d) => {
|
|
3321
3336
|
try {
|
|
3322
|
-
const mintAddress = d.account.data.parsed.info
|
|
3323
|
-
const value =
|
|
3324
|
-
const tokenId = (0, _talismn_chaindata_provider.solToken2022TokenId)(networkId, mintAddress);
|
|
3325
|
-
if (!knownTokenIds.has(tokenId) && value !== "0") unknownMints.add(mintAddress);
|
|
3337
|
+
const { mint: mintAddress, state, tokenAmount } = d.account.data.parsed.info;
|
|
3338
|
+
const value = BigInt(tokenAmount.amount ?? "0");
|
|
3326
3339
|
return {
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
address,
|
|
3330
|
-
source: MODULE_TYPE$7,
|
|
3331
|
-
status: "live",
|
|
3332
|
-
value
|
|
3340
|
+
mintAddress,
|
|
3341
|
+
value,
|
|
3342
|
+
isSpendable: value !== 0n && state !== "frozen" && d.pubkey === await getAssociatedTokenAccount(address, mintAddress)
|
|
3333
3343
|
};
|
|
3334
3344
|
} catch {
|
|
3335
3345
|
log_default.warn("Failed to parse token amount", {
|
|
@@ -3338,7 +3348,36 @@ const fetchBalances$7 = async ({ networkId, tokensWithAddresses, connector }) =>
|
|
|
3338
3348
|
});
|
|
3339
3349
|
return null;
|
|
3340
3350
|
}
|
|
3341
|
-
})
|
|
3351
|
+
}));
|
|
3352
|
+
const holdingsPerTokenId = /* @__PURE__ */ new Map();
|
|
3353
|
+
for (const { mintAddress, value, isSpendable } of tokenAccountHoldings.filter(_talismn_util.isNotNil)) {
|
|
3354
|
+
const tokenId = (0, _talismn_chaindata_provider.solToken2022TokenId)(networkId, mintAddress);
|
|
3355
|
+
if (!knownTokenIds.has(tokenId) && value !== 0n) unknownMints.add(mintAddress);
|
|
3356
|
+
const { total, spendable } = holdingsPerTokenId.get(tokenId) ?? {
|
|
3357
|
+
total: 0n,
|
|
3358
|
+
spendable: 0n
|
|
3359
|
+
};
|
|
3360
|
+
holdingsPerTokenId.set(tokenId, {
|
|
3361
|
+
total: total + value,
|
|
3362
|
+
spendable: spendable + (isSpendable ? value : 0n)
|
|
3363
|
+
});
|
|
3364
|
+
}
|
|
3365
|
+
const balances = [...holdingsPerTokenId].map(([tokenId, { total, spendable }]) => ({
|
|
3366
|
+
tokenId,
|
|
3367
|
+
networkId,
|
|
3368
|
+
address,
|
|
3369
|
+
source: MODULE_TYPE$7,
|
|
3370
|
+
status: "live",
|
|
3371
|
+
...spendable === total ? { value: total.toString() } : { values: [{
|
|
3372
|
+
type: "free",
|
|
3373
|
+
label: "free",
|
|
3374
|
+
amount: total.toString()
|
|
3375
|
+
}, {
|
|
3376
|
+
type: "locked",
|
|
3377
|
+
label: "locked",
|
|
3378
|
+
amount: (total - spendable).toString()
|
|
3379
|
+
}] }
|
|
3380
|
+
}));
|
|
3342
3381
|
setDetectedTokenIds(address, MODULE_TYPE$7, balances.map((b) => b.tokenId));
|
|
3343
3382
|
return [address, balances];
|
|
3344
3383
|
}));
|
|
@@ -3393,6 +3432,18 @@ const fetchBalances$7 = async ({ networkId, tokensWithAddresses, connector }) =>
|
|
|
3393
3432
|
};
|
|
3394
3433
|
};
|
|
3395
3434
|
const getBalanceKey = (tokenId, address) => `${tokenId}:${address}`;
|
|
3435
|
+
const getAssociatedTokenAccount = async (owner, mint) => {
|
|
3436
|
+
const key = `${owner}:${mint}`;
|
|
3437
|
+
const cached = associatedTokenAccountCache.get(key);
|
|
3438
|
+
if (cached) return cached;
|
|
3439
|
+
const [associatedTokenAccount] = await (0, _solana_program_token.findAssociatedTokenPda)({
|
|
3440
|
+
mint: (0, _solana_kit.address)(mint),
|
|
3441
|
+
owner: (0, _solana_kit.address)(owner),
|
|
3442
|
+
tokenProgram: _solana_program_token_2022.TOKEN_2022_PROGRAM_ADDRESS
|
|
3443
|
+
});
|
|
3444
|
+
associatedTokenAccountCache.set(key, associatedTokenAccount);
|
|
3445
|
+
return associatedTokenAccount;
|
|
3446
|
+
};
|
|
3396
3447
|
//#endregion
|
|
3397
3448
|
//#region src/modules/sol-token2022/fetchTokens.ts
|
|
3398
3449
|
const fetchTokens$7 = async ({ networkId, tokens, connector, cache }) => {
|
|
@@ -6807,6 +6858,8 @@ const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, sp
|
|
|
6807
6858
|
* Kept tight — a real incoming transfer above 0.1% of the position emits immediately.
|
|
6808
6859
|
*/
|
|
6809
6860
|
const GENERIC_DRIFT_TOLERANCE_BPS = 10n;
|
|
6861
|
+
/** Delay before resubscribing a substrate module stream that errored: 2s, doubling, capped at 1min */
|
|
6862
|
+
const substrateModuleRetryDelay = (retryCount) => (0, rxjs.timer)(Math.min(6e4, 2e3 * 2 ** (retryCount - 1)));
|
|
6810
6863
|
const DEFAULT_STORAGE = {
|
|
6811
6864
|
balances: [],
|
|
6812
6865
|
miniMetadatas: []
|
|
@@ -6944,7 +6997,10 @@ var BalancesProvider = class {
|
|
|
6944
6997
|
connector: this.#chainConnectors.substrate,
|
|
6945
6998
|
miniMetadata,
|
|
6946
6999
|
hasFrontierMirrorWithdraw
|
|
6947
|
-
})), stabilizeModuleResults(mod.type === "substrate-dtao" ? void 0 : classifySmallAmountDrift(GENERIC_DRIFT_TOLERANCE_BPS)), (0, rxjs.
|
|
7000
|
+
})), stabilizeModuleResults(mod.type === "substrate-dtao" ? void 0 : classifySmallAmountDrift(GENERIC_DRIFT_TOLERANCE_BPS)), (0, rxjs.retry)({
|
|
7001
|
+
delay: (_error, retryCount) => substrateModuleRetryDelay(retryCount),
|
|
7002
|
+
resetOnSuccess: true
|
|
7003
|
+
}), (0, rxjs.tap)((results) => {
|
|
6948
7004
|
(0, _talismn_util.reportJsActivity)(`module ${mod.type} ${networkId} (${results.success.length})`);
|
|
6949
7005
|
if (results.dynamicTokens?.length) this.#chaindataProvider.registerDynamicTokens(results.dynamicTokens);
|
|
6950
7006
|
}), (0, rxjs.map)((results) => ({
|