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
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Search, Loader2, ChevronRight } from "lucide-react"
|
|
2
2
|
import { useEffect, useState, useMemo, useRef, useCallback } from "react"
|
|
3
3
|
import type React from "react"
|
|
4
4
|
import type { Account, WalletClient } from "viem"
|
|
5
|
+
import { zeroAddress } from "viem"
|
|
5
6
|
import type { TransactionState } from "../../transactions.js"
|
|
6
7
|
import type { OnCompleteProps } from "../hooks/useSendForm.js"
|
|
7
8
|
import type { CheckoutOnHandlers } from "../hooks/useCheckout.js"
|
|
@@ -31,6 +32,10 @@ import type { PrepareSendQuote } from "../../prepareSend.js"
|
|
|
31
32
|
import type { SupportedToken } from "../../tokens.js"
|
|
32
33
|
import { logger } from "../../logger.js"
|
|
33
34
|
import { RefundWarning } from "./RefundWarning.js"
|
|
35
|
+
import { TokenSelectorButton } from "./TokenSelectorButton.js"
|
|
36
|
+
import { RequiredPropsError } from "./RequiredPropsError.js"
|
|
37
|
+
import { FundingMethodSelectorButton } from "./FundingMethodSelectorButton.js"
|
|
38
|
+
import { PercentageMaxButtons } from "./PercentageMaxButtons.js"
|
|
34
39
|
|
|
35
40
|
interface PayProps {
|
|
36
41
|
selectedToken?: any // Origin token (optional - user can select)
|
|
@@ -328,10 +333,12 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
328
333
|
const destTokenToUse = globalDestinationToken || defaultDestinationToken
|
|
329
334
|
|
|
330
335
|
if (destTokenToUse && !isLoadingDefaults) {
|
|
331
|
-
logger.console.log(
|
|
332
|
-
|
|
333
|
-
destTokenToUse,
|
|
334
|
-
|
|
336
|
+
logger.console.log("[trails-sdk] Initializing destination token:", {
|
|
337
|
+
symbol: destTokenToUse.symbol,
|
|
338
|
+
chainId: destTokenToUse.chainId,
|
|
339
|
+
name: destTokenToUse.name,
|
|
340
|
+
contractAddress: destTokenToUse.contractAddress,
|
|
341
|
+
})
|
|
335
342
|
|
|
336
343
|
// Set destination token if not already set by global state
|
|
337
344
|
if (!globalDestinationToken && defaultDestinationToken) {
|
|
@@ -417,21 +424,21 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
417
424
|
return inputDisplayValue
|
|
418
425
|
}, [inputDisplayValue])
|
|
419
426
|
|
|
420
|
-
// Dynamic font size based on input length
|
|
427
|
+
// Dynamic font size based on input length for destination amount
|
|
421
428
|
const inputStyles = useMemo(() => {
|
|
422
429
|
const inputLength = displayAmount.length
|
|
423
430
|
let fontSize: string
|
|
424
431
|
|
|
425
432
|
if (inputLength > 12) {
|
|
426
|
-
fontSize = "0.875rem"
|
|
427
|
-
} else if (inputLength > 9) {
|
|
428
433
|
fontSize = "1rem"
|
|
434
|
+
} else if (inputLength > 9) {
|
|
435
|
+
fontSize = "1.25rem"
|
|
429
436
|
} else if (inputLength > 6) {
|
|
430
|
-
fontSize = "1.
|
|
437
|
+
fontSize = "1.5rem"
|
|
431
438
|
} else if (inputLength > 3) {
|
|
432
|
-
fontSize = "1.
|
|
439
|
+
fontSize = "1.75rem"
|
|
433
440
|
} else {
|
|
434
|
-
fontSize = "
|
|
441
|
+
fontSize = "2rem"
|
|
435
442
|
}
|
|
436
443
|
|
|
437
444
|
return {
|
|
@@ -440,6 +447,29 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
440
447
|
}
|
|
441
448
|
}, [displayAmount.length])
|
|
442
449
|
|
|
450
|
+
// Dynamic font size based on input length for origin amount (Pay with section)
|
|
451
|
+
const originInputStyles = useMemo(() => {
|
|
452
|
+
const inputLength = (prepareSendQuote?.originAmountFormatted || "").length
|
|
453
|
+
let fontSize: string
|
|
454
|
+
|
|
455
|
+
if (inputLength > 12) {
|
|
456
|
+
fontSize = "1rem"
|
|
457
|
+
} else if (inputLength > 9) {
|
|
458
|
+
fontSize = "1.25rem"
|
|
459
|
+
} else if (inputLength > 6) {
|
|
460
|
+
fontSize = "1.5rem"
|
|
461
|
+
} else if (inputLength > 3) {
|
|
462
|
+
fontSize = "1.75rem"
|
|
463
|
+
} else {
|
|
464
|
+
fontSize = "2rem"
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return {
|
|
468
|
+
fontSize,
|
|
469
|
+
transition: "all 0.1s ease-in-out",
|
|
470
|
+
}
|
|
471
|
+
}, [prepareSendQuote?.originAmountFormatted])
|
|
472
|
+
|
|
443
473
|
const handleOriginTokenSelect = useCallback(
|
|
444
474
|
(token: any) => {
|
|
445
475
|
const formattedToken = {
|
|
@@ -533,7 +563,6 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
533
563
|
onBack={() => setShowOriginTokenSelector(false)}
|
|
534
564
|
headerContent="Select Token"
|
|
535
565
|
headerContentAlign="left"
|
|
536
|
-
showAccountActions={true}
|
|
537
566
|
/>
|
|
538
567
|
<TokenSelector
|
|
539
568
|
onTokenSelect={handleOriginTokenSelect}
|
|
@@ -580,7 +609,6 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
580
609
|
onBack={() => setShowDestinationTokenSelector(false)}
|
|
581
610
|
headerContent="Select Token"
|
|
582
611
|
headerContentAlign="left"
|
|
583
|
-
showAccountActions={true}
|
|
584
612
|
/>
|
|
585
613
|
<TokenSelector
|
|
586
614
|
onTokenSelect={handleDestinationTokenSelect}
|
|
@@ -617,11 +645,35 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
617
645
|
)
|
|
618
646
|
}
|
|
619
647
|
|
|
648
|
+
// Check for required props
|
|
649
|
+
const missingRequiredProps = []
|
|
650
|
+
if (!toAmount) missingRequiredProps.push("toAmount")
|
|
651
|
+
if (!toToken) missingRequiredProps.push("toToken")
|
|
652
|
+
if (!toRecipient) missingRequiredProps.push("toRecipient")
|
|
653
|
+
|
|
620
654
|
// Check if this is a payment request (all required props are set)
|
|
621
655
|
const isPaymentRequest = !!(toToken && toAmount && toChainId && toRecipient)
|
|
622
656
|
|
|
657
|
+
// If required props are missing, only show the error
|
|
658
|
+
if (missingRequiredProps.length > 0) {
|
|
659
|
+
return (
|
|
660
|
+
<div className="space-y-4">
|
|
661
|
+
<ScreenHeader
|
|
662
|
+
onBack={onBack}
|
|
663
|
+
headerContent="Pay"
|
|
664
|
+
headerContentAlign="left"
|
|
665
|
+
showAccountActions={true}
|
|
666
|
+
/>
|
|
667
|
+
<RequiredPropsError
|
|
668
|
+
missingProps={missingRequiredProps}
|
|
669
|
+
componentName="Pay"
|
|
670
|
+
/>
|
|
671
|
+
</div>
|
|
672
|
+
)
|
|
673
|
+
}
|
|
674
|
+
|
|
623
675
|
return (
|
|
624
|
-
<div className="space-y-
|
|
676
|
+
<div className="space-y-2">
|
|
625
677
|
<ScreenHeader
|
|
626
678
|
onBack={onBack}
|
|
627
679
|
headerContent="Pay"
|
|
@@ -635,7 +687,7 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
635
687
|
{/* Payment Request Header */}
|
|
636
688
|
<div className="space-y-1 trails-bg-secondary trails-border-radius-container p-3">
|
|
637
689
|
<div className="flex justify-start">
|
|
638
|
-
<div className="flex items-center font-medium trails-text-primary text-sm whitespace-nowrap overflow-hidden">
|
|
690
|
+
<div className="flex items-center font-medium trails-text-primary text-sm whitespace-nowrap overflow-hidden min-w-full">
|
|
639
691
|
{payMessage}
|
|
640
692
|
</div>
|
|
641
693
|
</div>
|
|
@@ -643,10 +695,10 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
643
695
|
|
|
644
696
|
{/* Origin Token Selection for Payment Request */}
|
|
645
697
|
<div className="space-y-1">
|
|
646
|
-
<div className="trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 group transition-all duration-200 border border-transparent focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary">
|
|
698
|
+
<div className="trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 group transition-all duration-200 border border-transparent focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary min-h-[120px] flex flex-col">
|
|
647
699
|
{/* Amount to Pay Label */}
|
|
648
700
|
<div className="flex justify-between items-center mb-2">
|
|
649
|
-
<div className="text-sm font-
|
|
701
|
+
<div className="text-sm font-medium trails-text-secondary text-left">
|
|
650
702
|
Pay with
|
|
651
703
|
{fundMethod === "qr-code"
|
|
652
704
|
? " QR Code"
|
|
@@ -654,82 +706,38 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
654
706
|
? " Exchange"
|
|
655
707
|
: ""}
|
|
656
708
|
</div>
|
|
709
|
+
<FundingMethodSelectorButton />
|
|
657
710
|
</div>
|
|
658
711
|
|
|
659
|
-
<div className="flex items-center space-x-2">
|
|
712
|
+
<div className="flex items-center space-x-2 flex-1">
|
|
660
713
|
{/* Amount Display - Non-editable */}
|
|
661
714
|
<div className="flex-1">
|
|
662
|
-
<div
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
width: prepareSendQuote?.originAmountFormatted
|
|
678
|
-
? `${Math.max(prepareSendQuote.originAmountFormatted.length - 1 + 0.5, 1)}ch`
|
|
679
|
-
: "1ch",
|
|
680
|
-
minWidth: "1ch",
|
|
681
|
-
maxWidth: "200px",
|
|
682
|
-
padding: "0",
|
|
683
|
-
margin: "0",
|
|
684
|
-
transition: "all 0.1s ease-in-out",
|
|
685
|
-
}}
|
|
686
|
-
/>
|
|
687
|
-
<span
|
|
688
|
-
className="font-bold text-gray-400 dark:text-gray-500"
|
|
689
|
-
style={{
|
|
690
|
-
fontSize: inputStyles.fontSize,
|
|
691
|
-
marginLeft: "0.1em",
|
|
692
|
-
padding: "0",
|
|
693
|
-
transition: "all 0.2s ease-in-out",
|
|
694
|
-
}}
|
|
695
|
-
>
|
|
696
|
-
{originToken?.symbol || "TOKEN"}
|
|
697
|
-
</span>
|
|
698
|
-
{isLoadingQuote && (
|
|
699
|
-
<div className="ml-2 animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
|
|
700
|
-
)}
|
|
701
|
-
</div>
|
|
715
|
+
<div className="flex items-center space-x-2">
|
|
716
|
+
<input
|
|
717
|
+
ref={paymentRequestInputRef}
|
|
718
|
+
type="text"
|
|
719
|
+
value={prepareSendQuote?.originAmountFormatted || ""}
|
|
720
|
+
placeholder={`0 ${originToken?.symbol || ""}`.trim()}
|
|
721
|
+
readOnly={true}
|
|
722
|
+
className={`w-full bg-transparent font-bold trails-text-primary border-none outline-none ${
|
|
723
|
+
isLoadingQuote ? "animate-pulse" : ""
|
|
724
|
+
}`}
|
|
725
|
+
style={originInputStyles}
|
|
726
|
+
/>
|
|
727
|
+
{isLoadingQuote && (
|
|
728
|
+
<div className="animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
|
|
729
|
+
)}
|
|
702
730
|
</div>
|
|
703
731
|
</div>
|
|
704
732
|
|
|
705
|
-
{/* Origin Token Selection
|
|
706
|
-
<
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
<TokenImage
|
|
714
|
-
symbol={originToken.symbol}
|
|
715
|
-
imageUrl={originToken.imageUrl}
|
|
716
|
-
chainId={originToken.chainId}
|
|
717
|
-
size={20}
|
|
718
|
-
/>
|
|
719
|
-
<span className="font-medium trails-text-primary text-sm">
|
|
720
|
-
{originToken.symbol}
|
|
721
|
-
</span>
|
|
722
|
-
<ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
|
|
723
|
-
</>
|
|
724
|
-
) : (
|
|
725
|
-
<>
|
|
726
|
-
<span className="font-medium trails-text-muted text-sm">
|
|
727
|
-
Select Token
|
|
728
|
-
</span>
|
|
729
|
-
<ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
|
|
730
|
-
</>
|
|
731
|
-
)}
|
|
732
|
-
</button>
|
|
733
|
+
{/* Origin Token Selection */}
|
|
734
|
+
<div className="relative">
|
|
735
|
+
<TokenSelectorButton
|
|
736
|
+
token={originToken}
|
|
737
|
+
chainId={originToken?.chainId}
|
|
738
|
+
onSelect={() => setShowOriginTokenSelector(true)}
|
|
739
|
+
/>
|
|
740
|
+
</div>
|
|
733
741
|
</div>
|
|
734
742
|
|
|
735
743
|
{/* Bottom Info Row */}
|
|
@@ -743,17 +751,59 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
743
751
|
<span> </span>
|
|
744
752
|
)}
|
|
745
753
|
</div>
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
754
|
+
|
|
755
|
+
{/* Origin Token Balance and Percentage Buttons */}
|
|
756
|
+
{originToken && originTokenBalance?.balanceFormatted && (
|
|
757
|
+
<div className="flex items-center space-x-2">
|
|
758
|
+
<button
|
|
759
|
+
type="button"
|
|
760
|
+
className="text-xs trails-text-muted cursor-pointer hover:trails-hover-text transition-colors bg-transparent border-none p-0"
|
|
761
|
+
onClick={() => {
|
|
762
|
+
if (originTokenBalance.balanceFormatted) {
|
|
763
|
+
const balance = parseFloat(
|
|
764
|
+
originTokenBalance.balanceFormatted,
|
|
765
|
+
)
|
|
766
|
+
if (!Number.isNaN(balance)) {
|
|
767
|
+
setTokenAmountForBackend(balance.toFixed(6))
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}}
|
|
771
|
+
onKeyDown={(e) => {
|
|
772
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
773
|
+
e.preventDefault()
|
|
774
|
+
if (originTokenBalance.balanceFormatted) {
|
|
775
|
+
const balance = parseFloat(
|
|
776
|
+
originTokenBalance.balanceFormatted,
|
|
777
|
+
)
|
|
778
|
+
if (!Number.isNaN(balance)) {
|
|
779
|
+
setTokenAmountForBackend(balance.toFixed(6))
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}}
|
|
784
|
+
title="Click to use full balance"
|
|
785
|
+
>
|
|
786
|
+
Balance: {originTokenBalance.balanceFormatted}
|
|
787
|
+
</button>
|
|
788
|
+
|
|
789
|
+
{/* Percentage Buttons - Only show if toAmount is not set */}
|
|
790
|
+
{!toAmount && (
|
|
791
|
+
<PercentageMaxButtons
|
|
792
|
+
userBalance={originTokenBalance.balanceFormatted}
|
|
793
|
+
isNativeToken={
|
|
794
|
+
originToken.contractAddress === zeroAddress ||
|
|
795
|
+
originToken.contractAddress === undefined
|
|
796
|
+
}
|
|
797
|
+
gasCostFormatted={prepareSendQuote?.gasCostFormatted}
|
|
798
|
+
chainId={originToken.chainId}
|
|
799
|
+
onAmountSelect={(amount) => {
|
|
800
|
+
setTokenAmountForBackend(amount)
|
|
801
|
+
}}
|
|
802
|
+
className="opacity-100"
|
|
803
|
+
/>
|
|
804
|
+
)}
|
|
805
|
+
</div>
|
|
806
|
+
)}
|
|
757
807
|
</div>
|
|
758
808
|
</div>
|
|
759
809
|
</div>
|
|
@@ -839,12 +889,7 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
839
889
|
>
|
|
840
890
|
<div className="text-left">
|
|
841
891
|
<div className="font-medium trails-text-primary text-sm">
|
|
842
|
-
|
|
843
|
-
{fundMethod === "qr-code"
|
|
844
|
-
? " QR Code"
|
|
845
|
-
: fundMethod === "exchange"
|
|
846
|
-
? " Exchange"
|
|
847
|
-
: ""}
|
|
892
|
+
Payment method
|
|
848
893
|
</div>
|
|
849
894
|
</div>
|
|
850
895
|
|
|
@@ -888,113 +933,46 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
888
933
|
|
|
889
934
|
<div className="space-y-1">
|
|
890
935
|
{/* Destination Amount Input Section - Like Fund.tsx but for destination token */}
|
|
891
|
-
<div className="trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 group transition-all duration-200 border border-transparent focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary">
|
|
936
|
+
<div className="trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 group transition-all duration-200 border border-transparent focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary min-h-[120px] flex flex-col">
|
|
892
937
|
{/* Amount to Pay Label */}
|
|
893
938
|
<div className="flex justify-between items-center mb-2">
|
|
894
939
|
<div className="text-sm font-medium trails-text-secondary text-left">
|
|
895
940
|
Recipient receives
|
|
896
941
|
</div>
|
|
942
|
+
<FundingMethodSelectorButton />
|
|
897
943
|
</div>
|
|
898
944
|
|
|
899
|
-
<div className="flex items-center space-x-2">
|
|
945
|
+
<div className="flex items-center space-x-2 flex-1">
|
|
900
946
|
{/* Amount Input */}
|
|
901
947
|
<div className="flex-1">
|
|
902
|
-
<div
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
fontSize: inputStyles.fontSize,
|
|
919
|
-
width: `${Math.max((displayAmount || "0").length - 1 + 0.5, 1)}ch`,
|
|
920
|
-
minWidth: "1ch",
|
|
921
|
-
maxWidth: "270px",
|
|
922
|
-
padding: "0",
|
|
923
|
-
margin: "0",
|
|
924
|
-
transition: "all 0.1s ease-in-out",
|
|
925
|
-
}}
|
|
926
|
-
inputMode="decimal"
|
|
927
|
-
/>
|
|
928
|
-
<span
|
|
929
|
-
className="font-bold text-gray-400 dark:text-gray-500"
|
|
930
|
-
style={{
|
|
931
|
-
fontSize: inputStyles.fontSize,
|
|
932
|
-
marginLeft: "0.1em",
|
|
933
|
-
padding: "0",
|
|
934
|
-
transition: "all 0.2s ease-in-out",
|
|
935
|
-
}}
|
|
936
|
-
>
|
|
937
|
-
{selectedDestToken?.symbol?.slice(0, 4) || "TOKEN"}
|
|
938
|
-
</span>
|
|
939
|
-
{isLoadingQuote && (
|
|
940
|
-
<div className="ml-2 animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
|
|
941
|
-
)}
|
|
942
|
-
</div>
|
|
948
|
+
<div className="flex items-center space-x-2">
|
|
949
|
+
<input
|
|
950
|
+
ref={inputRef}
|
|
951
|
+
type="text"
|
|
952
|
+
value={displayAmount}
|
|
953
|
+
onChange={(e) => handleAmountChange(e.target.value)}
|
|
954
|
+
placeholder={`0 ${selectedDestToken?.symbol || ""}`}
|
|
955
|
+
readOnly={!!toAmount}
|
|
956
|
+
className={`w-full bg-transparent font-bold trails-text-primary placeholder:trails-text-muted border-none outline-none ${
|
|
957
|
+
isLoadingQuote ? "animate-pulse" : ""
|
|
958
|
+
}`}
|
|
959
|
+
style={inputStyles}
|
|
960
|
+
/>
|
|
961
|
+
{isLoadingQuote && (
|
|
962
|
+
<div className="animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
|
|
963
|
+
)}
|
|
943
964
|
</div>
|
|
944
965
|
</div>
|
|
945
966
|
|
|
946
|
-
{/* Destination Token Selection
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
{
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
chainId={globalDestinationToken?.chainId || toChainId}
|
|
956
|
-
size={20}
|
|
957
|
-
/>
|
|
958
|
-
<span className="font-medium trails-text-primary text-sm">
|
|
959
|
-
{selectedDestToken.symbol}
|
|
960
|
-
</span>
|
|
961
|
-
</>
|
|
962
|
-
) : (
|
|
963
|
-
<span className="font-medium trails-text-muted text-sm">
|
|
964
|
-
Select Token
|
|
965
|
-
</span>
|
|
966
|
-
)}
|
|
967
|
-
</div>
|
|
968
|
-
) : (
|
|
969
|
-
/* Clickable button when toToken is not provided */
|
|
970
|
-
<button
|
|
971
|
-
type="button"
|
|
972
|
-
onClick={() => setShowDestinationTokenSelector(true)}
|
|
973
|
-
className="flex items-center space-x-2 trails-bg-card hover:trails-hover-bg trails-border-radius-input px-2.5 py-1.5 border trails-border-primary transition-colors cursor-pointer"
|
|
974
|
-
>
|
|
975
|
-
{selectedDestToken ? (
|
|
976
|
-
<>
|
|
977
|
-
<TokenImage
|
|
978
|
-
symbol={selectedDestToken.symbol}
|
|
979
|
-
imageUrl={selectedDestToken.imageUrl}
|
|
980
|
-
chainId={globalDestinationToken?.chainId || toChainId}
|
|
981
|
-
size={20}
|
|
982
|
-
/>
|
|
983
|
-
<span className="font-medium trails-text-primary text-sm">
|
|
984
|
-
{selectedDestToken.symbol}
|
|
985
|
-
</span>
|
|
986
|
-
<ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
|
|
987
|
-
</>
|
|
988
|
-
) : (
|
|
989
|
-
<>
|
|
990
|
-
<span className="font-medium trails-text-muted text-sm">
|
|
991
|
-
Select Token
|
|
992
|
-
</span>
|
|
993
|
-
<ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
|
|
994
|
-
</>
|
|
995
|
-
)}
|
|
996
|
-
</button>
|
|
997
|
-
)}
|
|
967
|
+
{/* Destination Token Selection */}
|
|
968
|
+
<div className="relative">
|
|
969
|
+
<TokenSelectorButton
|
|
970
|
+
token={selectedDestToken}
|
|
971
|
+
chainId={globalDestinationToken?.chainId || toChainId}
|
|
972
|
+
onSelect={() => setShowDestinationTokenSelector(true)}
|
|
973
|
+
unselectable={!!toToken}
|
|
974
|
+
/>
|
|
975
|
+
</div>
|
|
998
976
|
</div>
|
|
999
977
|
|
|
1000
978
|
{/* Bottom Info Row */}
|
|
@@ -1007,9 +985,6 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
1007
985
|
<span> </span>
|
|
1008
986
|
)}
|
|
1009
987
|
</div>
|
|
1010
|
-
<div className="text-xs trails-text-muted">
|
|
1011
|
-
<span> </span>
|
|
1012
|
-
</div>
|
|
1013
988
|
</div>
|
|
1014
989
|
</div>
|
|
1015
990
|
</div>
|
|
@@ -1063,6 +1038,13 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
1063
1038
|
chainId={originToken?.chainId}
|
|
1064
1039
|
/>
|
|
1065
1040
|
|
|
1041
|
+
{/* Quote Details */}
|
|
1042
|
+
{prepareSendQuote && (
|
|
1043
|
+
<div className="space-y-2">
|
|
1044
|
+
<QuoteDetails quote={prepareSendQuote} showContent={true} />
|
|
1045
|
+
</div>
|
|
1046
|
+
)}
|
|
1047
|
+
|
|
1066
1048
|
{/* Pay Button */}
|
|
1067
1049
|
<form onSubmit={handleSubmit}>
|
|
1068
1050
|
<button
|
|
@@ -1101,13 +1083,6 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
1101
1083
|
)}
|
|
1102
1084
|
</button>
|
|
1103
1085
|
</form>
|
|
1104
|
-
|
|
1105
|
-
{/* Quote Details */}
|
|
1106
|
-
{prepareSendQuote && (
|
|
1107
|
-
<div className="space-y-2">
|
|
1108
|
-
<QuoteDetails quote={prepareSendQuote} showContent={true} />
|
|
1109
|
-
</div>
|
|
1110
|
-
)}
|
|
1111
1086
|
</div>
|
|
1112
1087
|
)
|
|
1113
1088
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type React from "react"
|
|
2
|
+
import {
|
|
3
|
+
calculateMaxNativeAmount,
|
|
4
|
+
getDefaultGasCostEstimate,
|
|
5
|
+
} from "../../estimate.js"
|
|
6
|
+
|
|
7
|
+
interface PercentageMaxButtonsProps {
|
|
8
|
+
userBalance: string | undefined
|
|
9
|
+
isNativeToken: boolean
|
|
10
|
+
gasCostFormatted?: string
|
|
11
|
+
chainId?: number
|
|
12
|
+
onAmountSelect: (amount: string) => void
|
|
13
|
+
className?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const PercentageMaxButtons: React.FC<PercentageMaxButtonsProps> = ({
|
|
17
|
+
userBalance,
|
|
18
|
+
isNativeToken,
|
|
19
|
+
gasCostFormatted,
|
|
20
|
+
chainId,
|
|
21
|
+
onAmountSelect,
|
|
22
|
+
className = "",
|
|
23
|
+
}) => {
|
|
24
|
+
// Don't render if no balance
|
|
25
|
+
if (!userBalance) return null
|
|
26
|
+
|
|
27
|
+
const handlePercentageClick = (percentage: number) => {
|
|
28
|
+
const balance = parseFloat(userBalance)
|
|
29
|
+
if (Number.isNaN(balance)) return
|
|
30
|
+
|
|
31
|
+
const amount = (balance * percentage) / 100
|
|
32
|
+
onAmountSelect(amount.toFixed(6))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const handleMaxClick = async () => {
|
|
36
|
+
if (isNativeToken) {
|
|
37
|
+
// For native tokens, subtract gas cost
|
|
38
|
+
// Priority: 1) Use actual gas cost from quote, 2) Fetch real gas price, 3) Fallback to 1% of balance
|
|
39
|
+
const effectiveGasCost =
|
|
40
|
+
gasCostFormatted ||
|
|
41
|
+
(await getDefaultGasCostEstimate(userBalance, chainId))
|
|
42
|
+
const maxAmount = calculateMaxNativeAmount(userBalance, effectiveGasCost)
|
|
43
|
+
onAmountSelect(maxAmount)
|
|
44
|
+
} else {
|
|
45
|
+
// For ERC20 tokens, use full balance
|
|
46
|
+
onAmountSelect(userBalance)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
className={`flex space-x-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200 ${className}`}
|
|
53
|
+
>
|
|
54
|
+
<button
|
|
55
|
+
type="button"
|
|
56
|
+
onClick={() => handlePercentageClick(20)}
|
|
57
|
+
className="py-0.5 px-1.5 text-xs font-medium rounded-full transition-colors cursor-pointer trails-bg-percentage-button trails-text-percentage-button trails-hover-percentage-button"
|
|
58
|
+
>
|
|
59
|
+
20%
|
|
60
|
+
</button>
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
onClick={() => handlePercentageClick(50)}
|
|
64
|
+
className="py-0.5 px-1.5 text-xs font-medium rounded-full transition-colors cursor-pointer trails-bg-percentage-button trails-text-percentage-button trails-hover-percentage-button"
|
|
65
|
+
>
|
|
66
|
+
50%
|
|
67
|
+
</button>
|
|
68
|
+
<button
|
|
69
|
+
type="button"
|
|
70
|
+
onClick={handleMaxClick}
|
|
71
|
+
className="py-0.5 px-1.5 text-xs font-medium rounded-full transition-colors cursor-pointer trails-bg-percentage-button trails-text-percentage-button trails-hover-percentage-button"
|
|
72
|
+
>
|
|
73
|
+
Max
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
)
|
|
77
|
+
}
|