0xtrails 0.1.2 → 0.1.3

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.
Files changed (103) hide show
  1. package/dist/analytics.d.ts +68 -1
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/{ccip-BmFTEOaB.js → ccip-CWd4g9uZ.js} +1 -1
  4. package/dist/chains.d.ts +9 -3
  5. package/dist/chains.d.ts.map +1 -1
  6. package/dist/ens.d.ts +7 -0
  7. package/dist/ens.d.ts.map +1 -0
  8. package/dist/error.d.ts +2 -0
  9. package/dist/error.d.ts.map +1 -1
  10. package/dist/{index-BPsVj7zK.js → index-BTUBzx4R.js} +23624 -21770
  11. package/dist/index.js +2 -2
  12. package/dist/lifi.d.ts +4 -0
  13. package/dist/lifi.d.ts.map +1 -0
  14. package/dist/mode.d.ts +1 -1
  15. package/dist/mode.d.ts.map +1 -1
  16. package/dist/prepareSend.d.ts +3 -1
  17. package/dist/prepareSend.d.ts.map +1 -1
  18. package/dist/prices.d.ts +2 -0
  19. package/dist/prices.d.ts.map +1 -1
  20. package/dist/relaySdk.d.ts.map +1 -1
  21. package/dist/relayer.d.ts.map +1 -1
  22. package/dist/tokenBalances.d.ts.map +1 -1
  23. package/dist/tokens.d.ts +2 -1
  24. package/dist/tokens.d.ts.map +1 -1
  25. package/dist/trails.d.ts +3 -3
  26. package/dist/trails.d.ts.map +1 -1
  27. package/dist/transactions.d.ts.map +1 -1
  28. package/dist/wallets.d.ts +247 -5
  29. package/dist/wallets.d.ts.map +1 -1
  30. package/dist/widget/components/ChainFilterDropdown.d.ts +2 -0
  31. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  32. package/dist/widget/components/ConnectWallet.d.ts +1 -0
  33. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  34. package/dist/widget/components/DebugScreensDropdown.d.ts.map +1 -1
  35. package/dist/widget/components/FundSendForm.d.ts +2 -2
  36. package/dist/widget/components/FundSendForm.d.ts.map +1 -1
  37. package/dist/widget/components/PaySendForm.d.ts +2 -2
  38. package/dist/widget/components/PaySendForm.d.ts.map +1 -1
  39. package/dist/widget/components/QrCode.d.ts +1 -1
  40. package/dist/widget/components/QrCode.d.ts.map +1 -1
  41. package/dist/widget/components/RefundAddressInput.d.ts +13 -0
  42. package/dist/widget/components/RefundAddressInput.d.ts.map +1 -0
  43. package/dist/widget/components/Swap.d.ts +43 -0
  44. package/dist/widget/components/Swap.d.ts.map +1 -0
  45. package/dist/widget/components/TokenList.d.ts +0 -2
  46. package/dist/widget/components/TokenList.d.ts.map +1 -1
  47. package/dist/widget/components/TokenSelector.d.ts +26 -0
  48. package/dist/widget/components/TokenSelector.d.ts.map +1 -0
  49. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  50. package/dist/widget/components/WalletConnectionPending.d.ts +12 -0
  51. package/dist/widget/components/WalletConnectionPending.d.ts.map +1 -0
  52. package/dist/widget/components/WalletList.d.ts.map +1 -1
  53. package/dist/widget/hooks/useAmountUsd.d.ts +1 -3
  54. package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
  55. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  56. package/dist/widget/hooks/useSendForm.d.ts +6 -4
  57. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  58. package/dist/widget/hooks/useTokenList.d.ts +2 -3
  59. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  60. package/dist/widget/index.js +1 -1
  61. package/dist/widget/widget.d.ts.map +1 -1
  62. package/package.json +9 -6
  63. package/src/aave.ts +13 -13
  64. package/src/analytics.ts +87 -4
  65. package/src/chains.ts +45 -7
  66. package/src/constants.ts +4 -4
  67. package/src/ens.ts +17 -0
  68. package/src/error.ts +16 -1
  69. package/src/lifi.ts +58 -0
  70. package/src/mode.ts +1 -1
  71. package/src/morpho.ts +3 -3
  72. package/src/pools.ts +18 -18
  73. package/src/prepareSend.ts +35 -3
  74. package/src/prices.ts +21 -0
  75. package/src/relaySdk.ts +1 -0
  76. package/src/relayer.ts +8 -0
  77. package/src/tokenBalances.ts +3 -0
  78. package/src/tokens.ts +85 -19
  79. package/src/trails.ts +2 -2
  80. package/src/transactions.ts +1 -0
  81. package/src/wallets.ts +275 -35
  82. package/src/widget/compiled.css +1 -1
  83. package/src/widget/components/ChainFilterDropdown.tsx +42 -33
  84. package/src/widget/components/ChainImage.tsx +1 -1
  85. package/src/widget/components/ConnectWallet.tsx +92 -128
  86. package/src/widget/components/DebugScreensDropdown.tsx +3 -0
  87. package/src/widget/components/FundSendForm.tsx +17 -3
  88. package/src/widget/components/PaySendForm.tsx +16 -2
  89. package/src/widget/components/QRCodeDeposit.tsx +1 -1
  90. package/src/widget/components/QrCode.tsx +277 -16
  91. package/src/widget/components/Receipt.tsx +1 -1
  92. package/src/widget/components/RefundAddressInput.tsx +149 -0
  93. package/src/widget/components/Swap.tsx +648 -0
  94. package/src/widget/components/TokenList.tsx +27 -363
  95. package/src/widget/components/TokenSelector.tsx +405 -0
  96. package/src/widget/components/WalletConnect.tsx +9 -7
  97. package/src/widget/components/WalletConnectionPending.tsx +157 -0
  98. package/src/widget/components/WalletList.tsx +6 -5
  99. package/src/widget/hooks/useAmountUsd.ts +3 -8
  100. package/src/widget/hooks/useCheckout.ts +3 -2
  101. package/src/widget/hooks/useSendForm.ts +66 -32
  102. package/src/widget/hooks/useTokenList.ts +158 -106
  103. package/src/widget/widget.tsx +335 -72
@@ -1,5 +1,6 @@
1
1
  import type React from "react"
2
- import { QRCodeSVG } from "qrcode.react"
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
- <QRCodeSVG
20
- value={url}
21
- size={size}
22
- bgColor="#FFFFFF"
23
- fgColor="#000000"
24
- level="M"
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
- }}
290
+ <svg
291
+ ref={svgRef}
292
+ width={size}
293
+ height={size}
294
+ viewBox={`0 0 ${size} ${size}`}
32
295
  className="rounded-lg border border-solid border-gray-200 dark:border-gray-700"
33
296
  />
34
297
  </div>
35
298
  )
36
299
  }
37
-
38
- export default QrCode
@@ -152,7 +152,7 @@ export const Receipt: React.FC<ReceiptProps> = ({
152
152
  className={`transition-all duration-500 ease-out ${showContent ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
153
153
  >
154
154
  <h2 className="mt-4 text-2xl font-bold text-gray-900 dark:text-white">
155
- Transaction Confirmed
155
+ Completed
156
156
  </h2>
157
157
  {completionTimeSeconds > 0 && (
158
158
  <div
@@ -0,0 +1,149 @@
1
+ import { ChevronDown, ChevronRight } from "lucide-react"
2
+ import type React from "react"
3
+ import { useState, useEffect } from "react"
4
+ import type { Account } from "viem"
5
+ import { isAddress } from "viem"
6
+ import { TruncatedAddress } from "./TruncatedAddress.js"
7
+ import { useResolveEnsAddress } from "../../ens.js"
8
+
9
+ interface RefundAddressInputProps {
10
+ account: Account
11
+ isOpen: boolean
12
+ onToggle: () => void
13
+ refundAddress: string
14
+ onRefundAddressChange: (address: string) => void
15
+ chainId: number
16
+ }
17
+
18
+ export const RefundAddressInput: React.FC<RefundAddressInputProps> = ({
19
+ account,
20
+ isOpen,
21
+ onToggle,
22
+ refundAddress,
23
+ onRefundAddressChange,
24
+ chainId,
25
+ }) => {
26
+ const [inputValue, setInputValue] = useState(refundAddress)
27
+
28
+ // Resolve ENS names to addresses
29
+ const { ensAddress, isLoading } = useResolveEnsAddress({
30
+ textInput: inputValue,
31
+ })
32
+
33
+ // Update the refund address when ENS is resolved
34
+ useEffect(() => {
35
+ if (ensAddress && inputValue.endsWith(".eth")) {
36
+ onRefundAddressChange(ensAddress)
37
+ }
38
+ }, [ensAddress, inputValue, onRefundAddressChange])
39
+
40
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
41
+ const value = e.target.value
42
+ setInputValue(value)
43
+ // Only update immediately if it's not an ENS name (let ENS resolution handle .eth names)
44
+ if (!value.endsWith(".eth")) {
45
+ onRefundAddressChange(value)
46
+ }
47
+ }
48
+
49
+ const handleUseAccount = () => {
50
+ setInputValue(account.address)
51
+ onRefundAddressChange(account.address)
52
+ }
53
+
54
+ // Check if the input is valid (either a valid address or a resolved ENS name)
55
+ const isValidAddress =
56
+ inputValue &&
57
+ (isAddress(inputValue) || (ensAddress && inputValue.endsWith(".eth")))
58
+
59
+ return (
60
+ <div className="space-y-2">
61
+ <button
62
+ type="button"
63
+ onClick={onToggle}
64
+ className="flex items-center space-x-1 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 cursor-pointer"
65
+ >
66
+ <span>Set refund address</span>
67
+ {isOpen ? (
68
+ <ChevronDown className="w-3 h-3" />
69
+ ) : (
70
+ <ChevronRight className="w-3 h-3" />
71
+ )}
72
+ </button>
73
+
74
+ {isOpen && (
75
+ <div className="space-y-2 p-3 trails-border-radius-container trails-bg-secondary trails-border">
76
+ <div className="flex justify-between items-center">
77
+ <label
78
+ htmlFor="refund-address"
79
+ className="text-sm font-medium text-gray-700 dark:text-gray-300"
80
+ >
81
+ Refund Address
82
+ </label>
83
+ <button
84
+ type="button"
85
+ onClick={handleUseAccount}
86
+ disabled={
87
+ inputValue.toLowerCase() === account.address.toLowerCase()
88
+ }
89
+ className={`px-2 py-1 text-xs cursor-pointer trails-border-radius-button transition-colors ${
90
+ inputValue.toLowerCase() === account.address.toLowerCase()
91
+ ? "bg-gray-400 cursor-not-allowed text-white"
92
+ : "bg-blue-500 hover:bg-blue-600 text-white"
93
+ }`}
94
+ >
95
+ Use Account
96
+ </button>
97
+ </div>
98
+
99
+ <input
100
+ id="refund-address"
101
+ type="text"
102
+ value={inputValue}
103
+ onChange={handleInputChange}
104
+ placeholder="0x... or name.eth"
105
+ className="block w-full px-3 py-2 border border-solid trails-border-radius-input focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm trails-input"
106
+ />
107
+
108
+ {inputValue && (
109
+ <div className="text-xs text-gray-500 dark:text-gray-400">
110
+ {isValidAddress ? (
111
+ <div className="space-y-1">
112
+ <div className="flex items-center space-x-2">
113
+ <span>Refund to:</span>
114
+ <TruncatedAddress
115
+ address={
116
+ inputValue.endsWith(".eth")
117
+ ? ensAddress || inputValue
118
+ : inputValue
119
+ }
120
+ chainId={chainId}
121
+ />
122
+ </div>
123
+ {inputValue.endsWith(".eth") && ensAddress && (
124
+ <div className="text-gray-400">
125
+ Resolved from: {inputValue}
126
+ </div>
127
+ )}
128
+ </div>
129
+ ) : inputValue.endsWith(".eth") && isLoading ? (
130
+ <div className="flex items-center space-x-2 text-blue-500">
131
+ <div className="animate-spin rounded-full h-3 w-3 border-b-2 border-blue-500"></div>
132
+ <span>Resolving ENS name...</span>
133
+ </div>
134
+ ) : (
135
+ <span className="text-red-500">Invalid address format</span>
136
+ )}
137
+ </div>
138
+ )}
139
+
140
+ <p className="text-xs text-gray-500 dark:text-gray-400">
141
+ If the transaction fails or cannot be completed, funds will be
142
+ refunded to this address. Please make sure that this address can
143
+ receive funds on both the origin and destination chain.
144
+ </p>
145
+ </div>
146
+ )}
147
+ </div>
148
+ )
149
+ }