0xtrails 0.1.13 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aave.d.ts.map +1 -1
- package/dist/analytics.d.ts +11 -2
- package/dist/analytics.d.ts.map +1 -1
- package/dist/apiClient.d.ts +1 -1
- package/dist/apiClient.d.ts.map +1 -1
- package/dist/{proxyCaller.d.ts → balanceInjector.d.ts} +5 -4
- package/dist/balanceInjector.d.ts.map +1 -0
- package/dist/{ccip-D3gTQONK.js → ccip-D6ToCrWc.js} +12 -12
- package/dist/cctp.d.ts.map +1 -1
- package/dist/cctpqueue.d.ts +3 -3
- package/dist/cctpqueue.d.ts.map +1 -1
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +17 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +5 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/contractUtils.d.ts +2 -0
- package/dist/contractUtils.d.ts.map +1 -1
- package/dist/customChains.d.ts +24 -0
- package/dist/customChains.d.ts.map +1 -0
- package/dist/{index-CnUM7lKf.js → index-BqgeTLL8.js} +34072 -30146
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +411 -400
- package/dist/intentEntrypoint.d.ts +96 -0
- package/dist/intentEntrypoint.d.ts.map +1 -0
- package/dist/intents.d.ts +5 -3
- package/dist/intents.d.ts.map +1 -1
- package/dist/metaTxnMonitor.d.ts.map +1 -1
- package/dist/morpho.d.ts.map +1 -1
- package/dist/pools.d.ts +3 -1
- package/dist/pools.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +8 -2
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/prices.d.ts +1 -1
- package/dist/prices.d.ts.map +1 -1
- package/dist/relaySdk.d.ts.map +1 -1
- package/dist/relayer.d.ts.map +1 -1
- package/dist/toast.d.ts +9 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/tokenBalances.d.ts +6 -2
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/trails.d.ts +6 -5
- package/dist/trails.d.ts.map +1 -1
- package/dist/trailsClient.d.ts +12 -0
- package/dist/trailsClient.d.ts.map +1 -0
- package/dist/transactions.d.ts +8 -0
- package/dist/transactions.d.ts.map +1 -1
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/AccountSettings.d.ts +7 -0
- package/dist/widget/components/AccountSettings.d.ts.map +1 -0
- package/dist/widget/components/ChainList.d.ts +0 -1
- package/dist/widget/components/ChainList.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts +46 -0
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -0
- package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
- package/dist/widget/components/ConnectedWallets.d.ts +9 -0
- package/dist/widget/components/ConnectedWallets.d.ts.map +1 -0
- package/dist/widget/components/DebugMenu.d.ts.map +1 -1
- package/dist/widget/components/DebugScreensList.d.ts.map +1 -1
- package/dist/widget/components/DebugToast.d.ts +3 -0
- package/dist/widget/components/DebugToast.d.ts.map +1 -0
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/EarnPools.d.ts.map +1 -1
- package/dist/widget/components/Fund.d.ts +44 -0
- package/dist/widget/components/Fund.d.ts.map +1 -0
- package/dist/widget/components/Identicon.d.ts +9 -0
- package/dist/widget/components/Identicon.d.ts.map +1 -0
- package/dist/widget/components/Pay.d.ts +46 -0
- package/dist/widget/components/Pay.d.ts.map +1 -0
- package/dist/widget/components/Receive.d.ts.map +1 -1
- package/dist/widget/components/RecentTokens.d.ts.map +1 -1
- package/dist/widget/components/Recipients.d.ts +9 -0
- package/dist/widget/components/Recipients.d.ts.map +1 -0
- package/dist/widget/components/RefundWarning.d.ts +9 -0
- package/dist/widget/components/RefundWarning.d.ts.map +1 -0
- package/dist/widget/components/SimpleSwap.d.ts.map +1 -1
- package/dist/widget/components/Swap.d.ts.map +1 -1
- package/dist/widget/components/SwapSettings.d.ts +1 -5
- package/dist/widget/components/SwapSettings.d.ts.map +1 -1
- package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
- package/dist/widget/components/ThemeSyncer.d.ts +6 -0
- package/dist/widget/components/ThemeSyncer.d.ts.map +1 -0
- package/dist/widget/components/Toast.d.ts +24 -0
- package/dist/widget/components/Toast.d.ts.map +1 -0
- package/dist/widget/components/TokenList.d.ts.map +1 -1
- package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
- package/dist/widget/components/TruncatedAddress.d.ts +2 -0
- package/dist/widget/components/TruncatedAddress.d.ts.map +1 -1
- package/dist/widget/components/UserPreferences.d.ts +7 -0
- package/dist/widget/components/UserPreferences.d.ts.map +1 -0
- package/dist/widget/hooks/useBalanceVisible.d.ts +1 -0
- package/dist/widget/hooks/useBalanceVisible.d.ts.map +1 -1
- package/dist/widget/hooks/useCheckout.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/useDebugScreens.d.ts +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts +54 -0
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -0
- package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/usePayMessage.d.ts +34 -0
- package/dist/widget/hooks/usePayMessage.d.ts.map +1 -0
- package/dist/widget/hooks/useRecipients.d.ts +17 -0
- package/dist/widget/hooks/useRecipients.d.ts.map +1 -0
- package/dist/widget/hooks/useSelectedRecipient.d.ts +12 -0
- package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +2 -0
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useSwapAmount.d.ts +13 -0
- package/dist/widget/hooks/useSwapAmount.d.ts.map +1 -0
- package/dist/widget/hooks/useSwapSettings.d.ts +16 -0
- package/dist/widget/hooks/useSwapSettings.d.ts.map +1 -0
- package/dist/widget/hooks/useTargetAmount.d.ts +5 -0
- package/dist/widget/hooks/useTargetAmount.d.ts.map +1 -0
- package/dist/widget/hooks/useTheme.d.ts +14 -0
- package/dist/widget/hooks/useTheme.d.ts.map +1 -0
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/index.js +2 -2
- package/dist/widget/widget.d.ts +9 -0
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +29 -28
- package/src/aave.ts +6 -1
- package/src/analytics.ts +103 -53
- package/src/apiClient.ts +1 -1
- package/src/{proxyCaller.ts → balanceInjector.ts} +22 -17
- package/src/cctp.ts +6 -2
- package/src/cctpqueue.ts +7 -7
- package/src/chains.ts +8 -0
- package/src/config.ts +40 -9
- package/src/constants.ts +11 -8
- package/src/contractUtils.ts +33 -2
- package/src/customChains.ts +24 -0
- package/src/index.ts +11 -1
- package/src/intentEntrypoint.ts +253 -0
- package/src/intents.ts +87 -54
- package/src/metaTxnMonitor.ts +1 -0
- package/src/morpho.ts +13 -2
- package/src/pools.ts +68 -86
- package/src/prepareSend.ts +437 -207
- package/src/prices.ts +51 -7
- package/src/relaySdk.ts +6 -4
- package/src/relayer.ts +2 -0
- package/src/toast.ts +110 -0
- package/src/tokenBalances.ts +112 -20
- package/src/tokens.ts +70 -7
- package/src/trails.ts +80 -77
- package/src/trailsClient.ts +45 -0
- package/src/transactions.ts +27 -35
- package/src/umd.tsx +1 -1
- package/src/wallets.ts +2 -1
- package/src/widget/assets/sequence-logo.svg +15 -0
- package/src/widget/compiled.css +2 -2
- package/src/widget/components/AccountActionsDropdown.tsx +18 -159
- package/src/widget/components/AccountIntentTransactionHistory.tsx +346 -63
- package/src/widget/components/AccountSettings.tsx +96 -0
- package/src/widget/components/ChainFilterDropdown.tsx +1 -1
- package/src/widget/components/ChainList.tsx +10 -20
- package/src/widget/components/ClassicSwap.tsx +923 -0
- package/src/widget/components/ConfigDisplay.tsx +8 -5
- package/src/widget/components/ConnectedWallets.tsx +260 -0
- package/src/widget/components/DebugMenu.tsx +2 -0
- package/src/widget/components/DebugScreensList.tsx +3 -0
- package/src/widget/components/DebugToast.tsx +63 -0
- package/src/widget/components/Earn.tsx +108 -116
- package/src/widget/components/EarnPools.tsx +2 -4
- package/src/widget/components/EarnPoolsFilters.tsx +6 -6
- package/src/widget/components/Fund.tsx +1245 -0
- package/src/widget/components/FundMethods.tsx +1 -1
- package/src/widget/components/FundSendForm.tsx +1 -1
- package/src/widget/components/Identicon.tsx +158 -0
- package/src/widget/components/Pay.tsx +1088 -0
- package/src/widget/components/PaySendForm.tsx +1 -1
- package/src/widget/components/QuoteDetails.tsx +1 -1
- package/src/widget/components/Receipt.tsx +1 -1
- package/src/widget/components/Receive.tsx +4 -2
- package/src/widget/components/RecentTokens.tsx +2 -1
- package/src/widget/components/Recipients.tsx +448 -0
- package/src/widget/components/RefundWarning.tsx +61 -0
- package/src/widget/components/ScreenHeader.tsx +1 -1
- package/src/widget/components/SimpleSwap.tsx +74 -58
- package/src/widget/components/Swap.tsx +35 -853
- package/src/widget/components/SwapSettings.tsx +5 -11
- package/src/widget/components/ThemeProvider.tsx +32 -0
- package/src/widget/components/ThemeSyncer.tsx +47 -0
- package/src/widget/components/Toast.tsx +315 -0
- package/src/widget/components/TokenList.tsx +2 -34
- package/src/widget/components/TokenSelector.tsx +3 -3
- package/src/widget/components/TransactionDetails.tsx +153 -13
- package/src/widget/components/TruncatedAddress.tsx +5 -1
- package/src/widget/components/UserPreferences.tsx +156 -0
- package/src/widget/components/WalletList.tsx +1 -1
- package/src/widget/hooks/useBalanceVisible.tsx +40 -2
- package/src/widget/hooks/useCheckout.ts +13 -0
- package/src/widget/hooks/useCurrentScreen.tsx +3 -0
- package/src/widget/hooks/useDebugScreens.ts +12 -2
- package/src/widget/hooks/useDefaultTokenSelection.tsx +475 -0
- package/src/widget/hooks/useIntentTransactionHistory.ts +212 -0
- package/src/widget/hooks/usePayMessage.tsx +370 -0
- package/src/widget/hooks/useRecipients.ts +168 -0
- package/src/widget/hooks/useSelectedRecipient.tsx +48 -0
- package/src/widget/hooks/useSendForm.ts +179 -26
- package/src/widget/hooks/useSwapAmount.tsx +50 -0
- package/src/widget/hooks/useSwapSettings.tsx +100 -0
- package/src/widget/hooks/useTargetAmount.ts +23 -0
- package/src/widget/hooks/useTheme.tsx +80 -0
- package/src/widget/hooks/useTokenList.ts +20 -11
- package/src/widget/index.css +45 -21
- package/src/widget/widget.tsx +164 -68
- package/dist/address.d.ts +0 -2
- package/dist/address.d.ts.map +0 -1
- package/dist/proxyCaller.d.ts.map +0 -1
- package/src/address.ts +0 -6
|
@@ -39,6 +39,7 @@ import { useResolveEnsAddress } from "../../ens.js"
|
|
|
39
39
|
import { etherlink } from "viem/chains"
|
|
40
40
|
import { logger } from "../../logger.js"
|
|
41
41
|
import { getIsContract } from "../../contractUtils.js"
|
|
42
|
+
import { useTrailsClient } from "../../trailsClient.js"
|
|
42
43
|
|
|
43
44
|
export interface Token {
|
|
44
45
|
id: number
|
|
@@ -178,6 +179,8 @@ export type UseSendReturn = {
|
|
|
178
179
|
quoteErrorPrettified: string | null
|
|
179
180
|
isSameTokenWithoutCustomCalldata: boolean
|
|
180
181
|
isRecipientContract: boolean
|
|
182
|
+
isSenderContractOnOrigin: boolean
|
|
183
|
+
isSenderContractOnDestination: boolean
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
export function useSendForm({
|
|
@@ -277,6 +280,7 @@ export function useSendForm({
|
|
|
277
280
|
recipient as `0x${string}`,
|
|
278
281
|
selectedDestinationChain.id,
|
|
279
282
|
)
|
|
283
|
+
logger.console.log("[trails-sdk] isRecipientContract:", isContract)
|
|
280
284
|
setIsRecipientContract(isContract)
|
|
281
285
|
} catch (error) {
|
|
282
286
|
logger.console.error(
|
|
@@ -293,6 +297,64 @@ export function useSendForm({
|
|
|
293
297
|
checkRecipientContract()
|
|
294
298
|
}, [recipient, selectedDestinationChain?.id])
|
|
295
299
|
|
|
300
|
+
// Check if sender is a contract address on origin chain
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
const checkSenderContractOnOrigin = async () => {
|
|
303
|
+
if (account?.address && selectedToken?.chainId) {
|
|
304
|
+
try {
|
|
305
|
+
const isContract = await getIsContract(
|
|
306
|
+
account.address as `0x${string}`,
|
|
307
|
+
selectedToken.chainId,
|
|
308
|
+
)
|
|
309
|
+
logger.console.log(
|
|
310
|
+
"[trails-sdk] isSenderContractOnOrigin:",
|
|
311
|
+
isContract,
|
|
312
|
+
)
|
|
313
|
+
setIsSenderContractOnOrigin(isContract)
|
|
314
|
+
} catch (error) {
|
|
315
|
+
logger.console.error(
|
|
316
|
+
"[trails-sdk] Error checking if sender is contract on origin:",
|
|
317
|
+
error,
|
|
318
|
+
)
|
|
319
|
+
setIsSenderContractOnOrigin(false)
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
setIsSenderContractOnOrigin(false)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
checkSenderContractOnOrigin()
|
|
327
|
+
}, [account?.address, selectedToken?.chainId])
|
|
328
|
+
|
|
329
|
+
// Check if sender is a contract address on destination chain
|
|
330
|
+
useEffect(() => {
|
|
331
|
+
const checkSenderContractOnDestination = async () => {
|
|
332
|
+
if (account?.address && selectedDestinationChain?.id) {
|
|
333
|
+
try {
|
|
334
|
+
const isContract = await getIsContract(
|
|
335
|
+
account.address as `0x${string}`,
|
|
336
|
+
selectedDestinationChain.id,
|
|
337
|
+
)
|
|
338
|
+
logger.console.log(
|
|
339
|
+
"[trails-sdk] isSenderContractOnDestination:",
|
|
340
|
+
isContract,
|
|
341
|
+
)
|
|
342
|
+
setIsSenderContractOnDestination(isContract)
|
|
343
|
+
} catch (error) {
|
|
344
|
+
logger.console.error(
|
|
345
|
+
"[trails-sdk] Error checking if sender is contract on destination:",
|
|
346
|
+
error,
|
|
347
|
+
)
|
|
348
|
+
setIsSenderContractOnDestination(false)
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
setIsSenderContractOnDestination(false)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
checkSenderContractOnDestination()
|
|
356
|
+
}, [account?.address, selectedDestinationChain?.id])
|
|
357
|
+
|
|
296
358
|
const isCustomToken = useMemo(() => toToken?.startsWith("0x"), [toToken])
|
|
297
359
|
|
|
298
360
|
const {
|
|
@@ -354,7 +416,7 @@ export function useSendForm({
|
|
|
354
416
|
token = supportedTokens.find(
|
|
355
417
|
(token) =>
|
|
356
418
|
(isToTokenAddress // Match by specified destination token address or symbol
|
|
357
|
-
? token.contractAddress === toToken
|
|
419
|
+
? token.contractAddress.toLowerCase() === toToken.toLowerCase()
|
|
358
420
|
: token.symbol === toToken) &&
|
|
359
421
|
(toChainId // Match by specified destination chain id
|
|
360
422
|
? token.chainId === toChainId
|
|
@@ -372,22 +434,30 @@ export function useSendForm({
|
|
|
372
434
|
}, [selectedDestToken, defaultDestToken])
|
|
373
435
|
|
|
374
436
|
const apiClient = useAPIClient()
|
|
437
|
+
const trailsClient = useTrailsClient()
|
|
375
438
|
|
|
376
439
|
const destTokenAddress = useTokenAddress({
|
|
377
440
|
chainId: selectedDestinationChain?.id,
|
|
378
441
|
tokenSymbol: selectedDestToken?.symbol,
|
|
379
442
|
})
|
|
380
443
|
|
|
444
|
+
const destTokenPricesInput = useMemo(() => {
|
|
445
|
+
const input =
|
|
446
|
+
selectedDestToken && destTokenAddress && selectedDestinationChain?.id
|
|
447
|
+
? [
|
|
448
|
+
{
|
|
449
|
+
tokenId: selectedDestToken.symbol,
|
|
450
|
+
contractAddress: destTokenAddress,
|
|
451
|
+
chainId: selectedDestinationChain.id,
|
|
452
|
+
},
|
|
453
|
+
]
|
|
454
|
+
: []
|
|
455
|
+
|
|
456
|
+
return input
|
|
457
|
+
}, [selectedDestToken, destTokenAddress, selectedDestinationChain?.id])
|
|
458
|
+
|
|
381
459
|
const { tokenPrices: destTokenPrices } = useTokenPrices(
|
|
382
|
-
|
|
383
|
-
? [
|
|
384
|
-
{
|
|
385
|
-
tokenId: selectedDestToken.symbol,
|
|
386
|
-
contractAddress: destTokenAddress,
|
|
387
|
-
chainId: selectedDestinationChain.id,
|
|
388
|
-
},
|
|
389
|
-
]
|
|
390
|
-
: [],
|
|
460
|
+
destTokenPricesInput,
|
|
391
461
|
apiClient,
|
|
392
462
|
)
|
|
393
463
|
|
|
@@ -421,7 +491,7 @@ export function useSendForm({
|
|
|
421
491
|
const newToken = supportedTokens.find(
|
|
422
492
|
(token) =>
|
|
423
493
|
(isToTokenAddress // Match by specified destination token address or symbol
|
|
424
|
-
? token.contractAddress === toToken
|
|
494
|
+
? token.contractAddress.toLowerCase() === toToken.toLowerCase()
|
|
425
495
|
: token.symbol === toToken) &&
|
|
426
496
|
(toChainId // Match by specified destination chain id
|
|
427
497
|
? token.chainId === toChainId
|
|
@@ -479,12 +549,13 @@ export function useSendForm({
|
|
|
479
549
|
[onTransactionStateChange],
|
|
480
550
|
)
|
|
481
551
|
|
|
482
|
-
const balanceFormatted =
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
552
|
+
const balanceFormatted =
|
|
553
|
+
selectedToken?.balance && selectedToken?.contractInfo?.decimals
|
|
554
|
+
? formatRawAmount(
|
|
555
|
+
selectedToken.balance,
|
|
556
|
+
selectedToken.contractInfo?.decimals!,
|
|
557
|
+
)
|
|
558
|
+
: "0"
|
|
488
559
|
const balanceUsdDisplay = selectedToken?.balanceUsdFormatted ?? ""
|
|
489
560
|
const isValidRecipient = Boolean(recipient && isAddress(recipient))
|
|
490
561
|
|
|
@@ -502,6 +573,10 @@ export function useSendForm({
|
|
|
502
573
|
null,
|
|
503
574
|
)
|
|
504
575
|
const [isRecipientContract, setIsRecipientContract] = useState(false)
|
|
576
|
+
const [isSenderContractOnOrigin, setIsSenderContractOnOrigin] =
|
|
577
|
+
useState(false)
|
|
578
|
+
const [isSenderContractOnDestination, setIsSenderContractOnDestination] =
|
|
579
|
+
useState(false)
|
|
505
580
|
|
|
506
581
|
const { hasParam } = useQueryParams()
|
|
507
582
|
const isDryMode = hasParam("dryMode", "true")
|
|
@@ -520,7 +595,39 @@ export function useSendForm({
|
|
|
520
595
|
|
|
521
596
|
// Calculate raw amount (in wei/smallest unit)
|
|
522
597
|
const amountRaw = useMemo(() => {
|
|
523
|
-
if (
|
|
598
|
+
if (
|
|
599
|
+
!amount &&
|
|
600
|
+
!(selectedToken?.contractInfo?.decimals || selectedDestToken?.decimals)
|
|
601
|
+
) {
|
|
602
|
+
logger.console.warn("[trails-sdk] Missing token decimals for quote", {
|
|
603
|
+
amount,
|
|
604
|
+
selectedToken,
|
|
605
|
+
selectedDestToken,
|
|
606
|
+
tradeType,
|
|
607
|
+
})
|
|
608
|
+
return "0"
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (
|
|
612
|
+
tradeType === TradeType.EXACT_INPUT &&
|
|
613
|
+
!selectedToken?.contractInfo?.decimals
|
|
614
|
+
) {
|
|
615
|
+
logger.console.warn(
|
|
616
|
+
"[trails-sdk] Missing source token decimals for quote",
|
|
617
|
+
{
|
|
618
|
+
selectedToken,
|
|
619
|
+
tradeType,
|
|
620
|
+
},
|
|
621
|
+
)
|
|
622
|
+
return "0"
|
|
623
|
+
} else if (!selectedDestToken?.decimals) {
|
|
624
|
+
logger.console.warn(
|
|
625
|
+
"[trails-sdk] Missing destination token decimals for quote",
|
|
626
|
+
{
|
|
627
|
+
selectedDestToken,
|
|
628
|
+
tradeType,
|
|
629
|
+
},
|
|
630
|
+
)
|
|
524
631
|
return "0"
|
|
525
632
|
}
|
|
526
633
|
|
|
@@ -528,7 +635,7 @@ export function useSendForm({
|
|
|
528
635
|
// For EXACT_OUTPUT: use destination token decimals (user enters destination amount)
|
|
529
636
|
const decimals =
|
|
530
637
|
tradeType === TradeType.EXACT_INPUT
|
|
531
|
-
? selectedToken
|
|
638
|
+
? selectedToken?.contractInfo?.decimals
|
|
532
639
|
: selectedDestToken?.decimals
|
|
533
640
|
|
|
534
641
|
if (!decimals) {
|
|
@@ -569,6 +676,18 @@ export function useSendForm({
|
|
|
569
676
|
amountRaw === "0" ||
|
|
570
677
|
!selectedToken
|
|
571
678
|
) {
|
|
679
|
+
logger.console.log(
|
|
680
|
+
"[trails-sdk] Skipping quote because of missing inputs",
|
|
681
|
+
{
|
|
682
|
+
amount,
|
|
683
|
+
destinationTokenAddress,
|
|
684
|
+
isValidRecipient,
|
|
685
|
+
selectedDestToken,
|
|
686
|
+
selectedDestinationChain,
|
|
687
|
+
amountRaw,
|
|
688
|
+
selectedToken,
|
|
689
|
+
},
|
|
690
|
+
)
|
|
572
691
|
setQuoteError(null)
|
|
573
692
|
setPrepareSendResult(null)
|
|
574
693
|
return
|
|
@@ -589,7 +708,13 @@ export function useSendForm({
|
|
|
589
708
|
const destinationTokenDecimals = selectedDestToken.decimals
|
|
590
709
|
|
|
591
710
|
if (!sourceTokenDecimals || !destinationTokenDecimals) {
|
|
592
|
-
logger.console.warn("[trails-sdk] Missing token decimals for quote"
|
|
711
|
+
logger.console.warn("[trails-sdk] Missing token decimals for quote", {
|
|
712
|
+
sourceTokenDecimals,
|
|
713
|
+
destinationTokenDecimals,
|
|
714
|
+
selectedToken,
|
|
715
|
+
selectedDestToken,
|
|
716
|
+
tradeType,
|
|
717
|
+
})
|
|
593
718
|
setPrepareSendResult(null)
|
|
594
719
|
setIsLoadingQuote(false)
|
|
595
720
|
return
|
|
@@ -681,6 +806,7 @@ export function useSendForm({
|
|
|
681
806
|
fee: "0",
|
|
682
807
|
client: walletClient,
|
|
683
808
|
apiClient,
|
|
809
|
+
trailsClient,
|
|
684
810
|
originRelayer,
|
|
685
811
|
destinationRelayer,
|
|
686
812
|
destinationCalldata: toCalldata,
|
|
@@ -721,6 +847,7 @@ export function useSendForm({
|
|
|
721
847
|
account,
|
|
722
848
|
walletClient,
|
|
723
849
|
apiClient,
|
|
850
|
+
trailsClient,
|
|
724
851
|
selectedDestToken?.decimals,
|
|
725
852
|
recipient,
|
|
726
853
|
destinationTokenAddress,
|
|
@@ -776,6 +903,10 @@ export function useSendForm({
|
|
|
776
903
|
selectedDestinationChain?.id,
|
|
777
904
|
toCalldata,
|
|
778
905
|
refetchTrigger,
|
|
906
|
+
selectedToken?.contractAddress,
|
|
907
|
+
selectedToken?.chainId,
|
|
908
|
+
selectedToken?.balance,
|
|
909
|
+
selectedToken?.tokenPriceUsd,
|
|
779
910
|
])
|
|
780
911
|
|
|
781
912
|
// Calculate destination amount from quote if available
|
|
@@ -991,9 +1122,13 @@ export function useSendForm({
|
|
|
991
1122
|
const checksummedRecipient = getAddress(recipient)
|
|
992
1123
|
const checksummedAccount = getAddress(account.address)
|
|
993
1124
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1125
|
+
logger.console.log("[trails-sdk] buttonText:", {
|
|
1126
|
+
mode,
|
|
1127
|
+
fundMethod,
|
|
1128
|
+
isSameChain,
|
|
1129
|
+
isSameToken,
|
|
1130
|
+
checksummedRecipient,
|
|
1131
|
+
})
|
|
997
1132
|
|
|
998
1133
|
if (fundMethod === "exchange") {
|
|
999
1134
|
return `Continue to Exchange`
|
|
@@ -1003,6 +1138,14 @@ export function useSendForm({
|
|
|
1003
1138
|
return `Continue to QR Code`
|
|
1004
1139
|
}
|
|
1005
1140
|
|
|
1141
|
+
if (!fundMethod || fundMethod === "wallet") {
|
|
1142
|
+
return `Continue on wallet`
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
if (mode === "swap") {
|
|
1146
|
+
return `Swap ${amountDisplay} ${tokenSymbol}`
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1006
1149
|
if (mode === "earn") {
|
|
1007
1150
|
return `Deposit ${destinationAmountDisplay} ${destinationTokenSymbol}`
|
|
1008
1151
|
}
|
|
@@ -1054,7 +1197,7 @@ export function useSendForm({
|
|
|
1054
1197
|
}, [quoteError])
|
|
1055
1198
|
|
|
1056
1199
|
// Check if origin and destination tokens are the same (same contract address on same chain)
|
|
1057
|
-
// Only block same-token transactions when there's no custom calldata
|
|
1200
|
+
// Only block same-token transactions when there's no custom calldata AND recipient is the same as sender
|
|
1058
1201
|
const isSameTokenWithoutCustomCalldata = useMemo(() => {
|
|
1059
1202
|
if (
|
|
1060
1203
|
!selectedToken ||
|
|
@@ -1069,14 +1212,22 @@ export function useSendForm({
|
|
|
1069
1212
|
selectedToken.contractAddress.toLowerCase() ===
|
|
1070
1213
|
destinationTokenAddress.toLowerCase() &&
|
|
1071
1214
|
selectedToken.chainId === selectedDestinationChain?.id
|
|
1072
|
-
|
|
1073
|
-
|
|
1215
|
+
|
|
1216
|
+
// Allow same-token transactions if:
|
|
1217
|
+
// 1. There's custom calldata (e.g., NFT minting)
|
|
1218
|
+
// 2. Recipient is different from sender (simple transfer to another address)
|
|
1219
|
+
const recipientIsSameAsSender =
|
|
1220
|
+
recipient?.toLowerCase() === account?.address?.toLowerCase()
|
|
1221
|
+
|
|
1222
|
+
return isSameChainAndToken && !toCalldata && recipientIsSameAsSender
|
|
1074
1223
|
}, [
|
|
1075
1224
|
selectedToken,
|
|
1076
1225
|
destinationTokenAddress,
|
|
1077
1226
|
selectedDestinationChain,
|
|
1078
1227
|
toCalldata,
|
|
1079
1228
|
selectedToken?.chainId,
|
|
1229
|
+
recipient,
|
|
1230
|
+
account?.address,
|
|
1080
1231
|
])
|
|
1081
1232
|
|
|
1082
1233
|
return {
|
|
@@ -1126,5 +1277,7 @@ export function useSendForm({
|
|
|
1126
1277
|
quoteErrorPrettified,
|
|
1127
1278
|
isSameTokenWithoutCustomCalldata,
|
|
1128
1279
|
isRecipientContract,
|
|
1280
|
+
isSenderContractOnOrigin,
|
|
1281
|
+
isSenderContractOnDestination,
|
|
1129
1282
|
}
|
|
1130
1283
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useState,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
} from "react"
|
|
7
|
+
|
|
8
|
+
interface SwapAmountContextType {
|
|
9
|
+
amount: string
|
|
10
|
+
setAmount: (amount: string) => void
|
|
11
|
+
clearAmount: () => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const SwapAmountContext = createContext<SwapAmountContextType | undefined>(
|
|
15
|
+
undefined,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
interface SwapAmountProviderProps {
|
|
19
|
+
children: ReactNode
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const SwapAmountProvider: React.FC<SwapAmountProviderProps> = ({
|
|
23
|
+
children,
|
|
24
|
+
}) => {
|
|
25
|
+
const [amount, setAmount] = useState<string>("")
|
|
26
|
+
|
|
27
|
+
const clearAmount = () => {
|
|
28
|
+
setAmount("")
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<SwapAmountContext.Provider
|
|
33
|
+
value={{
|
|
34
|
+
amount,
|
|
35
|
+
setAmount,
|
|
36
|
+
clearAmount,
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</SwapAmountContext.Provider>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const useSwapAmount = () => {
|
|
45
|
+
const context = useContext(SwapAmountContext)
|
|
46
|
+
if (context === undefined) {
|
|
47
|
+
throw new Error("useSwapAmount must be used within a SwapAmountProvider")
|
|
48
|
+
}
|
|
49
|
+
return context
|
|
50
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useState,
|
|
5
|
+
useCallback,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
} from "react"
|
|
8
|
+
import { logger } from "../../logger.js"
|
|
9
|
+
|
|
10
|
+
interface SwapSettingsContextType {
|
|
11
|
+
isSimpleSwapMode: boolean
|
|
12
|
+
setIsSimpleSwapMode: (simple: boolean) => void
|
|
13
|
+
setIsSimpleSwapModeWithStorage: (simple: boolean) => void
|
|
14
|
+
toggleSimpleSwapMode: () => void
|
|
15
|
+
resetSwapSettings: () => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const SwapSettingsContext = createContext<SwapSettingsContextType | undefined>(
|
|
19
|
+
undefined,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
interface SwapSettingsProviderProps {
|
|
23
|
+
children: ReactNode
|
|
24
|
+
initialSimpleMode?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const SIMPLE_SWAP_MODE_KEY = "trails-simple-swap-mode"
|
|
28
|
+
|
|
29
|
+
export const SwapSettingsProvider: React.FC<SwapSettingsProviderProps> = ({
|
|
30
|
+
children,
|
|
31
|
+
initialSimpleMode = false,
|
|
32
|
+
}) => {
|
|
33
|
+
// Initialize state from localStorage or use initialSimpleMode as fallback
|
|
34
|
+
const [isSimpleSwapMode, setIsSimpleSwapMode] = useState<boolean>(() => {
|
|
35
|
+
try {
|
|
36
|
+
const stored = localStorage.getItem(SIMPLE_SWAP_MODE_KEY)
|
|
37
|
+
return stored !== null ? JSON.parse(stored) : initialSimpleMode
|
|
38
|
+
} catch (error) {
|
|
39
|
+
logger.console.warn(
|
|
40
|
+
"Failed to read simple swap mode from localStorage:",
|
|
41
|
+
error,
|
|
42
|
+
)
|
|
43
|
+
return initialSimpleMode
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Function that updates state and saves to localStorage (for user preferences)
|
|
48
|
+
const setIsSimpleSwapModeWithStorage = useCallback((simple: boolean) => {
|
|
49
|
+
setIsSimpleSwapMode(simple)
|
|
50
|
+
try {
|
|
51
|
+
localStorage.setItem(SIMPLE_SWAP_MODE_KEY, JSON.stringify(simple))
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.console.warn(
|
|
54
|
+
"Failed to save simple swap mode to localStorage:",
|
|
55
|
+
error,
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
}, [])
|
|
59
|
+
|
|
60
|
+
const toggleSimpleSwapMode = useCallback(() => {
|
|
61
|
+
const newMode = !isSimpleSwapMode
|
|
62
|
+
setIsSimpleSwapModeWithStorage(newMode)
|
|
63
|
+
}, [isSimpleSwapMode, setIsSimpleSwapModeWithStorage])
|
|
64
|
+
|
|
65
|
+
const resetSwapSettings = useCallback(() => {
|
|
66
|
+
try {
|
|
67
|
+
localStorage.removeItem(SIMPLE_SWAP_MODE_KEY)
|
|
68
|
+
setIsSimpleSwapMode(false) // Default: advanced swap mode
|
|
69
|
+
} catch (error) {
|
|
70
|
+
logger.console.warn("Failed to reset swap settings:", error)
|
|
71
|
+
setIsSimpleSwapMode(false) // Still reset state even if localStorage fails
|
|
72
|
+
}
|
|
73
|
+
}, [])
|
|
74
|
+
|
|
75
|
+
const value: SwapSettingsContextType = {
|
|
76
|
+
isSimpleSwapMode,
|
|
77
|
+
setIsSimpleSwapMode, // This one doesn't save to localStorage
|
|
78
|
+
setIsSimpleSwapModeWithStorage, // This one saves to localStorage
|
|
79
|
+
toggleSimpleSwapMode,
|
|
80
|
+
resetSwapSettings,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<SwapSettingsContext.Provider value={value}>
|
|
85
|
+
{children}
|
|
86
|
+
</SwapSettingsContext.Provider>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const useSwapSettings = (): SwapSettingsContextType => {
|
|
91
|
+
const context = useContext(SwapSettingsContext)
|
|
92
|
+
|
|
93
|
+
if (context === undefined) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
"useSwapSettings must be used within a SwapSettingsProvider",
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return context
|
|
100
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useAmountUsd } from "./useAmountUsd.js"
|
|
2
|
+
import { useWidgetProps } from "./useWidgetProps.js"
|
|
3
|
+
|
|
4
|
+
export function useTargetAmount(): {
|
|
5
|
+
targetAmountUsd: number | null
|
|
6
|
+
targetAmountUsdFormatted: string
|
|
7
|
+
} {
|
|
8
|
+
const { toAmount, toToken, toChainId } = useWidgetProps()
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
amountUsd: targetAmountUsd,
|
|
12
|
+
amountUsdFormatted: targetAmountUsdFormatted,
|
|
13
|
+
} = useAmountUsd({
|
|
14
|
+
amount: toAmount,
|
|
15
|
+
token: toToken,
|
|
16
|
+
chainId: Number(toChainId),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
targetAmountUsd,
|
|
21
|
+
targetAmountUsdFormatted,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useState,
|
|
5
|
+
useCallback,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
} from "react"
|
|
8
|
+
import { logger } from "../../logger.js"
|
|
9
|
+
|
|
10
|
+
type Theme = "auto" | "light" | "dark"
|
|
11
|
+
|
|
12
|
+
interface ThemeContextType {
|
|
13
|
+
selectedTheme: Theme | null
|
|
14
|
+
setSelectedTheme: (theme: Theme) => void
|
|
15
|
+
resetThemePreference: () => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
|
|
19
|
+
|
|
20
|
+
interface ThemeProviderProps {
|
|
21
|
+
children: ReactNode
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const THEME_KEY = "trails-theme"
|
|
25
|
+
|
|
26
|
+
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
|
|
27
|
+
// Initialize state from localStorage or return null if no preference is stored
|
|
28
|
+
const [selectedTheme, setSelectedThemeState] = useState<Theme | null>(() => {
|
|
29
|
+
try {
|
|
30
|
+
const stored = localStorage.getItem(THEME_KEY)
|
|
31
|
+
if (stored && ["auto", "light", "dark"].includes(stored)) {
|
|
32
|
+
return stored as Theme
|
|
33
|
+
}
|
|
34
|
+
return null // No user preference stored
|
|
35
|
+
} catch (error) {
|
|
36
|
+
logger.console.warn("Failed to read theme from localStorage:", error)
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Function that updates state and saves to localStorage
|
|
42
|
+
const setSelectedTheme = useCallback((theme: Theme) => {
|
|
43
|
+
setSelectedThemeState(theme)
|
|
44
|
+
try {
|
|
45
|
+
localStorage.setItem(THEME_KEY, theme)
|
|
46
|
+
} catch (error) {
|
|
47
|
+
logger.console.warn("Failed to save theme to localStorage:", error)
|
|
48
|
+
}
|
|
49
|
+
}, [])
|
|
50
|
+
|
|
51
|
+
const resetThemePreference = useCallback(() => {
|
|
52
|
+
try {
|
|
53
|
+
localStorage.removeItem(THEME_KEY)
|
|
54
|
+
setSelectedThemeState(null) // Clear user preference
|
|
55
|
+
} catch (error) {
|
|
56
|
+
logger.console.warn("Failed to reset theme preference:", error)
|
|
57
|
+
setSelectedThemeState(null) // Still reset state even if localStorage fails
|
|
58
|
+
}
|
|
59
|
+
}, [])
|
|
60
|
+
|
|
61
|
+
const value: ThemeContextType = {
|
|
62
|
+
selectedTheme,
|
|
63
|
+
setSelectedTheme,
|
|
64
|
+
resetThemePreference,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const useThemePreference = (): ThemeContextType => {
|
|
71
|
+
const context = useContext(ThemeContext)
|
|
72
|
+
|
|
73
|
+
if (context === undefined) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
"useThemePreference must be used within a ThemePreferenceProvider",
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return context
|
|
80
|
+
}
|
|
@@ -310,9 +310,14 @@ export function useTokenList({
|
|
|
310
310
|
}, [balanceError, onError])
|
|
311
311
|
|
|
312
312
|
const handleTokenSelect = (token: TokenFormatted) => {
|
|
313
|
-
const isNative =
|
|
313
|
+
const isNative =
|
|
314
|
+
!("contractAddress" in token) || token.contractAddress === zeroAddress
|
|
314
315
|
const chainInfo = getChainInfo(token.chainId)
|
|
315
316
|
const imageUrl = token.imageUrl
|
|
317
|
+
const decimals = isNative ? 18 : token.contractInfo?.decimals
|
|
318
|
+
if (!decimals) {
|
|
319
|
+
throw new Error("Decimals not found")
|
|
320
|
+
}
|
|
316
321
|
|
|
317
322
|
let formattedToken: Token
|
|
318
323
|
if (isNative) {
|
|
@@ -328,7 +333,7 @@ export function useTokenList({
|
|
|
328
333
|
(token as TokenBalanceExtended).balanceUsdFormatted ?? "",
|
|
329
334
|
tokenPriceUsd: (token as TokenBalanceExtended).price?.value ?? 0,
|
|
330
335
|
contractInfo: {
|
|
331
|
-
decimals
|
|
336
|
+
decimals,
|
|
332
337
|
symbol: chainInfo?.nativeCurrency.symbol || "ETH",
|
|
333
338
|
name: chainInfo?.nativeCurrency.name || "Native Token",
|
|
334
339
|
},
|
|
@@ -346,7 +351,7 @@ export function useTokenList({
|
|
|
346
351
|
...token.contractInfo,
|
|
347
352
|
name: token.contractInfo?.name ?? "Unknown Token",
|
|
348
353
|
symbol: token.contractInfo?.symbol ?? "???",
|
|
349
|
-
decimals
|
|
354
|
+
decimals,
|
|
350
355
|
},
|
|
351
356
|
balanceUsdFormatted: token.balanceUsdFormatted ?? "",
|
|
352
357
|
tokenPriceUsd: token.price?.value ?? 0,
|
|
@@ -373,7 +378,8 @@ export function useTokenList({
|
|
|
373
378
|
const isTokenSelected = (token: TokenBalanceExtended): boolean => {
|
|
374
379
|
if (!selectedToken) return false
|
|
375
380
|
|
|
376
|
-
const isNative =
|
|
381
|
+
const isNative =
|
|
382
|
+
!("contractAddress" in token) || token.contractAddress === zeroAddress
|
|
377
383
|
return (
|
|
378
384
|
selectedToken.chainId === token.chainId &&
|
|
379
385
|
(isNative
|
|
@@ -395,7 +401,8 @@ export function useTokenList({
|
|
|
395
401
|
|
|
396
402
|
const matchingTokens = sortedTokens.filter(
|
|
397
403
|
(token: TokenBalanceExtended) => {
|
|
398
|
-
const isNative =
|
|
404
|
+
const isNative =
|
|
405
|
+
!("contractAddress" in token) || token.contractAddress === zeroAddress
|
|
399
406
|
const chainInfo = getChainInfo(token.chainId)
|
|
400
407
|
const chainName = chainInfo?.name || ""
|
|
401
408
|
const chainNameLower = chainName.toLowerCase()
|
|
@@ -506,7 +513,8 @@ export function useTokenList({
|
|
|
506
513
|
// Get base formatted tokens
|
|
507
514
|
const baseFormattedTokens = filteredTokens.map(
|
|
508
515
|
(token: TokenBalanceExtended): TokenFormatted => {
|
|
509
|
-
const isNative =
|
|
516
|
+
const isNative =
|
|
517
|
+
!("contractAddress" in token) || token.contractAddress === zeroAddress
|
|
510
518
|
const chainInfo = getChainInfo(token.chainId)
|
|
511
519
|
const nativeSymbol = chainInfo?.nativeCurrency.symbol || "ETH"
|
|
512
520
|
const tokenSymbol = isNative
|
|
@@ -525,13 +533,14 @@ export function useTokenList({
|
|
|
525
533
|
tokenSymbol,
|
|
526
534
|
token.chainId,
|
|
527
535
|
)
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
536
|
+
const decimals = isNative ? 18 : token.contractInfo?.decimals
|
|
537
|
+
if (!decimals) {
|
|
538
|
+
throw new Error("Decimals not found")
|
|
539
|
+
}
|
|
540
|
+
const formattedBalance = formatRawAmount(token.balance, decimals)
|
|
532
541
|
const priceUsd = Number(token.price?.value) ?? 0
|
|
533
542
|
const balanceUsdFormatted = token.balanceUsdFormatted ?? ""
|
|
534
|
-
|
|
543
|
+
|
|
535
544
|
let isSufficientBalance = true
|
|
536
545
|
if (targetAmountUsd) {
|
|
537
546
|
isSufficientBalance = (token.balanceUsd ?? 0) >= targetAmountUsd
|