0xtrails 0.1.2 → 0.1.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/address.d.ts.map +1 -1
- package/dist/analytics.d.ts +86 -1
- package/dist/analytics.d.ts.map +1 -1
- package/dist/apiClient.d.ts +1 -1
- package/dist/apiClient.d.ts.map +1 -1
- package/dist/{ccip-BmFTEOaB.js → ccip-dLSEJjCf.js} +55 -55
- package/dist/cctpqueue.d.ts +1 -1
- package/dist/cctpqueue.d.ts.map +1 -1
- package/dist/chains.d.ts +9 -3
- package/dist/chains.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/decoders.d.ts +58 -0
- package/dist/decoders.d.ts.map +1 -0
- package/dist/ens.d.ts +13 -0
- package/dist/ens.d.ts.map +1 -0
- package/dist/error.d.ts +9 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/{index-BPsVj7zK.js → index-BXbaLmtt.js} +28779 -25738
- package/dist/index.js +2 -2
- package/dist/intents.d.ts +4 -4
- package/dist/intents.d.ts.map +1 -1
- package/dist/lifi.d.ts +4 -0
- package/dist/lifi.d.ts.map +1 -0
- package/dist/metaTxns.d.ts +1 -1
- package/dist/metaTxns.d.ts.map +1 -1
- package/dist/mode.d.ts +1 -1
- package/dist/mode.d.ts.map +1 -1
- package/dist/preconditions.d.ts +1 -1
- package/dist/preconditions.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +32 -24
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/prices.d.ts +3 -1
- package/dist/prices.d.ts.map +1 -1
- package/dist/proxyCaller.d.ts +0 -1
- package/dist/proxyCaller.d.ts.map +1 -1
- package/dist/relaySdk.d.ts.map +1 -1
- package/dist/relayer.d.ts.map +1 -1
- package/dist/tokenBalances.d.ts +1 -1
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/tokens.d.ts +2 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/trails.d.ts +4 -4
- package/dist/trails.d.ts.map +1 -1
- package/dist/transactions.d.ts +4 -0
- package/dist/transactions.d.ts.map +1 -1
- package/dist/utils.d.ts +6 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/wallets.d.ts +247 -5
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/ChainFilterDropdown.d.ts +2 -0
- package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
- package/dist/widget/components/ConnectWallet.d.ts +1 -0
- package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
- package/dist/widget/components/DebugScreensDropdown.d.ts.map +1 -1
- package/dist/widget/components/ErrorDisplay.d.ts +9 -0
- package/dist/widget/components/ErrorDisplay.d.ts.map +1 -0
- package/dist/widget/components/FundSendForm.d.ts +2 -2
- package/dist/widget/components/FundSendForm.d.ts.map +1 -1
- package/dist/widget/components/OriginTransferInformation.d.ts +10 -0
- package/dist/widget/components/OriginTransferInformation.d.ts.map +1 -0
- package/dist/widget/components/PaySendForm.d.ts +2 -2
- package/dist/widget/components/PaySendForm.d.ts.map +1 -1
- package/dist/widget/components/QrCode.d.ts +1 -1
- package/dist/widget/components/QrCode.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Receipt.d.ts.map +1 -1
- package/dist/widget/components/Receive.d.ts +12 -0
- package/dist/widget/components/Receive.d.ts.map +1 -0
- package/dist/widget/components/RefundAddressInput.d.ts +13 -0
- package/dist/widget/components/RefundAddressInput.d.ts.map +1 -0
- package/dist/widget/components/Swap.d.ts +47 -0
- package/dist/widget/components/Swap.d.ts.map +1 -0
- package/dist/widget/components/SwapDisplay.d.ts +9 -0
- package/dist/widget/components/SwapDisplay.d.ts.map +1 -0
- package/dist/widget/components/TokenList.d.ts +0 -2
- package/dist/widget/components/TokenList.d.ts.map +1 -1
- package/dist/widget/components/TokenSelector.d.ts +26 -0
- package/dist/widget/components/TokenSelector.d.ts.map +1 -0
- package/dist/widget/components/TransferPendingVertical.d.ts +2 -0
- package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
- package/dist/widget/components/WalletConnect.d.ts.map +1 -1
- package/dist/widget/components/WalletConnectionPending.d.ts +12 -0
- package/dist/widget/components/WalletConnectionPending.d.ts.map +1 -0
- package/dist/widget/components/WalletList.d.ts.map +1 -1
- package/dist/widget/components/YellowWarningAnimation.d.ts +2 -0
- package/dist/widget/components/YellowWarningAnimation.d.ts.map +1 -0
- package/dist/widget/hooks/useAmountUsd.d.ts +1 -3
- package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
- package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts +22 -0
- package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +12 -6
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTokenList.d.ts +2 -3
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +19 -15
- package/src/aave.ts +13 -13
- package/src/address.ts +3 -0
- package/src/analytics.ts +192 -8
- package/src/apiClient.ts +1 -1
- package/src/cctpqueue.ts +1 -1
- package/src/chains.ts +45 -7
- package/src/constants.ts +7 -4
- package/src/decoders.ts +310 -0
- package/src/ens.ts +32 -0
- package/src/error.ts +101 -1
- package/src/intents.ts +10 -2
- package/src/lifi.ts +58 -0
- package/src/metaTxns.ts +1 -1
- package/src/mode.ts +1 -1
- package/src/morpho.ts +3 -3
- package/src/pools.ts +18 -18
- package/src/preconditions.ts +1 -1
- package/src/prepareSend.ts +463 -113
- package/src/prices.ts +26 -1
- package/src/proxyCaller.ts +2 -14
- package/src/relaySdk.ts +1 -0
- package/src/relayer.ts +8 -0
- package/src/tokenBalances.ts +24 -17
- package/src/tokens.ts +147 -22
- package/src/trails.ts +4 -4
- package/src/transactions.ts +35 -17
- package/src/utils.ts +28 -0
- package/src/wallets.ts +275 -35
- package/src/widget/compiled.css +2 -2
- package/src/widget/components/ChainFilterDropdown.tsx +42 -33
- package/src/widget/components/ChainImage.tsx +1 -1
- package/src/widget/components/ConnectWallet.tsx +92 -128
- package/src/widget/components/DebugScreensDropdown.tsx +6 -0
- package/src/widget/components/ErrorDisplay.tsx +150 -0
- package/src/widget/components/FundSendForm.tsx +78 -11
- package/src/widget/components/OriginTransferInformation.tsx +59 -0
- package/src/widget/components/PaySendForm.tsx +80 -13
- package/src/widget/components/QRCodeDeposit.tsx +6 -6
- package/src/widget/components/QrCode.tsx +278 -17
- package/src/widget/components/QuoteDetails.tsx +93 -25
- package/src/widget/components/Receipt.tsx +296 -103
- package/src/widget/components/Receive.tsx +146 -0
- package/src/widget/components/RecentTokens.tsx +1 -1
- package/src/widget/components/RefundAddressInput.tsx +149 -0
- package/src/widget/components/Swap.tsx +769 -0
- package/src/widget/components/SwapDisplay.tsx +68 -0
- package/src/widget/components/TokenList.tsx +27 -363
- package/src/widget/components/TokenSelector.tsx +405 -0
- package/src/widget/components/TransferPendingVertical.tsx +162 -112
- package/src/widget/components/WalletConnect.tsx +9 -7
- package/src/widget/components/WalletConnectionPending.tsx +157 -0
- package/src/widget/components/WalletList.tsx +6 -5
- package/src/widget/components/YellowWarningAnimation.tsx +146 -0
- package/src/widget/hooks/useAmountUsd.ts +3 -8
- package/src/widget/hooks/useCheckout.ts +3 -2
- package/src/widget/hooks/useDebugScreens.ts +583 -0
- package/src/widget/hooks/useSendForm.ts +111 -35
- package/src/widget/hooks/useTokenList.ts +155 -122
- package/src/widget/widget.tsx +503 -523
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type React from "react"
|
|
2
|
+
import { TokenImage } from "./TokenImage.js"
|
|
3
|
+
import type { PrepareSendQuote } from "../../prepareSend.js"
|
|
4
|
+
import { getTimeAgo } from "../../utils.js"
|
|
5
|
+
|
|
6
|
+
interface OriginTransferInformationProps {
|
|
7
|
+
quote: PrepareSendQuote
|
|
8
|
+
elapsedSeconds: number
|
|
9
|
+
timestamp?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const OriginTransferInformation: React.FC<
|
|
13
|
+
OriginTransferInformationProps
|
|
14
|
+
> = ({ quote, elapsedSeconds, timestamp }) => {
|
|
15
|
+
return (
|
|
16
|
+
<div className="w-full max-w-sm p-3 trails-border-radius-container trails-bg-secondary trails-text-secondary">
|
|
17
|
+
<div className="flex items-start justify-between">
|
|
18
|
+
{/* Left side - Chain and Token images with token name */}
|
|
19
|
+
<div className="flex items-start space-x-2">
|
|
20
|
+
{/* Token Image and Name */}
|
|
21
|
+
<div className="flex items-center space-x-2">
|
|
22
|
+
<div style={{ width: "24px", height: "24px" }}>
|
|
23
|
+
<TokenImage
|
|
24
|
+
imageUrl={quote.originToken.imageUrl}
|
|
25
|
+
symbol={quote.originToken.symbol}
|
|
26
|
+
chainId={quote.originChain.id}
|
|
27
|
+
size={24}
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
<div className="flex flex-col">
|
|
31
|
+
<span className="text-xs font-medium text-left text-gray-900 dark:text-white">
|
|
32
|
+
{quote.originToken.name}
|
|
33
|
+
</span>
|
|
34
|
+
<span className="text-xs text-gray-500 dark:text-gray-400 flex items-center">
|
|
35
|
+
{getTimeAgo(timestamp)}
|
|
36
|
+
<span
|
|
37
|
+
className="ml-1 font-mono animate-pulse text-gray-500 dark:text-gray-400"
|
|
38
|
+
title="Elapsed time"
|
|
39
|
+
>
|
|
40
|
+
{elapsedSeconds}s
|
|
41
|
+
</span>
|
|
42
|
+
</span>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{/* Right side - USD value and amount */}
|
|
48
|
+
<div className="text-right">
|
|
49
|
+
<div className="text-xs font-medium text-right text-gray-900 dark:text-white">
|
|
50
|
+
{quote.originAmountUsdDisplay}
|
|
51
|
+
</div>
|
|
52
|
+
<div className="text-xs text-gray-600 dark:text-gray-400">
|
|
53
|
+
{quote.originAmountFormatted} {quote.originToken.symbol}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ChevronDown, Loader2, TrendingUp } from "lucide-react"
|
|
1
|
+
import { ChevronDown, Loader2, RefreshCcw, TrendingUp } from "lucide-react"
|
|
2
2
|
import type React from "react"
|
|
3
|
-
import { useCallback, useEffect, useRef } from "react"
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react"
|
|
4
4
|
import type { Account, WalletClient } from "viem"
|
|
5
5
|
import { isAddress } from "viem"
|
|
6
6
|
import type { TransactionState } from "../../transactions.js"
|
|
@@ -12,6 +12,7 @@ import { FeeOptions } from "./FeeOptions.js"
|
|
|
12
12
|
import { TokenImage } from "./TokenImage.js"
|
|
13
13
|
import { QuoteDetails } from "./QuoteDetails.js"
|
|
14
14
|
import { TruncatedAddress } from "./TruncatedAddress.js"
|
|
15
|
+
// import { RefundAddressInput } from "./RefundAddressInput.js"
|
|
15
16
|
import { type PrepareSendQuote, TradeType } from "../../prepareSend.js"
|
|
16
17
|
import { getChainInfo, getChainColor } from "../../chains.js"
|
|
17
18
|
import { formatTvl } from "../../prices.js"
|
|
@@ -20,6 +21,7 @@ import morphoLogo from "../assets/morpho.svg"
|
|
|
20
21
|
import { formatUsdAmountDisplay } from "../../tokenBalances.js"
|
|
21
22
|
import { MINIMUM_USD_AMOUNT_FOR_SWAP } from "../../constants.js"
|
|
22
23
|
import { ScreenHeader } from "./ScreenHeader.js"
|
|
24
|
+
import { ErrorDisplay } from "./ErrorDisplay.js"
|
|
23
25
|
|
|
24
26
|
interface PaySendFormProps {
|
|
25
27
|
selectedToken: Token
|
|
@@ -41,7 +43,7 @@ interface PaySendFormProps {
|
|
|
41
43
|
gasless?: boolean
|
|
42
44
|
setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
|
|
43
45
|
quoteProvider?: string
|
|
44
|
-
fundMethod?: string
|
|
46
|
+
fundMethod?: string
|
|
45
47
|
onNavigateToMeshConnect?: (
|
|
46
48
|
props: {
|
|
47
49
|
toTokenSymbol: string
|
|
@@ -52,7 +54,7 @@ interface PaySendFormProps {
|
|
|
52
54
|
quote?: PrepareSendQuote | null,
|
|
53
55
|
) => void
|
|
54
56
|
onAmountUpdate?: (amount: string) => void
|
|
55
|
-
mode?: "pay" | "fund" | "earn"
|
|
57
|
+
mode?: "pay" | "fund" | "earn" | "swap" | "receive"
|
|
56
58
|
selectedPool?: {
|
|
57
59
|
id: string
|
|
58
60
|
name: string
|
|
@@ -102,6 +104,9 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
102
104
|
selectedPool,
|
|
103
105
|
checkoutOnHandlers,
|
|
104
106
|
}) => {
|
|
107
|
+
// const [isRefundAddressOpen, setIsRefundAddressOpen] = useState(false)
|
|
108
|
+
// const [refundAddress, setRefundAddress] = useState<string>(account.address)
|
|
109
|
+
const [refetchTrigger, setRefetchTrigger] = useState(0)
|
|
105
110
|
const {
|
|
106
111
|
amount,
|
|
107
112
|
amountRaw,
|
|
@@ -138,6 +143,9 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
138
143
|
supportedChains,
|
|
139
144
|
isValidCustomToken,
|
|
140
145
|
prepareSendQuote,
|
|
146
|
+
quoteError,
|
|
147
|
+
quoteErrorPrettified,
|
|
148
|
+
isSameTokenWithoutCustomCalldata,
|
|
141
149
|
} = useSendForm({
|
|
142
150
|
account,
|
|
143
151
|
toAmount,
|
|
@@ -145,6 +153,7 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
145
153
|
toChainId,
|
|
146
154
|
toToken,
|
|
147
155
|
toCalldata,
|
|
156
|
+
// refundAddress,
|
|
148
157
|
walletClient,
|
|
149
158
|
onTransactionStateChange,
|
|
150
159
|
onError,
|
|
@@ -162,6 +171,7 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
162
171
|
mode,
|
|
163
172
|
onNavigateToMeshConnect,
|
|
164
173
|
checkoutOnHandlers,
|
|
174
|
+
refetchTrigger,
|
|
165
175
|
})
|
|
166
176
|
|
|
167
177
|
// Handle amount input changes with decimal validation
|
|
@@ -177,6 +187,11 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
177
187
|
[setAmount],
|
|
178
188
|
)
|
|
179
189
|
|
|
190
|
+
// Handle manual quote refetch
|
|
191
|
+
const handleRefetchQuote = useCallback(() => {
|
|
192
|
+
setRefetchTrigger((prev) => prev + 1)
|
|
193
|
+
}, [])
|
|
194
|
+
|
|
180
195
|
// Call onAmountUpdate when amountRaw changes
|
|
181
196
|
useEffect(() => {
|
|
182
197
|
if (onAmountUpdate) {
|
|
@@ -373,7 +388,7 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
373
388
|
<div className="mb-4">
|
|
374
389
|
<label
|
|
375
390
|
htmlFor="destination-chain"
|
|
376
|
-
className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300"
|
|
391
|
+
className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300 text-left"
|
|
377
392
|
>
|
|
378
393
|
Destination Chain
|
|
379
394
|
</label>
|
|
@@ -436,7 +451,7 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
436
451
|
<div className="mb-4">
|
|
437
452
|
<label
|
|
438
453
|
htmlFor="token"
|
|
439
|
-
className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300"
|
|
454
|
+
className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300 text-left"
|
|
440
455
|
>
|
|
441
456
|
Receive Token
|
|
442
457
|
</label>
|
|
@@ -505,7 +520,7 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
505
520
|
<div className="mb-2">
|
|
506
521
|
<label
|
|
507
522
|
htmlFor="amount"
|
|
508
|
-
className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300"
|
|
523
|
+
className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300 text-left"
|
|
509
524
|
>
|
|
510
525
|
Amount to {mode === "earn" ? "Deposit" : "Receive"}
|
|
511
526
|
</label>
|
|
@@ -526,7 +541,7 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
526
541
|
</div>
|
|
527
542
|
{amountUsdDisplay && selectedDestToken?.symbol && (
|
|
528
543
|
<div className="h-6 mt-1">
|
|
529
|
-
<div className="text-sm text-gray-400">
|
|
544
|
+
<div className="text-sm text-gray-400 text-left">
|
|
530
545
|
≈ {amountUsdDisplay}
|
|
531
546
|
</div>
|
|
532
547
|
</div>
|
|
@@ -537,10 +552,37 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
537
552
|
{/* Receive Section - Similar to FundSendForm */}
|
|
538
553
|
{(toAmount || toChainId || toToken) && (
|
|
539
554
|
<div className="space-y-1">
|
|
540
|
-
<div
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
555
|
+
<div className="flex items-center justify-between">
|
|
556
|
+
<div
|
|
557
|
+
className={`text-lg font-semibold text-left ${"text-gray-900 dark:text-white"}`}
|
|
558
|
+
>
|
|
559
|
+
{mode === "earn" ? "Deposit" : "Receive"}
|
|
560
|
+
</div>
|
|
561
|
+
<button
|
|
562
|
+
type="button"
|
|
563
|
+
onClick={handleRefetchQuote}
|
|
564
|
+
disabled={
|
|
565
|
+
isLoadingQuote ||
|
|
566
|
+
!amount ||
|
|
567
|
+
!selectedDestToken ||
|
|
568
|
+
!selectedDestinationChain ||
|
|
569
|
+
!isValidRecipient
|
|
570
|
+
}
|
|
571
|
+
className={`p-2 rounded-md transition-colors cursor-pointer ${
|
|
572
|
+
isLoadingQuote ||
|
|
573
|
+
!amount ||
|
|
574
|
+
!selectedDestToken ||
|
|
575
|
+
!selectedDestinationChain ||
|
|
576
|
+
!isValidRecipient
|
|
577
|
+
? "opacity-50 cursor-not-allowed text-gray-400 dark:text-gray-500"
|
|
578
|
+
: "text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:text-gray-700 dark:hover:text-gray-200"
|
|
579
|
+
}`}
|
|
580
|
+
title="Refetch quote"
|
|
581
|
+
>
|
|
582
|
+
<RefreshCcw
|
|
583
|
+
className={`h-4 w-4 ${isLoadingQuote ? "animate-spin" : ""}`}
|
|
584
|
+
/>
|
|
585
|
+
</button>
|
|
544
586
|
</div>
|
|
545
587
|
|
|
546
588
|
<div className="p-2">
|
|
@@ -653,6 +695,16 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
653
695
|
</div>
|
|
654
696
|
)}
|
|
655
697
|
|
|
698
|
+
{/* Refund Address Input */}
|
|
699
|
+
{/* <RefundAddressInput
|
|
700
|
+
account={account}
|
|
701
|
+
isOpen={isRefundAddressOpen}
|
|
702
|
+
onToggle={() => setIsRefundAddressOpen(!isRefundAddressOpen)}
|
|
703
|
+
refundAddress={refundAddress}
|
|
704
|
+
onRefundAddressChange={setRefundAddress}
|
|
705
|
+
chainId={selectedDestinationChain.id}
|
|
706
|
+
/> */}
|
|
707
|
+
|
|
656
708
|
{/* Fee Options */}
|
|
657
709
|
<FeeOptions
|
|
658
710
|
options={FEE_TOKENS}
|
|
@@ -661,6 +713,18 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
661
713
|
/>
|
|
662
714
|
|
|
663
715
|
{/* Warning Messages - Show only one at a time */}
|
|
716
|
+
{isSameTokenWithoutCustomCalldata ? (
|
|
717
|
+
<ErrorDisplay
|
|
718
|
+
errorPrettified="Cannot swap to the same token on the same chain without custom calldata. Please select a different destination token."
|
|
719
|
+
severity="error"
|
|
720
|
+
/>
|
|
721
|
+
) : (
|
|
722
|
+
<ErrorDisplay
|
|
723
|
+
errorPrettified={quoteErrorPrettified}
|
|
724
|
+
error={quoteError}
|
|
725
|
+
severity="warning"
|
|
726
|
+
/>
|
|
727
|
+
)}
|
|
664
728
|
{prepareSendQuote?.noSufficientBalance ? (
|
|
665
729
|
<div className="px-2 py-3 rounded-lg bg-amber-500/10 border border-solid border-amber-500/30">
|
|
666
730
|
<div className="flex items-center space-x-2">
|
|
@@ -719,7 +783,8 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
719
783
|
!isValidCustomToken ||
|
|
720
784
|
isLoadingQuote ||
|
|
721
785
|
!prepareSendQuote ||
|
|
722
|
-
prepareSendQuote?.noSufficientBalance
|
|
786
|
+
prepareSendQuote?.noSufficientBalance ||
|
|
787
|
+
isSameTokenWithoutCustomCalldata
|
|
723
788
|
}
|
|
724
789
|
className={`w-full font-semibold py-4 px-4 trails-border-radius-button transition-colors bg-blue-500 hover:bg-blue-600 disabled:bg-gray-300 text-white disabled:text-gray-500 disabled:cursor-not-allowed cursor-pointer relative`}
|
|
725
790
|
>
|
|
@@ -730,6 +795,8 @@ export const PaySendForm: React.FC<PaySendFormProps> = ({
|
|
|
730
795
|
/>
|
|
731
796
|
<span>{buttonText}</span>
|
|
732
797
|
</div>
|
|
798
|
+
) : isSameTokenWithoutCustomCalldata ? (
|
|
799
|
+
"Select Different Tokens"
|
|
733
800
|
) : prepareSendQuote?.noSufficientBalance ? (
|
|
734
801
|
"Insufficient Balance"
|
|
735
802
|
) : (
|
|
@@ -23,12 +23,12 @@ export const QRCodeDeposit: React.FC<QRCodeDepositProps> = ({
|
|
|
23
23
|
|
|
24
24
|
const eip631Url = useMemo(() => {
|
|
25
25
|
if (!quote) return ""
|
|
26
|
-
return `ethereum:${quote.
|
|
26
|
+
return `ethereum:${quote.originDepositAddress}@${quote.originChain.id}`
|
|
27
27
|
}, [quote])
|
|
28
28
|
|
|
29
29
|
const eip681Url = useMemo(() => {
|
|
30
30
|
if (!quote) return ""
|
|
31
|
-
return `ethereum:${quote.originToken.contractAddress}@${quote.originChain.id}/transfer?address=${quote.
|
|
31
|
+
return `ethereum:${quote.originToken.contractAddress}@${quote.originChain.id}/transfer?address=${quote.originDepositAddress}&uint256=${quote.originAmount}`
|
|
32
32
|
}, [quote])
|
|
33
33
|
|
|
34
34
|
return (
|
|
@@ -64,7 +64,7 @@ export const QRCodeDeposit: React.FC<QRCodeDepositProps> = ({
|
|
|
64
64
|
>
|
|
65
65
|
<QrCode
|
|
66
66
|
url={useSimpleQrCode ? eip631Url : eip681Url}
|
|
67
|
-
size={
|
|
67
|
+
size={300}
|
|
68
68
|
/>
|
|
69
69
|
</button>
|
|
70
70
|
{!useSimpleQrCode && (
|
|
@@ -114,7 +114,7 @@ export const QRCodeDeposit: React.FC<QRCodeDepositProps> = ({
|
|
|
114
114
|
</span>
|
|
115
115
|
<a
|
|
116
116
|
href={getExplorerUrlForAddress({
|
|
117
|
-
address: quote.
|
|
117
|
+
address: quote.originDepositAddress,
|
|
118
118
|
chainId: quote.originChain.id,
|
|
119
119
|
})}
|
|
120
120
|
target="_blank"
|
|
@@ -122,8 +122,8 @@ export const QRCodeDeposit: React.FC<QRCodeDepositProps> = ({
|
|
|
122
122
|
className="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400 hover:underline transition-all"
|
|
123
123
|
>
|
|
124
124
|
<span>
|
|
125
|
-
{quote.
|
|
126
|
-
{quote.
|
|
125
|
+
{quote.originDepositAddress.slice(0, 6)}...
|
|
126
|
+
{quote.originDepositAddress.slice(-4)}
|
|
127
127
|
</span>
|
|
128
128
|
<ExternalLink className="w-3 h-3" />
|
|
129
129
|
</a>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type React from "react"
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef } from "react"
|
|
3
|
+
import QRCodeUtil from "qrcode"
|
|
3
4
|
|
|
4
5
|
interface QrCodeProps {
|
|
5
6
|
url: string
|
|
@@ -8,31 +9,291 @@ interface QrCodeProps {
|
|
|
8
9
|
className?: string
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
const CONNECTING_ERROR_MARGIN = 0.1
|
|
13
|
+
const CIRCLE_SIZE_MODIFIER = 2.5
|
|
14
|
+
const QRCODE_MATRIX_MARGIN = 7
|
|
15
|
+
|
|
16
|
+
function isAdjecentDots(cy: number, otherCy: number, cellSize: number) {
|
|
17
|
+
if (cy === otherCy) {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
const diff = cy - otherCy < 0 ? otherCy - cy : cy - otherCy
|
|
21
|
+
return diff <= cellSize + CONNECTING_ERROR_MARGIN
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getMatrix(
|
|
25
|
+
value: string,
|
|
26
|
+
errorCorrectionLevel: QRCodeUtil.QRCodeErrorCorrectionLevel,
|
|
27
|
+
) {
|
|
28
|
+
const arr = Array.prototype.slice.call(
|
|
29
|
+
QRCodeUtil.create(value, {
|
|
30
|
+
errorCorrectionLevel,
|
|
31
|
+
}).modules.data,
|
|
32
|
+
0,
|
|
33
|
+
)
|
|
34
|
+
const sqrt = Math.sqrt(arr.length)
|
|
35
|
+
return arr.reduce(
|
|
36
|
+
(rows, key, index) =>
|
|
37
|
+
(index % sqrt === 0
|
|
38
|
+
? rows.push([key])
|
|
39
|
+
: rows[rows.length - 1].push(key)) && rows,
|
|
40
|
+
[],
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
11
44
|
export const QrCode: React.FC<QrCodeProps> = ({
|
|
12
45
|
url,
|
|
13
46
|
imageUrl,
|
|
14
47
|
size = 250,
|
|
15
48
|
className = "",
|
|
16
49
|
}) => {
|
|
50
|
+
const svgRef = useRef<SVGSVGElement>(null)
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!svgRef.current) return
|
|
54
|
+
|
|
55
|
+
// Clear existing content
|
|
56
|
+
while (svgRef.current.firstChild) {
|
|
57
|
+
svgRef.current.removeChild(svgRef.current.firstChild)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const strokeWidth = 5
|
|
61
|
+
const padding = 8
|
|
62
|
+
const logoSize = size * 0.16 // 16% of size for logo
|
|
63
|
+
const matrix = getMatrix(url, "Q")
|
|
64
|
+
const cellSize = (size - 2 * padding) / matrix.length
|
|
65
|
+
|
|
66
|
+
// Corner squares positions
|
|
67
|
+
const qrList = [
|
|
68
|
+
{ x: 0, y: 0 }, // Top-left
|
|
69
|
+
{ x: 1, y: 0 }, // Top-right
|
|
70
|
+
{ x: 0, y: 1 }, // Bottom-left
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
// Draw corner squares
|
|
74
|
+
qrList.forEach(({ x, y }) => {
|
|
75
|
+
const x1 = (matrix.length - QRCODE_MATRIX_MARGIN) * cellSize * x + padding
|
|
76
|
+
const y1 = (matrix.length - QRCODE_MATRIX_MARGIN) * cellSize * y + padding
|
|
77
|
+
const borderRadius = 0.45
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < qrList.length; i += 1) {
|
|
80
|
+
const dotSize = cellSize * (QRCODE_MATRIX_MARGIN - i * 2)
|
|
81
|
+
const rect = document.createElementNS(
|
|
82
|
+
"http://www.w3.org/2000/svg",
|
|
83
|
+
"rect",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if (i === 0) {
|
|
87
|
+
// Outer square: transparent with dark stroke
|
|
88
|
+
rect.setAttribute("fill", "transparent")
|
|
89
|
+
rect.setAttribute("stroke", "#141414")
|
|
90
|
+
rect.setAttribute("stroke-width", strokeWidth.toString())
|
|
91
|
+
rect.setAttribute("width", (dotSize - strokeWidth).toString())
|
|
92
|
+
rect.setAttribute("height", (dotSize - strokeWidth).toString())
|
|
93
|
+
rect.setAttribute(
|
|
94
|
+
"rx",
|
|
95
|
+
((dotSize - strokeWidth) * borderRadius).toString(),
|
|
96
|
+
)
|
|
97
|
+
rect.setAttribute(
|
|
98
|
+
"ry",
|
|
99
|
+
((dotSize - strokeWidth) * borderRadius).toString(),
|
|
100
|
+
)
|
|
101
|
+
rect.setAttribute(
|
|
102
|
+
"x",
|
|
103
|
+
(y1 + cellSize * i + strokeWidth / 2).toString(),
|
|
104
|
+
)
|
|
105
|
+
rect.setAttribute(
|
|
106
|
+
"y",
|
|
107
|
+
(x1 + cellSize * i + strokeWidth / 2).toString(),
|
|
108
|
+
)
|
|
109
|
+
} else if (i === 1) {
|
|
110
|
+
// Middle square: transparent with no stroke
|
|
111
|
+
rect.setAttribute("fill", "transparent")
|
|
112
|
+
rect.setAttribute("stroke", "#141414")
|
|
113
|
+
rect.setAttribute("stroke-width", "0")
|
|
114
|
+
rect.setAttribute("width", dotSize.toString())
|
|
115
|
+
rect.setAttribute("height", dotSize.toString())
|
|
116
|
+
rect.setAttribute("rx", (dotSize * borderRadius).toString())
|
|
117
|
+
rect.setAttribute("ry", (dotSize * borderRadius).toString())
|
|
118
|
+
rect.setAttribute("x", (y1 + cellSize * i).toString())
|
|
119
|
+
rect.setAttribute("y", (x1 + cellSize * i).toString())
|
|
120
|
+
} else {
|
|
121
|
+
// Inner square: dark fill with no stroke
|
|
122
|
+
rect.setAttribute("fill", "#141414")
|
|
123
|
+
rect.setAttribute("stroke", "#141414")
|
|
124
|
+
rect.setAttribute("stroke-width", "0")
|
|
125
|
+
rect.setAttribute("width", dotSize.toString())
|
|
126
|
+
rect.setAttribute("height", dotSize.toString())
|
|
127
|
+
rect.setAttribute("rx", (dotSize * borderRadius).toString())
|
|
128
|
+
rect.setAttribute("ry", (dotSize * borderRadius).toString())
|
|
129
|
+
rect.setAttribute("x", (y1 + cellSize * i).toString())
|
|
130
|
+
rect.setAttribute("y", (x1 + cellSize * i).toString())
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (svgRef.current) {
|
|
134
|
+
svgRef.current.appendChild(rect)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
// Calculate logo area to exclude from data dots
|
|
140
|
+
const clearArenaSize = Math.floor((logoSize + 25) / cellSize)
|
|
141
|
+
const matrixMiddleStart = matrix.length / 2 - clearArenaSize / 2
|
|
142
|
+
const matrixMiddleEnd = matrix.length / 2 + clearArenaSize / 2 - 1
|
|
143
|
+
|
|
144
|
+
const circles: [number, number][] = []
|
|
145
|
+
|
|
146
|
+
// Get coordinates for each QR code dot
|
|
147
|
+
matrix.forEach((row: QRCodeUtil.QRCode[], i: number) => {
|
|
148
|
+
row.forEach((_, j: number) => {
|
|
149
|
+
if (matrix[i][j]) {
|
|
150
|
+
// Skip corner areas
|
|
151
|
+
if (
|
|
152
|
+
!(
|
|
153
|
+
(i < QRCODE_MATRIX_MARGIN && j < QRCODE_MATRIX_MARGIN) ||
|
|
154
|
+
(i > matrix.length - (QRCODE_MATRIX_MARGIN + 1) &&
|
|
155
|
+
j < QRCODE_MATRIX_MARGIN) ||
|
|
156
|
+
(i < QRCODE_MATRIX_MARGIN &&
|
|
157
|
+
j > matrix.length - (QRCODE_MATRIX_MARGIN + 1))
|
|
158
|
+
)
|
|
159
|
+
) {
|
|
160
|
+
// Skip logo area
|
|
161
|
+
if (
|
|
162
|
+
!(
|
|
163
|
+
i > matrixMiddleStart &&
|
|
164
|
+
i < matrixMiddleEnd &&
|
|
165
|
+
j > matrixMiddleStart &&
|
|
166
|
+
j < matrixMiddleEnd
|
|
167
|
+
)
|
|
168
|
+
) {
|
|
169
|
+
const cx = i * cellSize + cellSize / 2 + padding
|
|
170
|
+
const cy = j * cellSize + cellSize / 2 + padding
|
|
171
|
+
circles.push([cx, cy])
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// Group dots by x-coordinate
|
|
179
|
+
const circlesToConnect: Record<number, number[]> = {}
|
|
180
|
+
circles.forEach(([cx, cy]) => {
|
|
181
|
+
if (circlesToConnect[cx]) {
|
|
182
|
+
circlesToConnect[cx]?.push(cy)
|
|
183
|
+
} else {
|
|
184
|
+
circlesToConnect[cx] = [cy]
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// Draw individual dots (those without neighbors)
|
|
189
|
+
Object.entries(circlesToConnect)
|
|
190
|
+
.map(([cx, cys]) => {
|
|
191
|
+
const newCys = cys.filter((cy) =>
|
|
192
|
+
cys.every((otherCy) => !isAdjecentDots(cy, otherCy, cellSize)),
|
|
193
|
+
)
|
|
194
|
+
return [Number(cx), newCys] as [number, number[]]
|
|
195
|
+
})
|
|
196
|
+
.forEach(([cx, cys]) => {
|
|
197
|
+
cys.forEach((cy) => {
|
|
198
|
+
const circle = document.createElementNS(
|
|
199
|
+
"http://www.w3.org/2000/svg",
|
|
200
|
+
"circle",
|
|
201
|
+
)
|
|
202
|
+
circle.setAttribute("cx", cx.toString())
|
|
203
|
+
circle.setAttribute("cy", cy.toString())
|
|
204
|
+
circle.setAttribute("fill", "#141414")
|
|
205
|
+
circle.setAttribute("r", (cellSize / CIRCLE_SIZE_MODIFIER).toString())
|
|
206
|
+
if (svgRef.current) {
|
|
207
|
+
svgRef.current.appendChild(circle)
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
// Draw dots for connected groups (instead of lines)
|
|
213
|
+
Object.entries(circlesToConnect)
|
|
214
|
+
.filter(([_, cys]) => cys.length > 1)
|
|
215
|
+
.map(([cx, cys]) => {
|
|
216
|
+
const newCys = cys.filter((cy) =>
|
|
217
|
+
cys.some((otherCy) => isAdjecentDots(cy, otherCy, cellSize)),
|
|
218
|
+
)
|
|
219
|
+
return [Number(cx), newCys] as [number, number[]]
|
|
220
|
+
})
|
|
221
|
+
.map(([cx, cys]) => {
|
|
222
|
+
cys.sort((a, b) => (a < b ? -1 : 1))
|
|
223
|
+
const groups: number[][] = []
|
|
224
|
+
for (const cy of cys) {
|
|
225
|
+
const group = groups.find((item) =>
|
|
226
|
+
item.some((otherCy) => isAdjecentDots(cy, otherCy, cellSize)),
|
|
227
|
+
)
|
|
228
|
+
if (group) {
|
|
229
|
+
group.push(cy)
|
|
230
|
+
} else {
|
|
231
|
+
groups.push([cy])
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return [cx, groups.map((item) => [item[0], item[item.length - 1]])] as [
|
|
235
|
+
number,
|
|
236
|
+
number[][],
|
|
237
|
+
]
|
|
238
|
+
})
|
|
239
|
+
.forEach(([cx, groups]) => {
|
|
240
|
+
groups.forEach(([y1, y2]) => {
|
|
241
|
+
if (y1 !== undefined && y2 !== undefined) {
|
|
242
|
+
// Instead of drawing a line, draw individual dots at each position
|
|
243
|
+
const positions = []
|
|
244
|
+
for (let y = y1; y <= y2; y += cellSize) {
|
|
245
|
+
positions.push(y)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
positions.forEach((y) => {
|
|
249
|
+
const circle = document.createElementNS(
|
|
250
|
+
"http://www.w3.org/2000/svg",
|
|
251
|
+
"circle",
|
|
252
|
+
)
|
|
253
|
+
circle.setAttribute("cx", cx.toString())
|
|
254
|
+
circle.setAttribute("cy", y.toString())
|
|
255
|
+
circle.setAttribute("fill", "#141414")
|
|
256
|
+
circle.setAttribute(
|
|
257
|
+
"r",
|
|
258
|
+
(cellSize / CIRCLE_SIZE_MODIFIER).toString(),
|
|
259
|
+
)
|
|
260
|
+
if (svgRef.current) {
|
|
261
|
+
svgRef.current.appendChild(circle)
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
// Add logo if provided
|
|
269
|
+
if (imageUrl) {
|
|
270
|
+
const logoX = (size - logoSize) / 2
|
|
271
|
+
const logoY = (size - logoSize) / 2
|
|
272
|
+
|
|
273
|
+
const logo = document.createElementNS(
|
|
274
|
+
"http://www.w3.org/2000/svg",
|
|
275
|
+
"image",
|
|
276
|
+
)
|
|
277
|
+
logo.setAttribute("href", imageUrl)
|
|
278
|
+
logo.setAttribute("x", logoX.toString())
|
|
279
|
+
logo.setAttribute("y", logoY.toString())
|
|
280
|
+
logo.setAttribute("width", logoSize.toString())
|
|
281
|
+
logo.setAttribute("height", logoSize.toString())
|
|
282
|
+
if (svgRef.current) {
|
|
283
|
+
svgRef.current.appendChild(logo)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}, [url, imageUrl, size])
|
|
287
|
+
|
|
17
288
|
return (
|
|
18
289
|
<div className={`inline-block ${className}`}>
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
marginSize={2}
|
|
26
|
-
imageSettings={{
|
|
27
|
-
src: imageUrl || "https://trails.build/favicon.ico",
|
|
28
|
-
height: size * 0.16,
|
|
29
|
-
width: size * 0.16,
|
|
30
|
-
excavate: true,
|
|
31
|
-
}}
|
|
32
|
-
className="rounded-lg border border-solid border-gray-200 dark:border-gray-700"
|
|
290
|
+
<svg
|
|
291
|
+
ref={svgRef}
|
|
292
|
+
width={size}
|
|
293
|
+
height={size}
|
|
294
|
+
viewBox={`0 0 ${size} ${size}`}
|
|
295
|
+
className="rounded-lg bg-white"
|
|
33
296
|
/>
|
|
34
297
|
</div>
|
|
35
298
|
)
|
|
36
299
|
}
|
|
37
|
-
|
|
38
|
-
export default QrCode
|