@augustdigital/sdk 9.8.0 → 9.9.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.
Files changed (35) hide show
  1. package/README.md +57 -0
  2. package/lib/adapters/evm/index.d.ts +23 -5
  3. package/lib/adapters/evm/index.js +25 -4
  4. package/lib/core/analytics/method-taxonomy.js +2 -0
  5. package/lib/core/analytics/version.d.ts +1 -1
  6. package/lib/core/analytics/version.js +1 -1
  7. package/lib/core/attribution.d.ts +37 -5
  8. package/lib/core/attribution.js +39 -3
  9. package/lib/core/base.class.d.ts +6 -0
  10. package/lib/core/constants/core.d.ts +1 -0
  11. package/lib/core/constants/core.js +1 -0
  12. package/lib/core/constants/swap-router.d.ts +21 -4
  13. package/lib/core/constants/swap-router.js +22 -5
  14. package/lib/core/helpers/swap-router.d.ts +66 -5
  15. package/lib/core/helpers/swap-router.js +158 -7
  16. package/lib/main.d.ts +21 -3
  17. package/lib/main.js +20 -3
  18. package/lib/modules/vaults/getters.d.ts +287 -14
  19. package/lib/modules/vaults/getters.js +458 -48
  20. package/lib/modules/vaults/main.d.ts +18 -1
  21. package/lib/modules/vaults/main.js +23 -0
  22. package/lib/modules/vaults/write.actions.d.ts +119 -29
  23. package/lib/modules/vaults/write.actions.js +384 -77
  24. package/lib/sdk.d.ts +11675 -11107
  25. package/lib/services/layerzero/deposits.d.ts +6 -0
  26. package/lib/services/layerzero/deposits.js +8 -0
  27. package/lib/services/octavfi/fetcher.d.ts +30 -6
  28. package/lib/services/octavfi/fetcher.js +27 -91
  29. package/lib/services/subgraph/schema.d.ts +5 -2
  30. package/lib/services/subgraph/schema.js +22 -10
  31. package/lib/services/subgraph/vaults.d.ts +23 -1
  32. package/lib/services/subgraph/vaults.js +65 -0
  33. package/lib/types/subgraph.d.ts +10 -0
  34. package/lib/types/vaults.d.ts +38 -6
  35. package/package.json +1 -1
@@ -6,10 +6,15 @@ exports.getSwapRouterName = getSwapRouterName;
6
6
  exports.isSwapRouterEligible = isSwapRouterEligible;
7
7
  exports.resolveOriginCode = resolveOriginCode;
8
8
  exports.getSwapRouterDepositResult = getSwapRouterDepositResult;
9
+ exports.getOriginInfo = getOriginInfo;
10
+ exports.readOriginInfo = readOriginInfo;
11
+ exports.readSwapRouterVaultInfo = readSwapRouterVaultInfo;
12
+ exports.readSwapRouterTokenWhitelisted = readSwapRouterTokenWhitelisted;
9
13
  exports.getSwapRouterWhitelistedTokens = getSwapRouterWhitelistedTokens;
10
14
  exports.getSwapRouterEligibleVaults = getSwapRouterEligibleVaults;
11
15
  const ethers_1 = require("ethers");
12
16
  const swap_router_1 = require("../constants/swap-router");
17
+ const attribution_1 = require("../attribution");
13
18
  const abis_1 = require("../../abis");
14
19
  const errors_1 = require("../errors");
15
20
  const cache_1 = require("../cache");
@@ -59,7 +64,7 @@ function getSwapRouterName(chainId) {
59
64
  *
60
65
  * This gates the (flag-controlled) swap-router deposit UI and can fail-fast a
61
66
  * {@link swapRouterDeposit} call. It does **not** affect {@link vaultDeposit},
62
- * which is always the native/multiasset/adapter path.
67
+ * which never swaps (see `getDepositRoute` for when it uses the router).
63
68
  *
64
69
  * @param vaultAddress - August vault address.
65
70
  * @returns `true` when the vault is in {@link SWAP_ROUTER_ELIGIBLE_VAULTS}.
@@ -84,21 +89,33 @@ exports.vaultUsesSwapRouter = isSwapRouterEligible;
84
89
  /**
85
90
  * Normalize an optional origin code to the value to send on-chain.
86
91
  *
87
- * Returns the caller-supplied code when present, otherwise the default
88
- * (all-zero) sentinel from {@link ORIGIN_CODES}. Validates that any
92
+ * Returns the caller-supplied code when present (an explicit all-zero
93
+ * sentinel opts one call out), otherwise the SDK instance's
94
+ * `attribution.originCode` when `chainId` is in
95
+ * {@link ORIGIN_ATTRIBUTION_CHAINS} (Ethereum mainnet only), otherwise the
96
+ * default (all-zero) sentinel from {@link ORIGIN_CODES}. Validates that any
89
97
  * supplied code is exactly 32 bytes (66 chars including the `0x` prefix);
90
98
  * the on-chain SwapRouter reverts with `InvalidOrigin` otherwise, but
91
99
  * surfacing the failure here as a typed SDK error gives partners a
92
100
  * grepable Sentry event instead of a generic `execution reverted`.
93
101
  *
94
102
  * @param provided - Optional caller-supplied origin code.
103
+ * @param chainId - Chain the deposit is sent on. The instance code is never
104
+ * returned without it, or on a chain outside
105
+ * {@link ORIGIN_ATTRIBUTION_CHAINS}.
95
106
  * @returns The 32-byte origin code to pass to the router.
96
107
  * @throws {@link AugustValidationError} when `provided` is not a 32-byte hex string.
97
108
  */
98
- function resolveOriginCode(provided) {
99
- if (provided === undefined)
100
- return swap_router_1.ORIGIN_CODES.default;
101
- if (!/^0x[0-9a-fA-F]{64}$/.test(provided)) {
109
+ function resolveOriginCode(provided, chainId) {
110
+ if (provided === undefined) {
111
+ const instanceCode = (0, attribution_1.getOriginCode)();
112
+ return instanceCode &&
113
+ chainId !== undefined &&
114
+ swap_router_1.ORIGIN_ATTRIBUTION_CHAINS.includes(chainId)
115
+ ? instanceCode
116
+ : swap_router_1.ORIGIN_CODES.default;
117
+ }
118
+ if (!swap_router_1.ORIGIN_CODE_PATTERN.test(provided)) {
102
119
  throw new errors_1.AugustValidationError('INVALID_INPUT', `resolveOriginCode: originCode must be a 32-byte hex string (66 chars incl. 0x), got "${provided}" (${provided.length} chars)`, { context: { provided, expectedLength: 66 } });
103
120
  }
104
121
  return provided;
@@ -181,6 +198,140 @@ async function getSwapRouterDepositResult(signer, { txHash, vault }) {
181
198
  }
182
199
  return null;
183
200
  }
201
+ /** Cache TTL for resolved origin-code, vault and token registrations (5 minutes). */
202
+ const SWAP_ROUTER_ORIGIN_INFO_TTL_MS = 5 * 60 * 1000;
203
+ /**
204
+ * Read how an origin code is registered on a chain's `SwapRouter`: whether it
205
+ * exists, its origin fee, and its fee collector.
206
+ *
207
+ * Check a partner code with this before configuring it as
208
+ * `attribution.originCode` — the router reverts every deposit carrying an
209
+ * unregistered code with `InvalidOrigin`. A code is registered when its
210
+ * collector is non-zero (`addOrigin` always sets one); a zero-fee code is
211
+ * still registered but never emits `OriginFeeApplied`. Cached per (chain,
212
+ * router, code) for 5 minutes.
213
+ *
214
+ * @param options - {@link IGetOriginInfoOptions}.
215
+ * @returns {@link IOriginInfo}; `registered: false` with a zero fee and
216
+ * collector, and no RPC, on a chain with no SwapRouter deployment.
217
+ * @throws {@link AugustValidationError} when `originCode` is not a 32-byte hex
218
+ * string.
219
+ * @throws {@link AugustSDKError} when the `origins(bytes32)` read fails.
220
+ * @worstCaseRpcCalls 1 — one `origins(bytes32)` call; none on a cache hit.
221
+ * @example
222
+ * ```ts
223
+ * const info = await getOriginInfo({ chainId: 1, originCode, rpcUrl });
224
+ * if (!info.registered) throw new Error('origin code is not registered');
225
+ * ```
226
+ */
227
+ async function getOriginInfo({ chainId, originCode, rpcUrl, }) {
228
+ if (!swap_router_1.ORIGIN_CODE_PATTERN.test(originCode)) {
229
+ throw new errors_1.AugustValidationError('INVALID_INPUT', `getOriginInfo: originCode must be a 32-byte hex string (66 chars incl. 0x), got ${String(originCode).length} chars`, { context: { chainId, expectedLength: 66 } });
230
+ }
231
+ try {
232
+ return await readOriginInfo(chainId, originCode, () => (0, web3_1.createProvider)(rpcUrl, chainId));
233
+ }
234
+ catch (e) {
235
+ if (e instanceof errors_1.AugustSDKError)
236
+ throw e;
237
+ throw new errors_1.AugustSDKError('UNKNOWN', `getOriginInfo failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { chainId } });
238
+ }
239
+ }
240
+ /**
241
+ * {@link getOriginInfo} without input validation, reading through a caller
242
+ * runner (created lazily, only on a cache miss). Shared with the deposit-route
243
+ * resolver so both hit the same cache entry.
244
+ * @internal
245
+ */
246
+ async function readOriginInfo(chainId, originCode, runner) {
247
+ const unregistered = {
248
+ registered: false,
249
+ originFeeBps: 0,
250
+ collector: ethers_1.ethers.ZeroAddress,
251
+ };
252
+ const router = getSwapRouterAddress(chainId);
253
+ if (!router)
254
+ return unregistered;
255
+ const cacheKey = `swap-router-origin-${chainId}-${router.toLowerCase()}-${originCode.toLowerCase()}`;
256
+ const cached = await (0, cache_1.getSafeCache)(cacheKey);
257
+ if (cached)
258
+ return cached;
259
+ const contract = (0, web3_1.createContract)({
260
+ address: router,
261
+ provider: runner(),
262
+ abi: abis_1.ABI_SWAP_ROUTER,
263
+ });
264
+ if (!contract)
265
+ return unregistered;
266
+ const [originFee, originFeeCollector] = await contract.origins(originCode);
267
+ const info = {
268
+ registered: originFeeCollector !== ethers_1.ethers.ZeroAddress,
269
+ originFeeBps: Number(originFee),
270
+ collector: originFeeCollector,
271
+ };
272
+ cache_1.CACHE.set(cacheKey, info, { ttl: SWAP_ROUTER_ORIGIN_INFO_TTL_MS });
273
+ return info;
274
+ }
275
+ /**
276
+ * Read a vault's `SwapRouter` registration (`vaultInfo`), cached for 5
277
+ * minutes like the other router reads.
278
+ *
279
+ * @returns The router's `referenceAsset` for the vault (the zero address when
280
+ * it is not registered) and its `vaultType`; `undefined` on a chain with no
281
+ * SwapRouter.
282
+ * @internal
283
+ */
284
+ async function readSwapRouterVaultInfo(runner, chainId, vault) {
285
+ const router = getSwapRouterAddress(chainId);
286
+ if (!router)
287
+ return undefined;
288
+ const cacheKey = `swap-router-vault-info-${chainId}-${router.toLowerCase()}-${vault.toLowerCase()}`;
289
+ const cached = await (0, cache_1.getSafeCache)(cacheKey);
290
+ if (cached)
291
+ return cached;
292
+ const contract = (0, web3_1.createContract)({
293
+ address: router,
294
+ provider: runner,
295
+ abi: abis_1.ABI_SWAP_ROUTER,
296
+ });
297
+ if (!contract)
298
+ return undefined;
299
+ const [, vaultType, referenceAsset] = await contract.vaultInfo(vault);
300
+ const info = {
301
+ referenceAsset: referenceAsset,
302
+ vaultType: Number(vaultType),
303
+ };
304
+ cache_1.CACHE.set(cacheKey, info, { ttl: SWAP_ROUTER_ORIGIN_INFO_TTL_MS });
305
+ return info;
306
+ }
307
+ /**
308
+ * Read whether a chain's `SwapRouter` whitelists `token`
309
+ * (`whitelistedTokens`), cached for 5 minutes like the other router reads.
310
+ * `SwapRouter.deposit` reverts `InputTokenNotWhitelisted` for any token it
311
+ * does not whitelist, the vault's reference asset included.
312
+ *
313
+ * @returns `false` on a chain with no SwapRouter.
314
+ * @internal
315
+ */
316
+ async function readSwapRouterTokenWhitelisted(runner, chainId, token) {
317
+ const router = getSwapRouterAddress(chainId);
318
+ if (!router)
319
+ return false;
320
+ const cacheKey = `swap-router-token-whitelisted-${chainId}-${router.toLowerCase()}-${token.toLowerCase()}`;
321
+ const cached = await (0, cache_1.getSafeCache)(cacheKey);
322
+ if (cached !== null)
323
+ return cached;
324
+ const contract = (0, web3_1.createContract)({
325
+ address: router,
326
+ provider: runner,
327
+ abi: abis_1.ABI_SWAP_ROUTER,
328
+ });
329
+ if (!contract)
330
+ return false;
331
+ const whitelisted = (await contract.whitelistedTokens(token)) === true;
332
+ cache_1.CACHE.set(cacheKey, whitelisted, { ttl: SWAP_ROUTER_ORIGIN_INFO_TTL_MS });
333
+ return whitelisted;
334
+ }
184
335
  /** Cache TTL for a chain's resolved SwapRouter token allowlist (5 minutes). */
185
336
  const SWAP_ROUTER_WHITELIST_TTL_MS = 5 * 60 * 1000;
186
337
  /**
package/lib/main.d.ts CHANGED
@@ -89,10 +89,12 @@ export declare class AugustSDK extends AugustBase {
89
89
  }): Promise<number>;
90
90
  /**
91
91
  * Deposit into a vault with an explicit signer. When `depositAsset` differs
92
- * from the vault's underlying asset the deposit is routed through the
93
- * matching adapter (native wrapper, swap router, or permit path).
92
+ * from the vault's underlying asset the deposit goes through the vault's
93
+ * multi-asset `deposit`, permit path, or configured adapter. It never swaps
94
+ * (use `evm.swapRouterDeposit` for that); with `attribution.originCode` set,
95
+ * eligible deposits go through the SwapRouter (see `evm.getDepositRoute`).
94
96
  * @param signer - ethers Signer or Wallet that sends the transaction
95
- * @param options - Vault address (`target`), depositor `wallet`, `amount`, and optional `depositAsset`, `wait`, `receiver`, `slippageBps`
97
+ * @param options - Vault address (`target`), depositor `wallet`, `amount`, and optional `depositAsset`, `wait`, `isDepositWithPermit`
96
98
  * @returns Deposit transaction hash
97
99
  */
98
100
  vaultDeposit(signer: Signer | Wallet, options: IContractWriteOptions): Promise<string>;
@@ -350,6 +352,22 @@ export declare class AugustSDK extends AugustBase {
350
352
  getSwapRouterEligibleVaults(chainId: number, options?: {
351
353
  fromBlock?: number;
352
354
  }): Promise<IAddress[]>;
355
+ /**
356
+ * Read a SwapRouter origin code's on-chain registration (registered, fee in
357
+ * bps, collector). Delegates to {@link AugustVaults.getOriginInfo}.
358
+ * @param options - `chainId` and the `bytes32` `originCode` to look up.
359
+ * @returns `{ registered, originFeeBps, collector }`.
360
+ * @throws AugustValidationError when no provider is configured for `chainId`
361
+ * or `originCode` is malformed.
362
+ * @example
363
+ * ```ts
364
+ * const { registered } = await augustSdk.getOriginInfo({ chainId: 1, originCode });
365
+ * ```
366
+ */
367
+ getOriginInfo(options: {
368
+ chainId: number;
369
+ originCode: `0x${string}`;
370
+ }): Promise<import("./types").IOriginInfo>;
353
371
  /**
354
372
  * Get lifetime PnL for a user in a specific vault.
355
373
  * Calculates realized and unrealized PnL based on deposit/withdrawal history and current position.
package/lib/main.js CHANGED
@@ -220,10 +220,12 @@ class AugustSDK extends core_1.AugustBase {
220
220
  }
221
221
  /**
222
222
  * Deposit into a vault with an explicit signer. When `depositAsset` differs
223
- * from the vault's underlying asset the deposit is routed through the
224
- * matching adapter (native wrapper, swap router, or permit path).
223
+ * from the vault's underlying asset the deposit goes through the vault's
224
+ * multi-asset `deposit`, permit path, or configured adapter. It never swaps
225
+ * (use `evm.swapRouterDeposit` for that); with `attribution.originCode` set,
226
+ * eligible deposits go through the SwapRouter (see `evm.getDepositRoute`).
225
227
  * @param signer - ethers Signer or Wallet that sends the transaction
226
- * @param options - Vault address (`target`), depositor `wallet`, `amount`, and optional `depositAsset`, `wait`, `receiver`, `slippageBps`
228
+ * @param options - Vault address (`target`), depositor `wallet`, `amount`, and optional `depositAsset`, `wait`, `isDepositWithPermit`
227
229
  * @returns Deposit transaction hash
228
230
  */
229
231
  async vaultDeposit(signer, options) {
@@ -425,6 +427,21 @@ class AugustSDK extends core_1.AugustBase {
425
427
  async getSwapRouterEligibleVaults(chainId, options) {
426
428
  return await this.vaults.getSwapRouterEligibleVaults(chainId, options);
427
429
  }
430
+ /**
431
+ * Read a SwapRouter origin code's on-chain registration (registered, fee in
432
+ * bps, collector). Delegates to {@link AugustVaults.getOriginInfo}.
433
+ * @param options - `chainId` and the `bytes32` `originCode` to look up.
434
+ * @returns `{ registered, originFeeBps, collector }`.
435
+ * @throws AugustValidationError when no provider is configured for `chainId`
436
+ * or `originCode` is malformed.
437
+ * @example
438
+ * ```ts
439
+ * const { registered } = await augustSdk.getOriginInfo({ chainId: 1, originCode });
440
+ * ```
441
+ */
442
+ async getOriginInfo(options) {
443
+ return await this.vaults.getOriginInfo(options);
444
+ }
428
445
  /**
429
446
  * Get lifetime PnL for a user in a specific vault.
430
447
  * Calculates realized and unrealized PnL based on deposit/withdrawal history and current position.
@@ -1,4 +1,4 @@
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';
1
+ import { type IAddress, type IHistoricalTimeseriesPoint, type IHistoricalTimeseriesResponse, type INormalizedNumber, type ISubgraphReserveDeposit, type ISubgraphTransfer, type ISubgraphUserHistoryItem, 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
@@ -444,12 +444,15 @@ export type PnlTransfer = Pick<ISubgraphTransfer, 'from' | 'to' | 'value' | 'tim
444
444
  * another wallet. Mints (from the zero address), burns (to it) and transfers to
445
445
  * or from the vault itself are dropped: those are the deposit and withdrawal
446
446
  * legs the subgraph history already records, and counting them again would
447
- * double the cost basis.
447
+ * double the cost basis. Transfers to or from `exclude` are dropped too.
448
448
  *
449
449
  * @param transfers - Receipt-token transfers touching `wallet`, as returned by
450
450
  * the vault's transfer subgraph.
451
451
  * @param wallet - The wallet whose position is being valued.
452
452
  * @param vault - The vault contract address.
453
+ * @param exclude - Other counterparties whose transfers are not peer
454
+ * transfers, such as a LayerZero OVault's hub composer and share OFT
455
+ * (`hubComposer` / `hubShareOFT` in `LAYERZERO_VAULTS`). Defaults to none.
453
456
  * @returns The peer-to-peer transfers, in input order. Addresses compare
454
457
  * case-insensitively.
455
458
  * @example
@@ -457,7 +460,7 @@ export type PnlTransfer = Pick<ISubgraphTransfer, 'from' | 'to' | 'value' | 'tim
457
460
  * const peer = selectPeerTransfers(transfers, wallet, vault);
458
461
  * ```
459
462
  */
460
- export declare function selectPeerTransfers<T extends PnlTransfer>(transfers: T[], wallet: IAddress, vault: IAddress): T[];
463
+ export declare function selectPeerTransfers<T extends PnlTransfer>(transfers: T[], wallet: IAddress, vault: IAddress, exclude?: string[]): T[];
461
464
  /** Inputs {@link transferCostBasis} prices. */
462
465
  export interface TransferCostBasisParams {
463
466
  /** Peer transfers touching `wallet` — see {@link selectPeerTransfers}. */
@@ -472,6 +475,13 @@ export interface TransferCostBasisParams {
472
475
  * read.
473
476
  */
474
477
  history: Record<string, Pick<IHistoricalTimeseriesPoint, 'asset_share_ratio'>>;
478
+ /**
479
+ * Report sent shares even when none were received. Defaults to `false`, which
480
+ * zeroes them so a zero-deposit wallet's history guard still decides; pass
481
+ * `true` when the caller applies that rule itself, as {@link pnlCostBasis}
482
+ * does.
483
+ */
484
+ countSentWithoutReceipt?: boolean;
475
485
  }
476
486
  /** Asset amounts that peer transfers moved into and out of a position. */
477
487
  export interface TransferCostBasis {
@@ -491,9 +501,11 @@ export interface TransferCostBasis {
491
501
  * profit. A day with no usable ratio takes the latest earlier day's, and a
492
502
  * transfer older than the history takes the earliest day's.
493
503
  *
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.
504
+ * Sent shares count only against a received basis by default. A wallet that
505
+ * sent shares but received none gets a zero basis, so the lifetime-PnL history
506
+ * guard still decides for it instead of reporting the sent shares as profit.
507
+ * `countSentWithoutReceipt: true` reports them anyway, for a caller that knows
508
+ * the wallet also deposited.
497
509
  *
498
510
  * Pure and synchronous. Assumes, as the getter does, that shares and assets
499
511
  * share the vault's decimals.
@@ -514,7 +526,242 @@ export interface TransferCostBasis {
514
526
  * });
515
527
  * ```
516
528
  */
517
- export declare function transferCostBasis({ transfers, wallet, vault, history, }: TransferCostBasisParams): TransferCostBasis;
529
+ export declare function transferCostBasis({ transfers, wallet, vault, history, countSentWithoutReceipt, }: TransferCostBasisParams): TransferCostBasis;
530
+ /** Inputs {@link historyCoversMints} compares. */
531
+ export interface HistoryCoversMintsParams {
532
+ /** Every share-token transfer touching `wallet`, mints included. */
533
+ transfers: Pick<ISubgraphTransfer, 'from' | 'to' | 'transactionHash_'>[];
534
+ /** The wallet whose history is being checked. */
535
+ wallet: IAddress;
536
+ /** Transaction hashes of the wallet's indexed deposits. */
537
+ depositTransactionHashes: string[];
538
+ }
539
+ /**
540
+ * Check that a wallet's indexed deposit history accounts for every share mint
541
+ * its share-token ledger shows.
542
+ *
543
+ * A deposit mints shares to the wallet in the same transaction, so a mint with
544
+ * no deposit of the same hash means the history is missing that deposit (an
545
+ * older deposit the subgraph never indexed, or one a third party made for the
546
+ * wallet). Folding peer transfers into such a history would credit the wallet
547
+ * for shares it sent while missing what it paid for them, so
548
+ * {@link getVaultUserLifetimePnl} only folds transfers for a depositor when
549
+ * this holds.
550
+ *
551
+ * Pure and synchronous. Hashes and addresses compare case-insensitively.
552
+ *
553
+ * @param params - See {@link HistoryCoversMintsParams}.
554
+ * @returns `true` when every mint to `wallet` has a matching deposit, including
555
+ * when there are no mints.
556
+ * @example
557
+ * ```ts
558
+ * const covered = historyCoversMints({
559
+ * transfers,
560
+ * wallet,
561
+ * depositTransactionHashes: history
562
+ * .filter((item) => item.type === 'deposit')
563
+ * .map((item) => item.transactionHash_),
564
+ * });
565
+ * ```
566
+ */
567
+ export declare function historyCoversMints({ transfers, wallet, depositTransactionHashes, }: HistoryCoversMintsParams): boolean;
568
+ /** Inputs {@link selectReserveDeposits} matches. */
569
+ export interface SelectReserveDepositsParams {
570
+ /** The wallet's `ReserveDeposit` events (see `getSubgraphUserReserveDeposits`). */
571
+ reserveDeposits: ISubgraphReserveDeposit[];
572
+ /** Every share-token transfer touching `wallet`, mints included. */
573
+ transfers: Pick<ISubgraphTransfer, 'from' | 'to' | 'transactionHash_'>[];
574
+ /** The wallet whose history is being completed. */
575
+ wallet: IAddress;
576
+ /** Transaction hashes of the wallet's indexed deposits. */
577
+ depositTransactionHashes: string[];
578
+ }
579
+ /**
580
+ * Keep the `ReserveDeposit` events that explain a share mint the indexed
581
+ * deposit history misses.
582
+ *
583
+ * High Growth ETH mints shares for deposits made through Kelp's gain adapter
584
+ * with a `ReserveDeposit` event instead of an ERC-4626 `Deposit`, so those
585
+ * deposits are absent from the user history and fail {@link historyCoversMints}.
586
+ * An event counts only when its transaction mints shares to `wallet` and holds
587
+ * no indexed deposit, so a deposit is never counted twice.
588
+ *
589
+ * Pure and synchronous. Hashes and addresses compare case-insensitively.
590
+ *
591
+ * @param params - See {@link SelectReserveDepositsParams}.
592
+ * @returns The matching events, in input order.
593
+ * @example
594
+ * ```ts
595
+ * const reserve = selectReserveDeposits({
596
+ * reserveDeposits: await getSubgraphUserReserveDeposits(wallet, provider, vault),
597
+ * transfers,
598
+ * wallet,
599
+ * depositTransactionHashes,
600
+ * });
601
+ * ```
602
+ */
603
+ export declare function selectReserveDeposits({ reserveDeposits, transfers, wallet, depositTransactionHashes, }: SelectReserveDepositsParams): ISubgraphReserveDeposit[];
604
+ /** Inputs {@link countWithdrawalRequestsOnce} reads. */
605
+ export interface CountWithdrawalRequestsOnceParams {
606
+ /** The wallet's user history (see `getSubgraphUserHistory`). */
607
+ history: ISubgraphUserHistoryItem[];
608
+ /** Every share-token transfer touching `wallet`. */
609
+ transfers: Pick<ISubgraphTransfer, 'from' | 'to' | 'transactionHash_'>[];
610
+ /** The wallet whose history is being read. */
611
+ wallet: IAddress;
612
+ /** The vault contract address. */
613
+ vault: IAddress;
614
+ }
615
+ /**
616
+ * Drop withdrawal-request rows the vault logged more than once for a single
617
+ * request.
618
+ *
619
+ * A redemption requested through Kelp's gain adapter makes High Growth ETH emit
620
+ * `WithdrawalRequested` twice for one share transfer, and the subgraph indexes
621
+ * both. In a transaction where the share ledger shows the wallet sending shares
622
+ * to the vault `n` times, at most `n` request rows are kept. A transaction the
623
+ * ledger shows no such transfer for keeps every row, so a missing ledger leaves
624
+ * the history unchanged.
625
+ *
626
+ * Pure and synchronous. Hashes and addresses compare case-insensitively.
627
+ *
628
+ * @param params - See {@link CountWithdrawalRequestsOnceParams}.
629
+ * @returns The history without the surplus request rows, in input order.
630
+ * @example
631
+ * ```ts
632
+ * const history = countWithdrawalRequestsOnce({ history: userHistory, transfers, wallet, vault });
633
+ * ```
634
+ */
635
+ export declare function countWithdrawalRequestsOnce({ history, transfers, wallet, vault, }: CountWithdrawalRequestsOnceParams): ISubgraphUserHistoryItem[];
636
+ /** Inputs {@link settleV2Redemptions} values. */
637
+ export interface SettleV2RedemptionsParams {
638
+ /** The wallet's user history (see `getSubgraphUserHistory`). */
639
+ history: ISubgraphUserHistoryItem[];
640
+ /** The vault's `lagDuration()`, in seconds. */
641
+ lagDuration: number;
642
+ /**
643
+ * The vault's daily history from `fetchVaultHistoricalTimeseries`, keyed by
644
+ * UTC date (`YYYY-MM-DD`). Only `asset_share_ratio` (assets per share) is
645
+ * read.
646
+ */
647
+ ratios: Record<string, Pick<IHistoricalTimeseriesPoint, 'asset_share_ratio'>>;
648
+ /** The current time, in unix seconds. */
649
+ now: number;
650
+ }
651
+ /** What a wallet's Tokenized Vault V2 redemption requests are worth. */
652
+ export interface V2RedemptionValue {
653
+ /** Assets paid, or due, for requests past their claim date; raw at vault decimals. */
654
+ settledAssetsRaw: bigint;
655
+ /** Shares to value at the current share price, raw. */
656
+ pendingShares: bigint;
657
+ }
658
+ /**
659
+ * Value a wallet's Tokenized Vault V2 redemption requests, which carry only
660
+ * shares.
661
+ *
662
+ * Every processed withdrawal (`WithdrawalProcessed.assetsAmount`) counts at the
663
+ * assets the vault paid. Its shares, estimated at the vault's
664
+ * `asset_share_ratio` on the day it was processed, retire the oldest requests
665
+ * first. Request shares left over count at the ratio on their claim date (see
666
+ * `computeClaimableDate`) once claimable: the subgraph lacks some processed rows
667
+ * (none for earnAUSD before 2026-02-01), and that ratio lands within about
668
+ * 0.05% of what the vault paid. Shares not yet claimable are left to the
669
+ * current share price. Matching by amount, not by claim date, keeps the result
670
+ * right when a vault processes a request before the date today's `lagDuration`
671
+ * implies. A leftover under 0.5% of a request that a later processed
672
+ * withdrawal followed is rounding from the daily ratio, so it is dropped.
673
+ *
674
+ * Pure and synchronous. Assumes, as the getter does, that shares and assets
675
+ * share the vault's decimals. A day with no usable ratio takes the latest
676
+ * earlier day's, and a date older than the history takes the earliest day's.
677
+ *
678
+ * @param params - See {@link SettleV2RedemptionsParams}.
679
+ * @returns The settled assets and the shares left to the current price; every
680
+ * requested share is pending when `ratios` holds no usable day.
681
+ * @example
682
+ * ```ts
683
+ * const { settledAssetsRaw, pendingShares } = settleV2Redemptions({
684
+ * history,
685
+ * lagDuration: 259_200,
686
+ * ratios: (await fetchVaultHistoricalTimeseries(vault, nDays)).data,
687
+ * now: Math.floor(Date.now() / 1000),
688
+ * });
689
+ * ```
690
+ */
691
+ export declare function settleV2Redemptions({ history, lagDuration, ratios, now, }: SettleV2RedemptionsParams): V2RedemptionValue;
692
+ /** Inputs {@link pnlCostBasis} folds together. */
693
+ export interface PnlCostBasisParams {
694
+ /** Indexed deposits at face value, raw at the vault's decimals. */
695
+ depositedRaw: bigint;
696
+ /** Withdrawals already in asset terms, raw at the vault's decimals. */
697
+ withdrawnAssetsRaw: bigint;
698
+ /** Whether history indexed any deposit, LayerZero deposits included. */
699
+ hasDeposits: boolean;
700
+ /** Whether the history accounts for every share mint (see {@link historyCoversMints}). */
701
+ mintsMatchHistory: boolean;
702
+ /**
703
+ * Peer transfers priced by {@link transferCostBasis} with
704
+ * `countSentWithoutReceipt: true`.
705
+ */
706
+ transfers: TransferCostBasis;
707
+ }
708
+ /** The cost basis {@link getVaultUserLifetimePnl} computes PnL against. */
709
+ export interface PnlCostBasis {
710
+ /** Indexed deposits plus shares received by transfer, raw at vault decimals. */
711
+ depositedRaw: bigint;
712
+ /** Asset-terms withdrawals plus shares sent by transfer, raw at vault decimals. */
713
+ withdrawnAssetsRaw: bigint;
714
+ /** The part of `depositedRaw` that came from shares received by transfer. */
715
+ receivedByTransferRaw: bigint;
716
+ }
717
+ /**
718
+ * Fold peer share transfers into a wallet's lifetime-PnL cost basis.
719
+ *
720
+ * Shares a wallet received from another holder (a migration airdrop, a DEX
721
+ * purchase, a transfer from another wallet) cost the wallet their value on
722
+ * arrival, so they add to the deposited amount; shares it sent away count as
723
+ * withdrawn. This applies whether or not the wallet also deposited, so a holder
724
+ * who received shares and then topped up is not credited with the received
725
+ * shares as profit, and a depositor who moved its shares elsewhere (a lending
726
+ * market, a DEX) is not shown as having lost them. A wallet with no deposit and
727
+ * no received shares keeps a zero basis, so the history guard still decides.
728
+ *
729
+ * A depositor whose history misses a mint ({@link historyCoversMints}) is the
730
+ * exception: its transfers do not count, so the result matches what the getter
731
+ * returned before transfers counted for depositors. On a LayerZero OVault the
732
+ * caller leaves the hub composer and share OFT out of the transfers (see
733
+ * {@link selectPeerTransfers}): the composer's deliveries are already counted
734
+ * as LayerZero deposits, and shares bridged to a spoke chain still count in the
735
+ * position.
736
+ *
737
+ * Pure and synchronous.
738
+ *
739
+ * @param params - See {@link PnlCostBasisParams}.
740
+ * @returns The deposited and withdrawn amounts to compute PnL from, and the
741
+ * transfer-priced part of the deposited amount (zero when transfers did not
742
+ * count).
743
+ * @example
744
+ * ```ts
745
+ * const basis = pnlCostBasis({
746
+ * depositedRaw,
747
+ * withdrawnAssetsRaw,
748
+ * hasDeposits: depositedRaw > BigInt(0),
749
+ * mintsMatchHistory: historyCoversMints({
750
+ * transfers,
751
+ * wallet,
752
+ * depositTransactionHashes,
753
+ * }),
754
+ * transfers: transferCostBasis({
755
+ * transfers,
756
+ * wallet,
757
+ * vault,
758
+ * history,
759
+ * countSentWithoutReceipt: true,
760
+ * }),
761
+ * });
762
+ * ```
763
+ */
764
+ export declare function pnlCostBasis({ depositedRaw, withdrawnAssetsRaw, hasDeposits, mintsMatchHistory, transfers, }: PnlCostBasisParams): PnlCostBasis;
518
765
  /**
519
766
  * Calculate lifetime PnL for a user in a specific vault.
520
767
  *
@@ -536,13 +783,39 @@ export declare function transferCostBasis({ transfers, wallet, vault, history, }
536
783
  * from the price server. There is no stand-in price: when none resolves the
537
784
  * getter throws rather than returning USD values it cannot know.
538
785
  *
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.
786
+ * Shares moved by transfer: peer transfers of the share token (see
787
+ * {@link selectPeerTransfers}) are valued at the vault's daily share ratio
788
+ * ({@link transferCostBasis}) and folded in by {@link pnlCostBasis} — received
789
+ * shares add to `totalDeposited` (and are reported again as
790
+ * `receivedByTransfer`), sent shares add to `totalWithdrawn`. This holds
791
+ * alongside indexed deposits, so a wallet that received shares and also
792
+ * deposited is not credited with the received shares as profit. LayerZero
793
+ * OVaults count transfers only when history holds no deposit, because their
794
+ * composer delivers cross-chain deposits by transfer.
795
+ *
796
+ * Gaps in the indexed history: a share mint with no `Deposit` event counts as
797
+ * a deposit of the assets in its `ReserveDeposit` event
798
+ * ({@link selectReserveDeposits}; High Growth ETH emits it for Kelp gain-adapter
799
+ * deposits), and a withdrawal request the vault logged twice counts once
800
+ * ({@link countWithdrawalRequestsOnce}). Transfers fold in for a depositor only
801
+ * once every mint is explained.
802
+ *
803
+ * Tokenized Vault V2 redemptions carry only shares, so a settled one counts at
804
+ * the assets the vault paid, or at the share ratio on its claim date when the
805
+ * subgraph has no processed row for it ({@link settleV2Redemptions}). Only a
806
+ * request not yet claimable is valued at the current share price. When the lag
807
+ * or the ratio history cannot be read, every request is valued at the current
808
+ * share price instead.
809
+ *
810
+ * Request cost: one receipt-transfer subgraph query (and a `symbol()` RPC read)
811
+ * per call, plus one backend timeseries request when peer transfers exist and
812
+ * one `reserveDeposits` subgraph query when a mint has no indexed deposit, and
813
+ * for a Tokenized Vault V2 wallet with redemption requests, a `lagDuration()`
814
+ * read plus one backend timeseries request. A timeseries window with no usable
815
+ * ratio (a closed vault whose history stopped earlier) is read again in full,
816
+ * so the latest earlier day still prices it. For
817
+ * non-LayerZero vaults the transfer query runs in parallel with the history
818
+ * read; for LayerZero vaults it runs only when history holds no deposit.
546
819
  *
547
820
  * @param vault - Vault contract address
548
821
  * @param wallet - User wallet address