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.
- package/dist/analytics.d.ts +68 -1
- package/dist/analytics.d.ts.map +1 -1
- package/dist/{ccip-BmFTEOaB.js → ccip-CWd4g9uZ.js} +1 -1
- package/dist/chains.d.ts +9 -3
- package/dist/chains.d.ts.map +1 -1
- package/dist/ens.d.ts +7 -0
- package/dist/ens.d.ts.map +1 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/{index-BPsVj7zK.js → index-BTUBzx4R.js} +23624 -21770
- package/dist/index.js +2 -2
- package/dist/lifi.d.ts +4 -0
- package/dist/lifi.d.ts.map +1 -0
- package/dist/mode.d.ts +1 -1
- package/dist/mode.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +3 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/prices.d.ts +2 -0
- package/dist/prices.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.map +1 -1
- package/dist/tokens.d.ts +2 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/trails.d.ts +3 -3
- package/dist/trails.d.ts.map +1 -1
- package/dist/transactions.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/FundSendForm.d.ts +2 -2
- package/dist/widget/components/FundSendForm.d.ts.map +1 -1
- 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/RefundAddressInput.d.ts +13 -0
- package/dist/widget/components/RefundAddressInput.d.ts.map +1 -0
- package/dist/widget/components/Swap.d.ts +43 -0
- package/dist/widget/components/Swap.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/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/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/useSendForm.d.ts +6 -4
- 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 +9 -6
- package/src/aave.ts +13 -13
- package/src/analytics.ts +87 -4
- package/src/chains.ts +45 -7
- package/src/constants.ts +4 -4
- package/src/ens.ts +17 -0
- package/src/error.ts +16 -1
- package/src/lifi.ts +58 -0
- package/src/mode.ts +1 -1
- package/src/morpho.ts +3 -3
- package/src/pools.ts +18 -18
- package/src/prepareSend.ts +35 -3
- package/src/prices.ts +21 -0
- package/src/relaySdk.ts +1 -0
- package/src/relayer.ts +8 -0
- package/src/tokenBalances.ts +3 -0
- package/src/tokens.ts +85 -19
- package/src/trails.ts +2 -2
- package/src/transactions.ts +1 -0
- package/src/wallets.ts +275 -35
- package/src/widget/compiled.css +1 -1
- 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 +3 -0
- package/src/widget/components/FundSendForm.tsx +17 -3
- package/src/widget/components/PaySendForm.tsx +16 -2
- package/src/widget/components/QRCodeDeposit.tsx +1 -1
- package/src/widget/components/QrCode.tsx +277 -16
- package/src/widget/components/Receipt.tsx +1 -1
- package/src/widget/components/RefundAddressInput.tsx +149 -0
- package/src/widget/components/Swap.tsx +648 -0
- package/src/widget/components/TokenList.tsx +27 -363
- package/src/widget/components/TokenSelector.tsx +405 -0
- 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/hooks/useAmountUsd.ts +3 -8
- package/src/widget/hooks/useCheckout.ts +3 -2
- package/src/widget/hooks/useSendForm.ts +66 -32
- package/src/widget/hooks/useTokenList.ts +158 -106
- package/src/widget/widget.tsx +335 -72
|
@@ -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
|
-
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
|
-
|
|
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
|
+
}
|