0xtrails 0.12.0 → 0.12.2
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/dist/analytics.d.ts +65 -50
- package/dist/analytics.d.ts.map +1 -1
- package/dist/{ccip-DtfgR432.js → ccip-62W6LwH2.js} +28 -28
- package/dist/chains.d.ts.map +1 -1
- package/dist/error.d.ts +2 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/estimate.d.ts.map +1 -1
- package/dist/fees.d.ts.map +1 -1
- package/dist/{index-CHiCSmCD.js → index-C0QTNYIA.js} +43750 -41806
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +199 -171
- package/dist/localeUtils.d.ts.map +1 -1
- package/dist/meld/components/MeldCountriesList.d.ts +0 -2
- package/dist/meld/components/MeldCountriesList.d.ts.map +1 -1
- package/dist/meld/components/MeldFundMethods.d.ts.map +1 -1
- package/dist/meld/components/MeldTokensList.d.ts.map +1 -1
- package/dist/meld/utils/meld.d.ts +2 -52
- package/dist/meld/utils/meld.d.ts.map +1 -1
- package/dist/poolUtils.d.ts.map +1 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/prices.d.ts +1 -2
- package/dist/prices.d.ts.map +1 -1
- package/dist/query/balance.fetchers.d.ts +2 -2
- package/dist/query/balance.fetchers.d.ts.map +1 -1
- package/dist/query/fiat.fetchers.d.ts +11 -0
- package/dist/query/fiat.fetchers.d.ts.map +1 -0
- package/dist/query/fiat.hooks.d.ts +18 -0
- package/dist/query/fiat.hooks.d.ts.map +1 -0
- package/dist/query/fiat.queries.d.ts +24 -0
- package/dist/query/fiat.queries.d.ts.map +1 -0
- package/dist/query/meld.fetchers.d.ts +19 -0
- package/dist/query/meld.fetchers.d.ts.map +1 -0
- package/dist/query/meld.hooks.d.ts +4 -0
- package/dist/query/meld.hooks.d.ts.map +1 -0
- package/dist/query/meld.queries.d.ts +61 -0
- package/dist/query/meld.queries.d.ts.map +1 -0
- package/dist/recover.d.ts.map +1 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/standardDeposit.d.ts +7 -1
- package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/intentHandler.d.ts +2 -0
- package/dist/transactionIntent/handlers/intentHandler.d.ts.map +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts +2 -2
- package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
- package/dist/transactionIntent/types.d.ts +2 -0
- package/dist/transactionIntent/types.d.ts.map +1 -1
- package/dist/transactionIntent/utils/balanceChecker.d.ts +3 -1
- package/dist/transactionIntent/utils/balanceChecker.d.ts.map +1 -1
- package/dist/transactions.d.ts +2 -9
- package/dist/transactions.d.ts.map +1 -1
- package/dist/umd/trails.min.js +206 -152
- package/dist/utils/fiat.d.ts +8 -0
- package/dist/utils/fiat.d.ts.map +1 -0
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/passthrough.d.ts +5 -2
- package/dist/utils/passthrough.d.ts.map +1 -1
- package/dist/utils/validation.d.ts +33 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/walletUtils.d.ts +1 -1
- package/dist/walletUtils.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/DepositTracker.d.ts.map +1 -1
- package/dist/widget/components/Earn.d.ts +2 -0
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/FeeOption.d.ts.map +1 -1
- package/dist/widget/components/Fund.d.ts.map +1 -1
- package/dist/widget/components/FundMethods.d.ts.map +1 -1
- package/dist/widget/components/HookModalContent.d.ts.map +1 -1
- package/dist/widget/components/MeldForm.d.ts.map +1 -1
- package/dist/widget/components/MeldHistory.d.ts.map +1 -1
- package/dist/widget/components/MeldStepsFlow.d.ts.map +1 -1
- package/dist/widget/components/OFTProgressBar.d.ts +2 -0
- package/dist/widget/components/OFTProgressBar.d.ts.map +1 -1
- package/dist/widget/components/OnRampProviderSelector.d.ts.map +1 -1
- package/dist/widget/components/OnrampHistoryRow.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -1
- package/dist/widget/components/PoolDeposit.d.ts +2 -0
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Receipt.d.ts.map +1 -1
- package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
- package/dist/widget/components/Swap.d.ts +2 -0
- package/dist/widget/components/Swap.d.ts.map +1 -1
- package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
- package/dist/widget/components/Withdraw.d.ts.map +1 -1
- package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
- package/dist/widget/hooks/useCustomTokenSearch.d.ts.map +1 -1
- package/dist/widget/hooks/useDisplayCurrencyPreference.d.ts.map +1 -1
- package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts +3 -21
- package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts.map +1 -1
- package/dist/widget/hooks/useMeldTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/useOnRampCountryDefaults.d.ts +0 -18
- package/dist/widget/hooks/useOnRampCountryDefaults.d.ts.map +1 -1
- package/dist/widget/hooks/useOnRampPaymentMethods.d.ts +2 -18
- package/dist/widget/hooks/useOnRampPaymentMethods.d.ts.map +1 -1
- package/dist/widget/hooks/useOnRampQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useQuote.d.ts +5 -1
- package/dist/widget/hooks/useQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useSendForm.d.ts +3 -1
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/hooks/useTokenWithFreshBalance.d.ts +3 -2
- package/dist/widget/hooks/useTokenWithFreshBalance.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/dist/widget/types/commonProps.d.ts +2 -0
- package/dist/widget/types/commonProps.d.ts.map +1 -1
- package/dist/widget/utils/transactionFailure.d.ts +20 -0
- package/dist/widget/utils/transactionFailure.d.ts.map +1 -0
- package/dist/widget/widget.d.ts +44 -3
- package/dist/widget/widget.d.ts.map +1 -1
- package/dist/widget/workers/intentExecutionWorker.d.ts.map +1 -1
- package/package.json +22 -22
- package/src/analytics.ts +115 -79
- package/src/chains.ts +0 -1
- package/src/error.ts +11 -0
- package/src/estimate.ts +12 -7
- package/src/fees.ts +0 -1
- package/src/index.ts +11 -0
- package/src/localeUtils.ts +3 -1
- package/src/meld/components/MeldCountriesList.tsx +30 -15
- package/src/meld/components/MeldFundMethods.tsx +8 -4
- package/src/meld/components/MeldTokensList.tsx +90 -2
- package/src/meld/utils/meld.ts +3 -400
- package/src/poolUtils.ts +5 -19
- package/src/prepareSend.ts +32 -5
- package/src/prices.ts +7 -33
- package/src/query/balance.fetchers.ts +128 -168
- package/src/query/fiat.fetchers.ts +33 -0
- package/src/query/fiat.hooks.ts +71 -0
- package/src/query/fiat.queries.ts +67 -0
- package/src/query/meld.fetchers.ts +97 -0
- package/src/query/meld.hooks.ts +18 -0
- package/src/query/meld.queries.ts +184 -0
- package/src/recover.ts +6 -1
- package/src/tokens.ts +31 -6
- package/src/transactionIntent/deposits/depositOrchestrator.ts +2 -0
- package/src/transactionIntent/deposits/gaslessDeposit.ts +9 -2
- package/src/transactionIntent/deposits/standardDeposit.ts +35 -14
- package/src/transactionIntent/handlers/intentHandler.ts +134 -138
- package/src/transactionIntent/quote/normalizeQuote.ts +31 -22
- package/src/transactionIntent/quote/quoteHelpers.ts +24 -7
- package/src/transactionIntent/types.ts +2 -0
- package/src/transactionIntent/utils/balanceChecker.ts +10 -4
- package/src/transactions.ts +22 -13
- package/src/umd.tsx +1 -1
- package/src/utils/fiat.ts +32 -0
- package/src/utils/format.ts +1 -3
- package/src/utils/passthrough.ts +19 -3
- package/src/utils/validation.ts +88 -0
- package/src/utils.ts +2 -1
- package/src/walletUtils.ts +2 -2
- package/src/widget/components/AccountIntentTransactionHistory.tsx +2 -2
- package/src/widget/components/ClassicSwap.tsx +10 -4
- package/src/widget/components/DepositTracker.tsx +2 -5
- package/src/widget/components/Earn.tsx +6 -0
- package/src/widget/components/FeeOption.tsx +15 -8
- package/src/widget/components/Fund.tsx +16 -11
- package/src/widget/components/FundMethods.tsx +255 -192
- package/src/widget/components/HookModalContent.tsx +4 -0
- package/src/widget/components/MeldForm.tsx +44 -42
- package/src/widget/components/MeldHistory.tsx +4 -3
- package/src/widget/components/MeldStepsFlow.tsx +33 -71
- package/src/widget/components/OFTProgressBar.tsx +32 -12
- package/src/widget/components/OnRampProviderSelector.tsx +2 -1
- package/src/widget/components/OnrampHistoryRow.tsx +2 -1
- package/src/widget/components/Pay.tsx +8 -2
- package/src/widget/components/PercentageMaxButtons.tsx +5 -3
- package/src/widget/components/PoolDeposit.tsx +6 -0
- package/src/widget/components/PoolWithdraw.tsx +1 -1
- package/src/widget/components/QuoteDetails.tsx +5 -4
- package/src/widget/components/Receipt.tsx +4 -3
- package/src/widget/components/SlippageToleranceSettings.tsx +3 -2
- package/src/widget/components/Swap.tsx +2 -0
- package/src/widget/components/TransferPendingVertical.tsx +21 -28
- package/src/widget/components/UserPreferences.tsx +1 -1
- package/src/widget/components/Withdraw.tsx +20 -14
- package/src/widget/hooks/useAmountUsd.ts +3 -15
- package/src/widget/hooks/useCustomTokenSearch.tsx +2 -6
- package/src/widget/hooks/useDisplayCurrencyPreference.tsx +1 -2
- package/src/widget/hooks/useFiatOnRampCurrencies.ts +11 -76
- package/src/widget/hooks/useMeldTransactionHistory.ts +24 -89
- package/src/widget/hooks/useOnRampCountryDefaults.ts +3 -49
- package/src/widget/hooks/useOnRampPaymentMethods.ts +21 -100
- package/src/widget/hooks/useOnRampQuote.ts +2 -5
- package/src/widget/hooks/useQuote.ts +10 -12
- package/src/widget/hooks/useSendForm.ts +6 -0
- package/src/widget/hooks/useTokenList.ts +3 -6
- package/src/widget/hooks/useTokenWithFreshBalance.ts +141 -11
- package/src/widget/types/commonProps.ts +2 -0
- package/src/widget/utils/transactionFailure.ts +52 -0
- package/src/widget/widget.tsx +137 -59
- package/src/widget/workers/intentExecutionWorker.ts +3 -1
- package/dist/widget/hooks/useExchangeRate.d.ts +0 -31
- package/dist/widget/hooks/useExchangeRate.d.ts.map +0 -1
- package/dist/widget/hooks/useFiatCurrencyList.d.ts +0 -3
- package/dist/widget/hooks/useFiatCurrencyList.d.ts.map +0 -1
- package/src/widget/hooks/useExchangeRate.ts +0 -257
- package/src/widget/hooks/useFiatCurrencyList.ts +0 -66
|
@@ -45,10 +45,6 @@ import { formatRawAmount, rawAmountToNumber } from "../../utils/format.js"
|
|
|
45
45
|
import { invalidateTokenBalancesCache } from "../../query/balance.queries.js"
|
|
46
46
|
import { calcAmountUsdPrice } from "../../prices.js"
|
|
47
47
|
import { checkAccountBalance } from "../utils/balanceChecker.js"
|
|
48
|
-
import {
|
|
49
|
-
fetchCachedGasLimit,
|
|
50
|
-
createGasEstimationParams,
|
|
51
|
-
} from "../../useGasEstimation.js"
|
|
52
48
|
import { queryClient as defaultQueryClient } from "../../query/client.js"
|
|
53
49
|
import {
|
|
54
50
|
getFeesFromIntent,
|
|
@@ -63,7 +59,7 @@ import {
|
|
|
63
59
|
trackPaymentError,
|
|
64
60
|
trackTransactionConfirmed,
|
|
65
61
|
} from "../../analytics.js"
|
|
66
|
-
import { getFullErrorMessage } from "../../error.js"
|
|
62
|
+
import { getFullErrorMessage, getIsAlreadyExecutingError } from "../../error.js"
|
|
67
63
|
import { getIsCustomCalldata } from "../../contractUtils.js"
|
|
68
64
|
import { isNativeToken } from "../../utils/address.js"
|
|
69
65
|
import { ensureErc20Approval } from "../utils/erc20Approval.js"
|
|
@@ -109,6 +105,8 @@ export interface IntentHandlerParams {
|
|
|
109
105
|
tradeType?: TradeType
|
|
110
106
|
swapProvider?: RouteProvider | null
|
|
111
107
|
bridgeProvider?: RouteProvider | null
|
|
108
|
+
swapProviderFallback?: boolean
|
|
109
|
+
bridgeProviderFallback?: boolean
|
|
112
110
|
fundMethod?: FundMethod
|
|
113
111
|
mode?: "pay" | "fund" | "earn" | "swap" | "withdraw"
|
|
114
112
|
checkoutOnHandlers?: Partial<CheckoutOnHandlers>
|
|
@@ -132,10 +130,33 @@ export interface IntentHandlerParams {
|
|
|
132
130
|
refundAddress?: string
|
|
133
131
|
}
|
|
134
132
|
|
|
133
|
+
function isFeeSelectionPassthroughEligible(params: {
|
|
134
|
+
selectedFeeOption: FeeOption | null | undefined
|
|
135
|
+
gasFeeOptions: GasFeeOptions | undefined
|
|
136
|
+
}): boolean {
|
|
137
|
+
const { selectedFeeOption, gasFeeOptions } = params
|
|
138
|
+
|
|
139
|
+
// Undefined means no explicit user selection yet: stay conservative and use intent flow.
|
|
140
|
+
if (selectedFeeOption === undefined) {
|
|
141
|
+
return false
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Null means "native fee selected". This is only passthrough-safe if the native fee option
|
|
145
|
+
// advertised by backend is explicitly marked passthrough-eligible.
|
|
146
|
+
if (selectedFeeOption === null) {
|
|
147
|
+
const nativeFeeOption = gasFeeOptions?.feeOptions?.find((option) =>
|
|
148
|
+
isNativeToken(option.tokenAddress),
|
|
149
|
+
)
|
|
150
|
+
return nativeFeeOption?.isPassthroughEligible === true
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return selectedFeeOption.isPassthroughEligible === true
|
|
154
|
+
}
|
|
155
|
+
|
|
135
156
|
/**
|
|
136
157
|
* Execute a passthrough transaction (direct transfer, no intents).
|
|
137
158
|
*
|
|
138
|
-
* Used
|
|
159
|
+
* Used when backend marks quote passthrough-eligible and client-side fee gating allows it.
|
|
139
160
|
* Handles ERC20 approval if needed for custom calldata scenarios (S4c).
|
|
140
161
|
* See SPEC.md for flow decision details.
|
|
141
162
|
*/
|
|
@@ -556,18 +577,7 @@ async function executeIntent(
|
|
|
556
577
|
})
|
|
557
578
|
} catch (executeError: unknown) {
|
|
558
579
|
// If intent is already executing, that's fine - it means the system picked it up
|
|
559
|
-
|
|
560
|
-
executeError instanceof Error ? executeError.message : ""
|
|
561
|
-
const errorCause =
|
|
562
|
-
typeof executeError === "object" &&
|
|
563
|
-
executeError !== null &&
|
|
564
|
-
"cause" in executeError
|
|
565
|
-
? String((executeError as { cause?: unknown }).cause)
|
|
566
|
-
: ""
|
|
567
|
-
if (
|
|
568
|
-
errorMessage.includes("EXECUTING") ||
|
|
569
|
-
errorCause.includes("EXECUTING")
|
|
570
|
-
) {
|
|
580
|
+
if (getIsAlreadyExecutingError(executeError)) {
|
|
571
581
|
logger.console.log(
|
|
572
582
|
"[trails-sdk] Intent already executing, continuing...",
|
|
573
583
|
)
|
|
@@ -655,24 +665,28 @@ async function executeIntent(
|
|
|
655
665
|
intentId: intent.intentId,
|
|
656
666
|
depositTransactionHash: depositUserTxnReceipt.transactionHash,
|
|
657
667
|
})
|
|
658
|
-
} catch (
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
668
|
+
} catch (executeError: unknown) {
|
|
669
|
+
// If intent is already executing (e.g. from early executeIntent call), that's fine
|
|
670
|
+
if (getIsAlreadyExecutingError(executeError)) {
|
|
671
|
+
logger.console.log(
|
|
672
|
+
"[trails-sdk] Intent already executing after deposit, continuing...",
|
|
673
|
+
)
|
|
674
|
+
} else {
|
|
675
|
+
logger.console.error(
|
|
676
|
+
"[trails-sdk] Error calling executeIntent:",
|
|
677
|
+
executeError,
|
|
678
|
+
)
|
|
679
|
+
}
|
|
663
680
|
}
|
|
664
681
|
}
|
|
665
682
|
}
|
|
666
683
|
}
|
|
667
|
-
|
|
668
684
|
// Start polling for intent completion to update transaction states
|
|
669
685
|
let finalReceipt: Awaited<ReturnType<typeof pollIntentReceipt>> = null
|
|
670
686
|
if (intent.intentId) {
|
|
671
|
-
// Determine max wait time based on route provider
|
|
672
|
-
// OFT routes (LayerZero OFT
|
|
673
|
-
const isOFTRoute = intent.quote?.routeProviders?.some(
|
|
674
|
-
(p) => p === "LZ_OFT" || p === "LZ_STARGATE",
|
|
675
|
-
)
|
|
687
|
+
// Determine max wait time based on route provider.
|
|
688
|
+
// OFT routes (LayerZero OFT) can take 5-10+ minutes to complete.
|
|
689
|
+
const isOFTRoute = intent.quote?.routeProviders?.some((p) => p === "LZ_OFT")
|
|
676
690
|
const maxWaitTime = isOFTRoute ? HOUR_MS : 10 * 60 * 1000 // 1 hour for OFT, 10 min for others
|
|
677
691
|
|
|
678
692
|
logger.console.log("[trails-sdk] Intent polling configuration:", {
|
|
@@ -1082,11 +1096,10 @@ export async function handleIntent(
|
|
|
1082
1096
|
params: IntentHandlerParams,
|
|
1083
1097
|
): Promise<PrepareSendReturn> {
|
|
1084
1098
|
const {
|
|
1085
|
-
queryClient: paramQueryClient,
|
|
1086
1099
|
mainSignerAddress,
|
|
1087
1100
|
originChainId,
|
|
1088
1101
|
originTokenAddress,
|
|
1089
|
-
|
|
1102
|
+
originTokenBalance,
|
|
1090
1103
|
originTokenDecimals,
|
|
1091
1104
|
originTokenSymbol,
|
|
1092
1105
|
destinationChainId,
|
|
@@ -1107,6 +1120,8 @@ export async function handleIntent(
|
|
|
1107
1120
|
tradeType = TradeType.EXACT_INPUT,
|
|
1108
1121
|
swapProvider: initialSwapProvider,
|
|
1109
1122
|
bridgeProvider: initialBridgeProvider,
|
|
1123
|
+
swapProviderFallback,
|
|
1124
|
+
bridgeProviderFallback,
|
|
1110
1125
|
fundMethod: fundMethodFromParams,
|
|
1111
1126
|
mode,
|
|
1112
1127
|
checkoutOnHandlers,
|
|
@@ -1123,7 +1138,6 @@ export async function handleIntent(
|
|
|
1123
1138
|
|
|
1124
1139
|
logger.console.log("[trails-sdk] handleIntent called")
|
|
1125
1140
|
|
|
1126
|
-
const effectiveQueryClient = paramQueryClient ?? defaultQueryClient
|
|
1127
1141
|
const effectiveFundMethod = fundMethodFromParams ?? "wallet"
|
|
1128
1142
|
const effectiveOriginChain = chain
|
|
1129
1143
|
const effectiveOriginTokenAddress = originTokenAddress
|
|
@@ -1165,6 +1179,8 @@ export async function handleIntent(
|
|
|
1165
1179
|
tradeType,
|
|
1166
1180
|
swapProvider,
|
|
1167
1181
|
bridgeProvider,
|
|
1182
|
+
swapProviderFallback,
|
|
1183
|
+
bridgeProviderFallback,
|
|
1168
1184
|
undefined,
|
|
1169
1185
|
walletId,
|
|
1170
1186
|
isSmartWallet,
|
|
@@ -1212,30 +1228,6 @@ export async function handleIntent(
|
|
|
1212
1228
|
throw new Error("Invalid intent")
|
|
1213
1229
|
}
|
|
1214
1230
|
|
|
1215
|
-
// Estimate gas using depositTransaction from API
|
|
1216
|
-
// Use passthrough depositTransaction if available, otherwise use intent depositTransaction
|
|
1217
|
-
const depositTxForGasEstimate =
|
|
1218
|
-
passthrough?.passthroughTransaction ?? intent.depositTransaction
|
|
1219
|
-
let estimatedGasLimit: bigint | undefined
|
|
1220
|
-
if (depositTxForGasEstimate) {
|
|
1221
|
-
try {
|
|
1222
|
-
estimatedGasLimit = await fetchCachedGasLimit(
|
|
1223
|
-
effectiveQueryClient,
|
|
1224
|
-
effectivePublicClient,
|
|
1225
|
-
createGasEstimationParams(
|
|
1226
|
-
account.address,
|
|
1227
|
-
depositTxForGasEstimate.to,
|
|
1228
|
-
depositTxForGasEstimate.data ?? "0x",
|
|
1229
|
-
BigInt(depositTxForGasEstimate.value ?? "0"),
|
|
1230
|
-
),
|
|
1231
|
-
"intent-quote",
|
|
1232
|
-
)
|
|
1233
|
-
} catch (error) {
|
|
1234
|
-
logger.console.warn("[trails-sdk] Gas estimation failed:", error)
|
|
1235
|
-
estimatedGasLimit = undefined
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
1231
|
// Determine if passthrough should be used
|
|
1240
1232
|
// SPEC: Only same-chain same-token with wallet fund method may use passthrough (C9b/C10/C11b/C12/D9b/D10/D11b/D12).
|
|
1241
1233
|
// SPEC: Cross-chain (A/B), different-token (C1-C8/D1-D8), non-wallet (C13/C14/D13/D14), and gasless (G1-G20) must always use intents.
|
|
@@ -1243,21 +1235,22 @@ export async function handleIntent(
|
|
|
1243
1235
|
// Backend sets passthrough.eligible, but we enforce structural invariants client-side as a safety net.
|
|
1244
1236
|
// For ERC20 passthrough with custom calldata, the SDK handles approval if insufficient (see executePassthrough).
|
|
1245
1237
|
const usePassthrough =
|
|
1246
|
-
isSameChainSameToken &&
|
|
1247
1238
|
effectiveFundMethod === "wallet" &&
|
|
1248
1239
|
passthrough?.eligible === true &&
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1240
|
+
!!passthrough?.passthroughTransaction &&
|
|
1241
|
+
isFeeSelectionPassthroughEligible({
|
|
1242
|
+
selectedFeeOption,
|
|
1243
|
+
gasFeeOptions,
|
|
1244
|
+
})
|
|
1254
1245
|
|
|
1255
1246
|
logger.console.log("[trails-sdk] Passthrough decision:", {
|
|
1256
1247
|
usePassthrough,
|
|
1257
1248
|
isSameChainSameToken,
|
|
1258
1249
|
fundMethod: effectiveFundMethod,
|
|
1259
1250
|
passthroughEligible: passthrough?.eligible,
|
|
1251
|
+
hasPassthroughTransaction: !!passthrough?.passthroughTransaction,
|
|
1260
1252
|
feeOptionPassthroughEligible: selectedFeeOption?.isPassthroughEligible,
|
|
1253
|
+
selectedFeeOption,
|
|
1261
1254
|
})
|
|
1262
1255
|
|
|
1263
1256
|
// Use passthrough-specific deposit transaction and states when doing passthrough
|
|
@@ -1270,27 +1263,6 @@ export async function handleIntent(
|
|
|
1270
1263
|
const quoteToAmount = intent.quote.toAmount.toString()
|
|
1271
1264
|
const quoteToAmountMin = intent.quote.toAmountMin.toString() || quoteToAmount
|
|
1272
1265
|
|
|
1273
|
-
// Now do the balance check with the actual deposit amount from the intent API
|
|
1274
|
-
// This is the correct amount needed in origin token units
|
|
1275
|
-
if (effectiveFundMethod === "wallet") {
|
|
1276
|
-
const { hasEnoughBalance } = await checkAccountBalance({
|
|
1277
|
-
account,
|
|
1278
|
-
tokenAddress: originTokenAddress,
|
|
1279
|
-
depositAmount: depositAmount,
|
|
1280
|
-
publicClient: effectivePublicClient,
|
|
1281
|
-
})
|
|
1282
|
-
noSufficientBalance = !hasEnoughBalance
|
|
1283
|
-
logger.console.log(
|
|
1284
|
-
"[trails-sdk] Balance check with actual deposit amount:",
|
|
1285
|
-
{
|
|
1286
|
-
depositAmount,
|
|
1287
|
-
originTokenAddress,
|
|
1288
|
-
hasEnoughBalance,
|
|
1289
|
-
noSufficientBalance,
|
|
1290
|
-
},
|
|
1291
|
-
)
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
1266
|
// Compute transaction states for both passthrough and intent flows
|
|
1295
1267
|
// This allows the UI to switch between them based on fee option selection
|
|
1296
1268
|
// SPEC: Passthrough-capable scenarios (C9b/C10/C11b/C12/D9b/D10/D11b/D12) expose passthrough transaction states; other scenarios use intent-flow states.
|
|
@@ -1311,51 +1283,78 @@ export async function handleIntent(
|
|
|
1311
1283
|
? passthroughTransactionStates
|
|
1312
1284
|
: intentTransactionStates
|
|
1313
1285
|
|
|
1314
|
-
//
|
|
1315
|
-
const quote = await
|
|
1316
|
-
|
|
1317
|
-
?
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1286
|
+
// Run balance check and quote normalization in parallel to reduce latency
|
|
1287
|
+
const [balanceResult, quote] = await Promise.all([
|
|
1288
|
+
effectiveFundMethod === "wallet"
|
|
1289
|
+
? checkAccountBalance({
|
|
1290
|
+
account,
|
|
1291
|
+
tokenAddress: originTokenAddress,
|
|
1292
|
+
depositAmount: depositAmount,
|
|
1293
|
+
publicClient: effectivePublicClient,
|
|
1294
|
+
knownBalance: originTokenBalance,
|
|
1295
|
+
decimals: originTokenDecimals,
|
|
1296
|
+
})
|
|
1297
|
+
: Promise.resolve({ hasEnoughBalance: true }),
|
|
1298
|
+
getNormalizedQuoteObject({
|
|
1299
|
+
originDepositAddress: usePassthrough
|
|
1300
|
+
? recipient
|
|
1301
|
+
: intent.originIntentAddress,
|
|
1302
|
+
destinationDepositAddress: usePassthrough
|
|
1303
|
+
? recipient
|
|
1304
|
+
: intent.destinationIntentAddress,
|
|
1305
|
+
destinationAddress: recipient,
|
|
1306
|
+
trailsApiKey: trailsApiKey || "",
|
|
1307
|
+
trailsApiUrl: trailsApiUrl || "",
|
|
1308
|
+
sequenceMetadataUrl: sequenceMetadataUrl || "",
|
|
1309
|
+
destinationCalldata,
|
|
1310
|
+
originAmount: depositAmount,
|
|
1311
|
+
destinationAmount: quoteToAmount,
|
|
1312
|
+
originAmountMin: depositAmount,
|
|
1313
|
+
destinationAmountMin: quoteToAmountMin,
|
|
1314
|
+
originTokenAddress,
|
|
1315
|
+
destinationTokenAddress,
|
|
1316
|
+
originTokenPriceUsd: sourceTokenPriceUsd?.toString() || null,
|
|
1317
|
+
destinationTokenPriceUsd: destinationTokenPriceUsd?.toString() || null,
|
|
1318
|
+
fees: isSameChainSameToken
|
|
1319
|
+
? undefined
|
|
1320
|
+
: getFeesFromIntent(intent, {
|
|
1321
|
+
tradeType,
|
|
1322
|
+
fromAmountUsd: calcAmountUsdPrice({
|
|
1323
|
+
amount: rawAmountToNumber(depositAmount, originTokenDecimals),
|
|
1324
|
+
usdPrice: sourceTokenPriceUsd,
|
|
1325
|
+
}),
|
|
1326
|
+
toAmountUsd: calcAmountUsdPrice({
|
|
1327
|
+
amount: rawAmountToNumber(
|
|
1328
|
+
quoteToAmount,
|
|
1329
|
+
destinationTokenDecimals,
|
|
1330
|
+
),
|
|
1331
|
+
usdPrice: destinationTokenPriceUsd,
|
|
1332
|
+
}),
|
|
1346
1333
|
}),
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1334
|
+
originChainId,
|
|
1335
|
+
destinationChainId,
|
|
1336
|
+
slippageTolerance,
|
|
1337
|
+
priceImpact: getPriceImpactFromIntent(intent),
|
|
1338
|
+
priceImpactUsd: getPriceImpactUsdFromIntent(intent),
|
|
1339
|
+
transactionStates,
|
|
1340
|
+
originNativeTokenPriceUsd,
|
|
1341
|
+
intent: usePassthrough ? undefined : intent,
|
|
1342
|
+
}),
|
|
1343
|
+
])
|
|
1344
|
+
|
|
1345
|
+
noSufficientBalance = !balanceResult.hasEnoughBalance
|
|
1346
|
+
quote.noSufficientBalance = noSufficientBalance
|
|
1347
|
+
if (effectiveFundMethod === "wallet") {
|
|
1348
|
+
logger.console.log(
|
|
1349
|
+
"[trails-sdk] Balance check with actual deposit amount:",
|
|
1350
|
+
{
|
|
1351
|
+
depositAmount,
|
|
1352
|
+
originTokenAddress,
|
|
1353
|
+
hasEnoughBalance: balanceResult.hasEnoughBalance,
|
|
1354
|
+
noSufficientBalance,
|
|
1355
|
+
},
|
|
1356
|
+
)
|
|
1357
|
+
}
|
|
1359
1358
|
|
|
1360
1359
|
// Add passthrough-specific fields to the quote
|
|
1361
1360
|
// This allows the UI to switch between passthrough and intent states based on fee option
|
|
@@ -1379,25 +1378,22 @@ export async function handleIntent(
|
|
|
1379
1378
|
const { skipCommit, commitOnly, skipIntentMonitoring } = options
|
|
1380
1379
|
|
|
1381
1380
|
try {
|
|
1382
|
-
// Recalculate passthrough decision at send time based on fee option
|
|
1383
|
-
// This allows
|
|
1384
|
-
// SPEC: All passthrough-capable scenarios (C9b/C10/C11b/C12/D9b/D10/D11b/D12) can switch to intent flow when a gasless fee option is selected.
|
|
1381
|
+
// Recalculate passthrough decision at send time based on fee option.
|
|
1382
|
+
// This allows switching between passthrough and intent flow after quote.
|
|
1385
1383
|
const sendFeeOption = options.selectedFeeOption
|
|
1386
|
-
// SPEC: Only same-chain same-token + wallet may passthrough (C9b/C10/C11b/C12/D9b/D10/D11b/D12).
|
|
1387
|
-
// When selectedFeeOption is null, it means native token is selected (which is passthrough eligible)
|
|
1388
1384
|
const sendUsePassthrough =
|
|
1389
|
-
isSameChainSameToken &&
|
|
1390
1385
|
effectiveFundMethod === "wallet" &&
|
|
1391
1386
|
passthrough?.eligible === true &&
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1387
|
+
!!passthrough?.passthroughTransaction &&
|
|
1388
|
+
isFeeSelectionPassthroughEligible({
|
|
1389
|
+
selectedFeeOption: sendFeeOption,
|
|
1390
|
+
gasFeeOptions,
|
|
1391
|
+
})
|
|
1397
1392
|
|
|
1398
1393
|
logger.console.log("[trails-sdk] Send passthrough decision:", {
|
|
1399
1394
|
sendUsePassthrough,
|
|
1400
1395
|
passthroughEligible: passthrough?.eligible,
|
|
1396
|
+
hasPassthroughTransaction: !!passthrough?.passthroughTransaction,
|
|
1401
1397
|
sendFeeOptionPassthroughEligible:
|
|
1402
1398
|
sendFeeOption?.isPassthroughEligible,
|
|
1403
1399
|
sendFeeOption,
|
|
@@ -22,25 +22,22 @@ import { getZeroFees } from "./feeExtractors.js"
|
|
|
22
22
|
import { formatDuration } from "../../utils/time.js"
|
|
23
23
|
import { IntentProtocolVersion } from "@0xtrails/api"
|
|
24
24
|
|
|
25
|
-
// TODO: make this dyanamic
|
|
26
|
-
function isOFTRoute(routeProviders?: string[]): boolean {
|
|
27
|
-
return (
|
|
28
|
-
routeProviders?.some((p) => p === "LZ_OFT" || p === "LZ_STARGATE") ?? false
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
25
|
export function getCompletionEstimateSeconds({
|
|
33
26
|
originChainId,
|
|
34
27
|
destinationChainId,
|
|
35
|
-
|
|
28
|
+
esimatedDurationSeconds,
|
|
36
29
|
}: {
|
|
37
30
|
originChainId: number
|
|
38
31
|
destinationChainId: number
|
|
39
|
-
|
|
32
|
+
esimatedDurationSeconds?: number | null
|
|
40
33
|
}): number {
|
|
41
|
-
//
|
|
42
|
-
if (
|
|
43
|
-
|
|
34
|
+
// Prefer provider-estimated route duration from QuoteIntent API when available.
|
|
35
|
+
if (
|
|
36
|
+
esimatedDurationSeconds != null &&
|
|
37
|
+
Number.isFinite(esimatedDurationSeconds) &&
|
|
38
|
+
esimatedDurationSeconds >= 0
|
|
39
|
+
) {
|
|
40
|
+
return Math.round(esimatedDurationSeconds)
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
if (originChainId === mainnet.id && destinationChainId === mainnet.id) {
|
|
@@ -57,25 +54,35 @@ export function getCompletionEstimateSeconds({
|
|
|
57
54
|
function getCompletionEstimateDisplay({
|
|
58
55
|
originChainId,
|
|
59
56
|
destinationChainId,
|
|
60
|
-
|
|
57
|
+
esimatedDurationSeconds,
|
|
61
58
|
}: {
|
|
62
59
|
originChainId: number
|
|
63
60
|
destinationChainId: number
|
|
64
|
-
|
|
61
|
+
esimatedDurationSeconds?: number | null
|
|
65
62
|
}): string {
|
|
66
|
-
if (isOFTRoute(routeProviders)) {
|
|
67
|
-
return "5-20 min"
|
|
68
|
-
}
|
|
69
|
-
|
|
70
63
|
return formatDuration(
|
|
71
64
|
getCompletionEstimateSeconds({
|
|
72
65
|
originChainId,
|
|
73
66
|
destinationChainId,
|
|
74
|
-
|
|
67
|
+
esimatedDurationSeconds,
|
|
75
68
|
}),
|
|
76
69
|
)
|
|
77
70
|
}
|
|
78
71
|
|
|
72
|
+
function getEsimatedDurationSeconds(intent?: Intent): number | undefined {
|
|
73
|
+
if (!intent?.quote) return undefined
|
|
74
|
+
|
|
75
|
+
const quote = intent.quote as { estimatedDuration?: unknown }
|
|
76
|
+
if (
|
|
77
|
+
typeof quote.estimatedDuration === "number" &&
|
|
78
|
+
Number.isFinite(quote.estimatedDuration)
|
|
79
|
+
) {
|
|
80
|
+
return quote.estimatedDuration
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return undefined
|
|
84
|
+
}
|
|
85
|
+
|
|
79
86
|
export async function getNormalizedQuoteObject({
|
|
80
87
|
originDepositAddress,
|
|
81
88
|
destinationDepositAddress,
|
|
@@ -253,7 +260,7 @@ export async function getNormalizedQuoteObject({
|
|
|
253
260
|
let gasCost: string = "0"
|
|
254
261
|
let gasCostFormatted: string = "0"
|
|
255
262
|
try {
|
|
256
|
-
if (originNativeTokenPriceUsd) {
|
|
263
|
+
if (originNativeTokenPriceUsd && (estimatedGasLimit || providedFeeData)) {
|
|
257
264
|
// Use the actual estimated gas limit if provided, otherwise use default
|
|
258
265
|
// This ensures the quote gas cost matches what will be used in the actual transaction
|
|
259
266
|
const gasLimitForCost = estimatedGasLimit || DEFAULT_MIN_GASLIMIT
|
|
@@ -414,10 +421,12 @@ export async function getNormalizedQuoteObject({
|
|
|
414
421
|
},
|
|
415
422
|
)
|
|
416
423
|
|
|
424
|
+
const esimatedDurationSeconds = getEsimatedDurationSeconds(intent)
|
|
425
|
+
|
|
417
426
|
const completionEstimateSeconds = getCompletionEstimateSeconds({
|
|
418
427
|
originChainId,
|
|
419
428
|
destinationChainId,
|
|
420
|
-
|
|
429
|
+
esimatedDurationSeconds,
|
|
421
430
|
})
|
|
422
431
|
|
|
423
432
|
return {
|
|
@@ -465,7 +474,7 @@ export async function getNormalizedQuoteObject({
|
|
|
465
474
|
completionEstimateDisplay: getCompletionEstimateDisplay({
|
|
466
475
|
originChainId,
|
|
467
476
|
destinationChainId,
|
|
468
|
-
|
|
477
|
+
esimatedDurationSeconds,
|
|
469
478
|
}),
|
|
470
479
|
transactionStates: transactionStates || [],
|
|
471
480
|
gasCostUsd,
|
|
@@ -9,6 +9,13 @@ import {
|
|
|
9
9
|
import { TradeType } from "../types.js"
|
|
10
10
|
import { logger } from "../../logger.js"
|
|
11
11
|
|
|
12
|
+
type QuoteIntentRequestOptionsWithFallback = NonNullable<
|
|
13
|
+
QuoteIntentRequest["options"]
|
|
14
|
+
> & {
|
|
15
|
+
swapProviderFallback?: boolean
|
|
16
|
+
bridgeProviderFallback?: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
export async function getIntentArgs(
|
|
13
20
|
mainSignerAddress: string,
|
|
14
21
|
originChainId: number,
|
|
@@ -24,6 +31,8 @@ export async function getIntentArgs(
|
|
|
24
31
|
tradeType: TradeType,
|
|
25
32
|
swapProvider?: RouteProvider | null,
|
|
26
33
|
bridgeProvider?: RouteProvider | null,
|
|
34
|
+
swapProviderFallback?: boolean,
|
|
35
|
+
bridgeProviderFallback?: boolean,
|
|
27
36
|
connector?: Connector | undefined,
|
|
28
37
|
walletId?: string | undefined,
|
|
29
38
|
isSmartWallet?: boolean,
|
|
@@ -73,6 +82,20 @@ export async function getIntentArgs(
|
|
|
73
82
|
)
|
|
74
83
|
}
|
|
75
84
|
|
|
85
|
+
const options: QuoteIntentRequestOptionsWithFallback = {
|
|
86
|
+
// When slippageTolerance is null (AUTO mode), don't send it so backend calculates optimal slippage
|
|
87
|
+
slippageTolerance:
|
|
88
|
+
slippageTolerance !== null ? Number(slippageTolerance) : undefined,
|
|
89
|
+
swapProvider: swapProvider || undefined,
|
|
90
|
+
bridgeProvider: bridgeProvider || undefined,
|
|
91
|
+
}
|
|
92
|
+
if (swapProviderFallback !== undefined) {
|
|
93
|
+
options.swapProviderFallback = swapProviderFallback
|
|
94
|
+
}
|
|
95
|
+
if (bridgeProviderFallback !== undefined) {
|
|
96
|
+
options.bridgeProviderFallback = bridgeProviderFallback
|
|
97
|
+
}
|
|
98
|
+
|
|
76
99
|
const intentArgs: QuoteIntentRequest = {
|
|
77
100
|
ownerAddress: mainSignerAddress,
|
|
78
101
|
originChainId,
|
|
@@ -86,13 +109,7 @@ export async function getIntentArgs(
|
|
|
86
109
|
destinationCallData: hasCustomCalldata ? destinationCalldata : "0x",
|
|
87
110
|
destinationCallValue: BigInt("0"),
|
|
88
111
|
onlyNativeGasFee,
|
|
89
|
-
options
|
|
90
|
-
// When slippageTolerance is null (AUTO mode), don't send it so backend calculates optimal slippage
|
|
91
|
-
slippageTolerance:
|
|
92
|
-
slippageTolerance !== null ? Number(slippageTolerance) : undefined,
|
|
93
|
-
swapProvider: swapProvider || undefined,
|
|
94
|
-
bridgeProvider: bridgeProvider || undefined,
|
|
95
|
-
},
|
|
112
|
+
options,
|
|
96
113
|
tradeType,
|
|
97
114
|
}
|
|
98
115
|
|
|
@@ -80,6 +80,8 @@ export type PrepareSendOptions = {
|
|
|
80
80
|
originNativeTokenPriceUsd?: number | null
|
|
81
81
|
swapProvider?: RouteProvider | null
|
|
82
82
|
bridgeProvider?: RouteProvider | null
|
|
83
|
+
swapProviderFallback?: boolean
|
|
84
|
+
bridgeProviderFallback?: boolean
|
|
83
85
|
fundMethod?: FundMethod
|
|
84
86
|
mode?: "pay" | "fund" | "earn" | "swap" | "withdraw"
|
|
85
87
|
originPublicClient?: PublicClient
|
|
@@ -13,11 +13,15 @@ export async function checkAccountBalance({
|
|
|
13
13
|
tokenAddress,
|
|
14
14
|
depositAmount,
|
|
15
15
|
publicClient,
|
|
16
|
+
knownBalance,
|
|
17
|
+
decimals: knownDecimals,
|
|
16
18
|
}: {
|
|
17
19
|
account: Account
|
|
18
20
|
tokenAddress: string
|
|
19
21
|
depositAmount: string
|
|
20
22
|
publicClient: PublicClient
|
|
23
|
+
knownBalance?: string
|
|
24
|
+
decimals?: number
|
|
21
25
|
}): Promise<{
|
|
22
26
|
hasEnoughBalance: boolean
|
|
23
27
|
balance: bigint
|
|
@@ -29,11 +33,11 @@ export async function checkAccountBalance({
|
|
|
29
33
|
try {
|
|
30
34
|
let balance: bigint
|
|
31
35
|
|
|
32
|
-
if (
|
|
33
|
-
|
|
36
|
+
if (knownBalance !== undefined) {
|
|
37
|
+
balance = BigInt(knownBalance)
|
|
38
|
+
} else if (isNativeToken(tokenAddress)) {
|
|
34
39
|
balance = await publicClient.getBalance({ address: account.address })
|
|
35
40
|
} else {
|
|
36
|
-
// ERC20 token balance
|
|
37
41
|
balance = await publicClient.readContract({
|
|
38
42
|
address: tokenAddress as `0x${string}`,
|
|
39
43
|
abi: erc20Abi,
|
|
@@ -54,8 +58,10 @@ export async function checkAccountBalance({
|
|
|
54
58
|
if (isNativeToken(tokenAddress)) {
|
|
55
59
|
balanceFormatted = formatUnits(balance, 18)
|
|
56
60
|
requiredAmountFormatted = formatUnits(requiredAmount, 18)
|
|
61
|
+
} else if (knownDecimals !== undefined) {
|
|
62
|
+
balanceFormatted = formatUnits(balance, knownDecimals)
|
|
63
|
+
requiredAmountFormatted = formatUnits(requiredAmount, knownDecimals)
|
|
57
64
|
} else {
|
|
58
|
-
// ERC20 token balance
|
|
59
65
|
const decimals = await publicClient.readContract({
|
|
60
66
|
address: tokenAddress as `0x${string}`,
|
|
61
67
|
abi: erc20Abi,
|