@upshiftfinance/sdk 0.0.0-stage → 0.2.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/client.d.ts +83 -0
- package/lib/adapters/sui/client.js +216 -0
- package/lib/adapters/sui/constants.d.ts +33 -0
- package/lib/adapters/sui/constants.js +31 -0
- package/lib/adapters/sui/index.d.ts +57 -0
- package/lib/adapters/sui/index.js +100 -0
- package/lib/adapters/sui/targets.d.ts +171 -0
- package/lib/adapters/sui/targets.js +225 -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 +30173 -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 +100 -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 +79 -3
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Stellar Transaction Submission
|
|
4
|
+
*
|
|
5
|
+
* Submits signed Soroban transactions and polls for confirmation.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.submitStellarTransaction = submitStellarTransaction;
|
|
9
|
+
const stellar_sdk_1 = require("@stellar/stellar-sdk");
|
|
10
|
+
const core_1 = require("../../core");
|
|
11
|
+
const curator_alert_1 = require("../../core/logger/curator-alert");
|
|
12
|
+
const soroban_1 = require("./soroban");
|
|
13
|
+
const invocation_1 = require("./invocation");
|
|
14
|
+
const constants_1 = require("./constants");
|
|
15
|
+
/**
|
|
16
|
+
* Best-effort decode of the transaction-level result code (the XDR union
|
|
17
|
+
* discriminant, e.g. `"txBadSeq"`, `"txTooLate"`) from a Soroban
|
|
18
|
+
* `TransactionResult`. Typed `unknown` and probed structurally so it stays
|
|
19
|
+
* robust to RPC/SDK shape drift; returns `undefined` for anything that isn't a
|
|
20
|
+
* decoded union, so callers fall back to the stringified XDR in the message.
|
|
21
|
+
*/
|
|
22
|
+
function extractTxResultCode(result) {
|
|
23
|
+
try {
|
|
24
|
+
const decoded = result;
|
|
25
|
+
if (typeof decoded?.result !== 'function')
|
|
26
|
+
return undefined;
|
|
27
|
+
// `|| undefined`: an empty discriminant shares the clean "no code" path.
|
|
28
|
+
return decoded.result().switch().name || undefined;
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
// Only reached on a malformed / shape-drifted union (the non-union case
|
|
32
|
+
// already returned above). Breadcrumb it so decoder rot stays detectable;
|
|
33
|
+
// the caller still throws with the stringified XDR in the message.
|
|
34
|
+
core_1.Logger.log.warn('extractTxResultCode', { decodeError: String(e) });
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* `JSON.stringify` that never throws. A real `xdr.TransactionResult` can hold
|
|
40
|
+
* `bigint` fields (e.g. `feeCharged`), which make `JSON.stringify` throw a
|
|
41
|
+
* `TypeError` — that would escape and replace the `UpshiftSDKError` this detail
|
|
42
|
+
* is only meant to enrich. Returns `''` on failure so the caller falls back to
|
|
43
|
+
* the status; the decoded `resultCode` still carries the precise reason.
|
|
44
|
+
*/
|
|
45
|
+
function safeStringify(value) {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.stringify(value) ?? '';
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Submit a signed Soroban transaction and poll until confirmed.
|
|
55
|
+
*
|
|
56
|
+
* @param signedXdr - Base64-encoded XDR of the signed transaction
|
|
57
|
+
* @param network - Stellar network name
|
|
58
|
+
* @param sorobanRpcUrl - Optional override Soroban RPC endpoint (e.g. a keyed
|
|
59
|
+
* Alchemy URL); becomes the primary with the SDK's public endpoints kept as
|
|
60
|
+
* failover. Omit to use the public endpoints.
|
|
61
|
+
* @returns The transaction hash once the network confirms it as successful
|
|
62
|
+
* @throws {@link UpshiftTimeoutError} when the transaction is not confirmed
|
|
63
|
+
* within the poll budget (`MAX_POLL_ATTEMPTS`).
|
|
64
|
+
* @throws {@link UpshiftSDKError} when the RPC rejects the submission or the
|
|
65
|
+
* transaction confirms as failed. Its `context` carries `{ network, status }`
|
|
66
|
+
* plus, when the result XDR decodes, a `resultCode` string holding the
|
|
67
|
+
* transaction-level reason (e.g. `"txBadSeq"`, `"txTooLate"`); `resultCode`
|
|
68
|
+
* is `undefined` when the code cannot be decoded.
|
|
69
|
+
*
|
|
70
|
+
* Side effect: a transaction whose `resultCode` is `txFailed` — the operation
|
|
71
|
+
* itself ran and failed — and whose envelope shows a vault `redeem` is relayed
|
|
72
|
+
* to that vault's curator (fire-and-forget, deduped — see
|
|
73
|
+
* {@link reportRedeemFailure}). Nothing else alerts: an RPC-rejected
|
|
74
|
+
* broadcast is a retryable race, a poll timeout is indeterminate (the
|
|
75
|
+
* transaction may yet succeed), and any other result code means the redeem
|
|
76
|
+
* never executed.
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* try {
|
|
80
|
+
* const hash = await submitStellarTransaction(signedXdr, 'mainnet');
|
|
81
|
+
* } catch (err) {
|
|
82
|
+
* if (err instanceof UpshiftSDKError && err.context?.resultCode === 'txBadSeq') {
|
|
83
|
+
* // stale sequence number — rebuild the transaction and resubmit
|
|
84
|
+
* }
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
async function submitStellarTransaction(signedXdr, network, sorobanRpcUrl) {
|
|
89
|
+
const config = (0, soroban_1.resolveNetworkConfig)(network, sorobanRpcUrl);
|
|
90
|
+
const { passphrase } = config;
|
|
91
|
+
const server = await (0, soroban_1.getHealthyServer)(config);
|
|
92
|
+
const tx = stellar_sdk_1.TransactionBuilder.fromXDR(signedXdr, passphrase);
|
|
93
|
+
const sendResult = await server.sendTransaction(tx);
|
|
94
|
+
if (sendResult.status === 'ERROR') {
|
|
95
|
+
const errorResult = 'errorResult' in sendResult ? sendResult.errorResult : undefined;
|
|
96
|
+
const resultCode = extractTxResultCode(errorResult);
|
|
97
|
+
const detail = errorResult ? safeStringify(errorResult) : '';
|
|
98
|
+
const msg = `Transaction submission failed: ${detail || sendResult.status}`;
|
|
99
|
+
core_1.Logger.log.error('submitStellarTransaction', msg, { network, resultCode });
|
|
100
|
+
throw new core_1.UpshiftSDKError('UNKNOWN', msg, {
|
|
101
|
+
context: { network, status: sendResult.status, resultCode },
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Only PENDING (and DUPLICATE) mean the tx was accepted by the RPC.
|
|
105
|
+
// DUPLICATE means this exact transaction was already submitted — it may
|
|
106
|
+
// already be confirmed or still pending from a prior submission.
|
|
107
|
+
// TRY_AGAIN_LATER and any other status should be surfaced immediately
|
|
108
|
+
// so callers can retry submission instead of waiting for a poll timeout.
|
|
109
|
+
if (sendResult.status !== 'PENDING' && sendResult.status !== 'DUPLICATE') {
|
|
110
|
+
const msg = `Transaction not accepted by RPC (status: ${sendResult.status}). ` +
|
|
111
|
+
'This may be retryable — the RPC node could be overloaded.';
|
|
112
|
+
core_1.Logger.log.error('submitStellarTransaction', msg, { network });
|
|
113
|
+
throw new core_1.UpshiftSDKError('UNKNOWN', msg, {
|
|
114
|
+
context: { network, status: sendResult.status },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// Geometric backoff (start POLL_INTERVAL_MS, ×POLL_INTERVAL_BACKOFF up to
|
|
118
|
+
// POLL_INTERVAL_MAX_MS) — fast on the happy path, gentler on slow ledgers.
|
|
119
|
+
let getResult = await server.getTransaction(sendResult.hash);
|
|
120
|
+
let polls = 0;
|
|
121
|
+
let nextDelay = constants_1.POLL_INTERVAL_MS;
|
|
122
|
+
while (getResult.status === 'NOT_FOUND') {
|
|
123
|
+
if (++polls >= constants_1.MAX_POLL_ATTEMPTS) {
|
|
124
|
+
const msg = `Transaction ${sendResult.hash} not confirmed after ${constants_1.MAX_POLL_ATTEMPTS} poll attempts`;
|
|
125
|
+
core_1.Logger.log.error('submitStellarTransaction', msg, {
|
|
126
|
+
hash: sendResult.hash,
|
|
127
|
+
network,
|
|
128
|
+
});
|
|
129
|
+
throw new core_1.UpshiftTimeoutError(msg, constants_1.POLL_INTERVAL_MAX_MS * polls, {
|
|
130
|
+
context: { hash: sendResult.hash, network },
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
await new Promise((r) => setTimeout(r, nextDelay));
|
|
134
|
+
nextDelay = Math.min(Math.round(nextDelay * constants_1.POLL_INTERVAL_BACKOFF), constants_1.POLL_INTERVAL_MAX_MS);
|
|
135
|
+
getResult = await server.getTransaction(sendResult.hash);
|
|
136
|
+
}
|
|
137
|
+
if (getResult.status === 'SUCCESS') {
|
|
138
|
+
return sendResult.hash;
|
|
139
|
+
}
|
|
140
|
+
const resultXdr = 'resultXdr' in getResult ? getResult.resultXdr : undefined;
|
|
141
|
+
const resultCode = extractTxResultCode(resultXdr);
|
|
142
|
+
const detail = resultXdr ? safeStringify(resultXdr) : '';
|
|
143
|
+
const failDetail = detail ? ` (${detail})` : '';
|
|
144
|
+
const msg = `Transaction failed: ${getResult.status}${failDetail}`;
|
|
145
|
+
core_1.Logger.log.error('submitStellarTransaction', msg, {
|
|
146
|
+
hash: sendResult.hash,
|
|
147
|
+
network,
|
|
148
|
+
resultCode,
|
|
149
|
+
});
|
|
150
|
+
// A redemption that executed and failed is the strongest form of the signal
|
|
151
|
+
// the curator needs — and the only one carrying a hash the relay can verify
|
|
152
|
+
// on chain. Two gates, because a FAILED confirmation is not by itself a vault
|
|
153
|
+
// failure:
|
|
154
|
+
// - `txFailed` is the only result code meaning the operations were applied
|
|
155
|
+
// and one of them failed. Everything else at this layer is
|
|
156
|
+
// transaction-level — `txBadSeq`, `txInsufficientBalance`,
|
|
157
|
+
// `txTooLate`, resource-limit overruns — where the redeem never ran, and
|
|
158
|
+
// which the caller resolves by retrying. An undecodable code is treated
|
|
159
|
+
// the same way: without it we cannot tell the two apart, and silence
|
|
160
|
+
// beats paging a curator over a sequence-number race.
|
|
161
|
+
// - the envelope must actually be a redeem: this function is handed an
|
|
162
|
+
// opaque XDR and would otherwise misattribute deposits.
|
|
163
|
+
const invocation = resultCode === 'txFailed' ? (0, invocation_1.describeVaultInvocation)(tx) : undefined;
|
|
164
|
+
// A partially-decoded envelope is not reported at all: an alert naming no
|
|
165
|
+
// wallet or no amount is one a curator cannot act on, and the reporting
|
|
166
|
+
// endpoint rejects it anyway.
|
|
167
|
+
const redeemer = invocation?.owner ?? invocation?.receiver;
|
|
168
|
+
if (invocation?.functionName === 'redeem' &&
|
|
169
|
+
redeemer &&
|
|
170
|
+
invocation.amountRaw) {
|
|
171
|
+
(0, curator_alert_1.reportRedeemFailure)({
|
|
172
|
+
phase: 'submission',
|
|
173
|
+
chain: 'stellar',
|
|
174
|
+
network,
|
|
175
|
+
contractId: invocation.contractId,
|
|
176
|
+
// `owner` is the account whose shares burn; `receiver` only receives the
|
|
177
|
+
// assets. They are the same for a self-redeem, and owner is the right one
|
|
178
|
+
// to name when they differ.
|
|
179
|
+
eoa: redeemer,
|
|
180
|
+
sharesRaw: invocation.amountRaw,
|
|
181
|
+
txHash: sendResult.hash,
|
|
182
|
+
resultCode,
|
|
183
|
+
error: msg,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
throw new core_1.UpshiftSDKError('UNKNOWN', msg, {
|
|
187
|
+
context: {
|
|
188
|
+
hash: sendResult.hash,
|
|
189
|
+
network,
|
|
190
|
+
status: getResult.status,
|
|
191
|
+
resultCode,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=submit.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stellar Vault Types
|
|
3
|
+
*/
|
|
4
|
+
import type { IStellarNetwork } from '../../types/web3';
|
|
5
|
+
export type { IStellarNetwork };
|
|
6
|
+
/** Parameters for building an unsigned Stellar vault deposit transaction. */
|
|
7
|
+
export interface IStellarDepositParams {
|
|
8
|
+
contractId: string;
|
|
9
|
+
amount: string;
|
|
10
|
+
senderAddress: string;
|
|
11
|
+
network: IStellarNetwork;
|
|
12
|
+
/**
|
|
13
|
+
* Optional override Soroban RPC endpoint (e.g. a keyed Alchemy URL). When set
|
|
14
|
+
* it is the primary, with the SDK's public endpoints kept behind it as
|
|
15
|
+
* failover.
|
|
16
|
+
*/
|
|
17
|
+
sorobanRpcUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Parameters for building an unsigned Stellar vault redeem transaction. */
|
|
20
|
+
export interface IStellarRedeemParams {
|
|
21
|
+
contractId: string;
|
|
22
|
+
shares: string;
|
|
23
|
+
receiverAddress: string;
|
|
24
|
+
network: IStellarNetwork;
|
|
25
|
+
/**
|
|
26
|
+
* Optional override Soroban RPC endpoint (e.g. a keyed Alchemy URL). When set
|
|
27
|
+
* it is the primary, with the SDK's public endpoints kept behind it as
|
|
28
|
+
* failover.
|
|
29
|
+
*/
|
|
30
|
+
sorobanRpcUrl?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* User vault position returned by on-chain balance queries.
|
|
34
|
+
*
|
|
35
|
+
* `decimals` carries the share-token scale used to interpret `shares`. When the
|
|
36
|
+
* on-chain `decimals()` read fails, it falls back to STELLAR_FALLBACK_DECIMALS
|
|
37
|
+
* (7) and `decimalsFromFallback` is set to `true`. Callers that size a redeem
|
|
38
|
+
* from this position MUST check `decimalsFromFallback` and refuse to settle
|
|
39
|
+
* when it is `true` — trusting a fabricated 7 against an ERC4626 offset vault
|
|
40
|
+
* (share decimals = asset + offset) under-redeems by `10^offset`.
|
|
41
|
+
*/
|
|
42
|
+
export interface IStellarUserPosition {
|
|
43
|
+
/** Raw share balance in smallest unit, as a non-negative integer string */
|
|
44
|
+
shares: string;
|
|
45
|
+
/** Share-token decimals; may be the fallback — see `decimalsFromFallback`. */
|
|
46
|
+
decimals: number;
|
|
47
|
+
/**
|
|
48
|
+
* `true` when `decimals` is the fallback (the on-chain `decimals()` read
|
|
49
|
+
* failed), `false` when it is the authoritative on-chain value. Treat `true`
|
|
50
|
+
* as a decimals read failure: do not trust `decimals` to scale or redeem.
|
|
51
|
+
*
|
|
52
|
+
* Optional for source back-compat (a consumer's existing object literal still
|
|
53
|
+
* compiles), but `getStellarUserPosition` always populates it — so a position
|
|
54
|
+
* obtained from the SDK never omits it.
|
|
55
|
+
*/
|
|
56
|
+
decimalsFromFallback?: boolean;
|
|
57
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stellar Utilities
|
|
3
|
+
*/
|
|
4
|
+
import type { IStellarNetwork } from './types';
|
|
5
|
+
import { isStellarAddress } from '../../core/helpers/chain-address';
|
|
6
|
+
export { isStellarAddress };
|
|
7
|
+
/**
|
|
8
|
+
* Get the Stellar explorer URL for a given address or transaction.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getExplorerLink({ id, type, network, }: {
|
|
11
|
+
id: string;
|
|
12
|
+
type?: 'contract' | 'account' | 'tx';
|
|
13
|
+
network?: IStellarNetwork;
|
|
14
|
+
}): string;
|
|
15
|
+
/**
|
|
16
|
+
* Throw if the given address is a Stellar address.
|
|
17
|
+
* Used as a guard in vault operations that don't yet support Stellar.
|
|
18
|
+
*/
|
|
19
|
+
export declare function assertNotStellar(address: string, operation: string): void;
|
|
20
|
+
export declare const StellarUtils: {
|
|
21
|
+
isStellarAddress: typeof isStellarAddress;
|
|
22
|
+
getExplorerLink: typeof getExplorerLink;
|
|
23
|
+
assertNotStellar: typeof assertNotStellar;
|
|
24
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Stellar Utilities
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.StellarUtils = exports.isStellarAddress = void 0;
|
|
7
|
+
exports.getExplorerLink = getExplorerLink;
|
|
8
|
+
exports.assertNotStellar = assertNotStellar;
|
|
9
|
+
const core_1 = require("../../core");
|
|
10
|
+
const chain_address_1 = require("../../core/helpers/chain-address");
|
|
11
|
+
Object.defineProperty(exports, "isStellarAddress", { enumerable: true, get: function () { return chain_address_1.isStellarAddress; } });
|
|
12
|
+
/**
|
|
13
|
+
* Get the Stellar explorer URL for a given address or transaction.
|
|
14
|
+
*/
|
|
15
|
+
function getExplorerLink({ id, type = 'contract', network = 'mainnet', }) {
|
|
16
|
+
const baseUrl = 'https://stellar.expert/explorer';
|
|
17
|
+
const net = network === 'mainnet' ? 'public' : 'testnet';
|
|
18
|
+
return `${baseUrl}/${net}/${type}/${id}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Throw if the given address is a Stellar address.
|
|
22
|
+
* Used as a guard in vault operations that don't yet support Stellar.
|
|
23
|
+
*/
|
|
24
|
+
function assertNotStellar(address, operation) {
|
|
25
|
+
if ((0, chain_address_1.isStellarAddress)(address)) {
|
|
26
|
+
throw new core_1.UpshiftValidationError('INVALID_CHAIN', `${operation} is not yet supported for Stellar vaults: ${address}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.StellarUtils = {
|
|
30
|
+
isStellarAddress: chain_address_1.isStellarAddress,
|
|
31
|
+
getExplorerLink,
|
|
32
|
+
assertNotStellar,
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sui gRPC client factory with endpoint failover.
|
|
3
|
+
*
|
|
4
|
+
* Sui removed JSON-RPC (Sui v1.82), so the SDK talks to fullnodes over gRPC.
|
|
5
|
+
* It uses gRPC-web — `@mysten/sui`'s `SuiGrpcClient` — so one code path works
|
|
6
|
+
* in browsers and Node.js.
|
|
7
|
+
*
|
|
8
|
+
* `@mysten/sui` is ESM-only and this SDK is published as CommonJS. The module
|
|
9
|
+
* is loaded on first use, not when the SDK is imported, so importing the SDK
|
|
10
|
+
* never requires it. That keeps Jest (which cannot `require` an ES module) and
|
|
11
|
+
* other CommonJS test runners working for integrators that never call Sui.
|
|
12
|
+
*
|
|
13
|
+
* @module SuiClient
|
|
14
|
+
*/
|
|
15
|
+
import type { SuiGrpcClient } from '@mysten/sui/grpc';
|
|
16
|
+
import type { ISuiConfig, ISuiNetwork } from '../../types';
|
|
17
|
+
/** Network and ordered endpoints for Sui calls: primary first, failover after. */
|
|
18
|
+
export interface ISuiNetworkConfig {
|
|
19
|
+
network: ISuiNetwork;
|
|
20
|
+
/** gRPC-web endpoints in priority order. */
|
|
21
|
+
rpcUrls: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the network and ordered endpoints for a Sui config.
|
|
25
|
+
*
|
|
26
|
+
* A consumer `rpcUrl` becomes the primary, with the public fullnode kept behind
|
|
27
|
+
* it as failover, so a premium provider still degrades to the public network
|
|
28
|
+
* instead of becoming a single point of failure.
|
|
29
|
+
*
|
|
30
|
+
* @param config - The `sui` config passed to `new UpshiftSDK({ sui })`.
|
|
31
|
+
* @returns The network (`'mainnet'` by default) and its endpoints.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveSuiNetworkConfig(config?: ISuiConfig): ISuiNetworkConfig;
|
|
34
|
+
/**
|
|
35
|
+
* Host-only view of an endpoint, safe for logs and errors: keyed provider URLs
|
|
36
|
+
* carry their key in the path.
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
export declare function suiEndpointHost(url: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Whether an error from a Sui call means the endpoint failed and the next one
|
|
42
|
+
* should be tried.
|
|
43
|
+
*
|
|
44
|
+
* @param err - What the call rejected with.
|
|
45
|
+
* @returns `true` for transport failures, gRPC statuses in
|
|
46
|
+
* {@link RETRYABLE_GRPC_CODES}, a bare `NOT_FOUND` (the endpoint has no Sui
|
|
47
|
+
* gRPC service at that path) and the SDK's own call timeout.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
export declare function isRetryableSuiError(err: unknown): boolean;
|
|
51
|
+
type SuiClientFactory = (baseUrl: string, network: ISuiNetwork) => Promise<SuiGrpcClient>;
|
|
52
|
+
/**
|
|
53
|
+
* Replace the client factory and clear the caches. Test-only seam.
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
export declare function setSuiClientFactoryForTests(factory: SuiClientFactory | undefined): void;
|
|
57
|
+
/**
|
|
58
|
+
* Run a Sui call against the first endpoint that answers.
|
|
59
|
+
*
|
|
60
|
+
* Endpoints are tried in order, starting with the one that last worked (for
|
|
61
|
+
* {@link PREFERRED_ENDPOINT_TTL_MS}). An endpoint failure — see
|
|
62
|
+
* {@link isRetryableSuiError} — moves on to the next endpoint; any other error
|
|
63
|
+
* is thrown at once. Each attempt is bounded by a 15 s timeout.
|
|
64
|
+
*
|
|
65
|
+
* Worst case: one attempt per endpoint (two with a consumer `rpcUrl`).
|
|
66
|
+
*
|
|
67
|
+
* @param config - Network and endpoints from {@link resolveSuiNetworkConfig}.
|
|
68
|
+
* @param call - The call to make with a client; it may run more than once, so
|
|
69
|
+
* it must not have side effects before its RPC completes.
|
|
70
|
+
* @returns What `call` resolves to.
|
|
71
|
+
* @throws {@link UpshiftNetworkError} when every endpoint fails, naming their
|
|
72
|
+
* hosts (never their full URLs); the last failure is the `cause`.
|
|
73
|
+
* @throws Whatever `call` throws for a non-endpoint error.
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const config = resolveSuiNetworkConfig({ network: 'testnet' });
|
|
77
|
+
* const { object } = await withSuiClient(config, (client) =>
|
|
78
|
+
* client.core.getObject({ objectId: vaultId }),
|
|
79
|
+
* );
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function withSuiClient<T>(config: ISuiNetworkConfig, call: (client: SuiGrpcClient) => Promise<T>): Promise<T>;
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,216 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.resolveSuiNetworkConfig = resolveSuiNetworkConfig;
|
|
37
|
+
exports.suiEndpointHost = suiEndpointHost;
|
|
38
|
+
exports.isRetryableSuiError = isRetryableSuiError;
|
|
39
|
+
exports.setSuiClientFactoryForTests = setSuiClientFactoryForTests;
|
|
40
|
+
exports.withSuiClient = withSuiClient;
|
|
41
|
+
const logger_1 = require("../../core/logger");
|
|
42
|
+
const errors_1 = require("../../core/errors");
|
|
43
|
+
const concurrency_1 = require("../../core/helpers/concurrency");
|
|
44
|
+
const constants_1 = require("./constants");
|
|
45
|
+
/** Bound on one Sui call, so a hung endpoint fails over instead of blocking. */
|
|
46
|
+
const SUI_CALL_TIMEOUT_MS = 15_000;
|
|
47
|
+
/** How long a working endpoint is tried first before the order resets. */
|
|
48
|
+
const PREFERRED_ENDPOINT_TTL_MS = 30_000;
|
|
49
|
+
/**
|
|
50
|
+
* gRPC status codes that mean the endpoint, not the request, failed: the next
|
|
51
|
+
* endpoint may succeed. `fetch failed` (refused connection, DNS) arrives as
|
|
52
|
+
* `INTERNAL`; a provider that does not serve gRPC-web answers `UNAUTHENTICATED`
|
|
53
|
+
* or similar. Request errors such as `INVALID_ARGUMENT` would fail the same way
|
|
54
|
+
* everywhere, so they are thrown at once.
|
|
55
|
+
*/
|
|
56
|
+
const RETRYABLE_GRPC_CODES = new Set([
|
|
57
|
+
'INTERNAL',
|
|
58
|
+
'UNAVAILABLE',
|
|
59
|
+
'UNKNOWN',
|
|
60
|
+
'DEADLINE_EXCEEDED',
|
|
61
|
+
'RESOURCE_EXHAUSTED',
|
|
62
|
+
'UNAUTHENTICATED',
|
|
63
|
+
'PERMISSION_DENIED',
|
|
64
|
+
'UNIMPLEMENTED',
|
|
65
|
+
]);
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the network and ordered endpoints for a Sui config.
|
|
68
|
+
*
|
|
69
|
+
* A consumer `rpcUrl` becomes the primary, with the public fullnode kept behind
|
|
70
|
+
* it as failover, so a premium provider still degrades to the public network
|
|
71
|
+
* instead of becoming a single point of failure.
|
|
72
|
+
*
|
|
73
|
+
* @param config - The `sui` config passed to `new UpshiftSDK({ sui })`.
|
|
74
|
+
* @returns The network (`'mainnet'` by default) and its endpoints.
|
|
75
|
+
*/
|
|
76
|
+
function resolveSuiNetworkConfig(config = {}) {
|
|
77
|
+
const network = config.network ?? 'mainnet';
|
|
78
|
+
const publicUrl = constants_1.SUI_GRPC_URLS[network];
|
|
79
|
+
const override = config.rpcUrl?.trim();
|
|
80
|
+
const rpcUrls = override && override !== publicUrl ? [override, publicUrl] : [publicUrl];
|
|
81
|
+
return { network, rpcUrls };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Host-only view of an endpoint, safe for logs and errors: keyed provider URLs
|
|
85
|
+
* carry their key in the path.
|
|
86
|
+
* @internal
|
|
87
|
+
*/
|
|
88
|
+
function suiEndpointHost(url) {
|
|
89
|
+
try {
|
|
90
|
+
return new URL(url).host;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return '<invalid url>';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Whether an error from a Sui call means the endpoint failed and the next one
|
|
98
|
+
* should be tried.
|
|
99
|
+
*
|
|
100
|
+
* @param err - What the call rejected with.
|
|
101
|
+
* @returns `true` for transport failures, gRPC statuses in
|
|
102
|
+
* {@link RETRYABLE_GRPC_CODES}, a bare `NOT_FOUND` (the endpoint has no Sui
|
|
103
|
+
* gRPC service at that path) and the SDK's own call timeout.
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
function isRetryableSuiError(err) {
|
|
107
|
+
if (!(err instanceof Error))
|
|
108
|
+
return false;
|
|
109
|
+
if (err.message.startsWith('sui call timed out'))
|
|
110
|
+
return true;
|
|
111
|
+
if (err.name !== 'RpcError')
|
|
112
|
+
return false;
|
|
113
|
+
const code = err.code;
|
|
114
|
+
if (typeof code !== 'string')
|
|
115
|
+
return false;
|
|
116
|
+
if (RETRYABLE_GRPC_CODES.has(code))
|
|
117
|
+
return true;
|
|
118
|
+
// A path with no gRPC service answers HTTP 404, which the transport reports
|
|
119
|
+
// as NOT_FOUND with no message; a missing object is a different error type.
|
|
120
|
+
return code === 'NOT_FOUND' && err.message === '';
|
|
121
|
+
}
|
|
122
|
+
/** Default factory: loads `@mysten/sui/grpc` on first use. */
|
|
123
|
+
const defaultClientFactory = async (baseUrl, network) => {
|
|
124
|
+
const { SuiGrpcClient } = await Promise.resolve().then(() => __importStar(require('@mysten/sui/grpc')));
|
|
125
|
+
return new SuiGrpcClient({ baseUrl, network });
|
|
126
|
+
};
|
|
127
|
+
let clientFactory = defaultClientFactory;
|
|
128
|
+
const clientCache = new Map();
|
|
129
|
+
const preferredEndpoint = new Map();
|
|
130
|
+
/**
|
|
131
|
+
* Replace the client factory and clear the caches. Test-only seam.
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
134
|
+
function setSuiClientFactoryForTests(factory) {
|
|
135
|
+
clientFactory = factory ?? defaultClientFactory;
|
|
136
|
+
clientCache.clear();
|
|
137
|
+
preferredEndpoint.clear();
|
|
138
|
+
}
|
|
139
|
+
function clientFor(url, network) {
|
|
140
|
+
const key = `${network}|${url}`;
|
|
141
|
+
let client = clientCache.get(key);
|
|
142
|
+
if (!client) {
|
|
143
|
+
client = clientFactory(url, network);
|
|
144
|
+
clientCache.set(key, client);
|
|
145
|
+
// A failed construction must not be cached forever.
|
|
146
|
+
client.catch(() => clientCache.delete(key));
|
|
147
|
+
}
|
|
148
|
+
return client;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Run a Sui call against the first endpoint that answers.
|
|
152
|
+
*
|
|
153
|
+
* Endpoints are tried in order, starting with the one that last worked (for
|
|
154
|
+
* {@link PREFERRED_ENDPOINT_TTL_MS}). An endpoint failure — see
|
|
155
|
+
* {@link isRetryableSuiError} — moves on to the next endpoint; any other error
|
|
156
|
+
* is thrown at once. Each attempt is bounded by a 15 s timeout.
|
|
157
|
+
*
|
|
158
|
+
* Worst case: one attempt per endpoint (two with a consumer `rpcUrl`).
|
|
159
|
+
*
|
|
160
|
+
* @param config - Network and endpoints from {@link resolveSuiNetworkConfig}.
|
|
161
|
+
* @param call - The call to make with a client; it may run more than once, so
|
|
162
|
+
* it must not have side effects before its RPC completes.
|
|
163
|
+
* @returns What `call` resolves to.
|
|
164
|
+
* @throws {@link UpshiftNetworkError} when every endpoint fails, naming their
|
|
165
|
+
* hosts (never their full URLs); the last failure is the `cause`.
|
|
166
|
+
* @throws Whatever `call` throws for a non-endpoint error.
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* const config = resolveSuiNetworkConfig({ network: 'testnet' });
|
|
170
|
+
* const { object } = await withSuiClient(config, (client) =>
|
|
171
|
+
* client.core.getObject({ objectId: vaultId }),
|
|
172
|
+
* );
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
async function withSuiClient(config, call) {
|
|
176
|
+
const cacheKey = `${config.network}|${config.rpcUrls.join('|')}`;
|
|
177
|
+
const preferred = preferredEndpoint.get(cacheKey);
|
|
178
|
+
const order = preferred && Date.now() - preferred.at < PREFERRED_ENDPOINT_TTL_MS
|
|
179
|
+
? [preferred.url, ...config.rpcUrls.filter((u) => u !== preferred.url)]
|
|
180
|
+
: [...config.rpcUrls];
|
|
181
|
+
let lastError;
|
|
182
|
+
for (const url of order) {
|
|
183
|
+
let client;
|
|
184
|
+
try {
|
|
185
|
+
client = await clientFor(url, config.network);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
// A malformed endpoint (e.g. a typo'd consumer rpcUrl) must not disable
|
|
189
|
+
// the endpoints behind it.
|
|
190
|
+
lastError = err;
|
|
191
|
+
logger_1.Logger.log.warn('withSuiClient', 'Invalid Sui endpoint; trying next', {
|
|
192
|
+
host: suiEndpointHost(url),
|
|
193
|
+
});
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const result = await (0, concurrency_1.withTimeout)(call(client), SUI_CALL_TIMEOUT_MS, 'sui call');
|
|
198
|
+
preferredEndpoint.set(cacheKey, { url, at: Date.now() });
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
if (!isRetryableSuiError(err))
|
|
203
|
+
throw err;
|
|
204
|
+
lastError = err;
|
|
205
|
+
logger_1.Logger.log.warn('withSuiClient', 'Sui endpoint failed; trying next', {
|
|
206
|
+
host: suiEndpointHost(url),
|
|
207
|
+
code: err.code,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
preferredEndpoint.delete(cacheKey);
|
|
212
|
+
throw new errors_1.UpshiftNetworkError(`Every Sui ${config.network} endpoint failed: ${order
|
|
213
|
+
.map(suiEndpointHost)
|
|
214
|
+
.join(', ')}`, { cause: lastError, context: { network: config.network } });
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ISuiNetwork } from '../../types';
|
|
2
|
+
export { SUI_CHAIN_ID } from '../../core/constants/web3';
|
|
3
|
+
/** Sui's shared `Clock` system object, passed to time-dependent vault calls. */
|
|
4
|
+
export declare const SUI_CLOCK_ID = "0x6";
|
|
5
|
+
/** Sui's shared `CoinRegistry` system object (coin metadata / currencies). */
|
|
6
|
+
export declare const SUI_COIN_REGISTRY_ID = "0xc";
|
|
7
|
+
/**
|
|
8
|
+
* Mysten's public fullnodes, which serve gRPC-web. The SDK talks gRPC-web so
|
|
9
|
+
* the same code runs in browsers; a consumer `rpcUrl` goes in front of these as
|
|
10
|
+
* the primary (see `resolveSuiNetworkConfig`).
|
|
11
|
+
*/
|
|
12
|
+
export declare const SUI_GRPC_URLS: Record<ISuiNetwork, string>;
|
|
13
|
+
/**
|
|
14
|
+
* IDs of the deployed Upshift vault contracts on one Sui network.
|
|
15
|
+
*
|
|
16
|
+
* `packageId` is the latest published package (what move calls target);
|
|
17
|
+
* `originalPackageId` is the first published one, which Move keeps in type
|
|
18
|
+
* names across upgrades, so it names the vault's on-chain type.
|
|
19
|
+
*/
|
|
20
|
+
export interface ISuiVaultContracts {
|
|
21
|
+
/** Latest `upshift_vault` package ID; move-call targets use it. */
|
|
22
|
+
packageId: string;
|
|
23
|
+
/** Original `upshift_vault` package ID; vault and key types use it. */
|
|
24
|
+
originalPackageId: string;
|
|
25
|
+
/** Shared `upshift_vault::registry::Registry` object ID. */
|
|
26
|
+
registryId: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Built-in contract IDs per network. Empty until the contracts are published
|
|
30
|
+
* there: until then callers pass `packageId`, `originalPackageId` and
|
|
31
|
+
* `registryId` through `new UpshiftSDK({ sui })`.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SUI_VAULT_CONTRACTS: Record<ISuiNetwork, Partial<ISuiVaultContracts>>;
|