0xtrails 0.2.0 → 0.2.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/aave.d.ts.map +1 -1
- package/dist/analytics.d.ts +1 -0
- package/dist/analytics.d.ts.map +1 -1
- package/dist/{ccip-D6ToCrWc.js → ccip-ConT1gDe.js} +1 -1
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +1 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +2 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/gasless.d.ts +19 -7
- package/dist/gasless.d.ts.map +1 -1
- package/dist/{index-BqgeTLL8.js → index-CMh8uEbV.js} +27716 -26616
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -68
- package/dist/indexerClient.d.ts +10 -0
- package/dist/indexerClient.d.ts.map +1 -1
- package/dist/intentEntrypoint.d.ts +40 -14
- package/dist/intentEntrypoint.d.ts.map +1 -1
- package/dist/intents.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +11 -8
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/relayer.d.ts.map +1 -1
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/trails.d.ts.map +1 -1
- package/dist/trailsClient.d.ts.map +1 -1
- package/dist/trailsRouter.d.ts +22 -0
- package/dist/trailsRouter.d.ts.map +1 -0
- package/dist/transactions.d.ts +0 -1
- package/dist/transactions.d.ts.map +1 -1
- package/dist/widget/components/AccountSettings.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
- package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
- package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/FeeOption.d.ts +22 -0
- package/dist/widget/components/FeeOption.d.ts.map +1 -0
- package/dist/widget/components/FeeOptions.d.ts +13 -17
- package/dist/widget/components/FeeOptions.d.ts.map +1 -1
- package/dist/widget/components/Fund.d.ts.map +1 -1
- package/dist/widget/components/FundMethods.d.ts +1 -1
- package/dist/widget/components/FundMethods.d.ts.map +1 -1
- package/dist/widget/components/FundSendForm.d.ts.map +1 -1
- package/dist/widget/components/MeshConnectExchanges.d.ts +5 -2
- package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
- package/dist/widget/components/MeshConnectFlow.d.ts +2 -0
- package/dist/widget/components/MeshConnectFlow.d.ts.map +1 -1
- package/dist/widget/components/NativeGasOption.d.ts +12 -0
- package/dist/widget/components/NativeGasOption.d.ts.map +1 -0
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PaySendForm.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/TokenSelector.d.ts.map +1 -1
- package/dist/widget/components/UserPreferences.d.ts.map +1 -1
- package/dist/widget/hooks/useBack.d.ts +2 -0
- package/dist/widget/hooks/useBack.d.ts.map +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
- package/dist/widget/hooks/useSelectedFeeToken.d.ts +32 -0
- package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -0
- package/dist/widget/hooks/useSelectedMeshExchange.d.ts +14 -0
- package/dist/widget/hooks/useSelectedMeshExchange.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +8 -13
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +29 -28
- package/src/aave.ts +90 -74
- package/src/analytics.ts +6 -0
- package/src/chains.ts +10 -0
- package/src/config.ts +25 -10
- package/src/constants.ts +11 -10
- package/src/gasless.ts +162 -109
- package/src/index.ts +1 -1
- package/src/indexerClient.ts +73 -1
- package/src/intentEntrypoint.ts +66 -101
- package/src/intents.ts +0 -2
- package/src/prepareSend.ts +1425 -887
- package/src/relayer.ts +4 -3
- package/src/tokenBalances.ts +8 -1
- package/src/trails.ts +1 -3
- package/src/trailsClient.ts +4 -1
- package/src/{balanceInjector.ts → trailsRouter.ts} +14 -14
- package/src/transactions.ts +4 -54
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountSettings.tsx +7 -1
- package/src/widget/components/ClassicSwap.tsx +173 -175
- package/src/widget/components/ConfigDisplay.tsx +34 -1
- package/src/widget/components/ConnectWallet.tsx +168 -11
- package/src/widget/components/ConnectedWallets.tsx +184 -102
- package/src/widget/components/DebugToast.tsx +3 -3
- package/src/widget/components/Earn.tsx +4 -27
- package/src/widget/components/FeeOption.tsx +78 -0
- package/src/widget/components/FeeOptions.tsx +192 -127
- package/src/widget/components/Fund.tsx +18 -27
- package/src/widget/components/FundMethods.tsx +3 -3
- package/src/widget/components/FundSendForm.tsx +0 -33
- package/src/widget/components/MeshConnectExchanges.tsx +32 -3
- package/src/widget/components/MeshConnectFlow.tsx +23 -4
- package/src/widget/components/NativeGasOption.tsx +99 -0
- package/src/widget/components/Pay.tsx +36 -32
- package/src/widget/components/PaySendForm.tsx +0 -37
- package/src/widget/components/QuoteDetails.tsx +0 -29
- package/src/widget/components/TokenSelector.tsx +11 -0
- package/src/widget/components/TransferPendingVertical.tsx +1 -1
- package/src/widget/components/UserPreferences.tsx +3 -4
- package/src/widget/hooks/useBack.tsx +4 -0
- package/src/widget/hooks/useCurrentScreen.tsx +1 -0
- package/src/widget/hooks/useDefaultTokenSelection.tsx +8 -8
- package/src/widget/hooks/useSelectedFeeToken.tsx +299 -0
- package/src/widget/hooks/useSelectedMeshExchange.tsx +46 -0
- package/src/widget/hooks/useSendForm.ts +78 -23
- package/src/widget/hooks/useTokenList.ts +10 -1
- package/src/widget/widget.tsx +173 -111
- package/dist/balanceInjector.d.ts +0 -22
- package/dist/balanceInjector.d.ts.map +0 -1
package/src/prepareSend.ts
CHANGED
|
@@ -51,8 +51,12 @@ import {
|
|
|
51
51
|
import { queueCCTPTransfer } from "./cctpqueue.js"
|
|
52
52
|
import { getChainInfo, getTestnetChainInfo } from "./chains.js"
|
|
53
53
|
import { attemptSwitchChain } from "./chainSwitch.js"
|
|
54
|
-
import {
|
|
55
|
-
|
|
54
|
+
import {
|
|
55
|
+
getSequenceEnv,
|
|
56
|
+
getSlippageTolerance,
|
|
57
|
+
type SequenceEnv,
|
|
58
|
+
} from "./config.js"
|
|
59
|
+
import { intentEntrypoints } from "./constants.js"
|
|
56
60
|
import {
|
|
57
61
|
decodeGuestModuleEvents,
|
|
58
62
|
decodeTrailsTokenSweeperEvents,
|
|
@@ -65,8 +69,13 @@ import {
|
|
|
65
69
|
getNeedsIntentEntrypointApproval,
|
|
66
70
|
getPermitCalls,
|
|
67
71
|
getPermitSignature,
|
|
72
|
+
getUserNonce,
|
|
68
73
|
signIntent,
|
|
69
74
|
} from "./gasless.js"
|
|
75
|
+
import {
|
|
76
|
+
getIntentEntrypointFeeOptions,
|
|
77
|
+
isIntentEntrypointSupported,
|
|
78
|
+
} from "./intentEntrypoint.js"
|
|
70
79
|
import { useIndexerGatewayClient } from "./indexerClient.js"
|
|
71
80
|
import {
|
|
72
81
|
commitIntentConfig,
|
|
@@ -84,10 +93,6 @@ import {
|
|
|
84
93
|
} from "./paymasterSend.js"
|
|
85
94
|
import { findFirstPreconditionForChainId } from "./preconditions.js"
|
|
86
95
|
import { calcAmountUsdPrice, getTokenPrice } from "./prices.js"
|
|
87
|
-
import {
|
|
88
|
-
TRAILS_CONTRACT_PLACEHOLDER_AMOUNT,
|
|
89
|
-
wrapCalldataWithBalanceInjectorIfNeeded,
|
|
90
|
-
} from "./balanceInjector.js"
|
|
91
96
|
import { getQueryParam } from "./queryParams.js"
|
|
92
97
|
import type { MetaTxnReceipt, RelayerEnv } from "./relayer.js"
|
|
93
98
|
import { useRelayers } from "./relayer.js"
|
|
@@ -115,6 +120,10 @@ import {
|
|
|
115
120
|
useSupportedTokens,
|
|
116
121
|
type SupportedToken,
|
|
117
122
|
} from "./tokens.js"
|
|
123
|
+
import {
|
|
124
|
+
TRAILS_ROUTER_PLACEHOLDER_AMOUNT,
|
|
125
|
+
wrapCalldataWithTrailsRouterIfNeeded,
|
|
126
|
+
} from "./trailsRouter.js"
|
|
118
127
|
import type {
|
|
119
128
|
TransactionState,
|
|
120
129
|
TransactionStateStatus,
|
|
@@ -168,6 +177,7 @@ export type PrepareSendOptions = {
|
|
|
168
177
|
mode?: "pay" | "fund" | "earn" | "swap" | "receive"
|
|
169
178
|
checkoutOnHandlers?: CheckoutOnHandlers
|
|
170
179
|
refundAddress?: string
|
|
180
|
+
selectedFeeToken?: any
|
|
171
181
|
}
|
|
172
182
|
|
|
173
183
|
export type PrepareSendFees = {
|
|
@@ -216,7 +226,6 @@ export type PrepareSendQuote = {
|
|
|
216
226
|
destinationTokenRate: string
|
|
217
227
|
quoteProvider: QuoteProviderInfo | null
|
|
218
228
|
noSufficientBalance: boolean
|
|
219
|
-
minimumNotMet: boolean
|
|
220
229
|
}
|
|
221
230
|
|
|
222
231
|
export type PrepareSendReturn = {
|
|
@@ -224,10 +233,10 @@ export type PrepareSendReturn = {
|
|
|
224
233
|
feeOptions?: any
|
|
225
234
|
send: ({
|
|
226
235
|
onOriginSend,
|
|
227
|
-
|
|
236
|
+
selectedFeeToken,
|
|
228
237
|
}: {
|
|
229
238
|
onOriginSend?: () => void
|
|
230
|
-
|
|
239
|
+
selectedFeeToken?: any
|
|
231
240
|
}) => Promise<SendReturn>
|
|
232
241
|
}
|
|
233
242
|
|
|
@@ -379,6 +388,7 @@ export async function prepareSend(
|
|
|
379
388
|
fundMethod,
|
|
380
389
|
mode,
|
|
381
390
|
checkoutOnHandlers,
|
|
391
|
+
selectedFeeToken,
|
|
382
392
|
} = options
|
|
383
393
|
let { sourceTokenPriceUsd, destinationTokenPriceUsd } = options
|
|
384
394
|
|
|
@@ -416,14 +426,14 @@ export async function prepareSend(
|
|
|
416
426
|
// we need to set custom calldata for the cctp transfer in order to have destination intent adddress execution needed for metatxn tracking
|
|
417
427
|
effectiveDestinationCalldata = getERC20TransferData({
|
|
418
428
|
recipient,
|
|
419
|
-
amount: BigInt(
|
|
429
|
+
amount: BigInt(TRAILS_ROUTER_PLACEHOLDER_AMOUNT),
|
|
420
430
|
})
|
|
421
431
|
effectiveDestinationAddress = destinationTokenAddress
|
|
422
432
|
hasCustomCalldata = true
|
|
423
433
|
}
|
|
424
434
|
|
|
425
435
|
if (hasCustomCalldata && tradeType === TradeType.EXACT_INPUT) {
|
|
426
|
-
const wrapResult =
|
|
436
|
+
const wrapResult = wrapCalldataWithTrailsRouterIfNeeded({
|
|
427
437
|
token: destinationTokenAddress,
|
|
428
438
|
target: effectiveDestinationAddress,
|
|
429
439
|
calldata: effectiveDestinationCalldata as `0x${string}`,
|
|
@@ -435,7 +445,7 @@ export async function prepareSend(
|
|
|
435
445
|
})
|
|
436
446
|
|
|
437
447
|
if (wrapResult) {
|
|
438
|
-
effectiveDestinationAddress = wrapResult.
|
|
448
|
+
effectiveDestinationAddress = wrapResult.trailsRouterAddress
|
|
439
449
|
effectiveDestinationCalldata = wrapResult.encodedCalldata
|
|
440
450
|
}
|
|
441
451
|
}
|
|
@@ -686,6 +696,7 @@ export async function prepareSend(
|
|
|
686
696
|
fundMethod,
|
|
687
697
|
mode,
|
|
688
698
|
checkoutOnHandlers,
|
|
699
|
+
selectedFeeToken,
|
|
689
700
|
})
|
|
690
701
|
}
|
|
691
702
|
|
|
@@ -725,6 +736,7 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
725
736
|
fundMethod,
|
|
726
737
|
mode,
|
|
727
738
|
checkoutOnHandlers,
|
|
739
|
+
selectedFeeToken,
|
|
728
740
|
}: {
|
|
729
741
|
mainSignerAddress: string
|
|
730
742
|
originChainId: number
|
|
@@ -762,6 +774,7 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
762
774
|
fundMethod?: string
|
|
763
775
|
mode?: "pay" | "fund" | "earn" | "swap" | "receive"
|
|
764
776
|
checkoutOnHandlers?: CheckoutOnHandlers
|
|
777
|
+
selectedFeeToken?: any
|
|
765
778
|
}): Promise<PrepareSendReturn> {
|
|
766
779
|
const testnet = isTestnetDebugMode()
|
|
767
780
|
const useCctp = getUseCctp(
|
|
@@ -809,10 +822,22 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
809
822
|
quote,
|
|
810
823
|
send: async ({
|
|
811
824
|
onOriginSend,
|
|
825
|
+
selectedFeeToken: runtimeSelectedFeeToken,
|
|
812
826
|
}: {
|
|
813
827
|
onOriginSend?: () => void
|
|
814
|
-
|
|
828
|
+
selectedFeeToken?: any
|
|
815
829
|
}): Promise<SendReturn> => {
|
|
830
|
+
// Use runtime selectedFeeToken if provided, otherwise fall back to the one from prepareSend
|
|
831
|
+
const effectiveSelectedFeeToken =
|
|
832
|
+
runtimeSelectedFeeToken ?? selectedFeeToken
|
|
833
|
+
logger.console.log(
|
|
834
|
+
"[trails-sdk] [FEE-SELECT] [GASLESS-FLOW] send() called (LEGACY PATH):",
|
|
835
|
+
{
|
|
836
|
+
runtimeSelectedFeeToken,
|
|
837
|
+
prepareTimeSelectedFeeToken: selectedFeeToken,
|
|
838
|
+
effectiveSelectedFeeToken,
|
|
839
|
+
},
|
|
840
|
+
)
|
|
816
841
|
const originChain = testnet ? getTestnetChainInfo(chain)! : chain
|
|
817
842
|
const destinationChain = testnet
|
|
818
843
|
? getTestnetChainInfo(destinationChainId)!
|
|
@@ -1085,7 +1110,6 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1085
1110
|
)
|
|
1086
1111
|
|
|
1087
1112
|
let noSufficientBalance = false
|
|
1088
|
-
let minimumNotMet = false
|
|
1089
1113
|
|
|
1090
1114
|
if (!(fundMethod === "qr-code" || fundMethod === "exchange")) {
|
|
1091
1115
|
const { hasEnoughBalance } = await checkAccountBalance({
|
|
@@ -1100,10 +1124,6 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1100
1124
|
}
|
|
1101
1125
|
}
|
|
1102
1126
|
|
|
1103
|
-
if (Number(depositAmountUsd) < Number(MINIMUM_USD_AMOUNT_FOR_SWAP)) {
|
|
1104
|
-
minimumNotMet = true
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
1127
|
const quote = await getNormalizedQuoteObject({
|
|
1108
1128
|
originDepositAddress: originIntentAddress,
|
|
1109
1129
|
destinationDepositAddress: intent.payloads.destinationIntentAddress,
|
|
@@ -1131,7 +1151,6 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1131
1151
|
originNativeTokenPriceUsd,
|
|
1132
1152
|
quoteProvider: intent.payloads?.quote?.quoteProvider,
|
|
1133
1153
|
noSufficientBalance,
|
|
1134
|
-
minimumNotMet,
|
|
1135
1154
|
})
|
|
1136
1155
|
|
|
1137
1156
|
// Call onCheckoutQuote callback if provided
|
|
@@ -1139,255 +1158,294 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1139
1158
|
checkoutOnHandlers.triggerCheckoutQuote(quote)
|
|
1140
1159
|
}
|
|
1141
1160
|
|
|
1142
|
-
//
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1161
|
+
// Get intent entrypoint fee options if supported, gasless is enabled, and fundMethod is wallet
|
|
1162
|
+
let intentEntrypointFeeOptions: any = null
|
|
1163
|
+
if (gasless && fundMethod === "wallet") {
|
|
1164
|
+
try {
|
|
1165
|
+
logger.console.log(
|
|
1166
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] Gasless enabled and fundMethod is wallet, checking intent entrypoint support for chain:",
|
|
1167
|
+
originChainId,
|
|
1168
|
+
)
|
|
1169
|
+
if (isIntentEntrypointSupported(originChainId)) {
|
|
1170
|
+
logger.console.log(
|
|
1171
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] Chain supported, fetching fee options...",
|
|
1172
|
+
)
|
|
1173
|
+
intentEntrypointFeeOptions = await getIntentEntrypointFeeOptions({
|
|
1174
|
+
trailsClient,
|
|
1175
|
+
userAddress: mainSignerAddress as `0x${string}`,
|
|
1176
|
+
tokenAddress: originTokenAddress as `0x${string}`,
|
|
1177
|
+
amount: depositAmount,
|
|
1178
|
+
intentAddress: originIntentAddress as `0x${string}`,
|
|
1179
|
+
chainId: originChainId,
|
|
1180
|
+
})
|
|
1181
|
+
logger.console.log(
|
|
1182
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] Intent entrypoint fee options:",
|
|
1183
|
+
intentEntrypointFeeOptions,
|
|
1184
|
+
)
|
|
1185
|
+
} else {
|
|
1186
|
+
logger.console.log(
|
|
1187
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] Chain not supported for intent entrypoint:",
|
|
1188
|
+
originChainId,
|
|
1189
|
+
)
|
|
1190
|
+
}
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
logger.console.error(
|
|
1193
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] Error getting intent entrypoint fee options:",
|
|
1194
|
+
error,
|
|
1195
|
+
)
|
|
1196
|
+
}
|
|
1197
|
+
} else {
|
|
1198
|
+
logger.console.log(
|
|
1199
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] Gasless disabled or fundMethod is not wallet, skipping intent entrypoint fee options fetch",
|
|
1200
|
+
)
|
|
1201
|
+
}
|
|
1169
1202
|
|
|
1170
1203
|
return {
|
|
1171
1204
|
quote,
|
|
1205
|
+
feeOptions: intentEntrypointFeeOptions,
|
|
1172
1206
|
send: async ({
|
|
1173
1207
|
onOriginSend,
|
|
1208
|
+
selectedFeeToken: runtimeSelectedFeeToken,
|
|
1174
1209
|
}: {
|
|
1175
1210
|
onOriginSend?: () => void
|
|
1176
|
-
|
|
1211
|
+
selectedFeeToken?: any
|
|
1177
1212
|
}): Promise<SendReturn> => {
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1213
|
+
try {
|
|
1214
|
+
// Use runtime selectedFeeToken if provided, otherwise fall back to the one from prepareSend
|
|
1215
|
+
const effectiveSelectedFeeToken =
|
|
1216
|
+
runtimeSelectedFeeToken ?? selectedFeeToken
|
|
1217
|
+
logger.console.log(
|
|
1218
|
+
"[trails-sdk] [FEE-SELECT] [GASLESS-FLOW] send() called with:",
|
|
1219
|
+
{
|
|
1220
|
+
runtimeSelectedFeeToken,
|
|
1221
|
+
prepareTimeSelectedFeeToken: selectedFeeToken,
|
|
1222
|
+
effectiveSelectedFeeToken,
|
|
1223
|
+
},
|
|
1224
|
+
)
|
|
1225
|
+
await commitIntentConfig(
|
|
1226
|
+
trailsClient,
|
|
1227
|
+
mainSignerAddress,
|
|
1228
|
+
intent.payloads.calls,
|
|
1229
|
+
intent.payloads.preconditions,
|
|
1230
|
+
{
|
|
1231
|
+
originTokenSymbol,
|
|
1232
|
+
destinationTokenSymbol,
|
|
1233
|
+
},
|
|
1234
|
+
intentArgs as IntentRequestParams,
|
|
1235
|
+
)
|
|
1189
1236
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1237
|
+
if (!(fundMethod === "qr-code" || fundMethod === "exchange")) {
|
|
1238
|
+
const { hasEnoughBalance, balanceError } = await checkAccountBalance({
|
|
1239
|
+
account,
|
|
1240
|
+
tokenAddress: originTokenAddress,
|
|
1241
|
+
depositAmount,
|
|
1242
|
+
publicClient,
|
|
1243
|
+
})
|
|
1197
1244
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1245
|
+
if (!hasEnoughBalance) {
|
|
1246
|
+
throw balanceError
|
|
1247
|
+
}
|
|
1200
1248
|
}
|
|
1201
|
-
}
|
|
1202
1249
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1250
|
+
logger.console.log("[trails-sdk] sending origin transaction")
|
|
1251
|
+
const usingLIfi = false
|
|
1252
|
+
let needsNativeFee = false
|
|
1206
1253
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1254
|
+
if (usingLIfi) {
|
|
1255
|
+
needsNativeFee = getNeedsLifiNativeFee({
|
|
1256
|
+
originTokenAddress,
|
|
1257
|
+
destinationTokenAmount: swapAmount,
|
|
1258
|
+
destinationTokenDecimals,
|
|
1259
|
+
sourceTokenDecimals,
|
|
1260
|
+
sourceTokenPriceUsd: sourceTokenPriceUsd ?? null,
|
|
1261
|
+
destinationTokenPriceUsd: destinationTokenPriceUsd ?? null,
|
|
1262
|
+
depositAmount,
|
|
1263
|
+
})
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
logger.console.log("[trails-sdk] needsNativeFee", needsNativeFee)
|
|
1267
|
+
logger.console.log(
|
|
1268
|
+
"[trails-sdk] sourceTokenPriceUsd",
|
|
1269
|
+
sourceTokenPriceUsd,
|
|
1270
|
+
)
|
|
1271
|
+
logger.console.log(
|
|
1272
|
+
"[trails-sdk] destinationTokenPriceUsd",
|
|
1273
|
+
destinationTokenPriceUsd,
|
|
1274
|
+
)
|
|
1275
|
+
logger.console.log(
|
|
1276
|
+
"[trails-sdk] sourceTokenDecimals",
|
|
1212
1277
|
sourceTokenDecimals,
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1278
|
+
)
|
|
1279
|
+
logger.console.log(
|
|
1280
|
+
"[trails-sdk] destinationTokenDecimals",
|
|
1281
|
+
destinationTokenDecimals,
|
|
1282
|
+
)
|
|
1218
1283
|
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
sourceTokenPriceUsd,
|
|
1223
|
-
)
|
|
1224
|
-
logger.console.log(
|
|
1225
|
-
"[trails-sdk] destinationTokenPriceUsd",
|
|
1226
|
-
destinationTokenPriceUsd,
|
|
1227
|
-
)
|
|
1228
|
-
logger.console.log(
|
|
1229
|
-
"[trails-sdk] sourceTokenDecimals",
|
|
1230
|
-
sourceTokenDecimals,
|
|
1231
|
-
)
|
|
1232
|
-
logger.console.log(
|
|
1233
|
-
"[trails-sdk] destinationTokenDecimals",
|
|
1234
|
-
destinationTokenDecimals,
|
|
1235
|
-
)
|
|
1284
|
+
let originUserTxReceipt: TransactionReceipt | null = null
|
|
1285
|
+
let originMetaTxnReceipt: MetaTxnReceipt | null = null
|
|
1286
|
+
let destinationMetaTxnReceipt: MetaTxnReceipt | null = null
|
|
1236
1287
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1288
|
+
const testnet = isTestnetDebugMode()
|
|
1289
|
+
const effectiveOriginChain = testnet
|
|
1290
|
+
? getTestnetChainInfo(chain)!
|
|
1291
|
+
: chain
|
|
1292
|
+
const effectiveOriginTokenAddress = testnet
|
|
1293
|
+
? getTestnetOriginTokenAddress(effectiveOriginChain.id)
|
|
1294
|
+
: originTokenAddress
|
|
1240
1295
|
|
|
1241
|
-
|
|
1242
|
-
const effectiveOriginChain = testnet ? getTestnetChainInfo(chain)! : chain
|
|
1243
|
-
const effectiveOriginTokenAddress = testnet
|
|
1244
|
-
? getTestnetOriginTokenAddress(effectiveOriginChain.id)
|
|
1245
|
-
: originTokenAddress
|
|
1296
|
+
logger.console.log("[trails-sdk] testnet", testnet)
|
|
1246
1297
|
|
|
1247
|
-
|
|
1298
|
+
const destinationPublicClient = createPublicClient({
|
|
1299
|
+
chain: getChainInfo(destinationChainId)!,
|
|
1300
|
+
transport: http(),
|
|
1301
|
+
})
|
|
1248
1302
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1303
|
+
const depositPromise = async () => {
|
|
1304
|
+
logger.console.log(
|
|
1305
|
+
"[trails-sdk] depositPromise called - starting deposit transaction",
|
|
1306
|
+
)
|
|
1307
|
+
logger.console.log("[trails-sdk] fundMethod value:", fundMethod)
|
|
1253
1308
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1309
|
+
// Skip wallet deposit if fund method is qr-code
|
|
1310
|
+
if (fundMethod === "qr-code" || fundMethod === "exchange") {
|
|
1311
|
+
logger.console.log(
|
|
1312
|
+
"[trails-sdk] Skipping wallet deposit for QR code mode - fundMethod is:",
|
|
1313
|
+
fundMethod,
|
|
1314
|
+
)
|
|
1315
|
+
return
|
|
1316
|
+
}
|
|
1259
1317
|
|
|
1260
|
-
// Skip wallet deposit if fund method is qr-code
|
|
1261
|
-
if (fundMethod === "qr-code" || fundMethod === "exchange") {
|
|
1262
1318
|
logger.console.log(
|
|
1263
|
-
"[trails-sdk]
|
|
1264
|
-
|
|
1319
|
+
"[trails-sdk] Calling attemptUserDepositTx with params:",
|
|
1320
|
+
{
|
|
1321
|
+
originTokenAddress: effectiveOriginTokenAddress,
|
|
1322
|
+
gasless,
|
|
1323
|
+
paymasterUrl,
|
|
1324
|
+
chain: effectiveOriginChain.id,
|
|
1325
|
+
account: account.address,
|
|
1326
|
+
firstPreconditionMin,
|
|
1327
|
+
originIntentAddress,
|
|
1328
|
+
fee,
|
|
1329
|
+
dryMode,
|
|
1330
|
+
feeOptions: intentEntrypointFeeOptions,
|
|
1331
|
+
selectedFeeToken: effectiveSelectedFeeToken,
|
|
1332
|
+
selectedFeeTokenType: typeof effectiveSelectedFeeToken,
|
|
1333
|
+
selectedFeeTokenValue: JSON.stringify(effectiveSelectedFeeToken),
|
|
1334
|
+
},
|
|
1265
1335
|
)
|
|
1266
|
-
return
|
|
1267
|
-
}
|
|
1268
1336
|
|
|
1269
|
-
|
|
1270
|
-
"[trails-sdk] Calling attemptUserDepositTx with params:",
|
|
1271
|
-
{
|
|
1337
|
+
originUserTxReceipt = await attemptUserDepositTx({
|
|
1272
1338
|
originTokenAddress: effectiveOriginTokenAddress,
|
|
1273
1339
|
gasless,
|
|
1274
1340
|
paymasterUrl,
|
|
1275
|
-
chain: effectiveOriginChain
|
|
1276
|
-
account
|
|
1341
|
+
chain: effectiveOriginChain,
|
|
1342
|
+
account,
|
|
1343
|
+
originRelayer,
|
|
1277
1344
|
firstPreconditionMin,
|
|
1278
1345
|
originIntentAddress,
|
|
1346
|
+
onOriginSend,
|
|
1347
|
+
publicClient,
|
|
1348
|
+
walletClient,
|
|
1349
|
+
destinationTokenDecimals,
|
|
1350
|
+
sourceTokenDecimals,
|
|
1279
1351
|
fee,
|
|
1280
1352
|
dryMode,
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
publicClient,
|
|
1295
|
-
walletClient,
|
|
1296
|
-
destinationTokenDecimals,
|
|
1297
|
-
sourceTokenDecimals,
|
|
1298
|
-
fee,
|
|
1299
|
-
dryMode,
|
|
1300
|
-
sourceTokenPriceUsd: sourceTokenPriceUsd ?? null,
|
|
1301
|
-
destinationTokenPriceUsd: destinationTokenPriceUsd ?? null,
|
|
1302
|
-
swapAmount,
|
|
1303
|
-
onTransactionStateChange,
|
|
1304
|
-
transactionStates,
|
|
1305
|
-
fundMethod,
|
|
1306
|
-
originTokenSymbol,
|
|
1307
|
-
destinationTokenSymbol,
|
|
1308
|
-
depositAmountUsd,
|
|
1309
|
-
trailsClient,
|
|
1310
|
-
})
|
|
1311
|
-
|
|
1312
|
-
if (!originUserTxReceipt) {
|
|
1313
|
-
throw new Error("Failed to send origin transaction")
|
|
1314
|
-
}
|
|
1353
|
+
sourceTokenPriceUsd: sourceTokenPriceUsd ?? null,
|
|
1354
|
+
destinationTokenPriceUsd: destinationTokenPriceUsd ?? null,
|
|
1355
|
+
swapAmount,
|
|
1356
|
+
onTransactionStateChange,
|
|
1357
|
+
transactionStates,
|
|
1358
|
+
fundMethod,
|
|
1359
|
+
originTokenSymbol,
|
|
1360
|
+
destinationTokenSymbol,
|
|
1361
|
+
depositAmountUsd,
|
|
1362
|
+
feeOptions: intentEntrypointFeeOptions,
|
|
1363
|
+
trailsClient,
|
|
1364
|
+
selectedFeeToken: effectiveSelectedFeeToken,
|
|
1365
|
+
})
|
|
1315
1366
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
transactionStates[0]?.label,
|
|
1320
|
-
)
|
|
1321
|
-
onTransactionStateChange(transactionStates)
|
|
1367
|
+
if (!originUserTxReceipt) {
|
|
1368
|
+
throw new Error("Failed to send origin transaction")
|
|
1369
|
+
}
|
|
1322
1370
|
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
`Your transaction to ${destinationChain?.name || "chain"} is in progress`,
|
|
1328
|
-
"info",
|
|
1371
|
+
transactionStates[0] = getTransactionStateFromReceipt(
|
|
1372
|
+
originUserTxReceipt,
|
|
1373
|
+
originChainId,
|
|
1374
|
+
transactionStates[0]?.label,
|
|
1329
1375
|
)
|
|
1330
|
-
}, 1000)
|
|
1331
|
-
}
|
|
1332
1376
|
|
|
1333
|
-
const checkForDepositTx = async () => {
|
|
1334
|
-
while (true) {
|
|
1335
1377
|
try {
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1378
|
+
transactionStates[0].decodedTrailsTokenSweeperEvents =
|
|
1379
|
+
decodeTrailsTokenSweeperEvents(originUserTxReceipt)
|
|
1380
|
+
transactionStates[0].decodedGuestModuleEvents =
|
|
1381
|
+
decodeGuestModuleEvents(originUserTxReceipt)
|
|
1382
|
+
transactionStates[0].refunded =
|
|
1383
|
+
transactionStates[0].decodedTrailsTokenSweeperEvents.findIndex(
|
|
1384
|
+
(event) =>
|
|
1385
|
+
event.type === "Refund" || event.type === "RefundAndSweep",
|
|
1386
|
+
) !== -1
|
|
1340
1387
|
logger.console.log(
|
|
1341
|
-
"[trails-sdk]
|
|
1342
|
-
|
|
1388
|
+
"[trails-sdk] [GASLESS-FLOW] Gasless deposit events decoded",
|
|
1389
|
+
{
|
|
1390
|
+
chainId: originChainId,
|
|
1391
|
+
callFailed: (
|
|
1392
|
+
transactionStates[0].decodedGuestModuleEvents || []
|
|
1393
|
+
).filter((e: any) => e?.type === "CallFailed").length,
|
|
1394
|
+
sweeperEvents: (
|
|
1395
|
+
transactionStates[0].decodedTrailsTokenSweeperEvents || []
|
|
1396
|
+
).length,
|
|
1397
|
+
refunded: transactionStates[0].refunded,
|
|
1398
|
+
},
|
|
1343
1399
|
)
|
|
1344
|
-
if (response.transactions.length > 0) {
|
|
1345
|
-
const tx = response.transactions[0]
|
|
1346
|
-
if (!tx?.txnHash) {
|
|
1347
|
-
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
1348
|
-
continue
|
|
1349
|
-
}
|
|
1350
|
-
// const isReceive = tx.transfers.some(
|
|
1351
|
-
// (transfer) => transfer.transferType === "RECEIVE",
|
|
1352
|
-
// )
|
|
1353
|
-
// if (!isReceive) {
|
|
1354
|
-
// await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
1355
|
-
// continue
|
|
1356
|
-
// }
|
|
1357
|
-
const originDepositTxReceipt =
|
|
1358
|
-
await publicClient.getTransactionReceipt({
|
|
1359
|
-
hash: tx.txnHash as `0x${string}`,
|
|
1360
|
-
})
|
|
1361
1400
|
|
|
1362
|
-
|
|
1401
|
+
// Check for transaction failure or refund
|
|
1402
|
+
const hasCallFailed = (
|
|
1403
|
+
transactionStates[0].decodedGuestModuleEvents || []
|
|
1404
|
+
).some((e: any) => e?.type === "CallFailed")
|
|
1363
1405
|
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1406
|
+
if (transactionStates[0].refunded || hasCallFailed) {
|
|
1407
|
+
const errorMessage = transactionStates[0].refunded
|
|
1408
|
+
? "Transaction was refunded"
|
|
1409
|
+
: "Transaction call failed"
|
|
1410
|
+
|
|
1411
|
+
logger.console.error(
|
|
1412
|
+
"[trails-sdk] [GASLESS-FLOW] Deposit transaction failed",
|
|
1413
|
+
{
|
|
1414
|
+
refunded: transactionStates[0].refunded,
|
|
1415
|
+
callFailed: hasCallFailed,
|
|
1416
|
+
},
|
|
1368
1417
|
)
|
|
1369
|
-
onTransactionStateChange(transactionStates)
|
|
1370
1418
|
|
|
1371
|
-
if
|
|
1372
|
-
|
|
1419
|
+
// Call onCheckoutError callback if provided
|
|
1420
|
+
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
1421
|
+
checkoutOnHandlers.triggerCheckoutError(errorMessage)
|
|
1373
1422
|
}
|
|
1374
|
-
break
|
|
1375
1423
|
}
|
|
1376
1424
|
} catch (error) {
|
|
1377
|
-
logger.console.error(
|
|
1425
|
+
logger.console.error(
|
|
1426
|
+
"[trails-sdk] Error decoding gasless deposit events",
|
|
1427
|
+
error,
|
|
1428
|
+
)
|
|
1378
1429
|
}
|
|
1379
|
-
|
|
1430
|
+
|
|
1431
|
+
onTransactionStateChange(transactionStates)
|
|
1432
|
+
|
|
1433
|
+
setTimeout(() => {
|
|
1434
|
+
const destinationChain = getChainInfo(destinationChainId)
|
|
1435
|
+
updatePersistentToast(
|
|
1436
|
+
"In Progress",
|
|
1437
|
+
`Your transaction to ${destinationChain?.name || "chain"} is in progress`,
|
|
1438
|
+
"info",
|
|
1439
|
+
)
|
|
1440
|
+
}, 1000)
|
|
1380
1441
|
}
|
|
1381
|
-
}
|
|
1382
1442
|
|
|
1383
|
-
|
|
1384
|
-
async () => {
|
|
1443
|
+
const checkForDepositTx = async () => {
|
|
1385
1444
|
while (true) {
|
|
1386
1445
|
try {
|
|
1387
1446
|
const response = await getAccountTransactionHistory({
|
|
1388
|
-
chainId:
|
|
1389
|
-
accountAddress:
|
|
1390
|
-
.destinationIntentAddress as `0x${string}`,
|
|
1447
|
+
chainId: originChainId,
|
|
1448
|
+
accountAddress: originIntentAddress,
|
|
1391
1449
|
})
|
|
1392
1450
|
logger.console.log(
|
|
1393
1451
|
"[trails-sdk] getAccountTransactionHistory response",
|
|
@@ -1406,19 +1464,24 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1406
1464
|
// await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
1407
1465
|
// continue
|
|
1408
1466
|
// }
|
|
1409
|
-
const
|
|
1410
|
-
await
|
|
1467
|
+
const originDepositTxReceipt =
|
|
1468
|
+
await publicClient.getTransactionReceipt({
|
|
1411
1469
|
hash: tx.txnHash as `0x${string}`,
|
|
1412
1470
|
})
|
|
1413
1471
|
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1472
|
+
originUserTxReceipt = originDepositTxReceipt
|
|
1473
|
+
|
|
1474
|
+
transactionStates[0] = getTransactionStateFromReceipt(
|
|
1475
|
+
originDepositTxReceipt,
|
|
1476
|
+
originChainId,
|
|
1477
|
+
transactionStates[0]?.label,
|
|
1418
1478
|
)
|
|
1419
1479
|
onTransactionStateChange(transactionStates)
|
|
1420
1480
|
|
|
1421
|
-
|
|
1481
|
+
if (onOriginSend) {
|
|
1482
|
+
onOriginSend()
|
|
1483
|
+
}
|
|
1484
|
+
break
|
|
1422
1485
|
}
|
|
1423
1486
|
} catch (error) {
|
|
1424
1487
|
logger.console.error("Error checking for deposit tx", error)
|
|
@@ -1427,206 +1490,77 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1427
1490
|
}
|
|
1428
1491
|
}
|
|
1429
1492
|
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
// First phase: Send meta transactions and queue CCTP
|
|
1440
|
-
const originSendMetaTxnPromise = async () => {
|
|
1441
|
-
logger.console.log("[trails-sdk] Starting originSendMetaTxnPromise", {
|
|
1442
|
-
hasMetaTxn: !!intent.payloads.metaTxns[0],
|
|
1443
|
-
hasPrecondition: !!intent.payloads.preconditions[0],
|
|
1444
|
-
metaTxnId: intent.payloads.metaTxns[0]?.id,
|
|
1445
|
-
chainId: intent.payloads.metaTxns[0]?.chainId,
|
|
1446
|
-
})
|
|
1447
|
-
|
|
1448
|
-
if (intent.payloads.metaTxns[0] && intent.payloads.preconditions[0]) {
|
|
1449
|
-
// Extract fee quote from intent response using metatxnid as key
|
|
1450
|
-
const metaTxnId = intent.payloads.metaTxns[0].id
|
|
1451
|
-
const feeQuote = intent.payloads.feeQuotes?.[metaTxnId]
|
|
1452
|
-
logger.console.log(
|
|
1453
|
-
"[trails-sdk] Extracted fee quote for origin meta txn",
|
|
1454
|
-
{
|
|
1455
|
-
metaTxnId,
|
|
1456
|
-
feeQuote,
|
|
1457
|
-
hasFeeQuote: !!feeQuote,
|
|
1458
|
-
},
|
|
1459
|
-
)
|
|
1460
|
-
|
|
1461
|
-
logger.console.log(
|
|
1462
|
-
"[trails-sdk] Calling sendMetaTxAndWaitForReceipt for origin",
|
|
1463
|
-
{
|
|
1464
|
-
metaTxnId,
|
|
1465
|
-
chainId: intent.payloads.metaTxns[0].chainId,
|
|
1466
|
-
walletAddress: intent.payloads.metaTxns[0].walletAddress,
|
|
1467
|
-
contract: intent.payloads.metaTxns[0].contract,
|
|
1468
|
-
},
|
|
1469
|
-
)
|
|
1470
|
-
|
|
1471
|
-
const { waitForReceipt } = await sendMetaTxAndWaitForReceipt({
|
|
1472
|
-
metaTx: intent.payloads.metaTxns[0] as MetaTxn,
|
|
1473
|
-
relayer: originRelayer,
|
|
1474
|
-
precondition: intent.payloads
|
|
1475
|
-
.preconditions[0] as IntentPrecondition,
|
|
1476
|
-
feeQuote: feeQuote,
|
|
1477
|
-
})
|
|
1478
|
-
|
|
1479
|
-
logger.console.log(
|
|
1480
|
-
"[trails-sdk] Origin meta transaction sent successfully",
|
|
1481
|
-
{
|
|
1482
|
-
metaTxnId,
|
|
1483
|
-
},
|
|
1484
|
-
)
|
|
1485
|
-
|
|
1486
|
-
// Store the waitForReceipt function for later use
|
|
1487
|
-
originMetaTxnReceiptPromise = waitForReceipt
|
|
1488
|
-
} else {
|
|
1489
|
-
logger.console.warn(
|
|
1490
|
-
"[trails-sdk] Skipping origin sendMetaTxn - missing metaTxn or precondition",
|
|
1491
|
-
{
|
|
1492
|
-
hasMetaTxn: !!intent.payloads.metaTxns[0],
|
|
1493
|
-
hasPrecondition: !!intent.payloads.preconditions[0],
|
|
1494
|
-
},
|
|
1495
|
-
)
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
const destinationSendMetaTxnPromise = async () => {
|
|
1500
|
-
logger.console.log(
|
|
1501
|
-
"[trails-sdk] Starting destinationSendMetaTxnPromise",
|
|
1502
|
-
{
|
|
1503
|
-
quoteProvider: intent.payloads.quote.quoteProvider,
|
|
1504
|
-
hasQuoteProviderRequestId:
|
|
1505
|
-
!!intent.payloads.quote.quoteProviderRequestId,
|
|
1506
|
-
hasPrecondition1: !!intent.payloads.preconditions[1],
|
|
1507
|
-
hasMetaTxn1: !!intent.payloads.metaTxns[1],
|
|
1508
|
-
},
|
|
1509
|
-
)
|
|
1510
|
-
|
|
1511
|
-
if (
|
|
1512
|
-
intent.payloads.quote.quoteProvider === "relay" &&
|
|
1513
|
-
intent.payloads.quote.quoteProviderRequestId &&
|
|
1514
|
-
!intent.payloads.preconditions[1] &&
|
|
1515
|
-
!intent.payloads.metaTxns[1]
|
|
1516
|
-
) {
|
|
1517
|
-
logger.console.log(
|
|
1518
|
-
"[trails-sdk] Setting up relay destination promise",
|
|
1519
|
-
{
|
|
1520
|
-
quoteProviderRequestId:
|
|
1521
|
-
intent.payloads.quote.quoteProviderRequestId,
|
|
1522
|
-
},
|
|
1523
|
-
)
|
|
1524
|
-
// For relay, we'll wait for the receipt in the wait phase
|
|
1525
|
-
// Just store the requestId for later use
|
|
1526
|
-
destinationMetaTxnReceiptPromise = async (
|
|
1527
|
-
abortSignal?: AbortSignal,
|
|
1528
|
-
) => {
|
|
1529
|
-
logger.console.log(
|
|
1530
|
-
"[trails-sdk] waitForRelayDestinationTx starting",
|
|
1531
|
-
{
|
|
1532
|
-
quoteProviderRequestId:
|
|
1533
|
-
intent.payloads.quote.quoteProviderRequestId,
|
|
1534
|
-
aborted: abortSignal?.aborted,
|
|
1535
|
-
},
|
|
1536
|
-
)
|
|
1537
|
-
try {
|
|
1538
|
-
// Check if we should abort before starting
|
|
1539
|
-
if (abortSignal?.aborted) {
|
|
1540
|
-
logger.console.log(
|
|
1541
|
-
"[trails-sdk] Aborting relay destination tx due to abort signal",
|
|
1542
|
-
)
|
|
1543
|
-
return null
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
const txHash = await waitForRelayDestinationTx(
|
|
1547
|
-
intent.payloads.quote.quoteProviderRequestId,
|
|
1548
|
-
)
|
|
1549
|
-
logger.console.log(
|
|
1550
|
-
"[trails-sdk] waitForRelayDestinationTx completed",
|
|
1551
|
-
{
|
|
1552
|
-
txHash,
|
|
1553
|
-
quoteProviderRequestId:
|
|
1554
|
-
intent.payloads.quote.quoteProviderRequestId,
|
|
1555
|
-
},
|
|
1556
|
-
)
|
|
1557
|
-
if (txHash) {
|
|
1493
|
+
const _checkForDestinationDepositTx: () => Promise<TransactionReceipt | null> =
|
|
1494
|
+
async () => {
|
|
1495
|
+
while (true) {
|
|
1496
|
+
try {
|
|
1497
|
+
const response = await getAccountTransactionHistory({
|
|
1498
|
+
chainId: destinationChainId,
|
|
1499
|
+
accountAddress: intent.payloads
|
|
1500
|
+
.destinationIntentAddress as `0x${string}`,
|
|
1501
|
+
})
|
|
1558
1502
|
logger.console.log(
|
|
1559
|
-
"[trails-sdk]
|
|
1560
|
-
|
|
1561
|
-
txHash,
|
|
1562
|
-
chainId: destinationChainId,
|
|
1563
|
-
},
|
|
1503
|
+
"[trails-sdk] getAccountTransactionHistory response",
|
|
1504
|
+
response,
|
|
1564
1505
|
)
|
|
1506
|
+
if (response.transactions.length > 0) {
|
|
1507
|
+
const tx = response.transactions[0]
|
|
1508
|
+
if (!tx?.txnHash) {
|
|
1509
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
1510
|
+
continue
|
|
1511
|
+
}
|
|
1512
|
+
// const isReceive = tx.transfers.some(
|
|
1513
|
+
// (transfer) => transfer.transferType === "RECEIVE",
|
|
1514
|
+
// )
|
|
1515
|
+
// if (!isReceive) {
|
|
1516
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
1517
|
+
// continue
|
|
1518
|
+
// }
|
|
1519
|
+
const destinationDepositTxReceipt =
|
|
1520
|
+
await destinationPublicClient.getTransactionReceipt({
|
|
1521
|
+
hash: tx.txnHash as `0x${string}`,
|
|
1522
|
+
})
|
|
1565
1523
|
|
|
1566
|
-
const destinationTxnReceipt =
|
|
1567
|
-
await destinationPublicClient.getTransactionReceipt({
|
|
1568
|
-
hash: txHash as `0x${string}`,
|
|
1569
|
-
})
|
|
1570
|
-
logger.console.log(
|
|
1571
|
-
"[trails-sdk] relay destinationTxnReceipt received",
|
|
1572
|
-
{
|
|
1573
|
-
txHash,
|
|
1574
|
-
blockNumber: destinationTxnReceipt?.blockNumber,
|
|
1575
|
-
status: destinationTxnReceipt?.status,
|
|
1576
|
-
gasUsed: destinationTxnReceipt?.gasUsed,
|
|
1577
|
-
},
|
|
1578
|
-
)
|
|
1579
|
-
if (transactionStates[2]) {
|
|
1580
1524
|
transactionStates[2] = getTransactionStateFromReceipt(
|
|
1581
|
-
|
|
1525
|
+
destinationDepositTxReceipt,
|
|
1582
1526
|
destinationChainId,
|
|
1583
1527
|
transactionStates[2]?.label,
|
|
1584
1528
|
)
|
|
1529
|
+
onTransactionStateChange(transactionStates)
|
|
1530
|
+
|
|
1531
|
+
return destinationDepositTxReceipt
|
|
1585
1532
|
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
} else {
|
|
1589
|
-
logger.console.warn(
|
|
1590
|
-
"[trails-sdk] No txHash returned from waitForRelayDestinationTx",
|
|
1591
|
-
{
|
|
1592
|
-
quoteProviderRequestId:
|
|
1593
|
-
intent.payloads.quote.quoteProviderRequestId,
|
|
1594
|
-
},
|
|
1595
|
-
)
|
|
1596
|
-
}
|
|
1597
|
-
} catch (error: unknown) {
|
|
1598
|
-
logger.console.error(
|
|
1599
|
-
"[trails-sdk] Error waiting for relay destination tx",
|
|
1600
|
-
{
|
|
1601
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1602
|
-
quoteProviderRequestId:
|
|
1603
|
-
intent.payloads.quote.quoteProviderRequestId,
|
|
1604
|
-
},
|
|
1605
|
-
)
|
|
1606
|
-
if (transactionStates?.[2]) {
|
|
1607
|
-
transactionStates[2].state = "failed"
|
|
1608
|
-
onTransactionStateChange(transactionStates)
|
|
1533
|
+
} catch (error) {
|
|
1534
|
+
logger.console.error("Error checking for deposit tx", error)
|
|
1609
1535
|
}
|
|
1610
|
-
|
|
1536
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
1611
1537
|
}
|
|
1612
|
-
return null
|
|
1613
1538
|
}
|
|
1614
|
-
} else {
|
|
1615
|
-
logger.console.log(
|
|
1616
|
-
"[trails-sdk] Setting up destination meta transaction promise (non-relay)",
|
|
1617
|
-
{
|
|
1618
|
-
quoteProvider: intent.payloads.quote.quoteProvider,
|
|
1619
|
-
hasMetaTxn1: !!intent.payloads.metaTxns[1],
|
|
1620
|
-
hasPrecondition1: !!intent.payloads.preconditions[1],
|
|
1621
|
-
},
|
|
1622
|
-
)
|
|
1623
1539
|
|
|
1624
|
-
|
|
1540
|
+
// Variables to store the waitForReceipt functions
|
|
1541
|
+
let originMetaTxnReceiptPromise:
|
|
1542
|
+
| (() => Promise<MetaTxnReceipt | null>)
|
|
1543
|
+
| null = null
|
|
1544
|
+
let destinationMetaTxnReceiptPromise:
|
|
1545
|
+
| ((abortSignal?: AbortSignal) => Promise<MetaTxnReceipt | null>)
|
|
1546
|
+
| ((abortSignal?: AbortSignal) => Promise<TransactionReceipt | null>)
|
|
1547
|
+
| null = null
|
|
1548
|
+
|
|
1549
|
+
// First phase: Send meta transactions and queue CCTP
|
|
1550
|
+
const originSendMetaTxnPromise = async () => {
|
|
1551
|
+
logger.console.log("[trails-sdk] Starting originSendMetaTxnPromise", {
|
|
1552
|
+
hasMetaTxn: !!intent.payloads.metaTxns[0],
|
|
1553
|
+
hasPrecondition: !!intent.payloads.preconditions[0],
|
|
1554
|
+
metaTxnId: intent.payloads.metaTxns[0]?.id,
|
|
1555
|
+
chainId: intent.payloads.metaTxns[0]?.chainId,
|
|
1556
|
+
})
|
|
1557
|
+
|
|
1558
|
+
if (intent.payloads.metaTxns[0] && intent.payloads.preconditions[0]) {
|
|
1625
1559
|
// Extract fee quote from intent response using metatxnid as key
|
|
1626
|
-
const metaTxnId = intent.payloads.metaTxns[
|
|
1560
|
+
const metaTxnId = intent.payloads.metaTxns[0].id
|
|
1627
1561
|
const feeQuote = intent.payloads.feeQuotes?.[metaTxnId]
|
|
1628
1562
|
logger.console.log(
|
|
1629
|
-
"[trails-sdk] Extracted fee quote for
|
|
1563
|
+
"[trails-sdk] Extracted fee quote for origin meta txn",
|
|
1630
1564
|
{
|
|
1631
1565
|
metaTxnId,
|
|
1632
1566
|
feeQuote,
|
|
@@ -1635,324 +1569,597 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1635
1569
|
)
|
|
1636
1570
|
|
|
1637
1571
|
logger.console.log(
|
|
1638
|
-
"[trails-sdk] Calling sendMetaTxAndWaitForReceipt for
|
|
1572
|
+
"[trails-sdk] Calling sendMetaTxAndWaitForReceipt for origin",
|
|
1639
1573
|
{
|
|
1640
1574
|
metaTxnId,
|
|
1641
|
-
chainId: intent.payloads.metaTxns[
|
|
1642
|
-
walletAddress: intent.payloads.metaTxns[
|
|
1643
|
-
contract: intent.payloads.metaTxns[
|
|
1575
|
+
chainId: intent.payloads.metaTxns[0].chainId,
|
|
1576
|
+
walletAddress: intent.payloads.metaTxns[0].walletAddress,
|
|
1577
|
+
contract: intent.payloads.metaTxns[0].contract,
|
|
1644
1578
|
},
|
|
1645
1579
|
)
|
|
1646
1580
|
|
|
1647
1581
|
const { waitForReceipt } = await sendMetaTxAndWaitForReceipt({
|
|
1648
|
-
metaTx: intent.payloads.metaTxns[
|
|
1649
|
-
relayer:
|
|
1582
|
+
metaTx: intent.payloads.metaTxns[0] as MetaTxn,
|
|
1583
|
+
relayer: originRelayer,
|
|
1650
1584
|
precondition: intent.payloads
|
|
1651
|
-
.preconditions[
|
|
1585
|
+
.preconditions[0] as IntentPrecondition,
|
|
1652
1586
|
feeQuote: feeQuote,
|
|
1653
1587
|
})
|
|
1654
1588
|
|
|
1655
1589
|
logger.console.log(
|
|
1656
|
-
"[trails-sdk]
|
|
1590
|
+
"[trails-sdk] Origin meta transaction sent successfully",
|
|
1657
1591
|
{
|
|
1658
1592
|
metaTxnId,
|
|
1659
1593
|
},
|
|
1660
1594
|
)
|
|
1661
1595
|
|
|
1662
1596
|
// Store the waitForReceipt function for later use
|
|
1663
|
-
|
|
1597
|
+
originMetaTxnReceiptPromise = waitForReceipt
|
|
1664
1598
|
} else {
|
|
1665
1599
|
logger.console.warn(
|
|
1666
|
-
"[trails-sdk] Skipping
|
|
1600
|
+
"[trails-sdk] Skipping origin sendMetaTxn - missing metaTxn or precondition",
|
|
1667
1601
|
{
|
|
1668
|
-
hasMetaTxn: !!intent.payloads.metaTxns[
|
|
1669
|
-
hasPrecondition: !!intent.payloads.preconditions[
|
|
1602
|
+
hasMetaTxn: !!intent.payloads.metaTxns[0],
|
|
1603
|
+
hasPrecondition: !!intent.payloads.preconditions[0],
|
|
1670
1604
|
},
|
|
1671
1605
|
)
|
|
1672
1606
|
}
|
|
1673
|
-
// } else if (intent.payloads.destinationIntentAddress) {
|
|
1674
|
-
// destinationMetaTxnReceiptPromise = checkForDestinationDepositTx
|
|
1675
|
-
// }
|
|
1676
1607
|
}
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
let queueCctpPromise: (() => Promise<void>) | null = null
|
|
1680
1608
|
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
})
|
|
1693
|
-
break
|
|
1694
|
-
}
|
|
1695
|
-
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
} else {
|
|
1699
|
-
queueCctpPromise = () => Promise.resolve()
|
|
1700
|
-
}
|
|
1609
|
+
const destinationSendMetaTxnPromise = async () => {
|
|
1610
|
+
logger.console.log(
|
|
1611
|
+
"[trails-sdk] Starting destinationSendMetaTxnPromise",
|
|
1612
|
+
{
|
|
1613
|
+
quoteProvider: intent.payloads.quote.quoteProvider,
|
|
1614
|
+
hasQuoteProviderRequestId:
|
|
1615
|
+
!!intent.payloads.quote.quoteProviderRequestId,
|
|
1616
|
+
hasPrecondition1: !!intent.payloads.preconditions[1],
|
|
1617
|
+
hasMetaTxn1: !!intent.payloads.metaTxns[1],
|
|
1618
|
+
},
|
|
1619
|
+
)
|
|
1701
1620
|
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1621
|
+
if (
|
|
1622
|
+
intent.payloads.quote.quoteProvider === "relay" &&
|
|
1623
|
+
intent.payloads.quote.quoteProviderRequestId &&
|
|
1624
|
+
!intent.payloads.preconditions[1] &&
|
|
1625
|
+
!intent.payloads.metaTxns[1]
|
|
1626
|
+
) {
|
|
1627
|
+
logger.console.log(
|
|
1628
|
+
"[trails-sdk] Setting up relay destination promise",
|
|
1629
|
+
{
|
|
1630
|
+
quoteProviderRequestId:
|
|
1631
|
+
intent.payloads.quote.quoteProviderRequestId,
|
|
1632
|
+
},
|
|
1633
|
+
)
|
|
1634
|
+
// For relay, we'll wait for the receipt in the wait phase
|
|
1635
|
+
// Just store the requestId for later use
|
|
1636
|
+
destinationMetaTxnReceiptPromise = async (
|
|
1637
|
+
abortSignal?: AbortSignal,
|
|
1638
|
+
) => {
|
|
1639
|
+
logger.console.log(
|
|
1640
|
+
"[trails-sdk] waitForRelayDestinationTx starting",
|
|
1641
|
+
{
|
|
1642
|
+
quoteProviderRequestId:
|
|
1643
|
+
intent.payloads.quote.quoteProviderRequestId,
|
|
1644
|
+
aborted: abortSignal?.aborted,
|
|
1645
|
+
},
|
|
1646
|
+
)
|
|
1647
|
+
try {
|
|
1648
|
+
// Check if we should abort before starting
|
|
1649
|
+
if (abortSignal?.aborted) {
|
|
1650
|
+
logger.console.log(
|
|
1651
|
+
"[trails-sdk] Aborting relay destination tx due to abort signal",
|
|
1652
|
+
)
|
|
1653
|
+
return null
|
|
1654
|
+
}
|
|
1705
1655
|
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1656
|
+
const txHash = await waitForRelayDestinationTx(
|
|
1657
|
+
intent.payloads.quote.quoteProviderRequestId,
|
|
1658
|
+
)
|
|
1659
|
+
logger.console.log(
|
|
1660
|
+
"[trails-sdk] waitForRelayDestinationTx completed",
|
|
1661
|
+
{
|
|
1662
|
+
txHash,
|
|
1663
|
+
quoteProviderRequestId:
|
|
1664
|
+
intent.payloads.quote.quoteProviderRequestId,
|
|
1665
|
+
},
|
|
1666
|
+
)
|
|
1667
|
+
if (txHash) {
|
|
1668
|
+
logger.console.log(
|
|
1669
|
+
"[trails-sdk] Fetching transaction receipt for relay destination",
|
|
1670
|
+
{
|
|
1671
|
+
txHash,
|
|
1672
|
+
chainId: destinationChainId,
|
|
1673
|
+
},
|
|
1674
|
+
)
|
|
1710
1675
|
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1676
|
+
const destinationTxnReceipt =
|
|
1677
|
+
await destinationPublicClient.getTransactionReceipt({
|
|
1678
|
+
hash: txHash as `0x${string}`,
|
|
1679
|
+
})
|
|
1680
|
+
logger.console.log(
|
|
1681
|
+
"[trails-sdk] relay destinationTxnReceipt received",
|
|
1682
|
+
{
|
|
1683
|
+
txHash,
|
|
1684
|
+
blockNumber: destinationTxnReceipt?.blockNumber,
|
|
1685
|
+
status: destinationTxnReceipt?.status,
|
|
1686
|
+
gasUsed: destinationTxnReceipt?.gasUsed,
|
|
1687
|
+
},
|
|
1688
|
+
)
|
|
1689
|
+
if (transactionStates[2]) {
|
|
1690
|
+
transactionStates[2] = getTransactionStateFromReceipt(
|
|
1691
|
+
destinationTxnReceipt,
|
|
1692
|
+
destinationChainId,
|
|
1693
|
+
transactionStates[2]?.label,
|
|
1694
|
+
)
|
|
1695
|
+
}
|
|
1696
|
+
onTransactionStateChange(transactionStates)
|
|
1697
|
+
return destinationTxnReceipt
|
|
1698
|
+
} else {
|
|
1699
|
+
logger.console.warn(
|
|
1700
|
+
"[trails-sdk] No txHash returned from waitForRelayDestinationTx",
|
|
1701
|
+
{
|
|
1702
|
+
quoteProviderRequestId:
|
|
1703
|
+
intent.payloads.quote.quoteProviderRequestId,
|
|
1704
|
+
},
|
|
1705
|
+
)
|
|
1706
|
+
}
|
|
1707
|
+
} catch (error: unknown) {
|
|
1708
|
+
logger.console.error(
|
|
1709
|
+
"[trails-sdk] Error waiting for relay destination tx",
|
|
1710
|
+
{
|
|
1711
|
+
error:
|
|
1712
|
+
error instanceof Error ? error.message : String(error),
|
|
1713
|
+
quoteProviderRequestId:
|
|
1714
|
+
intent.payloads.quote.quoteProviderRequestId,
|
|
1715
|
+
},
|
|
1716
|
+
)
|
|
1717
|
+
if (transactionStates?.[2]) {
|
|
1718
|
+
transactionStates[2].state = "failed"
|
|
1719
|
+
onTransactionStateChange(transactionStates)
|
|
1720
|
+
}
|
|
1721
|
+
throw error
|
|
1722
|
+
}
|
|
1723
|
+
return null
|
|
1724
|
+
}
|
|
1725
|
+
} else {
|
|
1726
|
+
logger.console.log(
|
|
1727
|
+
"[trails-sdk] Setting up destination meta transaction promise (non-relay)",
|
|
1728
|
+
{
|
|
1729
|
+
quoteProvider: intent.payloads.quote.quoteProvider,
|
|
1730
|
+
hasMetaTxn1: !!intent.payloads.metaTxns[1],
|
|
1731
|
+
hasPrecondition1: !!intent.payloads.preconditions[1],
|
|
1732
|
+
},
|
|
1733
|
+
)
|
|
1715
1734
|
|
|
1716
|
-
|
|
1735
|
+
if (
|
|
1736
|
+
intent.payloads.metaTxns[1] &&
|
|
1737
|
+
intent.payloads.preconditions[1]
|
|
1738
|
+
) {
|
|
1739
|
+
// Extract fee quote from intent response using metatxnid as key
|
|
1740
|
+
const metaTxnId = intent.payloads.metaTxns[1].id
|
|
1741
|
+
const feeQuote = intent.payloads.feeQuotes?.[metaTxnId]
|
|
1742
|
+
logger.console.log(
|
|
1743
|
+
"[trails-sdk] Extracted fee quote for destination meta txn",
|
|
1744
|
+
{
|
|
1745
|
+
metaTxnId,
|
|
1746
|
+
feeQuote,
|
|
1747
|
+
hasFeeQuote: !!feeQuote,
|
|
1748
|
+
},
|
|
1749
|
+
)
|
|
1717
1750
|
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1751
|
+
logger.console.log(
|
|
1752
|
+
"[trails-sdk] Calling sendMetaTxAndWaitForReceipt for destination",
|
|
1753
|
+
{
|
|
1754
|
+
metaTxnId,
|
|
1755
|
+
chainId: intent.payloads.metaTxns[1].chainId,
|
|
1756
|
+
walletAddress: intent.payloads.metaTxns[1].walletAddress,
|
|
1757
|
+
contract: intent.payloads.metaTxns[1].contract,
|
|
1758
|
+
},
|
|
1759
|
+
)
|
|
1722
1760
|
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1761
|
+
const { waitForReceipt } = await sendMetaTxAndWaitForReceipt({
|
|
1762
|
+
metaTx: intent.payloads.metaTxns[1] as MetaTxn,
|
|
1763
|
+
relayer: destinationRelayer,
|
|
1764
|
+
precondition: intent.payloads
|
|
1765
|
+
.preconditions[1] as IntentPrecondition,
|
|
1766
|
+
feeQuote: feeQuote,
|
|
1767
|
+
})
|
|
1730
1768
|
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1769
|
+
logger.console.log(
|
|
1770
|
+
"[trails-sdk] Destination meta transaction sent successfully",
|
|
1771
|
+
{
|
|
1772
|
+
metaTxnId,
|
|
1773
|
+
},
|
|
1736
1774
|
)
|
|
1737
|
-
onTransactionStateChange(transactionStates)
|
|
1738
1775
|
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1776
|
+
// Store the waitForReceipt function for later use
|
|
1777
|
+
destinationMetaTxnReceiptPromise = waitForReceipt
|
|
1778
|
+
} else {
|
|
1779
|
+
logger.console.warn(
|
|
1780
|
+
"[trails-sdk] Skipping destination sendMetaTxn - missing metaTxn or precondition",
|
|
1781
|
+
{
|
|
1782
|
+
hasMetaTxn: !!intent.payloads.metaTxns[1],
|
|
1783
|
+
hasPrecondition: !!intent.payloads.preconditions[1],
|
|
1784
|
+
},
|
|
1785
|
+
)
|
|
1786
|
+
}
|
|
1787
|
+
// } else if (intent.payloads.destinationIntentAddress) {
|
|
1788
|
+
// destinationMetaTxnReceiptPromise = checkForDestinationDepositTx
|
|
1789
|
+
// }
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
let queueCctpPromise: (() => Promise<void>) | null = null
|
|
1794
|
+
|
|
1795
|
+
const isCctp = intent.payloads.quote.quoteProvider === "cctp"
|
|
1796
|
+
if (isCctp) {
|
|
1797
|
+
queueCctpPromise = async () => {
|
|
1798
|
+
while (true) {
|
|
1799
|
+
const originMetaTxnHash = originMetaTxnReceipt?.txnHash
|
|
1800
|
+
if (originMetaTxnHash) {
|
|
1801
|
+
await queueCCTPTransfer({
|
|
1802
|
+
trailsClient,
|
|
1803
|
+
sourceTxHash: originMetaTxnHash,
|
|
1804
|
+
sourceChainId: originChainId,
|
|
1805
|
+
destinationChainId: destinationChainId,
|
|
1762
1806
|
})
|
|
1763
|
-
|
|
1764
|
-
} catch (error) {
|
|
1765
|
-
logger.console.error("Error decoding origin tx events", error)
|
|
1807
|
+
break
|
|
1766
1808
|
}
|
|
1809
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
1767
1810
|
}
|
|
1768
|
-
} catch (error) {
|
|
1769
|
-
logger.console.error(
|
|
1770
|
-
"[trails-sdk] Error waiting for origin receipt:",
|
|
1771
|
-
error,
|
|
1772
|
-
)
|
|
1773
1811
|
}
|
|
1774
1812
|
} else {
|
|
1775
|
-
|
|
1776
|
-
"[trails-sdk] No origin meta transaction receipt promise to wait for",
|
|
1777
|
-
)
|
|
1813
|
+
queueCctpPromise = () => Promise.resolve()
|
|
1778
1814
|
}
|
|
1779
|
-
}
|
|
1780
1815
|
|
|
1781
|
-
|
|
1816
|
+
checkForDepositTx().catch((error) => {
|
|
1817
|
+
console.error("Error checking for deposit tx", error)
|
|
1818
|
+
})
|
|
1819
|
+
|
|
1820
|
+
// Phase 1: Send meta transactions and queue CCTP
|
|
1782
1821
|
logger.console.log(
|
|
1783
|
-
"[trails-sdk]
|
|
1822
|
+
"[trails-sdk] Starting Phase 1: Sending meta transactions and queuing CCTP",
|
|
1784
1823
|
)
|
|
1785
|
-
if (destinationMetaTxnReceiptPromise) {
|
|
1786
|
-
try {
|
|
1787
|
-
// Create abort controller for cancelling destination polling
|
|
1788
|
-
const abortController = new AbortController()
|
|
1789
|
-
|
|
1790
|
-
// Race between destination receipt and failure polling
|
|
1791
|
-
const destinationReceiptPromise = destinationMetaTxnReceiptPromise
|
|
1792
|
-
? destinationMetaTxnReceiptPromise(abortController.signal)
|
|
1793
|
-
: Promise.resolve(null)
|
|
1794
|
-
|
|
1795
|
-
const failurePollingPromise = new Promise<null>((resolve) => {
|
|
1796
|
-
const pollForFailures = () => {
|
|
1797
|
-
const isPreviousTxCallFailed =
|
|
1798
|
-
transactionStates?.some((tx) => tx.state === "failed") ||
|
|
1799
|
-
transactionStates?.some((tx) =>
|
|
1800
|
-
tx?.decodedGuestModuleEvents?.some(
|
|
1801
|
-
(event) => event.type === "CallFailed",
|
|
1802
|
-
),
|
|
1803
|
-
)
|
|
1804
1824
|
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1825
|
+
await Promise.all([
|
|
1826
|
+
originSendMetaTxnPromise(),
|
|
1827
|
+
destinationSendMetaTxnPromise(),
|
|
1828
|
+
])
|
|
1829
|
+
|
|
1830
|
+
logger.console.log("[trails-sdk] Phase 1 completed successfully")
|
|
1831
|
+
|
|
1832
|
+
// Phase 2: Wait for receipts and execute deposit
|
|
1833
|
+
logger.console.log(
|
|
1834
|
+
"[trails-sdk] Starting Phase 2: Waiting for receipts and executing deposit",
|
|
1835
|
+
)
|
|
1836
|
+
|
|
1837
|
+
const waitForOriginMetaTxnReceiptPromise = async () => {
|
|
1838
|
+
logger.console.log(
|
|
1839
|
+
"[trails-sdk] Waiting for origin meta transaction receipt",
|
|
1840
|
+
)
|
|
1841
|
+
if (originMetaTxnReceiptPromise) {
|
|
1842
|
+
try {
|
|
1843
|
+
originMetaTxnReceipt = await originMetaTxnReceiptPromise()
|
|
1818
1844
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1845
|
+
if (originMetaTxnReceipt && transactionStates[1]) {
|
|
1846
|
+
transactionStates[1] = getTransactionStateFromReceipt(
|
|
1847
|
+
originMetaTxnReceipt,
|
|
1848
|
+
originChainId,
|
|
1849
|
+
transactionStates[1]?.label,
|
|
1850
|
+
)
|
|
1851
|
+
onTransactionStateChange(transactionStates)
|
|
1852
|
+
|
|
1853
|
+
try {
|
|
1854
|
+
const receipt = await publicClient.getTransactionReceipt({
|
|
1855
|
+
hash: originMetaTxnReceipt.txnHash as `0x${string}`,
|
|
1856
|
+
})
|
|
1857
|
+
transactionStates[1].decodedTrailsTokenSweeperEvents =
|
|
1858
|
+
decodeTrailsTokenSweeperEvents(receipt)
|
|
1859
|
+
transactionStates[1].decodedGuestModuleEvents =
|
|
1860
|
+
decodeGuestModuleEvents(receipt)
|
|
1861
|
+
transactionStates[1].refunded =
|
|
1862
|
+
transactionStates[1].decodedTrailsTokenSweeperEvents.findIndex(
|
|
1863
|
+
(event) =>
|
|
1864
|
+
event.type === "Refund" ||
|
|
1865
|
+
event.type === "RefundAndSweep",
|
|
1866
|
+
) !== -1
|
|
1867
|
+
logger.console.log("[trails-sdk] Origin meta-tx events", {
|
|
1868
|
+
chainId: originChainId,
|
|
1869
|
+
callFailed: (
|
|
1870
|
+
transactionStates[1].decodedGuestModuleEvents || []
|
|
1871
|
+
).filter((e: any) => e?.type === "CallFailed").length,
|
|
1872
|
+
sweeperEvents: (
|
|
1873
|
+
transactionStates[1].decodedTrailsTokenSweeperEvents || []
|
|
1874
|
+
).length,
|
|
1875
|
+
refunded: transactionStates[1].refunded,
|
|
1876
|
+
})
|
|
1823
1877
|
|
|
1878
|
+
// Check for transaction failure or refund
|
|
1879
|
+
const hasCallFailed = (
|
|
1880
|
+
transactionStates[1].decodedGuestModuleEvents || []
|
|
1881
|
+
).some((e: any) => e?.type === "CallFailed")
|
|
1882
|
+
|
|
1883
|
+
if (transactionStates[1].refunded || hasCallFailed) {
|
|
1884
|
+
const errorMessage = transactionStates[1].refunded
|
|
1885
|
+
? "Origin transaction was refunded"
|
|
1886
|
+
: "Origin transaction call failed"
|
|
1887
|
+
|
|
1888
|
+
logger.console.error("[trails-sdk] Origin meta-tx failed", {
|
|
1889
|
+
refunded: transactionStates[1].refunded,
|
|
1890
|
+
callFailed: hasCallFailed,
|
|
1891
|
+
})
|
|
1892
|
+
|
|
1893
|
+
// Call onCheckoutError callback if provided
|
|
1894
|
+
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
1895
|
+
checkoutOnHandlers.triggerCheckoutError(errorMessage)
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
onTransactionStateChange(transactionStates)
|
|
1900
|
+
} catch (error) {
|
|
1901
|
+
logger.console.error("Error decoding origin tx events", error)
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
} catch (error) {
|
|
1905
|
+
logger.console.error(
|
|
1906
|
+
"[trails-sdk] Error waiting for origin receipt:",
|
|
1907
|
+
error,
|
|
1908
|
+
)
|
|
1909
|
+
}
|
|
1910
|
+
} else {
|
|
1824
1911
|
logger.console.log(
|
|
1825
|
-
"[trails-sdk]
|
|
1826
|
-
destinationMetaTxnReceipt,
|
|
1912
|
+
"[trails-sdk] No origin meta transaction receipt promise to wait for",
|
|
1827
1913
|
)
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1828
1916
|
|
|
1829
|
-
|
|
1830
|
-
|
|
1917
|
+
const waitForDestinationMetaTxnReceiptPromise = async () => {
|
|
1918
|
+
logger.console.log(
|
|
1919
|
+
"[trails-sdk] Waiting for destination meta transaction receipt",
|
|
1920
|
+
)
|
|
1921
|
+
if (destinationMetaTxnReceiptPromise) {
|
|
1922
|
+
try {
|
|
1923
|
+
// Create abort controller for cancelling destination polling
|
|
1924
|
+
const abortController = new AbortController()
|
|
1925
|
+
|
|
1926
|
+
// Race between destination receipt and failure polling
|
|
1927
|
+
const destinationReceiptPromise = destinationMetaTxnReceiptPromise
|
|
1928
|
+
? destinationMetaTxnReceiptPromise(abortController.signal)
|
|
1929
|
+
: Promise.resolve(null)
|
|
1930
|
+
|
|
1931
|
+
const failurePollingPromise = new Promise<null>((resolve) => {
|
|
1932
|
+
const pollForFailures = () => {
|
|
1933
|
+
const isPreviousTxCallFailed =
|
|
1934
|
+
transactionStates?.some((tx) => tx.state === "failed") ||
|
|
1935
|
+
transactionStates?.some((tx) =>
|
|
1936
|
+
tx?.decodedGuestModuleEvents?.some(
|
|
1937
|
+
(event) => event.type === "CallFailed",
|
|
1938
|
+
),
|
|
1939
|
+
)
|
|
1940
|
+
|
|
1941
|
+
if (isPreviousTxCallFailed) {
|
|
1942
|
+
logger.console.log(
|
|
1943
|
+
"[trails-sdk] Aborting destination meta transaction due to previous transaction failure",
|
|
1944
|
+
)
|
|
1945
|
+
abortController.abort()
|
|
1946
|
+
resolve(null)
|
|
1947
|
+
} else {
|
|
1948
|
+
// Continue polling every 1 second
|
|
1949
|
+
setTimeout(pollForFailures, 1000)
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
pollForFailures()
|
|
1953
|
+
})
|
|
1954
|
+
|
|
1955
|
+
destinationMetaTxnReceipt = (await Promise.race([
|
|
1956
|
+
destinationReceiptPromise,
|
|
1957
|
+
failurePollingPromise,
|
|
1958
|
+
])) as MetaTxnReceipt
|
|
1959
|
+
|
|
1960
|
+
logger.console.log(
|
|
1961
|
+
"[trails-sdk] destinationMetaTxnReceipt",
|
|
1831
1962
|
destinationMetaTxnReceipt,
|
|
1832
|
-
destinationChainId,
|
|
1833
|
-
transactionStates[2]?.label,
|
|
1834
1963
|
)
|
|
1835
|
-
onTransactionStateChange(transactionStates)
|
|
1836
1964
|
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
decodeTrailsTokenSweeperEvents(receipt)
|
|
1844
|
-
transactionStates[2].decodedGuestModuleEvents =
|
|
1845
|
-
decodeGuestModuleEvents(receipt)
|
|
1846
|
-
transactionStates[2].refunded =
|
|
1847
|
-
transactionStates[2].decodedTrailsTokenSweeperEvents.findIndex(
|
|
1848
|
-
(event) =>
|
|
1849
|
-
event.type === "Refund" ||
|
|
1850
|
-
event.type === "RefundAndSweep",
|
|
1851
|
-
) !== -1 ||
|
|
1852
|
-
(transactionStates[2].decodedTrailsTokenSweeperEvents.findIndex(
|
|
1853
|
-
(event) => event.type === "Sweep",
|
|
1854
|
-
) !== -1 &&
|
|
1855
|
-
transactionStates[2].decodedGuestModuleEvents.findIndex(
|
|
1856
|
-
(event) => event.type === "CallFailed",
|
|
1857
|
-
) !== -1)
|
|
1858
|
-
logger.console.log("[trails-sdk] Destination meta-tx events", {
|
|
1859
|
-
chainId: destinationChainId,
|
|
1860
|
-
callFailed: (
|
|
1861
|
-
transactionStates[2].decodedGuestModuleEvents || []
|
|
1862
|
-
).filter((e: any) => e?.type === "CallFailed").length,
|
|
1863
|
-
sweeperEvents: (
|
|
1864
|
-
transactionStates[2].decodedTrailsTokenSweeperEvents || []
|
|
1865
|
-
).length,
|
|
1866
|
-
refunded: transactionStates[2].refunded,
|
|
1867
|
-
})
|
|
1965
|
+
if (destinationMetaTxnReceipt && transactionStates[2]) {
|
|
1966
|
+
transactionStates[2] = getTransactionStateFromReceipt(
|
|
1967
|
+
destinationMetaTxnReceipt,
|
|
1968
|
+
destinationChainId,
|
|
1969
|
+
transactionStates[2]?.label,
|
|
1970
|
+
)
|
|
1868
1971
|
onTransactionStateChange(transactionStates)
|
|
1869
|
-
|
|
1870
|
-
|
|
1972
|
+
|
|
1973
|
+
try {
|
|
1974
|
+
const receipt =
|
|
1975
|
+
await destinationPublicClient.getTransactionReceipt({
|
|
1976
|
+
hash: destinationMetaTxnReceipt.txnHash as `0x${string}`,
|
|
1977
|
+
})
|
|
1978
|
+
transactionStates[2].decodedTrailsTokenSweeperEvents =
|
|
1979
|
+
decodeTrailsTokenSweeperEvents(receipt)
|
|
1980
|
+
transactionStates[2].decodedGuestModuleEvents =
|
|
1981
|
+
decodeGuestModuleEvents(receipt)
|
|
1982
|
+
transactionStates[2].refunded =
|
|
1983
|
+
transactionStates[2].decodedTrailsTokenSweeperEvents.findIndex(
|
|
1984
|
+
(event) =>
|
|
1985
|
+
event.type === "Refund" ||
|
|
1986
|
+
event.type === "RefundAndSweep",
|
|
1987
|
+
) !== -1 ||
|
|
1988
|
+
(transactionStates[2].decodedTrailsTokenSweeperEvents.findIndex(
|
|
1989
|
+
(event) => event.type === "Sweep",
|
|
1990
|
+
) !== -1 &&
|
|
1991
|
+
transactionStates[2].decodedGuestModuleEvents.findIndex(
|
|
1992
|
+
(event) => event.type === "CallFailed",
|
|
1993
|
+
) !== -1)
|
|
1994
|
+
logger.console.log(
|
|
1995
|
+
"[trails-sdk] Destination meta-tx events",
|
|
1996
|
+
{
|
|
1997
|
+
chainId: destinationChainId,
|
|
1998
|
+
callFailed: (
|
|
1999
|
+
transactionStates[2].decodedGuestModuleEvents || []
|
|
2000
|
+
).filter((e: any) => e?.type === "CallFailed").length,
|
|
2001
|
+
sweeperEvents: (
|
|
2002
|
+
transactionStates[2].decodedTrailsTokenSweeperEvents ||
|
|
2003
|
+
[]
|
|
2004
|
+
).length,
|
|
2005
|
+
refunded: transactionStates[2].refunded,
|
|
2006
|
+
},
|
|
2007
|
+
)
|
|
2008
|
+
|
|
2009
|
+
// Check for transaction failure or refund
|
|
2010
|
+
const hasCallFailed = (
|
|
2011
|
+
transactionStates[2].decodedGuestModuleEvents || []
|
|
2012
|
+
).some((e: any) => e?.type === "CallFailed")
|
|
2013
|
+
|
|
2014
|
+
if (transactionStates[2].refunded || hasCallFailed) {
|
|
2015
|
+
const errorMessage = transactionStates[2].refunded
|
|
2016
|
+
? "Destination transaction was refunded"
|
|
2017
|
+
: "Destination transaction call failed"
|
|
2018
|
+
|
|
2019
|
+
logger.console.error(
|
|
2020
|
+
"[trails-sdk] Destination meta-tx failed",
|
|
2021
|
+
{
|
|
2022
|
+
refunded: transactionStates[2].refunded,
|
|
2023
|
+
callFailed: hasCallFailed,
|
|
2024
|
+
},
|
|
2025
|
+
)
|
|
2026
|
+
|
|
2027
|
+
// Call onCheckoutError callback if provided
|
|
2028
|
+
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
2029
|
+
checkoutOnHandlers.triggerCheckoutError(errorMessage)
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
onTransactionStateChange(transactionStates)
|
|
2034
|
+
} catch (error) {
|
|
2035
|
+
console.error("Error decoding destination tx events", error)
|
|
2036
|
+
}
|
|
1871
2037
|
}
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
error,
|
|
1877
|
-
)
|
|
1878
|
-
// For relay transactions, this might be expected if still waiting
|
|
1879
|
-
if (intent.payloads.quote.quoteProvider === "relay") {
|
|
1880
|
-
logger.console.log(
|
|
1881
|
-
"[trails-sdk] Relay transaction still waiting, this is normal",
|
|
2038
|
+
} catch (error) {
|
|
2039
|
+
console.error(
|
|
2040
|
+
"[trails-sdk] Error waiting for destination receipt:",
|
|
2041
|
+
error,
|
|
1882
2042
|
)
|
|
2043
|
+
// For relay transactions, this might be expected if still waiting
|
|
2044
|
+
if (intent.payloads.quote.quoteProvider === "relay") {
|
|
2045
|
+
logger.console.log(
|
|
2046
|
+
"[trails-sdk] Relay transaction still waiting, this is normal",
|
|
2047
|
+
)
|
|
2048
|
+
}
|
|
1883
2049
|
}
|
|
2050
|
+
} else {
|
|
2051
|
+
logger.console.log(
|
|
2052
|
+
"[trails-sdk] No destination meta transaction receipt promise to wait for",
|
|
2053
|
+
)
|
|
1884
2054
|
}
|
|
1885
|
-
} else {
|
|
1886
|
-
logger.console.log(
|
|
1887
|
-
"[trails-sdk] No destination meta transaction receipt promise to wait for",
|
|
1888
|
-
)
|
|
1889
2055
|
}
|
|
1890
|
-
}
|
|
1891
2056
|
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
2057
|
+
logger.console.log(
|
|
2058
|
+
"[trails-sdk] Executing Phase 2 Promise.all with deposit",
|
|
2059
|
+
)
|
|
2060
|
+
logger.console.log(
|
|
2061
|
+
"[trails-sdk] About to call depositPromise - fundMethod is:",
|
|
2062
|
+
fundMethod,
|
|
2063
|
+
)
|
|
1899
2064
|
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
2065
|
+
await Promise.all([
|
|
2066
|
+
depositPromise(),
|
|
2067
|
+
waitForOriginMetaTxnReceiptPromise(),
|
|
2068
|
+
waitForDestinationMetaTxnReceiptPromise(),
|
|
2069
|
+
queueCctpPromise(),
|
|
2070
|
+
])
|
|
2071
|
+
logger.console.log("[trails-sdk] Phase 2 completed successfully")
|
|
1907
2072
|
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
2073
|
+
// Track payment completion for different chain and different token
|
|
2074
|
+
if (originUserTxReceipt && destinationMetaTxnReceipt) {
|
|
2075
|
+
trackPaymentCompleted({
|
|
2076
|
+
userAddress: account.address,
|
|
2077
|
+
originIntentAddress,
|
|
2078
|
+
originTxHash: (originUserTxReceipt as TransactionReceipt)
|
|
2079
|
+
.transactionHash,
|
|
2080
|
+
destinationTxHash: (destinationMetaTxnReceipt as MetaTxnReceipt)
|
|
2081
|
+
?.txnHash,
|
|
2082
|
+
originChainId,
|
|
2083
|
+
destinationChainId,
|
|
2084
|
+
mode,
|
|
2085
|
+
fundMethod,
|
|
2086
|
+
originTokenSymbol,
|
|
2087
|
+
originTokenAddress,
|
|
2088
|
+
destinationTokenAddress,
|
|
2089
|
+
destinationTokenSymbol,
|
|
2090
|
+
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
2091
|
+
destinationTokenAmountUsd:
|
|
2092
|
+
effectiveDestinationTokenAmountUsd?.toString(),
|
|
2093
|
+
})
|
|
1929
2094
|
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
2095
|
+
// Call onCheckoutComplete callback if provided
|
|
2096
|
+
if (checkoutOnHandlers?.triggerCheckoutComplete) {
|
|
2097
|
+
checkoutOnHandlers.triggerCheckoutComplete()
|
|
2098
|
+
}
|
|
2099
|
+
} else {
|
|
2100
|
+
if (
|
|
2101
|
+
transactionStates[1] &&
|
|
2102
|
+
transactionStates[1]?.transactionHash === "" &&
|
|
2103
|
+
transactionStates[1]?.state === "pending"
|
|
2104
|
+
) {
|
|
2105
|
+
transactionStates[1].state = "aborted"
|
|
2106
|
+
onTransactionStateChange(transactionStates)
|
|
2107
|
+
}
|
|
2108
|
+
if (
|
|
2109
|
+
transactionStates[2] &&
|
|
2110
|
+
transactionStates[2]?.transactionHash === "" &&
|
|
2111
|
+
transactionStates[2]?.state === "pending"
|
|
2112
|
+
) {
|
|
2113
|
+
transactionStates[2].state = "aborted"
|
|
2114
|
+
onTransactionStateChange(transactionStates)
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
// Track payment error if transactions didn't complete successfully
|
|
2118
|
+
trackPaymentError({
|
|
2119
|
+
error:
|
|
2120
|
+
"Payment transactions possibly did not complete successfully. Was not able to receive both origin and destination meta transaction receipts. May be an API error.",
|
|
2121
|
+
userAddress: account.address,
|
|
2122
|
+
originIntentAddress,
|
|
2123
|
+
mode,
|
|
2124
|
+
fundMethod,
|
|
2125
|
+
originChainId,
|
|
2126
|
+
destinationChainId,
|
|
2127
|
+
originTokenSymbol,
|
|
2128
|
+
originTokenAddress,
|
|
2129
|
+
destinationTokenAddress,
|
|
2130
|
+
destinationTokenSymbol,
|
|
2131
|
+
})
|
|
2132
|
+
|
|
2133
|
+
// Call onCheckoutError callback if provided
|
|
2134
|
+
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
2135
|
+
checkoutOnHandlers.triggerCheckoutError(
|
|
2136
|
+
"Payment transactions did not complete successfully",
|
|
2137
|
+
)
|
|
2138
|
+
}
|
|
1942
2139
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
2140
|
+
|
|
2141
|
+
return {
|
|
2142
|
+
originUserTxReceipt,
|
|
2143
|
+
originMetaTxnReceipt,
|
|
2144
|
+
destinationMetaTxnReceipt,
|
|
2145
|
+
totalCompletionSeconds: await getTxTimeDiff(
|
|
2146
|
+
transactionStates[0],
|
|
2147
|
+
transactionStates[2],
|
|
2148
|
+
),
|
|
1950
2149
|
}
|
|
2150
|
+
} catch (error) {
|
|
2151
|
+
const errorMessage =
|
|
2152
|
+
error instanceof Error
|
|
2153
|
+
? error.message
|
|
2154
|
+
: "Unknown error occurred during transaction"
|
|
2155
|
+
logger.console.error(
|
|
2156
|
+
"[trails-sdk] Error in sendHandlerForDifferentChainDifferentToken:",
|
|
2157
|
+
error,
|
|
2158
|
+
)
|
|
1951
2159
|
|
|
1952
|
-
// Track payment error
|
|
2160
|
+
// Track payment error
|
|
1953
2161
|
trackPaymentError({
|
|
1954
|
-
error:
|
|
1955
|
-
"Payment transactions possibly did not complete successfully. Was not able to receive both origin and destination meta transaction receipts. May be an API error.",
|
|
2162
|
+
error: errorMessage,
|
|
1956
2163
|
userAddress: account.address,
|
|
1957
2164
|
originIntentAddress,
|
|
1958
2165
|
mode,
|
|
@@ -1967,20 +2174,11 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1967
2174
|
|
|
1968
2175
|
// Call onCheckoutError callback if provided
|
|
1969
2176
|
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
1970
|
-
checkoutOnHandlers.triggerCheckoutError(
|
|
1971
|
-
"Payment transactions did not complete successfully",
|
|
1972
|
-
)
|
|
2177
|
+
checkoutOnHandlers.triggerCheckoutError(errorMessage)
|
|
1973
2178
|
}
|
|
1974
|
-
}
|
|
1975
2179
|
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
originMetaTxnReceipt,
|
|
1979
|
-
destinationMetaTxnReceipt,
|
|
1980
|
-
totalCompletionSeconds: await getTxTimeDiff(
|
|
1981
|
-
transactionStates[0],
|
|
1982
|
-
transactionStates[2],
|
|
1983
|
-
),
|
|
2180
|
+
// Re-throw the error so caller can handle if needed
|
|
2181
|
+
throw error
|
|
1984
2182
|
}
|
|
1985
2183
|
},
|
|
1986
2184
|
}
|
|
@@ -2040,7 +2238,6 @@ async function sendHandlerForSameChainSameToken({
|
|
|
2040
2238
|
})
|
|
2041
2239
|
|
|
2042
2240
|
let noSufficientBalance = false
|
|
2043
|
-
const minimumNotMet = false
|
|
2044
2241
|
|
|
2045
2242
|
const { hasEnoughBalance } = await checkAccountBalance({
|
|
2046
2243
|
account,
|
|
@@ -2071,7 +2268,6 @@ async function sendHandlerForSameChainSameToken({
|
|
|
2071
2268
|
slippageTolerance,
|
|
2072
2269
|
quoteProvider: "",
|
|
2073
2270
|
noSufficientBalance,
|
|
2074
|
-
minimumNotMet,
|
|
2075
2271
|
})
|
|
2076
2272
|
|
|
2077
2273
|
// Call onCheckoutQuote callback if provided
|
|
@@ -2085,198 +2281,240 @@ async function sendHandlerForSameChainSameToken({
|
|
|
2085
2281
|
onOriginSend,
|
|
2086
2282
|
}: {
|
|
2087
2283
|
onOriginSend?: () => void
|
|
2088
|
-
feeTokenAddress?: string | null
|
|
2089
2284
|
}): Promise<SendReturn> => {
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2285
|
+
try {
|
|
2286
|
+
const { hasEnoughBalance, balanceError } = await checkAccountBalance({
|
|
2287
|
+
account,
|
|
2288
|
+
tokenAddress: effectiveOriginTokenAddress,
|
|
2289
|
+
depositAmount: swapAmount,
|
|
2290
|
+
publicClient: effectivePublicClient,
|
|
2291
|
+
})
|
|
2096
2292
|
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2293
|
+
if (!hasEnoughBalance) {
|
|
2294
|
+
throw balanceError
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
const depositAmountFormatted = Number(
|
|
2298
|
+
formatUnits(BigInt(swapAmount), originTokenDecimals),
|
|
2299
|
+
)
|
|
2300
|
+
const depositAmountUsd = calcAmountUsdPrice({
|
|
2301
|
+
amount: depositAmountFormatted,
|
|
2302
|
+
usdPrice: sourceTokenPriceUsd,
|
|
2303
|
+
})
|
|
2304
|
+
|
|
2305
|
+
const hasCustomCalldata = getIsCustomCalldata(destinationCalldata)
|
|
2306
|
+
const originCallParams = {
|
|
2307
|
+
to: hasCustomCalldata
|
|
2308
|
+
? recipient
|
|
2309
|
+
: effectiveOriginTokenAddress === zeroAddress
|
|
2310
|
+
? recipient
|
|
2311
|
+
: effectiveOriginTokenAddress,
|
|
2312
|
+
data: hasCustomCalldata
|
|
2313
|
+
? destinationCalldata
|
|
2314
|
+
: effectiveOriginTokenAddress === zeroAddress
|
|
2315
|
+
? "0x"
|
|
2316
|
+
: getERC20TransferData({
|
|
2317
|
+
recipient,
|
|
2318
|
+
amount: BigInt(swapAmount),
|
|
2319
|
+
}),
|
|
2320
|
+
value:
|
|
2321
|
+
effectiveOriginTokenAddress === zeroAddress
|
|
2322
|
+
? BigInt(swapAmount)
|
|
2323
|
+
: "0",
|
|
2324
|
+
chainId: effectiveOriginChainId,
|
|
2325
|
+
chain: effectiveOriginChain,
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
logger.console.log("[trails-sdk] origin call params", originCallParams)
|
|
2329
|
+
|
|
2330
|
+
let originUserTxReceipt: TransactionReceipt | null = null
|
|
2331
|
+
const originMetaTxnReceipt: MetaTxnReceipt | null = null
|
|
2332
|
+
const destinationMetaTxnReceipt: MetaTxnReceipt | null = null
|
|
2333
|
+
|
|
2334
|
+
await attemptSwitchChain({
|
|
2335
|
+
walletClient,
|
|
2336
|
+
desiredChainId: effectiveOriginChainId,
|
|
2337
|
+
})
|
|
2338
|
+
if (!dryMode) {
|
|
2339
|
+
try {
|
|
2340
|
+
onTransactionStateChange([
|
|
2341
|
+
{
|
|
2342
|
+
transactionHash: "",
|
|
2343
|
+
explorerUrl: "",
|
|
2344
|
+
chainId: effectiveOriginChainId,
|
|
2345
|
+
state: "pending",
|
|
2346
|
+
label: "Execute",
|
|
2347
|
+
},
|
|
2348
|
+
])
|
|
2349
|
+
} catch (error) {
|
|
2350
|
+
logger.console.error(
|
|
2351
|
+
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
2352
|
+
error,
|
|
2353
|
+
)
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
if (hasCustomCalldata) {
|
|
2357
|
+
try {
|
|
2358
|
+
const needsApproval = await getNeedsApproval({
|
|
2359
|
+
publicClient: effectivePublicClient,
|
|
2360
|
+
token: effectiveOriginTokenAddress,
|
|
2361
|
+
account: account.address,
|
|
2362
|
+
spender: recipient,
|
|
2363
|
+
amount: maxUint256,
|
|
2364
|
+
})
|
|
2365
|
+
|
|
2366
|
+
if (needsApproval) {
|
|
2367
|
+
const txHash = await approveERC20({
|
|
2368
|
+
walletClient,
|
|
2369
|
+
tokenAddress: effectiveOriginTokenAddress,
|
|
2370
|
+
spender: recipient,
|
|
2371
|
+
amount: maxUint256,
|
|
2372
|
+
chain: effectiveOriginChain,
|
|
2373
|
+
})
|
|
2374
|
+
|
|
2375
|
+
logger.console.log("waiting for approve", txHash)
|
|
2376
|
+
await effectivePublicClient.waitForTransactionReceipt({
|
|
2377
|
+
hash: txHash,
|
|
2378
|
+
})
|
|
2379
|
+
logger.console.log("approve done")
|
|
2380
|
+
}
|
|
2381
|
+
} catch (error) {
|
|
2382
|
+
logger.console.error("[trails-sdk] Error approving ERC20", error)
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2100
2385
|
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
})
|
|
2386
|
+
// Show persistent toast for checkout flow
|
|
2387
|
+
updatePersistentToast(
|
|
2388
|
+
"Payment Started",
|
|
2389
|
+
"Waiting for wallet confirmation...",
|
|
2390
|
+
"info",
|
|
2391
|
+
)
|
|
2108
2392
|
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
recipient,
|
|
2122
|
-
amount: BigInt(swapAmount),
|
|
2123
|
-
}),
|
|
2124
|
-
value:
|
|
2125
|
-
effectiveOriginTokenAddress === zeroAddress
|
|
2126
|
-
? BigInt(swapAmount)
|
|
2127
|
-
: "0",
|
|
2128
|
-
chainId: effectiveOriginChainId,
|
|
2129
|
-
chain: effectiveOriginChain,
|
|
2130
|
-
}
|
|
2393
|
+
logger.console.log(
|
|
2394
|
+
"[trails-sdk] origin call params",
|
|
2395
|
+
originCallParams,
|
|
2396
|
+
)
|
|
2397
|
+
const txHash = await sendOriginTransaction(
|
|
2398
|
+
account,
|
|
2399
|
+
walletClient,
|
|
2400
|
+
originCallParams as any,
|
|
2401
|
+
{
|
|
2402
|
+
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
2403
|
+
},
|
|
2404
|
+
) // TODO: Add proper type
|
|
2131
2405
|
|
|
2132
|
-
|
|
2406
|
+
logger.console.log("[trails-sdk] origin tx", txHash)
|
|
2133
2407
|
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2408
|
+
if (onOriginSend) {
|
|
2409
|
+
onOriginSend()
|
|
2410
|
+
}
|
|
2137
2411
|
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
desiredChainId: effectiveOriginChainId,
|
|
2141
|
-
})
|
|
2142
|
-
if (!dryMode) {
|
|
2143
|
-
try {
|
|
2144
|
-
onTransactionStateChange([
|
|
2412
|
+
// Wait for transaction receipt
|
|
2413
|
+
const receipt = await effectivePublicClient.waitForTransactionReceipt(
|
|
2145
2414
|
{
|
|
2146
|
-
|
|
2147
|
-
explorerUrl: "",
|
|
2148
|
-
chainId: effectiveOriginChainId,
|
|
2149
|
-
state: "pending",
|
|
2150
|
-
label: "Execute",
|
|
2415
|
+
hash: txHash,
|
|
2151
2416
|
},
|
|
2152
|
-
])
|
|
2153
|
-
} catch (error) {
|
|
2154
|
-
logger.console.error(
|
|
2155
|
-
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
2156
|
-
error,
|
|
2157
2417
|
)
|
|
2158
|
-
|
|
2418
|
+
logger.console.log("[trails-sdk] receipt", receipt)
|
|
2419
|
+
originUserTxReceipt = receipt
|
|
2159
2420
|
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
})
|
|
2421
|
+
trackTransactionConfirmed({
|
|
2422
|
+
transactionHash: txHash,
|
|
2423
|
+
chainId: effectiveOriginChainId,
|
|
2424
|
+
userAddress: account.address,
|
|
2425
|
+
blockNumber: Number(receipt.blockNumber),
|
|
2426
|
+
originTokenAddress,
|
|
2427
|
+
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
2428
|
+
})
|
|
2169
2429
|
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
})
|
|
2430
|
+
// Remove persistent toast and show success
|
|
2431
|
+
const chainInfo = getChainInfo(effectiveOriginChainId)
|
|
2432
|
+
updatePersistentToast(
|
|
2433
|
+
"Transfer Confirmed",
|
|
2434
|
+
`Your transaction on ${chainInfo?.name || "chain"} has been confirmed`,
|
|
2435
|
+
"info",
|
|
2436
|
+
)
|
|
2178
2437
|
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2438
|
+
try {
|
|
2439
|
+
onTransactionStateChange([
|
|
2440
|
+
getTransactionStateFromReceipt(
|
|
2441
|
+
originUserTxReceipt,
|
|
2442
|
+
effectiveOriginChainId,
|
|
2443
|
+
transactionStates[0]?.label,
|
|
2444
|
+
),
|
|
2445
|
+
])
|
|
2185
2446
|
} catch (error) {
|
|
2186
|
-
logger.console.error(
|
|
2447
|
+
logger.console.error(
|
|
2448
|
+
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
2449
|
+
error,
|
|
2450
|
+
)
|
|
2187
2451
|
}
|
|
2188
|
-
}
|
|
2189
2452
|
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
{
|
|
2203
|
-
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
2204
|
-
},
|
|
2205
|
-
) // TODO: Add proper type
|
|
2453
|
+
// Track payment completion for same-chain same-token transaction
|
|
2454
|
+
if (originUserTxReceipt && originUserTxReceipt.status === "success") {
|
|
2455
|
+
trackPaymentCompleted({
|
|
2456
|
+
userAddress: account.address,
|
|
2457
|
+
originTxHash: originUserTxReceipt.transactionHash,
|
|
2458
|
+
originChainId: effectiveOriginChainId, // Same chain
|
|
2459
|
+
mode,
|
|
2460
|
+
fundMethod,
|
|
2461
|
+
originTokenAddress,
|
|
2462
|
+
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
2463
|
+
destinationTokenAmountUsd: depositAmountUsd?.toString(), // same as deposit amount
|
|
2464
|
+
})
|
|
2206
2465
|
|
|
2207
|
-
|
|
2466
|
+
// Call onCheckoutComplete callback if provided
|
|
2467
|
+
if (checkoutOnHandlers?.triggerCheckoutComplete) {
|
|
2468
|
+
checkoutOnHandlers.triggerCheckoutComplete()
|
|
2469
|
+
}
|
|
2470
|
+
} else if (originUserTxReceipt) {
|
|
2471
|
+
trackPaymentError({
|
|
2472
|
+
error: "Transaction failed",
|
|
2473
|
+
userAddress: account.address,
|
|
2474
|
+
mode,
|
|
2475
|
+
fundMethod,
|
|
2476
|
+
originTokenAddress,
|
|
2477
|
+
})
|
|
2208
2478
|
|
|
2209
|
-
|
|
2210
|
-
|
|
2479
|
+
// Call onCheckoutError callback if provided
|
|
2480
|
+
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
2481
|
+
checkoutOnHandlers.triggerCheckoutError("Transaction failed")
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2211
2484
|
}
|
|
2212
2485
|
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2486
|
+
return {
|
|
2487
|
+
originUserTxReceipt,
|
|
2488
|
+
originMetaTxnReceipt,
|
|
2489
|
+
destinationMetaTxnReceipt,
|
|
2490
|
+
totalCompletionSeconds: 0,
|
|
2491
|
+
}
|
|
2492
|
+
} catch (error) {
|
|
2493
|
+
const errorMessage =
|
|
2494
|
+
error instanceof Error
|
|
2495
|
+
? error.message
|
|
2496
|
+
: "Unknown error occurred during transaction"
|
|
2497
|
+
logger.console.error(
|
|
2498
|
+
"[trails-sdk] Error in sendHandlerForSameChainSameToken:",
|
|
2499
|
+
error,
|
|
2500
|
+
)
|
|
2219
2501
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2502
|
+
// Track payment error
|
|
2503
|
+
trackPaymentError({
|
|
2504
|
+
error: errorMessage,
|
|
2223
2505
|
userAddress: account.address,
|
|
2224
|
-
|
|
2506
|
+
mode,
|
|
2507
|
+
fundMethod,
|
|
2225
2508
|
originTokenAddress,
|
|
2226
|
-
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
2227
2509
|
})
|
|
2228
2510
|
|
|
2229
|
-
//
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
"Transfer Confirmed",
|
|
2233
|
-
`Your transaction on ${chainInfo?.name || "chain"} has been confirmed`,
|
|
2234
|
-
"info",
|
|
2235
|
-
)
|
|
2236
|
-
|
|
2237
|
-
try {
|
|
2238
|
-
onTransactionStateChange([
|
|
2239
|
-
getTransactionStateFromReceipt(
|
|
2240
|
-
originUserTxReceipt,
|
|
2241
|
-
effectiveOriginChainId,
|
|
2242
|
-
transactionStates[0]?.label,
|
|
2243
|
-
),
|
|
2244
|
-
])
|
|
2245
|
-
} catch (error) {
|
|
2246
|
-
logger.console.error(
|
|
2247
|
-
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
2248
|
-
error,
|
|
2249
|
-
)
|
|
2250
|
-
}
|
|
2251
|
-
|
|
2252
|
-
// Track payment completion for same-chain same-token transaction
|
|
2253
|
-
if (originUserTxReceipt && originUserTxReceipt.status === "success") {
|
|
2254
|
-
trackPaymentCompleted({
|
|
2255
|
-
userAddress: account.address,
|
|
2256
|
-
originTxHash: originUserTxReceipt.transactionHash,
|
|
2257
|
-
originChainId: effectiveOriginChainId, // Same chain
|
|
2258
|
-
mode,
|
|
2259
|
-
fundMethod,
|
|
2260
|
-
originTokenAddress,
|
|
2261
|
-
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
2262
|
-
destinationTokenAmountUsd: depositAmountUsd?.toString(), // same as deposit amount
|
|
2263
|
-
})
|
|
2264
|
-
} else if (originUserTxReceipt) {
|
|
2265
|
-
trackPaymentError({
|
|
2266
|
-
error: "Transaction failed",
|
|
2267
|
-
userAddress: account.address,
|
|
2268
|
-
mode,
|
|
2269
|
-
fundMethod,
|
|
2270
|
-
originTokenAddress,
|
|
2271
|
-
})
|
|
2511
|
+
// Call onCheckoutError callback if provided
|
|
2512
|
+
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
2513
|
+
checkoutOnHandlers.triggerCheckoutError(errorMessage)
|
|
2272
2514
|
}
|
|
2273
|
-
}
|
|
2274
2515
|
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
originMetaTxnReceipt,
|
|
2278
|
-
destinationMetaTxnReceipt,
|
|
2279
|
-
totalCompletionSeconds: 0,
|
|
2516
|
+
// Re-throw the error so caller can handle if needed
|
|
2517
|
+
throw error
|
|
2280
2518
|
}
|
|
2281
2519
|
},
|
|
2282
2520
|
}
|
|
@@ -2443,7 +2681,6 @@ async function _sendHandlerForSameChainDifferentToken({
|
|
|
2443
2681
|
onOriginSend,
|
|
2444
2682
|
}: {
|
|
2445
2683
|
onOriginSend?: () => void
|
|
2446
|
-
feeTokenAddress?: string | null
|
|
2447
2684
|
}): Promise<SendReturn> => {
|
|
2448
2685
|
const { hasEnoughBalance, balanceError } = await checkAccountBalance({
|
|
2449
2686
|
account,
|
|
@@ -2557,6 +2794,8 @@ async function attemptGaslessDeposit({
|
|
|
2557
2794
|
account,
|
|
2558
2795
|
trailsClient,
|
|
2559
2796
|
originRelayer,
|
|
2797
|
+
feeOptions,
|
|
2798
|
+
selectedFeeToken,
|
|
2560
2799
|
}: {
|
|
2561
2800
|
paymasterUrl?: string
|
|
2562
2801
|
depositTokenAddress: string
|
|
@@ -2569,11 +2808,25 @@ async function attemptGaslessDeposit({
|
|
|
2569
2808
|
trailsClient: TrailsAPIClient
|
|
2570
2809
|
originRelayer: Relayer.Standard.Rpc.RpcRelayer
|
|
2571
2810
|
feeOptions: any
|
|
2572
|
-
|
|
2811
|
+
selectedFeeToken?: any
|
|
2573
2812
|
}): Promise<TransactionReceipt | null> {
|
|
2574
2813
|
let originUserTxReceipt: TransactionReceipt | null = null
|
|
2575
2814
|
const originChainId = chain.id
|
|
2576
|
-
|
|
2815
|
+
|
|
2816
|
+
logger.console.log(
|
|
2817
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] attemptGaslessDeposit called with:",
|
|
2818
|
+
{
|
|
2819
|
+
originChainId,
|
|
2820
|
+
depositTokenAddress,
|
|
2821
|
+
depositTokenAmount,
|
|
2822
|
+
depositRecipient,
|
|
2823
|
+
hasFeeOptions: !!feeOptions,
|
|
2824
|
+
feeOptionsLength: feeOptions?.feeOptions?.length,
|
|
2825
|
+
selectedFeeToken,
|
|
2826
|
+
hasSelectedFeeToken: !!selectedFeeToken,
|
|
2827
|
+
paymasterUrl,
|
|
2828
|
+
},
|
|
2829
|
+
)
|
|
2577
2830
|
|
|
2578
2831
|
const publicClient = createPublicClient({
|
|
2579
2832
|
chain,
|
|
@@ -2581,12 +2834,20 @@ async function attemptGaslessDeposit({
|
|
|
2581
2834
|
})
|
|
2582
2835
|
|
|
2583
2836
|
const intentEntrypoint = intentEntrypoints[chain.id]
|
|
2584
|
-
logger.console.log("[trails-sdk]
|
|
2837
|
+
logger.console.log("[trails-sdk] [GASLESS-FLOW] Intent entrypoint check:", {
|
|
2838
|
+
chainId: chain.id,
|
|
2839
|
+
chainName: chain.name,
|
|
2840
|
+
intentEntrypoint,
|
|
2841
|
+
hasIntentEntrypoint: !!intentEntrypoint,
|
|
2842
|
+
availableChains: Object.keys(intentEntrypoints).map(Number),
|
|
2843
|
+
})
|
|
2585
2844
|
|
|
2586
2845
|
// If intent entrypoint is not available, fall back to old flow
|
|
2587
2846
|
if (!intentEntrypoint) {
|
|
2588
|
-
logger.console.
|
|
2589
|
-
|
|
2847
|
+
logger.console.warn(
|
|
2848
|
+
`[trails-sdk] ⚠️ No intent entrypoint configured for chain ${chain.id} (${chain.name}). ` +
|
|
2849
|
+
`Gasless deposits with fee options are only supported on chains: ${Object.keys(intentEntrypoints).join(", ")}. ` +
|
|
2850
|
+
`Falling back to old flow (permit2/paymaster).`,
|
|
2590
2851
|
)
|
|
2591
2852
|
|
|
2592
2853
|
let calls: Array<{
|
|
@@ -2596,7 +2857,19 @@ async function attemptGaslessDeposit({
|
|
|
2596
2857
|
}> = []
|
|
2597
2858
|
|
|
2598
2859
|
if (paymasterUrl) {
|
|
2599
|
-
logger.console.log(
|
|
2860
|
+
logger.console.log(
|
|
2861
|
+
"[trails-sdk] [GASLESS-FLOW] doing gasless with paymaster",
|
|
2862
|
+
)
|
|
2863
|
+
|
|
2864
|
+
// Switch to correct chain before requesting signatures
|
|
2865
|
+
logger.console.log(
|
|
2866
|
+
"[trails-sdk] [GASLESS-FLOW] Switching chain for paymaster flow",
|
|
2867
|
+
)
|
|
2868
|
+
await attemptSwitchChain({
|
|
2869
|
+
walletClient,
|
|
2870
|
+
desiredChainId: originChainId,
|
|
2871
|
+
})
|
|
2872
|
+
|
|
2600
2873
|
const delegatorSmartAccount = await getDelegatorSmartAccount({
|
|
2601
2874
|
publicClient,
|
|
2602
2875
|
})
|
|
@@ -2631,7 +2904,9 @@ async function attemptGaslessDeposit({
|
|
|
2631
2904
|
logger.console.log("[trails-sdk] receipt", receipt)
|
|
2632
2905
|
return receipt
|
|
2633
2906
|
} else {
|
|
2634
|
-
logger.console.log(
|
|
2907
|
+
logger.console.log(
|
|
2908
|
+
"[trails-sdk] [GASLESS-FLOW] doing gasless with sequence wallet",
|
|
2909
|
+
)
|
|
2635
2910
|
const delegatorPrivateKey = generatePrivateKey()
|
|
2636
2911
|
const delegatorAccount = privateKeyToAccount(delegatorPrivateKey)
|
|
2637
2912
|
const delegatorClient = createWalletClient({
|
|
@@ -2702,56 +2977,148 @@ async function attemptGaslessDeposit({
|
|
|
2702
2977
|
"[trails-sdk] Using Intent Entrypoint API flow with permit2 support for gasless deposit",
|
|
2703
2978
|
)
|
|
2704
2979
|
|
|
2980
|
+
// Switch to correct chain before requesting signatures
|
|
2981
|
+
logger.console.log(
|
|
2982
|
+
"[trails-sdk] [GASLESS-FLOW] Switching to chain before permit/intent signatures",
|
|
2983
|
+
{ originChainId },
|
|
2984
|
+
)
|
|
2985
|
+
await attemptSwitchChain({
|
|
2986
|
+
walletClient,
|
|
2987
|
+
desiredChainId: originChainId,
|
|
2988
|
+
})
|
|
2989
|
+
|
|
2705
2990
|
try {
|
|
2706
2991
|
const deadline = Math.floor(Date.now() / 1000) + 3600 // 1 hour from now
|
|
2992
|
+
const hasFeeOptions = Boolean(
|
|
2993
|
+
feeOptions && feeOptions.feeOptions?.length > 0,
|
|
2994
|
+
)
|
|
2995
|
+
|
|
2996
|
+
// 1. Check if we need approval - check if we have enough allowance for this transaction (deposit + fee)
|
|
2997
|
+
let requiredAmount = BigInt(depositTokenAmount)
|
|
2998
|
+
|
|
2999
|
+
// Match selectedFeeToken by tokenAddress to get the latest fee amount from feeOptions
|
|
3000
|
+
let selectedFeeOption = null
|
|
3001
|
+
if (selectedFeeToken && hasFeeOptions) {
|
|
3002
|
+
// Find matching fee option by tokenAddress to get latest amount
|
|
3003
|
+
selectedFeeOption = feeOptions.feeOptions.find(
|
|
3004
|
+
(opt: any) =>
|
|
3005
|
+
opt.tokenAddress?.toLowerCase() ===
|
|
3006
|
+
selectedFeeToken.tokenAddress?.toLowerCase(),
|
|
3007
|
+
)
|
|
3008
|
+
logger.console.log(
|
|
3009
|
+
"[trails-sdk] Matched selectedFeeToken to latest fee option:",
|
|
3010
|
+
{
|
|
3011
|
+
selectedFeeToken,
|
|
3012
|
+
matchedOption: selectedFeeOption,
|
|
3013
|
+
},
|
|
3014
|
+
)
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
// Fallback to first fee option if no match or no selectedFeeToken
|
|
3018
|
+
if (!selectedFeeOption && hasFeeOptions) {
|
|
3019
|
+
selectedFeeOption = feeOptions.feeOptions[0]
|
|
3020
|
+
logger.console.log(
|
|
3021
|
+
"[trails-sdk] Using first fee option as fallback:",
|
|
3022
|
+
selectedFeeOption,
|
|
3023
|
+
)
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
if (selectedFeeOption?.amount) {
|
|
3027
|
+
requiredAmount = requiredAmount + BigInt(selectedFeeOption.amount)
|
|
3028
|
+
logger.console.log("[trails-sdk] Including fee in required amount:", {
|
|
3029
|
+
depositAmount: depositTokenAmount,
|
|
3030
|
+
feeAmount: selectedFeeOption.amount,
|
|
3031
|
+
feeTokenAddress: selectedFeeOption.tokenAddress,
|
|
3032
|
+
totalRequired: requiredAmount.toString(),
|
|
3033
|
+
})
|
|
3034
|
+
}
|
|
2707
3035
|
|
|
2708
|
-
// 1. Check if we need approval for permit2
|
|
2709
3036
|
const needsApproval = await getNeedsIntentEntrypointApproval({
|
|
2710
3037
|
client: publicClient,
|
|
2711
3038
|
token: depositTokenAddress as `0x${string}`,
|
|
2712
3039
|
account: account.address,
|
|
2713
3040
|
entrypoint: intentEntrypoint as `0x${string}`,
|
|
2714
|
-
amount:
|
|
3041
|
+
amount: requiredAmount, // Check if we have enough allowance for this specific transaction
|
|
2715
3042
|
})
|
|
2716
3043
|
|
|
2717
|
-
logger.console.log(
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
3044
|
+
logger.console.log(
|
|
3045
|
+
"[trails-sdk] [GASLESS-FLOW] Checking permit requirements",
|
|
3046
|
+
{
|
|
3047
|
+
userAddress: account.address,
|
|
3048
|
+
tokenAddress: depositTokenAddress,
|
|
3049
|
+
depositAmount: depositTokenAmount,
|
|
3050
|
+
requiredAmount: requiredAmount.toString(),
|
|
3051
|
+
intentAddress: depositRecipient,
|
|
3052
|
+
chainID: originChainId,
|
|
3053
|
+
deadline,
|
|
3054
|
+
needsApproval,
|
|
3055
|
+
},
|
|
3056
|
+
)
|
|
2726
3057
|
|
|
2727
3058
|
// 2. Get permit signature if approval needed
|
|
2728
3059
|
let permitSignature: string | undefined
|
|
2729
3060
|
let permitDeadline: number | undefined
|
|
2730
3061
|
|
|
2731
3062
|
if (needsApproval) {
|
|
2732
|
-
logger.console.log(
|
|
2733
|
-
|
|
2734
|
-
|
|
3063
|
+
logger.console.log(
|
|
3064
|
+
"[trails-sdk] Getting permit signature for infinite approval",
|
|
3065
|
+
)
|
|
3066
|
+
|
|
3067
|
+
// Use infinite approval (maxUint256) so user doesn't need to approve again
|
|
3068
|
+
const permitAmount = maxUint256
|
|
3069
|
+
logger.console.log(
|
|
3070
|
+
"[trails-sdk] Using infinite approval for gasless deposits",
|
|
3071
|
+
{
|
|
3072
|
+
depositAmount: depositTokenAmount,
|
|
3073
|
+
permitAmount: permitAmount.toString(),
|
|
3074
|
+
},
|
|
3075
|
+
)
|
|
3076
|
+
|
|
2735
3077
|
const permitSig = await getPermitSignature({
|
|
2736
3078
|
publicClient,
|
|
2737
3079
|
walletClient,
|
|
2738
3080
|
signer: account.address,
|
|
2739
3081
|
spender: intentEntrypoint as `0x${string}`,
|
|
2740
3082
|
tokenAddress: depositTokenAddress as `0x${string}`,
|
|
2741
|
-
amount:
|
|
3083
|
+
amount: permitAmount, // Infinite approval
|
|
2742
3084
|
chain,
|
|
2743
3085
|
deadline: BigInt(deadline),
|
|
2744
3086
|
})
|
|
2745
3087
|
permitSignature = permitSig.signature
|
|
2746
3088
|
permitDeadline = Number(permitSig.deadline)
|
|
2747
3089
|
logger.console.log(
|
|
2748
|
-
"[trails-sdk] Permit signature obtained for
|
|
2749
|
-
depositTokenAmount,
|
|
3090
|
+
"[trails-sdk] Permit signature obtained for infinite approval",
|
|
2750
3091
|
)
|
|
2751
3092
|
}
|
|
2752
3093
|
|
|
2753
|
-
// 3. Get
|
|
3094
|
+
// 3. Get current nonce for the user
|
|
3095
|
+
logger.console.log("[trails-sdk] Getting user nonce")
|
|
3096
|
+
const nonce = await getUserNonce({
|
|
3097
|
+
publicClient,
|
|
3098
|
+
userAddress: account.address,
|
|
3099
|
+
intentEntrypoint: intentEntrypoint as `0x${string}`,
|
|
3100
|
+
})
|
|
3101
|
+
logger.console.log("[trails-sdk] User nonce:", nonce.toString())
|
|
3102
|
+
|
|
3103
|
+
// 4. Get intent signature
|
|
2754
3104
|
logger.console.log("[trails-sdk] Requesting intent signature via EIP-712")
|
|
3105
|
+
// Get fee collector address from fee options response, or use selected fee option's collector
|
|
3106
|
+
const feeCollectorAddress = (selectedFeeOption?.feeCollector ||
|
|
3107
|
+
feeOptions?.feeCollector) as `0x${string}` | undefined
|
|
3108
|
+
|
|
3109
|
+
// Validate that we have a valid fee collector address
|
|
3110
|
+
if (!feeCollectorAddress || feeCollectorAddress === zeroAddress) {
|
|
3111
|
+
throw new Error(
|
|
3112
|
+
"[trails-sdk] Fee collector address not provided by API. Cannot proceed with gasless deposit. " +
|
|
3113
|
+
"Please ensure the API is returning feeCollector in the fee options response.",
|
|
3114
|
+
)
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
logger.console.log(
|
|
3118
|
+
"[trails-sdk] Using fee collector address:",
|
|
3119
|
+
feeCollectorAddress,
|
|
3120
|
+
)
|
|
3121
|
+
|
|
2755
3122
|
const { signature: intentSignature } = await signIntent({
|
|
2756
3123
|
client: walletClient,
|
|
2757
3124
|
intentParams: {
|
|
@@ -2762,15 +3129,39 @@ async function attemptGaslessDeposit({
|
|
|
2762
3129
|
deadline: BigInt(deadline),
|
|
2763
3130
|
chainId: originChainId,
|
|
2764
3131
|
contractAddress: intentEntrypoint as `0x${string}`,
|
|
3132
|
+
nonce,
|
|
3133
|
+
feeAmount: BigInt(selectedFeeOption?.amount || "0"),
|
|
3134
|
+
feeCollector: feeCollectorAddress,
|
|
2765
3135
|
},
|
|
2766
3136
|
})
|
|
2767
3137
|
logger.console.log("[trails-sdk] Intent signature received")
|
|
2768
3138
|
|
|
2769
|
-
//
|
|
3139
|
+
// 5. Call the deposit endpoint with permit support and optional fee
|
|
3140
|
+
logger.console.log(
|
|
3141
|
+
"[trails-sdk] Calling getIntentEntrypointDeposit with permit enabled",
|
|
3142
|
+
{ usePermit: needsApproval, hasFee: !!feeOptions },
|
|
3143
|
+
)
|
|
3144
|
+
|
|
3145
|
+
// selectedFeeOption was already determined at the start of the try block
|
|
3146
|
+
|
|
2770
3147
|
logger.console.log(
|
|
2771
|
-
"[trails-sdk] Calling getIntentEntrypointDeposit with
|
|
2772
|
-
{
|
|
3148
|
+
"[trails-sdk] Calling getIntentEntrypointDeposit with params:",
|
|
3149
|
+
{
|
|
3150
|
+
userAddress: account.address,
|
|
3151
|
+
tokenAddress: depositTokenAddress,
|
|
3152
|
+
amount: depositTokenAmount,
|
|
3153
|
+
intentAddress: depositRecipient,
|
|
3154
|
+
chainID: originChainId,
|
|
3155
|
+
deadline,
|
|
3156
|
+
usePermit: needsApproval,
|
|
3157
|
+
hasPermitSignature: !!permitSignature,
|
|
3158
|
+
feeAmount: selectedFeeOption?.amount,
|
|
3159
|
+
feeTokenSymbol: selectedFeeOption?.tokenSymbol,
|
|
3160
|
+
selectedFeeToken,
|
|
3161
|
+
usingSelectedFeeToken: !!selectedFeeToken,
|
|
3162
|
+
},
|
|
2773
3163
|
)
|
|
3164
|
+
|
|
2774
3165
|
const depositDataResponse = await trailsClient.getIntentEntrypointDeposit({
|
|
2775
3166
|
params: {
|
|
2776
3167
|
userAddress: account.address,
|
|
@@ -2780,9 +3171,11 @@ async function attemptGaslessDeposit({
|
|
|
2780
3171
|
chainID: originChainId,
|
|
2781
3172
|
deadline,
|
|
2782
3173
|
intentSignature,
|
|
2783
|
-
usePermit: needsApproval, // Use
|
|
3174
|
+
usePermit: needsApproval, // Use permit if approval needed
|
|
3175
|
+
permitAmount: needsApproval ? maxUint256.toString() : undefined, // Pass infinite approval amount
|
|
2784
3176
|
permitSignature,
|
|
2785
3177
|
permitDeadline,
|
|
3178
|
+
feeAmount: selectedFeeOption?.amount || undefined,
|
|
2786
3179
|
},
|
|
2787
3180
|
})
|
|
2788
3181
|
|
|
@@ -3192,8 +3585,8 @@ async function attemptUserDepositTx({
|
|
|
3192
3585
|
destinationTokenSymbol,
|
|
3193
3586
|
depositAmountUsd,
|
|
3194
3587
|
feeOptions,
|
|
3195
|
-
feeTokenAddress,
|
|
3196
3588
|
trailsClient,
|
|
3589
|
+
selectedFeeToken,
|
|
3197
3590
|
}: {
|
|
3198
3591
|
originTokenAddress: string
|
|
3199
3592
|
gasless: boolean
|
|
@@ -3220,8 +3613,8 @@ async function attemptUserDepositTx({
|
|
|
3220
3613
|
destinationTokenSymbol: string
|
|
3221
3614
|
depositAmountUsd: number
|
|
3222
3615
|
feeOptions?: any
|
|
3223
|
-
feeTokenAddress?: string | null
|
|
3224
3616
|
trailsClient: TrailsAPIClient
|
|
3617
|
+
selectedFeeToken?: any
|
|
3225
3618
|
}): Promise<TransactionReceipt | null> {
|
|
3226
3619
|
let originUserTxReceipt: TransactionReceipt | null = null
|
|
3227
3620
|
const originChainId = chain.id
|
|
@@ -3240,11 +3633,36 @@ async function attemptUserDepositTx({
|
|
|
3240
3633
|
return null
|
|
3241
3634
|
}
|
|
3242
3635
|
|
|
3243
|
-
const doGasless =
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3636
|
+
const doGasless = getDoGasless(
|
|
3637
|
+
originTokenAddress,
|
|
3638
|
+
gasless,
|
|
3639
|
+
feeOptions,
|
|
3640
|
+
selectedFeeToken,
|
|
3641
|
+
)
|
|
3642
|
+
logger.console.log(
|
|
3643
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] doGasless check results:",
|
|
3644
|
+
{
|
|
3645
|
+
doGasless,
|
|
3646
|
+
paymasterUrl,
|
|
3647
|
+
hasFeeOptions: !!feeOptions,
|
|
3648
|
+
feeOptionsCount: feeOptions?.feeOptions?.length || 0,
|
|
3649
|
+
selectedFeeToken,
|
|
3650
|
+
selectedFeeTokenSet:
|
|
3651
|
+
selectedFeeToken !== null && selectedFeeToken !== undefined,
|
|
3652
|
+
originTokenAddress,
|
|
3653
|
+
gasless,
|
|
3654
|
+
isNotNative: originTokenAddress !== zeroAddress,
|
|
3655
|
+
},
|
|
3656
|
+
)
|
|
3657
|
+
if (doGasless || paymasterUrl) {
|
|
3658
|
+
logger.console.log(
|
|
3659
|
+
"[trails-sdk] [GASLESS-FLOW] Entering gasless deposit flow",
|
|
3660
|
+
{
|
|
3661
|
+
doGasless,
|
|
3662
|
+
hasPaymasterUrl: !!paymasterUrl,
|
|
3663
|
+
paymasterUrl,
|
|
3664
|
+
},
|
|
3665
|
+
)
|
|
3248
3666
|
try {
|
|
3249
3667
|
originUserTxReceipt = await attemptGaslessDeposit({
|
|
3250
3668
|
paymasterUrl,
|
|
@@ -3257,11 +3675,12 @@ async function attemptUserDepositTx({
|
|
|
3257
3675
|
account,
|
|
3258
3676
|
trailsClient,
|
|
3259
3677
|
originRelayer,
|
|
3260
|
-
feeOptions,
|
|
3261
|
-
|
|
3678
|
+
feeOptions: feeOptions,
|
|
3679
|
+
selectedFeeToken: selectedFeeToken,
|
|
3262
3680
|
})
|
|
3263
3681
|
} catch (error) {
|
|
3264
3682
|
logger.console.log("[trails-sdk] gassless attempt failed", error)
|
|
3683
|
+
throw error
|
|
3265
3684
|
}
|
|
3266
3685
|
}
|
|
3267
3686
|
|
|
@@ -3298,11 +3717,108 @@ async function attemptUserDepositTx({
|
|
|
3298
3717
|
export function getDoGasless(
|
|
3299
3718
|
originTokenAddress: string,
|
|
3300
3719
|
gasless: boolean,
|
|
3301
|
-
|
|
3720
|
+
feeOptions?: any,
|
|
3721
|
+
selectedFeeToken?: any,
|
|
3302
3722
|
): boolean {
|
|
3303
|
-
|
|
3304
|
-
|
|
3723
|
+
const hasFeeOptions = Boolean(feeOptions && feeOptions.feeOptions?.length > 0)
|
|
3724
|
+
|
|
3725
|
+
// Important: The UI passes selectedFeeToken in these states:
|
|
3726
|
+
// - null: User explicitly chose "Pay with native gas"
|
|
3727
|
+
// - {object}: User selected a fee token OR it was auto-selected
|
|
3728
|
+
// - undefined: Should not happen (initial state auto-selects if options exist)
|
|
3729
|
+
|
|
3730
|
+
// If selectedFeeToken is null, user explicitly chose native gas
|
|
3731
|
+
if (selectedFeeToken === null) {
|
|
3732
|
+
logger.console.log(
|
|
3733
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] getDoGasless: User explicitly selected native gas (null)",
|
|
3734
|
+
)
|
|
3735
|
+
return false
|
|
3736
|
+
}
|
|
3737
|
+
|
|
3738
|
+
// If selectedFeeToken is undefined and no fee options, can't do gasless
|
|
3739
|
+
if (!selectedFeeToken && !hasFeeOptions) {
|
|
3740
|
+
logger.console.log(
|
|
3741
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] getDoGasless: No fee token selected and no fee options available",
|
|
3742
|
+
)
|
|
3743
|
+
return false
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3746
|
+
// If selectedFeeToken is undefined but fee options exist, use first ERC20 option
|
|
3747
|
+
let effectiveFeeToken = selectedFeeToken
|
|
3748
|
+
if (!effectiveFeeToken && hasFeeOptions) {
|
|
3749
|
+
const firstFeeOption = feeOptions.feeOptions[0]
|
|
3750
|
+
|
|
3751
|
+
// Check if first option is native gas
|
|
3752
|
+
const isFirstOptionNative =
|
|
3753
|
+
firstFeeOption?.tokenAddress === zeroAddress ||
|
|
3754
|
+
firstFeeOption?.tokenAddress?.toLowerCase() === zeroAddress ||
|
|
3755
|
+
firstFeeOption?.isNative === true
|
|
3756
|
+
|
|
3757
|
+
if (!isFirstOptionNative && firstFeeOption?.tokenAddress) {
|
|
3758
|
+
// First fee option is ERC20, use it
|
|
3759
|
+
effectiveFeeToken = firstFeeOption
|
|
3760
|
+
logger.console.log(
|
|
3761
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] getDoGasless: No fee token selected, using first ERC20 fee option",
|
|
3762
|
+
{
|
|
3763
|
+
feeOption: effectiveFeeToken,
|
|
3764
|
+
},
|
|
3765
|
+
)
|
|
3766
|
+
} else {
|
|
3767
|
+
logger.console.log(
|
|
3768
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] getDoGasless: First fee option is native gas, skipping gasless",
|
|
3769
|
+
)
|
|
3770
|
+
return false
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
// Check if the effective fee token is native gas
|
|
3775
|
+
const isNativeGasFee =
|
|
3776
|
+
!effectiveFeeToken ||
|
|
3777
|
+
effectiveFeeToken.tokenAddress === zeroAddress ||
|
|
3778
|
+
effectiveFeeToken.tokenAddress?.toLowerCase() === zeroAddress ||
|
|
3779
|
+
effectiveFeeToken.isNative === true
|
|
3780
|
+
|
|
3781
|
+
// Don't use gasless if origin token is native (sending ETH)
|
|
3782
|
+
if (originTokenAddress === zeroAddress) {
|
|
3783
|
+
logger.console.log(
|
|
3784
|
+
"[trails-sdk] [GASLESS-FLOW] getDoGasless: Origin token is native, skipping gasless",
|
|
3785
|
+
)
|
|
3786
|
+
return false
|
|
3787
|
+
}
|
|
3788
|
+
|
|
3789
|
+
// Don't use gasless if fee token is native
|
|
3790
|
+
if (isNativeGasFee) {
|
|
3791
|
+
logger.console.log(
|
|
3792
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] getDoGasless: Fee token is native gas, skipping gasless",
|
|
3793
|
+
{
|
|
3794
|
+
effectiveFeeToken,
|
|
3795
|
+
},
|
|
3796
|
+
)
|
|
3797
|
+
return false
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3800
|
+
// Don't use gasless if disabled
|
|
3801
|
+
if (!gasless) {
|
|
3802
|
+
logger.console.log(
|
|
3803
|
+
"[trails-sdk] [GASLESS-FLOW] getDoGasless: Gasless disabled",
|
|
3804
|
+
)
|
|
3805
|
+
return false
|
|
3806
|
+
}
|
|
3807
|
+
|
|
3808
|
+
// All conditions met, use gasless with ERC20 fee token
|
|
3809
|
+
logger.console.log(
|
|
3810
|
+
"[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] getDoGasless decision: Using gasless",
|
|
3811
|
+
{
|
|
3812
|
+
originTokenAddress,
|
|
3813
|
+
gasless,
|
|
3814
|
+
hasFeeOptions,
|
|
3815
|
+
selectedFeeToken,
|
|
3816
|
+
effectiveFeeToken,
|
|
3817
|
+
feeOptionsCount: feeOptions?.feeOptions?.length || 0,
|
|
3818
|
+
},
|
|
3305
3819
|
)
|
|
3820
|
+
|
|
3821
|
+
return true
|
|
3306
3822
|
}
|
|
3307
3823
|
|
|
3308
3824
|
function getTransactionStateFromReceipt(
|
|
@@ -3659,11 +4175,20 @@ export function useQuote({
|
|
|
3659
4175
|
quoteProvider,
|
|
3660
4176
|
gasless,
|
|
3661
4177
|
paymasterUrl,
|
|
3662
|
-
|
|
4178
|
+
relayerEnv,
|
|
4179
|
+
nodeGatewayEnv,
|
|
4180
|
+
}: Partial<
|
|
4181
|
+
UseQuoteProps & { relayerEnv?: RelayerEnv; nodeGatewayEnv?: SequenceEnv }
|
|
4182
|
+
> = {}): UseQuoteReturn {
|
|
4183
|
+
// Set node gateway environment override for this quote session
|
|
4184
|
+
if (nodeGatewayEnv) {
|
|
4185
|
+
;(globalThis as any).__testNodeGatewayEnv = nodeGatewayEnv
|
|
4186
|
+
}
|
|
4187
|
+
|
|
3663
4188
|
const apiClient = useAPIClient()
|
|
3664
4189
|
const trailsClient = useTrailsClient()
|
|
3665
4190
|
const { getRelayer } = useRelayers({
|
|
3666
|
-
env: getSequenceEnv() as RelayerEnv,
|
|
4191
|
+
env: relayerEnv || (getSequenceEnv() as RelayerEnv),
|
|
3667
4192
|
})
|
|
3668
4193
|
const indexerGatewayClient = useIndexerGatewayClient()
|
|
3669
4194
|
|
|
@@ -3738,10 +4263,26 @@ export function useQuote({
|
|
|
3738
4263
|
|
|
3739
4264
|
const sourceTokenDecimals = originToken?.decimals
|
|
3740
4265
|
if (!sourceTokenDecimals) {
|
|
4266
|
+
logger.console.error(
|
|
4267
|
+
"[trails-sdk] [useQuote] Missing source token decimals:",
|
|
4268
|
+
{
|
|
4269
|
+
originToken,
|
|
4270
|
+
fromTokenAddress,
|
|
4271
|
+
fromChainId,
|
|
4272
|
+
},
|
|
4273
|
+
)
|
|
3741
4274
|
throw new Error("Source token decimals not found")
|
|
3742
4275
|
}
|
|
3743
4276
|
const destinationTokenDecimals = destinationToken?.decimals
|
|
3744
4277
|
if (!destinationTokenDecimals) {
|
|
4278
|
+
logger.console.error(
|
|
4279
|
+
"[trails-sdk] Missing destination token decimals:",
|
|
4280
|
+
{
|
|
4281
|
+
destinationToken,
|
|
4282
|
+
toTokenAddress,
|
|
4283
|
+
toChainId,
|
|
4284
|
+
},
|
|
4285
|
+
)
|
|
3745
4286
|
throw new Error("Destination token decimals not found")
|
|
3746
4287
|
}
|
|
3747
4288
|
const destinationTokenSymbol = destinationToken?.symbol ?? ""
|
|
@@ -3991,7 +4532,6 @@ export async function getNormalizedQuoteObject({
|
|
|
3991
4532
|
originNativeTokenPriceUsd,
|
|
3992
4533
|
quoteProvider,
|
|
3993
4534
|
noSufficientBalance,
|
|
3994
|
-
minimumNotMet,
|
|
3995
4535
|
}: {
|
|
3996
4536
|
originDepositAddress?: string
|
|
3997
4537
|
destinationDepositAddress?: string
|
|
@@ -4015,7 +4555,6 @@ export async function getNormalizedQuoteObject({
|
|
|
4015
4555
|
originNativeTokenPriceUsd?: number | null
|
|
4016
4556
|
quoteProvider?: string
|
|
4017
4557
|
noSufficientBalance?: boolean
|
|
4018
|
-
minimumNotMet?: boolean
|
|
4019
4558
|
}): Promise<PrepareSendQuote> {
|
|
4020
4559
|
if (!destinationChainId) {
|
|
4021
4560
|
throw new Error("Destination chain id is required")
|
|
@@ -4213,7 +4752,6 @@ export async function getNormalizedQuoteObject({
|
|
|
4213
4752
|
),
|
|
4214
4753
|
quoteProvider: quoteProviderInfo,
|
|
4215
4754
|
noSufficientBalance: noSufficientBalance || false,
|
|
4216
|
-
minimumNotMet: minimumNotMet || false,
|
|
4217
4755
|
}
|
|
4218
4756
|
}
|
|
4219
4757
|
|