@augustdigital/sdk 9.6.0 → 9.7.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/README.md +1 -1
- package/lib/adapters/solana/types.d.ts +8 -0
- package/lib/adapters/solana/vault.actions.d.ts +6 -1
- package/lib/adapters/solana/vault.actions.js +99 -21
- package/lib/core/analytics/sanitize.d.ts +13 -0
- package/lib/core/analytics/sanitize.js +24 -12
- package/lib/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/core/auth/verify.js +2 -2
- package/lib/core/base.class.d.ts +8 -1
- package/lib/core/base.class.js +4 -1
- package/lib/core/fetcher.d.ts +11 -5
- package/lib/core/fetcher.js +10 -7
- package/lib/main.d.ts +2 -1
- package/lib/main.js +2 -1
- package/lib/modules/api/main.d.ts +6 -6
- package/lib/modules/api/main.js +6 -6
- package/lib/modules/sub-accounts/main.d.ts +7 -1
- package/lib/modules/sub-accounts/main.js +7 -1
- package/lib/modules/vaults/getters.d.ts +97 -11
- package/lib/modules/vaults/getters.js +169 -17
- package/lib/modules/vaults/main.d.ts +2 -1
- package/lib/modules/vaults/main.js +2 -1
- package/lib/sdk.d.ts +1202 -1072
- package/lib/services/debank/fetcher.js +1 -1
- package/lib/services/subgraph/vaults.d.ts +18 -1
- package/lib/services/subgraph/vaults.js +19 -3
- package/lib/types/vaults.d.ts +5 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type IAddress, type IHistoricalTimeseriesResponse, type INormalizedNumber, type ITokenizedVault, type ISubgraphWithdrawProccessed, type IVault, type IVaultAllocations, type IVaultAnnualizedApy, type IVaultAvailableRedemption, type IVaultLoan, type IVaultPosition, type IVaultRedemptionHistoryItem, type IVaultSummary, type IVaultWithdrawals, type IVaultPendingRedemptions, type IActiveStakingPosition, type IVaultHistoricalParams, type IVaultUserLifetimePnl, type IVaultBorrowerHealthFactor, type IVaultPnl, type VaultAddress } from '../../types';
|
|
1
|
+
import { type IAddress, type IHistoricalTimeseriesPoint, type IHistoricalTimeseriesResponse, type INormalizedNumber, type ISubgraphTransfer, type ITokenizedVault, type ISubgraphWithdrawProccessed, type IVault, type IVaultAllocations, type IVaultAnnualizedApy, type IVaultAvailableRedemption, type IVaultLoan, type IVaultPosition, type IVaultRedemptionHistoryItem, type IVaultSummary, type IVaultWithdrawals, type IVaultPendingRedemptions, type IActiveStakingPosition, type IVaultHistoricalParams, type IVaultUserLifetimePnl, type IVaultBorrowerHealthFactor, type IVaultPnl, type VaultAddress } from '../../types';
|
|
2
2
|
import type { IVaultBaseOptions } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Vault Data Getters
|
|
@@ -337,15 +337,10 @@ export interface PnlHistoryConsistencyParams {
|
|
|
337
337
|
* Pure and synchronous — no RPC, no I/O — so it is safe to call inside the hot
|
|
338
338
|
* path with no added latency.
|
|
339
339
|
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
* not a regression — the pre-guard formula reported that same position as 100%
|
|
345
|
-
* profit, which was also wrong — but the error message is misleading for that
|
|
346
|
-
* (rare) path. If receipt-token transfer becomes a real user flow, distinguish
|
|
347
|
-
* it here (e.g. by checking for a share-transfer event) rather than widening
|
|
348
|
-
* the guard.
|
|
340
|
+
* Shares received by a direct token transfer (never deposited, never withdrew)
|
|
341
|
+
* reach this guard as deposits: {@link getVaultUserLifetimePnl} prices them
|
|
342
|
+
* with {@link transferCostBasis} first. The guard still throws for such a
|
|
343
|
+
* wallet when its transfers cannot be read or priced.
|
|
349
344
|
*
|
|
350
345
|
* @param params - See {@link PnlHistoryConsistencyParams}.
|
|
351
346
|
* @returns `void` when the history is consistent.
|
|
@@ -438,6 +433,88 @@ export declare function assertPnlTokenPrice({ vault, wallet, asset, tokenPriceUs
|
|
|
438
433
|
* @returns Total deposited, in raw base units at `vaultDecimals`.
|
|
439
434
|
*/
|
|
440
435
|
export declare function sumDepositsAtFaceValue(depositsByAsset: Map<string, bigint>, assetDecimals: Map<string, number>, vaultDecimals: number): bigint;
|
|
436
|
+
/** The receipt-token transfer fields the transfer cost basis reads. */
|
|
437
|
+
export type PnlTransfer = Pick<ISubgraphTransfer, 'from' | 'to' | 'value' | 'timestamp_'>;
|
|
438
|
+
/**
|
|
439
|
+
* Keep the receipt-token transfers that moved shares between `wallet` and
|
|
440
|
+
* another holder.
|
|
441
|
+
*
|
|
442
|
+
* Vault shares are transferable ERC-20 tokens, so a wallet can hold a position
|
|
443
|
+
* it never deposited into — a migration airdrop, a DEX purchase, a transfer from
|
|
444
|
+
* another wallet. Mints (from the zero address), burns (to it) and transfers to
|
|
445
|
+
* or from the vault itself are dropped: those are the deposit and withdrawal
|
|
446
|
+
* legs the subgraph history already records, and counting them again would
|
|
447
|
+
* double the cost basis.
|
|
448
|
+
*
|
|
449
|
+
* @param transfers - Receipt-token transfers touching `wallet`, as returned by
|
|
450
|
+
* the vault's transfer subgraph.
|
|
451
|
+
* @param wallet - The wallet whose position is being valued.
|
|
452
|
+
* @param vault - The vault contract address.
|
|
453
|
+
* @returns The peer-to-peer transfers, in input order. Addresses compare
|
|
454
|
+
* case-insensitively.
|
|
455
|
+
* @example
|
|
456
|
+
* ```ts
|
|
457
|
+
* const peer = selectPeerTransfers(transfers, wallet, vault);
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
export declare function selectPeerTransfers<T extends PnlTransfer>(transfers: T[], wallet: IAddress, vault: IAddress): T[];
|
|
461
|
+
/** Inputs {@link transferCostBasis} prices. */
|
|
462
|
+
export interface TransferCostBasisParams {
|
|
463
|
+
/** Peer transfers touching `wallet` — see {@link selectPeerTransfers}. */
|
|
464
|
+
transfers: PnlTransfer[];
|
|
465
|
+
/** The wallet whose cost basis is being computed. */
|
|
466
|
+
wallet: IAddress;
|
|
467
|
+
/** The vault contract address, used for the error message and context. */
|
|
468
|
+
vault: IAddress;
|
|
469
|
+
/**
|
|
470
|
+
* The vault's daily history from `fetchVaultHistoricalTimeseries`, keyed by
|
|
471
|
+
* UTC date (`YYYY-MM-DD`). Only `asset_share_ratio` (assets per share) is
|
|
472
|
+
* read.
|
|
473
|
+
*/
|
|
474
|
+
history: Record<string, Pick<IHistoricalTimeseriesPoint, 'asset_share_ratio'>>;
|
|
475
|
+
}
|
|
476
|
+
/** Asset amounts that peer transfers moved into and out of a position. */
|
|
477
|
+
export interface TransferCostBasis {
|
|
478
|
+
/** Assets the received shares were worth on arrival, raw at vault decimals. */
|
|
479
|
+
receivedRaw: bigint;
|
|
480
|
+
/** Assets the sent shares were worth when they left, raw at vault decimals. */
|
|
481
|
+
sentRaw: bigint;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Value peer share transfers in the vault's asset, at the vault's share ratio
|
|
485
|
+
* on the day each one happened.
|
|
486
|
+
*
|
|
487
|
+
* Used by {@link getVaultUserLifetimePnl} when a wallet has no indexed
|
|
488
|
+
* deposits: received shares become its cost basis and sent shares count as
|
|
489
|
+
* withdrawn, so lifetime PnL for a transfer recipient reads "PnL since the
|
|
490
|
+
* shares arrived" rather than failing or reporting the whole position as
|
|
491
|
+
* profit. A day with no usable ratio takes the latest earlier day's, and a
|
|
492
|
+
* transfer older than the history takes the earliest day's.
|
|
493
|
+
*
|
|
494
|
+
* Sent shares count only against a received basis. A wallet that sent shares
|
|
495
|
+
* but received none gets a zero basis, so the lifetime-PnL history guard still
|
|
496
|
+
* decides for it instead of reporting the sent shares as profit.
|
|
497
|
+
*
|
|
498
|
+
* Pure and synchronous. Assumes, as the getter does, that shares and assets
|
|
499
|
+
* share the vault's decimals.
|
|
500
|
+
*
|
|
501
|
+
* @param params - See {@link TransferCostBasisParams}.
|
|
502
|
+
* @returns The received and sent asset amounts, raw at the vault's decimals;
|
|
503
|
+
* both zero when no shares were received.
|
|
504
|
+
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when no
|
|
505
|
+
* day in `history` carries a positive, finite `asset_share_ratio`.
|
|
506
|
+
* @example
|
|
507
|
+
* ```ts
|
|
508
|
+
* const { data } = await fetchVaultHistoricalTimeseries(vault, nDays);
|
|
509
|
+
* const { receivedRaw, sentRaw } = transferCostBasis({
|
|
510
|
+
* transfers: selectPeerTransfers(transfers, wallet, vault),
|
|
511
|
+
* wallet,
|
|
512
|
+
* vault,
|
|
513
|
+
* history: data,
|
|
514
|
+
* });
|
|
515
|
+
* ```
|
|
516
|
+
*/
|
|
517
|
+
export declare function transferCostBasis({ transfers, wallet, vault, history, }: TransferCostBasisParams): TransferCostBasis;
|
|
441
518
|
/**
|
|
442
519
|
* Calculate lifetime PnL for a user in a specific vault.
|
|
443
520
|
*
|
|
@@ -459,12 +536,21 @@ export declare function sumDepositsAtFaceValue(depositsByAsset: Map<string, bigi
|
|
|
459
536
|
* from the price server. There is no stand-in price: when none resolves the
|
|
460
537
|
* getter throws rather than returning USD values it cannot know.
|
|
461
538
|
*
|
|
539
|
+
* Shares received by transfer: when history has no deposit, peer transfers of
|
|
540
|
+
* the share token (see {@link selectPeerTransfers}) are valued at the vault's
|
|
541
|
+
* daily share ratio ({@link transferCostBasis}) — received shares become
|
|
542
|
+
* `totalDeposited`, sent shares add to `totalWithdrawn`. PnL for such a wallet
|
|
543
|
+
* therefore runs from the day its shares arrived. This costs one extra
|
|
544
|
+
* subgraph query, plus one backend timeseries request when transfers exist,
|
|
545
|
+
* and only for wallets with no indexed deposit.
|
|
546
|
+
*
|
|
462
547
|
* @param vault - Vault contract address
|
|
463
548
|
* @param wallet - User wallet address
|
|
464
549
|
* @param options - RPC configuration and service options
|
|
465
550
|
* @returns Lifetime PnL data in both native token and USD
|
|
466
551
|
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when a
|
|
467
|
-
* live position has no indexed deposits or
|
|
552
|
+
* live position has no indexed deposits, withdrawals or priceable share
|
|
553
|
+
* transfers.
|
|
468
554
|
* @throws {@link AugustPriceUnavailableError} (`PRICE_UNAVAILABLE`) when the
|
|
469
555
|
* underlying asset has no USD price, or `asset()` cannot be read.
|
|
470
556
|
*/
|
|
@@ -56,6 +56,8 @@ exports.getYieldLastRealizedOn = getYieldLastRealizedOn;
|
|
|
56
56
|
exports.assertPnlHistoryConsistent = assertPnlHistoryConsistent;
|
|
57
57
|
exports.assertPnlTokenPrice = assertPnlTokenPrice;
|
|
58
58
|
exports.sumDepositsAtFaceValue = sumDepositsAtFaceValue;
|
|
59
|
+
exports.selectPeerTransfers = selectPeerTransfers;
|
|
60
|
+
exports.transferCostBasis = transferCostBasis;
|
|
59
61
|
exports.getVaultUserLifetimePnl = getVaultUserLifetimePnl;
|
|
60
62
|
exports.getVaultPnl = getVaultPnl;
|
|
61
63
|
exports.getVaultHistoricalTimeseries = getVaultHistoricalTimeseries;
|
|
@@ -1541,13 +1543,26 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, o
|
|
|
1541
1543
|
}
|
|
1542
1544
|
}
|
|
1543
1545
|
const balance = (0, core_1.toNormalizedBn)(bal, decimals);
|
|
1544
|
-
//
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1546
|
+
// Redemption requests are keyed by receiver, so without an EVM
|
|
1547
|
+
// wallet there is nothing of the caller's to find. Skip the read
|
|
1548
|
+
// rather than delegate: the wallet-less path of
|
|
1549
|
+
// `getVaultAvailableRedemptions` lists EVERY receiver's processed
|
|
1550
|
+
// requests for the vault, which in a positions read surfaced other
|
|
1551
|
+
// users' withdrawals as the caller's and flipped the status to
|
|
1552
|
+
// REDEEM for every EVM vault in a Stellar/Solana-only session
|
|
1553
|
+
// (AUGUST-7938). Skipping also saves one subgraph query per EVM
|
|
1554
|
+
// vault for those callers.
|
|
1555
|
+
const { availableRedemptions, pendingRedemptions } = wallet && (0, ethers_1.isAddress)(wallet)
|
|
1556
|
+
? await getVaultAvailableRedemptions({
|
|
1557
|
+
vault: v.address,
|
|
1558
|
+
wallet,
|
|
1559
|
+
options,
|
|
1560
|
+
prefetchedReads: prefetched,
|
|
1561
|
+
})
|
|
1562
|
+
: {
|
|
1563
|
+
availableRedemptions: [],
|
|
1564
|
+
pendingRedemptions: [],
|
|
1565
|
+
};
|
|
1551
1566
|
// get aggregate redemption amount
|
|
1552
1567
|
const aggregateAvailableRedemptions = availableRedemptions.reduce((acc, curr) => acc + BigInt(curr.amount.raw), BigInt(0));
|
|
1553
1568
|
// return obj
|
|
@@ -2261,15 +2276,10 @@ async function getYieldLastRealizedOn({ vault, options, }) {
|
|
|
2261
2276
|
* Pure and synchronous — no RPC, no I/O — so it is safe to call inside the hot
|
|
2262
2277
|
* path with no added latency.
|
|
2263
2278
|
*
|
|
2264
|
-
*
|
|
2265
|
-
*
|
|
2266
|
-
*
|
|
2267
|
-
*
|
|
2268
|
-
* not a regression — the pre-guard formula reported that same position as 100%
|
|
2269
|
-
* profit, which was also wrong — but the error message is misleading for that
|
|
2270
|
-
* (rare) path. If receipt-token transfer becomes a real user flow, distinguish
|
|
2271
|
-
* it here (e.g. by checking for a share-transfer event) rather than widening
|
|
2272
|
-
* the guard.
|
|
2279
|
+
* Shares received by a direct token transfer (never deposited, never withdrew)
|
|
2280
|
+
* reach this guard as deposits: {@link getVaultUserLifetimePnl} prices them
|
|
2281
|
+
* with {@link transferCostBasis} first. The guard still throws for such a
|
|
2282
|
+
* wallet when its transfers cannot be read or priced.
|
|
2273
2283
|
*
|
|
2274
2284
|
* @param params - See {@link PnlHistoryConsistencyParams}.
|
|
2275
2285
|
* @returns `void` when the history is consistent.
|
|
@@ -2369,6 +2379,130 @@ function sumDepositsAtFaceValue(depositsByAsset, assetDecimals, vaultDecimals) {
|
|
|
2369
2379
|
}
|
|
2370
2380
|
return total;
|
|
2371
2381
|
}
|
|
2382
|
+
/**
|
|
2383
|
+
* Keep the receipt-token transfers that moved shares between `wallet` and
|
|
2384
|
+
* another holder.
|
|
2385
|
+
*
|
|
2386
|
+
* Vault shares are transferable ERC-20 tokens, so a wallet can hold a position
|
|
2387
|
+
* it never deposited into — a migration airdrop, a DEX purchase, a transfer from
|
|
2388
|
+
* another wallet. Mints (from the zero address), burns (to it) and transfers to
|
|
2389
|
+
* or from the vault itself are dropped: those are the deposit and withdrawal
|
|
2390
|
+
* legs the subgraph history already records, and counting them again would
|
|
2391
|
+
* double the cost basis.
|
|
2392
|
+
*
|
|
2393
|
+
* @param transfers - Receipt-token transfers touching `wallet`, as returned by
|
|
2394
|
+
* the vault's transfer subgraph.
|
|
2395
|
+
* @param wallet - The wallet whose position is being valued.
|
|
2396
|
+
* @param vault - The vault contract address.
|
|
2397
|
+
* @returns The peer-to-peer transfers, in input order. Addresses compare
|
|
2398
|
+
* case-insensitively.
|
|
2399
|
+
* @example
|
|
2400
|
+
* ```ts
|
|
2401
|
+
* const peer = selectPeerTransfers(transfers, wallet, vault);
|
|
2402
|
+
* ```
|
|
2403
|
+
*/
|
|
2404
|
+
function selectPeerTransfers(transfers, wallet, vault) {
|
|
2405
|
+
const self = wallet.toLowerCase();
|
|
2406
|
+
const excluded = new Set([ethers_1.ZeroAddress.toLowerCase(), vault.toLowerCase()]);
|
|
2407
|
+
return transfers.filter((transfer) => {
|
|
2408
|
+
const from = transfer.from.toLowerCase();
|
|
2409
|
+
const to = transfer.to.toLowerCase();
|
|
2410
|
+
if (from === to || excluded.has(from) || excluded.has(to))
|
|
2411
|
+
return false;
|
|
2412
|
+
return from === self || to === self;
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
/**
|
|
2416
|
+
* Value peer share transfers in the vault's asset, at the vault's share ratio
|
|
2417
|
+
* on the day each one happened.
|
|
2418
|
+
*
|
|
2419
|
+
* Used by {@link getVaultUserLifetimePnl} when a wallet has no indexed
|
|
2420
|
+
* deposits: received shares become its cost basis and sent shares count as
|
|
2421
|
+
* withdrawn, so lifetime PnL for a transfer recipient reads "PnL since the
|
|
2422
|
+
* shares arrived" rather than failing or reporting the whole position as
|
|
2423
|
+
* profit. A day with no usable ratio takes the latest earlier day's, and a
|
|
2424
|
+
* transfer older than the history takes the earliest day's.
|
|
2425
|
+
*
|
|
2426
|
+
* Sent shares count only against a received basis. A wallet that sent shares
|
|
2427
|
+
* but received none gets a zero basis, so the lifetime-PnL history guard still
|
|
2428
|
+
* decides for it instead of reporting the sent shares as profit.
|
|
2429
|
+
*
|
|
2430
|
+
* Pure and synchronous. Assumes, as the getter does, that shares and assets
|
|
2431
|
+
* share the vault's decimals.
|
|
2432
|
+
*
|
|
2433
|
+
* @param params - See {@link TransferCostBasisParams}.
|
|
2434
|
+
* @returns The received and sent asset amounts, raw at the vault's decimals;
|
|
2435
|
+
* both zero when no shares were received.
|
|
2436
|
+
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when no
|
|
2437
|
+
* day in `history` carries a positive, finite `asset_share_ratio`.
|
|
2438
|
+
* @example
|
|
2439
|
+
* ```ts
|
|
2440
|
+
* const { data } = await fetchVaultHistoricalTimeseries(vault, nDays);
|
|
2441
|
+
* const { receivedRaw, sentRaw } = transferCostBasis({
|
|
2442
|
+
* transfers: selectPeerTransfers(transfers, wallet, vault),
|
|
2443
|
+
* wallet,
|
|
2444
|
+
* vault,
|
|
2445
|
+
* history: data,
|
|
2446
|
+
* });
|
|
2447
|
+
* ```
|
|
2448
|
+
*/
|
|
2449
|
+
function transferCostBasis({ transfers, wallet, vault, history, }) {
|
|
2450
|
+
const ratios = Object.entries(history)
|
|
2451
|
+
.filter(([, day]) => Number.isFinite(day.asset_share_ratio) &&
|
|
2452
|
+
(day.asset_share_ratio ?? 0) > 0)
|
|
2453
|
+
.map(([date, day]) => ({ date, ratio: day.asset_share_ratio }))
|
|
2454
|
+
.sort((a, b) => a.date.localeCompare(b.date));
|
|
2455
|
+
if (ratios.length === 0) {
|
|
2456
|
+
throw new errors_1.AugustHistoryUnavailableError(`#getVaultUserLifetimePnl::${vault}::${wallet}:no share ratio history to price shares received by transfer`, { context: { vault, wallet } });
|
|
2457
|
+
}
|
|
2458
|
+
const scale = BigInt(10) ** BigInt(18);
|
|
2459
|
+
const assetsAt = (shares, timestamp) => {
|
|
2460
|
+
const date = new Date(Number(timestamp) * 1000).toISOString().slice(0, 10);
|
|
2461
|
+
let ratio = ratios[0].ratio;
|
|
2462
|
+
for (const entry of ratios) {
|
|
2463
|
+
if (entry.date > date)
|
|
2464
|
+
break;
|
|
2465
|
+
ratio = entry.ratio;
|
|
2466
|
+
}
|
|
2467
|
+
return (shares * BigInt(Math.round(ratio * 1e18))) / scale;
|
|
2468
|
+
};
|
|
2469
|
+
const self = wallet.toLowerCase();
|
|
2470
|
+
let receivedRaw = BigInt(0);
|
|
2471
|
+
let sentRaw = BigInt(0);
|
|
2472
|
+
for (const transfer of transfers) {
|
|
2473
|
+
const shares = BigInt(transfer.value);
|
|
2474
|
+
if (transfer.to.toLowerCase() === self) {
|
|
2475
|
+
receivedRaw += assetsAt(shares, transfer.timestamp_);
|
|
2476
|
+
}
|
|
2477
|
+
else if (transfer.from.toLowerCase() === self) {
|
|
2478
|
+
sentRaw += assetsAt(shares, transfer.timestamp_);
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
if (receivedRaw === BigInt(0))
|
|
2482
|
+
return { receivedRaw, sentRaw: BigInt(0) };
|
|
2483
|
+
return { receivedRaw, sentRaw };
|
|
2484
|
+
}
|
|
2485
|
+
// One receipt-transfer subgraph query, plus one backend timeseries request when
|
|
2486
|
+
// peer transfers exist. Any failure yields a zero basis, which leaves the
|
|
2487
|
+
// getter exactly where it was without transfers (the history guard decides).
|
|
2488
|
+
async function fetchTransferCostBasis(vault, wallet, provider, headers) {
|
|
2489
|
+
const none = { receivedRaw: BigInt(0), sentRaw: BigInt(0) };
|
|
2490
|
+
try {
|
|
2491
|
+
const transfers = selectPeerTransfers(await (0, vaults_1.getSubgraphUserTransfers)(wallet, provider, vault, undefined, {
|
|
2492
|
+
receiptSubgraph: true,
|
|
2493
|
+
}), wallet, vault);
|
|
2494
|
+
if (transfers.length === 0)
|
|
2495
|
+
return none;
|
|
2496
|
+
const earliest = Math.min(...transfers.map((t) => Number(t.timestamp_)));
|
|
2497
|
+
const nDays = Math.ceil((Date.now() / 1000 - earliest) / 86_400) + 1;
|
|
2498
|
+
const { data } = await (0, core_1.fetchVaultHistoricalTimeseries)(vault, nDays, headers);
|
|
2499
|
+
return transferCostBasis({ transfers, wallet, vault, history: data });
|
|
2500
|
+
}
|
|
2501
|
+
catch (e) {
|
|
2502
|
+
core_1.Logger.log.warn('getVaultUserLifetimePnl:transferCostBasis', `Failed to price shares received by transfer: ${e}`);
|
|
2503
|
+
return none;
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2372
2506
|
/**
|
|
2373
2507
|
* Calculate lifetime PnL for a user in a specific vault.
|
|
2374
2508
|
*
|
|
@@ -2390,12 +2524,21 @@ function sumDepositsAtFaceValue(depositsByAsset, assetDecimals, vaultDecimals) {
|
|
|
2390
2524
|
* from the price server. There is no stand-in price: when none resolves the
|
|
2391
2525
|
* getter throws rather than returning USD values it cannot know.
|
|
2392
2526
|
*
|
|
2527
|
+
* Shares received by transfer: when history has no deposit, peer transfers of
|
|
2528
|
+
* the share token (see {@link selectPeerTransfers}) are valued at the vault's
|
|
2529
|
+
* daily share ratio ({@link transferCostBasis}) — received shares become
|
|
2530
|
+
* `totalDeposited`, sent shares add to `totalWithdrawn`. PnL for such a wallet
|
|
2531
|
+
* therefore runs from the day its shares arrived. This costs one extra
|
|
2532
|
+
* subgraph query, plus one backend timeseries request when transfers exist,
|
|
2533
|
+
* and only for wallets with no indexed deposit.
|
|
2534
|
+
*
|
|
2393
2535
|
* @param vault - Vault contract address
|
|
2394
2536
|
* @param wallet - User wallet address
|
|
2395
2537
|
* @param options - RPC configuration and service options
|
|
2396
2538
|
* @returns Lifetime PnL data in both native token and USD
|
|
2397
2539
|
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when a
|
|
2398
|
-
* live position has no indexed deposits or
|
|
2540
|
+
* live position has no indexed deposits, withdrawals or priceable share
|
|
2541
|
+
* transfers.
|
|
2399
2542
|
* @throws {@link AugustPriceUnavailableError} (`PRICE_UNAVAILABLE`) when the
|
|
2400
2543
|
* underlying asset has no USD price, or `asset()` cannot be read.
|
|
2401
2544
|
*/
|
|
@@ -2546,6 +2689,15 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
|
|
|
2546
2689
|
totalWithdrawalsInShares += BigInt(lzRedeem.shareAmt);
|
|
2547
2690
|
}
|
|
2548
2691
|
}
|
|
2692
|
+
// A wallet can hold shares it never deposited (a migration airdrop, a DEX
|
|
2693
|
+
// purchase). Only when history has no deposit at all do received shares
|
|
2694
|
+
// become the cost basis, so a depositor's PnL is computed as before.
|
|
2695
|
+
if (depositsByAsset.size === 0) {
|
|
2696
|
+
const { receivedRaw, sentRaw } = await fetchTransferCostBasis(vault, wallet, provider, options?.headers);
|
|
2697
|
+
if (receivedRaw > BigInt(0))
|
|
2698
|
+
depositsByAsset.set('default', receivedRaw);
|
|
2699
|
+
totalWithdrawalsInAssets += sentRaw;
|
|
2700
|
+
}
|
|
2549
2701
|
// Fetch decimals for each unique asset in parallel
|
|
2550
2702
|
const assetDecimals = new Map();
|
|
2551
2703
|
await Promise.all(Array.from(depositsByAsset.keys()).map(async (assetAddress) => {
|
|
@@ -245,7 +245,8 @@ export declare class AugustVaults extends AugustBase {
|
|
|
245
245
|
* @param props Vault address, wallet address, and optional chain ID
|
|
246
246
|
* @returns Lifetime PnL data including realized and unrealized PnL
|
|
247
247
|
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when a
|
|
248
|
-
* live position has no indexed deposits or
|
|
248
|
+
* live position has no indexed deposits, withdrawals or priceable share
|
|
249
|
+
* transfers.
|
|
249
250
|
* @throws {@link AugustPriceUnavailableError} (`PRICE_UNAVAILABLE`) when the
|
|
250
251
|
* vault's underlying asset has no USD price.
|
|
251
252
|
*/
|
|
@@ -934,7 +934,8 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
934
934
|
* @param props Vault address, wallet address, and optional chain ID
|
|
935
935
|
* @returns Lifetime PnL data including realized and unrealized PnL
|
|
936
936
|
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when a
|
|
937
|
-
* live position has no indexed deposits or
|
|
937
|
+
* live position has no indexed deposits, withdrawals or priceable share
|
|
938
|
+
* transfers.
|
|
938
939
|
* @throws {@link AugustPriceUnavailableError} (`PRICE_UNAVAILABLE`) when the
|
|
939
940
|
* vault's underlying asset has no USD price.
|
|
940
941
|
*/
|