@upshiftfinance/sdk 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +408 -2
- package/lib/abis/AddressResolver.d.ts +28 -0
- package/lib/abis/AddressResolver.js +23 -0
- package/lib/abis/ChainlinkV3.d.ts +87 -0
- package/lib/abis/ChainlinkV3.js +117 -0
- package/lib/abis/ERC20.d.ts +168 -0
- package/lib/abis/ERC20.js +226 -0
- package/lib/abis/ERC20_Bytes32.d.ts +139 -0
- package/lib/abis/ERC20_Bytes32.js +196 -0
- package/lib/abis/ERC4626.d.ts +364 -0
- package/lib/abis/ERC4626.js +507 -0
- package/lib/abis/ERC721.d.ts +231 -0
- package/lib/abis/ERC721.js +321 -0
- package/lib/abis/FeeOracle.d.ts +120 -0
- package/lib/abis/FeeOracle.js +162 -0
- package/lib/abis/LendingPool.d.ts +1393 -0
- package/lib/abis/LendingPool.js +1807 -0
- package/lib/abis/LendingPoolV2.d.ts +1413 -0
- package/lib/abis/LendingPoolV2.js +1833 -0
- package/lib/abis/LendingPoolV3.d.ts +1677 -0
- package/lib/abis/LendingPoolV3.js +1160 -0
- package/lib/abis/Loan.d.ts +837 -0
- package/lib/abis/Loan.js +1080 -0
- package/lib/abis/MultiAssetNativeDepositWrapper.d.ts +137 -0
- package/lib/abis/MultiAssetNativeDepositWrapper.js +125 -0
- package/lib/abis/Multicall3.d.ts +30 -0
- package/lib/abis/Multicall3.js +97 -0
- package/lib/abis/OFT.d.ts +116 -0
- package/lib/abis/OFT.js +85 -0
- package/lib/abis/PoolAdapter.d.ts +36 -0
- package/lib/abis/PoolAdapter.js +51 -0
- package/lib/abis/RewardDistributor.d.ts +267 -0
- package/lib/abis/RewardDistributor.js +352 -0
- package/lib/abis/RwaRedeemSubaccount.d.ts +747 -0
- package/lib/abis/RwaRedeemSubaccount.js +548 -0
- package/lib/abis/SmartAccount.d.ts +17 -0
- package/lib/abis/SmartAccount.js +19 -0
- package/lib/abis/SwapRouter.d.ts +953 -0
- package/lib/abis/SwapRouter.js +1228 -0
- package/lib/abis/TextResolver.d.ts +16 -0
- package/lib/abis/TextResolver.js +16 -0
- package/lib/abis/TokenizedVaultV2.d.ts +1364 -0
- package/lib/abis/TokenizedVaultV2.js +1041 -0
- package/lib/abis/TokenizedVaultV2DepositWithPermit.d.ts +1456 -0
- package/lib/abis/TokenizedVaultV2DepositWithPermit.js +1878 -0
- package/lib/abis/TokenizedVaultV2Receipt.d.ts +1568 -0
- package/lib/abis/TokenizedVaultV2Receipt.js +1061 -0
- package/lib/abis/TokenizedVaultV2SenderAllocationWhitelist.d.ts +454 -0
- package/lib/abis/TokenizedVaultV2SenderAllocationWhitelist.js +327 -0
- package/lib/abis/TokenizedVaultV2WhitelistedAllocation.d.ts +1466 -0
- package/lib/abis/TokenizedVaultV2WhitelistedAllocation.js +1092 -0
- package/lib/abis/TokenizedVaultV2WhitelistedAssets.d.ts +274 -0
- package/lib/abis/TokenizedVaultV2WhitelistedAssets.js +167 -0
- package/lib/abis/UniversalResolverResolve.d.ts +69 -0
- package/lib/abis/UniversalResolverResolve.js +35 -0
- package/lib/abis/UniversalSignatureValidator.d.ts +17 -0
- package/lib/abis/UniversalSignatureValidator.js +30 -0
- package/lib/abis/WrapperAdapter.d.ts +71 -0
- package/lib/abis/WrapperAdapter.js +77 -0
- package/lib/abis/index.d.ts +34 -0
- package/lib/abis/index.js +51 -0
- package/lib/adapters/evm/getters.d.ts +19 -0
- package/lib/adapters/evm/getters.js +249 -0
- package/lib/adapters/evm/index.d.ts +539 -0
- package/lib/adapters/evm/index.js +656 -0
- package/lib/adapters/evm/utils.d.ts +8 -0
- package/lib/adapters/evm/utils.js +51 -0
- package/lib/adapters/solana/confirm.d.ts +88 -0
- package/lib/adapters/solana/confirm.js +291 -0
- package/lib/adapters/solana/constants.d.ts +42 -0
- package/lib/adapters/solana/constants.js +59 -0
- package/lib/adapters/solana/getters.d.ts +42 -0
- package/lib/adapters/solana/getters.js +275 -0
- package/lib/adapters/solana/idl/vault-idl.d.ts +700 -0
- package/lib/adapters/solana/idl/vault-idl.js +2101 -0
- package/lib/adapters/solana/idl/withdrawal-queue-idl.d.ts +795 -0
- package/lib/adapters/solana/idl/withdrawal-queue-idl.js +1864 -0
- package/lib/adapters/solana/index.d.ts +372 -0
- package/lib/adapters/solana/index.js +459 -0
- package/lib/adapters/solana/pricing.d.ts +184 -0
- package/lib/adapters/solana/pricing.js +239 -0
- package/lib/adapters/solana/queue-hints.d.ts +29 -0
- package/lib/adapters/solana/queue-hints.js +59 -0
- package/lib/adapters/solana/queue.actions.d.ts +97 -0
- package/lib/adapters/solana/queue.actions.js +468 -0
- package/lib/adapters/solana/token-reads.d.ts +108 -0
- package/lib/adapters/solana/token-reads.js +145 -0
- package/lib/adapters/solana/types.d.ts +325 -0
- package/lib/adapters/solana/types.js +3 -0
- package/lib/adapters/solana/utils.d.ts +229 -0
- package/lib/adapters/solana/utils.js +773 -0
- package/lib/adapters/solana/vault.actions.d.ts +140 -0
- package/lib/adapters/solana/vault.actions.js +657 -0
- package/lib/adapters/solana/withdrawal-queue.d.ts +125 -0
- package/lib/adapters/solana/withdrawal-queue.js +301 -0
- package/lib/adapters/stellar/actions.d.ts +41 -0
- package/lib/adapters/stellar/actions.js +126 -0
- package/lib/adapters/stellar/constants.d.ts +43 -0
- package/lib/adapters/stellar/constants.js +53 -0
- package/lib/adapters/stellar/getters.d.ts +68 -0
- package/lib/adapters/stellar/getters.js +290 -0
- package/lib/adapters/stellar/index.d.ts +126 -0
- package/lib/adapters/stellar/index.js +187 -0
- package/lib/adapters/stellar/invocation.d.ts +55 -0
- package/lib/adapters/stellar/invocation.js +92 -0
- package/lib/adapters/stellar/soroban.d.ts +131 -0
- package/lib/adapters/stellar/soroban.js +617 -0
- package/lib/adapters/stellar/submit.d.ts +42 -0
- package/lib/adapters/stellar/submit.js +195 -0
- package/lib/adapters/stellar/types.d.ts +57 -0
- package/lib/adapters/stellar/types.js +6 -0
- package/lib/adapters/stellar/utils.d.ts +24 -0
- package/lib/adapters/stellar/utils.js +34 -0
- package/lib/adapters/sui/constants.d.ts +1 -0
- package/lib/adapters/sui/constants.js +9 -0
- package/lib/adapters/sui/getters.d.ts +24 -0
- package/lib/adapters/sui/getters.js +134 -0
- package/lib/adapters/sui/index.d.ts +53 -0
- package/lib/adapters/sui/index.js +96 -0
- package/lib/adapters/sui/transformer.d.ts +10 -0
- package/lib/adapters/sui/transformer.js +115 -0
- package/lib/adapters/sui/types.d.ts +70 -0
- package/lib/adapters/sui/types.js +3 -0
- package/lib/adapters/sui/utils.d.ts +8 -0
- package/lib/adapters/sui/utils.js +17 -0
- package/lib/core/analytics/app-name.d.ts +27 -0
- package/lib/core/analytics/app-name.js +48 -0
- package/lib/core/analytics/chain-name.d.ts +9 -0
- package/lib/core/analytics/chain-name.js +34 -0
- package/lib/core/analytics/constants.d.ts +15 -0
- package/lib/core/analytics/constants.js +28 -0
- package/lib/core/analytics/env.d.ts +46 -0
- package/lib/core/analytics/env.js +147 -0
- package/lib/core/analytics/index.d.ts +39 -0
- package/lib/core/analytics/index.js +83 -0
- package/lib/core/analytics/instrumentation.d.ts +29 -0
- package/lib/core/analytics/instrumentation.js +292 -0
- package/lib/core/analytics/method-taxonomy.d.ts +19 -0
- package/lib/core/analytics/method-taxonomy.js +156 -0
- package/lib/core/analytics/metrics.d.ts +48 -0
- package/lib/core/analytics/metrics.js +142 -0
- package/lib/core/analytics/sanitize.d.ts +57 -0
- package/lib/core/analytics/sanitize.js +272 -0
- package/lib/core/analytics/sentry-runtime.d.ts +15 -0
- package/lib/core/analytics/sentry-runtime.js +131 -0
- package/lib/core/analytics/sentry.d.ts +100 -0
- package/lib/core/analytics/sentry.js +696 -0
- package/lib/core/analytics/types.d.ts +55 -0
- package/lib/core/analytics/types.js +3 -0
- package/lib/core/analytics/user-identity.d.ts +55 -0
- package/lib/core/analytics/user-identity.js +218 -0
- package/lib/core/analytics/version.d.ts +6 -0
- package/lib/core/analytics/version.js +10 -0
- package/lib/core/attribution.d.ts +159 -0
- package/lib/core/attribution.js +194 -0
- package/lib/core/auth/index.d.ts +1 -0
- package/lib/core/auth/index.js +18 -0
- package/lib/core/auth/verify.d.ts +2 -0
- package/lib/core/auth/verify.js +35 -0
- package/lib/core/base.class.d.ts +193 -0
- package/lib/core/base.class.js +163 -0
- package/lib/core/cache.d.ts +9 -0
- package/lib/core/cache.js +31 -0
- package/lib/core/constants/adapters.d.ts +103 -0
- package/lib/core/constants/adapters.js +180 -0
- package/lib/core/constants/core.d.ts +162 -0
- package/lib/core/constants/core.js +281 -0
- package/lib/core/constants/swap-router.d.ts +180 -0
- package/lib/core/constants/swap-router.js +203 -0
- package/lib/core/constants/vaults.d.ts +93 -0
- package/lib/core/constants/vaults.js +277 -0
- package/lib/core/constants/web3.d.ts +106 -0
- package/lib/core/constants/web3.js +251 -0
- package/lib/core/errors/index.d.ts +177 -0
- package/lib/core/errors/index.js +257 -0
- package/lib/core/fetcher.d.ts +237 -0
- package/lib/core/fetcher.js +1103 -0
- package/lib/core/helpers/adapters.d.ts +13 -0
- package/lib/core/helpers/adapters.js +39 -0
- package/lib/core/helpers/chain-address.d.ts +13 -0
- package/lib/core/helpers/chain-address.js +47 -0
- package/lib/core/helpers/chain-error.d.ts +207 -0
- package/lib/core/helpers/chain-error.js +697 -0
- package/lib/core/helpers/chain-support.d.ts +98 -0
- package/lib/core/helpers/chain-support.js +139 -0
- package/lib/core/helpers/concurrency.d.ts +51 -0
- package/lib/core/helpers/concurrency.js +79 -0
- package/lib/core/helpers/core.d.ts +92 -0
- package/lib/core/helpers/core.js +368 -0
- package/lib/core/helpers/explorer-link.d.ts +16 -0
- package/lib/core/helpers/explorer-link.js +26 -0
- package/lib/core/helpers/multicall.d.ts +68 -0
- package/lib/core/helpers/multicall.js +103 -0
- package/lib/core/helpers/revert-decode.d.ts +248 -0
- package/lib/core/helpers/revert-decode.js +515 -0
- package/lib/core/helpers/signer.d.ts +61 -0
- package/lib/core/helpers/signer.js +184 -0
- package/lib/core/helpers/swap-router.d.ts +269 -0
- package/lib/core/helpers/swap-router.js +542 -0
- package/lib/core/helpers/vault-version.d.ts +24 -0
- package/lib/core/helpers/vault-version.js +83 -0
- package/lib/core/helpers/vaults.d.ts +89 -0
- package/lib/core/helpers/vaults.js +241 -0
- package/lib/core/helpers/web3.d.ts +366 -0
- package/lib/core/helpers/web3.js +1034 -0
- package/lib/core/index.d.ts +23 -0
- package/lib/core/index.js +40 -0
- package/lib/core/logger/curator-alert.d.ts +217 -0
- package/lib/core/logger/curator-alert.js +276 -0
- package/lib/core/logger/index.d.ts +99 -0
- package/lib/core/logger/index.js +163 -0
- package/lib/core/logger/slack.d.ts +49 -0
- package/lib/core/logger/slack.js +111 -0
- package/lib/core/vault-metadata.d.ts +12 -0
- package/lib/core/vault-metadata.js +42 -0
- package/lib/core/version-check.d.ts +58 -0
- package/lib/core/version-check.js +182 -0
- package/lib/evm/index.d.ts +2 -0
- package/lib/evm/index.js +19 -0
- package/lib/evm/methods/crossChainVault.d.ts +132 -0
- package/lib/evm/methods/crossChainVault.js +857 -0
- package/lib/evm/methods/crossChainVaultRegistry.d.ts +92 -0
- package/lib/evm/methods/crossChainVaultRegistry.js +239 -0
- package/lib/evm/methods/index.d.ts +2 -0
- package/lib/evm/methods/index.js +19 -0
- package/lib/evm/types/crossChain.d.ts +363 -0
- package/lib/evm/types/crossChain.js +20 -0
- package/lib/evm/types/index.d.ts +1 -0
- package/lib/evm/types/index.js +18 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.js +52 -0
- package/lib/main.d.ts +596 -0
- package/lib/main.js +681 -0
- package/lib/modules/api/fetcher.d.ts +108 -0
- package/lib/modules/api/fetcher.js +187 -0
- package/lib/modules/api/index.d.ts +1 -0
- package/lib/modules/api/index.js +6 -0
- package/lib/modules/api/main.d.ts +587 -0
- package/lib/modules/api/main.js +911 -0
- package/lib/modules/sub-accounts/fetcher.d.ts +53 -0
- package/lib/modules/sub-accounts/fetcher.js +96 -0
- package/lib/modules/sub-accounts/index.d.ts +2 -0
- package/lib/modules/sub-accounts/index.js +19 -0
- package/lib/modules/sub-accounts/main.d.ts +249 -0
- package/lib/modules/sub-accounts/main.js +211 -0
- package/lib/modules/sub-accounts/utils.d.ts +106 -0
- package/lib/modules/sub-accounts/utils.js +112 -0
- package/lib/modules/vaults/adapter.helpers.d.ts +84 -0
- package/lib/modules/vaults/adapter.helpers.js +202 -0
- package/lib/modules/vaults/fetcher.d.ts +147 -0
- package/lib/modules/vaults/fetcher.js +357 -0
- package/lib/modules/vaults/getters.d.ts +1060 -0
- package/lib/modules/vaults/getters.js +4079 -0
- package/lib/modules/vaults/index.d.ts +20 -0
- package/lib/modules/vaults/index.js +44 -0
- package/lib/modules/vaults/main.d.ts +607 -0
- package/lib/modules/vaults/main.js +1687 -0
- package/lib/modules/vaults/prefetch.d.ts +65 -0
- package/lib/modules/vaults/prefetch.js +120 -0
- package/lib/modules/vaults/read.actions.d.ts +406 -0
- package/lib/modules/vaults/read.actions.js +891 -0
- package/lib/modules/vaults/types.d.ts +149 -0
- package/lib/modules/vaults/types.js +3 -0
- package/lib/modules/vaults/utils/call-data-decoder.d.ts +61 -0
- package/lib/modules/vaults/utils/call-data-decoder.js +194 -0
- package/lib/modules/vaults/utils/date-utils.d.ts +50 -0
- package/lib/modules/vaults/utils/date-utils.js +84 -0
- package/lib/modules/vaults/utils.d.ts +153 -0
- package/lib/modules/vaults/utils.js +818 -0
- package/lib/modules/vaults/write.actions.d.ts +812 -0
- package/lib/modules/vaults/write.actions.js +2377 -0
- package/lib/polyfills.d.ts +1 -0
- package/lib/polyfills.js +12 -0
- package/lib/sdk.d.ts +30154 -0
- package/lib/services/coingecko/fetcher.d.ts +15 -0
- package/lib/services/coingecko/fetcher.js +67 -0
- package/lib/services/coingecko/index.d.ts +2 -0
- package/lib/services/coingecko/index.js +19 -0
- package/lib/services/coingecko/utils.d.ts +1 -0
- package/lib/services/coingecko/utils.js +24 -0
- package/lib/services/debank/fetcher.d.ts +126 -0
- package/lib/services/debank/fetcher.js +47 -0
- package/lib/services/debank/index.d.ts +2 -0
- package/lib/services/debank/index.js +19 -0
- package/lib/services/debank/utils.d.ts +50 -0
- package/lib/services/debank/utils.js +325 -0
- package/lib/services/layerzero/deposits.d.ts +55 -0
- package/lib/services/layerzero/deposits.js +174 -0
- package/lib/services/layerzero/redeems.d.ts +20 -0
- package/lib/services/layerzero/redeems.js +92 -0
- package/lib/services/layerzero/utils.d.ts +9 -0
- package/lib/services/layerzero/utils.js +22 -0
- package/lib/services/octavfi/fetcher.d.ts +33 -0
- package/lib/services/octavfi/fetcher.js +39 -0
- package/lib/services/octavfi/index.d.ts +3 -0
- package/lib/services/octavfi/index.js +20 -0
- package/lib/services/octavfi/types.d.ts +34 -0
- package/lib/services/octavfi/types.js +3 -0
- package/lib/services/octavfi/utils.d.ts +16 -0
- package/lib/services/octavfi/utils.js +244 -0
- package/lib/services/subgraph/fetcher.d.ts +2 -0
- package/lib/services/subgraph/fetcher.js +66 -0
- package/lib/services/subgraph/index.d.ts +2 -0
- package/lib/services/subgraph/index.js +19 -0
- package/lib/services/subgraph/schema.d.ts +48 -0
- package/lib/services/subgraph/schema.js +87 -0
- package/lib/services/subgraph/vaults.d.ts +83 -0
- package/lib/services/subgraph/vaults.js +1282 -0
- package/lib/services/swap-quotes/index.d.ts +105 -0
- package/lib/services/swap-quotes/index.js +77 -0
- package/lib/services/swap-quotes/paraswap.d.ts +51 -0
- package/lib/services/swap-quotes/paraswap.js +138 -0
- package/lib/services/untangled/fetcher.d.ts +40 -0
- package/lib/services/untangled/fetcher.js +110 -0
- package/lib/services/untangled/index.d.ts +3 -0
- package/lib/services/untangled/index.js +20 -0
- package/lib/services/untangled/types.d.ts +83 -0
- package/lib/services/untangled/types.js +10 -0
- package/lib/services/untangled/utils.d.ts +91 -0
- package/lib/services/untangled/utils.js +339 -0
- package/lib/types/api.d.ts +498 -0
- package/lib/types/api.js +3 -0
- package/lib/types/index.d.ts +12 -0
- package/lib/types/index.js +28 -0
- package/lib/types/points.d.ts +28 -0
- package/lib/types/points.js +3 -0
- package/lib/types/pools.d.ts +144 -0
- package/lib/types/pools.js +3 -0
- package/lib/types/staking.d.ts +28 -0
- package/lib/types/staking.js +3 -0
- package/lib/types/sub-accounts.d.ts +150 -0
- package/lib/types/sub-accounts.js +3 -0
- package/lib/types/subgraph.d.ts +84 -0
- package/lib/types/subgraph.js +3 -0
- package/lib/types/typed-contract.d.ts +102 -0
- package/lib/types/typed-contract.js +3 -0
- package/lib/types/user.d.ts +1 -0
- package/lib/types/user.js +3 -0
- package/lib/types/vaults.d.ts +886 -0
- package/lib/types/vaults.js +30 -0
- package/lib/types/web3.d.ts +81 -0
- package/lib/types/web3.js +14 -0
- package/lib/types/webserver.d.ts +730 -0
- package/lib/types/webserver.js +3 -0
- package/package.json +78 -3
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** @module core/services/swap-quotes */
|
|
2
|
+
import type { IAddress } from '../../types';
|
|
3
|
+
/** Quote-provider discriminant. Will widen as new providers are added. */
|
|
4
|
+
export type ISwapQuoteProvider = 'paraswap';
|
|
5
|
+
/**
|
|
6
|
+
* Input to {@link fetchSwapQuote}.
|
|
7
|
+
*/
|
|
8
|
+
export interface ISwapQuoteRequest {
|
|
9
|
+
/** EVM chain ID where the swap will execute. */
|
|
10
|
+
chainId: number;
|
|
11
|
+
/** ERC-20 the user holds. */
|
|
12
|
+
srcToken: IAddress;
|
|
13
|
+
/** `srcToken.decimals()`. */
|
|
14
|
+
srcDecimals: number;
|
|
15
|
+
/** ERC-20 the SwapRouter should produce — typically the vault's `referenceAsset`. */
|
|
16
|
+
destToken: IAddress;
|
|
17
|
+
/** `destToken.decimals()`. */
|
|
18
|
+
destDecimals: number;
|
|
19
|
+
/** Input amount in `srcToken`'s smallest unit. */
|
|
20
|
+
amount: bigint;
|
|
21
|
+
/**
|
|
22
|
+
* Address that will execute the resulting calldata. For SwapRouter use,
|
|
23
|
+
* pass the SwapRouter's address — the aggregator's calldata is bound to
|
|
24
|
+
* this address, so it must hold the tokens at swap time.
|
|
25
|
+
*/
|
|
26
|
+
receiver: IAddress;
|
|
27
|
+
/**
|
|
28
|
+
* Slippage tolerance in bps (1 bp = 0.01%). Defaults to 100 (1%). Must be
|
|
29
|
+
* in `[0, 10_000)` — 10_000 disables protection entirely and is rejected.
|
|
30
|
+
*/
|
|
31
|
+
slippageBps?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Optional aggregator contract-method filter. When set, the aggregator is
|
|
34
|
+
* constrained to this single method (ParaSwap's `/prices`
|
|
35
|
+
* `includeContractMethods`), so the returned calldata's leading 4-byte
|
|
36
|
+
* selector is deterministic — required when the on-chain SwapRouter only
|
|
37
|
+
* authorizes one selector per router. When omitted, the aggregator picks the
|
|
38
|
+
* optimal method freely and the selector may vary between quotes.
|
|
39
|
+
*/
|
|
40
|
+
contractMethod?: string;
|
|
41
|
+
/** Aggregator partner key. Defaults to `'upshift'`. */
|
|
42
|
+
partner?: string;
|
|
43
|
+
/** Aggregator partner-fee receiver. Defaults to the Upshift partner address. */
|
|
44
|
+
partnerAddress?: IAddress;
|
|
45
|
+
/**
|
|
46
|
+
* `fetch`-compatible function override. Defaults to `globalThis.fetch`.
|
|
47
|
+
* Exposed so callers in Node environments lacking a global `fetch` can
|
|
48
|
+
* inject one, and so tests can mock without touching the global.
|
|
49
|
+
*/
|
|
50
|
+
fetchImpl?: typeof fetch;
|
|
51
|
+
/** Optional AbortSignal to cancel the in-flight Paraswap requests. */
|
|
52
|
+
signal?: AbortSignal;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Output of {@link fetchSwapQuote}. The `router` + `payload` pair maps
|
|
56
|
+
* directly into one entry of an `ISwapParams[]` passed to
|
|
57
|
+
* `SwapRouter.swapAndDeposit`.
|
|
58
|
+
*
|
|
59
|
+
* Modeled as a discriminated union on `provider` so adding 1inch/0x is an
|
|
60
|
+
* additive change rather than a breaking widening.
|
|
61
|
+
*/
|
|
62
|
+
export type ISwapQuoteResult = {
|
|
63
|
+
/** Quote provider that produced this calldata. */
|
|
64
|
+
provider: 'paraswap';
|
|
65
|
+
/** DEX router that will execute the swap (the contract `payload` is calldata for). */
|
|
66
|
+
router: IAddress;
|
|
67
|
+
/** ABI-encoded calldata for `router`. */
|
|
68
|
+
payload: `0x${string}`;
|
|
69
|
+
/** Expected output in `destToken` units before slippage. */
|
|
70
|
+
expectedAmountOut: bigint;
|
|
71
|
+
/** Slippage-adjusted minimum output. Below this the on-chain call reverts. */
|
|
72
|
+
minAmountOut: bigint;
|
|
73
|
+
/**
|
|
74
|
+
* Epoch ms when the quote was fetched. Callers that prefetch a quote
|
|
75
|
+
* before submitting should re-fetch when this is older than ~60s — DEX
|
|
76
|
+
* pool state moves and a stale `minAmountOut` may have been computed
|
|
77
|
+
* against a price that no longer holds.
|
|
78
|
+
*/
|
|
79
|
+
fetchedAt: number;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Fetch a swap quote and the calldata required for the on-chain SwapRouter
|
|
83
|
+
* to execute it.
|
|
84
|
+
*
|
|
85
|
+
* @throws {@link UpshiftValidationError} on invalid input (slippage out of
|
|
86
|
+
* range, zero amount, src===dst).
|
|
87
|
+
* @throws {@link UpshiftNetworkError} on transport failure.
|
|
88
|
+
* @throws {@link UpshiftServerError} on aggregator-side errors.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* const quote = await fetchSwapQuote({
|
|
93
|
+
* chainId: 1,
|
|
94
|
+
* srcToken: WBTC,
|
|
95
|
+
* srcDecimals: 8,
|
|
96
|
+
* destToken: USDC,
|
|
97
|
+
* destDecimals: 6,
|
|
98
|
+
* amount: 100_000_000n,
|
|
99
|
+
* receiver: SWAP_ROUTER_ADDRESSES[1]!,
|
|
100
|
+
* });
|
|
101
|
+
* // quote.router -> Paraswap augustor
|
|
102
|
+
* // quote.payload -> calldata
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare function fetchSwapQuote(request: ISwapQuoteRequest): Promise<ISwapQuoteResult>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** @module core/services/swap-quotes */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.fetchSwapQuote = fetchSwapQuote;
|
|
5
|
+
const errors_1 = require("../../core/errors");
|
|
6
|
+
const paraswap_1 = require("./paraswap");
|
|
7
|
+
const DEFAULT_SLIPPAGE_BPS = 100;
|
|
8
|
+
const BPS_DENOMINATOR = 10000n;
|
|
9
|
+
/**
|
|
10
|
+
* Fetch a swap quote and the calldata required for the on-chain SwapRouter
|
|
11
|
+
* to execute it.
|
|
12
|
+
*
|
|
13
|
+
* @throws {@link UpshiftValidationError} on invalid input (slippage out of
|
|
14
|
+
* range, zero amount, src===dst).
|
|
15
|
+
* @throws {@link UpshiftNetworkError} on transport failure.
|
|
16
|
+
* @throws {@link UpshiftServerError} on aggregator-side errors.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const quote = await fetchSwapQuote({
|
|
21
|
+
* chainId: 1,
|
|
22
|
+
* srcToken: WBTC,
|
|
23
|
+
* srcDecimals: 8,
|
|
24
|
+
* destToken: USDC,
|
|
25
|
+
* destDecimals: 6,
|
|
26
|
+
* amount: 100_000_000n,
|
|
27
|
+
* receiver: SWAP_ROUTER_ADDRESSES[1]!,
|
|
28
|
+
* });
|
|
29
|
+
* // quote.router -> Paraswap augustor
|
|
30
|
+
* // quote.payload -> calldata
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
async function fetchSwapQuote(request) {
|
|
34
|
+
validateRequest(request);
|
|
35
|
+
const slippageBps = request.slippageBps ?? DEFAULT_SLIPPAGE_BPS;
|
|
36
|
+
const fetchImpl = request.fetchImpl ?? globalThis.fetch;
|
|
37
|
+
const { router, payload, expectedAmountOut, fetchedAt } = await (0, paraswap_1.fetchParaswapQuote)({
|
|
38
|
+
chainId: request.chainId,
|
|
39
|
+
srcToken: request.srcToken,
|
|
40
|
+
srcDecimals: request.srcDecimals,
|
|
41
|
+
destToken: request.destToken,
|
|
42
|
+
destDecimals: request.destDecimals,
|
|
43
|
+
amount: request.amount,
|
|
44
|
+
receiver: request.receiver,
|
|
45
|
+
slippageBps,
|
|
46
|
+
partner: request.partner ?? paraswap_1.PARASWAP_DEFAULTS.partner,
|
|
47
|
+
partnerAddress: request.partnerAddress ?? paraswap_1.PARASWAP_DEFAULTS.partnerAddress,
|
|
48
|
+
contractMethod: request.contractMethod,
|
|
49
|
+
fetchImpl,
|
|
50
|
+
signal: request.signal,
|
|
51
|
+
});
|
|
52
|
+
const minAmountOut = applySlippage(expectedAmountOut, slippageBps);
|
|
53
|
+
return {
|
|
54
|
+
provider: 'paraswap',
|
|
55
|
+
router,
|
|
56
|
+
payload,
|
|
57
|
+
expectedAmountOut,
|
|
58
|
+
minAmountOut,
|
|
59
|
+
fetchedAt,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function validateRequest(req) {
|
|
63
|
+
if (req.slippageBps !== undefined &&
|
|
64
|
+
(req.slippageBps < 0 || req.slippageBps >= 10_000)) {
|
|
65
|
+
throw new errors_1.UpshiftValidationError('INVALID_INPUT', `fetchSwapQuote: slippageBps must be in [0, 10000), got ${req.slippageBps}. 10000 bps = 100% slippage and disables protection entirely; use a tighter bound.`);
|
|
66
|
+
}
|
|
67
|
+
if (req.amount === 0n) {
|
|
68
|
+
throw new errors_1.UpshiftValidationError('INVALID_INPUT', 'fetchSwapQuote: amount must be greater than zero');
|
|
69
|
+
}
|
|
70
|
+
if (req.srcToken.toLowerCase() === req.destToken.toLowerCase()) {
|
|
71
|
+
throw new errors_1.UpshiftValidationError('INVALID_INPUT', 'fetchSwapQuote: srcToken and destToken are identical — no swap needed');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function applySlippage(amount, slippageBps) {
|
|
75
|
+
return (amount * (BPS_DENOMINATOR - BigInt(slippageBps))) / BPS_DENOMINATOR;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { IAddress } from '../../types';
|
|
2
|
+
export interface IParaswapQuoteInput {
|
|
3
|
+
chainId: number;
|
|
4
|
+
srcToken: IAddress;
|
|
5
|
+
srcDecimals: number;
|
|
6
|
+
destToken: IAddress;
|
|
7
|
+
destDecimals: number;
|
|
8
|
+
amount: bigint;
|
|
9
|
+
receiver: IAddress;
|
|
10
|
+
/**
|
|
11
|
+
* Slippage tolerance in bps (1 bp = 0.01%). Used to derive the
|
|
12
|
+
* `destAmount` minimum baked into the resulting Paraswap calldata, so the
|
|
13
|
+
* on-chain DEX leg enforces the same tolerance the SwapRouter does
|
|
14
|
+
* (otherwise the leg reverts at the aggregator's exact-out check before
|
|
15
|
+
* the SwapRouter's `minAmountOut` is ever evaluated).
|
|
16
|
+
*/
|
|
17
|
+
slippageBps: number;
|
|
18
|
+
partner: string;
|
|
19
|
+
partnerAddress: IAddress;
|
|
20
|
+
/**
|
|
21
|
+
* Optional `includeContractMethods` value passed to Paraswap's `/prices`. When
|
|
22
|
+
* set, Paraswap is restricted to this single contract method so the resulting
|
|
23
|
+
* calldata's leading selector is deterministic and matches the one the
|
|
24
|
+
* SwapRouter authorizes for this router. When omitted, Paraswap is free to
|
|
25
|
+
* pick whichever method is optimal for the route.
|
|
26
|
+
*/
|
|
27
|
+
contractMethod?: string;
|
|
28
|
+
fetchImpl: typeof fetch;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
}
|
|
31
|
+
export interface IParaswapQuoteOutput {
|
|
32
|
+
router: IAddress;
|
|
33
|
+
payload: `0x${string}`;
|
|
34
|
+
expectedAmountOut: bigint;
|
|
35
|
+
fetchedAt: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fetch a Paraswap quote and the calldata to execute it on behalf of the
|
|
39
|
+
* SwapRouter. Throws `UpshiftNetworkError` on fetch-layer failure and
|
|
40
|
+
* `UpshiftServerError` on Paraswap-side error responses or malformed bodies.
|
|
41
|
+
*
|
|
42
|
+
* `fetchedAt` is the wall-clock timestamp captured *after* the calldata
|
|
43
|
+
* response — callers downstream can use it to decide whether the leg is
|
|
44
|
+
* fresh enough to broadcast (DEX pool state moves; stale calldata may
|
|
45
|
+
* silently revert at the on-chain slippage check).
|
|
46
|
+
*/
|
|
47
|
+
export declare function fetchParaswapQuote(input: IParaswapQuoteInput): Promise<IParaswapQuoteOutput>;
|
|
48
|
+
export declare const PARASWAP_DEFAULTS: {
|
|
49
|
+
readonly partner: "upshift";
|
|
50
|
+
readonly partnerAddress: `0x${string}`;
|
|
51
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PARASWAP_DEFAULTS = void 0;
|
|
4
|
+
exports.fetchParaswapQuote = fetchParaswapQuote;
|
|
5
|
+
const errors_1 = require("../../core/errors");
|
|
6
|
+
const web3_1 = require("../../core/helpers/web3");
|
|
7
|
+
const PARASWAP_PRICES_URL = 'https://api.paraswap.io/prices';
|
|
8
|
+
const PARASWAP_TRANSACTIONS_BASE_URL = 'https://api.paraswap.io/transactions';
|
|
9
|
+
const DEFAULT_PARTNER = 'upshift';
|
|
10
|
+
const DEFAULT_PARTNER_ADDRESS = '0xb60ee0FA5B6bA917255A03e744967E0Bda531dD0';
|
|
11
|
+
const PARASWAP_VERSION = '6.2';
|
|
12
|
+
const BPS_DENOMINATOR = 10000n;
|
|
13
|
+
/**
|
|
14
|
+
* Fetch a Paraswap quote and the calldata to execute it on behalf of the
|
|
15
|
+
* SwapRouter. Throws `UpshiftNetworkError` on fetch-layer failure and
|
|
16
|
+
* `UpshiftServerError` on Paraswap-side error responses or malformed bodies.
|
|
17
|
+
*
|
|
18
|
+
* `fetchedAt` is the wall-clock timestamp captured *after* the calldata
|
|
19
|
+
* response — callers downstream can use it to decide whether the leg is
|
|
20
|
+
* fresh enough to broadcast (DEX pool state moves; stale calldata may
|
|
21
|
+
* silently revert at the on-chain slippage check).
|
|
22
|
+
*/
|
|
23
|
+
async function fetchParaswapQuote(input) {
|
|
24
|
+
const priceRoute = await getPriceRoute(input);
|
|
25
|
+
const destAmountRaw = priceRoute.destAmount;
|
|
26
|
+
if (typeof destAmountRaw !== 'string' || !/^\d+$/.test(destAmountRaw)) {
|
|
27
|
+
throw new errors_1.UpshiftServerError(200, `fetchSwapQuote: Paraswap /prices returned non-numeric destAmount: ${String(destAmountRaw)}`, { context: { destAmount: destAmountRaw } });
|
|
28
|
+
}
|
|
29
|
+
const expectedAmountOut = BigInt(destAmountRaw);
|
|
30
|
+
const { router, payload } = await getTransactionData(input, priceRoute);
|
|
31
|
+
return { router, payload, expectedAmountOut, fetchedAt: Date.now() };
|
|
32
|
+
}
|
|
33
|
+
async function getPriceRoute(input) {
|
|
34
|
+
const params = new URLSearchParams({
|
|
35
|
+
network: String(input.chainId),
|
|
36
|
+
srcToken: input.srcToken,
|
|
37
|
+
srcDecimals: String(input.srcDecimals),
|
|
38
|
+
destToken: input.destToken,
|
|
39
|
+
destDecimals: String(input.destDecimals),
|
|
40
|
+
amount: input.amount.toString(),
|
|
41
|
+
partner: input.partner,
|
|
42
|
+
partnerAddress: input.partnerAddress,
|
|
43
|
+
version: PARASWAP_VERSION,
|
|
44
|
+
});
|
|
45
|
+
// Restrict the route to a single contract method when the caller pins one,
|
|
46
|
+
// so the calldata's selector is deterministic and matches the SwapRouter's
|
|
47
|
+
// per-router authorized selector. Without this the aggregator may return a
|
|
48
|
+
// route-optimized method (e.g. swapExactAmountInOnUniswapV3) whose selector
|
|
49
|
+
// the SwapRouter has not whitelisted, reverting with InvalidNotWhitelisted().
|
|
50
|
+
if (input.contractMethod) {
|
|
51
|
+
params.append('includeContractMethods', input.contractMethod);
|
|
52
|
+
}
|
|
53
|
+
const url = `${PARASWAP_PRICES_URL}?${params.toString()}`;
|
|
54
|
+
let response;
|
|
55
|
+
try {
|
|
56
|
+
response = await input.fetchImpl(url, {
|
|
57
|
+
method: 'GET',
|
|
58
|
+
headers: { 'Content-Type': 'application/json' },
|
|
59
|
+
signal: input.signal,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (cause) {
|
|
63
|
+
throw new errors_1.UpshiftNetworkError('fetchSwapQuote: Paraswap /prices fetch failed', { cause, context: { url } });
|
|
64
|
+
}
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
throw new errors_1.UpshiftServerError(response.status, `fetchSwapQuote: Paraswap /prices returned HTTP ${response.status}`, { context: { url } });
|
|
67
|
+
}
|
|
68
|
+
const body = (await response.json());
|
|
69
|
+
if (body.error) {
|
|
70
|
+
throw new errors_1.UpshiftServerError(response.status, `fetchSwapQuote: Paraswap /prices error: ${body.error}`, { context: { providerError: body.error } });
|
|
71
|
+
}
|
|
72
|
+
if (!body.priceRoute) {
|
|
73
|
+
throw new errors_1.UpshiftServerError(response.status, 'fetchSwapQuote: Paraswap /prices returned no priceRoute', { context: { body } });
|
|
74
|
+
}
|
|
75
|
+
return body.priceRoute;
|
|
76
|
+
}
|
|
77
|
+
async function getTransactionData(input, priceRoute) {
|
|
78
|
+
const url = `${PARASWAP_TRANSACTIONS_BASE_URL}/${input.chainId}/?ignoreChecks=true&ignoreGasEstimate=true&onlyParams=false`;
|
|
79
|
+
// Bake slippage into the destAmount Paraswap embeds as the aggregator's
|
|
80
|
+
// own min-out, so the DEX leg enforces the same lower bound the
|
|
81
|
+
// SwapRouter will check. Without this the aggregator uses the exact
|
|
82
|
+
// priceRoute quote (0% tolerance) and reverts on any adverse movement.
|
|
83
|
+
const expectedDestAmount = BigInt(priceRoute.destAmount);
|
|
84
|
+
const minDestAmount = (expectedDestAmount * (BPS_DENOMINATOR - BigInt(input.slippageBps))) /
|
|
85
|
+
BPS_DENOMINATOR;
|
|
86
|
+
const requestBody = {
|
|
87
|
+
userAddress: input.receiver,
|
|
88
|
+
srcToken: input.srcToken,
|
|
89
|
+
destToken: input.destToken,
|
|
90
|
+
srcDecimals: input.srcDecimals,
|
|
91
|
+
destDecimals: input.destDecimals,
|
|
92
|
+
srcAmount: input.amount.toString(),
|
|
93
|
+
destAmount: minDestAmount.toString(),
|
|
94
|
+
priceRoute,
|
|
95
|
+
partner: input.partner,
|
|
96
|
+
partnerAddress: input.partnerAddress,
|
|
97
|
+
};
|
|
98
|
+
let response;
|
|
99
|
+
try {
|
|
100
|
+
response = await input.fetchImpl(url, {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: { 'Content-Type': 'application/json' },
|
|
103
|
+
body: JSON.stringify(requestBody),
|
|
104
|
+
signal: input.signal,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch (cause) {
|
|
108
|
+
throw new errors_1.UpshiftNetworkError('fetchSwapQuote: Paraswap /transactions fetch failed', { cause, context: { url } });
|
|
109
|
+
}
|
|
110
|
+
if (!response.ok) {
|
|
111
|
+
throw new errors_1.UpshiftServerError(response.status, `fetchSwapQuote: Paraswap /transactions returned HTTP ${response.status}`, { context: { url } });
|
|
112
|
+
}
|
|
113
|
+
const body = (await response.json());
|
|
114
|
+
if (body.error) {
|
|
115
|
+
throw new errors_1.UpshiftServerError(response.status, `fetchSwapQuote: Paraswap /transactions error: ${body.error}`, { context: { providerError: body.error } });
|
|
116
|
+
}
|
|
117
|
+
if (!body.to || !body.data) {
|
|
118
|
+
throw new errors_1.UpshiftServerError(response.status, 'fetchSwapQuote: Paraswap /transactions returned no to/data', { context: { body } });
|
|
119
|
+
}
|
|
120
|
+
if (!(0, web3_1.checkAddress)(body.to, console, 'contract')) {
|
|
121
|
+
throw new errors_1.UpshiftServerError(response.status, `fetchSwapQuote: Paraswap /transactions returned an invalid router address: ${body.to}`, { context: { to: body.to } });
|
|
122
|
+
}
|
|
123
|
+
if (!/^0x[0-9a-fA-F]+$/.test(body.data) || body.data.length % 2 !== 0) {
|
|
124
|
+
throw new errors_1.UpshiftServerError(response.status, `fetchSwapQuote: Paraswap /transactions returned malformed calldata (${body.data.length} chars)`, { context: { dataPrefix: body.data.slice(0, 16) } });
|
|
125
|
+
}
|
|
126
|
+
if (body.data.length < 10) {
|
|
127
|
+
throw new errors_1.UpshiftValidationError('INVALID_INPUT', 'fetchSwapQuote: Paraswap /transactions returned calldata shorter than a 4-byte selector');
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
router: body.to,
|
|
131
|
+
payload: body.data,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
exports.PARASWAP_DEFAULTS = {
|
|
135
|
+
partner: DEFAULT_PARTNER,
|
|
136
|
+
partnerAddress: DEFAULT_PARTNER_ADDRESS,
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=paraswap.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { IUntangledVaultExposure } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Fetch a vault's full capital exposure from the Untangled portfolio API.
|
|
4
|
+
*
|
|
5
|
+
* Untangled reconciles Stellar vaults end to end — the vault's idle buffer,
|
|
6
|
+
* capital deployed into Stellar protocols, custody wallet balances, and value
|
|
7
|
+
* bridged to other chains. DeBank and Octav.fi cover none of that, which is why
|
|
8
|
+
* Stellar vaults previously rendered an empty exposure breakdown.
|
|
9
|
+
*
|
|
10
|
+
* The endpoint is keyed by **vault** address and already aggregates every
|
|
11
|
+
* borrower, so it must be called once per vault — never once per borrower, which
|
|
12
|
+
* would multiply the same totals.
|
|
13
|
+
*
|
|
14
|
+
* Requests are plain `GET`s with no custom headers. That is deliberate: it keeps
|
|
15
|
+
* them preflight-free in the browser, where the API's `access-control-allow-origin: *`
|
|
16
|
+
* covers the simple request but a bare `OPTIONS` is rejected.
|
|
17
|
+
*
|
|
18
|
+
* Never throws. Every failure — unknown vault, upstream error, transport
|
|
19
|
+
* failure, malformed body — resolves to `null` so callers can degrade to an
|
|
20
|
+
* empty exposure section instead of losing the rest of their response.
|
|
21
|
+
*
|
|
22
|
+
* Worst case: exactly **1 HTTP call**. No retry, no caching (the API is edge
|
|
23
|
+
* cached at `s-maxage=60`, well inside its 60 req/min budget for one call per
|
|
24
|
+
* vault page load).
|
|
25
|
+
*
|
|
26
|
+
* @param vault - Vault address, e.g. a Stellar contract address (`C…`).
|
|
27
|
+
* @returns The resolved exposure report, or `null` when Untangled does not
|
|
28
|
+
* reconcile this vault or the request failed.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const exposure = await fetchUntangledVaultExposure(
|
|
33
|
+
* 'CCL3WITWFFXIHV2I52ECV5DPIEOFSTU3PBPR53ILPLF2IP5KHECXRUTY',
|
|
34
|
+
* );
|
|
35
|
+
* // exposure?.tvlUsd → 24845637.05
|
|
36
|
+
* // exposure?.byProtocol → [{ protocolId: 'stellar-wallet', … }, { protocolId: 'blend', … }, …]
|
|
37
|
+
* const normalized = transformUntangledToDebank(exposure);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function fetchUntangledVaultExposure(vault: string): Promise<IUntangledVaultExposure | null>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchUntangledVaultExposure = fetchUntangledVaultExposure;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const BASE_URL = 'https://api-portfolio-octopos-mainnet.untangled.finance';
|
|
6
|
+
/**
|
|
7
|
+
* Read a response body as JSON without throwing.
|
|
8
|
+
*
|
|
9
|
+
* Used on the error path, where a malformed or empty body must not mask the
|
|
10
|
+
* HTTP status we actually want to report.
|
|
11
|
+
*
|
|
12
|
+
* @param response - The fetch response to drain.
|
|
13
|
+
* @returns The parsed body, or `null` when it is absent or not JSON.
|
|
14
|
+
*/
|
|
15
|
+
async function readJsonSafely(response) {
|
|
16
|
+
try {
|
|
17
|
+
return await response.json();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Fetch a vault's full capital exposure from the Untangled portfolio API.
|
|
25
|
+
*
|
|
26
|
+
* Untangled reconciles Stellar vaults end to end — the vault's idle buffer,
|
|
27
|
+
* capital deployed into Stellar protocols, custody wallet balances, and value
|
|
28
|
+
* bridged to other chains. DeBank and Octav.fi cover none of that, which is why
|
|
29
|
+
* Stellar vaults previously rendered an empty exposure breakdown.
|
|
30
|
+
*
|
|
31
|
+
* The endpoint is keyed by **vault** address and already aggregates every
|
|
32
|
+
* borrower, so it must be called once per vault — never once per borrower, which
|
|
33
|
+
* would multiply the same totals.
|
|
34
|
+
*
|
|
35
|
+
* Requests are plain `GET`s with no custom headers. That is deliberate: it keeps
|
|
36
|
+
* them preflight-free in the browser, where the API's `access-control-allow-origin: *`
|
|
37
|
+
* covers the simple request but a bare `OPTIONS` is rejected.
|
|
38
|
+
*
|
|
39
|
+
* Never throws. Every failure — unknown vault, upstream error, transport
|
|
40
|
+
* failure, malformed body — resolves to `null` so callers can degrade to an
|
|
41
|
+
* empty exposure section instead of losing the rest of their response.
|
|
42
|
+
*
|
|
43
|
+
* Worst case: exactly **1 HTTP call**. No retry, no caching (the API is edge
|
|
44
|
+
* cached at `s-maxage=60`, well inside its 60 req/min budget for one call per
|
|
45
|
+
* vault page load).
|
|
46
|
+
*
|
|
47
|
+
* @param vault - Vault address, e.g. a Stellar contract address (`C…`).
|
|
48
|
+
* @returns The resolved exposure report, or `null` when Untangled does not
|
|
49
|
+
* reconcile this vault or the request failed.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const exposure = await fetchUntangledVaultExposure(
|
|
54
|
+
* 'CCL3WITWFFXIHV2I52ECV5DPIEOFSTU3PBPR53ILPLF2IP5KHECXRUTY',
|
|
55
|
+
* );
|
|
56
|
+
* // exposure?.tvlUsd → 24845637.05
|
|
57
|
+
* // exposure?.byProtocol → [{ protocolId: 'stellar-wallet', … }, { protocolId: 'blend', … }, …]
|
|
58
|
+
* const normalized = transformUntangledToDebank(exposure);
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
async function fetchUntangledVaultExposure(vault) {
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetch(`${BASE_URL}/v1/vaults/${encodeURIComponent(vault)}/exposure`, { method: 'GET' });
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
const body = await readJsonSafely(response);
|
|
66
|
+
// 404 is the documented "we don't reconcile this vault" answer, not a
|
|
67
|
+
// fault — most vaults are not Untangled-covered. Keep it a breadcrumb so
|
|
68
|
+
// a known, permanent gap does not fill Sentry.
|
|
69
|
+
if (response.status === 404) {
|
|
70
|
+
core_1.Logger.log.warn('fetchUntangledVaultExposure:unresolved', {
|
|
71
|
+
vault,
|
|
72
|
+
status: response.status,
|
|
73
|
+
reason: body?.reason ?? 'unknown_vault',
|
|
74
|
+
});
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
core_1.Logger.log.error('fetchUntangledVaultExposure', new Error(`HTTP ${response.status} ${response.statusText}`), { vault, status: response.status });
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const data = await response.json();
|
|
81
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
82
|
+
core_1.Logger.log.error('fetchUntangledVaultExposure.unexpected-type', new Error(`unexpected response type: ${typeof data}`), { vault });
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
if (data.resolved !== true) {
|
|
86
|
+
core_1.Logger.log.warn('fetchUntangledVaultExposure:unresolved', {
|
|
87
|
+
vault,
|
|
88
|
+
status: response.status,
|
|
89
|
+
reason: data.reason ?? 'unknown_vault',
|
|
90
|
+
});
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (!Array.isArray(data.byProtocol)) {
|
|
94
|
+
core_1.Logger.log.error('fetchUntangledVaultExposure.malformed', new Error('resolved response is missing byProtocol[]'), { vault });
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
if (data.stale) {
|
|
98
|
+
core_1.Logger.log.warn('fetchUntangledVaultExposure:stale', {
|
|
99
|
+
vault,
|
|
100
|
+
asOf: data.asOf,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return data;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
core_1.Logger.log.error('fetchUntangledVaultExposure', error, { vault });
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=fetcher.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./fetcher"), exports);
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
19
|
+
__exportStar(require("./utils"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response types for the Untangled portfolio API.
|
|
3
|
+
*
|
|
4
|
+
* Shape verified live on 2026-09-01 against
|
|
5
|
+
* `GET https://api-portfolio-octopos-mainnet.untangled.finance/v1/vaults/{address}/exposure`
|
|
6
|
+
* for the Gami earnUSDC Stellar vault.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* One protocol slice of a vault's TVL.
|
|
10
|
+
*
|
|
11
|
+
* Untangled reports the whole vault, so the slices cover capital held in the
|
|
12
|
+
* vault contract itself, in custody wallets, and bridged to other chains — not
|
|
13
|
+
* just on-chain Stellar protocol positions.
|
|
14
|
+
*/
|
|
15
|
+
export interface IUntangledProtocolExposure {
|
|
16
|
+
/**
|
|
17
|
+
* Untangled protocol slug, e.g. `blend`, `templar`, `aquarius`. Two slugs are
|
|
18
|
+
* special: `stellar-wallet` is custody wallet cash, and `upshift` is the
|
|
19
|
+
* vault's own idle buffer plus any position it holds in another Upshift
|
|
20
|
+
* vault — only the part beyond the `idle` location is deployed capital.
|
|
21
|
+
*/
|
|
22
|
+
protocolId: string;
|
|
23
|
+
/** USD value held in this protocol. */
|
|
24
|
+
valueUsd: number;
|
|
25
|
+
/** Share of the vault's `tvlUsd`, as a percentage (`23.54` means 23.54%). */
|
|
26
|
+
pct: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* One asset slice of a vault's TVL. Vault-wide — the API does not currently
|
|
30
|
+
* break assets down per protocol.
|
|
31
|
+
*/
|
|
32
|
+
export interface IUntangledAssetExposure {
|
|
33
|
+
/** Asset ticker, e.g. `USDC`, `XLM`. */
|
|
34
|
+
symbol: string;
|
|
35
|
+
/** USD value held in this asset across the whole vault. */
|
|
36
|
+
valueUsd: number;
|
|
37
|
+
/** Share of the vault's `tvlUsd`, as a percentage. */
|
|
38
|
+
pct: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* One address holding part of a vault's capital — the vault contract, a custody
|
|
42
|
+
* wallet, or a bridged account on another chain.
|
|
43
|
+
*/
|
|
44
|
+
export interface IUntangledLocation {
|
|
45
|
+
/** Address on `chain`. Not necessarily a Stellar address (NEAR accounts appear here). */
|
|
46
|
+
address: string;
|
|
47
|
+
/** Chain slug the address lives on, e.g. `stellar`, `near`. */
|
|
48
|
+
chain: string;
|
|
49
|
+
/** Human-readable label, e.g. `idle`, `ForDefi`, `Templar`. */
|
|
50
|
+
label: string;
|
|
51
|
+
/** USD value held at this address. */
|
|
52
|
+
valueUsd: number;
|
|
53
|
+
/** ISO-8601 timestamp this location was last reconciled. */
|
|
54
|
+
asOf: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A resolved vault exposure report.
|
|
58
|
+
*
|
|
59
|
+
* Untangled answers with `{ resolved: false, reason }` and HTTP 404 for vaults
|
|
60
|
+
* it does not reconcile; `fetchUntangledVaultExposure` maps that to `null`, so
|
|
61
|
+
* `resolved` is always `true` on values that reach a caller.
|
|
62
|
+
*/
|
|
63
|
+
export interface IUntangledVaultExposure {
|
|
64
|
+
/** Always `true` — unresolved reports never reach callers. */
|
|
65
|
+
resolved: true;
|
|
66
|
+
/** The vault this report describes. */
|
|
67
|
+
vault: {
|
|
68
|
+
address: string;
|
|
69
|
+
chain: string;
|
|
70
|
+
};
|
|
71
|
+
/** Total USD value Untangled reconciled across every location. */
|
|
72
|
+
tvlUsd: number;
|
|
73
|
+
/** Protocol-level split of `tvlUsd`. */
|
|
74
|
+
byProtocol: IUntangledProtocolExposure[];
|
|
75
|
+
/** Asset-level split of `tvlUsd`, vault-wide. */
|
|
76
|
+
byAsset: IUntangledAssetExposure[];
|
|
77
|
+
/** Per-address drill-down of where the capital sits. */
|
|
78
|
+
locations: IUntangledLocation[];
|
|
79
|
+
/** ISO-8601 timestamp the report was assembled. */
|
|
80
|
+
asOf: string;
|
|
81
|
+
/** `true` when Untangled served a report it could not refresh in time. */
|
|
82
|
+
stale: boolean;
|
|
83
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Response types for the Untangled portfolio API.
|
|
4
|
+
*
|
|
5
|
+
* Shape verified live on 2026-09-01 against
|
|
6
|
+
* `GET https://api-portfolio-octopos-mainnet.untangled.finance/v1/vaults/{address}/exposure`
|
|
7
|
+
* for the Gami earnUSDC Stellar vault.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
//# sourceMappingURL=types.js.map
|