0xtrails 0.2.4 → 0.2.5
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 +8 -0
- package/dist/aave.d.ts.map +1 -1
- package/dist/{ccip-BlV1Mry3.js → ccip-CXlshvBY.js} +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/error.d.ts +1 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/estimate.d.ts +52 -0
- package/dist/estimate.d.ts.map +1 -1
- package/dist/{index-BNWCIGfQ.js → index-_QuyGrjU.js} +72332 -72246
- package/dist/index.js +2 -2
- package/dist/intents.d.ts +40 -0
- package/dist/intents.d.ts.map +1 -1
- package/dist/metaTxnMonitor.d.ts +3 -3
- package/dist/metaTxnMonitor.d.ts.map +1 -1
- package/dist/metaTxns.d.ts +3 -3
- package/dist/metaTxns.d.ts.map +1 -1
- package/dist/morpho.d.ts +8 -0
- package/dist/morpho.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +16 -6
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/queryParams.d.ts.map +1 -1
- package/dist/relayer.d.ts +6 -6
- package/dist/relayer.d.ts.map +1 -1
- package/dist/sequenceWallet.d.ts +2 -2
- package/dist/sequenceWallet.d.ts.map +1 -1
- package/dist/tokens.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/AccountSettings.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts +2 -0
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
- package/dist/widget/components/ConnectedWallets.d.ts +4 -0
- package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/Fund.d.ts.map +1 -1
- package/dist/widget/components/FundMethods.d.ts.map +1 -1
- package/dist/widget/components/{FundSendForm.d.ts → FundSwap.d.ts} +11 -5
- package/dist/widget/components/FundSwap.d.ts.map +1 -0
- package/dist/widget/components/FundingMethodSelectorButton.d.ts +4 -0
- package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -0
- package/dist/widget/components/Modal.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PercentageMaxButtons.d.ts +12 -0
- package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -0
- package/dist/widget/components/{PaySendForm.d.ts → PoolDeposit.d.ts} +11 -34
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -0
- package/dist/widget/components/{SimpleSwap.d.ts → PoolWithdraw.d.ts} +16 -8
- package/dist/widget/components/PoolWithdraw.d.ts.map +1 -0
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Receive.d.ts.map +1 -1
- package/dist/widget/components/RecipientSelectorButton.d.ts +4 -0
- package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -0
- package/dist/widget/components/Recipients.d.ts.map +1 -1
- package/dist/widget/components/RequiredPropsError.d.ts +8 -0
- package/dist/widget/components/RequiredPropsError.d.ts.map +1 -0
- package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
- package/dist/widget/components/SlippageToleranceSettings.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/SwapSettings.d.ts.map +1 -1
- package/dist/widget/components/TokenImage.d.ts +1 -0
- package/dist/widget/components/TokenImage.d.ts.map +1 -1
- package/dist/widget/components/TokenList.d.ts.map +1 -1
- package/dist/widget/components/TokenSelector.d.ts.map +1 -1
- package/dist/widget/components/TokenSelectorButton.d.ts +16 -0
- package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -0
- package/dist/widget/components/UserPreferences.d.ts.map +1 -1
- package/dist/widget/components/WaasFeeOptions.d.ts +8 -0
- package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -0
- package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
- package/dist/widget/components/WalletList.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +2 -0
- package/dist/widget/css/index.css +554 -0
- package/dist/widget/hooks/useBack.d.ts +1 -0
- package/dist/widget/hooks/useBack.d.ts.map +1 -1
- package/dist/widget/hooks/useCheckout.d.ts +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/useDefaultTokenSelection.d.ts +3 -3
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
- package/dist/widget/hooks/usePayMessage.d.ts.map +1 -1
- package/dist/widget/hooks/useSelectedFundMethod.d.ts +12 -0
- package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -0
- package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -1
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/dist/widget/widget.d.ts +4 -4
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +18 -12
- package/src/aave.ts +32 -0
- package/src/config.ts +12 -4
- package/src/constants.ts +2 -0
- package/src/error.ts +19 -1
- package/src/estimate.ts +416 -5
- package/src/intents.ts +161 -11
- package/src/metaTxnMonitor.ts +3 -3
- package/src/metaTxns.ts +3 -5
- package/src/morpho.ts +32 -0
- package/src/prepareSend.ts +503 -166
- package/src/queryParams.ts +2 -1
- package/src/relayer.ts +11 -11
- package/src/sequenceWallet.ts +2 -2
- package/src/tokens.ts +7 -1
- package/src/wallets.ts +8 -0
- package/src/widget/compiled.css +2 -2
- package/src/widget/components/AccountActionsDropdown.tsx +3 -13
- package/src/widget/components/AccountSettings.tsx +6 -24
- package/src/widget/components/ClassicSwap.tsx +111 -155
- package/src/widget/components/ConnectWallet.tsx +4 -37
- package/src/widget/components/ConnectedWallets.tsx +113 -58
- package/src/widget/components/Earn.tsx +73 -589
- package/src/widget/components/Fund.tsx +31 -82
- package/src/widget/components/FundMethods.tsx +82 -159
- package/src/widget/components/FundSwap.tsx +52 -0
- package/src/widget/components/FundingMethodSelectorButton.tsx +60 -0
- package/src/widget/components/Modal.tsx +6 -2
- package/src/widget/components/Pay.tsx +183 -208
- package/src/widget/components/PercentageMaxButtons.tsx +77 -0
- package/src/widget/components/PoolDeposit.tsx +593 -0
- package/src/widget/components/PoolWithdraw.tsx +903 -0
- package/src/widget/components/QuoteDetails.tsx +22 -8
- package/src/widget/components/Receive.tsx +0 -2
- package/src/widget/components/RecipientSelectorButton.tsx +42 -0
- package/src/widget/components/Recipients.tsx +62 -156
- package/src/widget/components/RequiredPropsError.tsx +33 -0
- package/src/widget/components/ScreenHeader.tsx +5 -1
- package/src/widget/components/SlippageToleranceSettings.tsx +2 -1
- package/src/widget/components/Swap.tsx +2 -43
- package/src/widget/components/SwapSettings.tsx +2 -14
- package/src/widget/components/TokenImage.tsx +21 -4
- package/src/widget/components/TokenList.tsx +0 -1
- package/src/widget/components/TokenSelector.tsx +1 -0
- package/src/widget/components/TokenSelectorButton.tsx +75 -0
- package/src/widget/components/UserPreferences.tsx +6 -24
- package/src/widget/components/WaasFeeOptions.tsx +331 -0
- package/src/widget/components/WalletConfirmation.tsx +55 -3
- package/src/widget/components/WalletList.tsx +4 -2
- package/src/widget/hooks/useBack.tsx +2 -0
- package/src/widget/hooks/useCheckout.ts +36 -20
- package/src/widget/hooks/useCurrentScreen.tsx +1 -0
- package/src/widget/hooks/useDefaultTokenSelection.tsx +104 -28
- package/src/widget/hooks/usePayMessage.tsx +86 -11
- package/src/widget/hooks/useSelectedFundMethod.tsx +41 -0
- package/src/widget/hooks/useSelectedRecipient.tsx +10 -0
- package/src/widget/hooks/useSendForm.ts +24 -2
- package/src/widget/index.css +27 -0
- package/src/widget/widget.tsx +169 -111
- package/dist/widget/components/FundSendForm.d.ts.map +0 -1
- package/dist/widget/components/PaySendForm.d.ts.map +0 -1
- package/dist/widget/components/SimpleSwap.d.ts.map +0 -1
- package/dist/widget/hooks/useSwapSettings.d.ts +0 -16
- package/dist/widget/hooks/useSwapSettings.d.ts.map +0 -1
- package/src/widget/components/FundSendForm.tsx +0 -903
- package/src/widget/components/PaySendForm.tsx +0 -869
- package/src/widget/components/SimpleSwap.tsx +0 -983
- package/src/widget/hooks/useSwapSettings.tsx +0 -100
|
@@ -2,6 +2,7 @@ import { useCallback, useRef } from "react"
|
|
|
2
2
|
import { getSessionId } from "../../analytics.js"
|
|
3
3
|
import { logger } from "../../logger.js"
|
|
4
4
|
import { updatePersistentToast } from "../../toast.js"
|
|
5
|
+
import { getPrettifiedErrorMessage } from "../../error.js"
|
|
5
6
|
|
|
6
7
|
export type CheckoutCallbacks = {
|
|
7
8
|
onCheckoutStart?: (data: { sessionId: string }) => void
|
|
@@ -17,7 +18,7 @@ export type CheckoutCallbacks = {
|
|
|
17
18
|
export type CheckoutOnHandlers = {
|
|
18
19
|
triggerCheckoutStart: () => void
|
|
19
20
|
triggerCheckoutQuote: (quote: any) => void
|
|
20
|
-
triggerCheckoutComplete: () => void
|
|
21
|
+
triggerCheckoutComplete: (txStatus: "success" | "fail") => void
|
|
21
22
|
triggerCheckoutError: (error: string) => void
|
|
22
23
|
triggerCheckoutStatusUpdate: (transactionStates: any[]) => void
|
|
23
24
|
}
|
|
@@ -81,26 +82,38 @@ export function useCheckout({
|
|
|
81
82
|
[onCheckoutQuote],
|
|
82
83
|
)
|
|
83
84
|
|
|
84
|
-
const triggerCheckoutComplete = useCallback(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
85
|
+
const triggerCheckoutComplete = useCallback(
|
|
86
|
+
(txStatus: "success" | "fail") => {
|
|
87
|
+
if (onCheckoutComplete && sessionIdRef.current) {
|
|
88
|
+
try {
|
|
89
|
+
onCheckoutComplete({
|
|
90
|
+
sessionId: sessionIdRef.current,
|
|
91
|
+
})
|
|
92
|
+
} catch (error) {
|
|
93
|
+
logger.console.error(
|
|
94
|
+
"[trails-sdk] Error calling onCheckoutComplete:",
|
|
95
|
+
error,
|
|
96
|
+
)
|
|
97
|
+
}
|
|
95
98
|
}
|
|
96
|
-
}
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
if (txStatus === "success") {
|
|
101
|
+
updatePersistentToast(
|
|
102
|
+
"Transaction Complete",
|
|
103
|
+
"Your transaction has been successfully processed",
|
|
104
|
+
"success",
|
|
105
|
+
)
|
|
106
|
+
} else {
|
|
107
|
+
// Default to error for any non-success status (including 'fail' and any unexpected values)
|
|
108
|
+
updatePersistentToast(
|
|
109
|
+
"Transaction Failed",
|
|
110
|
+
"Your transaction failed or was refunded",
|
|
111
|
+
"error",
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
[onCheckoutComplete],
|
|
116
|
+
)
|
|
104
117
|
|
|
105
118
|
const triggerCheckoutError = useCallback(
|
|
106
119
|
(error: string) => {
|
|
@@ -120,7 +133,10 @@ export function useCheckout({
|
|
|
120
133
|
|
|
121
134
|
updatePersistentToast(
|
|
122
135
|
"Transaction Failed",
|
|
123
|
-
|
|
136
|
+
getPrettifiedErrorMessage(
|
|
137
|
+
error,
|
|
138
|
+
"An error occurred while processing your transaction",
|
|
139
|
+
),
|
|
124
140
|
"error",
|
|
125
141
|
)
|
|
126
142
|
},
|
|
@@ -14,6 +14,7 @@ import { getChainInfo } from "../../chains.js"
|
|
|
14
14
|
import { logger } from "../../logger.js"
|
|
15
15
|
|
|
16
16
|
const MINIMUM_24H_VOLUME_USD = 1_000_000 // $1M
|
|
17
|
+
const MINIMUM_BALANCE_USD_FOR_PRIORITIZATION = 1 // $1
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Hook for intelligent default token selection in 0xtrails widget.
|
|
@@ -33,9 +34,9 @@ const MINIMUM_24H_VOLUME_USD = 1_000_000 // $1M
|
|
|
33
34
|
* - Fallback to any token with balance > 0
|
|
34
35
|
*
|
|
35
36
|
* Destination Token Logic (only when toToken/toChainId not provided):
|
|
36
|
-
* -
|
|
37
|
-
* - Default destination
|
|
38
|
-
* - Fallback
|
|
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
|
|
39
40
|
*
|
|
40
41
|
* @returns defaultOriginToken - Best origin token that can cover targetAmountUsd (if specified)
|
|
41
42
|
* @returns defaultDestinationToken - Matching token on different chain (null if toToken/toChainId set)
|
|
@@ -231,8 +232,11 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
// Helper to sort tokens by balance USD and volume
|
|
234
|
-
const sortTokensByBalanceAndVolume = (
|
|
235
|
-
|
|
235
|
+
const sortTokensByBalanceAndVolume = (
|
|
236
|
+
tokens: TokenBalanceExtended[],
|
|
237
|
+
targetAmount?: number,
|
|
238
|
+
) => {
|
|
239
|
+
const sortedTokens = [...tokens].sort((a, b) => {
|
|
236
240
|
const aVolume = getToken24hVolume(a)
|
|
237
241
|
const bVolume = getToken24hVolume(b)
|
|
238
242
|
const aHighVolume = aVolume >= MINIMUM_24H_VOLUME_USD
|
|
@@ -247,6 +251,56 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
247
251
|
const bBalanceUsd = b.balanceUsd ?? 0
|
|
248
252
|
return bBalanceUsd - aBalanceUsd
|
|
249
253
|
})
|
|
254
|
+
|
|
255
|
+
// After initial sorting, prioritize USDC and native tokens at the top
|
|
256
|
+
const effectiveTargetAmount =
|
|
257
|
+
targetAmount ?? MINIMUM_BALANCE_USD_FOR_PRIORITIZATION
|
|
258
|
+
|
|
259
|
+
const prioritizedTokens: TokenBalanceExtended[] = []
|
|
260
|
+
const remainingTokens: TokenBalanceExtended[] = []
|
|
261
|
+
|
|
262
|
+
// Separate tokens into prioritized and remaining
|
|
263
|
+
for (const token of sortedTokens) {
|
|
264
|
+
const balanceUsd = token.balanceUsd ?? 0
|
|
265
|
+
const isNative =
|
|
266
|
+
!("contractAddress" in token) || token.contractAddress === zeroAddress
|
|
267
|
+
const isUSDC =
|
|
268
|
+
!isNative && token.contractInfo?.symbol?.toUpperCase() === "USDC"
|
|
269
|
+
|
|
270
|
+
if ((isUSDC || isNative) && balanceUsd >= effectiveTargetAmount) {
|
|
271
|
+
prioritizedTokens.push(token)
|
|
272
|
+
} else {
|
|
273
|
+
remainingTokens.push(token)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Sort prioritized tokens: USDC first, then native tokens
|
|
278
|
+
prioritizedTokens.sort((a, b) => {
|
|
279
|
+
const aIsNative =
|
|
280
|
+
!("contractAddress" in a) || a.contractAddress === zeroAddress
|
|
281
|
+
const bIsNative =
|
|
282
|
+
!("contractAddress" in b) || b.contractAddress === zeroAddress
|
|
283
|
+
const aIsUSDC =
|
|
284
|
+
!aIsNative && a.contractInfo?.symbol?.toUpperCase() === "USDC"
|
|
285
|
+
const bIsUSDC =
|
|
286
|
+
!bIsNative && b.contractInfo?.symbol?.toUpperCase() === "USDC"
|
|
287
|
+
|
|
288
|
+
// USDC comes first
|
|
289
|
+
if (aIsUSDC && !bIsUSDC) return -1
|
|
290
|
+
if (!aIsUSDC && bIsUSDC) return 1
|
|
291
|
+
|
|
292
|
+
// Then native tokens
|
|
293
|
+
if (aIsNative && !bIsNative) return -1
|
|
294
|
+
if (!aIsNative && bIsNative) return 1
|
|
295
|
+
|
|
296
|
+
// Within same type, sort by balance USD (highest first)
|
|
297
|
+
const aBalanceUsd = a.balanceUsd ?? 0
|
|
298
|
+
const bBalanceUsd = b.balanceUsd ?? 0
|
|
299
|
+
return bBalanceUsd - aBalanceUsd
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
// Return prioritized tokens first, then remaining tokens
|
|
303
|
+
return [...prioritizedTokens, ...remainingTokens]
|
|
250
304
|
}
|
|
251
305
|
|
|
252
306
|
// Find the best origin token using intelligent selection
|
|
@@ -281,7 +335,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
281
335
|
const sameChainTokens = tokensWithSufficientBalance.filter(
|
|
282
336
|
(token) => token.chainId === targetChainId,
|
|
283
337
|
)
|
|
284
|
-
const sortedSameChain = sortTokensByBalanceAndVolume(
|
|
338
|
+
const sortedSameChain = sortTokensByBalanceAndVolume(
|
|
339
|
+
sameChainTokens,
|
|
340
|
+
effectiveTargetAmount,
|
|
341
|
+
)
|
|
285
342
|
bestOriginToken = sortedSameChain[0] ?? null
|
|
286
343
|
if (bestOriginToken) {
|
|
287
344
|
logger.console.log(
|
|
@@ -296,7 +353,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
296
353
|
(token) =>
|
|
297
354
|
tokenMatches(token, toToken) && token.chainId !== targetChainId,
|
|
298
355
|
)
|
|
299
|
-
const sortedSameToken = sortTokensByBalanceAndVolume(
|
|
356
|
+
const sortedSameToken = sortTokensByBalanceAndVolume(
|
|
357
|
+
sameTokenDiffChain,
|
|
358
|
+
effectiveTargetAmount,
|
|
359
|
+
)
|
|
300
360
|
bestOriginToken = sortedSameToken[0] ?? null
|
|
301
361
|
if (bestOriginToken) {
|
|
302
362
|
logger.console.log(
|
|
@@ -310,8 +370,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
310
370
|
const diffChainDiffToken = tokensWithSufficientBalance.filter(
|
|
311
371
|
(token) => token.chainId !== targetChainId,
|
|
312
372
|
)
|
|
313
|
-
const sortedDiffChainDiffToken =
|
|
314
|
-
|
|
373
|
+
const sortedDiffChainDiffToken = sortTokensByBalanceAndVolume(
|
|
374
|
+
diffChainDiffToken,
|
|
375
|
+
effectiveTargetAmount,
|
|
376
|
+
)
|
|
315
377
|
bestOriginToken = sortedDiffChainDiffToken[0] ?? null
|
|
316
378
|
if (bestOriginToken) {
|
|
317
379
|
logger.console.log(
|
|
@@ -322,7 +384,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
322
384
|
|
|
323
385
|
// Fallback: If no token can cover target amount, use any token (sorted by balance/volume)
|
|
324
386
|
if (!bestOriginToken) {
|
|
325
|
-
const allTokensSorted = sortTokensByBalanceAndVolume(
|
|
387
|
+
const allTokensSorted = sortTokensByBalanceAndVolume(
|
|
388
|
+
tokensWithBalance,
|
|
389
|
+
effectiveTargetAmount,
|
|
390
|
+
)
|
|
326
391
|
bestOriginToken = allTokensSorted[0] ?? null
|
|
327
392
|
logger.console.log(
|
|
328
393
|
"[trails-sdk] Fallback: No token can cover target amount, using highest balance token",
|
|
@@ -330,8 +395,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
330
395
|
}
|
|
331
396
|
} else {
|
|
332
397
|
// When no specific destination is set, select highest value token with good liquidity
|
|
333
|
-
const sortedByBalanceAndVolume =
|
|
334
|
-
|
|
398
|
+
const sortedByBalanceAndVolume = sortTokensByBalanceAndVolume(
|
|
399
|
+
tokensWithBalance,
|
|
400
|
+
MINIMUM_BALANCE_USD_FOR_PRIORITIZATION,
|
|
401
|
+
) // Use minimum balance for prioritization
|
|
335
402
|
bestOriginToken = sortedByBalanceAndVolume[0] ?? null
|
|
336
403
|
logger.console.log(
|
|
337
404
|
"[trails-sdk] No destination specified, selected highest value token with best liquidity",
|
|
@@ -358,30 +425,39 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
|
|
|
358
425
|
let destinationToken: DefaultToken | null = null
|
|
359
426
|
|
|
360
427
|
if (shouldComputeDestination) {
|
|
361
|
-
// Determine
|
|
428
|
+
// Determine destination chain: Base if origin is not Base, Arbitrum if origin is Base
|
|
362
429
|
const defaultDestChainId =
|
|
363
430
|
originToken.chainId === base.id ? arbitrum.id : base.id
|
|
364
431
|
|
|
365
|
-
// Find
|
|
366
|
-
const
|
|
367
|
-
(token: any) =>
|
|
432
|
+
// Find USDC on destination chain first
|
|
433
|
+
const usdcOnDestChain = supportedTokens.find(
|
|
434
|
+
(token: any) =>
|
|
435
|
+
token.chainId === defaultDestChainId && token.symbol === "USDC",
|
|
368
436
|
)
|
|
369
437
|
|
|
370
438
|
let destToken: any = null
|
|
371
439
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return token.symbol.toUpperCase() === originToken.symbol.toUpperCase()
|
|
375
|
-
})
|
|
376
|
-
|
|
377
|
-
if (sameSymbolToken) {
|
|
378
|
-
destToken = sameSymbolToken
|
|
440
|
+
if (usdcOnDestChain) {
|
|
441
|
+
destToken = usdcOnDestChain
|
|
379
442
|
} else {
|
|
380
|
-
// Fallback:
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
443
|
+
// Fallback: Find matching token on destination chain
|
|
444
|
+
const destChainTokens = supportedTokens.filter(
|
|
445
|
+
(token: any) => token.chainId === defaultDestChainId,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
// Try to find same symbol on destination chain first
|
|
449
|
+
const sameSymbolOnDestChain = destChainTokens.find((token: any) => {
|
|
450
|
+
return token.symbol.toUpperCase() === originToken.symbol.toUpperCase()
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
if (sameSymbolOnDestChain) {
|
|
454
|
+
destToken = sameSymbolOnDestChain
|
|
455
|
+
} else {
|
|
456
|
+
// Fallback: ETH on destination chain > first available token on destination chain
|
|
457
|
+
destToken =
|
|
458
|
+
destChainTokens.find((token: any) => token.symbol === "ETH") ||
|
|
459
|
+
destChainTokens[0]
|
|
460
|
+
}
|
|
385
461
|
}
|
|
386
462
|
|
|
387
463
|
const decimals = destToken?.decimals
|
|
@@ -8,6 +8,7 @@ import { truncateAddress } from "../../utils.js"
|
|
|
8
8
|
import { getExplorerUrlForAddress } from "../../explorer.js"
|
|
9
9
|
import ChainImage from "../components/ChainImage.js"
|
|
10
10
|
import TokenImage from "../components/TokenImage.js"
|
|
11
|
+
import { Identicon } from "../components/Identicon.js"
|
|
11
12
|
import { logger } from "../../logger.js"
|
|
12
13
|
|
|
13
14
|
export interface PayMessagePart {
|
|
@@ -85,25 +86,23 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
85
86
|
})
|
|
86
87
|
|
|
87
88
|
const parsed = useMemo(() => {
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const template = payMessage || defaultMessage
|
|
89
|
+
// If no payMessage provided, return empty parts (default layout handled separately)
|
|
90
|
+
if (!payMessage) {
|
|
91
|
+
return []
|
|
92
|
+
}
|
|
94
93
|
|
|
95
94
|
// Parse the template into parts
|
|
96
95
|
const parts: PayMessagePart[] = []
|
|
97
96
|
const regex = /\{([^}]+)\}/g
|
|
98
97
|
let lastIndex = 0
|
|
99
|
-
let match: RegExpExecArray | null = regex.exec(
|
|
98
|
+
let match: RegExpExecArray | null = regex.exec(payMessage)
|
|
100
99
|
|
|
101
100
|
while (match !== null) {
|
|
102
101
|
// Add text before the placeholder
|
|
103
102
|
if (match.index > lastIndex) {
|
|
104
103
|
parts.push({
|
|
105
104
|
type: "text",
|
|
106
|
-
value:
|
|
105
|
+
value: payMessage.substring(lastIndex, match.index),
|
|
107
106
|
})
|
|
108
107
|
}
|
|
109
108
|
|
|
@@ -176,14 +175,14 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
176
175
|
}
|
|
177
176
|
|
|
178
177
|
lastIndex = regex.lastIndex
|
|
179
|
-
match = regex.exec(
|
|
178
|
+
match = regex.exec(payMessage)
|
|
180
179
|
}
|
|
181
180
|
|
|
182
181
|
// Add remaining text
|
|
183
|
-
if (lastIndex <
|
|
182
|
+
if (lastIndex < payMessage.length) {
|
|
184
183
|
parts.push({
|
|
185
184
|
type: "text",
|
|
186
|
-
value:
|
|
185
|
+
value: payMessage.substring(lastIndex),
|
|
187
186
|
})
|
|
188
187
|
}
|
|
189
188
|
|
|
@@ -206,6 +205,78 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
206
205
|
|
|
207
206
|
// Render the message
|
|
208
207
|
const message = useMemo(() => {
|
|
208
|
+
// If no payMessage provided, render default layout
|
|
209
|
+
if (!payMessage) {
|
|
210
|
+
return (
|
|
211
|
+
<div className="flex flex-col items-center justify-center px-8 py-4 space-y-4 min-w-full">
|
|
212
|
+
{/* App image above the amount if it exists */}
|
|
213
|
+
{appImageUrl && (
|
|
214
|
+
<img
|
|
215
|
+
src={appImageUrl}
|
|
216
|
+
alt={appName || "App"}
|
|
217
|
+
className="w-12 h-12 rounded-lg"
|
|
218
|
+
onError={(e) => {
|
|
219
|
+
logger.console.error(
|
|
220
|
+
`Error loading app image: ${e}, appImageUrl: ${appImageUrl}`,
|
|
221
|
+
)
|
|
222
|
+
// Hide image on error
|
|
223
|
+
e.currentTarget.style.display = "none"
|
|
224
|
+
}}
|
|
225
|
+
/>
|
|
226
|
+
)}
|
|
227
|
+
|
|
228
|
+
{/* Large amount in center */}
|
|
229
|
+
<div className="font-bold text-center">
|
|
230
|
+
{(() => {
|
|
231
|
+
const amountToShow = targetAmountUsdFormatted || toAmount || "0"
|
|
232
|
+
const tokenSymbol = tokenInfo?.symbol || ""
|
|
233
|
+
const displayText = targetAmountUsdFormatted
|
|
234
|
+
? amountToShow
|
|
235
|
+
: `${amountToShow} ${tokenSymbol}`.trim()
|
|
236
|
+
|
|
237
|
+
// Dynamic font size based on text length
|
|
238
|
+
const textLength = displayText.length
|
|
239
|
+
let fontSize = "text-6xl" // Default large size
|
|
240
|
+
|
|
241
|
+
if (textLength > 12) {
|
|
242
|
+
fontSize = "text-2xl"
|
|
243
|
+
} else if (textLength > 10) {
|
|
244
|
+
fontSize = "text-3xl"
|
|
245
|
+
} else if (textLength > 8) {
|
|
246
|
+
fontSize = "text-4xl"
|
|
247
|
+
} else if (textLength > 6) {
|
|
248
|
+
fontSize = "text-5xl"
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return <div className={fontSize}>{displayText}</div>
|
|
252
|
+
})()}
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
{/* Recipient info below */}
|
|
256
|
+
{toAddress && (
|
|
257
|
+
<div className="flex items-center space-x-2 text-center">
|
|
258
|
+
<span className="text-sm text-gray-600 dark:text-gray-400">
|
|
259
|
+
to
|
|
260
|
+
</span>
|
|
261
|
+
<Identicon value={toAddress} size={20} />
|
|
262
|
+
<a
|
|
263
|
+
href={getExplorerUrlForAddress({
|
|
264
|
+
address: toAddress,
|
|
265
|
+
chainId: toChainId ? Number(toChainId) : 1,
|
|
266
|
+
})}
|
|
267
|
+
target="_blank"
|
|
268
|
+
rel="noopener noreferrer"
|
|
269
|
+
className="text-sm hover:underline cursor-pointer text-blue-600 dark:text-blue-400"
|
|
270
|
+
>
|
|
271
|
+
{truncateAddress(toAddress)}
|
|
272
|
+
</a>
|
|
273
|
+
</div>
|
|
274
|
+
)}
|
|
275
|
+
</div>
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Render user-provided message with handlebar interpolation
|
|
209
280
|
return (
|
|
210
281
|
<>
|
|
211
282
|
{parsed.map((part) => {
|
|
@@ -354,6 +425,7 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
354
425
|
</>
|
|
355
426
|
)
|
|
356
427
|
}, [
|
|
428
|
+
payMessage,
|
|
357
429
|
parsed,
|
|
358
430
|
appName,
|
|
359
431
|
appUrl,
|
|
@@ -361,6 +433,9 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
361
433
|
appDescription,
|
|
362
434
|
tokenInfo,
|
|
363
435
|
toChainId,
|
|
436
|
+
toAddress,
|
|
437
|
+
targetAmountUsdFormatted,
|
|
438
|
+
toAmount,
|
|
364
439
|
])
|
|
365
440
|
|
|
366
441
|
return {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createContext, useContext, useState, type ReactNode } from "react"
|
|
2
|
+
|
|
3
|
+
interface SelectedFundMethodContextType {
|
|
4
|
+
selectedFundMethod: string
|
|
5
|
+
setSelectedFundMethod: (method: string) => void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const SelectedFundMethodContext = createContext<
|
|
9
|
+
SelectedFundMethodContextType | undefined
|
|
10
|
+
>(undefined)
|
|
11
|
+
|
|
12
|
+
interface SelectedFundMethodProviderProps {
|
|
13
|
+
children: ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const SelectedFundMethodProvider = ({
|
|
17
|
+
children,
|
|
18
|
+
}: SelectedFundMethodProviderProps) => {
|
|
19
|
+
const [selectedFundMethod, setSelectedFundMethod] = useState<string>("wallet")
|
|
20
|
+
|
|
21
|
+
const value: SelectedFundMethodContextType = {
|
|
22
|
+
selectedFundMethod,
|
|
23
|
+
setSelectedFundMethod,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<SelectedFundMethodContext.Provider value={value}>
|
|
28
|
+
{children}
|
|
29
|
+
</SelectedFundMethodContext.Provider>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const useSelectedFundMethod = (): SelectedFundMethodContextType => {
|
|
34
|
+
const context = useContext(SelectedFundMethodContext)
|
|
35
|
+
if (context === undefined) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
"useSelectedFundMethod must be used within a SelectedFundMethodProvider",
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
return context
|
|
41
|
+
}
|
|
@@ -2,8 +2,10 @@ import React, {
|
|
|
2
2
|
createContext,
|
|
3
3
|
useContext,
|
|
4
4
|
useState,
|
|
5
|
+
useEffect,
|
|
5
6
|
type ReactNode,
|
|
6
7
|
} from "react"
|
|
8
|
+
import { useAccount } from "wagmi"
|
|
7
9
|
|
|
8
10
|
interface SelectedRecipientContextType {
|
|
9
11
|
selectedRecipient: string | null
|
|
@@ -21,10 +23,18 @@ interface SelectedRecipientProviderProps {
|
|
|
21
23
|
export const SelectedRecipientProvider: React.FC<
|
|
22
24
|
SelectedRecipientProviderProps
|
|
23
25
|
> = ({ children }) => {
|
|
26
|
+
const { address } = useAccount()
|
|
24
27
|
const [selectedRecipient, setSelectedRecipient] = useState<string | null>(
|
|
25
28
|
null,
|
|
26
29
|
)
|
|
27
30
|
|
|
31
|
+
// Set recipient to address only if address exists and no recipient is selected
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (address && !selectedRecipient) {
|
|
34
|
+
setSelectedRecipient(address)
|
|
35
|
+
}
|
|
36
|
+
}, [address, selectedRecipient])
|
|
37
|
+
|
|
28
38
|
return (
|
|
29
39
|
<SelectedRecipientContext.Provider
|
|
30
40
|
value={{
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type WalletClient,
|
|
10
10
|
zeroAddress,
|
|
11
11
|
} from "viem"
|
|
12
|
+
import { useAccount } from "wagmi"
|
|
12
13
|
import { useAPIClient } from "../../apiClient.js"
|
|
13
14
|
import { getChainInfo, useSupportedChains } from "../../chains.js"
|
|
14
15
|
import { getFullErrorMessage, getPrettifiedErrorMessage } from "../../error.js"
|
|
@@ -207,6 +208,10 @@ export function useSendForm({
|
|
|
207
208
|
checkoutOnHandlers,
|
|
208
209
|
refetchTrigger = 0,
|
|
209
210
|
}: UseSendProps): UseSendReturn {
|
|
211
|
+
// Get the active wallet ID from wagmi
|
|
212
|
+
const { connector } = useAccount()
|
|
213
|
+
const walletId = connector?.id
|
|
214
|
+
|
|
210
215
|
// Auto-set quoteProvider to "lifi" if either from or to chain is etherlink
|
|
211
216
|
const effectiveQuoteProvider = useMemo(() => {
|
|
212
217
|
if (!quoteProvider || quoteProvider === "auto") {
|
|
@@ -804,6 +809,9 @@ export function useSendForm({
|
|
|
804
809
|
nativeTokenPriceUsd = nativePrice?.price?.value ?? 0
|
|
805
810
|
}
|
|
806
811
|
|
|
812
|
+
// Disable gasless for sequence-waas wallet
|
|
813
|
+
const effectiveGasless = walletId === "sequence-waas" ? false : gasless
|
|
814
|
+
|
|
807
815
|
const options = {
|
|
808
816
|
account,
|
|
809
817
|
originTokenAddress: selectedToken.contractAddress,
|
|
@@ -840,13 +848,14 @@ export function useSendForm({
|
|
|
840
848
|
paymasterUrls?.find(
|
|
841
849
|
(p) => p.chainId.toString() === selectedToken.chainId.toString(),
|
|
842
850
|
)?.url ?? undefined,
|
|
843
|
-
gasless,
|
|
851
|
+
gasless: effectiveGasless,
|
|
844
852
|
originNativeTokenPriceUsd: nativeTokenPriceUsd,
|
|
845
853
|
quoteProvider: effectiveQuoteProvider,
|
|
846
854
|
mode,
|
|
847
855
|
fundMethod,
|
|
848
856
|
checkoutOnHandlers,
|
|
849
857
|
selectedFeeToken,
|
|
858
|
+
walletId,
|
|
850
859
|
}
|
|
851
860
|
|
|
852
861
|
logger.console.log(
|
|
@@ -901,6 +910,7 @@ export function useSendForm({
|
|
|
901
910
|
checkoutOnHandlers,
|
|
902
911
|
mode,
|
|
903
912
|
selectedFeeToken,
|
|
913
|
+
walletId,
|
|
904
914
|
])
|
|
905
915
|
|
|
906
916
|
// Auto-fetch quotes when inputs change (debounced)
|
|
@@ -1194,7 +1204,19 @@ export function useSendForm({
|
|
|
1194
1204
|
}
|
|
1195
1205
|
|
|
1196
1206
|
if (!fundMethod || fundMethod === "wallet") {
|
|
1197
|
-
|
|
1207
|
+
if (mode === "pay") {
|
|
1208
|
+
return `Pay`
|
|
1209
|
+
} else if (mode === "fund") {
|
|
1210
|
+
return `Fund`
|
|
1211
|
+
} else if (mode === "swap") {
|
|
1212
|
+
return `Swap`
|
|
1213
|
+
} else if (mode === "earn") {
|
|
1214
|
+
return `Deposit`
|
|
1215
|
+
} else if (mode === "receive") {
|
|
1216
|
+
return `Pay`
|
|
1217
|
+
} else {
|
|
1218
|
+
return `Continue on wallet`
|
|
1219
|
+
}
|
|
1198
1220
|
}
|
|
1199
1221
|
|
|
1200
1222
|
if (mode === "swap") {
|
package/src/widget/index.css
CHANGED
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
--trails-modal-button-hover-bg: var(--trails-primary-hover, rgb(17 75 202)); /* Defaults to primary hover */
|
|
40
40
|
--trails-modal-button-text: rgb(255 255 255); /* white text */
|
|
41
41
|
--trails-modal-button-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); /* subtle shadow */
|
|
42
|
+
|
|
43
|
+
/* Percentage Button Colors - Customizable percentage buttons */
|
|
44
|
+
--trails-percentage-button-bg: rgb(228 228 231); /* #E4E4E7 - Light gray background */
|
|
45
|
+
--trails-percentage-button-text: rgb(113 113 123); /* #71717B - Dark gray text */
|
|
46
|
+
--trails-percentage-button-hover-bg: rgb(212 212 216); /* #D4D4D8 - Slightly darker on hover */
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
/* Utility Classes that use CSS Variables */
|
|
@@ -110,6 +115,18 @@
|
|
|
110
115
|
color: var(--trails-hover-text);
|
|
111
116
|
}
|
|
112
117
|
|
|
118
|
+
.trails-bg-percentage-button {
|
|
119
|
+
background-color: var(--trails-percentage-button-bg);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.trails-text-percentage-button {
|
|
123
|
+
color: var(--trails-percentage-button-text);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.trails-hover-percentage-button:hover {
|
|
127
|
+
background-color: var(--trails-percentage-button-hover-bg);
|
|
128
|
+
}
|
|
129
|
+
|
|
113
130
|
|
|
114
131
|
|
|
115
132
|
/* Font Family Utility Class */
|
|
@@ -304,6 +321,11 @@
|
|
|
304
321
|
/* Shadow */
|
|
305
322
|
--trails-shadow:
|
|
306
323
|
0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
|
324
|
+
|
|
325
|
+
/* Percentage Button Colors - Light Mode */
|
|
326
|
+
--trails-percentage-button-bg: rgb(228 228 231); /* #E4E4E7 - Light gray background */
|
|
327
|
+
--trails-percentage-button-text: rgb(113 113 123); /* #71717B - Dark gray text */
|
|
328
|
+
--trails-percentage-button-hover-bg: rgb(212 212 216); /* #D4D4D8 - Slightly darker on hover */
|
|
307
329
|
}
|
|
308
330
|
|
|
309
331
|
/* Dark Mode Theme Variables */
|
|
@@ -391,6 +413,11 @@
|
|
|
391
413
|
/* Shadow */
|
|
392
414
|
--trails-shadow:
|
|
393
415
|
0 1px 3px 0 rgb(0 0 0 / 0.3), 0 1px 2px -1px rgb(0 0 0 / 0.3);
|
|
416
|
+
|
|
417
|
+
/* Percentage Button Colors - Dark Mode */
|
|
418
|
+
--trails-percentage-button-bg: rgb(55 65 81); /* gray-700 - Dark gray background */
|
|
419
|
+
--trails-percentage-button-text: rgb(209 213 219); /* gray-300 - Light gray text */
|
|
420
|
+
--trails-percentage-button-hover-bg: rgb(75 85 99); /* gray-600 - Slightly lighter on hover */
|
|
394
421
|
}
|
|
395
422
|
|
|
396
423
|
/* Custom Scrollbar Styles */
|