0xtrails 0.5.0 → 0.6.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/analytics.d.ts +8 -3
- package/dist/analytics.d.ts.map +1 -1
- package/dist/{ccip-DhEkQ6QC.js → ccip-Dw5AN7oU.js} +1 -1
- package/dist/cctp.d.ts +0 -149
- package/dist/cctp.d.ts.map +1 -1
- package/dist/chains.d.ts +28 -3
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/contractUtils.d.ts.map +1 -1
- package/dist/estimate.d.ts.map +1 -1
- package/dist/fees.d.ts.map +1 -1
- package/dist/gasless.d.ts +12 -0
- package/dist/gasless.d.ts.map +1 -1
- package/dist/{index-MhD2DA7_.js → index-BtVUTbEZ.js} +30984 -38945
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +108 -107
- package/dist/indexerClient.d.ts +2 -2
- package/dist/intents.d.ts +0 -17
- package/dist/intents.d.ts.map +1 -1
- package/dist/mutations.d.ts.map +1 -1
- package/dist/paymasterSend.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +1 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/sendUserOp.d.ts +0 -18
- package/dist/sendUserOp.d.ts.map +1 -1
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/tokens.d.ts +10 -8
- package/dist/tokens.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +4 -5
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +4 -5
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/standardDeposit.d.ts +2 -2
- package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/execution/transactionState.d.ts +2 -2
- package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/crossChain.d.ts +4 -4
- package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/index.d.ts +0 -1
- package/dist/transactionIntent/handlers/index.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +4 -34
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts +2 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
- package/dist/transactionIntent/types.d.ts +6 -19
- package/dist/transactionIntent/types.d.ts.map +1 -1
- package/dist/transactionIntent/utils/index.d.ts +0 -1
- package/dist/transactionIntent/utils/index.d.ts.map +1 -1
- package/dist/transactions.d.ts +2 -20
- package/dist/transactions.d.ts.map +1 -1
- package/dist/utils.d.ts +8 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/walletUtils.d.ts +21 -0
- package/dist/walletUtils.d.ts.map +1 -0
- package/dist/wallets.d.ts +33 -240
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/FeeOption.d.ts +8 -13
- package/dist/widget/components/FeeOption.d.ts.map +1 -1
- package/dist/widget/components/FeeOptions.d.ts +11 -5
- package/dist/widget/components/FeeOptions.d.ts.map +1 -1
- package/dist/widget/components/NativeGasOption.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
- package/dist/widget/components/QRCodeDeposit.d.ts +5 -0
- package/dist/widget/components/QRCodeDeposit.d.ts.map +1 -1
- package/dist/widget/components/QRCodeWalletSelect.d.ts +13 -0
- package/dist/widget/components/QRCodeWalletSelect.d.ts.map +1 -0
- package/dist/widget/components/QrCode.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Receipt.d.ts.map +1 -1
- package/dist/widget/components/ScreenHeader.d.ts +1 -1
- package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
- package/dist/widget/components/Toast.d.ts.map +1 -1
- package/dist/widget/components/TokenImage.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +1 -1
- package/dist/widget/hooks/useCheckout.d.ts +15 -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/useIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts +7 -0
- package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts.map +1 -0
- package/dist/widget/hooks/useIsSequenceWallet.d.ts +6 -0
- package/dist/widget/hooks/useIsSequenceWallet.d.ts.map +1 -0
- package/dist/widget/hooks/useQuote.d.ts +5 -8
- package/dist/widget/hooks/useQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useRecentTokens.d.ts.map +1 -1
- package/dist/widget/hooks/useSelectedFeeOption.d.ts +30 -0
- package/dist/widget/hooks/useSelectedFeeOption.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +6 -15
- 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/providers/TrailsProvider.d.ts +23 -12
- package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
- package/dist/widget/widget.d.ts +11 -0
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/analytics.ts +53 -21
- package/src/cctp.ts +0 -1016
- package/src/chains.ts +93 -39
- package/src/config.ts +24 -6
- package/src/constants.ts +1 -4
- package/src/contractUtils.ts +6 -6
- package/src/estimate.ts +3 -6
- package/src/fees.ts +5 -10
- package/src/gasless.ts +45 -0
- package/src/index.ts +7 -6
- package/src/indexerClient.ts +2 -2
- package/src/intents.ts +52 -206
- package/src/mutations.ts +3 -2
- package/src/paymasterSend.ts +2 -5
- package/src/prepareSend.ts +9 -12
- package/src/sendUserOp.ts +3 -64
- package/src/tokenBalances.ts +2 -1
- package/src/tokens.ts +62 -133
- package/src/trailsClient.ts +1 -1
- package/src/transactionIntent/deposits/depositOrchestrator.ts +14 -15
- package/src/transactionIntent/deposits/gaslessDeposit.ts +70 -100
- package/src/transactionIntent/deposits/standardDeposit.ts +22 -28
- package/src/transactionIntent/execution/transactionState.ts +2 -2
- package/src/transactionIntent/handlers/crossChain.ts +165 -385
- package/src/transactionIntent/handlers/index.ts +0 -1
- package/src/transactionIntent/handlers/sameChainSameToken.ts +228 -94
- package/src/transactionIntent/quote/normalizeQuote.ts +4 -6
- package/src/transactionIntent/quote/quoteHelpers.ts +35 -3
- package/src/transactionIntent/types.ts +6 -27
- package/src/transactionIntent/utils/index.ts +0 -1
- package/src/transactions.ts +6 -203
- package/src/umd.tsx +1 -3
- package/src/utils.ts +28 -8
- package/src/walletUtils.ts +42 -0
- package/src/wallets.ts +361 -203
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountIntentTransactionHistory.tsx +73 -4
- package/src/widget/components/AccountSettings.tsx +17 -17
- package/src/widget/components/ChainList.tsx +3 -3
- package/src/widget/components/ClassicSwap.tsx +19 -10
- package/src/widget/components/ConfigDisplay.tsx +1 -1
- package/src/widget/components/FeeOption.tsx +63 -20
- package/src/widget/components/FeeOptions.tsx +54 -123
- package/src/widget/components/NativeGasOption.tsx +3 -1
- package/src/widget/components/Pay.tsx +18 -11
- package/src/widget/components/PoolDeposit.tsx +23 -10
- package/src/widget/components/QRCodeDeposit.tsx +50 -30
- package/src/widget/components/QRCodeWalletSelect.tsx +77 -0
- package/src/widget/components/QrCode.tsx +188 -233
- package/src/widget/components/QuoteDetails.tsx +48 -2
- package/src/widget/components/Receipt.tsx +5 -2
- package/src/widget/components/ScreenHeader.tsx +10 -8
- package/src/widget/components/Toast.tsx +10 -0
- package/src/widget/components/TokenImage.tsx +56 -13
- package/src/widget/hooks/useCheckout.ts +71 -0
- package/src/widget/hooks/useCurrentScreen.tsx +1 -0
- package/src/widget/hooks/useDebugScreens.ts +5 -0
- package/src/widget/hooks/useIntentTransactionHistory.ts +788 -418
- package/src/widget/hooks/useIsConnectedWalletSmartContract.ts +43 -0
- package/src/widget/hooks/useIsSequenceWallet.ts +17 -0
- package/src/widget/hooks/useQuote.ts +16 -17
- package/src/widget/hooks/useRecentTokens.ts +2 -1
- package/src/widget/hooks/useSelectedFeeOption.tsx +257 -0
- package/src/widget/hooks/useSendForm.ts +172 -47
- package/src/widget/hooks/useTokenList.ts +15 -2
- package/src/widget/providers/TrailsProvider.tsx +53 -25
- package/src/widget/widget.tsx +119 -48
- package/dist/cctpqueue.d.ts +0 -18
- package/dist/cctpqueue.d.ts.map +0 -1
- package/dist/preconditions.d.ts +0 -12
- package/dist/preconditions.d.ts.map +0 -1
- package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +0 -62
- package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +0 -1
- package/dist/transactionIntent/utils/lifiHelpers.d.ts +0 -10
- package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +0 -1
- package/dist/widget/hooks/useSelectedFeeToken.d.ts +0 -33
- package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +0 -1
- package/src/cctpqueue.ts +0 -69
- package/src/preconditions.ts +0 -47
- package/src/transactionIntent/handlers/sameChainDifferentToken.ts +0 -323
- package/src/transactionIntent/utils/lifiHelpers.ts +0 -68
- package/src/widget/hooks/useSelectedFeeToken.tsx +0 -288
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useEffect, useState } from "react"
|
|
2
|
+
import { useAccount } from "wagmi"
|
|
3
|
+
import { isSmartContractWallet as checkIsSmartContractWallet } from "../../walletUtils.js"
|
|
4
|
+
import { logger } from "../../logger.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook to check if the currently connected wallet is a smart contract.
|
|
8
|
+
* @param chainId - The chain ID to check the wallet on
|
|
9
|
+
* @returns true if the connected wallet is a smart contract, false otherwise
|
|
10
|
+
*/
|
|
11
|
+
export function useIsConnectedWalletSmartContract(chainId?: number): boolean {
|
|
12
|
+
const { address } = useAccount()
|
|
13
|
+
const [isSmartContractWallet, setIsSmartContractWallet] = useState(false)
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const checkIfContract = async () => {
|
|
17
|
+
if (address && chainId) {
|
|
18
|
+
try {
|
|
19
|
+
const isContract = await checkIsSmartContractWallet(address, chainId)
|
|
20
|
+
setIsSmartContractWallet(isContract)
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger.console.error(
|
|
23
|
+
"[trails-sdk] Error checking if wallet is contract:",
|
|
24
|
+
error,
|
|
25
|
+
)
|
|
26
|
+
setIsSmartContractWallet(false)
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
setIsSmartContractWallet(false)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
checkIfContract()
|
|
34
|
+
}, [address, chainId])
|
|
35
|
+
|
|
36
|
+
logger.console.log("[trails-sdk] isSmartContractWallet:", {
|
|
37
|
+
isSmartContractWallet,
|
|
38
|
+
address,
|
|
39
|
+
chainId,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
return isSmartContractWallet
|
|
43
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useMemo } from "react"
|
|
2
|
+
import { useAccount } from "wagmi"
|
|
3
|
+
import { isSequenceWallet as checkIsSequenceWallet } from "../../walletUtils.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook to check if the currently connected wallet is a Sequence wallet.
|
|
7
|
+
* @returns true if the connected wallet is a Sequence wallet, false otherwise
|
|
8
|
+
*/
|
|
9
|
+
export function useIsSequenceWallet(): boolean {
|
|
10
|
+
const { connector } = useAccount()
|
|
11
|
+
|
|
12
|
+
const isSequenceWallet = useMemo(() => {
|
|
13
|
+
return checkIsSequenceWallet(connector)
|
|
14
|
+
}, [connector])
|
|
15
|
+
|
|
16
|
+
return isSequenceWallet
|
|
17
|
+
}
|
|
@@ -14,10 +14,8 @@ import { prepareSend } from "../../prepareSend.js"
|
|
|
14
14
|
import { abortControllerRegistry } from "../../abortController.js"
|
|
15
15
|
import { TradeType } from "../../prepareSend.js"
|
|
16
16
|
import { getChainInfo } from "../../chains.js"
|
|
17
|
-
import type {
|
|
18
|
-
|
|
19
|
-
PrepareSendOptions,
|
|
20
|
-
} from "../../transactionIntent/types.js"
|
|
17
|
+
import type { IntentTransaction } from "@0xsequence/trails-api"
|
|
18
|
+
import type { PrepareSendOptions } from "../../transactionIntent/types.js"
|
|
21
19
|
import type { Chain } from "../../chains.js"
|
|
22
20
|
import type { SupportedToken } from "../../tokens.js"
|
|
23
21
|
import type { TransactionState } from "../../transactions.js"
|
|
@@ -25,6 +23,7 @@ import type { PrepareSendFees } from "../../prepareSend.js"
|
|
|
25
23
|
import { getTokenPrice } from "../../prices.js"
|
|
26
24
|
import { useCommitIntent, useExecuteIntent } from "../../mutations.js"
|
|
27
25
|
import type { CheckoutOnHandlers } from "./useCheckout.js"
|
|
26
|
+
import type { FeeOption } from "@0xsequence/trails-api"
|
|
28
27
|
|
|
29
28
|
export type UseQuoteProps = {
|
|
30
29
|
walletClient?: any // TODO: fix this, has to do with viem/wagmi versions
|
|
@@ -69,10 +68,7 @@ export type UseQuoteProps = {
|
|
|
69
68
|
*/
|
|
70
69
|
checkoutOnHandlers?: Partial<CheckoutOnHandlers>
|
|
71
70
|
paymasterUrl?: string
|
|
72
|
-
|
|
73
|
-
tokenAddress: string
|
|
74
|
-
tokenSymbol?: string
|
|
75
|
-
} | null
|
|
71
|
+
selectedFeeOption?: FeeOption | null
|
|
76
72
|
abortSignal?: AbortSignal
|
|
77
73
|
apiKey?: string | null
|
|
78
74
|
nodeGatewayEnv?: "prod" | "dev" | "local" | "cors-anywhere"
|
|
@@ -82,12 +78,12 @@ export type SwapReturn = {
|
|
|
82
78
|
originTransaction: {
|
|
83
79
|
transactionHash?: string | null
|
|
84
80
|
explorerUrl?: string | null
|
|
85
|
-
receipt: TransactionReceipt |
|
|
81
|
+
receipt: TransactionReceipt | IntentTransaction | null
|
|
86
82
|
}
|
|
87
83
|
destinationTransaction: {
|
|
88
84
|
transactionHash?: string | null
|
|
89
85
|
explorerUrl?: string | null
|
|
90
|
-
receipt:
|
|
86
|
+
receipt: IntentTransaction | null
|
|
91
87
|
}
|
|
92
88
|
totalCompletionSeconds?: number
|
|
93
89
|
}
|
|
@@ -149,7 +145,7 @@ export function useQuote({
|
|
|
149
145
|
checkoutOnHandlers,
|
|
150
146
|
quoteProvider,
|
|
151
147
|
paymasterUrl,
|
|
152
|
-
|
|
148
|
+
selectedFeeOption,
|
|
153
149
|
nodeGatewayEnv,
|
|
154
150
|
abortSignal: externalAbortSignal,
|
|
155
151
|
apiKey,
|
|
@@ -183,6 +179,8 @@ export function useQuote({
|
|
|
183
179
|
: abortControllerRef.current.signal
|
|
184
180
|
|
|
185
181
|
const { trailsApiKey, trailsApiUrl, sequenceIndexerUrl } = useTrails()
|
|
182
|
+
// trailsApiKey is the same as sequenceProjectAccessKey
|
|
183
|
+
const sequenceProjectAccessKey = trailsApiKey
|
|
186
184
|
|
|
187
185
|
const trailsClient = useTrailsClient({
|
|
188
186
|
apiKey: trailsApiKey,
|
|
@@ -375,13 +373,14 @@ export function useQuote({
|
|
|
375
373
|
slippageTolerance: slippageTolerance?.toString(),
|
|
376
374
|
quoteProvider: quoteProvider,
|
|
377
375
|
paymasterUrl: paymasterUrl,
|
|
378
|
-
|
|
376
|
+
selectedFeeOption: selectedFeeOption ?? null,
|
|
379
377
|
abortSignal: combinedAbortSignal,
|
|
380
378
|
originNativeTokenPriceUsd: originNativeTokenPriceUsd,
|
|
381
379
|
commitIntentFn: commitIntentMutation.mutateAsync,
|
|
382
380
|
executeIntentFn: executeIntentMutation.mutateAsync,
|
|
383
381
|
checkoutOnHandlers,
|
|
384
382
|
sequenceIndexerUrl,
|
|
383
|
+
sequenceProjectAccessKey,
|
|
385
384
|
}
|
|
386
385
|
|
|
387
386
|
logger.console.log("[trails-sdk] options", options)
|
|
@@ -418,10 +417,10 @@ export function useQuote({
|
|
|
418
417
|
const swap = async (): Promise<SwapReturn> => {
|
|
419
418
|
const {
|
|
420
419
|
depositUserTxnReceipt,
|
|
421
|
-
|
|
420
|
+
destinationIntentTransaction,
|
|
422
421
|
totalCompletionSeconds,
|
|
423
422
|
} = await send({
|
|
424
|
-
|
|
423
|
+
selectedFeeOption: selectedFeeOption ?? null,
|
|
425
424
|
})
|
|
426
425
|
|
|
427
426
|
return {
|
|
@@ -434,12 +433,12 @@ export function useQuote({
|
|
|
434
433
|
receipt: depositUserTxnReceipt,
|
|
435
434
|
},
|
|
436
435
|
destinationTransaction: {
|
|
437
|
-
transactionHash:
|
|
436
|
+
transactionHash: destinationIntentTransaction?.txnHash,
|
|
438
437
|
explorerUrl: getExplorerUrl({
|
|
439
|
-
txHash:
|
|
438
|
+
txHash: destinationIntentTransaction?.txnHash as string,
|
|
440
439
|
chainId: toChainId,
|
|
441
440
|
}),
|
|
442
|
-
receipt:
|
|
441
|
+
receipt: destinationIntentTransaction,
|
|
443
442
|
},
|
|
444
443
|
totalCompletionSeconds,
|
|
445
444
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { JsonEncode } from "@0xsequence/trails-api"
|
|
1
2
|
import { useState, useEffect, useCallback } from "react"
|
|
2
3
|
import type { SupportedToken } from "../../tokens.js"
|
|
3
4
|
import { logger } from "../../logger.js"
|
|
@@ -66,7 +67,7 @@ export function useRecentTokens(accountAddress?: string) {
|
|
|
66
67
|
|
|
67
68
|
// Update storage
|
|
68
69
|
allRecentTokens[accountKey] = accountTokens
|
|
69
|
-
localStorage.setItem(RECENT_TOKENS_KEY,
|
|
70
|
+
localStorage.setItem(RECENT_TOKENS_KEY, JsonEncode(allRecentTokens))
|
|
70
71
|
|
|
71
72
|
// Update state
|
|
72
73
|
setRecentTokens(accountTokens)
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useState,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
type ReactNode,
|
|
9
|
+
} from "react"
|
|
10
|
+
import type { FeeOption } from "@0xsequence/trails-api"
|
|
11
|
+
import { formatRawAmount, formatUsdAmountDisplay } from "../../tokenBalances.js"
|
|
12
|
+
import { logger } from "../../index.js"
|
|
13
|
+
import { isNativeToken, normalizeAddress } from "../../utils.js"
|
|
14
|
+
import { isAddressEqual } from "viem"
|
|
15
|
+
|
|
16
|
+
const createBalanceKey = (
|
|
17
|
+
chainId?: number,
|
|
18
|
+
tokenAddress?: string | null,
|
|
19
|
+
): string =>
|
|
20
|
+
`${chainId ?? "unknown"}:${
|
|
21
|
+
!tokenAddress || isNativeToken(tokenAddress)
|
|
22
|
+
? "native"
|
|
23
|
+
: normalizeAddress(tokenAddress)
|
|
24
|
+
}`
|
|
25
|
+
|
|
26
|
+
const safeFormatAmountWithSymbol = (option: FeeOption): string => {
|
|
27
|
+
const tokenDecimals =
|
|
28
|
+
typeof option.tokenDecimals === "number" &&
|
|
29
|
+
option.tokenDecimals > 0 &&
|
|
30
|
+
option.tokenDecimals <= 18
|
|
31
|
+
? option.tokenDecimals
|
|
32
|
+
: isNativeToken(option.tokenAddress)
|
|
33
|
+
? 18
|
|
34
|
+
: undefined
|
|
35
|
+
|
|
36
|
+
if (tokenDecimals === undefined) {
|
|
37
|
+
logger.console.warn("[trails-sdk] [FEE-OPTIONS] Missing token decimals", {
|
|
38
|
+
tokenAddress: option.tokenAddress,
|
|
39
|
+
tokenSymbol: option.tokenSymbol,
|
|
40
|
+
})
|
|
41
|
+
return option.tokenSymbol ? `-- ${option.tokenSymbol}` : "--"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const formattedAmount = formatRawAmount(option.amount, tokenDecimals)
|
|
46
|
+
return option.tokenSymbol
|
|
47
|
+
? `${formattedAmount} ${option.tokenSymbol}`
|
|
48
|
+
: formattedAmount
|
|
49
|
+
} catch (error) {
|
|
50
|
+
logger.console.warn(
|
|
51
|
+
"[trails-sdk] [FEE-OPTIONS] Failed to format fee option amount",
|
|
52
|
+
{
|
|
53
|
+
option,
|
|
54
|
+
tokenDecimals,
|
|
55
|
+
error,
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
return option.tokenSymbol ? `-- ${option.tokenSymbol}` : "--"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type ProcessedFeeOption = FeeOption & {
|
|
63
|
+
notEnoughBalance?: boolean
|
|
64
|
+
tokenImageUrl?: string
|
|
65
|
+
chainId?: number
|
|
66
|
+
amountFormatted: string
|
|
67
|
+
amountUsdDisplay?: string
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Token type with balance information
|
|
71
|
+
export interface TokenWithBalance {
|
|
72
|
+
chainId: number
|
|
73
|
+
contractAddress?: string
|
|
74
|
+
balance?: string
|
|
75
|
+
imageUrl?: string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface SelectedFeeOptionContextType {
|
|
79
|
+
selectedFeeOption: FeeOption | null
|
|
80
|
+
setSelectedFeeOption: (token: FeeOption | null) => void
|
|
81
|
+
clearSelectedFeeOption: () => void
|
|
82
|
+
processedFeeOptions: ProcessedFeeOption[]
|
|
83
|
+
setFeeOptions: (
|
|
84
|
+
feeOptions: ReadonlyArray<FeeOption> | null | undefined,
|
|
85
|
+
originChainId: number | undefined,
|
|
86
|
+
availableTokens: TokenWithBalance[],
|
|
87
|
+
) => void
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const SelectedFeeOptionContext = createContext<
|
|
91
|
+
SelectedFeeOptionContextType | undefined
|
|
92
|
+
>(undefined)
|
|
93
|
+
|
|
94
|
+
interface SelectedFeeOptionProviderProps {
|
|
95
|
+
children: ReactNode
|
|
96
|
+
initialToken?: FeeOption | null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const SelectedFeeOptionProvider: React.FC<
|
|
100
|
+
SelectedFeeOptionProviderProps
|
|
101
|
+
> = ({ children, initialToken = null }) => {
|
|
102
|
+
const [selectedFeeOption, setSelectedFeeOptionInternalRaw] =
|
|
103
|
+
useState<FeeOption | null>(initialToken)
|
|
104
|
+
const [hasUserSelectedFeeOption, setHasUserSelectedFeeOption] =
|
|
105
|
+
useState(false)
|
|
106
|
+
const [feeOptions, setFeeOptionsInternal] = useState<FeeOption[]>([])
|
|
107
|
+
const [originTokenChainId, setOriginTokenChainId] = useState<
|
|
108
|
+
number | undefined
|
|
109
|
+
>(undefined)
|
|
110
|
+
const [availableTokens, setAvailableTokens] = useState<TokenWithBalance[]>([])
|
|
111
|
+
|
|
112
|
+
const tokenBalanceLookup = useMemo(() => {
|
|
113
|
+
const map = new Map<string, TokenWithBalance>()
|
|
114
|
+
for (const token of availableTokens) {
|
|
115
|
+
map.set(
|
|
116
|
+
createBalanceKey(token.chainId, token.contractAddress ?? null),
|
|
117
|
+
token,
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
return map
|
|
121
|
+
}, [availableTokens])
|
|
122
|
+
|
|
123
|
+
// Wrapper to track when user makes an explicit selection
|
|
124
|
+
const setSelectedFeeOption = useCallback((token: FeeOption | null) => {
|
|
125
|
+
setSelectedFeeOptionInternalRaw(token)
|
|
126
|
+
setHasUserSelectedFeeOption(true)
|
|
127
|
+
}, [])
|
|
128
|
+
|
|
129
|
+
const clearSelectedFeeOption = useCallback(() => {
|
|
130
|
+
setSelectedFeeOptionInternalRaw(null)
|
|
131
|
+
setHasUserSelectedFeeOption(false)
|
|
132
|
+
}, [])
|
|
133
|
+
|
|
134
|
+
// Process raw fee options with balance checks
|
|
135
|
+
const processedFeeOptions: ProcessedFeeOption[] = useMemo(() => {
|
|
136
|
+
if (!feeOptions.length) {
|
|
137
|
+
return []
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return feeOptions
|
|
141
|
+
.map((option) => {
|
|
142
|
+
const balanceRecord =
|
|
143
|
+
originTokenChainId !== undefined
|
|
144
|
+
? tokenBalanceLookup.get(
|
|
145
|
+
createBalanceKey(originTokenChainId, option.tokenAddress),
|
|
146
|
+
)
|
|
147
|
+
: undefined
|
|
148
|
+
|
|
149
|
+
const hasBalanceInfo =
|
|
150
|
+
Boolean(balanceRecord?.balance) && originTokenChainId !== undefined
|
|
151
|
+
const userBalance = balanceRecord?.balance ?? "0"
|
|
152
|
+
const requiredAmount = option.amount ? option.amount.toString() : "0"
|
|
153
|
+
const notEnoughBalance = hasBalanceInfo
|
|
154
|
+
? BigInt(userBalance) < BigInt(requiredAmount)
|
|
155
|
+
: true
|
|
156
|
+
const amountFormatted = safeFormatAmountWithSymbol(option)
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
...option,
|
|
160
|
+
notEnoughBalance,
|
|
161
|
+
tokenImageUrl: balanceRecord?.imageUrl,
|
|
162
|
+
chainId: balanceRecord?.chainId ?? originTokenChainId,
|
|
163
|
+
amountUsdDisplay: formatUsdAmountDisplay(option.amountUsd ?? 0),
|
|
164
|
+
amountFormatted,
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
.sort(
|
|
168
|
+
(a, b) => Number(!!a.notEnoughBalance) - Number(!!b.notEnoughBalance),
|
|
169
|
+
)
|
|
170
|
+
}, [feeOptions, originTokenChainId, tokenBalanceLookup])
|
|
171
|
+
|
|
172
|
+
// Auto-select first fee option (ERC20 or native gas) with sufficient balance
|
|
173
|
+
// NOTE: If sufficient balance, it will usually resolve to the native gas option because native gas is the first option in the list returned by the API
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
if (processedFeeOptions.length === 0) {
|
|
176
|
+
if (selectedFeeOption !== null) {
|
|
177
|
+
setSelectedFeeOptionInternalRaw(null)
|
|
178
|
+
setHasUserSelectedFeeOption(false)
|
|
179
|
+
}
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const selectionStillValid = selectedFeeOption
|
|
184
|
+
? processedFeeOptions.some((option) =>
|
|
185
|
+
isAddressEqual(
|
|
186
|
+
option.tokenAddress as `0x${string}`,
|
|
187
|
+
selectedFeeOption.tokenAddress as `0x${string}`,
|
|
188
|
+
),
|
|
189
|
+
)
|
|
190
|
+
: false
|
|
191
|
+
|
|
192
|
+
if (selectedFeeOption && !selectionStillValid) {
|
|
193
|
+
setSelectedFeeOptionInternalRaw(null)
|
|
194
|
+
setHasUserSelectedFeeOption(false)
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!hasUserSelectedFeeOption && !selectedFeeOption) {
|
|
199
|
+
const fallbackOption =
|
|
200
|
+
processedFeeOptions.find((option) => !option.notEnoughBalance) ??
|
|
201
|
+
processedFeeOptions[0] ??
|
|
202
|
+
null
|
|
203
|
+
|
|
204
|
+
// Only fallback if the fallback option is erc20
|
|
205
|
+
if (fallbackOption && !isNativeToken(fallbackOption.tokenAddress)) {
|
|
206
|
+
setSelectedFeeOptionInternalRaw(fallbackOption)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}, [processedFeeOptions, selectedFeeOption, hasUserSelectedFeeOption])
|
|
210
|
+
|
|
211
|
+
// Function to set raw fee options and process them
|
|
212
|
+
const setFeeOptions = useCallback(
|
|
213
|
+
(
|
|
214
|
+
feeOptions: ReadonlyArray<FeeOption> | null | undefined,
|
|
215
|
+
originChainId: number | undefined,
|
|
216
|
+
tokens: TokenWithBalance[],
|
|
217
|
+
) => {
|
|
218
|
+
const normalized = feeOptions ? [...feeOptions] : []
|
|
219
|
+
|
|
220
|
+
setFeeOptionsInternal(normalized)
|
|
221
|
+
setOriginTokenChainId(originChainId)
|
|
222
|
+
setAvailableTokens(tokens)
|
|
223
|
+
|
|
224
|
+
if (normalized.length === 0) {
|
|
225
|
+
setSelectedFeeOptionInternalRaw(null)
|
|
226
|
+
setHasUserSelectedFeeOption(false)
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
[],
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
const value: SelectedFeeOptionContextType = {
|
|
233
|
+
selectedFeeOption,
|
|
234
|
+
setSelectedFeeOption,
|
|
235
|
+
clearSelectedFeeOption,
|
|
236
|
+
processedFeeOptions,
|
|
237
|
+
setFeeOptions,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<SelectedFeeOptionContext.Provider value={value}>
|
|
242
|
+
{children}
|
|
243
|
+
</SelectedFeeOptionContext.Provider>
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const useSelectedFeeOption = (): SelectedFeeOptionContextType => {
|
|
248
|
+
const context = useContext(SelectedFeeOptionContext)
|
|
249
|
+
|
|
250
|
+
if (context === undefined) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
"useSelectedFeeOption must be used within a SelectedFeeOptionProvider",
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return context
|
|
257
|
+
}
|