@augustdigital/sdk 8.10.0 → 8.12.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/adapters/evm/index.d.ts +17 -1
- package/lib/adapters/evm/index.js +20 -0
- package/lib/adapters/stellar/actions.js +4 -4
- package/lib/adapters/stellar/getters.d.ts +9 -3
- package/lib/adapters/stellar/getters.js +18 -6
- package/lib/adapters/stellar/index.d.ts +17 -2
- package/lib/adapters/stellar/index.js +24 -5
- package/lib/adapters/stellar/soroban.d.ts +89 -2
- package/lib/adapters/stellar/soroban.js +487 -64
- package/lib/adapters/stellar/submit.d.ts +4 -1
- package/lib/adapters/stellar/submit.js +7 -3
- package/lib/adapters/stellar/types.d.ts +12 -0
- package/lib/core/analytics/method-taxonomy.js +1 -0
- package/lib/core/analytics/sanitize.js +19 -3
- package/lib/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/core/base.class.d.ts +2 -1
- package/lib/core/constants/swap-router.d.ts +2 -0
- package/lib/core/constants/swap-router.js +1 -0
- package/lib/core/constants/web3.d.ts +19 -0
- package/lib/core/constants/web3.js +37 -1
- package/lib/core/helpers/multicall.d.ts +68 -0
- package/lib/core/helpers/multicall.js +103 -0
- package/lib/core/helpers/swap-router.d.ts +36 -1
- package/lib/core/helpers/swap-router.js +80 -0
- package/lib/core/helpers/vault-version.d.ts +4 -1
- package/lib/core/helpers/vaults.d.ts +1 -1
- package/lib/core/index.d.ts +1 -0
- package/lib/core/index.js +1 -0
- package/lib/main.js +6 -1
- package/lib/modules/vaults/getters.d.ts +25 -2
- package/lib/modules/vaults/getters.js +46 -12
- package/lib/modules/vaults/main.d.ts +28 -0
- package/lib/modules/vaults/main.js +89 -0
- package/lib/modules/vaults/prefetch.d.ts +65 -0
- package/lib/modules/vaults/prefetch.js +120 -0
- package/lib/modules/vaults/types.d.ts +11 -1
- package/lib/sdk.d.ts +6725 -6507
- package/lib/types/subgraph.d.ts +9 -0
- package/lib/types/vaults.d.ts +44 -0
- package/lib/types/web3.d.ts +15 -0
- package/package.json +1 -1
|
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.getSwapRouterDepositResult = void 0;
|
|
39
40
|
exports.getVault = getVault;
|
|
40
41
|
exports.getVaultLoans = getVaultLoans;
|
|
41
42
|
exports.getVaultSubaccountLoans = getVaultSubaccountLoans;
|
|
@@ -548,7 +549,7 @@ async function getVaultAllocations(vault, options) {
|
|
|
548
549
|
unfilteredTokens: unfilteredTokens,
|
|
549
550
|
};
|
|
550
551
|
}
|
|
551
|
-
async function getVaultAvailableRedemptions({ vault, wallet, options, }) {
|
|
552
|
+
async function getVaultAvailableRedemptions({ vault, wallet, options, prefetchedReads, }) {
|
|
552
553
|
try {
|
|
553
554
|
// Stellar vaults don't support on-chain redemptions yet
|
|
554
555
|
if ((0, utils_3.isStellarAddress)(vault)) {
|
|
@@ -592,8 +593,9 @@ async function getVaultAvailableRedemptions({ vault, wallet, options, }) {
|
|
|
592
593
|
else {
|
|
593
594
|
decimals = await (0, core_1.getDecimals)(provider, vault);
|
|
594
595
|
}
|
|
595
|
-
const
|
|
596
|
-
|
|
596
|
+
const lagDuration = typeof prefetchedReads?.lagDuration === 'number'
|
|
597
|
+
? prefetchedReads.lagDuration
|
|
598
|
+
: Number(await vaultContract.lagDuration());
|
|
597
599
|
const { withdrawalRequesteds, withdrawalProcesseds } = await (0, subgraph_1.getSubgraphAllWithdrawals)(vault, provider);
|
|
598
600
|
// format
|
|
599
601
|
const availableRedemptions = [];
|
|
@@ -991,10 +993,15 @@ async function getVaultRedemptionHistory({ vault, wallet, lookbackBlocks, option
|
|
|
991
993
|
throw e;
|
|
992
994
|
}
|
|
993
995
|
}
|
|
996
|
+
// Moved to core/helpers/swap-router.ts to break the adapters/evm ↔
|
|
997
|
+
// modules/vaults/getters import cycle (CLAUDE.md §9); re-exported here for
|
|
998
|
+
// back-compat with existing importers.
|
|
999
|
+
var swap_router_1 = require("../../core/helpers/swap-router");
|
|
1000
|
+
Object.defineProperty(exports, "getSwapRouterDepositResult", { enumerable: true, get: function () { return swap_router_1.getSwapRouterDepositResult; } });
|
|
994
1001
|
/**
|
|
995
1002
|
* Vault Positions
|
|
996
1003
|
*/
|
|
997
|
-
async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, options, }) {
|
|
1004
|
+
async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, options, prefetchedReads, }) {
|
|
998
1005
|
try {
|
|
999
1006
|
const tokenizedVaults = await (0, core_1.fetchTokenizedVault)(vault ? vault : undefined, options?.headers, false, false);
|
|
1000
1007
|
if (!tokenizedVaults || tokenizedVaults.length === 0) {
|
|
@@ -1058,7 +1065,11 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, o
|
|
|
1058
1065
|
let balance = (0, core_1.toNormalizedBn)(0);
|
|
1059
1066
|
if (stellarWallet && (0, utils_3.isStellarAddress)(stellarWallet)) {
|
|
1060
1067
|
const stellarNetwork = v.stellar_vault_metadata?.network_name ?? 'mainnet';
|
|
1061
|
-
|
|
1068
|
+
// Only use the configured RPC override for its own network.
|
|
1069
|
+
const stellarOverrideRpcUrl = options.stellarRpc?.network === stellarNetwork
|
|
1070
|
+
? options.stellarRpc.rpcUrl
|
|
1071
|
+
: undefined;
|
|
1072
|
+
const position = await StellarGetters.getStellarUserPosition(v.address, stellarWallet, stellarNetwork, stellarOverrideRpcUrl);
|
|
1062
1073
|
if (position && !position.decimalsFromFallback) {
|
|
1063
1074
|
balance = (0, core_1.toNormalizedBn)(BigInt(position.shares), position.decimals);
|
|
1064
1075
|
}
|
|
@@ -1096,24 +1107,37 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, o
|
|
|
1096
1107
|
abi: abis_1.ABI_LENDING_POOL_V2,
|
|
1097
1108
|
address: v.address,
|
|
1098
1109
|
});
|
|
1110
|
+
// Batched reads only apply to the vault this call was made for —
|
|
1111
|
+
// guard against a multi-row backend response reusing them.
|
|
1112
|
+
const prefetched = v.address?.toLowerCase?.() === String(vault).toLowerCase()
|
|
1113
|
+
? prefetchedReads
|
|
1114
|
+
: undefined;
|
|
1099
1115
|
// get vault metadata and balance based on version
|
|
1100
1116
|
let bal = BigInt(0);
|
|
1101
1117
|
if (version === 'evm-2') {
|
|
1102
1118
|
const receiptAddress = await (0, core_1.getReceiptTokenAddress)(provider, vault);
|
|
1103
1119
|
decimals = await (0, core_1.getDecimals)(provider, receiptAddress, false);
|
|
1104
1120
|
if (wallet && (0, ethers_1.isAddress)(wallet)) {
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1121
|
+
if (typeof prefetched?.balance === 'bigint') {
|
|
1122
|
+
bal = prefetched.balance;
|
|
1123
|
+
}
|
|
1124
|
+
else {
|
|
1125
|
+
const receiptContract = (0, core_1.createContract)({
|
|
1126
|
+
provider,
|
|
1127
|
+
abi: TokenizedVaultV2Receipt_1.ABI_TOKENIZED_VAULT_V2_RECEIPT,
|
|
1128
|
+
address: receiptAddress,
|
|
1129
|
+
});
|
|
1130
|
+
bal = await receiptContract.balanceOf(wallet);
|
|
1131
|
+
}
|
|
1111
1132
|
}
|
|
1112
1133
|
}
|
|
1113
1134
|
else {
|
|
1114
1135
|
decimals = await (0, core_1.getDecimals)(provider, vault);
|
|
1115
1136
|
if (wallet && (0, ethers_1.isAddress)(wallet)) {
|
|
1116
|
-
bal =
|
|
1137
|
+
bal =
|
|
1138
|
+
typeof prefetched?.balance === 'bigint'
|
|
1139
|
+
? prefetched.balance
|
|
1140
|
+
: await vaultContract.balanceOf(wallet);
|
|
1117
1141
|
}
|
|
1118
1142
|
}
|
|
1119
1143
|
const balance = (0, core_1.toNormalizedBn)(bal, decimals);
|
|
@@ -1122,6 +1146,7 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, o
|
|
|
1122
1146
|
vault: v.address,
|
|
1123
1147
|
wallet,
|
|
1124
1148
|
options,
|
|
1149
|
+
prefetchedReads: prefetched,
|
|
1125
1150
|
});
|
|
1126
1151
|
// get aggregate redemption amount
|
|
1127
1152
|
const aggregateAvailableRedemptions = availableRedemptions.reduce((acc, curr) => acc + BigInt(curr.amount.raw), BigInt(0));
|
|
@@ -1855,6 +1880,15 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
|
|
|
1855
1880
|
if (!tokenizedVault) {
|
|
1856
1881
|
throw new Error(`Vault ${vault} not found`);
|
|
1857
1882
|
}
|
|
1883
|
+
// getDecimals resolves to `undefined` on a failed/absent decimals() read.
|
|
1884
|
+
// Unguarded, that undefined flows into `10 ** decimals` (=> NaN) and
|
|
1885
|
+
// toNormalizedBn(x, undefined), and the first BigInt(NaN) throws the opaque
|
|
1886
|
+
// "The number NaN cannot be converted to a BigInt" error. Decimals must come
|
|
1887
|
+
// from the chain — never assume a fallback — so fail fast with actionable
|
|
1888
|
+
// context. Transient RPC blips recover on the caller's retry.
|
|
1889
|
+
if (typeof decimals !== 'number' || !Number.isFinite(decimals)) {
|
|
1890
|
+
throw new Error(`Failed to resolve decimals for vault ${vault}`);
|
|
1891
|
+
}
|
|
1858
1892
|
// Fetch LayerZero deposits/redeems for supported vaults
|
|
1859
1893
|
const lzVaultKey = (0, deposits_1.isLayerZeroVault)(vault);
|
|
1860
1894
|
let lzDeposits = [];
|
|
@@ -23,6 +23,12 @@ export declare class AugustVaults extends AugustBase {
|
|
|
23
23
|
private solanaService;
|
|
24
24
|
private suiService;
|
|
25
25
|
private headers;
|
|
26
|
+
/**
|
|
27
|
+
* Consumer-supplied Soroban RPC override for Stellar vault reads, with the
|
|
28
|
+
* network it was configured for so it's only applied to matching-network
|
|
29
|
+
* vaults (see `IVaultBaseOptions.stellarRpc`).
|
|
30
|
+
*/
|
|
31
|
+
private _stellarRpc?;
|
|
26
32
|
constructor(baseConfig: IAugustBase, solana: SolanaAdapter, sui: SuiAdapter);
|
|
27
33
|
/**
|
|
28
34
|
* @description deposit underlying token into the specified pool with adapter support. This is for when we cannot pass in a signer in sdk and need to do so as an arg
|
|
@@ -44,6 +50,17 @@ export declare class AugustVaults extends AugustBase {
|
|
|
44
50
|
* Includes closed vaults that are still visible (`status: 'closed'`,
|
|
45
51
|
* `is_visible: true`) so consumers can render positions held in closed
|
|
46
52
|
* vaults; closed vaults with `is_visible: false` are excluded.
|
|
53
|
+
*
|
|
54
|
+
* RPC volume (worst case, wallet enrichment path): the per-vault
|
|
55
|
+
* `balanceOf`/`lagDuration` reads are aggregated into chunked Multicall3
|
|
56
|
+
* `aggregate3` batches per chain (10 calls per chunk) on chains where the
|
|
57
|
+
* canonical Multicall3 deployment is verified; other chains (e.g. Citrea
|
|
58
|
+
* 4114) keep one `eth_call` per read. Measured with
|
|
59
|
+
* `benchmarks/request-counter.js` on a cold cache for a wallet with 62
|
|
60
|
+
* vaults across Ethereum + Avalanche: 83 RPC HTTP requests before batching,
|
|
61
|
+
* 54 after (~35% fewer; 124 per-vault `eth_call`s collapse into ~13
|
|
62
|
+
* `aggregate3` calls — the remaining requests are the per-vault cached
|
|
63
|
+
* decimals/receipt-token reads on a cold cache).
|
|
47
64
|
* @param options Filtering and enrichment configuration
|
|
48
65
|
* @returns Array of vault objects with optional loans/allocations/positions
|
|
49
66
|
*/
|
|
@@ -196,7 +213,18 @@ export declare class AugustVaults extends AugustBase {
|
|
|
196
213
|
lookbackBlocks?: number;
|
|
197
214
|
}): Promise<import("../../types").IVaultRedemptionHistoryItem[]>;
|
|
198
215
|
/**
|
|
216
|
+
* Fetch the wallet's positions across vaults — a single vault, one chain,
|
|
217
|
+
* or every chain with a configured provider.
|
|
199
218
|
*
|
|
219
|
+
* RPC volume (worst case, multi-vault paths): the two non-cacheable
|
|
220
|
+
* per-vault reads (`balanceOf`, `lagDuration`) are aggregated into chunked
|
|
221
|
+
* Multicall3 `aggregate3` batches per chain (10 calls per chunk) on chains
|
|
222
|
+
* where the canonical deployment is verified; unverified chains (e.g.
|
|
223
|
+
* Citrea 4114) and non-EVM vaults keep one `eth_call` per read. Measured
|
|
224
|
+
* with `benchmarks/request-counter.js` on a cold cache for a wallet with 62
|
|
225
|
+
* vaults across Ethereum + Avalanche: 83 RPC HTTP requests before batching,
|
|
226
|
+
* 54 after (~35% fewer). A vault whose batched read fails (e.g. paused)
|
|
227
|
+
* falls back to its own per-vault reads — never to a zeroed balance.
|
|
200
228
|
*/
|
|
201
229
|
getVaultPositions({ vault, wallet, chainId, showAllVaults, solanaWallet, stellarWallet, options, }: {
|
|
202
230
|
vault?: IAddress;
|
|
@@ -66,6 +66,7 @@ const constants_1 = require("../../adapters/sui/constants");
|
|
|
66
66
|
const vaults_2 = require("../../core/constants/vaults");
|
|
67
67
|
const write_actions_1 = require("./write.actions");
|
|
68
68
|
const read_actions_1 = require("./read.actions");
|
|
69
|
+
const prefetch_1 = require("./prefetch");
|
|
69
70
|
/**
|
|
70
71
|
* Vault operations class handling multi-chain vault queries and user positions.
|
|
71
72
|
* Supports both EVM and Solana vaults with unified interface.
|
|
@@ -74,10 +75,23 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
74
75
|
solanaService;
|
|
75
76
|
suiService;
|
|
76
77
|
headers = null;
|
|
78
|
+
/**
|
|
79
|
+
* Consumer-supplied Soroban RPC override for Stellar vault reads, with the
|
|
80
|
+
* network it was configured for so it's only applied to matching-network
|
|
81
|
+
* vaults (see `IVaultBaseOptions.stellarRpc`).
|
|
82
|
+
*/
|
|
83
|
+
_stellarRpc;
|
|
77
84
|
constructor(baseConfig, solana, sui) {
|
|
78
85
|
super(baseConfig);
|
|
79
86
|
this.solanaService = solana;
|
|
80
87
|
this.suiService = sui || new sui_1.default();
|
|
88
|
+
// Network defaults to mainnet, matching the StellarAdapter constructor.
|
|
89
|
+
this._stellarRpc = baseConfig.stellar?.rpcUrl
|
|
90
|
+
? {
|
|
91
|
+
rpcUrl: baseConfig.stellar.rpcUrl,
|
|
92
|
+
network: baseConfig.stellar.network ?? 'mainnet',
|
|
93
|
+
}
|
|
94
|
+
: undefined;
|
|
81
95
|
this.headers = {
|
|
82
96
|
'x-environment': this.monitoring?.['x-environment'],
|
|
83
97
|
'x-user-id': this.monitoring?.['x-user-id'],
|
|
@@ -107,6 +121,17 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
107
121
|
* Includes closed vaults that are still visible (`status: 'closed'`,
|
|
108
122
|
* `is_visible: true`) so consumers can render positions held in closed
|
|
109
123
|
* vaults; closed vaults with `is_visible: false` are excluded.
|
|
124
|
+
*
|
|
125
|
+
* RPC volume (worst case, wallet enrichment path): the per-vault
|
|
126
|
+
* `balanceOf`/`lagDuration` reads are aggregated into chunked Multicall3
|
|
127
|
+
* `aggregate3` batches per chain (10 calls per chunk) on chains where the
|
|
128
|
+
* canonical Multicall3 deployment is verified; other chains (e.g. Citrea
|
|
129
|
+
* 4114) keep one `eth_call` per read. Measured with
|
|
130
|
+
* `benchmarks/request-counter.js` on a cold cache for a wallet with 62
|
|
131
|
+
* vaults across Ethereum + Avalanche: 83 RPC HTTP requests before batching,
|
|
132
|
+
* 54 after (~35% fewer; 124 per-vault `eth_call`s collapse into ~13
|
|
133
|
+
* `aggregate3` calls — the remaining requests are the per-vault cached
|
|
134
|
+
* decimals/receipt-token reads on a cold cache).
|
|
110
135
|
* @param options Filtering and enrichment configuration
|
|
111
136
|
* @returns Array of vault objects with optional loans/allocations/positions
|
|
112
137
|
*/
|
|
@@ -166,6 +191,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
166
191
|
options: {
|
|
167
192
|
rpcUrl,
|
|
168
193
|
solanaService: this.solanaService,
|
|
194
|
+
stellarRpc: this._stellarRpc,
|
|
169
195
|
env: this.monitoring?.env,
|
|
170
196
|
augustKey: this.keys?.august,
|
|
171
197
|
subgraphKey: this.keys?.graph,
|
|
@@ -234,6 +260,14 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
234
260
|
if ((options.wallet && (0, ethers_1.isAddress)(options.wallet)) ||
|
|
235
261
|
(options.solanaWallet &&
|
|
236
262
|
utils_1.SolanaUtils.isSolanaAddress(options.solanaWallet))) {
|
|
263
|
+
// Batch the per-vault balanceOf/lagDuration reads (Multicall3, grouped
|
|
264
|
+
// by chain) before fanning out — vaults the batch couldn't serve fall
|
|
265
|
+
// back to their own reads inside getVaultPositions.
|
|
266
|
+
const prefetchedReads = await (0, prefetch_1.prefetchVaultPositionReads)({
|
|
267
|
+
vaults: vaultsPerAvailableProviders,
|
|
268
|
+
wallet: options.wallet,
|
|
269
|
+
providers: this.providers,
|
|
270
|
+
});
|
|
237
271
|
// fetch all positions for user wallet
|
|
238
272
|
const vaultResponses = await (0, core_1.promiseSettle)(vaultsPerAvailableProviders.map((v) => (0, getters_1.getVaultPositions)({
|
|
239
273
|
vault: v.address,
|
|
@@ -243,10 +277,12 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
243
277
|
options: {
|
|
244
278
|
rpcUrl: this.providers?.[v.chain],
|
|
245
279
|
solanaService: this.solanaService,
|
|
280
|
+
stellarRpc: this._stellarRpc,
|
|
246
281
|
env: this.monitoring?.env,
|
|
247
282
|
augustKey: this.keys?.august,
|
|
248
283
|
subgraphKey: this.keys?.graph,
|
|
249
284
|
},
|
|
285
|
+
prefetchedReads: prefetchedReads.get(String(v.address).toLowerCase()),
|
|
250
286
|
})), 5, // 5 retries
|
|
251
287
|
2000);
|
|
252
288
|
// return vault object with position property
|
|
@@ -316,6 +352,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
316
352
|
? this.providers?.[chainId]
|
|
317
353
|
: this.activeNetwork?.rpcUrl,
|
|
318
354
|
solanaService: this.solanaService,
|
|
355
|
+
stellarRpc: this._stellarRpc,
|
|
319
356
|
env: this.monitoring?.env,
|
|
320
357
|
augustKey: this.keys?.august,
|
|
321
358
|
subgraphKey: this.keys?.graph,
|
|
@@ -340,6 +377,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
340
377
|
? this.providers?.[chainId]
|
|
341
378
|
: this.activeNetwork?.rpcUrl,
|
|
342
379
|
solanaService: this.solanaService,
|
|
380
|
+
stellarRpc: this._stellarRpc,
|
|
343
381
|
env: this.monitoring?.env,
|
|
344
382
|
augustKey: this.keys?.august,
|
|
345
383
|
subgraphKey: this.keys?.graph,
|
|
@@ -385,6 +423,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
385
423
|
subgraphKey: this.keys?.graph,
|
|
386
424
|
chainId: chainId,
|
|
387
425
|
solanaService: this.solanaService,
|
|
426
|
+
stellarRpc: this._stellarRpc,
|
|
388
427
|
headers: this.headers,
|
|
389
428
|
});
|
|
390
429
|
return vaultResponse;
|
|
@@ -416,6 +455,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
416
455
|
subgraphKey: this.keys?.graph,
|
|
417
456
|
chainId: chainId,
|
|
418
457
|
solanaService: this.solanaService,
|
|
458
|
+
stellarRpc: this._stellarRpc,
|
|
419
459
|
headers: this.headers,
|
|
420
460
|
});
|
|
421
461
|
return vaultResponse;
|
|
@@ -447,6 +487,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
447
487
|
? this.providers?.[chainId]
|
|
448
488
|
: this.activeNetwork?.rpcUrl,
|
|
449
489
|
solanaService: this.solanaService,
|
|
490
|
+
stellarRpc: this._stellarRpc,
|
|
450
491
|
env: this.monitoring?.env,
|
|
451
492
|
augustKey: this.keys?.august,
|
|
452
493
|
subgraphKey: this.keys?.graph,
|
|
@@ -527,6 +568,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
527
568
|
? this.providers?.[chainId]
|
|
528
569
|
: this.activeNetwork?.rpcUrl,
|
|
529
570
|
solanaService: this.solanaService,
|
|
571
|
+
stellarRpc: this._stellarRpc,
|
|
530
572
|
env: this.monitoring?.env,
|
|
531
573
|
augustKey: this.keys?.august,
|
|
532
574
|
subgraphKey: this.keys?.graph,
|
|
@@ -588,7 +630,18 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
588
630
|
});
|
|
589
631
|
}
|
|
590
632
|
/**
|
|
633
|
+
* Fetch the wallet's positions across vaults — a single vault, one chain,
|
|
634
|
+
* or every chain with a configured provider.
|
|
591
635
|
*
|
|
636
|
+
* RPC volume (worst case, multi-vault paths): the two non-cacheable
|
|
637
|
+
* per-vault reads (`balanceOf`, `lagDuration`) are aggregated into chunked
|
|
638
|
+
* Multicall3 `aggregate3` batches per chain (10 calls per chunk) on chains
|
|
639
|
+
* where the canonical deployment is verified; unverified chains (e.g.
|
|
640
|
+
* Citrea 4114) and non-EVM vaults keep one `eth_call` per read. Measured
|
|
641
|
+
* with `benchmarks/request-counter.js` on a cold cache for a wallet with 62
|
|
642
|
+
* vaults across Ethereum + Avalanche: 83 RPC HTTP requests before batching,
|
|
643
|
+
* 54 after (~35% fewer). A vault whose batched read fails (e.g. paused)
|
|
644
|
+
* falls back to its own per-vault reads — never to a zeroed balance.
|
|
592
645
|
*/
|
|
593
646
|
async getVaultPositions({ vault, wallet, chainId, showAllVaults, solanaWallet, stellarWallet, options, }) {
|
|
594
647
|
// if (!this.authorized) throw new Error('Not authorized.');
|
|
@@ -611,6 +664,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
611
664
|
? this.providers?.[chainId]
|
|
612
665
|
: this.activeNetwork?.rpcUrl,
|
|
613
666
|
solanaService: this.solanaService,
|
|
667
|
+
stellarRpc: this._stellarRpc,
|
|
614
668
|
env: this.monitoring?.env,
|
|
615
669
|
augustKey: this.keys?.august,
|
|
616
670
|
subgraphKey: this.keys?.graph,
|
|
@@ -624,6 +678,13 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
624
678
|
const allVaults = await (0, core_1.fetchTokenizedVaults)(undefined, this.headers, false, false);
|
|
625
679
|
if (chainId) {
|
|
626
680
|
const vaultsPerChain = allVaults.filter((v) => v.chain === chainId);
|
|
681
|
+
// Batch balanceOf/lagDuration via Multicall3 before the per-vault
|
|
682
|
+
// fan-out; un-batched vaults fall back to their own reads.
|
|
683
|
+
const prefetchedReads = await (0, prefetch_1.prefetchVaultPositionReads)({
|
|
684
|
+
vaults: vaultsPerChain,
|
|
685
|
+
wallet,
|
|
686
|
+
providers: this.providers,
|
|
687
|
+
});
|
|
627
688
|
const vaultResponses = await Promise.all(vaultsPerChain.map((v) => (0, getters_1.getVaultPositions)({
|
|
628
689
|
vault: v.address,
|
|
629
690
|
solanaWallet,
|
|
@@ -632,11 +693,13 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
632
693
|
options: {
|
|
633
694
|
rpcUrl: this.providers?.[v.chain],
|
|
634
695
|
solanaService: this.solanaService,
|
|
696
|
+
stellarRpc: this._stellarRpc,
|
|
635
697
|
env: this.monitoring?.env,
|
|
636
698
|
augustKey: this.keys?.august,
|
|
637
699
|
subgraphKey: this.keys?.graph,
|
|
638
700
|
headers: this.headers,
|
|
639
701
|
},
|
|
702
|
+
prefetchedReads: prefetchedReads.get(String(v.address).toLowerCase()),
|
|
640
703
|
})));
|
|
641
704
|
const flattened = vaultResponses.flat();
|
|
642
705
|
let final;
|
|
@@ -659,6 +722,13 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
659
722
|
// outer catch, and silently return PENDING (then get filtered out).
|
|
660
723
|
(v?.chain_type === 'solana' && !!this.solanaService) ||
|
|
661
724
|
(this.providers?.[v?.chain] ? true : false));
|
|
725
|
+
// Batch balanceOf/lagDuration via Multicall3, grouped by chain, before
|
|
726
|
+
// the per-vault fan-out; un-batched vaults fall back to their own reads.
|
|
727
|
+
const prefetchedReads = await (0, prefetch_1.prefetchVaultPositionReads)({
|
|
728
|
+
vaults: vaultsPerAvailableProviders,
|
|
729
|
+
wallet,
|
|
730
|
+
providers: this.providers,
|
|
731
|
+
});
|
|
662
732
|
const vaultResponses = await Promise.all(vaultsPerAvailableProviders.map((v) => (0, getters_1.getVaultPositions)({
|
|
663
733
|
vault: v.address,
|
|
664
734
|
solanaWallet,
|
|
@@ -667,11 +737,13 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
667
737
|
options: {
|
|
668
738
|
rpcUrl: this.providers?.[v.chain],
|
|
669
739
|
solanaService: this.solanaService,
|
|
740
|
+
stellarRpc: this._stellarRpc,
|
|
670
741
|
env: this.monitoring?.env,
|
|
671
742
|
augustKey: this.keys?.august,
|
|
672
743
|
subgraphKey: this.keys?.graph,
|
|
673
744
|
headers: this.headers,
|
|
674
745
|
},
|
|
746
|
+
prefetchedReads: prefetchedReads.get(String(v.address).toLowerCase()),
|
|
675
747
|
})));
|
|
676
748
|
const flattened = vaultResponses.flat();
|
|
677
749
|
let final;
|
|
@@ -706,6 +778,16 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
706
778
|
chainId: _chainId,
|
|
707
779
|
type: h.type,
|
|
708
780
|
transactionHash: h.transactionHash_,
|
|
781
|
+
// Deposit rows on multi-asset (pre-deposit) vaults carry the actual
|
|
782
|
+
// deposited token address; propagate it so consumers render the right
|
|
783
|
+
// asset symbol instead of guessing the vault's first deposit asset.
|
|
784
|
+
assetIn: h.assetIn,
|
|
785
|
+
// withdraw-request rows carry the burned share amount. Normalize with
|
|
786
|
+
// the vault decimals (the share token's own decimals) so UIs can show
|
|
787
|
+
// the receipt-token quantity that was actually burned.
|
|
788
|
+
shares: h.shares
|
|
789
|
+
? (0, core_1.toNormalizedBn)(BigInt(h.shares), h.decimals)
|
|
790
|
+
: undefined,
|
|
709
791
|
}));
|
|
710
792
|
}
|
|
711
793
|
// Helper function to fetch history for a vault
|
|
@@ -826,6 +908,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
826
908
|
? this.providers?.[chainId]
|
|
827
909
|
: this.requireActiveNetwork().rpcUrl,
|
|
828
910
|
solanaService: this.solanaService,
|
|
911
|
+
stellarRpc: this._stellarRpc,
|
|
829
912
|
env: this.monitoring?.env,
|
|
830
913
|
augustKey: this.keys?.august,
|
|
831
914
|
subgraphKey: this.keys?.graph,
|
|
@@ -851,6 +934,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
851
934
|
? this.providers?.[chainId]
|
|
852
935
|
: this.requireActiveNetwork().rpcUrl,
|
|
853
936
|
solanaService: this.solanaService,
|
|
937
|
+
stellarRpc: this._stellarRpc,
|
|
854
938
|
env: this.monitoring?.env,
|
|
855
939
|
augustKey: this.keys?.august,
|
|
856
940
|
subgraphKey: this.keys?.graph,
|
|
@@ -1033,6 +1117,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
1033
1117
|
rpcUrl: rpcUrl,
|
|
1034
1118
|
chainId: resolvedChainId,
|
|
1035
1119
|
solanaService: this.solanaService,
|
|
1120
|
+
stellarRpc: this._stellarRpc,
|
|
1036
1121
|
env: this.monitoring?.env,
|
|
1037
1122
|
augustKey: this.keys?.august,
|
|
1038
1123
|
subgraphKey: this.keys?.graph,
|
|
@@ -1056,6 +1141,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
1056
1141
|
options: {
|
|
1057
1142
|
rpcUrl: rpcUrl,
|
|
1058
1143
|
solanaService: this.solanaService,
|
|
1144
|
+
stellarRpc: this._stellarRpc,
|
|
1059
1145
|
env: this.monitoring?.env,
|
|
1060
1146
|
augustKey: this.keys?.august,
|
|
1061
1147
|
subgraphKey: this.keys?.graph,
|
|
@@ -1103,6 +1189,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
1103
1189
|
? this.providers?.[chainId]
|
|
1104
1190
|
: rpcUrl || this.requireActiveNetwork().rpcUrl,
|
|
1105
1191
|
solanaService: this.solanaService,
|
|
1192
|
+
stellarRpc: this._stellarRpc,
|
|
1106
1193
|
env: this.monitoring?.env,
|
|
1107
1194
|
augustKey: this.keys?.august,
|
|
1108
1195
|
subgraphKey: this.keys?.graph,
|
|
@@ -1376,6 +1463,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
1376
1463
|
options: {
|
|
1377
1464
|
rpcUrl: chainId ? this.providers?.[chainId] : this.activeNetwork.rpcUrl,
|
|
1378
1465
|
solanaService: this.solanaService,
|
|
1466
|
+
stellarRpc: this._stellarRpc,
|
|
1379
1467
|
env: this.monitoring?.env,
|
|
1380
1468
|
augustKey: this.keys?.august,
|
|
1381
1469
|
subgraphKey: this.keys?.graph,
|
|
@@ -1428,6 +1516,7 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
1428
1516
|
options: {
|
|
1429
1517
|
rpcUrl,
|
|
1430
1518
|
solanaService: this.solanaService,
|
|
1519
|
+
stellarRpc: this._stellarRpc,
|
|
1431
1520
|
env: this.monitoring?.env,
|
|
1432
1521
|
augustKey: this.keys?.august,
|
|
1433
1522
|
subgraphKey: this.keys?.graph,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { IAddress, ITokenizedVault } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Per-vault on-chain reads resolved ahead of the positions fan-out, so
|
|
4
|
+
* `getVaultPositions` / `getVaultAvailableRedemptions` can skip their own
|
|
5
|
+
* `balanceOf` / `lagDuration` RPCs. A missing field means the read was not
|
|
6
|
+
* (or could not be) prefetched — the getter falls back to its own call.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export interface IPrefetchedVaultReads {
|
|
10
|
+
/** Raw share/receipt-token balance of the wallet (`balanceOf`). */
|
|
11
|
+
balance?: bigint;
|
|
12
|
+
/** Vault redemption lag in seconds (`lagDuration`). */
|
|
13
|
+
lagDuration?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Minimal shape of a backend tokenized-vault row the prefetch needs. The
|
|
17
|
+
* full backend response type is looser than this; only these fields are read.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
interface IPrefetchableVault {
|
|
21
|
+
address: string;
|
|
22
|
+
chain: number;
|
|
23
|
+
chain_type?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Backend vault kind. `getVaultVersionV2` reads this to classify evm-2
|
|
26
|
+
* (`multiAssetVault`) vaults whose address isn't in the hardcoded
|
|
27
|
+
* `MULTI_ASSET_VAULTS` list; without it such a vault would be misclassified
|
|
28
|
+
* as evm-1 and the batch would target the vault itself for `balanceOf`
|
|
29
|
+
* instead of its receipt token.
|
|
30
|
+
*/
|
|
31
|
+
internal_type?: ITokenizedVault['internal_type'];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Batch the two non-cacheable per-vault reads (`balanceOf`, `lagDuration`)
|
|
35
|
+
* for a vault list via Multicall3, grouped by chain, ahead of the per-vault
|
|
36
|
+
* positions fan-out.
|
|
37
|
+
*
|
|
38
|
+
* Scope and fallback rules (task 52):
|
|
39
|
+
* - Only EVM vaults on chains with a configured provider AND a verified
|
|
40
|
+
* Multicall3 deployment are batched. Solana/Stellar vaults, unverified
|
|
41
|
+
* chains (e.g. Citrea 4114), and provider-less chains are skipped — they
|
|
42
|
+
* keep today's exact per-vault read path.
|
|
43
|
+
* - `balanceOf` targets the receipt token for evm-2 vaults (resolved through
|
|
44
|
+
* the same cached `getReceiptTokenAddress` the getters use) and the vault
|
|
45
|
+
* itself for evm-1. Without an EVM wallet only `lagDuration` is batched.
|
|
46
|
+
* - Failures never propagate: a reverting vault call is simply absent from
|
|
47
|
+
* the result map, and a whole-chain error drops that chain's entries. The
|
|
48
|
+
* worst case is always "no prefetch → per-vault fallback", never wrong data.
|
|
49
|
+
*
|
|
50
|
+
* RPC cost: per batched chain, `ceil(reads / 10)` `eth_call`s (+1 cold
|
|
51
|
+
* `lpTokenAddress` per evm-2 vault) instead of ~2 `eth_call`s per vault.
|
|
52
|
+
*
|
|
53
|
+
* @param vaults Backend tokenized-vault rows about to be fanned out over.
|
|
54
|
+
* @param wallet Optional EVM wallet for `balanceOf` reads.
|
|
55
|
+
* @param providers Chain-id → RPC-url map (the module's `this.providers`).
|
|
56
|
+
* @returns Map keyed by lowercased vault address; vaults with no batched
|
|
57
|
+
* reads have no entry.
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
export declare function prefetchVaultPositionReads({ vaults, wallet, providers, }: {
|
|
61
|
+
vaults: IPrefetchableVault[];
|
|
62
|
+
wallet?: IAddress;
|
|
63
|
+
providers?: Partial<Record<number, string>>;
|
|
64
|
+
}): Promise<Map<string, IPrefetchedVaultReads>>;
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prefetchVaultPositionReads = prefetchVaultPositionReads;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const abis_1 = require("../../abis");
|
|
6
|
+
const TokenizedVaultV2Receipt_1 = require("../../abis/TokenizedVaultV2Receipt");
|
|
7
|
+
const core_1 = require("../../core");
|
|
8
|
+
/**
|
|
9
|
+
* Batch the two non-cacheable per-vault reads (`balanceOf`, `lagDuration`)
|
|
10
|
+
* for a vault list via Multicall3, grouped by chain, ahead of the per-vault
|
|
11
|
+
* positions fan-out.
|
|
12
|
+
*
|
|
13
|
+
* Scope and fallback rules (task 52):
|
|
14
|
+
* - Only EVM vaults on chains with a configured provider AND a verified
|
|
15
|
+
* Multicall3 deployment are batched. Solana/Stellar vaults, unverified
|
|
16
|
+
* chains (e.g. Citrea 4114), and provider-less chains are skipped — they
|
|
17
|
+
* keep today's exact per-vault read path.
|
|
18
|
+
* - `balanceOf` targets the receipt token for evm-2 vaults (resolved through
|
|
19
|
+
* the same cached `getReceiptTokenAddress` the getters use) and the vault
|
|
20
|
+
* itself for evm-1. Without an EVM wallet only `lagDuration` is batched.
|
|
21
|
+
* - Failures never propagate: a reverting vault call is simply absent from
|
|
22
|
+
* the result map, and a whole-chain error drops that chain's entries. The
|
|
23
|
+
* worst case is always "no prefetch → per-vault fallback", never wrong data.
|
|
24
|
+
*
|
|
25
|
+
* RPC cost: per batched chain, `ceil(reads / 10)` `eth_call`s (+1 cold
|
|
26
|
+
* `lpTokenAddress` per evm-2 vault) instead of ~2 `eth_call`s per vault.
|
|
27
|
+
*
|
|
28
|
+
* @param vaults Backend tokenized-vault rows about to be fanned out over.
|
|
29
|
+
* @param wallet Optional EVM wallet for `balanceOf` reads.
|
|
30
|
+
* @param providers Chain-id → RPC-url map (the module's `this.providers`).
|
|
31
|
+
* @returns Map keyed by lowercased vault address; vaults with no batched
|
|
32
|
+
* reads have no entry.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
async function prefetchVaultPositionReads({ vaults, wallet, providers, }) {
|
|
36
|
+
const reads = new Map();
|
|
37
|
+
const hasWallet = !!(wallet && (0, ethers_1.isAddress)(wallet));
|
|
38
|
+
const byChain = new Map();
|
|
39
|
+
for (const v of vaults) {
|
|
40
|
+
if (!v?.address || !(0, ethers_1.isAddress)(v.address))
|
|
41
|
+
continue;
|
|
42
|
+
if (!(0, core_1.isMulticall3Supported)(v.chain))
|
|
43
|
+
continue;
|
|
44
|
+
if (!providers?.[v.chain])
|
|
45
|
+
continue;
|
|
46
|
+
const group = byChain.get(v.chain) ?? [];
|
|
47
|
+
group.push(v);
|
|
48
|
+
byChain.set(v.chain, group);
|
|
49
|
+
}
|
|
50
|
+
await Promise.all(Array.from(byChain.entries()).map(async ([chain, chainVaults]) => {
|
|
51
|
+
try {
|
|
52
|
+
// Same cache key as the getters (`createProvider(rpcUrl)` without a
|
|
53
|
+
// chainId) so the receipt-token/decimals caches stay shared.
|
|
54
|
+
const provider = (0, core_1.createProvider)(providers[chain]);
|
|
55
|
+
// Resolve each vault's `balanceOf` target — the receipt token for
|
|
56
|
+
// evm-2 vaults (one `lpTokenAddress` read each on a cold cache),
|
|
57
|
+
// the vault itself otherwise. Bound the fan-out to
|
|
58
|
+
// `MULTICALL3_BATCH_SIZE` concurrent reads (CLAUDE.md §4.1): a wallet
|
|
59
|
+
// with many uncached evm-2 vaults on one chain would otherwise burst
|
|
60
|
+
// N simultaneous reads before the aggregate3 batch even fires.
|
|
61
|
+
const balanceTargets = [];
|
|
62
|
+
for (let i = 0; i < chainVaults.length; i += core_1.MULTICALL3_BATCH_SIZE) {
|
|
63
|
+
const slice = chainVaults.slice(i, i + core_1.MULTICALL3_BATCH_SIZE);
|
|
64
|
+
const resolved = await Promise.all(slice.map(async (v) => {
|
|
65
|
+
if (!hasWallet)
|
|
66
|
+
return undefined;
|
|
67
|
+
if ((0, core_1.getVaultVersionV2)(v) === 'evm-2') {
|
|
68
|
+
// Cached + in-flight-deduped; a cold miss costs the same
|
|
69
|
+
// `lpTokenAddress` read the getter itself would make.
|
|
70
|
+
return (0, core_1.getReceiptTokenAddress)(provider, v.address);
|
|
71
|
+
}
|
|
72
|
+
return v.address;
|
|
73
|
+
}));
|
|
74
|
+
balanceTargets.push(...resolved);
|
|
75
|
+
}
|
|
76
|
+
const calls = [];
|
|
77
|
+
const slots = [];
|
|
78
|
+
chainVaults.forEach((v, i) => {
|
|
79
|
+
calls.push({
|
|
80
|
+
target: v.address,
|
|
81
|
+
abi: abis_1.ABI_LENDING_POOL_V2,
|
|
82
|
+
functionName: 'lagDuration',
|
|
83
|
+
});
|
|
84
|
+
slots.push({ address: v.address, field: 'lagDuration' });
|
|
85
|
+
const target = balanceTargets[i];
|
|
86
|
+
if (hasWallet && target && (0, ethers_1.isAddress)(target)) {
|
|
87
|
+
calls.push({
|
|
88
|
+
target: target,
|
|
89
|
+
abi: target === v.address
|
|
90
|
+
? abis_1.ABI_LENDING_POOL_V2
|
|
91
|
+
: TokenizedVaultV2Receipt_1.ABI_TOKENIZED_VAULT_V2_RECEIPT,
|
|
92
|
+
functionName: 'balanceOf',
|
|
93
|
+
args: [wallet],
|
|
94
|
+
});
|
|
95
|
+
slots.push({ address: v.address, field: 'balance' });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
const results = await (0, core_1.multicall3Aggregate)(provider, chain, calls);
|
|
99
|
+
results.forEach((r, i) => {
|
|
100
|
+
if (!r.success)
|
|
101
|
+
return;
|
|
102
|
+
const slot = slots[i];
|
|
103
|
+
const key = slot.address.toLowerCase();
|
|
104
|
+
const entry = reads.get(key) ?? {};
|
|
105
|
+
if (slot.field === 'lagDuration') {
|
|
106
|
+
entry.lagDuration = Number(r.value);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
entry.balance = BigInt(r.value);
|
|
110
|
+
}
|
|
111
|
+
reads.set(key, entry);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
core_1.Logger.log.warn('prefetchVaultPositionReads', `chain ${chain} prefetch failed — falling back to per-vault reads`, e);
|
|
116
|
+
}
|
|
117
|
+
}));
|
|
118
|
+
return reads;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=prefetch.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Connection } from '@solana/web3.js';
|
|
2
|
-
import type { IAddress, IEnv, IWSMonitorHeaders } from '../../types';
|
|
2
|
+
import type { IAddress, IEnv, IStellarNetwork, IWSMonitorHeaders } from '../../types';
|
|
3
3
|
/**
|
|
4
4
|
* Structural interface for the Solana adapter surface used by vault operations.
|
|
5
5
|
* Avoids importing the concrete SolanaAdapter class to prevent circular dependencies.
|
|
@@ -40,6 +40,16 @@ export interface IVaultBaseOptions {
|
|
|
40
40
|
subgraphKey?: string;
|
|
41
41
|
chainId?: number;
|
|
42
42
|
solanaService?: ISolanaService;
|
|
43
|
+
/**
|
|
44
|
+
* Optional Soroban RPC override (e.g. a keyed Alchemy URL) for Stellar vault
|
|
45
|
+
* reads, threaded from the SDK's `stellar` config. Carries the network it was
|
|
46
|
+
* configured for so it is only applied to matching-network vaults — a testnet
|
|
47
|
+
* override must not be used for a mainnet read. Unset → built-in endpoints.
|
|
48
|
+
*/
|
|
49
|
+
stellarRpc?: {
|
|
50
|
+
rpcUrl: string;
|
|
51
|
+
network: IStellarNetwork;
|
|
52
|
+
};
|
|
43
53
|
headers?: IWSMonitorHeaders;
|
|
44
54
|
/**
|
|
45
55
|
* Portfolio mode: when true, the per-vault EVM getter does NOT null out a
|