@augustdigital/sdk 9.5.0 → 9.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/adapters/evm/index.d.ts +1 -1
- package/lib/adapters/evm/index.js +29 -29
- 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/app-name.js +1 -1
- package/lib/core/analytics/constants.d.ts +10 -0
- package/lib/core/analytics/constants.js +17 -1
- package/lib/core/analytics/index.d.ts +7 -4
- package/lib/core/analytics/index.js +8 -4
- package/lib/core/analytics/instrumentation.js +8 -4
- package/lib/core/analytics/method-taxonomy.js +13 -0
- package/lib/core/analytics/metrics.d.ts +25 -10
- package/lib/core/analytics/metrics.js +41 -15
- package/lib/core/analytics/sentry.d.ts +24 -3
- package/lib/core/analytics/sentry.js +49 -18
- package/lib/core/analytics/types.d.ts +11 -4
- package/lib/core/analytics/user-identity.d.ts +19 -5
- package/lib/core/analytics/user-identity.js +35 -21
- package/lib/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/core/auth/verify.js +7 -3
- package/lib/core/base.class.d.ts +8 -1
- package/lib/core/base.class.js +7 -1
- package/lib/core/constants/core.d.ts +3 -0
- package/lib/core/constants/core.js +3 -0
- package/lib/core/constants/swap-router.d.ts +16 -0
- package/lib/core/constants/swap-router.js +16 -0
- package/lib/core/errors/index.d.ts +36 -1
- package/lib/core/errors/index.js +40 -1
- package/lib/core/fetcher.d.ts +24 -5
- package/lib/core/fetcher.js +43 -7
- package/lib/main.d.ts +6 -0
- package/lib/main.js +6 -0
- 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 +151 -10
- package/lib/modules/vaults/getters.js +226 -30
- package/lib/modules/vaults/main.d.ts +5 -0
- package/lib/modules/vaults/main.js +5 -0
- package/lib/modules/vaults/write.actions.d.ts +57 -1
- package/lib/modules/vaults/write.actions.js +86 -2
- package/lib/sdk.d.ts +13171 -12810
- 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 +2 -2
|
@@ -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.
|
|
@@ -366,6 +361,53 @@ export interface PnlHistoryConsistencyParams {
|
|
|
366
361
|
* ```
|
|
367
362
|
*/
|
|
368
363
|
export declare function assertPnlHistoryConsistent({ vault, wallet, hasDeposits, totalWithdrawnRaw, currentPositionRaw, }: PnlHistoryConsistencyParams): void;
|
|
364
|
+
/** Inputs the lifetime-PnL price guard inspects. */
|
|
365
|
+
export interface PnlTokenPriceParams {
|
|
366
|
+
/** Vault contract address, used for the error message and context. */
|
|
367
|
+
vault: IAddress;
|
|
368
|
+
/** User wallet address, used for the error message and context. */
|
|
369
|
+
wallet: IAddress;
|
|
370
|
+
/**
|
|
371
|
+
* The vault's underlying asset (`asset()`) that was priced, or `undefined`
|
|
372
|
+
* when `asset()` could not be read and no price lookup was possible.
|
|
373
|
+
*/
|
|
374
|
+
asset: IAddress | undefined;
|
|
375
|
+
/**
|
|
376
|
+
* USD price of `asset` as resolved by the price server. `0` means the lookup
|
|
377
|
+
* failed or the token is unknown to it (`fetchTokenPriceByAddress` never
|
|
378
|
+
* throws).
|
|
379
|
+
*/
|
|
380
|
+
tokenPriceUsd: number;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Guard against converting lifetime PnL to USD with a fabricated price.
|
|
384
|
+
*
|
|
385
|
+
* Every USD field `getVaultUserLifetimePnl` returns is a native amount times
|
|
386
|
+
* the underlying asset's USD price. When the price server has no price, the
|
|
387
|
+
* only honest outcome is "no USD value" — substituting a stand-in (the getter
|
|
388
|
+
* previously used `1`) reports a non-dollar vault's position at $1 per asset,
|
|
389
|
+
* a plausible-looking but wrong number that callers cannot detect.
|
|
390
|
+
*
|
|
391
|
+
* Pure and synchronous — no RPC, no I/O.
|
|
392
|
+
*
|
|
393
|
+
* @param params - See {@link PnlTokenPriceParams}.
|
|
394
|
+
* @returns `tokenPriceUsd`, unchanged, when it is a finite number above zero.
|
|
395
|
+
* @throws {@link AugustPriceUnavailableError} with code `PRICE_UNAVAILABLE`
|
|
396
|
+
* and `context: { vault, wallet, asset }` when the price is zero, negative,
|
|
397
|
+
* or not finite. The message carries the
|
|
398
|
+
* `#getVaultUserLifetimePnl::<vault>::<wallet>` prefix so it groups with the
|
|
399
|
+
* other errors leaving the getter.
|
|
400
|
+
* @example
|
|
401
|
+
* ```ts
|
|
402
|
+
* const price = assertPnlTokenPrice({
|
|
403
|
+
* vault,
|
|
404
|
+
* wallet,
|
|
405
|
+
* asset: underlyingAssetAddress,
|
|
406
|
+
* tokenPriceUsd: await pricePromise,
|
|
407
|
+
* });
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
410
|
+
export declare function assertPnlTokenPrice({ vault, wallet, asset, tokenPriceUsd, }: PnlTokenPriceParams): number;
|
|
369
411
|
/**
|
|
370
412
|
* Sum a wallet's per-asset deposits into the vault's own decimals, at face
|
|
371
413
|
* value — 1 unit of any deposited asset is treated as 1 unit of the vault's
|
|
@@ -391,6 +433,88 @@ export declare function assertPnlHistoryConsistent({ vault, wallet, hasDeposits,
|
|
|
391
433
|
* @returns Total deposited, in raw base units at `vaultDecimals`.
|
|
392
434
|
*/
|
|
393
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;
|
|
394
518
|
/**
|
|
395
519
|
* Calculate lifetime PnL for a user in a specific vault.
|
|
396
520
|
*
|
|
@@ -408,10 +532,27 @@ export declare function sumDepositsAtFaceValue(depositsByAsset: Map<string, bigi
|
|
|
408
532
|
* non-pegged deposit asset would need deposit-time price conversion, which
|
|
409
533
|
* this function does not perform.
|
|
410
534
|
*
|
|
535
|
+
* Every USD field is the native amount times the underlying asset's USD price
|
|
536
|
+
* from the price server. There is no stand-in price: when none resolves the
|
|
537
|
+
* getter throws rather than returning USD values it cannot know.
|
|
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
|
+
*
|
|
411
547
|
* @param vault - Vault contract address
|
|
412
548
|
* @param wallet - User wallet address
|
|
413
549
|
* @param options - RPC configuration and service options
|
|
414
550
|
* @returns Lifetime PnL data in both native token and USD
|
|
551
|
+
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when a
|
|
552
|
+
* live position has no indexed deposits, withdrawals or priceable share
|
|
553
|
+
* transfers.
|
|
554
|
+
* @throws {@link AugustPriceUnavailableError} (`PRICE_UNAVAILABLE`) when the
|
|
555
|
+
* underlying asset has no USD price, or `asset()` cannot be read.
|
|
415
556
|
*/
|
|
416
557
|
export declare function getVaultUserLifetimePnl({ vault, wallet, options, }: {
|
|
417
558
|
vault: IAddress;
|
|
@@ -54,7 +54,10 @@ exports.registerUserForPoints = registerUserForPoints;
|
|
|
54
54
|
exports.fetchPointsLeaderboard = fetchPointsLeaderboard;
|
|
55
55
|
exports.getYieldLastRealizedOn = getYieldLastRealizedOn;
|
|
56
56
|
exports.assertPnlHistoryConsistent = assertPnlHistoryConsistent;
|
|
57
|
+
exports.assertPnlTokenPrice = assertPnlTokenPrice;
|
|
57
58
|
exports.sumDepositsAtFaceValue = sumDepositsAtFaceValue;
|
|
59
|
+
exports.selectPeerTransfers = selectPeerTransfers;
|
|
60
|
+
exports.transferCostBasis = transferCostBasis;
|
|
58
61
|
exports.getVaultUserLifetimePnl = getVaultUserLifetimePnl;
|
|
59
62
|
exports.getVaultPnl = getVaultPnl;
|
|
60
63
|
exports.getVaultHistoricalTimeseries = getVaultHistoricalTimeseries;
|
|
@@ -1540,13 +1543,26 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, o
|
|
|
1540
1543
|
}
|
|
1541
1544
|
}
|
|
1542
1545
|
const balance = (0, core_1.toNormalizedBn)(bal, decimals);
|
|
1543
|
-
//
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
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
|
+
};
|
|
1550
1566
|
// get aggregate redemption amount
|
|
1551
1567
|
const aggregateAvailableRedemptions = availableRedemptions.reduce((acc, curr) => acc + BigInt(curr.amount.raw), BigInt(0));
|
|
1552
1568
|
// return obj
|
|
@@ -2260,15 +2276,10 @@ async function getYieldLastRealizedOn({ vault, options, }) {
|
|
|
2260
2276
|
* Pure and synchronous — no RPC, no I/O — so it is safe to call inside the hot
|
|
2261
2277
|
* path with no added latency.
|
|
2262
2278
|
*
|
|
2263
|
-
*
|
|
2264
|
-
*
|
|
2265
|
-
*
|
|
2266
|
-
*
|
|
2267
|
-
* not a regression — the pre-guard formula reported that same position as 100%
|
|
2268
|
-
* profit, which was also wrong — but the error message is misleading for that
|
|
2269
|
-
* (rare) path. If receipt-token transfer becomes a real user flow, distinguish
|
|
2270
|
-
* it here (e.g. by checking for a share-transfer event) rather than widening
|
|
2271
|
-
* 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.
|
|
2272
2283
|
*
|
|
2273
2284
|
* @param params - See {@link PnlHistoryConsistencyParams}.
|
|
2274
2285
|
* @returns `void` when the history is consistent.
|
|
@@ -2295,6 +2306,39 @@ function assertPnlHistoryConsistent({ vault, wallet, hasDeposits, totalWithdrawn
|
|
|
2295
2306
|
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 } });
|
|
2296
2307
|
}
|
|
2297
2308
|
}
|
|
2309
|
+
/**
|
|
2310
|
+
* Guard against converting lifetime PnL to USD with a fabricated price.
|
|
2311
|
+
*
|
|
2312
|
+
* Every USD field `getVaultUserLifetimePnl` returns is a native amount times
|
|
2313
|
+
* the underlying asset's USD price. When the price server has no price, the
|
|
2314
|
+
* only honest outcome is "no USD value" — substituting a stand-in (the getter
|
|
2315
|
+
* previously used `1`) reports a non-dollar vault's position at $1 per asset,
|
|
2316
|
+
* a plausible-looking but wrong number that callers cannot detect.
|
|
2317
|
+
*
|
|
2318
|
+
* Pure and synchronous — no RPC, no I/O.
|
|
2319
|
+
*
|
|
2320
|
+
* @param params - See {@link PnlTokenPriceParams}.
|
|
2321
|
+
* @returns `tokenPriceUsd`, unchanged, when it is a finite number above zero.
|
|
2322
|
+
* @throws {@link AugustPriceUnavailableError} with code `PRICE_UNAVAILABLE`
|
|
2323
|
+
* and `context: { vault, wallet, asset }` when the price is zero, negative,
|
|
2324
|
+
* or not finite. The message carries the
|
|
2325
|
+
* `#getVaultUserLifetimePnl::<vault>::<wallet>` prefix so it groups with the
|
|
2326
|
+
* other errors leaving the getter.
|
|
2327
|
+
* @example
|
|
2328
|
+
* ```ts
|
|
2329
|
+
* const price = assertPnlTokenPrice({
|
|
2330
|
+
* vault,
|
|
2331
|
+
* wallet,
|
|
2332
|
+
* asset: underlyingAssetAddress,
|
|
2333
|
+
* tokenPriceUsd: await pricePromise,
|
|
2334
|
+
* });
|
|
2335
|
+
* ```
|
|
2336
|
+
*/
|
|
2337
|
+
function assertPnlTokenPrice({ vault, wallet, asset, tokenPriceUsd, }) {
|
|
2338
|
+
if (Number.isFinite(tokenPriceUsd) && tokenPriceUsd > 0)
|
|
2339
|
+
return tokenPriceUsd;
|
|
2340
|
+
throw new errors_1.AugustPriceUnavailableError(`#getVaultUserLifetimePnl::${vault}::${wallet}:underlying token price unavailable for ${asset ?? 'an unreadable asset()'} — USD values cannot be computed without fabricating a price`, { context: { vault, wallet, asset: asset ?? null } });
|
|
2341
|
+
}
|
|
2298
2342
|
/**
|
|
2299
2343
|
* Sum a wallet's per-asset deposits into the vault's own decimals, at face
|
|
2300
2344
|
* value — 1 unit of any deposited asset is treated as 1 unit of the vault's
|
|
@@ -2335,6 +2379,130 @@ function sumDepositsAtFaceValue(depositsByAsset, assetDecimals, vaultDecimals) {
|
|
|
2335
2379
|
}
|
|
2336
2380
|
return total;
|
|
2337
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
|
+
}
|
|
2338
2506
|
/**
|
|
2339
2507
|
* Calculate lifetime PnL for a user in a specific vault.
|
|
2340
2508
|
*
|
|
@@ -2352,10 +2520,27 @@ function sumDepositsAtFaceValue(depositsByAsset, assetDecimals, vaultDecimals) {
|
|
|
2352
2520
|
* non-pegged deposit asset would need deposit-time price conversion, which
|
|
2353
2521
|
* this function does not perform.
|
|
2354
2522
|
*
|
|
2523
|
+
* Every USD field is the native amount times the underlying asset's USD price
|
|
2524
|
+
* from the price server. There is no stand-in price: when none resolves the
|
|
2525
|
+
* getter throws rather than returning USD values it cannot know.
|
|
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
|
+
*
|
|
2355
2535
|
* @param vault - Vault contract address
|
|
2356
2536
|
* @param wallet - User wallet address
|
|
2357
2537
|
* @param options - RPC configuration and service options
|
|
2358
2538
|
* @returns Lifetime PnL data in both native token and USD
|
|
2539
|
+
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when a
|
|
2540
|
+
* live position has no indexed deposits, withdrawals or priceable share
|
|
2541
|
+
* transfers.
|
|
2542
|
+
* @throws {@link AugustPriceUnavailableError} (`PRICE_UNAVAILABLE`) when the
|
|
2543
|
+
* underlying asset has no USD price, or `asset()` cannot be read.
|
|
2359
2544
|
*/
|
|
2360
2545
|
async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
|
|
2361
2546
|
try {
|
|
@@ -2504,6 +2689,15 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
|
|
|
2504
2689
|
totalWithdrawalsInShares += BigInt(lzRedeem.shareAmt);
|
|
2505
2690
|
}
|
|
2506
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
|
+
}
|
|
2507
2701
|
// Fetch decimals for each unique asset in parallel
|
|
2508
2702
|
const assetDecimals = new Map();
|
|
2509
2703
|
await Promise.all(Array.from(depositsByAsset.keys()).map(async (assetAddress) => {
|
|
@@ -2663,25 +2857,25 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
|
|
|
2663
2857
|
totalWithdrawnRaw,
|
|
2664
2858
|
currentPositionRaw: BigInt(currentPositionValue.raw),
|
|
2665
2859
|
});
|
|
2666
|
-
const tokenPriceUsd =
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
}
|
|
2860
|
+
const tokenPriceUsd = assertPnlTokenPrice({
|
|
2861
|
+
vault,
|
|
2862
|
+
wallet,
|
|
2863
|
+
asset: underlyingAssetAddress,
|
|
2864
|
+
tokenPriceUsd: await pricePromise,
|
|
2865
|
+
});
|
|
2672
2866
|
// totalDeposited is already in the vault's own decimals (face value,
|
|
2673
2867
|
// computed above) — convert to USD with the same underlying price used
|
|
2674
2868
|
// for the other two legs below, so all three legs share one conversion
|
|
2675
2869
|
// factor instead of drifting independently against each other.
|
|
2676
2870
|
const totalDeposited = (0, core_1.toNormalizedBn)(totalDepositedNativeRaw, decimals);
|
|
2677
|
-
const totalDepositedUsd = Number(totalDeposited.normalized) *
|
|
2678
|
-
const totalWithdrawnUsd = Number(totalWithdrawn.normalized) *
|
|
2679
|
-
const currentPositionValueUsd = Number(currentPositionValue.normalized) *
|
|
2871
|
+
const totalDepositedUsd = Number(totalDeposited.normalized) * tokenPriceUsd;
|
|
2872
|
+
const totalWithdrawnUsd = Number(totalWithdrawn.normalized) * tokenPriceUsd;
|
|
2873
|
+
const currentPositionValueUsd = Number(currentPositionValue.normalized) * tokenPriceUsd;
|
|
2680
2874
|
// Calculate lifetimePnl in USD first (since deposits may be in different tokens with different prices)
|
|
2681
2875
|
const lifetimePnlUsd = currentPositionValueUsd + totalWithdrawnUsd - totalDepositedUsd;
|
|
2682
2876
|
// Use BigInt-scaled arithmetic to minimize precision loss when converting USD to native
|
|
2683
2877
|
const scaleFactor = BigInt(10 ** 18); // High precision scale
|
|
2684
|
-
const tokenPriceScaled = BigInt(Math.round(
|
|
2878
|
+
const tokenPriceScaled = BigInt(Math.round(tokenPriceUsd * Number(scaleFactor)));
|
|
2685
2879
|
// Convert lifetimePnlUsd to native units
|
|
2686
2880
|
const lifetimePnlUsdScaled = BigInt(Math.round(lifetimePnlUsd * Number(scaleFactor)));
|
|
2687
2881
|
const lifetimePnlRaw = tokenPriceScaled > BigInt(0)
|
|
@@ -2706,10 +2900,12 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
|
|
|
2706
2900
|
}
|
|
2707
2901
|
catch (e) {
|
|
2708
2902
|
core_1.Logger.log.error('getVaultUserLifetimePnl', e, { vault, wallet });
|
|
2709
|
-
// Preserve the typed history
|
|
2710
|
-
// unavailable-history failure from a generic getter
|
|
2711
|
-
// already
|
|
2712
|
-
|
|
2903
|
+
// Preserve the typed history and price guards so callers can distinguish
|
|
2904
|
+
// an unavailable-history or unavailable-price failure from a generic getter
|
|
2905
|
+
// error. Their messages already carry the
|
|
2906
|
+
// `#getVaultUserLifetimePnl::vault::wallet` prefix.
|
|
2907
|
+
if (e instanceof errors_1.AugustHistoryUnavailableError ||
|
|
2908
|
+
e instanceof errors_1.AugustPriceUnavailableError)
|
|
2713
2909
|
throw e;
|
|
2714
2910
|
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
2715
2911
|
throw new Error(`#getVaultUserLifetimePnl::${vault}::${wallet}:${errorMessage}`);
|
|
@@ -244,6 +244,11 @@ export declare class AugustVaults extends AugustBase {
|
|
|
244
244
|
* Get lifetime PnL for a user in a specific vault.
|
|
245
245
|
* @param props Vault address, wallet address, and optional chain ID
|
|
246
246
|
* @returns Lifetime PnL data including realized and unrealized PnL
|
|
247
|
+
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when a
|
|
248
|
+
* live position has no indexed deposits, withdrawals or priceable share
|
|
249
|
+
* transfers.
|
|
250
|
+
* @throws {@link AugustPriceUnavailableError} (`PRICE_UNAVAILABLE`) when the
|
|
251
|
+
* vault's underlying asset has no USD price.
|
|
247
252
|
*/
|
|
248
253
|
getVaultUserLifetimePnl({ vault, wallet, chainId, }: {
|
|
249
254
|
vault: IAddress;
|
|
@@ -933,6 +933,11 @@ class AugustVaults extends core_1.AugustBase {
|
|
|
933
933
|
* Get lifetime PnL for a user in a specific vault.
|
|
934
934
|
* @param props Vault address, wallet address, and optional chain ID
|
|
935
935
|
* @returns Lifetime PnL data including realized and unrealized PnL
|
|
936
|
+
* @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when a
|
|
937
|
+
* live position has no indexed deposits, withdrawals or priceable share
|
|
938
|
+
* transfers.
|
|
939
|
+
* @throws {@link AugustPriceUnavailableError} (`PRICE_UNAVAILABLE`) when the
|
|
940
|
+
* vault's underlying asset has no USD price.
|
|
936
941
|
*/
|
|
937
942
|
async getVaultUserLifetimePnl({ vault, wallet, chainId, }) {
|
|
938
943
|
if (!(0, ethers_1.isAddress)(wallet))
|
|
@@ -535,6 +535,14 @@ export declare function depositViaSwapRouter(signer: Signer | Wallet, options: I
|
|
|
535
535
|
* before depositing — only valid when the vault's reference asset equals
|
|
536
536
|
* the wrapped-native token.
|
|
537
537
|
*
|
|
538
|
+
* **Wrap-only limitation**: the on-chain SwapRouter has no ETH→other-asset
|
|
539
|
+
* swap leg, so this only ever reaches a WETH-accepting vault (see
|
|
540
|
+
* {@link SWAP_ROUTER_WRAPPED_NATIVE}). There is no atomic native-ETH deposit
|
|
541
|
+
* into a non-WETH-reference vault (e.g. a USDC vault) — deposit the wrapped
|
|
542
|
+
* native token instead (already whitelisted, routes through the swap leg for
|
|
543
|
+
* a non-reference-asset vault). Use {@link isNativeSwapRouterDepositSupported}
|
|
544
|
+
* to check eligibility before offering this path in a UI.
|
|
545
|
+
*
|
|
538
546
|
* No ERC-20 approval is needed; `amount` travels as `msg.value`.
|
|
539
547
|
*
|
|
540
548
|
* @param signer - ethers `Signer` or `Wallet`.
|
|
@@ -545,6 +553,51 @@ export declare function depositViaSwapRouter(signer: Signer | Wallet, options: I
|
|
|
545
553
|
* addresses, or zero amount.
|
|
546
554
|
*/
|
|
547
555
|
export declare function depositNativeViaSwapRouter(signer: Signer | Wallet, options: ISwapRouterNativeDepositOptions): Promise<string>;
|
|
556
|
+
/**
|
|
557
|
+
* Whether a vault can take a native-ETH (chain-native token) SwapRouter
|
|
558
|
+
* deposit — the pre-flight check for {@link depositNativeViaSwapRouter} and
|
|
559
|
+
* {@link swapRouterDeposit} with a zero-address `depositAsset`.
|
|
560
|
+
*
|
|
561
|
+
* The on-chain SwapRouter only wraps native ETH to the chain's wrapped-native
|
|
562
|
+
* token and deposits that — it has no ETH→other-asset swap leg (see
|
|
563
|
+
* {@link SWAP_ROUTER_WRAPPED_NATIVE}). So a native deposit needs all three: a
|
|
564
|
+
* SwapRouter deployed on `chainId`, the vault registered on that router with
|
|
565
|
+
* the wrapped-native token as its `vaultInfo(vault).referenceAsset` (a
|
|
566
|
+
* deposit into an unregistered vault reverts with `InvalidVault`), and the
|
|
567
|
+
* vault's own on-chain `asset()` equal to that same token. Use this to gate a
|
|
568
|
+
* "deposit ETH" UI option before the user picks it, instead of discovering
|
|
569
|
+
* the answer via a thrown {@link AugustValidationError} or an on-chain revert.
|
|
570
|
+
*
|
|
571
|
+
* It checks configuration only — a deposit that passes this check can still
|
|
572
|
+
* fail on balance, gas, a paused vault, or a deposit cap.
|
|
573
|
+
*
|
|
574
|
+
* Read-only and provider-agnostic (no signer required) — safe to call before
|
|
575
|
+
* a wallet is connected. Makes no backend call.
|
|
576
|
+
*
|
|
577
|
+
* @param chainId - EVM chain ID the vault is deployed on.
|
|
578
|
+
* @param vault - August vault address to check.
|
|
579
|
+
* @param options - `rpcUrl` for `chainId`, passed to `createProvider`.
|
|
580
|
+
* @returns `true` iff a SwapRouter exists on `chainId`, the vault is
|
|
581
|
+
* registered on it with the wrapped-native reference asset, and the vault's
|
|
582
|
+
* `asset()` is that token. `false` — never throws — for a chain with no
|
|
583
|
+
* SwapRouter deployment, a missing/invalid `rpcUrl`, an unreadable vault or
|
|
584
|
+
* router, an unregistered vault, or a vault whose reference asset isn't the
|
|
585
|
+
* wrapped-native token.
|
|
586
|
+
* @worstCaseRpcCalls 2 — the vault's `asset()` and the router's
|
|
587
|
+
* `vaultInfo(vault)`, read in parallel. None on a chain with no SwapRouter.
|
|
588
|
+
* @example
|
|
589
|
+
* ```ts
|
|
590
|
+
* const canDepositEth = await isNativeSwapRouterDepositSupported(
|
|
591
|
+
* 1,
|
|
592
|
+
* '0x1234000000000000000000000000000000005678',
|
|
593
|
+
* { rpcUrl: 'https://eth-mainnet.example/v2/KEY' },
|
|
594
|
+
* );
|
|
595
|
+
* // true only if that vault is a router-registered WETH vault on mainnet
|
|
596
|
+
* ```
|
|
597
|
+
*/
|
|
598
|
+
export declare function isNativeSwapRouterDepositSupported(chainId: number, vault: IAddress, options: {
|
|
599
|
+
rpcUrl: string;
|
|
600
|
+
}): Promise<boolean>;
|
|
548
601
|
/**
|
|
549
602
|
* Deposit into an August vault through the on-chain `SwapRouter`, selecting the
|
|
550
603
|
* correct router path from the deposit asset. This is the high-level, explicit
|
|
@@ -564,7 +617,10 @@ export declare function depositNativeViaSwapRouter(signer: Signer | Wallet, opti
|
|
|
564
617
|
* - equals the reference asset → direct router deposit, no swap
|
|
565
618
|
* ({@link depositViaSwapRouter});
|
|
566
619
|
* - the zero address → native-token deposit ({@link depositNativeViaSwapRouter}),
|
|
567
|
-
* valid only when the reference asset is the chain's wrapped-native token
|
|
620
|
+
* valid only when the reference asset is the chain's wrapped-native token —
|
|
621
|
+
* the router only wraps ETH, it never swaps it, so this throws for any
|
|
622
|
+
* other reference asset (see {@link SWAP_ROUTER_WRAPPED_NATIVE} for why, and
|
|
623
|
+
* {@link isNativeSwapRouterDepositSupported} to check first);
|
|
568
624
|
* - any other ERC-20 → a swap to the reference asset (Paraswap quote pinned to
|
|
569
625
|
* the chain's whitelisted aggregator, fail-closed on router/selector drift)
|
|
570
626
|
* bundled into {@link swapAndDeposit}.
|