0xtrails 0.8.3 → 0.8.4
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-Bs-QcZXm.js → ccip-BKavX04a.js} +1 -1
- package/dist/gasless.d.ts.map +1 -1
- package/dist/{index-C_EsqqSn.js → index-D5kULpIU.js} +13339 -13299
- package/dist/index.js +3 -3
- package/dist/widget/components/ClassicSwap.d.ts +1 -0
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/DynamicSizeInputField.d.ts.map +1 -1
- package/dist/widget/components/Fund.d.ts.map +1 -1
- package/dist/widget/components/FundSwap.d.ts +1 -0
- package/dist/widget/components/FundSwap.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/Swap.d.ts +1 -0
- package/dist/widget/components/Swap.d.ts.map +1 -1
- package/dist/widget/components/WidgetProviders.d.ts.map +1 -1
- package/dist/widget/hooks/useDefaultDestinationToken.d.ts +20 -0
- package/dist/widget/hooks/useDefaultDestinationToken.d.ts.map +1 -0
- package/dist/widget/hooks/{useDefaultTokenSelection.d.ts → useDefaultOriginToken.d.ts} +4 -16
- package/dist/widget/hooks/useDefaultOriginToken.d.ts.map +1 -0
- package/dist/widget/index.js +1 -1
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/gasless.ts +62 -32
- package/src/widget/components/ClassicSwap.tsx +135 -35
- package/src/widget/components/DynamicSizeInputField.tsx +2 -0
- package/src/widget/components/Fund.tsx +12 -11
- package/src/widget/components/FundSwap.tsx +1 -0
- package/src/widget/components/Pay.tsx +15 -14
- package/src/widget/components/Swap.tsx +1 -0
- package/src/widget/components/WidgetProviders.tsx +1 -6
- package/src/widget/hooks/useDefaultDestinationToken.tsx +173 -0
- package/src/widget/hooks/{useDefaultTokenSelection.tsx → useDefaultOriginToken.tsx} +58 -191
- package/src/widget/widget.tsx +2 -0
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +0 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { useMemo, useRef } from "react"
|
|
2
|
+
import { useAccount } from "wagmi"
|
|
3
|
+
import { base, arbitrum } from "viem/chains"
|
|
4
|
+
import type { Address } from "ox"
|
|
5
|
+
import { useSupportedTokens } from "../../tokens.js"
|
|
6
|
+
import type { Token } from "../../tokens.js"
|
|
7
|
+
import { useTokenBalances } from "../../tokenBalances.js"
|
|
8
|
+
import { logger } from "../../logger.js"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hook for intelligent default destination token selection.
|
|
12
|
+
*
|
|
13
|
+
* This hook calculates the best destination token based on:
|
|
14
|
+
* - Origin token's chain (to minimize bridging costs)
|
|
15
|
+
* - Token availability on destination chain
|
|
16
|
+
* - User's existing balances for accurate balance display
|
|
17
|
+
*
|
|
18
|
+
* Only calculates destination when toToken/toChainId are NOT specified.
|
|
19
|
+
*
|
|
20
|
+
* @param originToken - The selected origin token
|
|
21
|
+
* @returns defaultDestinationToken - Best destination token or null
|
|
22
|
+
* @returns isLoading - Whether token data is still loading
|
|
23
|
+
*/
|
|
24
|
+
export function useDefaultDestinationToken(originToken: Token | null) {
|
|
25
|
+
const { address } = useAccount()
|
|
26
|
+
|
|
27
|
+
const { supportedTokens, isLoadingTokens } = useSupportedTokens()
|
|
28
|
+
|
|
29
|
+
const { sortedTokens, isLoadingSortedTokens } = useTokenBalances(
|
|
30
|
+
address as Address.Address,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const isLoading = isLoadingTokens || isLoadingSortedTokens
|
|
34
|
+
|
|
35
|
+
// Use a ref to prevent unnecessary recalculations on balance-only updates
|
|
36
|
+
const stableDestTokenRef = useRef<Token | null>(null)
|
|
37
|
+
const lastOriginTokenKey = useRef<string>("")
|
|
38
|
+
|
|
39
|
+
const defaultDestinationToken = useMemo(() => {
|
|
40
|
+
// Don't compute destination if no origin token or still loading
|
|
41
|
+
if (!originToken || !supportedTokens?.length || !sortedTokens?.length) {
|
|
42
|
+
stableDestTokenRef.current = null
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Create a key based on origin token identity (not balance)
|
|
47
|
+
const originTokenKey = `${originToken.symbol}-${originToken.chainId}-${originToken.contractAddress || "native"}`
|
|
48
|
+
|
|
49
|
+
// Only recalculate if origin token has actually changed
|
|
50
|
+
const shouldRecalculate =
|
|
51
|
+
originTokenKey !== lastOriginTokenKey.current ||
|
|
52
|
+
!stableDestTokenRef.current
|
|
53
|
+
|
|
54
|
+
if (!shouldRecalculate && stableDestTokenRef.current) {
|
|
55
|
+
return stableDestTokenRef.current
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
lastOriginTokenKey.current = originTokenKey
|
|
59
|
+
|
|
60
|
+
// Determine destination chain: Base if origin is not Base, Arbitrum if origin is Base
|
|
61
|
+
const defaultDestChainId =
|
|
62
|
+
originToken.chainId === base.id ? arbitrum.id : base.id
|
|
63
|
+
|
|
64
|
+
// Find USDC on destination chain first (preferred stable coin)
|
|
65
|
+
const usdcOnDestChain = supportedTokens.find(
|
|
66
|
+
(token: Token) =>
|
|
67
|
+
token.chainId === defaultDestChainId && token.symbol === "USDC",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
let destToken: Token | null = null
|
|
71
|
+
|
|
72
|
+
if (usdcOnDestChain) {
|
|
73
|
+
destToken = usdcOnDestChain
|
|
74
|
+
logger.console.log(
|
|
75
|
+
"[trails-sdk] Selected USDC as destination token on chain:",
|
|
76
|
+
defaultDestChainId,
|
|
77
|
+
)
|
|
78
|
+
} else {
|
|
79
|
+
// Fallback: Find matching token on destination chain
|
|
80
|
+
const destChainTokens = supportedTokens.filter(
|
|
81
|
+
(token: Token) => token.chainId === defaultDestChainId,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
// Try to find same symbol on destination chain first
|
|
85
|
+
const sameSymbolOnDestChain = destChainTokens.find((token: Token) => {
|
|
86
|
+
return token.symbol.toUpperCase() === originToken.symbol.toUpperCase()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
if (sameSymbolOnDestChain) {
|
|
90
|
+
destToken = sameSymbolOnDestChain
|
|
91
|
+
logger.console.log(
|
|
92
|
+
"[trails-sdk] Selected same symbol token on destination chain:",
|
|
93
|
+
sameSymbolOnDestChain.symbol,
|
|
94
|
+
)
|
|
95
|
+
} else {
|
|
96
|
+
// Fallback: ETH on destination chain > first available token on destination chain
|
|
97
|
+
destToken =
|
|
98
|
+
destChainTokens.find((token: Token) => token.symbol === "ETH") ||
|
|
99
|
+
destChainTokens[0] ||
|
|
100
|
+
null
|
|
101
|
+
|
|
102
|
+
if (destToken) {
|
|
103
|
+
logger.console.log(
|
|
104
|
+
"[trails-sdk] Selected fallback token on destination chain:",
|
|
105
|
+
destToken.symbol,
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!destToken) {
|
|
112
|
+
logger.console.log(
|
|
113
|
+
"[trails-sdk] No suitable destination token found on chain:",
|
|
114
|
+
defaultDestChainId,
|
|
115
|
+
)
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const decimals = destToken.decimals
|
|
120
|
+
if (!decimals) {
|
|
121
|
+
logger.console.warn(
|
|
122
|
+
"[trails-sdk] Missing decimals for destination token, skipping:",
|
|
123
|
+
destToken,
|
|
124
|
+
)
|
|
125
|
+
return null
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Try to find this token in user's balances to get the actual balance
|
|
129
|
+
const matchingUserToken = sortedTokens.find((t) => {
|
|
130
|
+
if (t.chainId !== destToken.chainId) return false
|
|
131
|
+
|
|
132
|
+
const isDestNative = destToken.isNativeToken ?? false
|
|
133
|
+
const isUserNative = t.isNativeToken ?? false
|
|
134
|
+
|
|
135
|
+
if (isDestNative && isUserNative) return true
|
|
136
|
+
if (isDestNative || isUserNative) return false
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
t.contractAddress?.toLowerCase() ===
|
|
140
|
+
destToken.contractAddress?.toLowerCase()
|
|
141
|
+
)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const finalDestinationToken = {
|
|
145
|
+
...destToken,
|
|
146
|
+
balance: matchingUserToken?.balance ?? "0",
|
|
147
|
+
balanceUsdFormatted: matchingUserToken?.balanceUsdFormatted ?? "0",
|
|
148
|
+
priceUsd: matchingUserToken?.priceUsd ?? 0,
|
|
149
|
+
decimals,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
logger.console.log("[trails-sdk] Default destination token selection:", {
|
|
153
|
+
destination: {
|
|
154
|
+
symbol: finalDestinationToken.symbol,
|
|
155
|
+
chainId: finalDestinationToken.chainId,
|
|
156
|
+
hasUserBalance: !!matchingUserToken,
|
|
157
|
+
userBalanceUsd: matchingUserToken?.balanceUsdFormatted ?? "0",
|
|
158
|
+
},
|
|
159
|
+
originChain: originToken.chainId,
|
|
160
|
+
destinationChain: defaultDestChainId,
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
// Store the calculated result in ref for stability
|
|
164
|
+
stableDestTokenRef.current = finalDestinationToken
|
|
165
|
+
|
|
166
|
+
return finalDestinationToken
|
|
167
|
+
}, [originToken, supportedTokens, sortedTokens])
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
defaultDestinationToken,
|
|
171
|
+
isLoading,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -1,53 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { ReactNode } from "react"
|
|
1
|
+
import { useMemo, useRef } from "react"
|
|
3
2
|
import { useAccount } from "wagmi"
|
|
4
3
|
import type { Address } from "ox"
|
|
5
|
-
import { base, arbitrum } from "viem/chains"
|
|
6
|
-
import { zeroAddress } from "viem"
|
|
7
4
|
import { useWidgetProps } from "./useWidgetProps.js"
|
|
8
5
|
import { useTargetAmount } from "./useTargetAmount.js"
|
|
9
6
|
import { useTokenBalances } from "../../tokenBalances.js"
|
|
10
7
|
import type { Token } from "../../tokens.js"
|
|
11
|
-
import { isNativeToken } from "../../utils.js"
|
|
12
|
-
import { useSupportedTokens } from "../../tokens.js"
|
|
13
8
|
import { getChainInfo } from "../../chains.js"
|
|
9
|
+
import { isNativeToken } from "../../utils.js"
|
|
14
10
|
import { logger } from "../../logger.js"
|
|
11
|
+
import { zeroAddress } from "viem"
|
|
15
12
|
|
|
16
13
|
const MINIMUM_24H_VOLUME_USD = 1_000_000 // $1M
|
|
17
14
|
const MINIMUM_BALANCE_USD_FOR_PRIORITIZATION = 1 // $1
|
|
18
15
|
|
|
19
|
-
/**
|
|
20
|
-
* Hook for intelligent default token selection in 0xtrails widget.
|
|
21
|
-
*
|
|
22
|
-
* When toToken and toChainId are specified (user wants specific destination):
|
|
23
|
-
* 1. Same-chain, same-token with balance >= targetAmountUsd (no swap, no bridge) - FASTEST
|
|
24
|
-
* 2. Same-chain, different-token with balance >= targetAmountUsd (swap only) - FAST
|
|
25
|
-
* - Ordered by: 24h volume > $1M first, then highest balance USD
|
|
26
|
-
* 3. Different-chain, same-token with balance >= targetAmountUsd (bridge only) - SLOWER
|
|
27
|
-
* - Ordered by: 24h volume > $1M first, then highest balance USD
|
|
28
|
-
* 4. Different-chain, different-token with balance >= targetAmountUsd (swap + bridge) - SLOWEST
|
|
29
|
-
* - Ordered by: 24h volume > $1M first, then highest balance USD
|
|
30
|
-
* 5. Fallback: Any token with balance > 0 if none can cover target amount
|
|
31
|
-
*
|
|
32
|
-
* When toToken/toChainId NOT specified (general send/bridge):
|
|
33
|
-
* - Select highest balance token with 24h volume > $1M
|
|
34
|
-
* - Fallback to any token with balance > 0
|
|
35
|
-
*
|
|
36
|
-
* Destination Token Logic (only when toToken/toChainId not provided):
|
|
37
|
-
* - Default destination chain: Base if origin is not Base, Arbitrum if origin is Base
|
|
38
|
-
* - Default destination token: USDC on destination chain
|
|
39
|
-
* - Fallback: Same symbol on destination chain > ETH on destination chain > first available token on destination chain
|
|
40
|
-
*
|
|
41
|
-
* @returns defaultOriginToken - Best origin token that can cover targetAmountUsd (if specified)
|
|
42
|
-
* @returns defaultDestinationToken - Matching token on different chain (null if toToken/toChainId set)
|
|
43
|
-
* @returns isLoading - Whether token data is still loading
|
|
44
|
-
*/
|
|
45
|
-
export interface UseDefaultTokenSelectionReturn {
|
|
46
|
-
defaultOriginToken: Token | null
|
|
47
|
-
defaultDestinationToken: Token | null
|
|
48
|
-
isLoading: boolean
|
|
49
|
-
}
|
|
50
|
-
|
|
51
16
|
function getToken24hVolume(_token: Token): number {
|
|
52
17
|
// Note: price24hVol is not in Token, so we return 0
|
|
53
18
|
// This can be added to Token later if needed
|
|
@@ -94,7 +59,27 @@ function tokenMatches(
|
|
|
94
59
|
return symbol?.toUpperCase() === targetSymbolOrAddress.toUpperCase()
|
|
95
60
|
}
|
|
96
61
|
|
|
97
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Hook for intelligent default origin token selection in 0xtrails widget.
|
|
64
|
+
*
|
|
65
|
+
* When toToken and toChainId are specified (user wants specific destination):
|
|
66
|
+
* 1. Same-chain, same-token with balance >= targetAmountUsd (no swap, no bridge) - FASTEST
|
|
67
|
+
* 2. Same-chain, different-token with balance >= targetAmountUsd (swap only) - FAST
|
|
68
|
+
* - Ordered by: 24h volume > $1M first, then highest balance USD
|
|
69
|
+
* 3. Different-chain, same-token with balance >= targetAmountUsd (bridge only) - SLOWER
|
|
70
|
+
* - Ordered by: 24h volume > $1M first, then highest balance USD
|
|
71
|
+
* 4. Different-chain, different-token with balance >= targetAmountUsd (swap + bridge) - SLOWEST
|
|
72
|
+
* - Ordered by: 24h volume > $1M first, then highest balance USD
|
|
73
|
+
* 5. Fallback: Any token with balance > 0 if none can cover target amount
|
|
74
|
+
*
|
|
75
|
+
* When toToken/toChainId NOT specified (general send/bridge):
|
|
76
|
+
* - Select highest balance token with 24h volume > $1M
|
|
77
|
+
* - Fallback to any token with balance > 0
|
|
78
|
+
*
|
|
79
|
+
* @returns defaultOriginToken - Best origin token that can cover targetAmountUsd (if specified)
|
|
80
|
+
* @returns isLoading - Whether token data is still loading
|
|
81
|
+
*/
|
|
82
|
+
export function useDefaultOriginToken() {
|
|
98
83
|
const { toToken, toChainId } = useWidgetProps()
|
|
99
84
|
const { targetAmountUsd } = useTargetAmount()
|
|
100
85
|
const { address } = useAccount()
|
|
@@ -103,22 +88,33 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
103
88
|
address as Address.Address,
|
|
104
89
|
)
|
|
105
90
|
|
|
106
|
-
const
|
|
107
|
-
useSupportedTokens()
|
|
91
|
+
const isLoading = isLoadingSortedTokens
|
|
108
92
|
|
|
109
|
-
|
|
93
|
+
// Use a ref to prevent unnecessary recalculations on balance-only updates
|
|
94
|
+
const stableTokenRef = useRef<Token | null>(null)
|
|
95
|
+
const lastTokenListKey = useRef<string>("")
|
|
110
96
|
|
|
111
|
-
const
|
|
112
|
-
if (!sortedTokens?.length
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
97
|
+
const defaultOriginToken = useMemo(() => {
|
|
98
|
+
if (!sortedTokens?.length) {
|
|
99
|
+
stableTokenRef.current = null
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Create a key based on available tokens (not their balances)
|
|
104
|
+
const tokenListKey = sortedTokens
|
|
105
|
+
.map((t) => `${t.symbol}-${t.chainId}-${t.contractAddress || "native"}`)
|
|
106
|
+
.join(",")
|
|
107
|
+
|
|
108
|
+
// Only recalculate if the token list has actually changed (not just balances)
|
|
109
|
+
// or if meaningful selection parameters have changed
|
|
110
|
+
const shouldRecalculate =
|
|
111
|
+
tokenListKey !== lastTokenListKey.current || !stableTokenRef.current
|
|
112
|
+
|
|
113
|
+
if (!shouldRecalculate && stableTokenRef.current) {
|
|
114
|
+
return stableTokenRef.current
|
|
117
115
|
}
|
|
118
116
|
|
|
119
|
-
|
|
120
|
-
// Only compute destination defaults if toToken and toChainId are NOT set
|
|
121
|
-
const shouldComputeDestination = !toToken && !toChainId
|
|
117
|
+
lastTokenListKey.current = tokenListKey
|
|
122
118
|
|
|
123
119
|
// Helper to ensure Token has required properties set
|
|
124
120
|
const ensureToken = (token: Token): Token | null => {
|
|
@@ -165,7 +161,7 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
165
161
|
}
|
|
166
162
|
}
|
|
167
163
|
|
|
168
|
-
// Filter tokens with balance > 0
|
|
164
|
+
// Filter tokens with balance > 0
|
|
169
165
|
const tokensWithBalance = sortedTokens.filter((token) => {
|
|
170
166
|
const balanceUsd = token.balanceUsd ?? 0
|
|
171
167
|
return balanceUsd > 0
|
|
@@ -173,12 +169,9 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
173
169
|
|
|
174
170
|
if (tokensWithBalance.length === 0) {
|
|
175
171
|
logger.console.log(
|
|
176
|
-
"[trails-sdk] No tokens with balance found for default selection",
|
|
172
|
+
"[trails-sdk] No tokens with balance found for default origin selection",
|
|
177
173
|
)
|
|
178
|
-
return
|
|
179
|
-
defaultOriginToken: null,
|
|
180
|
-
defaultDestinationToken: null,
|
|
181
|
-
}
|
|
174
|
+
return null
|
|
182
175
|
}
|
|
183
176
|
|
|
184
177
|
// Helper to sort tokens by balance USD and volume
|
|
@@ -342,7 +335,7 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
342
335
|
const sortedByBalanceAndVolume = sortTokensByBalanceAndVolume(
|
|
343
336
|
tokensWithBalance,
|
|
344
337
|
MINIMUM_BALANCE_USD_FOR_PRIORITIZATION,
|
|
345
|
-
)
|
|
338
|
+
)
|
|
346
339
|
bestOriginToken = sortedByBalanceAndVolume[0] ?? null
|
|
347
340
|
logger.console.log(
|
|
348
341
|
"[trails-sdk] No destination specified, selected highest value token with best liquidity",
|
|
@@ -350,102 +343,12 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
350
343
|
}
|
|
351
344
|
|
|
352
345
|
if (!bestOriginToken) {
|
|
353
|
-
return
|
|
354
|
-
defaultOriginToken: null,
|
|
355
|
-
defaultDestinationToken: null,
|
|
356
|
-
}
|
|
346
|
+
return null
|
|
357
347
|
}
|
|
358
348
|
|
|
359
349
|
const originToken = ensureToken(bestOriginToken)
|
|
360
350
|
|
|
361
|
-
|
|
362
|
-
return {
|
|
363
|
-
defaultOriginToken: null,
|
|
364
|
-
defaultDestinationToken: null,
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// Compute destination token only when toToken and toChainId are not provided
|
|
369
|
-
let destinationToken: Token | null = null
|
|
370
|
-
|
|
371
|
-
if (shouldComputeDestination) {
|
|
372
|
-
// Determine destination chain: Base if origin is not Base, Arbitrum if origin is Base
|
|
373
|
-
const defaultDestChainId =
|
|
374
|
-
originToken.chainId === base.id ? arbitrum.id : base.id
|
|
375
|
-
|
|
376
|
-
// Find USDC on destination chain first
|
|
377
|
-
const usdcOnDestChain = supportedTokens.find(
|
|
378
|
-
(token: Token) =>
|
|
379
|
-
token.chainId === defaultDestChainId && token.symbol === "USDC",
|
|
380
|
-
)
|
|
381
|
-
|
|
382
|
-
let destToken: Token | null = null
|
|
383
|
-
|
|
384
|
-
if (usdcOnDestChain) {
|
|
385
|
-
destToken = usdcOnDestChain
|
|
386
|
-
} else {
|
|
387
|
-
// Fallback: Find matching token on destination chain
|
|
388
|
-
const destChainTokens = supportedTokens.filter(
|
|
389
|
-
(token: Token) => token.chainId === defaultDestChainId,
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
// Try to find same symbol on destination chain first
|
|
393
|
-
const sameSymbolOnDestChain = destChainTokens.find((token: Token) => {
|
|
394
|
-
return token.symbol.toUpperCase() === originToken.symbol.toUpperCase()
|
|
395
|
-
})
|
|
396
|
-
|
|
397
|
-
if (sameSymbolOnDestChain) {
|
|
398
|
-
destToken = sameSymbolOnDestChain
|
|
399
|
-
} else {
|
|
400
|
-
// Fallback: ETH on destination chain > first available token on destination chain
|
|
401
|
-
destToken =
|
|
402
|
-
destChainTokens.find((token: Token) => token.symbol === "ETH") ||
|
|
403
|
-
destChainTokens[0] ||
|
|
404
|
-
null
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
if (!destToken) {
|
|
409
|
-
destinationToken = null
|
|
410
|
-
} else {
|
|
411
|
-
const decimals = destToken.decimals
|
|
412
|
-
if (!decimals) {
|
|
413
|
-
logger.console.warn(
|
|
414
|
-
"[trails-sdk] Missing decimals for destination token, skipping:",
|
|
415
|
-
{
|
|
416
|
-
token: destToken,
|
|
417
|
-
},
|
|
418
|
-
)
|
|
419
|
-
destinationToken = null
|
|
420
|
-
} else {
|
|
421
|
-
// Try to find this token in user's balances to get the actual balance
|
|
422
|
-
const matchingUserToken = sortedTokens.find((t) => {
|
|
423
|
-
if (t.chainId !== destToken.chainId) return false
|
|
424
|
-
|
|
425
|
-
const isDestNative = destToken.isNativeToken ?? false
|
|
426
|
-
const isUserNative = t.isNativeToken ?? false
|
|
427
|
-
|
|
428
|
-
if (isDestNative && isUserNative) return true
|
|
429
|
-
if (isDestNative || isUserNative) return false
|
|
430
|
-
|
|
431
|
-
return (
|
|
432
|
-
t.contractAddress?.toLowerCase() ===
|
|
433
|
-
destToken.contractAddress?.toLowerCase()
|
|
434
|
-
)
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
destinationToken = {
|
|
438
|
-
...destToken,
|
|
439
|
-
balance: matchingUserToken?.balance ?? "0",
|
|
440
|
-
balanceUsdFormatted: matchingUserToken?.balanceUsdFormatted ?? "0",
|
|
441
|
-
priceUsd: matchingUserToken?.priceUsd ?? 0,
|
|
442
|
-
decimals,
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
logger.console.log("[trails-sdk] Default token selection:", {
|
|
351
|
+
logger.console.log("[trails-sdk] Default origin token selection:", {
|
|
449
352
|
origin: originToken
|
|
450
353
|
? {
|
|
451
354
|
symbol: originToken.symbol,
|
|
@@ -454,13 +357,6 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
454
357
|
volume24h: getToken24hVolume(bestOriginToken),
|
|
455
358
|
}
|
|
456
359
|
: null,
|
|
457
|
-
destination: destinationToken
|
|
458
|
-
? {
|
|
459
|
-
symbol: destinationToken.symbol,
|
|
460
|
-
chainId: destinationToken.chainId,
|
|
461
|
-
}
|
|
462
|
-
: null,
|
|
463
|
-
shouldComputeDestination,
|
|
464
360
|
toToken,
|
|
465
361
|
toChainId,
|
|
466
362
|
targetAmountUsd,
|
|
@@ -468,43 +364,14 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
468
364
|
toToken && toChainId ? (targetAmountUsd ?? 0.01) : null,
|
|
469
365
|
})
|
|
470
366
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}, [toToken, toChainId, sortedTokens,
|
|
367
|
+
// Store the calculated result in ref for stability
|
|
368
|
+
stableTokenRef.current = originToken
|
|
369
|
+
|
|
370
|
+
return originToken
|
|
371
|
+
}, [toToken, toChainId, sortedTokens, targetAmountUsd])
|
|
476
372
|
|
|
477
373
|
return {
|
|
478
374
|
defaultOriginToken,
|
|
479
|
-
defaultDestinationToken,
|
|
480
375
|
isLoading,
|
|
481
376
|
}
|
|
482
377
|
}
|
|
483
|
-
|
|
484
|
-
// Context for sharing default token selection across components
|
|
485
|
-
const DefaultTokenSelectionContext =
|
|
486
|
-
createContext<UseDefaultTokenSelectionReturn | null>(null)
|
|
487
|
-
|
|
488
|
-
export function DefaultTokenSelectionProvider({
|
|
489
|
-
children,
|
|
490
|
-
}: {
|
|
491
|
-
children: ReactNode
|
|
492
|
-
}) {
|
|
493
|
-
const value = useDefaultTokenSelectionInternal()
|
|
494
|
-
|
|
495
|
-
return (
|
|
496
|
-
<DefaultTokenSelectionContext.Provider value={value}>
|
|
497
|
-
{children}
|
|
498
|
-
</DefaultTokenSelectionContext.Provider>
|
|
499
|
-
)
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
export function useDefaultTokenSelection(): UseDefaultTokenSelectionReturn {
|
|
503
|
-
const context = useContext(DefaultTokenSelectionContext)
|
|
504
|
-
if (!context) {
|
|
505
|
-
throw new Error(
|
|
506
|
-
"useDefaultTokenSelection must be used within a DefaultTokenSelectionProvider",
|
|
507
|
-
)
|
|
508
|
-
}
|
|
509
|
-
return context
|
|
510
|
-
}
|
package/src/widget/widget.tsx
CHANGED
|
@@ -2026,6 +2026,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
|
|
|
2026
2026
|
recentTokens={recentTokens}
|
|
2027
2027
|
onRecentTokenSelect={handleRecentTokenSelect}
|
|
2028
2028
|
onTrackToken={handleTrackToken}
|
|
2029
|
+
exactInputOnly={true}
|
|
2029
2030
|
/>
|
|
2030
2031
|
)
|
|
2031
2032
|
case "wallet-confirmation": {
|
|
@@ -2325,6 +2326,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
|
|
|
2325
2326
|
recentTokens={recentTokens}
|
|
2326
2327
|
onRecentTokenSelect={handleRecentTokenSelect}
|
|
2327
2328
|
onTrackToken={handleTrackToken}
|
|
2329
|
+
exactInputOnly={true}
|
|
2328
2330
|
/>
|
|
2329
2331
|
)
|
|
2330
2332
|
case "wallet-connection-pending":
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useDefaultTokenSelection.d.ts","sourceRoot":"","sources":["../../../src/widget/hooks/useDefaultTokenSelection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAQtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAS5C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,8BAA8B;IAC7C,kBAAkB,EAAE,KAAK,GAAG,IAAI,CAAA;IAChC,uBAAuB,EAAE,KAAK,GAAG,IAAI,CAAA;IACrC,SAAS,EAAE,OAAO,CAAA;CACnB;AAubD,wBAAgB,6BAA6B,CAAC,EAC5C,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,SAAS,CAAA;CACpB,2CAQA;AAED,wBAAgB,wBAAwB,IAAI,8BAA8B,CAQzE"}
|