@augustdigital/sdk 8.15.0 → 8.16.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/lib/adapters/solana/constants.d.ts +12 -4
- package/lib/adapters/solana/constants.js +23 -6
- package/lib/adapters/solana/idl/vault-idl.d.ts +103 -169
- package/lib/adapters/solana/idl/vault-idl.js +461 -475
- package/lib/adapters/solana/index.d.ts +40 -5
- package/lib/adapters/solana/index.js +47 -3
- package/lib/adapters/solana/utils.d.ts +1 -4
- package/lib/adapters/solana/utils.js +24 -10
- package/lib/core/analytics/method-taxonomy.js +4 -0
- package/lib/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/modules/vaults/getters.d.ts +34 -0
- package/lib/modules/vaults/getters.js +57 -41
- package/lib/modules/vaults/utils.d.ts +19 -1
- package/lib/modules/vaults/utils.js +30 -0
- package/lib/sdk.d.ts +1645 -1474
- package/lib/types/vaults.d.ts +88 -0
- package/lib/types/webserver.d.ts +47 -0
- package/package.json +2 -1
|
@@ -19,7 +19,7 @@ export declare const Solana: {
|
|
|
19
19
|
isSolanaAddress: typeof import("./utils").isSolanaAddress;
|
|
20
20
|
isSolana: (signature: string) => boolean;
|
|
21
21
|
getProgram: ({ provider, idl, programId, network, }: {
|
|
22
|
-
idl
|
|
22
|
+
idl?: any;
|
|
23
23
|
programId?: string;
|
|
24
24
|
} & import("./types").ISolanaConnectionOptions) => import("@coral-xyz/anchor").Program<any>;
|
|
25
25
|
getProvider: ({ connection, publicKey, signTransaction, }: {
|
|
@@ -83,9 +83,6 @@ export declare const Solana: {
|
|
|
83
83
|
"mainnet-beta": {
|
|
84
84
|
vault: string;
|
|
85
85
|
};
|
|
86
|
-
testnet: {
|
|
87
|
-
vault: string;
|
|
88
|
-
};
|
|
89
86
|
localnet: {
|
|
90
87
|
vault: string;
|
|
91
88
|
};
|
|
@@ -128,7 +125,36 @@ declare class SolanaAdapter {
|
|
|
128
125
|
get connection(): web3.Connection;
|
|
129
126
|
get provider(): AnchorProvider;
|
|
130
127
|
setWalletProvider(_publicKey: PublicKey | string, signTransaction: (<T extends Transaction | web3.VersionedTransaction>(transaction: T) => Promise<T>) | undefined): void;
|
|
131
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Build an Anchor `Program` bound to this adapter's provider and network.
|
|
130
|
+
*
|
|
131
|
+
* The program id is resolved as: explicit `programId` → the `address` carried
|
|
132
|
+
* by a custom `programIdl` → the canonical deployment for this adapter's
|
|
133
|
+
* network. A copy of the SDK's bundled `vaultIdl` defers to the network
|
|
134
|
+
* default (its embedded `address` is a build stamp, not network-aware), so
|
|
135
|
+
* forwarding it on devnet never silently targets the mainnet program.
|
|
136
|
+
*
|
|
137
|
+
* @param programIdl - Anchor IDL to bind. Defaults to the SDK's bundled
|
|
138
|
+
* `august_vault` IDL. A custom IDL's `address` is honoured when it differs
|
|
139
|
+
* from the bundled build stamp.
|
|
140
|
+
* @param programId - Base58 program id that overrides both the IDL's address
|
|
141
|
+
* and the network default. Use this to target a non-canonical deployment.
|
|
142
|
+
* @returns An Anchor `Program` for the resolved program id. No RPC is made —
|
|
143
|
+
* construction is offline.
|
|
144
|
+
* @throws {@link AugustValidationError} if no program id can be resolved
|
|
145
|
+
* because this adapter's network has no canonical deployment (e.g.
|
|
146
|
+
* `testnet`) and neither `programId` nor a custom IDL address was supplied.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* // canonical program on the adapter's network
|
|
151
|
+
* const program = sdk.solana.getProgram();
|
|
152
|
+
*
|
|
153
|
+
* // a different deployment, same interface
|
|
154
|
+
* const legacy = sdk.solana.getProgram(vaultIdl, '7B8n9vL51b6ibqRAd1adZsi3x3kxtq5NZaondh22Vkyq');
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
getProgram(programIdl?: any, programId?: string): import("@coral-xyz/anchor").Program<any>;
|
|
132
158
|
getVaultState(vaultProgramId: PublicKey | string, idl: any, vaultAddress?: PublicKey | string): Promise<{
|
|
133
159
|
vaultState: unknown | null;
|
|
134
160
|
depositMintDecimals: number | null;
|
|
@@ -193,6 +219,15 @@ declare class SolanaAdapter {
|
|
|
193
219
|
* @param redeemShares `bigint` (raw share units) or `number` (UI amount).
|
|
194
220
|
*/
|
|
195
221
|
vaultRedeem(vaultProgramId: PublicKey | string, idl: any, publicKey: PublicKey | string, redeemShares: number | bigint, sendTransaction: (transaction: Transaction | web3.VersionedTransaction, connection: Connection, options?: SendTransactionOptions) => Promise<web3.TransactionSignature>, vaultAddress?: PublicKey | string): Promise<any>;
|
|
222
|
+
/**
|
|
223
|
+
* Canonical program id for a program type on this adapter's network.
|
|
224
|
+
*
|
|
225
|
+
* @param type - Program to look up. Only `'vault'` (august_vault) exists today.
|
|
226
|
+
* @returns The base58 program id.
|
|
227
|
+
* @throws {@link AugustValidationError} if the canonical program is not
|
|
228
|
+
* deployed on this adapter's network (e.g. `testnet`). Previously an
|
|
229
|
+
* unmapped network threw a bare `TypeError`.
|
|
230
|
+
*/
|
|
196
231
|
getProgramId(type: 'vault'): string;
|
|
197
232
|
}
|
|
198
233
|
export default SolanaAdapter;
|
|
@@ -46,6 +46,7 @@ const web3_js_1 = require("@solana/web3.js");
|
|
|
46
46
|
const utils_1 = require("./utils");
|
|
47
47
|
const vault_actions_1 = require("./vault.actions");
|
|
48
48
|
const constants_1 = require("./constants");
|
|
49
|
+
const core_1 = require("../../core");
|
|
49
50
|
exports.Solana = {
|
|
50
51
|
utils: utils_1.SolanaUtils,
|
|
51
52
|
constants: SolanaConstants,
|
|
@@ -108,11 +109,41 @@ class SolanaAdapter {
|
|
|
108
109
|
});
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Build an Anchor `Program` bound to this adapter's provider and network.
|
|
114
|
+
*
|
|
115
|
+
* The program id is resolved as: explicit `programId` → the `address` carried
|
|
116
|
+
* by a custom `programIdl` → the canonical deployment for this adapter's
|
|
117
|
+
* network. A copy of the SDK's bundled `vaultIdl` defers to the network
|
|
118
|
+
* default (its embedded `address` is a build stamp, not network-aware), so
|
|
119
|
+
* forwarding it on devnet never silently targets the mainnet program.
|
|
120
|
+
*
|
|
121
|
+
* @param programIdl - Anchor IDL to bind. Defaults to the SDK's bundled
|
|
122
|
+
* `august_vault` IDL. A custom IDL's `address` is honoured when it differs
|
|
123
|
+
* from the bundled build stamp.
|
|
124
|
+
* @param programId - Base58 program id that overrides both the IDL's address
|
|
125
|
+
* and the network default. Use this to target a non-canonical deployment.
|
|
126
|
+
* @returns An Anchor `Program` for the resolved program id. No RPC is made —
|
|
127
|
+
* construction is offline.
|
|
128
|
+
* @throws {@link AugustValidationError} if no program id can be resolved
|
|
129
|
+
* because this adapter's network has no canonical deployment (e.g.
|
|
130
|
+
* `testnet`) and neither `programId` nor a custom IDL address was supplied.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* // canonical program on the adapter's network
|
|
135
|
+
* const program = sdk.solana.getProgram();
|
|
136
|
+
*
|
|
137
|
+
* // a different deployment, same interface
|
|
138
|
+
* const legacy = sdk.solana.getProgram(vaultIdl, '7B8n9vL51b6ibqRAd1adZsi3x3kxtq5NZaondh22Vkyq');
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
getProgram(programIdl, programId) {
|
|
112
142
|
return utils_1.SolanaUtils.getProgram({
|
|
113
143
|
network: this._network,
|
|
114
144
|
provider: this._provider,
|
|
115
145
|
idl: programIdl,
|
|
146
|
+
programId,
|
|
116
147
|
});
|
|
117
148
|
}
|
|
118
149
|
async getVaultState(vaultProgramId, idl, vaultAddress) {
|
|
@@ -238,9 +269,22 @@ class SolanaAdapter {
|
|
|
238
269
|
sendTransaction,
|
|
239
270
|
});
|
|
240
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Canonical program id for a program type on this adapter's network.
|
|
274
|
+
*
|
|
275
|
+
* @param type - Program to look up. Only `'vault'` (august_vault) exists today.
|
|
276
|
+
* @returns The base58 program id.
|
|
277
|
+
* @throws {@link AugustValidationError} if the canonical program is not
|
|
278
|
+
* deployed on this adapter's network (e.g. `testnet`). Previously an
|
|
279
|
+
* unmapped network threw a bare `TypeError`.
|
|
280
|
+
*/
|
|
241
281
|
getProgramId(type) {
|
|
242
|
-
const
|
|
243
|
-
|
|
282
|
+
const deployment = (0, constants_1.getDeployedProgramIds)(this._network);
|
|
283
|
+
if (!deployment) {
|
|
284
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', `No canonical august_vault deployment for Solana network '${this._network}'. ` +
|
|
285
|
+
`Deployed networks: ${Object.keys(constants_1.programIds).join(', ')}.`);
|
|
286
|
+
}
|
|
287
|
+
return deployment[type];
|
|
244
288
|
}
|
|
245
289
|
}
|
|
246
290
|
exports.default = SolanaAdapter;
|
|
@@ -9,7 +9,7 @@ import { isSolanaAddress } from '../../core/helpers/chain-address';
|
|
|
9
9
|
export { isSolanaAddress };
|
|
10
10
|
declare function isSolana(signature: string): boolean;
|
|
11
11
|
declare function getProgram({ provider, idl, programId, network, }: {
|
|
12
|
-
idl
|
|
12
|
+
idl?: any;
|
|
13
13
|
programId?: string;
|
|
14
14
|
} & ISolanaConnectionOptions): Program<any>;
|
|
15
15
|
declare function getBestRpcEndpoint({ endpoint, network, connection, }: ISolanaConnectionOptions): Promise<"http://127.0.0.1:8899" | `https://${string}`>;
|
|
@@ -132,9 +132,6 @@ export declare const SolanaUtils: {
|
|
|
132
132
|
"mainnet-beta": {
|
|
133
133
|
vault: string;
|
|
134
134
|
};
|
|
135
|
-
testnet: {
|
|
136
|
-
vault: string;
|
|
137
|
-
};
|
|
138
135
|
localnet: {
|
|
139
136
|
vault: string;
|
|
140
137
|
};
|
|
@@ -39,18 +39,32 @@ function isSolana(signature) {
|
|
|
39
39
|
return (signature.length >= 80 && signature.length <= 100);
|
|
40
40
|
}
|
|
41
41
|
function getProgram({ provider, idl, programId, network, }) {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const baseIdl = idl ?? constants_1.vaultIdl;
|
|
43
|
+
// Precedence: explicit `programId` → a custom IDL's `address` → the network
|
|
44
|
+
// default → the bundled IDL's embedded `address`.
|
|
45
|
+
//
|
|
46
|
+
// The custom-address check compares by VALUE, not reference. Keying off
|
|
47
|
+
// `idl === vaultIdl` broke for a structurally-identical-but-not-reference-equal
|
|
48
|
+
// copy (second bundle instance, or JSON round-tripped), silently retargeting
|
|
49
|
+
// devnet callers at mainnet. Every copy carries the same build stamp, so it
|
|
50
|
+
// still defers to the network default — but a genuinely custom IDL is honoured.
|
|
51
|
+
const suppliedAddress = typeof baseIdl?.address === 'string' && baseIdl.address !== constants_1.vaultIdl.address
|
|
52
|
+
? baseIdl.address
|
|
53
|
+
: undefined;
|
|
54
|
+
const explicitId = programId ?? suppliedAddress;
|
|
55
|
+
if (explicitId) {
|
|
56
|
+
return new anchor_1.Program({ ...baseIdl, address: explicitId }, provider);
|
|
44
57
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
if (network) {
|
|
59
|
+
const networkDefault = (0, constants_1.getDeployedProgramIds)(network)?.vault;
|
|
60
|
+
if (!networkDefault) {
|
|
61
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', `No canonical august_vault deployment for Solana network '${network}'. ` +
|
|
62
|
+
`Deployed networks: ${Object.keys(constants_1.programIds).join(', ')}. ` +
|
|
63
|
+
`Pass an explicit \`programId\` (or an IDL carrying the target \`address\`) to target a program on this network.`);
|
|
64
|
+
}
|
|
65
|
+
return new anchor_1.Program({ ...baseIdl, address: networkDefault }, provider);
|
|
52
66
|
}
|
|
53
|
-
return new anchor_1.Program(
|
|
67
|
+
return new anchor_1.Program(baseIdl, provider);
|
|
54
68
|
}
|
|
55
69
|
// ============================================================================
|
|
56
70
|
// SOLANA PROVIDERS
|
|
@@ -77,6 +77,10 @@ exports.METHOD_CATEGORIES = {
|
|
|
77
77
|
fetchUserTokenBalance: 'read.solana',
|
|
78
78
|
fetchUserShareBalance: 'read.solana',
|
|
79
79
|
fetchUserShareBalanceRaw: 'read.solana',
|
|
80
|
+
// Both are offline resolvers (no RPC) — classified so the partner dashboard
|
|
81
|
+
// sees program-targeting calls instead of dropping them into 'unknown'.
|
|
82
|
+
getProgram: 'read.solana',
|
|
83
|
+
getProgramId: 'read.solana',
|
|
80
84
|
// submitTransaction broadcasts a signed Soroban tx — it mutates on-chain
|
|
81
85
|
// state and belongs in the write bucket even though Stellar adapter call
|
|
82
86
|
// shape mirrors the read helpers (returns a hash, takes signed XDR).
|
|
@@ -339,6 +339,31 @@ export interface PnlHistoryConsistencyParams {
|
|
|
339
339
|
* ```
|
|
340
340
|
*/
|
|
341
341
|
export declare function assertPnlHistoryConsistent({ vault, wallet, hasDeposits, totalWithdrawnRaw, currentPositionRaw, }: PnlHistoryConsistencyParams): void;
|
|
342
|
+
/**
|
|
343
|
+
* Sum a wallet's per-asset deposits into the vault's own decimals, at face
|
|
344
|
+
* value — 1 unit of any deposited asset is treated as 1 unit of the vault's
|
|
345
|
+
* reporting currency, with no live price conversion.
|
|
346
|
+
*
|
|
347
|
+
* Used by {@link getVaultUserLifetimePnl} for `totalDeposited`. Pre-deposit
|
|
348
|
+
* multi-asset vaults only accept dollar-pegged stablecoins (USDC, USDT, DAI,
|
|
349
|
+
* USDS, RLUSD, etc.) into a dollar-denominated vault, so face value is a
|
|
350
|
+
* reasonable stand-in for USD value. This intentionally does NOT re-quote
|
|
351
|
+
* through a live price oracle: doing so priced a historical deposit at
|
|
352
|
+
* today's market rate, so `totalDeposited` (and lifetimePnl) drifted between
|
|
353
|
+
* calls purely from stablecoin peg noise, and approximately re-inflated
|
|
354
|
+
* deposits back toward face value — which silently excluded the vault's
|
|
355
|
+
* entry fee from PnL. The fee is money the user actually paid, so PnL must
|
|
356
|
+
* be computed net of it.
|
|
357
|
+
*
|
|
358
|
+
* @param depositsByAsset - Raw deposited amount per asset, keyed by
|
|
359
|
+
* lowercased asset address (or `'default'` / `'lz-default'` for
|
|
360
|
+
* single-asset / cross-chain rows).
|
|
361
|
+
* @param assetDecimals - Decimals for each key in `depositsByAsset`.
|
|
362
|
+
* @param vaultDecimals - The vault's own decimals — the target scale every
|
|
363
|
+
* asset is rescaled to.
|
|
364
|
+
* @returns Total deposited, in raw base units at `vaultDecimals`.
|
|
365
|
+
*/
|
|
366
|
+
export declare function sumDepositsAtFaceValue(depositsByAsset: Map<string, bigint>, assetDecimals: Map<string, number>, vaultDecimals: number): bigint;
|
|
342
367
|
/**
|
|
343
368
|
* Calculate lifetime PnL for a user in a specific vault.
|
|
344
369
|
*
|
|
@@ -347,6 +372,15 @@ export declare function assertPnlHistoryConsistent({ vault, wallet, hasDeposits,
|
|
|
347
372
|
* 2. Fetch user's current assets (balanceOf * sharePrice via convertToAssets)
|
|
348
373
|
* 3. Calculate PnL = assets withdrawn + current assets - assets deposited
|
|
349
374
|
*
|
|
375
|
+
* `totalDeposited` is the face value of what the user actually paid in
|
|
376
|
+
* (each deposited asset's raw amount, rescaled to the vault's decimals — no
|
|
377
|
+
* live price conversion), so any entry/exit fee the vault charges is
|
|
378
|
+
* automatically counted as a cost against PnL rather than excluded from it.
|
|
379
|
+
* Pre-deposit multi-asset vaults are assumed to only accept dollar-pegged
|
|
380
|
+
* stablecoins into a dollar-denominated vault; a vault accepting a
|
|
381
|
+
* non-pegged deposit asset would need deposit-time price conversion, which
|
|
382
|
+
* this function does not perform.
|
|
383
|
+
*
|
|
350
384
|
* @param vault - Vault contract address
|
|
351
385
|
* @param wallet - User wallet address
|
|
352
386
|
* @param options - RPC configuration and service options
|
|
@@ -54,6 +54,7 @@ exports.registerUserForPoints = registerUserForPoints;
|
|
|
54
54
|
exports.fetchPointsLeaderboard = fetchPointsLeaderboard;
|
|
55
55
|
exports.getYieldLastRealizedOn = getYieldLastRealizedOn;
|
|
56
56
|
exports.assertPnlHistoryConsistent = assertPnlHistoryConsistent;
|
|
57
|
+
exports.sumDepositsAtFaceValue = sumDepositsAtFaceValue;
|
|
57
58
|
exports.getVaultUserLifetimePnl = getVaultUserLifetimePnl;
|
|
58
59
|
exports.getVaultPnl = getVaultPnl;
|
|
59
60
|
exports.getVaultHistoricalTimeseries = getVaultHistoricalTimeseries;
|
|
@@ -1898,6 +1899,46 @@ function assertPnlHistoryConsistent({ vault, wallet, hasDeposits, totalWithdrawn
|
|
|
1898
1899
|
throw new errors_1.AugustHistoryUnavailableError(`#getVaultUserLifetimePnl::${vault}::${wallet}:transaction history unavailable — a live position exists but no deposits or withdrawals were indexed, so lifetime PnL cannot be computed without over-reporting the entire position as profit`, { context: { vault, wallet } });
|
|
1899
1900
|
}
|
|
1900
1901
|
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Sum a wallet's per-asset deposits into the vault's own decimals, at face
|
|
1904
|
+
* value — 1 unit of any deposited asset is treated as 1 unit of the vault's
|
|
1905
|
+
* reporting currency, with no live price conversion.
|
|
1906
|
+
*
|
|
1907
|
+
* Used by {@link getVaultUserLifetimePnl} for `totalDeposited`. Pre-deposit
|
|
1908
|
+
* multi-asset vaults only accept dollar-pegged stablecoins (USDC, USDT, DAI,
|
|
1909
|
+
* USDS, RLUSD, etc.) into a dollar-denominated vault, so face value is a
|
|
1910
|
+
* reasonable stand-in for USD value. This intentionally does NOT re-quote
|
|
1911
|
+
* through a live price oracle: doing so priced a historical deposit at
|
|
1912
|
+
* today's market rate, so `totalDeposited` (and lifetimePnl) drifted between
|
|
1913
|
+
* calls purely from stablecoin peg noise, and approximately re-inflated
|
|
1914
|
+
* deposits back toward face value — which silently excluded the vault's
|
|
1915
|
+
* entry fee from PnL. The fee is money the user actually paid, so PnL must
|
|
1916
|
+
* be computed net of it.
|
|
1917
|
+
*
|
|
1918
|
+
* @param depositsByAsset - Raw deposited amount per asset, keyed by
|
|
1919
|
+
* lowercased asset address (or `'default'` / `'lz-default'` for
|
|
1920
|
+
* single-asset / cross-chain rows).
|
|
1921
|
+
* @param assetDecimals - Decimals for each key in `depositsByAsset`.
|
|
1922
|
+
* @param vaultDecimals - The vault's own decimals — the target scale every
|
|
1923
|
+
* asset is rescaled to.
|
|
1924
|
+
* @returns Total deposited, in raw base units at `vaultDecimals`.
|
|
1925
|
+
*/
|
|
1926
|
+
function sumDepositsAtFaceValue(depositsByAsset, assetDecimals, vaultDecimals) {
|
|
1927
|
+
let total = BigInt(0);
|
|
1928
|
+
for (const [assetAddress, rawAmount] of depositsByAsset) {
|
|
1929
|
+
const assetDec = assetDecimals.get(assetAddress) ?? vaultDecimals;
|
|
1930
|
+
if (assetDec === vaultDecimals) {
|
|
1931
|
+
total += rawAmount;
|
|
1932
|
+
}
|
|
1933
|
+
else if (assetDec > vaultDecimals) {
|
|
1934
|
+
total += rawAmount / BigInt(10 ** (assetDec - vaultDecimals));
|
|
1935
|
+
}
|
|
1936
|
+
else {
|
|
1937
|
+
total += rawAmount * BigInt(10 ** (vaultDecimals - assetDec));
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
return total;
|
|
1941
|
+
}
|
|
1901
1942
|
/**
|
|
1902
1943
|
* Calculate lifetime PnL for a user in a specific vault.
|
|
1903
1944
|
*
|
|
@@ -1906,6 +1947,15 @@ function assertPnlHistoryConsistent({ vault, wallet, hasDeposits, totalWithdrawn
|
|
|
1906
1947
|
* 2. Fetch user's current assets (balanceOf * sharePrice via convertToAssets)
|
|
1907
1948
|
* 3. Calculate PnL = assets withdrawn + current assets - assets deposited
|
|
1908
1949
|
*
|
|
1950
|
+
* `totalDeposited` is the face value of what the user actually paid in
|
|
1951
|
+
* (each deposited asset's raw amount, rescaled to the vault's decimals — no
|
|
1952
|
+
* live price conversion), so any entry/exit fee the vault charges is
|
|
1953
|
+
* automatically counted as a cost against PnL rather than excluded from it.
|
|
1954
|
+
* Pre-deposit multi-asset vaults are assumed to only accept dollar-pegged
|
|
1955
|
+
* stablecoins into a dollar-denominated vault; a vault accepting a
|
|
1956
|
+
* non-pegged deposit asset would need deposit-time price conversion, which
|
|
1957
|
+
* this function does not perform.
|
|
1958
|
+
*
|
|
1909
1959
|
* @param vault - Vault contract address
|
|
1910
1960
|
* @param wallet - User wallet address
|
|
1911
1961
|
* @param options - RPC configuration and service options
|
|
@@ -2069,32 +2119,7 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
|
|
|
2069
2119
|
assetDecimals.set(assetAddress, dec);
|
|
2070
2120
|
}
|
|
2071
2121
|
}));
|
|
2072
|
-
|
|
2073
|
-
const totalDepositedRaw = new Map();
|
|
2074
|
-
for (const [assetAddress, rawAmount] of depositsByAsset) {
|
|
2075
|
-
const assetDec = assetDecimals.get(assetAddress) ?? decimals;
|
|
2076
|
-
totalDepositedRaw.set(assetAddress, (0, core_1.toNormalizedBn)(rawAmount, assetDec));
|
|
2077
|
-
}
|
|
2078
|
-
// Fetch USD prices for each unique deposit asset in parallel
|
|
2079
|
-
const assetPrices = new Map();
|
|
2080
|
-
if (tokenizedVault.chain) {
|
|
2081
|
-
await Promise.all(Array.from(depositsByAsset.keys()).map(async (assetAddress) => {
|
|
2082
|
-
if (assetAddress === 'default' || assetAddress === 'lz-default') {
|
|
2083
|
-
// Will use underlying asset price later
|
|
2084
|
-
assetPrices.set(assetAddress, 0);
|
|
2085
|
-
}
|
|
2086
|
-
else {
|
|
2087
|
-
try {
|
|
2088
|
-
const price = await (0, core_1.fetchTokenPriceByAddress)(assetAddress, tokenizedVault.chain, options?.headers);
|
|
2089
|
-
assetPrices.set(assetAddress, price);
|
|
2090
|
-
}
|
|
2091
|
-
catch (error) {
|
|
2092
|
-
core_1.Logger.log.warn('getVaultUserLifetimePnl:fetchAssetPrice', `Failed to fetch price for ${assetAddress}: ${error}`);
|
|
2093
|
-
assetPrices.set(assetAddress, 0);
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
}));
|
|
2097
|
-
}
|
|
2122
|
+
const totalDepositedNativeRaw = sumDepositsAtFaceValue(depositsByAsset, assetDecimals, decimals);
|
|
2098
2123
|
const { underlyingTokenSymbol, underlyingAssetAddress } = await underlyingSymbolPromise;
|
|
2099
2124
|
// Calculate current position value and fetch price in parallel
|
|
2100
2125
|
let currentPositionValue = (0, core_1.toNormalizedBn)(0, decimals);
|
|
@@ -2246,15 +2271,12 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
|
|
|
2246
2271
|
if (tokenPriceUsd <= 0) {
|
|
2247
2272
|
core_1.Logger.log.warn('getVaultUserLifetimePnl:tokenPrice', `Invalid underlying token price: ${tokenPriceUsd}, using fallback of 1`);
|
|
2248
2273
|
}
|
|
2249
|
-
//
|
|
2250
|
-
|
|
2251
|
-
for
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
: assetPrices.get(assetAddress) || effectiveTokenPrice;
|
|
2256
|
-
totalDepositedUsd += Number(normalizedAmount.normalized) * assetPrice;
|
|
2257
|
-
}
|
|
2274
|
+
// totalDeposited is already in the vault's own decimals (face value,
|
|
2275
|
+
// computed above) — convert to USD with the same underlying price used
|
|
2276
|
+
// for the other two legs below, so all three legs share one conversion
|
|
2277
|
+
// factor instead of drifting independently against each other.
|
|
2278
|
+
const totalDeposited = (0, core_1.toNormalizedBn)(totalDepositedNativeRaw, decimals);
|
|
2279
|
+
const totalDepositedUsd = Number(totalDeposited.normalized) * effectiveTokenPrice;
|
|
2258
2280
|
const totalWithdrawnUsd = Number(totalWithdrawn.normalized) * effectiveTokenPrice;
|
|
2259
2281
|
const currentPositionValueUsd = Number(currentPositionValue.normalized) * effectiveTokenPrice;
|
|
2260
2282
|
// Calculate lifetimePnl in USD first (since deposits may be in different tokens with different prices)
|
|
@@ -2262,12 +2284,6 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
|
|
|
2262
2284
|
// Use BigInt-scaled arithmetic to minimize precision loss when converting USD to native
|
|
2263
2285
|
const scaleFactor = BigInt(10 ** 18); // High precision scale
|
|
2264
2286
|
const tokenPriceScaled = BigInt(Math.round(effectiveTokenPrice * Number(scaleFactor)));
|
|
2265
|
-
// Convert totalDepositedUsd to native units for backwards compatibility
|
|
2266
|
-
const totalDepositedUsdScaled = BigInt(Math.round(totalDepositedUsd * Number(scaleFactor)));
|
|
2267
|
-
const totalDepositedNativeRaw = tokenPriceScaled > BigInt(0)
|
|
2268
|
-
? (totalDepositedUsdScaled * BigInt(10 ** decimals)) / tokenPriceScaled
|
|
2269
|
-
: BigInt(0);
|
|
2270
|
-
const totalDeposited = (0, core_1.toNormalizedBn)(totalDepositedNativeRaw, decimals);
|
|
2271
2287
|
// Convert lifetimePnlUsd to native units
|
|
2272
2288
|
const lifetimePnlUsdScaled = BigInt(Math.round(lifetimePnlUsd * Number(scaleFactor)));
|
|
2273
2289
|
const lifetimePnlRaw = tokenPriceScaled > BigInt(0)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IAddress, IContractRunner, INormalizedNumber, IPoolFunctions, ITokenizedVault, IVault } from '../../types';
|
|
1
|
+
import type { IAddress, IContractRunner, INormalizedNumber, IPoolFunctions, ITokenizedVault, IVault, IVaultFreshness } from '../../types';
|
|
2
2
|
import { ethers } from 'ethers';
|
|
3
3
|
import type { WalletClient } from 'viem';
|
|
4
4
|
/**
|
|
@@ -59,6 +59,24 @@ export declare function getYieldLastRealizedOn(provider: IContractRunner, vaultA
|
|
|
59
59
|
* Shared across all chain adapters (EVM, Solana, Stellar).
|
|
60
60
|
*/
|
|
61
61
|
export declare function formatVaultApy(tokenizedVault: ITokenizedVault): IVault['apy'];
|
|
62
|
+
/**
|
|
63
|
+
* Map the backend `freshness` object onto the camelCased {@link IVaultFreshness}
|
|
64
|
+
* shape carried on `IVault`. Shared across all chain adapters (EVM, Solana,
|
|
65
|
+
* Stellar) so every surface reports provenance identically.
|
|
66
|
+
*
|
|
67
|
+
* Each of the three stamps is passed through independently: a missing member
|
|
68
|
+
* becomes `null` and is never defaulted from one of the others. That is the point
|
|
69
|
+
* of the object — `apy_computed_at` and `share_ratio_snapshot_at` describe two
|
|
70
|
+
* different pipelines, and collapsing them would erase the very staleness signal
|
|
71
|
+
* (snapshot newer than APY) the field exists to expose.
|
|
72
|
+
*
|
|
73
|
+
* @param tokenizedVault - Backend tokenized-vault payload. `freshness` is absent on
|
|
74
|
+
* responses predating the backend change, and may be explicitly null.
|
|
75
|
+
* @returns The mapped freshness object, or `null` when the backend reported none.
|
|
76
|
+
* Values are the backend's RFC 3339 UTC strings verbatim — no reformatting, so
|
|
77
|
+
* the trailing `Z` is preserved.
|
|
78
|
+
*/
|
|
79
|
+
export declare function mapVaultFreshness(tokenizedVault: ITokenizedVault | undefined | null): IVaultFreshness | null;
|
|
62
80
|
/**
|
|
63
81
|
* Map strategists from backend subaccounts or hardcoded list.
|
|
64
82
|
* Shared across all chain adapters.
|
|
@@ -6,6 +6,7 @@ exports.getVaultRewards = getVaultRewards;
|
|
|
6
6
|
exports.getIdleAssets = getIdleAssets;
|
|
7
7
|
exports.getYieldLastRealizedOn = getYieldLastRealizedOn;
|
|
8
8
|
exports.formatVaultApy = formatVaultApy;
|
|
9
|
+
exports.mapVaultFreshness = mapVaultFreshness;
|
|
9
10
|
exports.mapStrategists = mapStrategists;
|
|
10
11
|
exports.mapComposabilityIntegrations = mapComposabilityIntegrations;
|
|
11
12
|
exports.backendTvlToNormalizedBn = backendTvlToNormalizedBn;
|
|
@@ -235,6 +236,33 @@ function formatVaultApy(tokenizedVault) {
|
|
|
235
236
|
underlyingApy: (reported_apy?.underlying_apy ?? 0) * 100,
|
|
236
237
|
};
|
|
237
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Map the backend `freshness` object onto the camelCased {@link IVaultFreshness}
|
|
241
|
+
* shape carried on `IVault`. Shared across all chain adapters (EVM, Solana,
|
|
242
|
+
* Stellar) so every surface reports provenance identically.
|
|
243
|
+
*
|
|
244
|
+
* Each of the three stamps is passed through independently: a missing member
|
|
245
|
+
* becomes `null` and is never defaulted from one of the others. That is the point
|
|
246
|
+
* of the object — `apy_computed_at` and `share_ratio_snapshot_at` describe two
|
|
247
|
+
* different pipelines, and collapsing them would erase the very staleness signal
|
|
248
|
+
* (snapshot newer than APY) the field exists to expose.
|
|
249
|
+
*
|
|
250
|
+
* @param tokenizedVault - Backend tokenized-vault payload. `freshness` is absent on
|
|
251
|
+
* responses predating the backend change, and may be explicitly null.
|
|
252
|
+
* @returns The mapped freshness object, or `null` when the backend reported none.
|
|
253
|
+
* Values are the backend's RFC 3339 UTC strings verbatim — no reformatting, so
|
|
254
|
+
* the trailing `Z` is preserved.
|
|
255
|
+
*/
|
|
256
|
+
function mapVaultFreshness(tokenizedVault) {
|
|
257
|
+
const freshness = tokenizedVault?.freshness;
|
|
258
|
+
if (!freshness)
|
|
259
|
+
return null;
|
|
260
|
+
return {
|
|
261
|
+
apyComputedAt: freshness.apy_computed_at ?? null,
|
|
262
|
+
shareRatioSnapshotAt: freshness.share_ratio_snapshot_at ?? null,
|
|
263
|
+
cachedAt: freshness.cached_at ?? null,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
238
266
|
/**
|
|
239
267
|
* Map strategists from backend subaccounts or hardcoded list.
|
|
240
268
|
* Shared across all chain adapters.
|
|
@@ -398,6 +426,7 @@ function buildBackendVault(tokenizedVault, overrides) {
|
|
|
398
426
|
composabilityIntegrations: mapComposabilityIntegrations(tokenizedVault),
|
|
399
427
|
strategists: mapStrategists(tokenizedVault),
|
|
400
428
|
cachedAt: tokenizedVault.cached_at ?? null,
|
|
429
|
+
freshness: mapVaultFreshness(tokenizedVault),
|
|
401
430
|
dailyPnlPerShare: mapDailyPnlPerShare(tokenizedVault.daily_pnl_per_share),
|
|
402
431
|
enabled_historical_price_horizons: tokenizedVault.enabled_historical_price_horizons || [],
|
|
403
432
|
latest_reported_tvl: tokenizedVault.latest_reported_tvl,
|
|
@@ -615,6 +644,7 @@ async function buildFormattedVault(provider, tokenizedVault, contractCalls) {
|
|
|
615
644
|
defaultApyHorizon: tokenizedVault?.default_apy_horizon ?? null,
|
|
616
645
|
latest_reported_tvl: tokenizedVault?.latest_reported_tvl,
|
|
617
646
|
cachedAt: tokenizedVault?.cached_at ?? null,
|
|
647
|
+
freshness: mapVaultFreshness(tokenizedVault),
|
|
618
648
|
showCapFilled: tokenizedVault?.show_cap_filled ?? null,
|
|
619
649
|
apyOverride: tokenizedVault?.apy_override ?? null,
|
|
620
650
|
};
|