0xtrails 0.7.0 → 0.8.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/{ccip-fConRNoG.js → ccip-uMWNlvmJ.js} +34 -34
- package/dist/fees.d.ts.map +1 -1
- package/dist/{index-BbajxCG_.js → index-BiPwqVkZ.js} +31527 -28874
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +478 -456
- package/dist/intents.d.ts +10 -4
- package/dist/intents.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +1 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/prices.d.ts +2 -2
- package/dist/prices.d.ts.map +1 -1
- package/dist/refund.d.ts +116 -0
- package/dist/refund.d.ts.map +1 -0
- package/dist/tokenBalances.d.ts +1 -1
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/crossChain.d.ts +4 -3
- package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +3 -3
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts +1 -2
- package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts +3 -3
- package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
- package/dist/transactionIntent/types.d.ts +5 -4
- package/dist/transactionIntent/types.d.ts.map +1 -1
- package/dist/transactions.d.ts +4 -0
- package/dist/transactions.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts +2 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/Earn.d.ts +2 -1
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/ErrorDisplay.d.ts.map +1 -1
- package/dist/widget/components/Fund.d.ts +2 -1
- package/dist/widget/components/Fund.d.ts.map +1 -1
- package/dist/widget/components/FundSwap.d.ts +2 -1
- package/dist/widget/components/FundSwap.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts +2 -1
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PoolDeposit.d.ts +2 -1
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts +1 -0
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Swap.d.ts +2 -1
- package/dist/widget/components/Swap.d.ts.map +1 -1
- package/dist/widget/components/TokenImage.d.ts.map +1 -1
- package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +1 -1
- package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
- package/dist/widget/hooks/useGetIntent.d.ts +18 -0
- package/dist/widget/hooks/useGetIntent.d.ts.map +1 -0
- package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/useQuote.d.ts +10 -7
- package/dist/widget/hooks/useQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useSendForm.d.ts +3 -2
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/hooks/useTrailsSendTransaction.d.ts.map +1 -1
- package/dist/widget/index.js +3 -3
- package/dist/widget/widget.d.ts +2 -1
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +5 -12
- package/src/fees.ts +8 -2
- package/src/index.ts +33 -1
- package/src/intents.ts +34 -7
- package/src/prepareSend.ts +6 -4
- package/src/prices.ts +6 -6
- package/src/refund.ts +914 -0
- package/src/tokenBalances.ts +4 -14
- package/src/transactionIntent/handlers/crossChain.ts +21 -10
- package/src/transactionIntent/handlers/sameChainSameToken.ts +12 -8
- package/src/transactionIntent/quote/normalizeQuote.ts +29 -27
- package/src/transactionIntent/quote/quoteHelpers.ts +5 -9
- package/src/transactionIntent/types.ts +5 -3
- package/src/transactions.ts +5 -0
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountIntentTransactionHistory.tsx +197 -5
- package/src/widget/components/ClassicSwap.tsx +6 -3
- package/src/widget/components/Earn.tsx +6 -3
- package/src/widget/components/ErrorDisplay.tsx +6 -4
- package/src/widget/components/Fund.tsx +6 -3
- package/src/widget/components/FundSwap.tsx +2 -1
- package/src/widget/components/Pay.tsx +15 -7
- package/src/widget/components/PoolDeposit.tsx +6 -3
- package/src/widget/components/QuoteDetails.tsx +34 -38
- package/src/widget/components/Swap.tsx +2 -1
- package/src/widget/components/TokenImage.tsx +3 -1
- package/src/widget/components/TransactionDetails.tsx +108 -0
- package/src/widget/hooks/useAmountUsd.ts +0 -3
- package/src/widget/hooks/useDefaultTokenSelection.tsx +0 -3
- package/src/widget/hooks/useGetIntent.ts +53 -0
- package/src/widget/hooks/useIntentTransactionHistory.ts +85 -3
- package/src/widget/hooks/useQuote.ts +16 -10
- package/src/widget/hooks/useSendForm.ts +30 -15
- package/src/widget/hooks/useTokenList.ts +2 -4
- package/src/widget/hooks/useTrailsSendTransaction.ts +2 -1
- package/src/widget/widget.tsx +12 -6
- package/dist/sequenceWallet.d.ts +0 -67
- package/dist/sequenceWallet.d.ts.map +0 -1
- package/src/sequenceWallet.ts +0 -532
|
@@ -4,6 +4,7 @@ import { ChainImage } from "./ChainImage.js"
|
|
|
4
4
|
import { getCommonTokenImageUrl } from "../../tokens.js"
|
|
5
5
|
import { logger } from "../../logger.js"
|
|
6
6
|
import { commonTokenImages } from "../../tokens.js"
|
|
7
|
+
import { useChainInfo } from "../../chains.js"
|
|
7
8
|
|
|
8
9
|
function normalizeImageUrl(
|
|
9
10
|
imageUrl?: string | null,
|
|
@@ -51,6 +52,7 @@ export const TokenImage: React.FC<TokenImageProps> = ({
|
|
|
51
52
|
height: number
|
|
52
53
|
} | null>(null)
|
|
53
54
|
const previousUrlRef = useRef<string | null>(null)
|
|
55
|
+
const chainInfo = useChainInfo(chainId || undefined)
|
|
54
56
|
|
|
55
57
|
const effectiveImageUrl = useMemo<string | null>(() => {
|
|
56
58
|
return normalizeImageUrl(imageUrl, symbol, chainId, contractAddress)
|
|
@@ -118,7 +120,7 @@ export const TokenImage: React.FC<TokenImageProps> = ({
|
|
|
118
120
|
height: size,
|
|
119
121
|
backgroundColor: symbol === "ETH" ? "#627eeb" : undefined,
|
|
120
122
|
}}
|
|
121
|
-
title={`${symbol}${chainId ? ` on chain ${chainId}` : ""}`}
|
|
123
|
+
title={`${symbol}${chainId ? ` on ${chainInfo?.name || "chain"} (${chainId})` : ""}`}
|
|
122
124
|
>
|
|
123
125
|
{shouldShowText ? (
|
|
124
126
|
<div
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type React from "react"
|
|
2
|
+
import { useState } from "react"
|
|
2
3
|
import type { IntentTransaction } from "../../transactions.js"
|
|
3
4
|
import { TokenImage } from "./TokenImage.js"
|
|
4
5
|
import { ChainImage } from "./ChainImage.js"
|
|
5
6
|
import { getExplorerUrlForAddress } from "../../explorer.js"
|
|
6
7
|
import { truncateAddress } from "../../utils.js"
|
|
7
8
|
import { formatRawAmount } from "../../tokenBalances.js"
|
|
9
|
+
import { logger } from "../../logger.js"
|
|
8
10
|
|
|
9
11
|
interface TransactionDetailsProps {
|
|
10
12
|
transaction: IntentTransaction
|
|
@@ -15,8 +17,114 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
|
|
|
15
17
|
transaction,
|
|
16
18
|
className = "",
|
|
17
19
|
}) => {
|
|
20
|
+
const [copiedIntentId, setCopiedIntentId] = useState<string | null>(null)
|
|
21
|
+
|
|
22
|
+
const handleCopyIntentId = async (intentId: string, e: React.MouseEvent) => {
|
|
23
|
+
e.preventDefault()
|
|
24
|
+
e.stopPropagation()
|
|
25
|
+
try {
|
|
26
|
+
await navigator.clipboard.writeText(intentId)
|
|
27
|
+
setCopiedIntentId(intentId)
|
|
28
|
+
setTimeout(() => setCopiedIntentId(null), 2000) // Clear after 2 seconds
|
|
29
|
+
logger.console.log(
|
|
30
|
+
"[trails-sdk] Successfully copied intent ID:",
|
|
31
|
+
intentId,
|
|
32
|
+
)
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.console.error("[trails-sdk] Failed to copy intent ID:", error)
|
|
35
|
+
// Fallback for older browsers or when clipboard API is not available
|
|
36
|
+
try {
|
|
37
|
+
const textArea = document.createElement("textarea")
|
|
38
|
+
textArea.value = intentId
|
|
39
|
+
document.body.appendChild(textArea)
|
|
40
|
+
textArea.select()
|
|
41
|
+
document.execCommand("copy")
|
|
42
|
+
document.body.removeChild(textArea)
|
|
43
|
+
setCopiedIntentId(intentId)
|
|
44
|
+
setTimeout(() => setCopiedIntentId(null), 2000)
|
|
45
|
+
logger.console.log(
|
|
46
|
+
"[trails-sdk] Successfully copied intent ID using fallback:",
|
|
47
|
+
intentId,
|
|
48
|
+
)
|
|
49
|
+
} catch (fallbackError) {
|
|
50
|
+
logger.console.error(
|
|
51
|
+
"[trails-sdk] Fallback copy also failed:",
|
|
52
|
+
fallbackError,
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
18
58
|
return (
|
|
19
59
|
<div className={`space-y-2 text-sm ${className}`}>
|
|
60
|
+
{/* Intent ID */}
|
|
61
|
+
{transaction.intentId && (
|
|
62
|
+
<div className="flex justify-between items-center">
|
|
63
|
+
<span className="text-xs text-gray-600 dark:text-gray-400">
|
|
64
|
+
Intent ID:
|
|
65
|
+
</span>
|
|
66
|
+
<div className="flex items-center gap-1">
|
|
67
|
+
<span className="text-xs font-mono text-gray-900 dark:text-gray-100">
|
|
68
|
+
{truncateAddress(transaction.intentId)}
|
|
69
|
+
</span>
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
onClick={(e) => {
|
|
73
|
+
if (transaction.intentId) {
|
|
74
|
+
handleCopyIntentId(transaction.intentId, e)
|
|
75
|
+
}
|
|
76
|
+
}}
|
|
77
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
78
|
+
onMouseUp={(e) => e.stopPropagation()}
|
|
79
|
+
className={`p-0.5 rounded transition-all duration-200 cursor-pointer z-10 relative ${
|
|
80
|
+
copiedIntentId === transaction.intentId
|
|
81
|
+
? "bg-green-100 dark:bg-green-900/30"
|
|
82
|
+
: "hover:bg-gray-200 dark:hover:bg-gray-600"
|
|
83
|
+
}`}
|
|
84
|
+
title={
|
|
85
|
+
copiedIntentId === transaction.intentId
|
|
86
|
+
? "Copied!"
|
|
87
|
+
: "Copy full intent ID"
|
|
88
|
+
}
|
|
89
|
+
>
|
|
90
|
+
{copiedIntentId === transaction.intentId ? (
|
|
91
|
+
<svg
|
|
92
|
+
className="w-3 h-3 text-green-600 dark:text-green-400"
|
|
93
|
+
fill="none"
|
|
94
|
+
viewBox="0 0 24 24"
|
|
95
|
+
stroke="currentColor"
|
|
96
|
+
aria-label="Copied"
|
|
97
|
+
>
|
|
98
|
+
<title>Copied</title>
|
|
99
|
+
<path
|
|
100
|
+
strokeLinecap="round"
|
|
101
|
+
strokeLinejoin="round"
|
|
102
|
+
strokeWidth={2}
|
|
103
|
+
d="M5 13l4 4L19 7"
|
|
104
|
+
/>
|
|
105
|
+
</svg>
|
|
106
|
+
) : (
|
|
107
|
+
<svg
|
|
108
|
+
className="w-3 h-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
|
109
|
+
fill="none"
|
|
110
|
+
viewBox="0 0 24 24"
|
|
111
|
+
stroke="currentColor"
|
|
112
|
+
aria-label="Copy"
|
|
113
|
+
>
|
|
114
|
+
<title>Copy</title>
|
|
115
|
+
<path
|
|
116
|
+
strokeLinecap="round"
|
|
117
|
+
strokeLinejoin="round"
|
|
118
|
+
strokeWidth={2}
|
|
119
|
+
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
120
|
+
/>
|
|
121
|
+
</svg>
|
|
122
|
+
)}
|
|
123
|
+
</button>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
|
|
20
128
|
{/* Origin Intent */}
|
|
21
129
|
{transaction.originIntentAddress && transaction.originToken && (
|
|
22
130
|
<div className="flex justify-between items-center">
|
|
@@ -2,7 +2,6 @@ import { useQuery } from "@tanstack/react-query"
|
|
|
2
2
|
import { useTokenPrice } from "../../prices.js"
|
|
3
3
|
import { formatUsdAmountDisplay } from "../../tokenBalances.js"
|
|
4
4
|
import { useTokenAddress } from "../../tokens.js"
|
|
5
|
-
import { useTrailsClient } from "../../trailsClient.js"
|
|
6
5
|
|
|
7
6
|
type UseAmountUsdProps = {
|
|
8
7
|
amount?: string | null
|
|
@@ -14,7 +13,6 @@ export function useAmountUsd({ amount, token, chainId }: UseAmountUsdProps): {
|
|
|
14
13
|
amountUsd: number | null
|
|
15
14
|
amountUsdFormatted: string
|
|
16
15
|
} {
|
|
17
|
-
const trailsClient = useTrailsClient()
|
|
18
16
|
const isTokenAddress = token?.startsWith("0x")
|
|
19
17
|
const resolvedTokenAddress = useTokenAddress({
|
|
20
18
|
chainId,
|
|
@@ -30,7 +28,6 @@ export function useAmountUsd({ amount, token, chainId }: UseAmountUsdProps): {
|
|
|
30
28
|
chainId: Number(chainId),
|
|
31
29
|
}
|
|
32
30
|
: null,
|
|
33
|
-
trailsClient,
|
|
34
31
|
)
|
|
35
32
|
|
|
36
33
|
const { data: amountUsd } = useQuery({
|
|
@@ -9,7 +9,6 @@ import { useTargetAmount } from "./useTargetAmount.js"
|
|
|
9
9
|
import { useTokenBalances } from "../../tokenBalances.js"
|
|
10
10
|
import type { Token } from "../../tokens.js"
|
|
11
11
|
import { isNativeToken } from "../../utils.js"
|
|
12
|
-
import { useIndexerGatewayClient } from "../../indexerClient.js"
|
|
13
12
|
import { useSupportedTokens } from "../../tokens.js"
|
|
14
13
|
import { getChainInfo } from "../../chains.js"
|
|
15
14
|
import { logger } from "../../logger.js"
|
|
@@ -99,11 +98,9 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
99
98
|
const { toToken, toChainId } = useWidgetProps()
|
|
100
99
|
const { targetAmountUsd } = useTargetAmount()
|
|
101
100
|
const { address } = useAccount()
|
|
102
|
-
const indexerGatewayClient = useIndexerGatewayClient()
|
|
103
101
|
|
|
104
102
|
const { sortedTokens, isLoadingSortedTokens } = useTokenBalances(
|
|
105
103
|
address as Address.Address,
|
|
106
|
-
indexerGatewayClient,
|
|
107
104
|
)
|
|
108
105
|
|
|
109
106
|
const { supportedTokens, isLoadingTokens: isLoadingSupportedTokens } =
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query"
|
|
2
|
+
import { useTrailsClient } from "../../trailsClient.js"
|
|
3
|
+
import { getIntent } from "../../intents.js"
|
|
4
|
+
import { useTrails } from "../providers/TrailsProvider.js"
|
|
5
|
+
|
|
6
|
+
export interface UseGetIntentParams {
|
|
7
|
+
intentId: string | undefined
|
|
8
|
+
enabled?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface UseGetIntentReturn {
|
|
12
|
+
intent: import("@0xtrails/api").Intent | null
|
|
13
|
+
isLoading: boolean
|
|
14
|
+
isError: boolean
|
|
15
|
+
error: Error | null
|
|
16
|
+
refetch: () => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hook to fetch intent data from the Trails API
|
|
21
|
+
* @param params - Object containing intentId and optional enabled flag
|
|
22
|
+
* @returns Intent data, loading state, error state, and refetch function
|
|
23
|
+
*/
|
|
24
|
+
export function useGetIntent({
|
|
25
|
+
intentId,
|
|
26
|
+
enabled = true,
|
|
27
|
+
}: UseGetIntentParams): UseGetIntentReturn {
|
|
28
|
+
const trailsConfig = useTrails()
|
|
29
|
+
const trailsClient = useTrailsClient()
|
|
30
|
+
|
|
31
|
+
const { data, isLoading, isError, error, refetch } = useQuery({
|
|
32
|
+
queryKey: ["getIntent", intentId],
|
|
33
|
+
queryFn: async () => {
|
|
34
|
+
if (!intentId || !trailsConfig.trailsApiKey) {
|
|
35
|
+
throw new Error("Intent ID and API key required")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const response = await getIntent(trailsClient, intentId)
|
|
39
|
+
return response.intent
|
|
40
|
+
},
|
|
41
|
+
enabled: enabled && !!intentId && !!trailsConfig.trailsApiKey,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
intent: data ?? null,
|
|
46
|
+
isLoading,
|
|
47
|
+
isError,
|
|
48
|
+
error: error as Error | null,
|
|
49
|
+
refetch: () => {
|
|
50
|
+
refetch()
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -39,6 +39,8 @@ function mapApiStatusToUiStatus(apiStatus: string): string {
|
|
|
39
39
|
return "failed"
|
|
40
40
|
case "CREATED":
|
|
41
41
|
return "created"
|
|
42
|
+
case "ABORTED":
|
|
43
|
+
return "aborted"
|
|
42
44
|
default:
|
|
43
45
|
return apiStatus?.toLowerCase() || "unknown"
|
|
44
46
|
}
|
|
@@ -50,16 +52,55 @@ function mapApiStatusToUiStatus(apiStatus: string): string {
|
|
|
50
52
|
function mapIntentSummaryToTransaction(
|
|
51
53
|
intent: IntentSummary,
|
|
52
54
|
): IntentTransaction {
|
|
55
|
+
// Determine effective status based on transaction status fields
|
|
56
|
+
// Priority: destinationTransactionStatus > originTransactionStatus > depositTransactionStatus > main status
|
|
57
|
+
let effectiveStatus = mapApiStatusToUiStatus(intent.status)
|
|
58
|
+
|
|
59
|
+
const depositStatus = intent.depositTransactionStatus
|
|
60
|
+
const originStatus = intent.originTransactionStatus
|
|
61
|
+
const destinationStatus = intent.destinationTransactionStatus
|
|
62
|
+
|
|
63
|
+
// If destinationTransactionStatus exists and is not ABORTED, use it
|
|
64
|
+
if (destinationStatus && destinationStatus !== "ABORTED") {
|
|
65
|
+
effectiveStatus = mapApiStatusToUiStatus(destinationStatus)
|
|
66
|
+
}
|
|
67
|
+
// If destinationTransactionStatus doesn't exist but originTransactionStatus exists and is not ABORTED, use it
|
|
68
|
+
else if (!destinationStatus && originStatus && originStatus !== "ABORTED") {
|
|
69
|
+
effectiveStatus = mapApiStatusToUiStatus(originStatus)
|
|
70
|
+
}
|
|
71
|
+
// If depositTransactionStatus is ABORTED but origin/destination have non-ABORTED statuses, don't use aborted
|
|
72
|
+
// Only use aborted if deposit is ABORTED and origin/destination are also ABORTED or don't exist
|
|
73
|
+
else if (depositStatus === "ABORTED") {
|
|
74
|
+
// Check if origin or destination have non-ABORTED statuses
|
|
75
|
+
const hasNonAbortedOrigin = originStatus && originStatus !== "ABORTED"
|
|
76
|
+
const hasNonAbortedDestination =
|
|
77
|
+
destinationStatus && destinationStatus !== "ABORTED"
|
|
78
|
+
|
|
79
|
+
if (!hasNonAbortedOrigin && !hasNonAbortedDestination) {
|
|
80
|
+
// Only mark as aborted if no non-aborted statuses exist
|
|
81
|
+
effectiveStatus = "aborted"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// If originTransactionStatus is ABORTED and no destination status, use aborted
|
|
85
|
+
else if (originStatus === "ABORTED" && !destinationStatus) {
|
|
86
|
+
effectiveStatus = "aborted"
|
|
87
|
+
}
|
|
88
|
+
// If destinationTransactionStatus is ABORTED, use aborted
|
|
89
|
+
else if (destinationStatus === "ABORTED") {
|
|
90
|
+
effectiveStatus = "aborted"
|
|
91
|
+
}
|
|
92
|
+
|
|
53
93
|
return {
|
|
54
94
|
originIntentAddress: intent.originIntentAddress,
|
|
55
95
|
destinationIntentAddress: intent.destinationIntentAddress,
|
|
56
96
|
mainSigner: intent.ownerAddress,
|
|
97
|
+
intentId: intent.intentId,
|
|
57
98
|
metaTxnId: intent.intentId,
|
|
58
99
|
txnHash:
|
|
59
100
|
intent.originTransactionHash ||
|
|
60
101
|
intent.destinationTransactionHash ||
|
|
61
102
|
undefined,
|
|
62
|
-
executionStatus:
|
|
103
|
+
executionStatus: effectiveStatus,
|
|
63
104
|
originChainId: intent.originChainId,
|
|
64
105
|
destinationChainId: intent.destinationChainId,
|
|
65
106
|
originTokenAddress: intent.originTokenAddress,
|
|
@@ -70,6 +111,9 @@ function mapIntentSummaryToTransaction(
|
|
|
70
111
|
createdAt: intent.createdAt,
|
|
71
112
|
originTransactionHash: intent.originTransactionHash,
|
|
72
113
|
destinationTransactionHash: intent.destinationTransactionHash,
|
|
114
|
+
depositTransactionStatus: intent.depositTransactionStatus,
|
|
115
|
+
originTransactionStatus: intent.originTransactionStatus,
|
|
116
|
+
destinationTransactionStatus: intent.destinationTransactionStatus,
|
|
73
117
|
originToken: intent.originTokenMetadata
|
|
74
118
|
? ({
|
|
75
119
|
symbol: intent.originTokenMetadata.symbol || "",
|
|
@@ -514,6 +558,40 @@ function getEffectiveExecutionStatus(
|
|
|
514
558
|
createdAt,
|
|
515
559
|
} = transaction
|
|
516
560
|
|
|
561
|
+
// Check for aborted status in transaction status fields
|
|
562
|
+
// Priority: destinationTransactionStatus > originTransactionStatus > depositTransactionStatus
|
|
563
|
+
const depositStatus = transaction.depositTransactionStatus
|
|
564
|
+
const originStatus = transaction.originTransactionStatus
|
|
565
|
+
const destinationStatus = transaction.destinationTransactionStatus
|
|
566
|
+
|
|
567
|
+
// If destinationTransactionStatus exists and is not ABORTED, use it (don't mark as aborted)
|
|
568
|
+
if (destinationStatus && destinationStatus !== "ABORTED") {
|
|
569
|
+
return mapApiStatusToUiStatus(destinationStatus)
|
|
570
|
+
}
|
|
571
|
+
// If destinationTransactionStatus doesn't exist but originTransactionStatus exists and is not ABORTED, use it
|
|
572
|
+
if (!destinationStatus && originStatus && originStatus !== "ABORTED") {
|
|
573
|
+
return mapApiStatusToUiStatus(originStatus)
|
|
574
|
+
}
|
|
575
|
+
// If depositTransactionStatus is ABORTED but origin/destination have non-ABORTED statuses, don't use aborted
|
|
576
|
+
if (depositStatus === "ABORTED") {
|
|
577
|
+
const hasNonAbortedOrigin = originStatus && originStatus !== "ABORTED"
|
|
578
|
+
const hasNonAbortedDestination =
|
|
579
|
+
destinationStatus && destinationStatus !== "ABORTED"
|
|
580
|
+
|
|
581
|
+
// Only mark as aborted if no non-aborted statuses exist
|
|
582
|
+
if (!hasNonAbortedOrigin && !hasNonAbortedDestination) {
|
|
583
|
+
return "aborted"
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
// If originTransactionStatus is ABORTED and no destination status, use aborted
|
|
587
|
+
if (originStatus === "ABORTED" && !destinationStatus) {
|
|
588
|
+
return "aborted"
|
|
589
|
+
}
|
|
590
|
+
// If destinationTransactionStatus is ABORTED, use aborted
|
|
591
|
+
if (destinationStatus === "ABORTED") {
|
|
592
|
+
return "aborted"
|
|
593
|
+
}
|
|
594
|
+
|
|
517
595
|
// If we decoded a refund, return "refunded"
|
|
518
596
|
if (decodedEvents.refunded) {
|
|
519
597
|
return "refunded"
|
|
@@ -524,8 +602,12 @@ function getEffectiveExecutionStatus(
|
|
|
524
602
|
return "failed"
|
|
525
603
|
}
|
|
526
604
|
|
|
527
|
-
// If API provided a status, use it
|
|
528
|
-
if (
|
|
605
|
+
// If API provided a status, use it (but skip if it's "executing" when we know it's aborted)
|
|
606
|
+
if (
|
|
607
|
+
executionStatus &&
|
|
608
|
+
executionStatus !== "unknown" &&
|
|
609
|
+
executionStatus !== "executing"
|
|
610
|
+
) {
|
|
529
611
|
return executionStatus
|
|
530
612
|
}
|
|
531
613
|
|
|
@@ -23,7 +23,7 @@ import type { PrepareSendFees } from "../../prepareSend.js"
|
|
|
23
23
|
import { getTokenPrice } from "../../prices.js"
|
|
24
24
|
import { useCommitIntent, useExecuteIntent } from "../../mutations.js"
|
|
25
25
|
import type { CheckoutOnHandlers } from "./useCheckout.js"
|
|
26
|
-
import type { FeeOption } from "@0xtrails/api"
|
|
26
|
+
import type { FeeOption, RouteProvider } from "@0xtrails/api"
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Configuration options for the `useQuote` hook.
|
|
@@ -55,8 +55,10 @@ export type UseQuoteProps = {
|
|
|
55
55
|
slippageTolerance?: string | number | null
|
|
56
56
|
/** Optional callback function that receives transaction state updates during swap execution. */
|
|
57
57
|
onStatusUpdate?: ((transactionStates: TransactionState[]) => void) | null
|
|
58
|
-
/** Optional quote provider ID to use. If not specified, the best available provider will be selected. */
|
|
59
|
-
|
|
58
|
+
/** Optional quote swap provider ID to use. If not specified, the best available provider will be selected. */
|
|
59
|
+
swapProvider?: RouteProvider | null
|
|
60
|
+
/** Optional quote bridge provider ID to use. If not specified, the best available provider will be selected. */
|
|
61
|
+
bridgeProvider?: RouteProvider | null
|
|
60
62
|
/**
|
|
61
63
|
* Optional checkout event handlers for tracking the states of a Trails transaction.
|
|
62
64
|
*
|
|
@@ -123,7 +125,7 @@ export type SwapReturn = {
|
|
|
123
125
|
/**
|
|
124
126
|
* Information about a quote provider (LiFi, CCTP, Relay, etc.) used for routing the swap.
|
|
125
127
|
*/
|
|
126
|
-
export type
|
|
128
|
+
export type RouteProviderInfo = {
|
|
127
129
|
id: string
|
|
128
130
|
name: string
|
|
129
131
|
url: string
|
|
@@ -152,7 +154,7 @@ export type Quote = {
|
|
|
152
154
|
completionEstimateSeconds: number
|
|
153
155
|
transactionStates?: TransactionState[]
|
|
154
156
|
originTokenRate?: string
|
|
155
|
-
|
|
157
|
+
routeProviders?: RouteProviderInfo[]
|
|
156
158
|
destinationTokenRate?: string
|
|
157
159
|
fromAmountUsdDisplay?: string
|
|
158
160
|
toAmountUsdDisplay?: string
|
|
@@ -271,7 +273,8 @@ export type UseQuoteReturn = {
|
|
|
271
273
|
* @param props.slippageTolerance - Slippage tolerance as a decimal string or number (e.g., '0.005' for 0.5%, '0.01' for 1%). Defaults to a reasonable value if not provided
|
|
272
274
|
* @param props.toCalldata - Optional custom calldata for the destination chain. Used for executing arbitrary contract calls (e.g., swap and mint an NFT on destination chain)
|
|
273
275
|
* @param props.onStatusUpdate - Optional callback function that receives transaction state updates during swap execution
|
|
274
|
-
* @param props.
|
|
276
|
+
* @param props.swapProvider - Optional swap provider ID to use (auto, lifi, relay, cctp, direct). If not specified, the best available provider will be selected
|
|
277
|
+
* @param props.bridgeProvider - Optional bridge provider ID to use (auto, lifi, relay, cctp, direct). If not specified, the best available provider will be selected
|
|
275
278
|
* @param props.checkoutOnHandlers - Optional event handlers for tracking checkout/transaction lifecycle events
|
|
276
279
|
* @param props.paymasterUrl - Optional paymaster URL for gasless ERC-4337 transactions
|
|
277
280
|
* @param props.selectedFeeOption - Optional fee option selection (e.g., user selects USDC as the fee token instead of ETH)
|
|
@@ -314,7 +317,8 @@ export function useQuote({
|
|
|
314
317
|
slippageTolerance,
|
|
315
318
|
onStatusUpdate,
|
|
316
319
|
checkoutOnHandlers,
|
|
317
|
-
|
|
320
|
+
swapProvider,
|
|
321
|
+
bridgeProvider,
|
|
318
322
|
paymasterUrl,
|
|
319
323
|
selectedFeeOption,
|
|
320
324
|
nodeGatewayEnv,
|
|
@@ -382,7 +386,8 @@ export function useQuote({
|
|
|
382
386
|
toCalldata,
|
|
383
387
|
tradeType,
|
|
384
388
|
slippageTolerance,
|
|
385
|
-
|
|
389
|
+
swapProvider,
|
|
390
|
+
bridgeProvider,
|
|
386
391
|
apiKey,
|
|
387
392
|
isSmartWallet,
|
|
388
393
|
],
|
|
@@ -628,7 +633,8 @@ export function useQuote({
|
|
|
628
633
|
dryMode: false,
|
|
629
634
|
onTransactionStateChange: onStatusUpdate ?? (() => {}),
|
|
630
635
|
slippageTolerance: slippageTolerance?.toString(),
|
|
631
|
-
|
|
636
|
+
swapProvider: swapProvider,
|
|
637
|
+
bridgeProvider: bridgeProvider,
|
|
632
638
|
paymasterUrl: paymasterUrl,
|
|
633
639
|
selectedFeeOption: selectedFeeOption ?? null,
|
|
634
640
|
abortSignal: combinedAbortSignal,
|
|
@@ -668,7 +674,7 @@ export function useQuote({
|
|
|
668
674
|
transactionStates: prepareSendQuote.transactionStates,
|
|
669
675
|
originTokenRate: prepareSendQuote.originTokenRate,
|
|
670
676
|
destinationTokenRate: prepareSendQuote.destinationTokenRate,
|
|
671
|
-
|
|
677
|
+
routeProviders: prepareSendQuote.routeProviders,
|
|
672
678
|
fromAmountUsdDisplay:
|
|
673
679
|
prepareSendQuote.originAmountUsdDisplay ?? undefined,
|
|
674
680
|
toAmountUsdDisplay:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TokenPrice, FeeOption } from "@0xtrails/api"
|
|
1
|
+
import type { TokenPrice, FeeOption, RouteProvider } from "@0xtrails/api"
|
|
2
2
|
import { JsonEncode } from "@0xtrails/api"
|
|
3
3
|
import type React from "react"
|
|
4
4
|
import { useCallback, useEffect, useMemo, useState, useRef } from "react"
|
|
@@ -89,7 +89,8 @@ export type UseSendProps = {
|
|
|
89
89
|
selectedToken?: Token
|
|
90
90
|
setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
|
|
91
91
|
tradeType?: TradeType
|
|
92
|
-
|
|
92
|
+
swapProvider?: string
|
|
93
|
+
bridgeProvider?: string
|
|
93
94
|
fundMethod?: string
|
|
94
95
|
mode?: "pay" | "fund" | "earn" | "swap"
|
|
95
96
|
onNavigateToOnramp?: (
|
|
@@ -175,7 +176,8 @@ export function useSendForm({
|
|
|
175
176
|
onComplete,
|
|
176
177
|
setWalletConfirmRetryHandler,
|
|
177
178
|
tradeType = TradeType.EXACT_OUTPUT,
|
|
178
|
-
|
|
179
|
+
swapProvider,
|
|
180
|
+
bridgeProvider,
|
|
179
181
|
fundMethod,
|
|
180
182
|
mode,
|
|
181
183
|
onNavigateToOnramp,
|
|
@@ -189,9 +191,9 @@ export function useSendForm({
|
|
|
189
191
|
// Read isSmartWallet from widget props
|
|
190
192
|
const { isSmartWallet } = useWidgetProps()
|
|
191
193
|
|
|
192
|
-
// Auto-set
|
|
193
|
-
const
|
|
194
|
-
if (!
|
|
194
|
+
// Auto-set swapProvider and bridgeProvider to "lifi" if either from or to chain is etherlink
|
|
195
|
+
const effectiveSwapProvider = useMemo(() => {
|
|
196
|
+
if (!swapProvider || swapProvider === "auto") {
|
|
195
197
|
if (
|
|
196
198
|
selectedToken?.chainId === etherlink.id ||
|
|
197
199
|
toChainId === etherlink.id
|
|
@@ -199,8 +201,20 @@ export function useSendForm({
|
|
|
199
201
|
return "lifi"
|
|
200
202
|
}
|
|
201
203
|
}
|
|
202
|
-
return
|
|
203
|
-
}, [
|
|
204
|
+
return swapProvider
|
|
205
|
+
}, [swapProvider, selectedToken?.chainId, toChainId])
|
|
206
|
+
|
|
207
|
+
const effectiveBridgeProvider = useMemo(() => {
|
|
208
|
+
if (!bridgeProvider || bridgeProvider === "auto") {
|
|
209
|
+
if (
|
|
210
|
+
selectedToken?.chainId === etherlink.id ||
|
|
211
|
+
toChainId === etherlink.id
|
|
212
|
+
) {
|
|
213
|
+
return "lifi"
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return bridgeProvider
|
|
217
|
+
}, [bridgeProvider, selectedToken?.chainId, toChainId])
|
|
204
218
|
|
|
205
219
|
const [amount, setAmount] = useState(
|
|
206
220
|
tradeType === TradeType.EXACT_INPUT ? "" : (toAmount ?? ""),
|
|
@@ -561,10 +575,7 @@ export function useSendForm({
|
|
|
561
575
|
return input
|
|
562
576
|
}, [selectedDestToken, destTokenAddress, selectedDestinationChain?.id])
|
|
563
577
|
|
|
564
|
-
const { tokenPrices: destTokenPrices } = useTokenPrices(
|
|
565
|
-
destTokenPricesInput,
|
|
566
|
-
trailsClient,
|
|
567
|
-
)
|
|
578
|
+
const { tokenPrices: destTokenPrices } = useTokenPrices(destTokenPricesInput)
|
|
568
579
|
|
|
569
580
|
const { tokenPrices: sourceTokenPrices } = useTokenPrices(
|
|
570
581
|
selectedToken
|
|
@@ -576,7 +587,6 @@ export function useSendForm({
|
|
|
576
587
|
},
|
|
577
588
|
]
|
|
578
589
|
: [],
|
|
579
|
-
trailsClient,
|
|
580
590
|
)
|
|
581
591
|
|
|
582
592
|
// Update selectedChain when toChainId prop changes
|
|
@@ -1071,7 +1081,11 @@ export function useSendForm({
|
|
|
1071
1081
|
p.chainId.toString() === (selectedToken.chainId || 0).toString(),
|
|
1072
1082
|
)?.url ?? undefined,
|
|
1073
1083
|
originNativeTokenPriceUsd: nativeTokenPriceUsd,
|
|
1074
|
-
|
|
1084
|
+
swapProvider: effectiveSwapProvider as RouteProvider | null | undefined,
|
|
1085
|
+
bridgeProvider: effectiveBridgeProvider as
|
|
1086
|
+
| RouteProvider
|
|
1087
|
+
| null
|
|
1088
|
+
| undefined,
|
|
1075
1089
|
mode,
|
|
1076
1090
|
fundMethod,
|
|
1077
1091
|
checkoutOnHandlers,
|
|
@@ -1166,7 +1180,8 @@ export function useSendForm({
|
|
|
1166
1180
|
selectedDestToken,
|
|
1167
1181
|
selectedDestinationChain,
|
|
1168
1182
|
selectedToken,
|
|
1169
|
-
|
|
1183
|
+
effectiveSwapProvider,
|
|
1184
|
+
effectiveBridgeProvider,
|
|
1170
1185
|
fundMethod,
|
|
1171
1186
|
amountRaw,
|
|
1172
1187
|
checkoutOnHandlers,
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
formatPriceFields,
|
|
14
14
|
} from "../../tokenBalances.js"
|
|
15
15
|
import { useSupportedTokens, useGetTokenImageUrl } from "../../tokens.js"
|
|
16
|
-
import { useIndexerGatewayClient } from "../../indexerClient.js"
|
|
17
16
|
import { useTrailsClient } from "../../trailsClient.js"
|
|
18
17
|
import { useTokenPrices } from "../../prices.js"
|
|
19
18
|
import { logger } from "../../logger.js"
|
|
@@ -182,14 +181,13 @@ export function useTokenList({
|
|
|
182
181
|
const [selectedToken, setSelectedToken] = useState<Token | null>(null)
|
|
183
182
|
const [searchQuery, setSearchQuery] = useState("")
|
|
184
183
|
const { address } = useAccount()
|
|
185
|
-
const indexerGatewayClient = useIndexerGatewayClient()
|
|
186
184
|
const trailsClient = useTrailsClient()
|
|
187
185
|
const { getTokenImageUrl } = useGetTokenImageUrl()
|
|
188
186
|
const {
|
|
189
187
|
sortedTokens: allSortedTokens,
|
|
190
188
|
isLoadingSortedTokens,
|
|
191
189
|
balanceError,
|
|
192
|
-
} = useTokenBalances(address as Address.Address
|
|
190
|
+
} = useTokenBalances(address as Address.Address)
|
|
193
191
|
|
|
194
192
|
// Use cached supported tokens hook
|
|
195
193
|
const { supportedTokens, isLoadingTokens: isLoadingSupportedTokens } =
|
|
@@ -364,7 +362,7 @@ export function useTokenList({
|
|
|
364
362
|
const {
|
|
365
363
|
tokenPrices: supportedTokenPrices,
|
|
366
364
|
isLoadingTokenPrices: isLoadingSupportedTokenPrices,
|
|
367
|
-
} = useTokenPrices(supportedTokensForPricing
|
|
365
|
+
} = useTokenPrices(supportedTokensForPricing)
|
|
368
366
|
|
|
369
367
|
// Determine loading state based on fund method and allSupportedTokens
|
|
370
368
|
const isLoadingTokens =
|
|
@@ -481,7 +481,8 @@ export function useTrailsSendTransaction(
|
|
|
481
481
|
paymasterUrl: params?.paymasterUrl,
|
|
482
482
|
slippageTolerance: params?.slippageTolerance?.toString(),
|
|
483
483
|
originNativeTokenPriceUsd,
|
|
484
|
-
|
|
484
|
+
swapProvider: params?.swapProvider,
|
|
485
|
+
bridgeProvider: params?.bridgeProvider,
|
|
485
486
|
commitIntentFn: commitIntentMutation.mutateAsync,
|
|
486
487
|
executeIntentFn: executeIntentMutation.mutateAsync,
|
|
487
488
|
checkoutOnHandlers: params?.checkoutOnHandlers,
|
package/src/widget/widget.tsx
CHANGED
|
@@ -199,7 +199,8 @@ export type TrailsWidgetProps = {
|
|
|
199
199
|
buttonText?: string
|
|
200
200
|
customCss?: string | Record<string, string>
|
|
201
201
|
disableCss?: boolean
|
|
202
|
-
|
|
202
|
+
swapProvider?: string
|
|
203
|
+
bridgeProvider?: string
|
|
203
204
|
slippageTolerance?: number | string
|
|
204
205
|
priceImpactWarningThresholdBps?: number
|
|
205
206
|
priceImpactWarningMessage?: string
|
|
@@ -479,7 +480,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
|
|
|
479
480
|
onClose,
|
|
480
481
|
paymasterUrls,
|
|
481
482
|
buttonText,
|
|
482
|
-
|
|
483
|
+
swapProvider,
|
|
484
|
+
bridgeProvider,
|
|
483
485
|
priceImpactWarningThresholdBps,
|
|
484
486
|
priceImpactWarningMessage,
|
|
485
487
|
priceImpactFallbackBridgeUrl,
|
|
@@ -1960,7 +1962,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
|
|
|
1960
1962
|
onError={handleSendError}
|
|
1961
1963
|
paymasterUrls={paymasterUrls}
|
|
1962
1964
|
setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
|
|
1963
|
-
|
|
1965
|
+
swapProvider={swapProvider}
|
|
1966
|
+
bridgeProvider={bridgeProvider}
|
|
1964
1967
|
fundMethod={selectedFundMethod}
|
|
1965
1968
|
onNavigateToOnramp={handleNavigateToOnramp}
|
|
1966
1969
|
onAmountUpdate={(amount: string) => {
|
|
@@ -2015,7 +2018,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
|
|
|
2015
2018
|
onError={handleSendError}
|
|
2016
2019
|
paymasterUrls={paymasterUrls}
|
|
2017
2020
|
setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
|
|
2018
|
-
|
|
2021
|
+
swapProvider={swapProvider}
|
|
2022
|
+
bridgeProvider={bridgeProvider}
|
|
2019
2023
|
fundMethod={selectedFundMethod}
|
|
2020
2024
|
onNavigateToOnramp={handleNavigateToOnramp}
|
|
2021
2025
|
checkoutOnHandlers={checkoutOnHandlers}
|
|
@@ -2271,7 +2275,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
|
|
|
2271
2275
|
onSend={handleOnSend}
|
|
2272
2276
|
paymasterUrls={paymasterUrls}
|
|
2273
2277
|
setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
|
|
2274
|
-
|
|
2278
|
+
swapProvider={swapProvider}
|
|
2279
|
+
bridgeProvider={bridgeProvider}
|
|
2275
2280
|
fundMethod={selectedFundMethod}
|
|
2276
2281
|
onNavigateToOnramp={handleNavigateToOnramp}
|
|
2277
2282
|
checkoutOnHandlers={checkoutOnHandlers}
|
|
@@ -2311,7 +2316,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
|
|
|
2311
2316
|
onError={handleSendError}
|
|
2312
2317
|
paymasterUrls={paymasterUrls}
|
|
2313
2318
|
setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
|
|
2314
|
-
|
|
2319
|
+
swapProvider={swapProvider}
|
|
2320
|
+
bridgeProvider={bridgeProvider}
|
|
2315
2321
|
fundMethod={selectedFundMethod}
|
|
2316
2322
|
onNavigateToOnramp={handleNavigateToOnramp}
|
|
2317
2323
|
onAmountUpdate={undefined}
|