@rhinestone/deposit-modal 0.0.0-dev-20260608080045
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/README.md +134 -0
- package/dist/DepositModalReown-6SUEC5IU.mjs +60 -0
- package/dist/DepositModalReown-DNW4GH6L.cjs +60 -0
- package/dist/QRCode-5DXFNKI2.cjs +58 -0
- package/dist/QRCode-WUC652SH.mjs +58 -0
- package/dist/WithdrawModalReown-7UAGSOSU.mjs +37 -0
- package/dist/WithdrawModalReown-OUWBSKSM.cjs +37 -0
- package/dist/caip-CsslyHGL.d.cts +62 -0
- package/dist/caip-CsslyHGL.d.ts +62 -0
- package/dist/chunk-2SMS542Q.cjs +1654 -0
- package/dist/chunk-33H6O5UU.cjs +162 -0
- package/dist/chunk-6YRDD462.mjs +614 -0
- package/dist/chunk-GPSBM66J.mjs +162 -0
- package/dist/chunk-KAWJABTW.mjs +3765 -0
- package/dist/chunk-KJ2RR2D4.mjs +7619 -0
- package/dist/chunk-MILJQWPT.cjs +614 -0
- package/dist/chunk-RABZINV3.cjs +3765 -0
- package/dist/chunk-TKQYTBU6.mjs +1654 -0
- package/dist/chunk-VVJAIMKB.cjs +7619 -0
- package/dist/constants.cjs +70 -0
- package/dist/constants.d.cts +21 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.mjs +70 -0
- package/dist/deposit.cjs +8 -0
- package/dist/deposit.d.cts +11 -0
- package/dist/deposit.d.ts +11 -0
- package/dist/deposit.mjs +8 -0
- package/dist/index.cjs +86 -0
- package/dist/index.d.cts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.mjs +86 -0
- package/dist/styles.css +5143 -0
- package/dist/styles.d.ts +3 -0
- package/dist/types-BMcGO5k_.d.cts +432 -0
- package/dist/types-BMcGO5k_.d.ts +432 -0
- package/dist/withdraw.cjs +8 -0
- package/dist/withdraw.d.cts +11 -0
- package/dist/withdraw.d.ts +11 -0
- package/dist/withdraw.mjs +8 -0
- package/package.json +190 -0
|
@@ -0,0 +1,3765 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CHAIN_BY_ID,
|
|
3
|
+
NATIVE_TOKEN_ADDRESS,
|
|
4
|
+
getChainIcon,
|
|
5
|
+
getChainName,
|
|
6
|
+
getExplorerTxUrl,
|
|
7
|
+
getSolanaTokenByMint,
|
|
8
|
+
getSupportedChainIds,
|
|
9
|
+
getTargetTokenSymbol,
|
|
10
|
+
getTokenAddress,
|
|
11
|
+
getTokenDecimalsByAddress,
|
|
12
|
+
getTokenIcon,
|
|
13
|
+
getTokenSymbol,
|
|
14
|
+
isSolanaCaip2,
|
|
15
|
+
parseEvmChainId
|
|
16
|
+
} from "./chunk-6YRDD462.mjs";
|
|
17
|
+
|
|
18
|
+
// src/components/ui/Modal.tsx
|
|
19
|
+
import {
|
|
20
|
+
useEffect,
|
|
21
|
+
useRef,
|
|
22
|
+
useCallback
|
|
23
|
+
} from "react";
|
|
24
|
+
import { createPortal } from "react-dom";
|
|
25
|
+
import { jsx } from "react/jsx-runtime";
|
|
26
|
+
function getFocusableElements(container) {
|
|
27
|
+
const elements = container.querySelectorAll(
|
|
28
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
29
|
+
);
|
|
30
|
+
return Array.from(elements).filter(
|
|
31
|
+
(el) => !el.hasAttribute("disabled") && el.offsetParent !== null
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function Modal({
|
|
35
|
+
isOpen,
|
|
36
|
+
onClose,
|
|
37
|
+
children,
|
|
38
|
+
className = "",
|
|
39
|
+
inline = false,
|
|
40
|
+
closeOnOverlayClick = false
|
|
41
|
+
}) {
|
|
42
|
+
const overlayRef = useRef(null);
|
|
43
|
+
const contentRef = useRef(null);
|
|
44
|
+
const previousActiveElement = useRef(null);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (isOpen) {
|
|
47
|
+
previousActiveElement.current = document.activeElement;
|
|
48
|
+
}
|
|
49
|
+
}, [isOpen]);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!isOpen && previousActiveElement.current) {
|
|
52
|
+
previousActiveElement.current.focus();
|
|
53
|
+
previousActiveElement.current = null;
|
|
54
|
+
}
|
|
55
|
+
}, [isOpen]);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (isOpen && contentRef.current) {
|
|
58
|
+
const focusable = getFocusableElements(contentRef.current);
|
|
59
|
+
if (focusable.length > 0) {
|
|
60
|
+
focusable[0].focus();
|
|
61
|
+
} else {
|
|
62
|
+
contentRef.current.focus();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}, [isOpen]);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (isOpen && !inline) {
|
|
68
|
+
const originalOverflow = document.body.style.overflow;
|
|
69
|
+
document.body.style.overflow = "hidden";
|
|
70
|
+
return () => {
|
|
71
|
+
document.body.style.overflow = originalOverflow;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}, [isOpen, inline]);
|
|
75
|
+
const handleKeyDown = useCallback(
|
|
76
|
+
(event) => {
|
|
77
|
+
if (event.key === "Escape") {
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
onClose();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (event.key === "Tab" && contentRef.current) {
|
|
83
|
+
const focusable = getFocusableElements(contentRef.current);
|
|
84
|
+
if (focusable.length === 0) return;
|
|
85
|
+
const firstElement = focusable[0];
|
|
86
|
+
const lastElement = focusable[focusable.length - 1];
|
|
87
|
+
if (event.shiftKey) {
|
|
88
|
+
if (document.activeElement === firstElement) {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
lastElement.focus();
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
if (document.activeElement === lastElement) {
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
firstElement.focus();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
[onClose]
|
|
101
|
+
);
|
|
102
|
+
const handleOverlayClick = useCallback(
|
|
103
|
+
(event) => {
|
|
104
|
+
if (closeOnOverlayClick && event.target === overlayRef.current) {
|
|
105
|
+
onClose();
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
[closeOnOverlayClick, onClose]
|
|
109
|
+
);
|
|
110
|
+
if (!isOpen) return null;
|
|
111
|
+
if (inline) {
|
|
112
|
+
return /* @__PURE__ */ jsx(
|
|
113
|
+
"div",
|
|
114
|
+
{
|
|
115
|
+
ref: contentRef,
|
|
116
|
+
className: `rs-modal-content rs-modal-content--inline ${className}`,
|
|
117
|
+
tabIndex: -1,
|
|
118
|
+
children
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return createPortal(
|
|
123
|
+
/* @__PURE__ */ jsx(
|
|
124
|
+
"div",
|
|
125
|
+
{
|
|
126
|
+
ref: overlayRef,
|
|
127
|
+
className: `rs-modal-overlay ${isOpen ? "rs-modal-overlay--open" : ""}`,
|
|
128
|
+
onClick: handleOverlayClick,
|
|
129
|
+
onKeyDown: handleKeyDown,
|
|
130
|
+
role: "dialog",
|
|
131
|
+
"aria-modal": "true",
|
|
132
|
+
"aria-labelledby": "rs-modal-title",
|
|
133
|
+
children: /* @__PURE__ */ jsx(
|
|
134
|
+
"div",
|
|
135
|
+
{
|
|
136
|
+
ref: contentRef,
|
|
137
|
+
className: `rs-modal-content ${className}`,
|
|
138
|
+
tabIndex: -1,
|
|
139
|
+
children
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
),
|
|
144
|
+
document.body
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
Modal.displayName = "Modal";
|
|
148
|
+
|
|
149
|
+
// src/components/ui/Icons.tsx
|
|
150
|
+
import {
|
|
151
|
+
Wallet,
|
|
152
|
+
ExternalLink,
|
|
153
|
+
Coins,
|
|
154
|
+
Check,
|
|
155
|
+
ChevronRight,
|
|
156
|
+
ChevronLeft,
|
|
157
|
+
ChevronDown,
|
|
158
|
+
X,
|
|
159
|
+
HandCoins,
|
|
160
|
+
History,
|
|
161
|
+
Info,
|
|
162
|
+
Copy,
|
|
163
|
+
ArrowRight,
|
|
164
|
+
ArrowUpRight,
|
|
165
|
+
AlertTriangle,
|
|
166
|
+
Percent,
|
|
167
|
+
Clock,
|
|
168
|
+
CirclePlus,
|
|
169
|
+
CircleArrowOutUpLeft,
|
|
170
|
+
CreditCard,
|
|
171
|
+
Landmark,
|
|
172
|
+
Apple
|
|
173
|
+
} from "lucide-react";
|
|
174
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
175
|
+
var WalletIcon = Wallet;
|
|
176
|
+
var ExternalLinkIcon = ExternalLink;
|
|
177
|
+
var CheckIcon = Check;
|
|
178
|
+
function TransferCryptoIcon() {
|
|
179
|
+
return /* @__PURE__ */ jsx2(
|
|
180
|
+
"svg",
|
|
181
|
+
{
|
|
182
|
+
width: "24",
|
|
183
|
+
height: "24",
|
|
184
|
+
viewBox: "8 8 24 24",
|
|
185
|
+
fill: "none",
|
|
186
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
187
|
+
"aria-hidden": "true",
|
|
188
|
+
children: /* @__PURE__ */ jsx2(
|
|
189
|
+
"path",
|
|
190
|
+
{
|
|
191
|
+
d: "M26.08 18.37C27.03 18.72 27.87 19.30 28.53 20.07C29.19 20.83 29.65 21.75 29.86 22.73C30.07 23.72 30.03 24.74 29.74 25.71C29.45 26.68 28.92 27.55 28.20 28.26C27.48 28.97 26.59 29.49 25.62 29.76C24.65 30.04 23.63 30.06 22.64 29.83C21.66 29.61 20.75 29.14 20.00 28.47C19.24 27.80 18.67 26.95 18.33 26.00M15 14H16V18M24.70 21.87L25.40 22.58L22.58 25.40M22 16C22 19.31 19.31 22 16 22C12.68 22 10 19.31 10 16C10 12.68 12.68 10 16 10C19.31 10 22 12.68 22 16Z",
|
|
192
|
+
stroke: "currentColor",
|
|
193
|
+
strokeWidth: "2",
|
|
194
|
+
strokeLinecap: "round",
|
|
195
|
+
strokeLinejoin: "round"
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
var ChevronRightIcon = ChevronRight;
|
|
202
|
+
var ChevronLeftIcon = ChevronLeft;
|
|
203
|
+
var ChevronDownIcon = ChevronDown;
|
|
204
|
+
var CloseIcon = X;
|
|
205
|
+
var HandCoinsIcon = HandCoins;
|
|
206
|
+
var HistoryIcon = History;
|
|
207
|
+
var InfoIcon = Info;
|
|
208
|
+
var CopyIcon = Copy;
|
|
209
|
+
var ArrowUpRightIcon = ArrowUpRight;
|
|
210
|
+
var AlertTriangleIcon = AlertTriangle;
|
|
211
|
+
var PercentIcon = Percent;
|
|
212
|
+
var ClockIcon = Clock;
|
|
213
|
+
var PlusCircleIcon = CirclePlus;
|
|
214
|
+
var CircleArrowOutUpLeftIcon = CircleArrowOutUpLeft;
|
|
215
|
+
var CardIcon = CreditCard;
|
|
216
|
+
var BankIcon = Landmark;
|
|
217
|
+
var AppleIcon = Apple;
|
|
218
|
+
|
|
219
|
+
// src/components/ui/Callout.tsx
|
|
220
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
221
|
+
function CircleAlertIcon() {
|
|
222
|
+
return /* @__PURE__ */ jsx3(
|
|
223
|
+
"svg",
|
|
224
|
+
{
|
|
225
|
+
width: "16",
|
|
226
|
+
height: "16",
|
|
227
|
+
viewBox: "0 0 16 16",
|
|
228
|
+
fill: "none",
|
|
229
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
230
|
+
"aria-hidden": "true",
|
|
231
|
+
children: /* @__PURE__ */ jsx3(
|
|
232
|
+
"path",
|
|
233
|
+
{
|
|
234
|
+
d: "M10 5.99L6 9.99M6 5.99L10 9.99M14.66 7.99C14.66 11.68 11.68 14.66 8 14.66C4.31 14.66 1.33 11.68 1.33 7.99C1.33 4.31 4.31 1.33 8 1.33C11.68 1.33 14.66 4.31 14.66 7.99Z",
|
|
235
|
+
stroke: "currentColor",
|
|
236
|
+
strokeWidth: "1.33",
|
|
237
|
+
strokeLinecap: "round",
|
|
238
|
+
strokeLinejoin: "round"
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
function Callout({ variant = "error", children, role }) {
|
|
245
|
+
return /* @__PURE__ */ jsxs(
|
|
246
|
+
"div",
|
|
247
|
+
{
|
|
248
|
+
className: `rs-callout rs-callout--${variant}`,
|
|
249
|
+
role: role ?? (variant === "error" ? "alert" : "status"),
|
|
250
|
+
children: [
|
|
251
|
+
/* @__PURE__ */ jsx3("span", { className: "rs-callout-icon", children: variant === "error" ? /* @__PURE__ */ jsx3(CircleAlertIcon, {}) : /* @__PURE__ */ jsx3(AlertTriangleIcon, {}) }),
|
|
252
|
+
/* @__PURE__ */ jsx3("span", { className: "rs-callout-text", children })
|
|
253
|
+
]
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
Callout.displayName = "Callout";
|
|
258
|
+
|
|
259
|
+
// src/core/useLatestRef.ts
|
|
260
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
261
|
+
function useLatestRef(value) {
|
|
262
|
+
const ref = useRef2(value);
|
|
263
|
+
useEffect2(() => {
|
|
264
|
+
ref.current = value;
|
|
265
|
+
}, [value]);
|
|
266
|
+
return ref;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/core/debug.ts
|
|
270
|
+
function truncateString(value, max = 240) {
|
|
271
|
+
if (value.length <= max) return value;
|
|
272
|
+
return `${value.slice(0, max)}...`;
|
|
273
|
+
}
|
|
274
|
+
function normalizeForLog(value, depth = 0) {
|
|
275
|
+
if (depth > 3) return "[MaxDepth]";
|
|
276
|
+
if (value === null || value === void 0) return value;
|
|
277
|
+
if (typeof value === "bigint") return value.toString();
|
|
278
|
+
if (typeof value === "string") return truncateString(value);
|
|
279
|
+
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
280
|
+
if (value instanceof Error) {
|
|
281
|
+
return {
|
|
282
|
+
name: value.name,
|
|
283
|
+
message: value.message,
|
|
284
|
+
stack: value.stack ? truncateString(value.stack, 600) : void 0
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
if (Array.isArray(value)) {
|
|
288
|
+
return value.slice(0, 12).map((entry) => normalizeForLog(entry, depth + 1));
|
|
289
|
+
}
|
|
290
|
+
if (typeof value === "object") {
|
|
291
|
+
const input = value;
|
|
292
|
+
const output = {};
|
|
293
|
+
for (const [key, raw] of Object.entries(input)) {
|
|
294
|
+
const keyLower = key.toLowerCase();
|
|
295
|
+
if (keyLower.includes("signature") || keyLower.includes("privatekey") || keyLower.includes("sessiondetails")) {
|
|
296
|
+
output[key] = "[Redacted]";
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
output[key] = normalizeForLog(raw, depth + 1);
|
|
300
|
+
}
|
|
301
|
+
return output;
|
|
302
|
+
}
|
|
303
|
+
return String(value);
|
|
304
|
+
}
|
|
305
|
+
function debugLog(enabled, scope, message, data) {
|
|
306
|
+
if (!enabled) return;
|
|
307
|
+
if (data === void 0) {
|
|
308
|
+
console.log(`[deposit-modal:${scope}] ${message}`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
console.log(
|
|
312
|
+
`[deposit-modal:${scope}] ${message}`,
|
|
313
|
+
normalizeForLog(data)
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
function debugError(enabled, scope, message, error, data) {
|
|
317
|
+
if (!enabled) return;
|
|
318
|
+
const payload = data === void 0 ? { error: normalizeForLog(error) } : { error: normalizeForLog(error), meta: normalizeForLog(data) };
|
|
319
|
+
console.error(`[deposit-modal:${scope}] ${message}`, payload);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/core/deposit-asset-helpers.ts
|
|
323
|
+
function getAssetId(asset) {
|
|
324
|
+
return `${asset.chainId}:${asset.token.toLowerCase()}`;
|
|
325
|
+
}
|
|
326
|
+
function isEvmAddress(value) {
|
|
327
|
+
return /^0x[a-fA-F0-9]{40}$/.test(value);
|
|
328
|
+
}
|
|
329
|
+
function portfolioTokenToAsset(token) {
|
|
330
|
+
if (typeof token.chainId !== "number") return null;
|
|
331
|
+
if (!isEvmAddress(token.address)) return null;
|
|
332
|
+
return {
|
|
333
|
+
id: getAssetId({ chainId: token.chainId, token: token.address }),
|
|
334
|
+
chainId: token.chainId,
|
|
335
|
+
token: token.address,
|
|
336
|
+
symbol: token.symbol,
|
|
337
|
+
name: token.name,
|
|
338
|
+
decimals: token.decimals,
|
|
339
|
+
balance: token.balance,
|
|
340
|
+
balanceUsd: token.balanceUsd
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function safeBigInt(value) {
|
|
344
|
+
if (!value) return BigInt(0);
|
|
345
|
+
try {
|
|
346
|
+
return BigInt(value);
|
|
347
|
+
} catch {
|
|
348
|
+
return BigInt(0);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function portfolioToAssets(tokens) {
|
|
352
|
+
return tokens.map(portfolioTokenToAsset).filter((token) => token !== null).sort((a, b) => {
|
|
353
|
+
const balanceA = safeBigInt(a.balance);
|
|
354
|
+
const balanceB = safeBigInt(b.balance);
|
|
355
|
+
if (balanceB > balanceA) return 1;
|
|
356
|
+
if (balanceB < balanceA) return -1;
|
|
357
|
+
return 0;
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
function isNativeAsset(asset) {
|
|
361
|
+
return asset.token.toLowerCase() === "0x0000000000000000000000000000000000000000";
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/core/deposit-service.ts
|
|
365
|
+
function depositRowToEvent(row) {
|
|
366
|
+
const status = row.status;
|
|
367
|
+
if (!status) return void 0;
|
|
368
|
+
const deposit = {
|
|
369
|
+
transactionHash: row.txHash,
|
|
370
|
+
chain: row.chain,
|
|
371
|
+
amount: row.sourceAmount ?? row.amount,
|
|
372
|
+
asset: row.token,
|
|
373
|
+
token: row.token
|
|
374
|
+
};
|
|
375
|
+
const source = row.sourceTxHash ? { transactionHash: row.sourceTxHash, chain: row.chain } : { transactionHash: row.txHash, chain: row.chain };
|
|
376
|
+
const destination = row.destinationTxHash ? {
|
|
377
|
+
transactionHash: row.destinationTxHash,
|
|
378
|
+
chain: row.targetChain,
|
|
379
|
+
amount: row.destinationAmount ?? void 0
|
|
380
|
+
} : void 0;
|
|
381
|
+
const time = row.completedAt ?? row.createdAt ?? void 0;
|
|
382
|
+
if (status === "completed") {
|
|
383
|
+
return {
|
|
384
|
+
type: "post-bridge-swap-complete",
|
|
385
|
+
time: time ?? void 0,
|
|
386
|
+
data: {
|
|
387
|
+
deposit,
|
|
388
|
+
source,
|
|
389
|
+
...destination && { destination },
|
|
390
|
+
...destination && {
|
|
391
|
+
swap: { transactionHash: destination.transactionHash }
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
if (status === "failed" || status === "refunded") {
|
|
397
|
+
return {
|
|
398
|
+
type: "bridge-failed",
|
|
399
|
+
time: time ?? void 0,
|
|
400
|
+
data: {
|
|
401
|
+
deposit,
|
|
402
|
+
source,
|
|
403
|
+
...row.errorCode && { error: { code: row.errorCode } }
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
if (status === "processing") {
|
|
408
|
+
return {
|
|
409
|
+
type: "bridge-started",
|
|
410
|
+
time: time ?? void 0,
|
|
411
|
+
data: {
|
|
412
|
+
deposit,
|
|
413
|
+
source
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
type: "deposit-received",
|
|
419
|
+
time: time ?? void 0,
|
|
420
|
+
data: {
|
|
421
|
+
transactionHash: row.txHash,
|
|
422
|
+
chain: row.chain,
|
|
423
|
+
amount: row.amount,
|
|
424
|
+
token: row.token
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function jsonReplacer(_key, value) {
|
|
429
|
+
return typeof value === "bigint" ? value.toString() : value;
|
|
430
|
+
}
|
|
431
|
+
function asRecord(value) {
|
|
432
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
433
|
+
}
|
|
434
|
+
function toBigInt(value) {
|
|
435
|
+
if (typeof value === "bigint") return value;
|
|
436
|
+
if (typeof value === "number" && Number.isInteger(value)) return BigInt(value);
|
|
437
|
+
if (typeof value === "string" && value.trim() !== "") return BigInt(value);
|
|
438
|
+
throw new Error("Invalid bigint value");
|
|
439
|
+
}
|
|
440
|
+
function normalizeSessionTypedData(raw) {
|
|
441
|
+
const data = structuredClone(raw);
|
|
442
|
+
const domain = asRecord(data.domain);
|
|
443
|
+
if (domain && domain.chainId !== void 0 && domain.chainId !== null) {
|
|
444
|
+
domain.chainId = toBigInt(domain.chainId);
|
|
445
|
+
}
|
|
446
|
+
const message = asRecord(data.message);
|
|
447
|
+
if (!message || !Array.isArray(message.sessionsAndChainIds)) {
|
|
448
|
+
return data;
|
|
449
|
+
}
|
|
450
|
+
for (const entry of message.sessionsAndChainIds) {
|
|
451
|
+
const chainSession = asRecord(entry);
|
|
452
|
+
if (!chainSession) continue;
|
|
453
|
+
if (chainSession.chainId !== void 0 && chainSession.chainId !== null) {
|
|
454
|
+
chainSession.chainId = toBigInt(chainSession.chainId);
|
|
455
|
+
}
|
|
456
|
+
const session = asRecord(chainSession.session);
|
|
457
|
+
if (!session) continue;
|
|
458
|
+
if (session.nonce !== void 0 && session.nonce !== null) {
|
|
459
|
+
session.nonce = toBigInt(session.nonce);
|
|
460
|
+
}
|
|
461
|
+
if (session.expires !== void 0 && session.expires !== null) {
|
|
462
|
+
session.expires = toBigInt(session.expires);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return data;
|
|
466
|
+
}
|
|
467
|
+
function normalizeSetupAccountResponse(raw) {
|
|
468
|
+
const data = raw;
|
|
469
|
+
return {
|
|
470
|
+
smartAccount: data.smartAccount,
|
|
471
|
+
isRegistered: data.isRegistered,
|
|
472
|
+
targetChain: data.targetChain,
|
|
473
|
+
targetToken: data.targetToken,
|
|
474
|
+
needsRegistration: data.needsRegistration,
|
|
475
|
+
solanaDepositAddress: data.solanaDepositAddress,
|
|
476
|
+
accountParams: data.accountParams,
|
|
477
|
+
sessionDetailsUnsigned: data.sessionDetailsUnsigned ? {
|
|
478
|
+
hashesAndChainIds: data.sessionDetailsUnsigned.hashesAndChainIds.map(
|
|
479
|
+
(h) => ({
|
|
480
|
+
chainId: toBigInt(h.chainId),
|
|
481
|
+
sessionDigest: h.sessionDigest
|
|
482
|
+
})
|
|
483
|
+
),
|
|
484
|
+
data: normalizeSessionTypedData(data.sessionDetailsUnsigned.data)
|
|
485
|
+
} : void 0
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function buildSessionDetails(unsigned, signature) {
|
|
489
|
+
return {
|
|
490
|
+
hashesAndChainIds: unsigned.hashesAndChainIds.map((h) => ({
|
|
491
|
+
chainId: toBigInt(h.chainId),
|
|
492
|
+
sessionDigest: h.sessionDigest
|
|
493
|
+
})),
|
|
494
|
+
signature
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function createDepositService(baseUrl, options) {
|
|
498
|
+
const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
|
|
499
|
+
const debug = options?.debug === true;
|
|
500
|
+
const scope = options?.debugScope ?? "service";
|
|
501
|
+
function apiUrl(path) {
|
|
502
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
503
|
+
return `${normalizedBaseUrl}${normalizedPath}`;
|
|
504
|
+
}
|
|
505
|
+
function shortRef(value) {
|
|
506
|
+
if (value.length <= 20) return value;
|
|
507
|
+
return `${value.slice(0, 10)}...${value.slice(-8)}`;
|
|
508
|
+
}
|
|
509
|
+
const PORTFOLIO_CACHE_TTL_MS = 3e4;
|
|
510
|
+
const portfolioCache = /* @__PURE__ */ new Map();
|
|
511
|
+
function cachedPortfolio(key, fetcher) {
|
|
512
|
+
const now = Date.now();
|
|
513
|
+
const entry = portfolioCache.get(key);
|
|
514
|
+
if (entry && entry.expiresAt > now) {
|
|
515
|
+
debugLog(debug, scope, "portfolio:cache-hit", { key });
|
|
516
|
+
return entry.promise;
|
|
517
|
+
}
|
|
518
|
+
const promise = fetcher().catch((err) => {
|
|
519
|
+
portfolioCache.delete(key);
|
|
520
|
+
throw err;
|
|
521
|
+
});
|
|
522
|
+
portfolioCache.set(key, {
|
|
523
|
+
promise,
|
|
524
|
+
expiresAt: now + PORTFOLIO_CACHE_TTL_MS
|
|
525
|
+
});
|
|
526
|
+
return promise;
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
async setupAccount(params) {
|
|
530
|
+
const url = apiUrl("/setup-account");
|
|
531
|
+
debugLog(debug, scope, "setupAccount:request", {
|
|
532
|
+
url,
|
|
533
|
+
ownerAddress: params.ownerAddress,
|
|
534
|
+
targetChain: params.targetChain,
|
|
535
|
+
targetToken: params.targetToken,
|
|
536
|
+
recipient: params.recipient ? shortRef(params.recipient) : void 0,
|
|
537
|
+
postBridgeActionCount: params.postBridgeActions?.length ?? 0,
|
|
538
|
+
forceRegister: params.forceRegister
|
|
539
|
+
});
|
|
540
|
+
const response = await fetch(url, {
|
|
541
|
+
method: "POST",
|
|
542
|
+
headers: { "Content-Type": "application/json" },
|
|
543
|
+
body: JSON.stringify(params)
|
|
544
|
+
});
|
|
545
|
+
if (!response.ok) {
|
|
546
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
547
|
+
debugError(
|
|
548
|
+
debug,
|
|
549
|
+
scope,
|
|
550
|
+
"setupAccount:failed",
|
|
551
|
+
error,
|
|
552
|
+
{ status: response.status, ownerAddress: params.ownerAddress }
|
|
553
|
+
);
|
|
554
|
+
const detail = Array.isArray(error.details) ? error.details.map((d) => d?.message).filter(Boolean).join("; ") : void 0;
|
|
555
|
+
const base = error.error || `Setup account failed: ${response.status}`;
|
|
556
|
+
throw new Error(detail ? `${base}: ${detail}` : base);
|
|
557
|
+
}
|
|
558
|
+
const normalized = normalizeSetupAccountResponse(await response.json());
|
|
559
|
+
debugLog(debug, scope, "setupAccount:success", {
|
|
560
|
+
smartAccount: normalized.smartAccount,
|
|
561
|
+
needsRegistration: normalized.needsRegistration,
|
|
562
|
+
isRegistered: normalized.isRegistered,
|
|
563
|
+
hasSolanaDepositAddress: Boolean(normalized.solanaDepositAddress)
|
|
564
|
+
});
|
|
565
|
+
return normalized;
|
|
566
|
+
},
|
|
567
|
+
async registerAccount(params) {
|
|
568
|
+
const { eoaAddress, sessionOwner, ...account } = params;
|
|
569
|
+
const url = apiUrl("/register");
|
|
570
|
+
debugLog(debug, scope, "registerAccount:request", {
|
|
571
|
+
url,
|
|
572
|
+
address: params.address,
|
|
573
|
+
targetChain: params.target.chain,
|
|
574
|
+
targetToken: params.target.token,
|
|
575
|
+
eoaAddress,
|
|
576
|
+
sessionOwner
|
|
577
|
+
});
|
|
578
|
+
const response = await fetch(url, {
|
|
579
|
+
method: "POST",
|
|
580
|
+
headers: { "Content-Type": "application/json" },
|
|
581
|
+
body: JSON.stringify(
|
|
582
|
+
{
|
|
583
|
+
account,
|
|
584
|
+
eoaAddress,
|
|
585
|
+
sessionOwner
|
|
586
|
+
},
|
|
587
|
+
jsonReplacer
|
|
588
|
+
)
|
|
589
|
+
});
|
|
590
|
+
if (!response.ok) {
|
|
591
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
592
|
+
debugError(
|
|
593
|
+
debug,
|
|
594
|
+
scope,
|
|
595
|
+
"registerAccount:failed",
|
|
596
|
+
error,
|
|
597
|
+
{ status: response.status, address: params.address }
|
|
598
|
+
);
|
|
599
|
+
throw new Error(
|
|
600
|
+
error.error || `Registration failed: ${response.status}`
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
const result = await response.json();
|
|
604
|
+
debugLog(debug, scope, "registerAccount:success", {
|
|
605
|
+
address: params.address,
|
|
606
|
+
evmDepositAddress: result?.evmDepositAddress,
|
|
607
|
+
hasSolanaDepositAddress: Boolean(result?.solanaDepositAddress)
|
|
608
|
+
});
|
|
609
|
+
return result;
|
|
610
|
+
},
|
|
611
|
+
async fetchPortfolio(address) {
|
|
612
|
+
return cachedPortfolio(`evm:${address.toLowerCase()}`, async () => {
|
|
613
|
+
const url = apiUrl(`/portfolio/${address}`);
|
|
614
|
+
debugLog(debug, scope, "fetchPortfolio:request", { url, address });
|
|
615
|
+
const response = await fetch(url, {
|
|
616
|
+
method: "GET",
|
|
617
|
+
headers: { "Content-Type": "application/json" }
|
|
618
|
+
});
|
|
619
|
+
if (!response.ok) {
|
|
620
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
621
|
+
debugError(
|
|
622
|
+
debug,
|
|
623
|
+
scope,
|
|
624
|
+
"fetchPortfolio:failed",
|
|
625
|
+
error,
|
|
626
|
+
{ status: response.status, address }
|
|
627
|
+
);
|
|
628
|
+
throw new Error(
|
|
629
|
+
error.error || `Portfolio fetch failed: ${response.status}`
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
const data = await response.json();
|
|
633
|
+
const direct = normalizeDirectPortfolio(data);
|
|
634
|
+
if (direct.tokens.length > 0) {
|
|
635
|
+
debugLog(debug, scope, "fetchPortfolio:success", {
|
|
636
|
+
address,
|
|
637
|
+
source: "direct",
|
|
638
|
+
tokenCount: direct.tokens.length,
|
|
639
|
+
totalUsd: direct.totalUsd
|
|
640
|
+
});
|
|
641
|
+
return direct;
|
|
642
|
+
}
|
|
643
|
+
const portfolioData = extractOrchestratorPortfolio(data);
|
|
644
|
+
if (portfolioData) {
|
|
645
|
+
const normalized = normalizeOrchestratorPortfolio(portfolioData);
|
|
646
|
+
debugLog(debug, scope, "fetchPortfolio:success", {
|
|
647
|
+
address,
|
|
648
|
+
source: "orchestrator",
|
|
649
|
+
tokenCount: normalized.tokens.length,
|
|
650
|
+
totalUsd: normalized.totalUsd
|
|
651
|
+
});
|
|
652
|
+
return normalized;
|
|
653
|
+
}
|
|
654
|
+
debugLog(debug, scope, "fetchPortfolio:empty", { address });
|
|
655
|
+
return { tokens: [], totalUsd: 0 };
|
|
656
|
+
});
|
|
657
|
+
},
|
|
658
|
+
async fetchSolanaPortfolio(address) {
|
|
659
|
+
return cachedPortfolio(`sol:${address}`, async () => {
|
|
660
|
+
const url = apiUrl(`/portfolio/solana/${address}`);
|
|
661
|
+
debugLog(debug, scope, "fetchSolanaPortfolio:request", { url, address });
|
|
662
|
+
const response = await fetch(url, {
|
|
663
|
+
method: "GET",
|
|
664
|
+
headers: { "Content-Type": "application/json" }
|
|
665
|
+
});
|
|
666
|
+
if (!response.ok) {
|
|
667
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
668
|
+
debugError(
|
|
669
|
+
debug,
|
|
670
|
+
scope,
|
|
671
|
+
"fetchSolanaPortfolio:failed",
|
|
672
|
+
error,
|
|
673
|
+
{ status: response.status, address }
|
|
674
|
+
);
|
|
675
|
+
throw new Error(
|
|
676
|
+
error.error || `Solana portfolio fetch failed: ${response.status}`
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
const data = await response.json();
|
|
680
|
+
const normalized = normalizeDirectPortfolio(data);
|
|
681
|
+
debugLog(debug, scope, "fetchSolanaPortfolio:success", {
|
|
682
|
+
address,
|
|
683
|
+
tokenCount: normalized.tokens.length,
|
|
684
|
+
totalUsd: normalized.totalUsd
|
|
685
|
+
});
|
|
686
|
+
return normalized;
|
|
687
|
+
});
|
|
688
|
+
},
|
|
689
|
+
async checkAccount(address) {
|
|
690
|
+
const url = apiUrl(`/check/${address}`);
|
|
691
|
+
debugLog(debug, scope, "checkAccount:request", { url, address });
|
|
692
|
+
const response = await fetch(url, {
|
|
693
|
+
method: "GET",
|
|
694
|
+
headers: { "Content-Type": "application/json" }
|
|
695
|
+
});
|
|
696
|
+
if (!response.ok) {
|
|
697
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
698
|
+
debugError(
|
|
699
|
+
debug,
|
|
700
|
+
scope,
|
|
701
|
+
"checkAccount:failed",
|
|
702
|
+
error,
|
|
703
|
+
{ status: response.status, address }
|
|
704
|
+
);
|
|
705
|
+
throw new Error(error.error || `Account check failed: ${response.status}`);
|
|
706
|
+
}
|
|
707
|
+
const data = await response.json();
|
|
708
|
+
const result = {
|
|
709
|
+
isRegistered: data?.isRegistered === true,
|
|
710
|
+
targetChain: data?.targetChain,
|
|
711
|
+
targetToken: data?.targetToken
|
|
712
|
+
};
|
|
713
|
+
debugLog(debug, scope, "checkAccount:success", {
|
|
714
|
+
address,
|
|
715
|
+
isRegistered: result.isRegistered,
|
|
716
|
+
targetChain: result.targetChain,
|
|
717
|
+
targetToken: result.targetToken
|
|
718
|
+
});
|
|
719
|
+
return result;
|
|
720
|
+
},
|
|
721
|
+
async fetchStatus(address, txHash) {
|
|
722
|
+
const normalized = txHash.startsWith("0x") || txHash.startsWith("0X") ? txHash.toLowerCase() : txHash;
|
|
723
|
+
const txHashParam = encodeURIComponent(normalized);
|
|
724
|
+
const url = apiUrl(
|
|
725
|
+
`/deposits?account=${encodeURIComponent(address)}&txHash=${txHashParam}&limit=1`
|
|
726
|
+
);
|
|
727
|
+
const response = await fetch(url, {
|
|
728
|
+
method: "GET",
|
|
729
|
+
headers: { "Content-Type": "application/json" },
|
|
730
|
+
cache: "no-store"
|
|
731
|
+
});
|
|
732
|
+
if (!response.ok) {
|
|
733
|
+
debugLog(debug, scope, "fetchStatus:miss", {
|
|
734
|
+
address,
|
|
735
|
+
txHash: shortRef(normalized),
|
|
736
|
+
status: response.status
|
|
737
|
+
});
|
|
738
|
+
return { lastEvent: void 0 };
|
|
739
|
+
}
|
|
740
|
+
const body = await response.json();
|
|
741
|
+
const row = body.deposits?.[0];
|
|
742
|
+
const lastEvent = row ? depositRowToEvent(row) : void 0;
|
|
743
|
+
debugLog(debug, scope, "fetchStatus:success", {
|
|
744
|
+
address,
|
|
745
|
+
txHash: shortRef(normalized),
|
|
746
|
+
eventType: lastEvent?.type
|
|
747
|
+
});
|
|
748
|
+
return { lastEvent };
|
|
749
|
+
},
|
|
750
|
+
async fetchLatestStatus(address) {
|
|
751
|
+
const url = apiUrl(
|
|
752
|
+
`/deposits?account=${encodeURIComponent(address)}&limit=1`
|
|
753
|
+
);
|
|
754
|
+
const response = await fetch(url, {
|
|
755
|
+
method: "GET",
|
|
756
|
+
headers: { "Content-Type": "application/json" },
|
|
757
|
+
cache: "no-store"
|
|
758
|
+
});
|
|
759
|
+
if (!response.ok) {
|
|
760
|
+
debugLog(debug, scope, "fetchLatestStatus:miss", {
|
|
761
|
+
address,
|
|
762
|
+
status: response.status
|
|
763
|
+
});
|
|
764
|
+
return { lastEvent: void 0 };
|
|
765
|
+
}
|
|
766
|
+
const body = await response.json();
|
|
767
|
+
const row = body.deposits?.[0];
|
|
768
|
+
const lastEvent = row ? depositRowToEvent(row) : void 0;
|
|
769
|
+
debugLog(debug, scope, "fetchLatestStatus:success", {
|
|
770
|
+
address,
|
|
771
|
+
eventType: lastEvent?.type
|
|
772
|
+
});
|
|
773
|
+
return { lastEvent };
|
|
774
|
+
},
|
|
775
|
+
async relayWithdraw(params) {
|
|
776
|
+
const { smartAccount, chainId, safeAddress, safeTransaction, signature } = params;
|
|
777
|
+
const url = apiUrl(`/safe/withdraw`);
|
|
778
|
+
debugLog(debug, scope, "relayWithdraw:request", {
|
|
779
|
+
url,
|
|
780
|
+
smartAccount,
|
|
781
|
+
chainId,
|
|
782
|
+
safeAddress,
|
|
783
|
+
to: safeTransaction.to
|
|
784
|
+
});
|
|
785
|
+
const response = await fetch(url, {
|
|
786
|
+
method: "POST",
|
|
787
|
+
headers: { "Content-Type": "application/json" },
|
|
788
|
+
body: JSON.stringify(
|
|
789
|
+
{
|
|
790
|
+
chainId,
|
|
791
|
+
safeAddress,
|
|
792
|
+
safeTransaction: {
|
|
793
|
+
to: safeTransaction.to,
|
|
794
|
+
value: safeTransaction.value.toString(),
|
|
795
|
+
data: safeTransaction.data,
|
|
796
|
+
operation: safeTransaction.operation,
|
|
797
|
+
safeTxGas: safeTransaction.safeTxGas.toString(),
|
|
798
|
+
baseGas: safeTransaction.baseGas.toString(),
|
|
799
|
+
gasPrice: safeTransaction.gasPrice.toString(),
|
|
800
|
+
gasToken: safeTransaction.gasToken,
|
|
801
|
+
refundReceiver: safeTransaction.refundReceiver,
|
|
802
|
+
nonce: safeTransaction.nonce.toString()
|
|
803
|
+
},
|
|
804
|
+
signature
|
|
805
|
+
},
|
|
806
|
+
jsonReplacer
|
|
807
|
+
)
|
|
808
|
+
});
|
|
809
|
+
if (!response.ok) {
|
|
810
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
811
|
+
debugError(
|
|
812
|
+
debug,
|
|
813
|
+
scope,
|
|
814
|
+
"relayWithdraw:failed",
|
|
815
|
+
error,
|
|
816
|
+
{
|
|
817
|
+
status: response.status,
|
|
818
|
+
smartAccount,
|
|
819
|
+
chainId,
|
|
820
|
+
safeAddress,
|
|
821
|
+
signature
|
|
822
|
+
}
|
|
823
|
+
);
|
|
824
|
+
throw new Error(
|
|
825
|
+
error.error || `Relay withdraw failed: ${response.status}`
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
const result = await response.json();
|
|
829
|
+
debugLog(debug, scope, "relayWithdraw:success", {
|
|
830
|
+
smartAccount,
|
|
831
|
+
chainId,
|
|
832
|
+
safeAddress,
|
|
833
|
+
txHash: result?.txHash ? shortRef(result.txHash) : void 0
|
|
834
|
+
});
|
|
835
|
+
return result;
|
|
836
|
+
},
|
|
837
|
+
async submitPolymarketWithdraw(params) {
|
|
838
|
+
const { depositWallet, owner, nonce, deadline, calls, signature } = params;
|
|
839
|
+
const url = apiUrl(`/polymarket/withdraw`);
|
|
840
|
+
debugLog(debug, scope, "submitPolymarketWithdraw:request", {
|
|
841
|
+
url,
|
|
842
|
+
depositWallet,
|
|
843
|
+
owner,
|
|
844
|
+
nonce,
|
|
845
|
+
callCount: calls.length
|
|
846
|
+
});
|
|
847
|
+
const response = await fetch(url, {
|
|
848
|
+
method: "POST",
|
|
849
|
+
headers: { "Content-Type": "application/json" },
|
|
850
|
+
body: JSON.stringify({
|
|
851
|
+
depositWallet,
|
|
852
|
+
owner,
|
|
853
|
+
nonce,
|
|
854
|
+
deadline,
|
|
855
|
+
calls,
|
|
856
|
+
signature
|
|
857
|
+
})
|
|
858
|
+
});
|
|
859
|
+
if (!response.ok) {
|
|
860
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
861
|
+
debugError(debug, scope, "submitPolymarketWithdraw:failed", error, {
|
|
862
|
+
status: response.status,
|
|
863
|
+
depositWallet,
|
|
864
|
+
owner
|
|
865
|
+
});
|
|
866
|
+
throw new Error(
|
|
867
|
+
error.error || `Polymarket withdraw failed: ${response.status}`
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
const result = await response.json();
|
|
871
|
+
debugLog(debug, scope, "submitPolymarketWithdraw:success", {
|
|
872
|
+
depositWallet,
|
|
873
|
+
txHash: result?.txHash ? shortRef(result.txHash) : void 0
|
|
874
|
+
});
|
|
875
|
+
return result;
|
|
876
|
+
},
|
|
877
|
+
async fetchDepositHistory(params) {
|
|
878
|
+
const searchParams = new URLSearchParams({ recipient: params.recipient });
|
|
879
|
+
searchParams.set("limit", String(params.limit ?? 20));
|
|
880
|
+
if (params.cursor) {
|
|
881
|
+
searchParams.set("cursor", params.cursor);
|
|
882
|
+
}
|
|
883
|
+
const url = apiUrl(`/deposits?${searchParams.toString()}`);
|
|
884
|
+
debugLog(debug, scope, "fetchDepositHistory:request", {
|
|
885
|
+
url,
|
|
886
|
+
recipient: shortRef(params.recipient),
|
|
887
|
+
cursor: params.cursor
|
|
888
|
+
});
|
|
889
|
+
const response = await fetch(url, {
|
|
890
|
+
method: "GET",
|
|
891
|
+
headers: { "Content-Type": "application/json" },
|
|
892
|
+
cache: "no-store"
|
|
893
|
+
});
|
|
894
|
+
if (!response.ok) {
|
|
895
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
896
|
+
debugError(
|
|
897
|
+
debug,
|
|
898
|
+
scope,
|
|
899
|
+
"fetchDepositHistory:failed",
|
|
900
|
+
error,
|
|
901
|
+
{ status: response.status, recipient: shortRef(params.recipient) }
|
|
902
|
+
);
|
|
903
|
+
throw new Error(
|
|
904
|
+
error.error || `Deposit history fetch failed: ${response.status}`
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
const data = await response.json();
|
|
908
|
+
const deposits = Array.isArray(data?.deposits) ? data.deposits : [];
|
|
909
|
+
const nextCursor = data?.nextCursor ?? data?.next_cursor ?? null;
|
|
910
|
+
debugLog(debug, scope, "fetchDepositHistory:success", {
|
|
911
|
+
recipient: shortRef(params.recipient),
|
|
912
|
+
count: deposits.length,
|
|
913
|
+
hasMore: Boolean(nextCursor)
|
|
914
|
+
});
|
|
915
|
+
return { deposits, nextCursor };
|
|
916
|
+
},
|
|
917
|
+
async checkLiquidity(params) {
|
|
918
|
+
if (params.destinationChainId === "solana") {
|
|
919
|
+
const token = getSolanaTokenByMint(params.destinationToken);
|
|
920
|
+
return {
|
|
921
|
+
hasLiquidity: true,
|
|
922
|
+
symbol: token?.symbol ?? "Token",
|
|
923
|
+
decimals: token?.decimals ?? 9,
|
|
924
|
+
unlimited: true,
|
|
925
|
+
maxAmount: null
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
const searchParams = new URLSearchParams({
|
|
929
|
+
sourceChainId: String(params.sourceChainId),
|
|
930
|
+
sourceToken: params.sourceToken,
|
|
931
|
+
destinationChainId: String(params.destinationChainId),
|
|
932
|
+
destinationToken: params.destinationToken,
|
|
933
|
+
amount: params.amount
|
|
934
|
+
});
|
|
935
|
+
const url = apiUrl(`/liquidity?${searchParams.toString()}`);
|
|
936
|
+
debugLog(debug, scope, "checkLiquidity:request", {
|
|
937
|
+
url,
|
|
938
|
+
sourceChainId: params.sourceChainId,
|
|
939
|
+
destinationChainId: params.destinationChainId,
|
|
940
|
+
amount: params.amount
|
|
941
|
+
});
|
|
942
|
+
const response = await fetch(url, {
|
|
943
|
+
method: "GET",
|
|
944
|
+
headers: { "Content-Type": "application/json" },
|
|
945
|
+
cache: "no-store"
|
|
946
|
+
});
|
|
947
|
+
if (!response.ok) {
|
|
948
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
949
|
+
debugError(debug, scope, "checkLiquidity:failed", error, {
|
|
950
|
+
status: response.status
|
|
951
|
+
});
|
|
952
|
+
throw new Error(
|
|
953
|
+
error.error || `Liquidity check failed: ${response.status}`
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
const data = await response.json();
|
|
957
|
+
const result = {
|
|
958
|
+
hasLiquidity: data.hasLiquidity === true,
|
|
959
|
+
symbol: typeof data.symbol === "string" ? data.symbol : "",
|
|
960
|
+
decimals: typeof data.decimals === "number" ? data.decimals : 0,
|
|
961
|
+
unlimited: data.unlimited === true,
|
|
962
|
+
maxAmount: typeof data.maxAmount === "string" ? data.maxAmount : null
|
|
963
|
+
};
|
|
964
|
+
debugLog(debug, scope, "checkLiquidity:success", {
|
|
965
|
+
hasLiquidity: result.hasLiquidity,
|
|
966
|
+
unlimited: result.unlimited,
|
|
967
|
+
maxAmount: result.maxAmount
|
|
968
|
+
});
|
|
969
|
+
return result;
|
|
970
|
+
},
|
|
971
|
+
async getQuotePreview(params) {
|
|
972
|
+
const url = apiUrl("/quotes/preview");
|
|
973
|
+
const body = {
|
|
974
|
+
account: params.account,
|
|
975
|
+
sourceChainId: `eip155:${params.sourceChainId}`,
|
|
976
|
+
sourceToken: params.sourceToken,
|
|
977
|
+
amount: params.amount
|
|
978
|
+
};
|
|
979
|
+
debugLog(debug, scope, "getQuotePreview:request", {
|
|
980
|
+
url,
|
|
981
|
+
sourceChainId: body.sourceChainId,
|
|
982
|
+
sourceToken: params.sourceToken,
|
|
983
|
+
amount: params.amount
|
|
984
|
+
});
|
|
985
|
+
try {
|
|
986
|
+
const response = await fetch(url, {
|
|
987
|
+
method: "POST",
|
|
988
|
+
headers: { "Content-Type": "application/json" },
|
|
989
|
+
body: JSON.stringify(body),
|
|
990
|
+
cache: "no-store"
|
|
991
|
+
});
|
|
992
|
+
if (!response.ok) {
|
|
993
|
+
debugLog(debug, scope, "getQuotePreview:unavailable", {
|
|
994
|
+
status: response.status
|
|
995
|
+
});
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
const quote = await response.json();
|
|
999
|
+
debugLog(debug, scope, "getQuotePreview:success", {
|
|
1000
|
+
settlementLayer: quote.settlementLayer,
|
|
1001
|
+
totalUsd: quote.fees?.totalUsd
|
|
1002
|
+
});
|
|
1003
|
+
return quote;
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
debugError(debug, scope, "getQuotePreview:error", error);
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
async fetchPrices(symbols) {
|
|
1010
|
+
const list = symbols.filter((s) => s.length > 0);
|
|
1011
|
+
if (list.length === 0) return {};
|
|
1012
|
+
const url = apiUrl(
|
|
1013
|
+
`/prices?${new URLSearchParams({ symbols: list.join(",") }).toString()}`
|
|
1014
|
+
);
|
|
1015
|
+
try {
|
|
1016
|
+
const response = await fetch(url, {
|
|
1017
|
+
method: "GET",
|
|
1018
|
+
headers: { "Content-Type": "application/json" },
|
|
1019
|
+
cache: "no-store"
|
|
1020
|
+
});
|
|
1021
|
+
if (!response.ok) {
|
|
1022
|
+
debugError(debug, scope, "fetchPrices:failed", {
|
|
1023
|
+
status: response.status
|
|
1024
|
+
});
|
|
1025
|
+
return {};
|
|
1026
|
+
}
|
|
1027
|
+
const data = await response.json();
|
|
1028
|
+
return Object.fromEntries(
|
|
1029
|
+
Object.entries(data.prices ?? {}).map(([symbol, price]) => [
|
|
1030
|
+
symbol.toUpperCase(),
|
|
1031
|
+
price
|
|
1032
|
+
])
|
|
1033
|
+
);
|
|
1034
|
+
} catch (err) {
|
|
1035
|
+
debugError(debug, scope, "fetchPrices:error", err);
|
|
1036
|
+
return {};
|
|
1037
|
+
}
|
|
1038
|
+
},
|
|
1039
|
+
async getSwappedWidgetUrl(params) {
|
|
1040
|
+
const url = apiUrl("/onramp/swapped/widget-url");
|
|
1041
|
+
debugLog(debug, scope, "getSwappedWidgetUrl:request", {
|
|
1042
|
+
smartAccount: shortRef(params.smartAccount),
|
|
1043
|
+
method: params.method
|
|
1044
|
+
});
|
|
1045
|
+
const response = await fetch(url, {
|
|
1046
|
+
method: "POST",
|
|
1047
|
+
headers: { "Content-Type": "application/json" },
|
|
1048
|
+
body: JSON.stringify(params)
|
|
1049
|
+
});
|
|
1050
|
+
if (!response.ok) {
|
|
1051
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
1052
|
+
debugError(debug, scope, "getSwappedWidgetUrl:failed", error, {
|
|
1053
|
+
status: response.status
|
|
1054
|
+
});
|
|
1055
|
+
throw new Error(
|
|
1056
|
+
error.error || `Swapped widget URL failed: ${response.status}`
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
const body = await response.json();
|
|
1060
|
+
if (typeof body.url !== "string" || typeof body.currencyCode !== "string" || typeof body.externalCustomerId !== "string") {
|
|
1061
|
+
throw new Error("Swapped widget URL: malformed response");
|
|
1062
|
+
}
|
|
1063
|
+
return {
|
|
1064
|
+
url: body.url,
|
|
1065
|
+
currencyCode: body.currencyCode,
|
|
1066
|
+
sandbox: body.sandbox === true,
|
|
1067
|
+
externalCustomerId: body.externalCustomerId,
|
|
1068
|
+
expiresAt: body.expiresAt
|
|
1069
|
+
};
|
|
1070
|
+
},
|
|
1071
|
+
async getSwappedConnectUrl(params) {
|
|
1072
|
+
const url = apiUrl("/onramp/swapped/connect-url");
|
|
1073
|
+
debugLog(debug, scope, "getSwappedConnectUrl:request", {
|
|
1074
|
+
smartAccount: shortRef(params.smartAccount),
|
|
1075
|
+
connection: params.connection
|
|
1076
|
+
});
|
|
1077
|
+
const response = await fetch(url, {
|
|
1078
|
+
method: "POST",
|
|
1079
|
+
headers: { "Content-Type": "application/json" },
|
|
1080
|
+
body: JSON.stringify(params)
|
|
1081
|
+
});
|
|
1082
|
+
if (!response.ok) {
|
|
1083
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
1084
|
+
debugError(debug, scope, "getSwappedConnectUrl:failed", error, {
|
|
1085
|
+
status: response.status
|
|
1086
|
+
});
|
|
1087
|
+
throw new Error(
|
|
1088
|
+
error.error || `Swapped Connect URL failed: ${response.status}`
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
const body = await response.json();
|
|
1092
|
+
if (typeof body.url !== "string" || typeof body.currencyCode !== "string" || typeof body.externalCustomerId !== "string") {
|
|
1093
|
+
throw new Error("Swapped Connect URL: malformed response");
|
|
1094
|
+
}
|
|
1095
|
+
return {
|
|
1096
|
+
url: body.url,
|
|
1097
|
+
currencyCode: body.currencyCode,
|
|
1098
|
+
sandbox: body.sandbox === true,
|
|
1099
|
+
externalCustomerId: body.externalCustomerId,
|
|
1100
|
+
expiresAt: body.expiresAt
|
|
1101
|
+
};
|
|
1102
|
+
},
|
|
1103
|
+
async getSwappedConnectExchanges() {
|
|
1104
|
+
const url = apiUrl("/onramp/swapped/connect-exchanges");
|
|
1105
|
+
debugLog(debug, scope, "getSwappedConnectExchanges:request");
|
|
1106
|
+
const response = await fetch(url, {
|
|
1107
|
+
method: "GET",
|
|
1108
|
+
headers: { "Content-Type": "application/json" },
|
|
1109
|
+
cache: "no-store"
|
|
1110
|
+
});
|
|
1111
|
+
if (!response.ok) {
|
|
1112
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
1113
|
+
debugError(debug, scope, "getSwappedConnectExchanges:failed", error, {
|
|
1114
|
+
status: response.status
|
|
1115
|
+
});
|
|
1116
|
+
throw new Error(
|
|
1117
|
+
error.error || `Swapped Connect exchanges failed: ${response.status}`
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
const body = await response.json();
|
|
1121
|
+
const rawExchanges = Array.isArray(body.exchanges) ? body.exchanges : null;
|
|
1122
|
+
const exchanges = [];
|
|
1123
|
+
let hasMalformedExchange = rawExchanges === null;
|
|
1124
|
+
for (const exchange of rawExchanges ?? []) {
|
|
1125
|
+
if (!exchange || typeof exchange !== "object") {
|
|
1126
|
+
hasMalformedExchange = true;
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
const raw = exchange;
|
|
1130
|
+
const connection = typeof raw.connection === "string" ? raw.connection : "";
|
|
1131
|
+
const name = typeof raw.name === "string" ? raw.name : "";
|
|
1132
|
+
const logoUrl = typeof raw.logoUrl === "string" || raw.logoUrl === null ? raw.logoUrl : null;
|
|
1133
|
+
if (!connection || !name) {
|
|
1134
|
+
hasMalformedExchange = true;
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
exchanges.push({ connection, name, logoUrl });
|
|
1138
|
+
}
|
|
1139
|
+
if (hasMalformedExchange || typeof body.fetchedAt !== "string" || typeof body.expiresAt !== "string") {
|
|
1140
|
+
throw new Error("Swapped Connect exchanges: malformed response");
|
|
1141
|
+
}
|
|
1142
|
+
return {
|
|
1143
|
+
exchanges,
|
|
1144
|
+
fetchedAt: body.fetchedAt,
|
|
1145
|
+
expiresAt: body.expiresAt,
|
|
1146
|
+
stale: body.stale === true ? true : void 0
|
|
1147
|
+
};
|
|
1148
|
+
},
|
|
1149
|
+
async fetchSwappedOrderStatus(smartAccount) {
|
|
1150
|
+
const url = apiUrl(
|
|
1151
|
+
`/onramp/swapped/status/${encodeURIComponent(smartAccount)}`
|
|
1152
|
+
);
|
|
1153
|
+
const response = await fetch(url, {
|
|
1154
|
+
method: "GET",
|
|
1155
|
+
headers: { "Content-Type": "application/json" },
|
|
1156
|
+
cache: "no-store"
|
|
1157
|
+
});
|
|
1158
|
+
if (!response.ok) return null;
|
|
1159
|
+
let body = null;
|
|
1160
|
+
try {
|
|
1161
|
+
body = await response.json();
|
|
1162
|
+
} catch {
|
|
1163
|
+
return null;
|
|
1164
|
+
}
|
|
1165
|
+
if (!body || body.ok !== true) return null;
|
|
1166
|
+
const status = typeof body.status === "string" ? body.status : null;
|
|
1167
|
+
const orderId = typeof body.orderId === "string" ? body.orderId : null;
|
|
1168
|
+
if (!status || !orderId) return null;
|
|
1169
|
+
const numericOrNull = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
1170
|
+
return {
|
|
1171
|
+
orderId,
|
|
1172
|
+
status,
|
|
1173
|
+
orderCrypto: typeof body.orderCrypto === "string" ? body.orderCrypto : null,
|
|
1174
|
+
orderCryptoAmount: typeof body.orderCryptoAmount === "string" ? body.orderCryptoAmount : null,
|
|
1175
|
+
transactionId: typeof body.transactionId === "string" ? body.transactionId : null,
|
|
1176
|
+
receivedAt: typeof body.receivedAt === "string" ? body.receivedAt : null,
|
|
1177
|
+
paidAmountUsd: numericOrNull(body.paidAmountUsd),
|
|
1178
|
+
paidAmountEur: numericOrNull(body.paidAmountEur),
|
|
1179
|
+
onrampFeeUsd: numericOrNull(body.onrampFeeUsd),
|
|
1180
|
+
paymentMethod: typeof body.paymentMethod === "string" ? body.paymentMethod : null
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
function normalizeDirectPortfolio(data) {
|
|
1186
|
+
const rawTokens = extractArray(data, "tokens") ?? extractArray(
|
|
1187
|
+
data?.data,
|
|
1188
|
+
"tokens"
|
|
1189
|
+
) ?? [];
|
|
1190
|
+
const totalUsd = extractNumber(data, "totalUsd") ?? extractNumber(data?.data, "totalUsd") ?? 0;
|
|
1191
|
+
const tokens = rawTokens.map((token) => normalizeDirectToken(token)).filter((token) => Boolean(token));
|
|
1192
|
+
return { tokens, totalUsd };
|
|
1193
|
+
}
|
|
1194
|
+
function extractOrchestratorPortfolio(data) {
|
|
1195
|
+
const portfolio = extractArray(data, "portfolio") ?? extractArray(
|
|
1196
|
+
data?.data,
|
|
1197
|
+
"portfolio"
|
|
1198
|
+
);
|
|
1199
|
+
if (!portfolio || !Array.isArray(portfolio)) return null;
|
|
1200
|
+
return { portfolio };
|
|
1201
|
+
}
|
|
1202
|
+
function normalizeOrchestratorPortfolio(data) {
|
|
1203
|
+
const tokens = [];
|
|
1204
|
+
for (const tokenData of data.portfolio || []) {
|
|
1205
|
+
const chainBalances = tokenData.tokenChainBalance ?? tokenData.chainBalances ?? [];
|
|
1206
|
+
for (const chainBalance of chainBalances) {
|
|
1207
|
+
const unlocked = chainBalance.balance?.unlocked ?? "0";
|
|
1208
|
+
const normalizedName = tokenData.tokenName.trim();
|
|
1209
|
+
const isNativeSymbol = normalizedName.toUpperCase() === "ETH" || normalizedName.toUpperCase() === "ETHER";
|
|
1210
|
+
const tokenAddress = chainBalance.tokenAddress ?? extractTokenAddress(tokenData, chainBalance.chainId) ?? getTokenAddress(tokenData.tokenName, chainBalance.chainId);
|
|
1211
|
+
const resolvedTokenAddress = isNativeSymbol ? NATIVE_TOKEN_ADDRESS : tokenAddress;
|
|
1212
|
+
if (!resolvedTokenAddress) {
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
const registrySymbol = getTokenSymbol(resolvedTokenAddress, chainBalance.chainId);
|
|
1216
|
+
const symbol = registrySymbol !== "Token" ? registrySymbol : normalizedName || "Token";
|
|
1217
|
+
const decimals = registrySymbol !== "Token" ? getTokenDecimalsByAddress(resolvedTokenAddress, chainBalance.chainId) : tokenData.tokenDecimals ?? 18;
|
|
1218
|
+
tokens.push({
|
|
1219
|
+
chainId: chainBalance.chainId,
|
|
1220
|
+
address: resolvedTokenAddress,
|
|
1221
|
+
symbol,
|
|
1222
|
+
name: symbol,
|
|
1223
|
+
decimals,
|
|
1224
|
+
balance: unlocked,
|
|
1225
|
+
balanceUsd: 0
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return {
|
|
1230
|
+
tokens,
|
|
1231
|
+
totalUsd: 0
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
function normalizeDirectToken(token) {
|
|
1235
|
+
const rawChainId = extractNumber(token, "chainId") ?? extractNumber(token.chain, "id");
|
|
1236
|
+
const rawChainString = extractString(token, "chainId") ?? extractString(token.chain, "id");
|
|
1237
|
+
const chainId = rawChainId ?? (rawChainString ? isSolanaCaip2(rawChainString) || rawChainString.toLowerCase() === "solana" ? "solana" : parseEvmChainId(rawChainString) : null);
|
|
1238
|
+
if (chainId === null) return null;
|
|
1239
|
+
const symbol = extractString(token, "symbol") ?? extractString(token, "tokenSymbol") ?? extractString(token, "tokenName") ?? extractString(token, "name");
|
|
1240
|
+
if (!symbol) return null;
|
|
1241
|
+
const balanceValue = extractString(token, "balance") ?? extractString(token, "amount") ?? extractString(token, "value") ?? extractString(token, "rawBalance") ?? extractNumber(token, "balance")?.toString() ?? extractNumber(token, "amount")?.toString() ?? extractNumber(token, "value")?.toString() ?? extractNumber(token, "rawBalance")?.toString();
|
|
1242
|
+
if (!balanceValue) return null;
|
|
1243
|
+
const address = extractString(token, "address") ?? extractString(token, "tokenAddress") ?? extractString(
|
|
1244
|
+
token.token,
|
|
1245
|
+
"address"
|
|
1246
|
+
) ?? (typeof chainId === "number" ? getTokenAddress(symbol, chainId) : void 0);
|
|
1247
|
+
if (!address) return null;
|
|
1248
|
+
const balanceUsd = extractNumber(token, "balanceUsd") ?? extractNumber(token, "usdValue") ?? extractNumber(token, "valueUsd") ?? extractNumericString(token, "balanceUsd") ?? extractNumericString(token, "usdValue") ?? extractNumericString(token, "valueUsd") ?? 0;
|
|
1249
|
+
const isSolanaToken = chainId === "solana";
|
|
1250
|
+
const registrySymbol = isSolanaToken ? "Token" : getTokenSymbol(address, chainId);
|
|
1251
|
+
const resolvedSymbol = isSolanaToken ? symbol : registrySymbol !== "Token" ? registrySymbol : symbol;
|
|
1252
|
+
const backendDecimals = extractNumber(token, "decimals") ?? extractNumber(token, "tokenDecimals");
|
|
1253
|
+
const resolvedDecimals = !isSolanaToken && registrySymbol !== "Token" ? getTokenDecimalsByAddress(address, chainId) : backendDecimals ?? 18;
|
|
1254
|
+
return {
|
|
1255
|
+
chainId,
|
|
1256
|
+
address,
|
|
1257
|
+
symbol: resolvedSymbol,
|
|
1258
|
+
name: resolvedSymbol,
|
|
1259
|
+
decimals: resolvedDecimals,
|
|
1260
|
+
balance: balanceValue,
|
|
1261
|
+
balanceUsd
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
function extractTokenAddress(tokenData, chainId) {
|
|
1265
|
+
const token = tokenData;
|
|
1266
|
+
return token.tokenAddress ?? token.address ?? token.addresses?.[chainId] ?? token.token?.address ?? token.token?.addresses?.[chainId];
|
|
1267
|
+
}
|
|
1268
|
+
function extractArray(data, key) {
|
|
1269
|
+
if (!data || typeof data !== "object") return null;
|
|
1270
|
+
const record = data;
|
|
1271
|
+
const value = record[key];
|
|
1272
|
+
return Array.isArray(value) ? value : null;
|
|
1273
|
+
}
|
|
1274
|
+
function extractNumber(data, key) {
|
|
1275
|
+
if (!data || typeof data !== "object") return null;
|
|
1276
|
+
const record = data;
|
|
1277
|
+
const value = record[key];
|
|
1278
|
+
return typeof value === "number" ? value : null;
|
|
1279
|
+
}
|
|
1280
|
+
function extractString(data, key) {
|
|
1281
|
+
if (!data || typeof data !== "object") return null;
|
|
1282
|
+
const record = data;
|
|
1283
|
+
const value = record[key];
|
|
1284
|
+
return typeof value === "string" ? value : null;
|
|
1285
|
+
}
|
|
1286
|
+
function extractNumericString(data, key) {
|
|
1287
|
+
const value = extractString(data, key);
|
|
1288
|
+
if (!value) return null;
|
|
1289
|
+
const parsed = Number(value);
|
|
1290
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// src/core/formatters.ts
|
|
1294
|
+
var currencyFormatter = new Intl.NumberFormat("en-US", {
|
|
1295
|
+
style: "currency",
|
|
1296
|
+
currency: "USD",
|
|
1297
|
+
maximumFractionDigits: 2
|
|
1298
|
+
});
|
|
1299
|
+
var tokenFormatter = new Intl.NumberFormat("en-US", {
|
|
1300
|
+
minimumFractionDigits: 2,
|
|
1301
|
+
maximumFractionDigits: 5
|
|
1302
|
+
});
|
|
1303
|
+
function isUnsupportedChainSwitchError(error) {
|
|
1304
|
+
if (!error) return false;
|
|
1305
|
+
const name = error instanceof Error ? error.name : "";
|
|
1306
|
+
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
1307
|
+
return name === "SwitchChainNotSupportedError" || message.includes("does not support programmatic chain switching") || message.includes("switch chain not supported") || message.includes("method not found") || message.includes("does not exist") || message.includes("is not available");
|
|
1308
|
+
}
|
|
1309
|
+
function formatUserError(raw) {
|
|
1310
|
+
const lower = raw.toLowerCase();
|
|
1311
|
+
if (lower.includes("user rejected") || lower.includes("user denied")) {
|
|
1312
|
+
return "Transaction cancelled";
|
|
1313
|
+
}
|
|
1314
|
+
if (lower.includes("insufficient funds")) {
|
|
1315
|
+
return "Insufficient funds for this transaction";
|
|
1316
|
+
}
|
|
1317
|
+
if (lower.includes("nonce too low") || lower.includes("nonce too high")) {
|
|
1318
|
+
return "Transaction conflict \u2014 please try again";
|
|
1319
|
+
}
|
|
1320
|
+
if (lower.includes("execution reverted")) {
|
|
1321
|
+
return "Transaction would fail on-chain";
|
|
1322
|
+
}
|
|
1323
|
+
if (lower.includes("timed out") || lower.includes("took too long")) {
|
|
1324
|
+
return "Request timed out \u2014 please try again";
|
|
1325
|
+
}
|
|
1326
|
+
if (lower.includes("econnrefused") || lower.includes("econnreset") || lower.includes("enotfound") || lower.includes("fetch failed")) {
|
|
1327
|
+
return "Service unavailable \u2014 please try again";
|
|
1328
|
+
}
|
|
1329
|
+
if (lower.includes("recent blockhash") || lower.includes("latest blockhash")) {
|
|
1330
|
+
return "Solana RPC unavailable \u2014 please retry";
|
|
1331
|
+
}
|
|
1332
|
+
if (lower.includes("network") || lower.includes("disconnected")) {
|
|
1333
|
+
return "Network error \u2014 check your connection";
|
|
1334
|
+
}
|
|
1335
|
+
if (lower.includes("rate limit") || lower.includes("429")) {
|
|
1336
|
+
return "Rate limited \u2014 please try again shortly";
|
|
1337
|
+
}
|
|
1338
|
+
let cleaned = raw;
|
|
1339
|
+
const stripMarkers = [
|
|
1340
|
+
"\n\nRequest Arguments:",
|
|
1341
|
+
"\nRaw Call Arguments:",
|
|
1342
|
+
"\nRequest body:",
|
|
1343
|
+
"\nContract Call:",
|
|
1344
|
+
"\nDocs:",
|
|
1345
|
+
"\nDetails:",
|
|
1346
|
+
"\nVersion:",
|
|
1347
|
+
"\nURL:"
|
|
1348
|
+
];
|
|
1349
|
+
for (const marker of stripMarkers) {
|
|
1350
|
+
const idx = cleaned.indexOf(marker);
|
|
1351
|
+
if (idx !== -1) {
|
|
1352
|
+
cleaned = cleaned.slice(0, idx).trim();
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
cleaned = cleaned.replace(/https?:\/\/\S+/g, "").trim();
|
|
1356
|
+
if (cleaned.length === 0) {
|
|
1357
|
+
return "An unexpected error occurred";
|
|
1358
|
+
}
|
|
1359
|
+
if (cleaned.length > 120) {
|
|
1360
|
+
return cleaned.slice(0, 120) + "...";
|
|
1361
|
+
}
|
|
1362
|
+
return cleaned;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// src/core/public-client.ts
|
|
1366
|
+
import { createPublicClient, http } from "viem";
|
|
1367
|
+
import { hyperliquid } from "viem/chains";
|
|
1368
|
+
var clientCache = /* @__PURE__ */ new Map();
|
|
1369
|
+
function getPublicClient(chainId) {
|
|
1370
|
+
let client = clientCache.get(chainId);
|
|
1371
|
+
if (!client) {
|
|
1372
|
+
const chain = CHAIN_BY_ID[chainId];
|
|
1373
|
+
client = createPublicClient({
|
|
1374
|
+
chain,
|
|
1375
|
+
transport: http()
|
|
1376
|
+
});
|
|
1377
|
+
clientCache.set(chainId, client);
|
|
1378
|
+
}
|
|
1379
|
+
return client;
|
|
1380
|
+
}
|
|
1381
|
+
var hyperEvmClient;
|
|
1382
|
+
function getHyperEvmReadClient() {
|
|
1383
|
+
if (!hyperEvmClient) {
|
|
1384
|
+
hyperEvmClient = createPublicClient({
|
|
1385
|
+
chain: hyperliquid,
|
|
1386
|
+
transport: http()
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
return hyperEvmClient;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// src/core/theme.ts
|
|
1393
|
+
var RADIUS_SCALE = {
|
|
1394
|
+
none: { sm: "0", md: "0", lg: "0" },
|
|
1395
|
+
sm: { sm: "4px", md: "6px", lg: "8px" },
|
|
1396
|
+
md: { sm: "8px", md: "10px", lg: "14px" },
|
|
1397
|
+
lg: { sm: "10px", md: "14px", lg: "18px" },
|
|
1398
|
+
full: { sm: "9999px", md: "9999px", lg: "9999px" }
|
|
1399
|
+
};
|
|
1400
|
+
function setVar(targets, prop, value) {
|
|
1401
|
+
for (const el of targets) {
|
|
1402
|
+
el.style.setProperty(prop, value);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
function clearVar(targets, prop) {
|
|
1406
|
+
for (const el of targets) {
|
|
1407
|
+
el.style.removeProperty(prop);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
function normalizeHex(input) {
|
|
1411
|
+
const v = input.trim().replace(/^#/, "");
|
|
1412
|
+
if (/^[0-9a-fA-F]{3}$/.test(v)) {
|
|
1413
|
+
return v.split("").map((c) => c + c).join("").toLowerCase();
|
|
1414
|
+
}
|
|
1415
|
+
if (/^[0-9a-fA-F]{6}$/.test(v)) return v.toLowerCase();
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
function hexToHsl(hex) {
|
|
1419
|
+
const normalized = normalizeHex(hex);
|
|
1420
|
+
if (!normalized) return null;
|
|
1421
|
+
const r = parseInt(normalized.slice(0, 2), 16) / 255;
|
|
1422
|
+
const g = parseInt(normalized.slice(2, 4), 16) / 255;
|
|
1423
|
+
const b = parseInt(normalized.slice(4, 6), 16) / 255;
|
|
1424
|
+
const max = Math.max(r, g, b);
|
|
1425
|
+
const min = Math.min(r, g, b);
|
|
1426
|
+
const l = (max + min) / 2;
|
|
1427
|
+
let h = 0;
|
|
1428
|
+
let s = 0;
|
|
1429
|
+
if (max !== min) {
|
|
1430
|
+
const d = max - min;
|
|
1431
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
1432
|
+
switch (max) {
|
|
1433
|
+
case r:
|
|
1434
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
1435
|
+
break;
|
|
1436
|
+
case g:
|
|
1437
|
+
h = (b - r) / d + 2;
|
|
1438
|
+
break;
|
|
1439
|
+
case b:
|
|
1440
|
+
h = (r - g) / d + 4;
|
|
1441
|
+
break;
|
|
1442
|
+
}
|
|
1443
|
+
h *= 60;
|
|
1444
|
+
}
|
|
1445
|
+
return { h, s: s * 100, l: l * 100 };
|
|
1446
|
+
}
|
|
1447
|
+
var clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
|
|
1448
|
+
var hsl = ({ h, s, l }) => `hsl(${h.toFixed(1)} ${clamp(s, 0, 100).toFixed(1)}% ${clamp(l, 0, 100).toFixed(1)}%)`;
|
|
1449
|
+
function deriveCustomPrimary(baseHex) {
|
|
1450
|
+
const base = hexToHsl(baseHex);
|
|
1451
|
+
if (!base) return null;
|
|
1452
|
+
const { h, s, l } = base;
|
|
1453
|
+
const direction = l < 25 ? 1 : -1;
|
|
1454
|
+
const hoverL = clamp(l + 8 * direction, 4, 96);
|
|
1455
|
+
const activeL = clamp(l + 14 * direction, 2, 98);
|
|
1456
|
+
const disabledBg = {
|
|
1457
|
+
h,
|
|
1458
|
+
s: clamp(s * 0.25, 0, 20),
|
|
1459
|
+
l: clamp(l > 75 ? l - 15 : 88, 12, 92)
|
|
1460
|
+
};
|
|
1461
|
+
const disabledFg = {
|
|
1462
|
+
h,
|
|
1463
|
+
s: clamp(s * 0.35, 0, 25),
|
|
1464
|
+
l: clamp(l > 75 ? 35 : 45, 30, 60)
|
|
1465
|
+
};
|
|
1466
|
+
const norm = normalizeHex(baseHex);
|
|
1467
|
+
const ch = (i) => {
|
|
1468
|
+
const v = parseInt(norm.slice(i, i + 2), 16) / 255;
|
|
1469
|
+
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
1470
|
+
};
|
|
1471
|
+
const y = 0.2126 * ch(0) + 0.7152 * ch(2) + 0.0722 * ch(4);
|
|
1472
|
+
const foreground = y > 0.45 ? "#18181b" : "#fafafa";
|
|
1473
|
+
return {
|
|
1474
|
+
base: hsl({ h, s, l }),
|
|
1475
|
+
hover: hsl({ h, s, l: hoverL }),
|
|
1476
|
+
active: hsl({ h, s, l: activeL }),
|
|
1477
|
+
disabledBg: hsl(disabledBg),
|
|
1478
|
+
disabledFg: hsl(disabledFg),
|
|
1479
|
+
foreground
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
var CUSTOM_PRIMARY_VARS = [
|
|
1483
|
+
"--rs-primary",
|
|
1484
|
+
"--rs-primary-hover",
|
|
1485
|
+
"--rs-primary-active",
|
|
1486
|
+
"--rs-primary-disabled-bg",
|
|
1487
|
+
"--rs-primary-disabled-fg",
|
|
1488
|
+
"--rs-primary-foreground",
|
|
1489
|
+
"--rs-border-accent"
|
|
1490
|
+
];
|
|
1491
|
+
function applyTheme(element, theme) {
|
|
1492
|
+
if (!element) return;
|
|
1493
|
+
const parent = element.parentElement;
|
|
1494
|
+
const targets = parent?.classList.contains("rs-modal-content") ? [element, parent] : [element];
|
|
1495
|
+
if (theme?.mode) {
|
|
1496
|
+
for (const t of targets) t.setAttribute("data-theme", theme.mode);
|
|
1497
|
+
} else {
|
|
1498
|
+
for (const t of targets) t.removeAttribute("data-theme");
|
|
1499
|
+
}
|
|
1500
|
+
if (theme?.fontColor) {
|
|
1501
|
+
setVar(targets, "--rs-foreground", theme.fontColor);
|
|
1502
|
+
}
|
|
1503
|
+
if (theme?.iconColor) {
|
|
1504
|
+
setVar(targets, "--rs-icon", theme.iconColor);
|
|
1505
|
+
}
|
|
1506
|
+
if (theme?.ctaColor) {
|
|
1507
|
+
const derived = deriveCustomPrimary(theme.ctaColor);
|
|
1508
|
+
if (derived) {
|
|
1509
|
+
setVar(targets, "--rs-primary", derived.base);
|
|
1510
|
+
setVar(
|
|
1511
|
+
targets,
|
|
1512
|
+
"--rs-primary-hover",
|
|
1513
|
+
theme.ctaHoverColor ?? derived.hover
|
|
1514
|
+
);
|
|
1515
|
+
setVar(targets, "--rs-primary-active", derived.active);
|
|
1516
|
+
setVar(targets, "--rs-primary-disabled-bg", derived.disabledBg);
|
|
1517
|
+
setVar(targets, "--rs-primary-disabled-fg", derived.disabledFg);
|
|
1518
|
+
setVar(targets, "--rs-primary-foreground", derived.foreground);
|
|
1519
|
+
setVar(targets, "--rs-border-accent", derived.base);
|
|
1520
|
+
} else {
|
|
1521
|
+
setVar(targets, "--rs-primary", theme.ctaColor);
|
|
1522
|
+
setVar(targets, "--rs-border-accent", theme.ctaColor);
|
|
1523
|
+
setVar(
|
|
1524
|
+
targets,
|
|
1525
|
+
"--rs-primary-hover",
|
|
1526
|
+
theme.ctaHoverColor ?? theme.ctaColor
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
} else {
|
|
1530
|
+
for (const v of CUSTOM_PRIMARY_VARS) clearVar(targets, v);
|
|
1531
|
+
if (theme?.ctaHoverColor) {
|
|
1532
|
+
setVar(targets, "--rs-primary-hover", theme.ctaHoverColor);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
if (theme?.borderColor) {
|
|
1536
|
+
setVar(targets, "--rs-border", theme.borderColor);
|
|
1537
|
+
setVar(targets, "--rs-border-surface", theme.borderColor);
|
|
1538
|
+
}
|
|
1539
|
+
if (theme?.backgroundColor) {
|
|
1540
|
+
setVar(targets, "--rs-background", theme.backgroundColor);
|
|
1541
|
+
}
|
|
1542
|
+
if (theme?.radius) {
|
|
1543
|
+
const scale = RADIUS_SCALE[theme.radius];
|
|
1544
|
+
setVar(targets, "--rs-radius-sm", scale.sm);
|
|
1545
|
+
setVar(targets, "--rs-radius-md", scale.md);
|
|
1546
|
+
setVar(targets, "--rs-radius-lg", scale.lg);
|
|
1547
|
+
setVar(targets, "--rs-radius", scale.md);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// src/components/ui/BodyHeader.tsx
|
|
1552
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1553
|
+
function BodyHeader({
|
|
1554
|
+
icon,
|
|
1555
|
+
title,
|
|
1556
|
+
subtitle,
|
|
1557
|
+
variant = "default"
|
|
1558
|
+
}) {
|
|
1559
|
+
const iconClasses = [
|
|
1560
|
+
"rs-body-header-icon",
|
|
1561
|
+
variant === "success" ? "rs-body-header-icon--success" : "",
|
|
1562
|
+
variant === "error" ? "rs-body-header-icon--error" : ""
|
|
1563
|
+
].filter(Boolean).join(" ");
|
|
1564
|
+
return /* @__PURE__ */ jsxs2("div", { className: "rs-body-header", children: [
|
|
1565
|
+
/* @__PURE__ */ jsx4("div", { className: iconClasses, children: icon }),
|
|
1566
|
+
/* @__PURE__ */ jsxs2("div", { className: "rs-body-header-text", children: [
|
|
1567
|
+
/* @__PURE__ */ jsx4("h2", { className: "rs-body-header-title", children: title }),
|
|
1568
|
+
subtitle && /* @__PURE__ */ jsx4("p", { className: "rs-body-header-subtitle", children: subtitle })
|
|
1569
|
+
] })
|
|
1570
|
+
] });
|
|
1571
|
+
}
|
|
1572
|
+
BodyHeader.displayName = "BodyHeader";
|
|
1573
|
+
|
|
1574
|
+
// src/components/ui/PoweredBy.tsx
|
|
1575
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1576
|
+
var rhinestoneLogo = /* @__PURE__ */ jsxs3(
|
|
1577
|
+
"svg",
|
|
1578
|
+
{
|
|
1579
|
+
className: "rs-powered-by-logo",
|
|
1580
|
+
viewBox: "0 0 72 16",
|
|
1581
|
+
fill: "none",
|
|
1582
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1583
|
+
"aria-hidden": "true",
|
|
1584
|
+
children: [
|
|
1585
|
+
/* @__PURE__ */ jsxs3("g", { fill: "currentColor", clipPath: "url(#rs-pb-clip)", children: [
|
|
1586
|
+
/* @__PURE__ */ jsx5(
|
|
1587
|
+
"path",
|
|
1588
|
+
{
|
|
1589
|
+
opacity: "0.5",
|
|
1590
|
+
fillRule: "evenodd",
|
|
1591
|
+
clipRule: "evenodd",
|
|
1592
|
+
d: "M10.48 14.82a4.3 4.3 0 0 1-2.9 1 4.3 4.3 0 0 1-2.4-1 25 25 0 0 1-2.08-1.95l-.13-.13c-.78-.78-1.46-1.46-1.94-2.08a4.3 4.3 0 0 1-1-2.9c.05-.96.48-1.72 1-2.4a25 25 0 0 1 1.94-2.08l.07-.06.06-.07a25 25 0 0 1 2.08-1.94 4.3 4.3 0 0 1 2.9-1c.96.05 1.73.48 2.4 1 .62.49 1.3 1.17 2.08 1.94l.13.13c.77.78 1.46 1.46 1.94 2.09a4.3 4.3 0 0 1 1 2.9 4.3 4.3 0 0 1-1 2.39c-.48.62-1.17 1.3-1.94 2.08l-.07.07-.06.06c-.78.78-1.46 1.46-2.08 1.94m-8.9-6.63c.07 1.02.9 1.86 2.56 3.52s2.5 2.5 3.53 2.56h.32c1.03-.06 1.86-.9 3.53-2.56s2.5-2.5 2.56-3.52v-.33c-.07-1.03-.9-1.86-2.56-3.52-1.67-1.67-2.5-2.5-3.53-2.56h-.32c-1.03.06-1.86.89-3.53 2.56-1.66 1.66-2.5 2.5-2.56 3.52z"
|
|
1593
|
+
}
|
|
1594
|
+
),
|
|
1595
|
+
/* @__PURE__ */ jsx5("path", { d: "M3.66 8.01q.02-.27.28-.35a4.6 4.6 0 0 0 2.53-1.48q.72-.8 1-2.05a.4.4 0 0 1 .36-.29c.17 0 .32.12.36.29a4.6 4.6 0 0 0 1.47 2.52c.55.49 1.21.8 2.06 1a.4.4 0 0 1 .28.36.4.4 0 0 1-.28.36 4.6 4.6 0 0 0-2.53 1.47 4.6 4.6 0 0 0-1 2.06.4.4 0 0 1-.36.28.4.4 0 0 1-.36-.28A4.6 4.6 0 0 0 6 9.38a4.6 4.6 0 0 0-2.06-1.01.4.4 0 0 1-.28-.36m26.46-3.44c0 .51.38.87.96.87s.95-.36.95-.87c0-.52-.37-.86-.95-.86s-.96.34-.96.86m.2 1.44v5.33h1.53V6.01zm-4.4 5.33h-1.55V3.88h1.54v2.98c.25-.5.8-1.01 1.6-1.01 1.29 0 1.87.83 1.87 2.28v3.21h-1.54V8.3c0-.77-.34-1.16-.92-1.16-.67 0-1 .53-1 1.3zm-4.45 0h-1.54V6.01h1.54l-.27 1.22a.1.1 0 0 0 .02.1.1.1 0 0 0 .1.04q.09 0 .1-.09c.24-.84.93-1.36 1.64-1.36q.4.01.57.05v1.42l-.67-.04c-.67 0-1.5.31-1.5 2zm13 0h-1.55V6.01h1.54l-.02.9a1.8 1.8 0 0 1 1.61-1.06c1.3 0 1.88.83 1.88 2.28v3.21H36.4V8.3c0-.77-.35-1.16-.93-1.16-.67 0-1 .53-1 1.3z" }),
|
|
1596
|
+
/* @__PURE__ */ jsx5(
|
|
1597
|
+
"path",
|
|
1598
|
+
{
|
|
1599
|
+
fillRule: "evenodd",
|
|
1600
|
+
clipRule: "evenodd",
|
|
1601
|
+
d: "M38.74 8.69c0 1.63 1.01 2.82 2.7 2.82 1.51 0 2.32-.82 2.55-1.73l-1.43-.26c-.1.37-.47.82-1.12.82-.67 0-1.23-.6-1.24-1.31H44q.05-.19.06-.56a2.55 2.55 0 0 0-2.6-2.63c-1.5 0-2.72 1.2-2.72 2.85m3.82-.58h-2.3a1.2 1.2 0 0 1 1.18-1.13c.6 0 1.14.45 1.12 1.13"
|
|
1602
|
+
}
|
|
1603
|
+
),
|
|
1604
|
+
/* @__PURE__ */ jsx5("path", { d: "M47.1 11.51c-1.2 0-2.33-.41-2.55-1.65l1.43-.21q.27.8 1.1.79c.5 0 .77-.24.77-.55 0-.27-.2-.45-.75-.55l-.5-.1c-1.1-.24-1.77-.74-1.77-1.63 0-1.06.91-1.78 2.25-1.78 1.26 0 2.2.59 2.32 1.68l-1.41.22c-.07-.52-.43-.82-.91-.82-.5 0-.75.26-.75.57 0 .25.17.42.63.51l.5.1c1.28.26 1.94.77 1.94 1.75 0 1.01-1.03 1.66-2.3 1.66" }),
|
|
1605
|
+
/* @__PURE__ */ jsx5(
|
|
1606
|
+
"path",
|
|
1607
|
+
{
|
|
1608
|
+
fillRule: "evenodd",
|
|
1609
|
+
clipRule: "evenodd",
|
|
1610
|
+
d: "M54.44 8.68c0 1.53 1.08 2.83 2.78 2.83s2.79-1.3 2.79-2.83c0-1.55-1.1-2.84-2.79-2.84-1.7 0-2.78 1.29-2.78 2.84m4.02 0c0 .98-.59 1.45-1.24 1.45-.64 0-1.23-.47-1.23-1.45 0-.97.59-1.47 1.23-1.47.65 0 1.24.48 1.24 1.47"
|
|
1611
|
+
}
|
|
1612
|
+
),
|
|
1613
|
+
/* @__PURE__ */ jsx5("path", { d: "M62.4 11.34h-1.53V6.01h1.54l-.02.9a1.8 1.8 0 0 1 1.6-1.06c1.3 0 1.89.83 1.89 2.28v3.21h-1.54V8.3c0-.77-.34-1.16-.93-1.16-.66 0-1 .53-1 1.3z" }),
|
|
1614
|
+
/* @__PURE__ */ jsx5(
|
|
1615
|
+
"path",
|
|
1616
|
+
{
|
|
1617
|
+
fillRule: "evenodd",
|
|
1618
|
+
clipRule: "evenodd",
|
|
1619
|
+
d: "M66.68 8.69c0 1.63 1.02 2.82 2.71 2.82 1.51 0 2.31-.82 2.55-1.73l-1.44-.26c-.1.37-.47.82-1.12.82-.67 0-1.23-.6-1.24-1.31h3.8q.06-.19.06-.56a2.55 2.55 0 0 0-2.6-2.63c-1.5 0-2.72 1.2-2.72 2.85m3.82-.58h-2.3a1.2 1.2 0 0 1 1.18-1.13c.6 0 1.15.45 1.12 1.13"
|
|
1620
|
+
}
|
|
1621
|
+
),
|
|
1622
|
+
/* @__PURE__ */ jsx5("path", { d: "M52.44 9.05V7.26h1.53V6h-1.53V4.42h-1.53V6h-1.1v1.26h1.1v1.85l.02.49a1.9 1.9 0 0 0 1.67 1.67c.22.03.47.03.62.03h.65V9.87h-.61c-.4 0-.59 0-.7-.12-.12-.11-.12-.3-.12-.64z" })
|
|
1623
|
+
] }),
|
|
1624
|
+
/* @__PURE__ */ jsx5("defs", { children: /* @__PURE__ */ jsx5("clipPath", { id: "rs-pb-clip", children: /* @__PURE__ */ jsx5("path", { fill: "#fff", d: "M0 0h72v16H0z" }) }) })
|
|
1625
|
+
]
|
|
1626
|
+
}
|
|
1627
|
+
);
|
|
1628
|
+
function PoweredBy() {
|
|
1629
|
+
return /* @__PURE__ */ jsxs3("div", { className: "rs-powered-by", "aria-label": "Powered by Rhinestone", children: [
|
|
1630
|
+
/* @__PURE__ */ jsx5("span", { children: "Powered by" }),
|
|
1631
|
+
/* @__PURE__ */ jsx5(
|
|
1632
|
+
"a",
|
|
1633
|
+
{
|
|
1634
|
+
className: "rs-powered-by-link",
|
|
1635
|
+
href: "https://www.rhinestone.dev",
|
|
1636
|
+
target: "_blank",
|
|
1637
|
+
rel: "noopener noreferrer",
|
|
1638
|
+
children: rhinestoneLogo
|
|
1639
|
+
}
|
|
1640
|
+
)
|
|
1641
|
+
] });
|
|
1642
|
+
}
|
|
1643
|
+
PoweredBy.displayName = "PoweredBy";
|
|
1644
|
+
|
|
1645
|
+
// src/components/ui/Spinner.tsx
|
|
1646
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1647
|
+
function Spinner({ className }) {
|
|
1648
|
+
return /* @__PURE__ */ jsxs4(
|
|
1649
|
+
"svg",
|
|
1650
|
+
{
|
|
1651
|
+
className: `rs-spinner ${className || ""}`,
|
|
1652
|
+
fill: "none",
|
|
1653
|
+
viewBox: "0 0 20 21",
|
|
1654
|
+
children: [
|
|
1655
|
+
/* @__PURE__ */ jsx6(
|
|
1656
|
+
"path",
|
|
1657
|
+
{
|
|
1658
|
+
d: "M10 0.5C8.02 0.5 6.08 1.08 4.44 2.18C2.79 3.28 1.51 4.84 0.76 6.67C0.00 8.50 -0.19 10.51 0.19 12.45C0.57 14.39 1.53 16.17 2.92 17.57C4.32 18.96 6.10 19.92 8.04 20.30C9.98 20.69 11.99 20.49 13.82 19.73C15.65 18.98 17.21 17.70 18.31 16.05C19.41 14.41 20 12.47 20 10.5C20 7.84 18.94 5.30 17.07 3.42C15.19 1.55 12.65 0.5 10 0.5ZM10 17.77C8.56 17.77 7.15 17.34 5.95 16.54C4.76 15.74 3.83 14.61 3.28 13.28C2.73 11.95 2.58 10.49 2.86 9.08C3.14 7.67 3.84 6.37 4.85 5.35C5.87 4.34 7.17 3.64 8.58 3.36C9.99 3.08 11.45 3.23 12.78 3.78C14.11 4.33 15.24 5.26 16.04 6.45C16.84 7.65 17.27 9.06 17.27 10.5C17.27 12.42 16.50 14.27 15.14 15.64C13.77 17.00 11.92 17.77 10 17.77Z",
|
|
1659
|
+
fill: "currentColor",
|
|
1660
|
+
opacity: 0.3
|
|
1661
|
+
}
|
|
1662
|
+
),
|
|
1663
|
+
/* @__PURE__ */ jsx6(
|
|
1664
|
+
"path",
|
|
1665
|
+
{
|
|
1666
|
+
d: "M10 3.22C11.74 3.22 13.42 3.84 14.75 4.95C16.08 6.07 16.96 7.61 17.25 9.31C17.30 9.64 17.46 9.94 17.71 10.15C17.96 10.37 18.28 10.49 18.62 10.5C18.82 10.50 19.01 10.45 19.20 10.37C19.38 10.29 19.54 10.16 19.67 10.01C19.80 9.86 19.89 9.68 19.95 9.49C20.00 9.30 20.01 9.10 19.98 8.90C19.59 6.56 18.38 4.42 16.55 2.88C14.72 1.34 12.40 0.5 10 0.5C7.59 0.5 5.27 1.34 3.44 2.88C1.61 4.42 0.40 6.56 0.01 8.90C-0.01 9.10 -0.00 9.30 0.04 9.49C0.10 9.68 0.19 9.86 0.32 10.01C0.45 10.16 0.61 10.29 0.79 10.37C0.98 10.45 1.17 10.50 1.37 10.5C1.71 10.49 2.03 10.37 2.28 10.15C2.53 9.94 2.69 9.64 2.74 9.31C3.03 7.61 3.91 6.07 5.24 4.95C6.57 3.84 8.25 3.22 10 3.22Z",
|
|
1667
|
+
fill: "currentColor"
|
|
1668
|
+
}
|
|
1669
|
+
)
|
|
1670
|
+
]
|
|
1671
|
+
}
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// src/components/ui/ListRow.tsx
|
|
1676
|
+
import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1677
|
+
function ListRow({
|
|
1678
|
+
leading,
|
|
1679
|
+
leadingMedia,
|
|
1680
|
+
title,
|
|
1681
|
+
subtitle,
|
|
1682
|
+
meta,
|
|
1683
|
+
trailing,
|
|
1684
|
+
action,
|
|
1685
|
+
className = "",
|
|
1686
|
+
onClick,
|
|
1687
|
+
disabled,
|
|
1688
|
+
...props
|
|
1689
|
+
}) {
|
|
1690
|
+
const inner = /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
1691
|
+
leadingMedia ? /* @__PURE__ */ jsx7("span", { className: "rs-list-row-leading rs-list-row-leading--media", children: /* @__PURE__ */ jsx7("img", { src: leadingMedia, alt: "" }) }) : leading ? /* @__PURE__ */ jsx7("span", { className: "rs-list-row-leading", children: leading }) : null,
|
|
1692
|
+
/* @__PURE__ */ jsxs5("div", { className: "rs-list-row-body", children: [
|
|
1693
|
+
/* @__PURE__ */ jsxs5("div", { className: "rs-list-row-text", children: [
|
|
1694
|
+
/* @__PURE__ */ jsx7("span", { className: "rs-list-row-title", children: title }),
|
|
1695
|
+
subtitle && /* @__PURE__ */ jsx7("span", { className: "rs-list-row-subtitle", children: subtitle })
|
|
1696
|
+
] }),
|
|
1697
|
+
action ? /* @__PURE__ */ jsx7(
|
|
1698
|
+
"button",
|
|
1699
|
+
{
|
|
1700
|
+
type: "button",
|
|
1701
|
+
className: "rs-list-row-action",
|
|
1702
|
+
disabled,
|
|
1703
|
+
onClick: (e) => {
|
|
1704
|
+
e.stopPropagation();
|
|
1705
|
+
action.onClick();
|
|
1706
|
+
},
|
|
1707
|
+
children: action.label
|
|
1708
|
+
}
|
|
1709
|
+
) : meta && /* @__PURE__ */ jsx7("div", { className: "rs-list-row-meta", children: meta })
|
|
1710
|
+
] }),
|
|
1711
|
+
/* @__PURE__ */ jsx7("span", { className: "rs-list-row-chevron", children: trailing ?? /* @__PURE__ */ jsx7(ChevronRightIcon, {}) })
|
|
1712
|
+
] });
|
|
1713
|
+
if (action) {
|
|
1714
|
+
const handleKey = (e) => {
|
|
1715
|
+
if (disabled) return;
|
|
1716
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1717
|
+
e.preventDefault();
|
|
1718
|
+
onClick?.(e);
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
return /* @__PURE__ */ jsx7(
|
|
1722
|
+
"div",
|
|
1723
|
+
{
|
|
1724
|
+
className: `rs-list-row ${className}`.trim(),
|
|
1725
|
+
role: "button",
|
|
1726
|
+
tabIndex: disabled ? -1 : 0,
|
|
1727
|
+
"aria-disabled": disabled || void 0,
|
|
1728
|
+
onClick: disabled ? void 0 : (e) => onClick?.(e),
|
|
1729
|
+
onKeyDown: handleKey,
|
|
1730
|
+
children: inner
|
|
1731
|
+
}
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
return /* @__PURE__ */ jsx7(
|
|
1735
|
+
"button",
|
|
1736
|
+
{
|
|
1737
|
+
type: "button",
|
|
1738
|
+
className: `rs-list-row ${className}`.trim(),
|
|
1739
|
+
onClick,
|
|
1740
|
+
disabled,
|
|
1741
|
+
...props,
|
|
1742
|
+
children: inner
|
|
1743
|
+
}
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
ListRow.displayName = "ListRow";
|
|
1747
|
+
|
|
1748
|
+
// src/components/ui/WalletBadgeIcons/RabbyIcon.tsx
|
|
1749
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1750
|
+
function RabbyIcon() {
|
|
1751
|
+
return /* @__PURE__ */ jsxs6(
|
|
1752
|
+
"svg",
|
|
1753
|
+
{
|
|
1754
|
+
width: "20",
|
|
1755
|
+
height: "20",
|
|
1756
|
+
viewBox: "0 0 20 20",
|
|
1757
|
+
fill: "none",
|
|
1758
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1759
|
+
children: [
|
|
1760
|
+
/* @__PURE__ */ jsxs6("g", { clipPath: "url(#rs-rabby-clip)", children: [
|
|
1761
|
+
/* @__PURE__ */ jsx8(
|
|
1762
|
+
"mask",
|
|
1763
|
+
{
|
|
1764
|
+
id: "rs-rabby-mask",
|
|
1765
|
+
style: { maskType: "luminance" },
|
|
1766
|
+
maskUnits: "userSpaceOnUse",
|
|
1767
|
+
x: "0",
|
|
1768
|
+
y: "0",
|
|
1769
|
+
width: "20",
|
|
1770
|
+
height: "20",
|
|
1771
|
+
children: /* @__PURE__ */ jsx8(
|
|
1772
|
+
"path",
|
|
1773
|
+
{
|
|
1774
|
+
d: "M20 10C20 4.47 15.52 0 10 0C4.47 0 0 4.47 0 10C0 15.52 4.47 20 10 20C15.52 20 20 15.52 20 10Z",
|
|
1775
|
+
fill: "white"
|
|
1776
|
+
}
|
|
1777
|
+
)
|
|
1778
|
+
}
|
|
1779
|
+
),
|
|
1780
|
+
/* @__PURE__ */ jsxs6("g", { mask: "url(#rs-rabby-mask)", children: [
|
|
1781
|
+
/* @__PURE__ */ jsx8(
|
|
1782
|
+
"path",
|
|
1783
|
+
{
|
|
1784
|
+
d: "M20 10C20 4.47 15.52 0 10 0C4.47 0 0 4.47 0 10C0 15.52 4.47 20 10 20C15.52 20 20 15.52 20 10Z",
|
|
1785
|
+
fill: "#7084FF"
|
|
1786
|
+
}
|
|
1787
|
+
),
|
|
1788
|
+
/* @__PURE__ */ jsx8(
|
|
1789
|
+
"path",
|
|
1790
|
+
{
|
|
1791
|
+
d: "M17.25 10.86C17.82 9.57 14.98 5.97 12.26 4.47C10.55 3.31 8.77 3.47 8.41 3.98C7.62 5.10 11.03 6.05 13.32 7.16C12.83 7.37 12.36 7.76 12.09 8.25C11.24 7.31 9.37 6.51 7.18 7.16C5.70 7.59 4.47 8.62 3.99 10.18C3.85 10.11 3.70 10.09 3.54 10.10C3.39 10.11 3.24 10.16 3.11 10.25C2.98 10.33 2.87 10.45 2.80 10.58C2.72 10.72 2.69 10.87 2.69 11.03C2.69 11.55 3.10 11.96 3.62 11.96C3.71 11.96 4.01 11.90 4.01 11.90L8.77 11.93C6.87 14.96 5.36 15.41 5.36 15.93C5.36 16.46 6.80 16.32 7.34 16.12C9.93 15.18 12.71 12.26 13.19 11.42C15.19 11.67 16.87 11.70 17.25 10.86Z",
|
|
1792
|
+
fill: "url(#rs-rabby-grad0)"
|
|
1793
|
+
}
|
|
1794
|
+
),
|
|
1795
|
+
/* @__PURE__ */ jsx8(
|
|
1796
|
+
"path",
|
|
1797
|
+
{
|
|
1798
|
+
fillRule: "evenodd",
|
|
1799
|
+
clipRule: "evenodd",
|
|
1800
|
+
d: "M13.31 7.15L13.32 7.16C13.42 7.11 13.41 6.96 13.38 6.83C13.31 6.55 12.16 5.41 11.07 4.90C9.60 4.20 8.51 4.23 8.35 4.56C8.65 5.18 10.04 5.76 11.50 6.36C12.12 6.62 12.75 6.88 13.31 7.15Z",
|
|
1801
|
+
fill: "url(#rs-rabby-grad1)"
|
|
1802
|
+
}
|
|
1803
|
+
),
|
|
1804
|
+
/* @__PURE__ */ jsx8(
|
|
1805
|
+
"path",
|
|
1806
|
+
{
|
|
1807
|
+
fillRule: "evenodd",
|
|
1808
|
+
clipRule: "evenodd",
|
|
1809
|
+
d: "M11.45 13.38C11.11 13.26 10.77 13.15 10.43 13.07C10.84 12.33 10.92 11.25 10.54 10.56C10.00 9.60 9.32 9.08 7.73 9.08C6.87 9.08 4.53 9.38 4.48 11.34C4.48 11.54 4.48 11.73 4.50 11.90L8.77 11.93C8.20 12.85 7.66 13.53 7.18 14.05C7.75 14.19 8.22 14.32 8.65 14.43C9.06 14.54 9.43 14.63 9.82 14.73C10.41 14.30 10.96 13.83 11.45 13.38Z",
|
|
1810
|
+
fill: "url(#rs-rabby-grad2)"
|
|
1811
|
+
}
|
|
1812
|
+
),
|
|
1813
|
+
/* @__PURE__ */ jsx8(
|
|
1814
|
+
"path",
|
|
1815
|
+
{
|
|
1816
|
+
d: "M3.94 11.70C4.11 13.19 4.96 13.77 6.68 13.94C8.40 14.12 9.39 14.00 10.71 14.12C11.81 14.22 12.79 14.78 13.15 14.59C13.48 14.42 13.30 13.78 12.86 13.38C12.29 12.85 11.50 12.49 10.12 12.36C10.39 11.60 10.32 10.54 9.89 9.96C9.27 9.12 8.13 8.75 6.68 8.91C5.17 9.08 3.72 9.83 3.94 11.70Z",
|
|
1817
|
+
fill: "url(#rs-rabby-grad3)"
|
|
1818
|
+
}
|
|
1819
|
+
)
|
|
1820
|
+
] })
|
|
1821
|
+
] }),
|
|
1822
|
+
/* @__PURE__ */ jsxs6("defs", { children: [
|
|
1823
|
+
/* @__PURE__ */ jsxs6(
|
|
1824
|
+
"linearGradient",
|
|
1825
|
+
{
|
|
1826
|
+
id: "rs-rabby-grad0",
|
|
1827
|
+
x1: "7.00",
|
|
1828
|
+
y1: "9.72",
|
|
1829
|
+
x2: "17.13",
|
|
1830
|
+
y2: "12.58",
|
|
1831
|
+
gradientUnits: "userSpaceOnUse",
|
|
1832
|
+
children: [
|
|
1833
|
+
/* @__PURE__ */ jsx8("stop", { stopColor: "white" }),
|
|
1834
|
+
/* @__PURE__ */ jsx8("stop", { offset: "1", stopColor: "white" })
|
|
1835
|
+
]
|
|
1836
|
+
}
|
|
1837
|
+
),
|
|
1838
|
+
/* @__PURE__ */ jsxs6(
|
|
1839
|
+
"linearGradient",
|
|
1840
|
+
{
|
|
1841
|
+
id: "rs-rabby-grad1",
|
|
1842
|
+
x1: "15.42",
|
|
1843
|
+
y1: "9.53",
|
|
1844
|
+
x2: "8.09",
|
|
1845
|
+
y2: "2.21",
|
|
1846
|
+
gradientUnits: "userSpaceOnUse",
|
|
1847
|
+
children: [
|
|
1848
|
+
/* @__PURE__ */ jsx8("stop", { stopColor: "#8697FF" }),
|
|
1849
|
+
/* @__PURE__ */ jsx8("stop", { offset: "1", stopColor: "#8697FF", stopOpacity: "0" })
|
|
1850
|
+
]
|
|
1851
|
+
}
|
|
1852
|
+
),
|
|
1853
|
+
/* @__PURE__ */ jsxs6(
|
|
1854
|
+
"linearGradient",
|
|
1855
|
+
{
|
|
1856
|
+
id: "rs-rabby-grad2",
|
|
1857
|
+
x1: "11.65",
|
|
1858
|
+
y1: "13.64",
|
|
1859
|
+
x2: "4.63",
|
|
1860
|
+
y2: "9.61",
|
|
1861
|
+
gradientUnits: "userSpaceOnUse",
|
|
1862
|
+
children: [
|
|
1863
|
+
/* @__PURE__ */ jsx8("stop", { stopColor: "#8697FF" }),
|
|
1864
|
+
/* @__PURE__ */ jsx8("stop", { offset: "1", stopColor: "#8697FF", stopOpacity: "0" })
|
|
1865
|
+
]
|
|
1866
|
+
}
|
|
1867
|
+
),
|
|
1868
|
+
/* @__PURE__ */ jsxs6(
|
|
1869
|
+
"linearGradient",
|
|
1870
|
+
{
|
|
1871
|
+
id: "rs-rabby-grad3",
|
|
1872
|
+
x1: "7.61",
|
|
1873
|
+
y1: "9.64",
|
|
1874
|
+
x2: "12.37",
|
|
1875
|
+
y2: "15.67",
|
|
1876
|
+
gradientUnits: "userSpaceOnUse",
|
|
1877
|
+
children: [
|
|
1878
|
+
/* @__PURE__ */ jsx8("stop", { stopColor: "white" }),
|
|
1879
|
+
/* @__PURE__ */ jsx8("stop", { offset: "0.98", stopColor: "#D1D8FF" })
|
|
1880
|
+
]
|
|
1881
|
+
}
|
|
1882
|
+
),
|
|
1883
|
+
/* @__PURE__ */ jsx8("clipPath", { id: "rs-rabby-clip", children: /* @__PURE__ */ jsx8("rect", { width: "20", height: "20", fill: "white" }) })
|
|
1884
|
+
] })
|
|
1885
|
+
]
|
|
1886
|
+
}
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
// src/components/ui/WalletBadgeIcons/PhantomIcon.tsx
|
|
1891
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1892
|
+
function PhantomIcon() {
|
|
1893
|
+
return /* @__PURE__ */ jsxs7(
|
|
1894
|
+
"svg",
|
|
1895
|
+
{
|
|
1896
|
+
width: "20",
|
|
1897
|
+
height: "20",
|
|
1898
|
+
viewBox: "0 0 20 20",
|
|
1899
|
+
fill: "none",
|
|
1900
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1901
|
+
children: [
|
|
1902
|
+
/* @__PURE__ */ jsx9(
|
|
1903
|
+
"path",
|
|
1904
|
+
{
|
|
1905
|
+
d: "M15.18 0H4.81C2.15 0 0 2.15 0 4.81V15.18C0 17.84 2.15 20 4.81 20H15.18C17.84 20 20 17.84 20 15.18V4.81C20 2.15 17.84 0 15.18 0Z",
|
|
1906
|
+
fill: "#AB9FF2"
|
|
1907
|
+
}
|
|
1908
|
+
),
|
|
1909
|
+
/* @__PURE__ */ jsx9(
|
|
1910
|
+
"path",
|
|
1911
|
+
{
|
|
1912
|
+
fillRule: "evenodd",
|
|
1913
|
+
clipRule: "evenodd",
|
|
1914
|
+
d: "M8.61 12.94C7.77 14.23 6.37 15.85 4.50 15.85C3.62 15.85 2.77 15.49 2.77 13.91C2.77 9.89 8.26 3.67 13.35 3.67C16.25 3.67 17.40 5.68 17.40 7.96C17.40 10.89 15.50 14.24 13.61 14.24C13.01 14.24 12.72 13.91 12.72 13.39C12.72 13.25 12.74 13.10 12.79 12.94C12.14 14.04 10.90 15.07 9.73 15.07C8.88 15.07 8.45 14.53 8.45 13.78C8.45 13.51 8.51 13.23 8.61 12.94ZM15.49 7.88C15.49 8.54 15.10 8.88 14.66 8.88C14.21 8.88 13.83 8.54 13.83 7.88C13.83 7.21 14.21 6.88 14.66 6.88C15.10 6.88 15.49 7.22 15.49 7.88ZM13.00 7.88C13.00 8.54 12.60 8.88 12.17 8.88C11.72 8.88 11.33 8.54 11.33 7.88C11.33 7.22 11.72 6.88 12.17 6.88C12.60 6.88 13.00 7.22 13.00 7.88",
|
|
1915
|
+
fill: "#FFFDF8"
|
|
1916
|
+
}
|
|
1917
|
+
)
|
|
1918
|
+
]
|
|
1919
|
+
}
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
// src/components/ui/WalletBadgeIcons/WalletConnectIcon.tsx
|
|
1924
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1925
|
+
function WalletConnectIcon() {
|
|
1926
|
+
return /* @__PURE__ */ jsxs8(
|
|
1927
|
+
"svg",
|
|
1928
|
+
{
|
|
1929
|
+
width: "20",
|
|
1930
|
+
height: "20",
|
|
1931
|
+
viewBox: "0 0 20 20",
|
|
1932
|
+
fill: "none",
|
|
1933
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1934
|
+
children: [
|
|
1935
|
+
/* @__PURE__ */ jsx10("rect", { width: "20", height: "20", rx: "2.5", fill: "#3B99FC" }),
|
|
1936
|
+
/* @__PURE__ */ jsx10(
|
|
1937
|
+
"path",
|
|
1938
|
+
{
|
|
1939
|
+
d: "M6.18 7.96a5.36 5.36 0 0 1 7.64 0l.25.26a.26.26 0 0 1 0 .37l-.87.87a.13.13 0 0 1-.19 0l-.35-.35a3.74 3.74 0 0 0-5.32 0l-.38.37a.13.13 0 0 1-.19 0l-.86-.86a.26.26 0 0 1 0-.37l.27-.29Zm9.43 1.79.78.78a.26.26 0 0 1 0 .37l-3.54 3.54a.26.26 0 0 1-.37 0l-2.51-2.5a.07.07 0 0 0-.09 0l-2.51 2.5a.26.26 0 0 1-.37 0L3.46 10.9a.26.26 0 0 1 0-.37l.78-.78a.26.26 0 0 1 .37 0l2.5 2.5a.07.07 0 0 0 .1 0L9.72 9.75a.26.26 0 0 1 .37 0l2.5 2.5a.07.07 0 0 0 .1 0l2.5-2.5a.26.26 0 0 1 .37 0Z",
|
|
1940
|
+
fill: "#fff"
|
|
1941
|
+
}
|
|
1942
|
+
)
|
|
1943
|
+
]
|
|
1944
|
+
}
|
|
1945
|
+
);
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
// src/components/ui/WalletBadgeIcons/EthBadgeIcon.tsx
|
|
1949
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1950
|
+
function EthBadgeIcon() {
|
|
1951
|
+
return /* @__PURE__ */ jsxs9(
|
|
1952
|
+
"svg",
|
|
1953
|
+
{
|
|
1954
|
+
width: "20",
|
|
1955
|
+
height: "20",
|
|
1956
|
+
viewBox: "0 0 20 20",
|
|
1957
|
+
fill: "none",
|
|
1958
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1959
|
+
children: [
|
|
1960
|
+
/* @__PURE__ */ jsx11(
|
|
1961
|
+
"path",
|
|
1962
|
+
{
|
|
1963
|
+
d: "M9.99 19.93C15.48 19.93 19.93 15.48 19.93 9.99C19.93 4.51 15.48 0.06 9.99 0.06C4.51 0.06 0.06 4.51 0.06 9.99C0.06 15.48 4.51 19.93 9.99 19.93Z",
|
|
1964
|
+
fill: "#E3E3E3"
|
|
1965
|
+
}
|
|
1966
|
+
),
|
|
1967
|
+
/* @__PURE__ */ jsx11("path", { d: "M10.00 2.18L14.69 9.99L10.02 7.98L10.00 2.18Z", fill: "#2F3030" }),
|
|
1968
|
+
/* @__PURE__ */ jsx11("path", { d: "M5.31 9.99L9.98 2.18L10.00 7.98L5.31 9.99Z", fill: "#828384" }),
|
|
1969
|
+
/* @__PURE__ */ jsx11("path", { d: "M9.98 12.80L5.31 10.04L10.00 8.11L9.98 12.80Z", fill: "#343535" }),
|
|
1970
|
+
/* @__PURE__ */ jsx11("path", { d: "M14.69 10.04L10.02 8.11L10.00 12.80L14.69 10.04Z", fill: "#131313" }),
|
|
1971
|
+
/* @__PURE__ */ jsx11("path", { d: "M10.00 14.00L14.69 11.24L10.00 17.80V14.00Z", fill: "#2F3030" }),
|
|
1972
|
+
/* @__PURE__ */ jsx11("path", { d: "M10.00 14.00L5.31 11.24L10.00 17.80V14.00Z", fill: "#828384" })
|
|
1973
|
+
]
|
|
1974
|
+
}
|
|
1975
|
+
);
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
// src/components/ui/WalletBadgeIcons/SolBadgeIcon.tsx
|
|
1979
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1980
|
+
function SolBadgeIcon() {
|
|
1981
|
+
return /* @__PURE__ */ jsxs10(
|
|
1982
|
+
"svg",
|
|
1983
|
+
{
|
|
1984
|
+
width: "20",
|
|
1985
|
+
height: "20",
|
|
1986
|
+
viewBox: "0 0 20 20",
|
|
1987
|
+
fill: "none",
|
|
1988
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1989
|
+
children: [
|
|
1990
|
+
/* @__PURE__ */ jsx12("rect", { width: "20", height: "20", rx: "10", fill: "#0C0C0C" }),
|
|
1991
|
+
/* @__PURE__ */ jsx12(
|
|
1992
|
+
"path",
|
|
1993
|
+
{
|
|
1994
|
+
fillRule: "evenodd",
|
|
1995
|
+
clipRule: "evenodd",
|
|
1996
|
+
d: "M4.62 9.04H13.87C13.99 9.04 14.10 9.08 14.18 9.17L15.64 10.64C15.91 10.91 15.72 11.38 15.34 11.38H6.08C5.97 11.38 5.86 11.33 5.78 11.25L4.31 9.78C4.04 9.51 4.23 9.04 4.62 9.04ZM4.31 7.09L5.77 5.62C5.86 5.53 5.97 5.49 6.08 5.49H15.33C15.71 5.49 15.91 5.95 15.63 6.22L14.17 7.70C14.09 7.78 13.98 7.82 13.87 7.82H4.62C4.23 7.82 4.04 7.36 4.31 7.09ZM15.64 13.34L14.17 14.81C14.09 14.89 13.98 14.94 13.87 14.94H4.62C4.23 14.94 4.04 14.48 4.31 14.20L5.77 12.73C5.86 12.65 5.97 12.60 6.08 12.60H15.33C15.72 12.6 15.91 13.06 15.64 13.34Z",
|
|
1997
|
+
fill: "url(#rs-sol-badge-grad)"
|
|
1998
|
+
}
|
|
1999
|
+
),
|
|
2000
|
+
/* @__PURE__ */ jsx12("defs", { children: /* @__PURE__ */ jsxs10(
|
|
2001
|
+
"linearGradient",
|
|
2002
|
+
{
|
|
2003
|
+
id: "rs-sol-badge-grad",
|
|
2004
|
+
x1: "4.85",
|
|
2005
|
+
y1: "15.34",
|
|
2006
|
+
x2: "15.11",
|
|
2007
|
+
y2: "5.08",
|
|
2008
|
+
gradientUnits: "userSpaceOnUse",
|
|
2009
|
+
children: [
|
|
2010
|
+
/* @__PURE__ */ jsx12("stop", { stopColor: "#CB4EE8" }),
|
|
2011
|
+
/* @__PURE__ */ jsx12("stop", { offset: "1", stopColor: "#10F4B1" })
|
|
2012
|
+
]
|
|
2013
|
+
}
|
|
2014
|
+
) })
|
|
2015
|
+
]
|
|
2016
|
+
}
|
|
2017
|
+
);
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// src/components/ui/WalletBadgeIcons/BaseBadgeIcon.tsx
|
|
2021
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2022
|
+
function BaseBadgeIcon() {
|
|
2023
|
+
return /* @__PURE__ */ jsxs11(
|
|
2024
|
+
"svg",
|
|
2025
|
+
{
|
|
2026
|
+
width: "20",
|
|
2027
|
+
height: "20",
|
|
2028
|
+
viewBox: "0 0 111 111",
|
|
2029
|
+
fill: "none",
|
|
2030
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2031
|
+
children: [
|
|
2032
|
+
/* @__PURE__ */ jsx13("circle", { cx: "55.5", cy: "55.5", r: "55.5", fill: "#0052FF" }),
|
|
2033
|
+
/* @__PURE__ */ jsx13(
|
|
2034
|
+
"path",
|
|
2035
|
+
{
|
|
2036
|
+
d: "M54.92 93.4c20.94 0 37.92-16.97 37.92-37.92S75.86 17.55 54.92 17.55c-19.49 0-35.57 14.72-37.65 33.64h49.82v5.54h-49.82C19.35 75.67 35.42 93.4 54.92 93.4z",
|
|
2037
|
+
fill: "#fff"
|
|
2038
|
+
}
|
|
2039
|
+
)
|
|
2040
|
+
]
|
|
2041
|
+
}
|
|
2042
|
+
);
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
// src/components/ui/WalletBadgeIcons/index.tsx
|
|
2046
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2047
|
+
function WalletBadgeIcons() {
|
|
2048
|
+
return /* @__PURE__ */ jsxs12("span", { className: "rs-list-row-meta-icons", children: [
|
|
2049
|
+
/* @__PURE__ */ jsx14("span", { children: /* @__PURE__ */ jsx14(RabbyIcon, {}) }),
|
|
2050
|
+
/* @__PURE__ */ jsx14("span", { children: /* @__PURE__ */ jsx14(PhantomIcon, {}) }),
|
|
2051
|
+
/* @__PURE__ */ jsx14("span", { children: /* @__PURE__ */ jsx14(WalletConnectIcon, {}) })
|
|
2052
|
+
] });
|
|
2053
|
+
}
|
|
2054
|
+
WalletBadgeIcons.displayName = "WalletBadgeIcons";
|
|
2055
|
+
function ChainBadgeIcons() {
|
|
2056
|
+
return /* @__PURE__ */ jsxs12("span", { className: "rs-list-row-meta-icons", children: [
|
|
2057
|
+
/* @__PURE__ */ jsx14("span", { children: /* @__PURE__ */ jsx14(EthBadgeIcon, {}) }),
|
|
2058
|
+
/* @__PURE__ */ jsx14("span", { children: /* @__PURE__ */ jsx14(SolBadgeIcon, {}) }),
|
|
2059
|
+
/* @__PURE__ */ jsx14("span", { children: /* @__PURE__ */ jsx14(BaseBadgeIcon, {}) })
|
|
2060
|
+
] });
|
|
2061
|
+
}
|
|
2062
|
+
ChainBadgeIcons.displayName = "ChainBadgeIcons";
|
|
2063
|
+
|
|
2064
|
+
// src/components/steps/ConnectStep.tsx
|
|
2065
|
+
import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2066
|
+
function formatBalanceUsd(value) {
|
|
2067
|
+
if (!Number.isFinite(value) || value <= 0) return "$0.00";
|
|
2068
|
+
return `$${value.toFixed(2)}`;
|
|
2069
|
+
}
|
|
2070
|
+
function fiatIcon(name) {
|
|
2071
|
+
if (name === "apple") return /* @__PURE__ */ jsx15(AppleIcon, {});
|
|
2072
|
+
if (name === "bank") return /* @__PURE__ */ jsx15(BankIcon, {});
|
|
2073
|
+
return /* @__PURE__ */ jsx15(CardIcon, {});
|
|
2074
|
+
}
|
|
2075
|
+
function shorten(addr) {
|
|
2076
|
+
return addr.length > 12 ? `${addr.slice(0, 6)}...${addr.slice(-4)}` : addr;
|
|
2077
|
+
}
|
|
2078
|
+
function renderWalletLeading(row) {
|
|
2079
|
+
if (row.icon) {
|
|
2080
|
+
return /* @__PURE__ */ jsx15(
|
|
2081
|
+
"img",
|
|
2082
|
+
{
|
|
2083
|
+
src: row.icon,
|
|
2084
|
+
alt: "",
|
|
2085
|
+
style: { width: 24, height: 24, borderRadius: 6 }
|
|
2086
|
+
}
|
|
2087
|
+
);
|
|
2088
|
+
}
|
|
2089
|
+
return /* @__PURE__ */ jsx15(WalletIcon, {});
|
|
2090
|
+
}
|
|
2091
|
+
var TRANSFER_CRYPTO_LEADING = /* @__PURE__ */ jsx15(TransferCryptoIcon, {});
|
|
2092
|
+
var WALLET_LEADING = /* @__PURE__ */ jsx15(WalletIcon, {});
|
|
2093
|
+
var SMALL_SPINNER = /* @__PURE__ */ jsx15(Spinner, { className: "rs-spinner--sm" });
|
|
2094
|
+
var CONNECT_META = /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2095
|
+
/* @__PURE__ */ jsx15(WalletBadgeIcons, {}),
|
|
2096
|
+
"+100 wallets"
|
|
2097
|
+
] });
|
|
2098
|
+
var EXTRA_CHAIN_COUNT = Math.max(0, getSupportedChainIds().length - 3);
|
|
2099
|
+
var CHAIN_BADGE_META = /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2100
|
+
/* @__PURE__ */ jsx15(ChainBadgeIcons, {}),
|
|
2101
|
+
EXTRA_CHAIN_COUNT > 0 ? `+${EXTRA_CHAIN_COUNT} chains` : "All chains"
|
|
2102
|
+
] });
|
|
2103
|
+
function renderRowTrailing(state) {
|
|
2104
|
+
if (state === "loading") {
|
|
2105
|
+
return SMALL_SPINNER;
|
|
2106
|
+
}
|
|
2107
|
+
return void 0;
|
|
2108
|
+
}
|
|
2109
|
+
function ConnectStep({
|
|
2110
|
+
walletRows,
|
|
2111
|
+
onConfirmWallet,
|
|
2112
|
+
onSelectTransferCrypto,
|
|
2113
|
+
transferCryptoState,
|
|
2114
|
+
transferCryptoErrorReason,
|
|
2115
|
+
onSelectPayWithCard,
|
|
2116
|
+
fiatPaymentMethods,
|
|
2117
|
+
onSelectFiatMethod,
|
|
2118
|
+
onSelectFundFromExchange,
|
|
2119
|
+
onRequestConnect,
|
|
2120
|
+
onConnect,
|
|
2121
|
+
onDisconnect,
|
|
2122
|
+
dappImports,
|
|
2123
|
+
onSelectDappImport,
|
|
2124
|
+
title = "Deposit",
|
|
2125
|
+
subtitle
|
|
2126
|
+
}) {
|
|
2127
|
+
const rows = walletRows ?? [];
|
|
2128
|
+
const handleConnect = onConnect ?? onRequestConnect;
|
|
2129
|
+
const hasReownWallet = rows.some(
|
|
2130
|
+
(row) => row.kind === "external" || row.kind === "solana"
|
|
2131
|
+
);
|
|
2132
|
+
const showDappImports = (dappImports?.length ?? 0) > 0;
|
|
2133
|
+
const defaultSubtitle = onSelectTransferCrypto ? "Add money to your balance" : "Choose a wallet to continue";
|
|
2134
|
+
return /* @__PURE__ */ jsxs13("div", { className: "rs-screen", children: [
|
|
2135
|
+
/* @__PURE__ */ jsxs13("div", { className: "rs-screen-body rs-screen-body--gap-32", children: [
|
|
2136
|
+
/* @__PURE__ */ jsx15(
|
|
2137
|
+
BodyHeader,
|
|
2138
|
+
{
|
|
2139
|
+
icon: /* @__PURE__ */ jsx15(HandCoinsIcon, {}),
|
|
2140
|
+
title,
|
|
2141
|
+
subtitle: subtitle ?? defaultSubtitle
|
|
2142
|
+
}
|
|
2143
|
+
),
|
|
2144
|
+
/* @__PURE__ */ jsxs13("div", { className: "rs-list", children: [
|
|
2145
|
+
onSelectTransferCrypto && /* @__PURE__ */ jsx15(
|
|
2146
|
+
ListRow,
|
|
2147
|
+
{
|
|
2148
|
+
leading: TRANSFER_CRYPTO_LEADING,
|
|
2149
|
+
title: "Transfer crypto",
|
|
2150
|
+
subtitle: transferCryptoState === "error" ? transferCryptoErrorReason ?? "Couldn't prepare account \u2014 tap to retry" : "Instant - No limit",
|
|
2151
|
+
meta: CHAIN_BADGE_META,
|
|
2152
|
+
onClick: onSelectTransferCrypto
|
|
2153
|
+
}
|
|
2154
|
+
),
|
|
2155
|
+
fiatPaymentMethods && fiatPaymentMethods.length > 0 && onSelectFiatMethod ? fiatPaymentMethods.map((opt) => /* @__PURE__ */ jsx15(
|
|
2156
|
+
ListRow,
|
|
2157
|
+
{
|
|
2158
|
+
leading: fiatIcon(opt.icon),
|
|
2159
|
+
title: opt.label,
|
|
2160
|
+
subtitle: opt.sublabel,
|
|
2161
|
+
onClick: () => onSelectFiatMethod(opt.method)
|
|
2162
|
+
},
|
|
2163
|
+
opt.method
|
|
2164
|
+
)) : onSelectPayWithCard && /* @__PURE__ */ jsx15(
|
|
2165
|
+
ListRow,
|
|
2166
|
+
{
|
|
2167
|
+
leading: /* @__PURE__ */ jsx15(CardIcon, {}),
|
|
2168
|
+
title: "Pay with Card",
|
|
2169
|
+
subtitle: "Buy crypto with card or bank",
|
|
2170
|
+
onClick: onSelectPayWithCard
|
|
2171
|
+
}
|
|
2172
|
+
),
|
|
2173
|
+
onSelectFundFromExchange && /* @__PURE__ */ jsx15(
|
|
2174
|
+
ListRow,
|
|
2175
|
+
{
|
|
2176
|
+
leading: /* @__PURE__ */ jsx15(BankIcon, {}),
|
|
2177
|
+
title: "Fund from Exchange",
|
|
2178
|
+
subtitle: "Use Coinbase, Binance, MetaMask\u2026",
|
|
2179
|
+
onClick: onSelectFundFromExchange
|
|
2180
|
+
}
|
|
2181
|
+
),
|
|
2182
|
+
rows.map((row) => {
|
|
2183
|
+
const collapseToExternal = Boolean(onSelectTransferCrypto) && row.kind !== "solana";
|
|
2184
|
+
const subtitleText = row.state === "loading" ? "Preparing\u2026" : row.state === "error" ? row.errorReason ?? "Couldn't prepare wallet \u2014 tap to retry" : shorten(row.address);
|
|
2185
|
+
return /* @__PURE__ */ jsx15(
|
|
2186
|
+
ListRow,
|
|
2187
|
+
{
|
|
2188
|
+
leading: renderWalletLeading(row),
|
|
2189
|
+
title: collapseToExternal ? "External wallet" : row.label,
|
|
2190
|
+
subtitle: subtitleText,
|
|
2191
|
+
onClick: () => onConfirmWallet?.(row.id),
|
|
2192
|
+
disabled: row.state === "loading",
|
|
2193
|
+
trailing: renderRowTrailing(row.state)
|
|
2194
|
+
},
|
|
2195
|
+
row.id
|
|
2196
|
+
);
|
|
2197
|
+
}),
|
|
2198
|
+
!hasReownWallet && handleConnect && /* @__PURE__ */ jsx15(
|
|
2199
|
+
ListRow,
|
|
2200
|
+
{
|
|
2201
|
+
leading: WALLET_LEADING,
|
|
2202
|
+
title: "Connect wallet",
|
|
2203
|
+
subtitle: "Instant - No limit",
|
|
2204
|
+
meta: CONNECT_META,
|
|
2205
|
+
onClick: handleConnect
|
|
2206
|
+
}
|
|
2207
|
+
),
|
|
2208
|
+
showDappImports && dappImports?.map((row) => {
|
|
2209
|
+
if (!hasReownWallet) {
|
|
2210
|
+
return /* @__PURE__ */ jsx15(
|
|
2211
|
+
ListRow,
|
|
2212
|
+
{
|
|
2213
|
+
leading: row.icon,
|
|
2214
|
+
title: row.label,
|
|
2215
|
+
subtitle: "Connect wallet to view balance",
|
|
2216
|
+
onClick: handleConnect,
|
|
2217
|
+
disabled: !handleConnect
|
|
2218
|
+
},
|
|
2219
|
+
row.id
|
|
2220
|
+
);
|
|
2221
|
+
}
|
|
2222
|
+
if (row.status === "loading" || row.status === "needs-connect") {
|
|
2223
|
+
return /* @__PURE__ */ jsx15(
|
|
2224
|
+
ListRow,
|
|
2225
|
+
{
|
|
2226
|
+
leading: row.icon,
|
|
2227
|
+
title: row.label,
|
|
2228
|
+
subtitle: "Checking balance\u2026",
|
|
2229
|
+
disabled: true,
|
|
2230
|
+
trailing: SMALL_SPINNER
|
|
2231
|
+
},
|
|
2232
|
+
row.id
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
if (row.status.enabled) {
|
|
2236
|
+
return /* @__PURE__ */ jsx15(
|
|
2237
|
+
ListRow,
|
|
2238
|
+
{
|
|
2239
|
+
leading: row.icon,
|
|
2240
|
+
title: row.label,
|
|
2241
|
+
subtitle: formatBalanceUsd(row.status.balanceUsd),
|
|
2242
|
+
onClick: () => onSelectDappImport?.(row.id)
|
|
2243
|
+
},
|
|
2244
|
+
row.id
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
if (row.status.retryable) {
|
|
2248
|
+
return /* @__PURE__ */ jsx15(
|
|
2249
|
+
ListRow,
|
|
2250
|
+
{
|
|
2251
|
+
leading: row.icon,
|
|
2252
|
+
title: row.label,
|
|
2253
|
+
subtitle: row.status.reason,
|
|
2254
|
+
onClick: () => onSelectDappImport?.(row.id)
|
|
2255
|
+
},
|
|
2256
|
+
row.id
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
return /* @__PURE__ */ jsx15(
|
|
2260
|
+
ListRow,
|
|
2261
|
+
{
|
|
2262
|
+
leading: row.icon,
|
|
2263
|
+
title: row.label,
|
|
2264
|
+
subtitle: row.status.reason,
|
|
2265
|
+
disabled: true
|
|
2266
|
+
},
|
|
2267
|
+
row.id
|
|
2268
|
+
);
|
|
2269
|
+
})
|
|
2270
|
+
] })
|
|
2271
|
+
] }),
|
|
2272
|
+
onDisconnect && hasReownWallet && /* @__PURE__ */ jsx15("div", { className: "rs-screen-tight-row", children: /* @__PURE__ */ jsx15(
|
|
2273
|
+
"button",
|
|
2274
|
+
{
|
|
2275
|
+
type: "button",
|
|
2276
|
+
className: "rs-connect-wallet-manage",
|
|
2277
|
+
onClick: onDisconnect,
|
|
2278
|
+
children: "Disconnect wallet"
|
|
2279
|
+
}
|
|
2280
|
+
) }),
|
|
2281
|
+
/* @__PURE__ */ jsx15(PoweredBy, {})
|
|
2282
|
+
] });
|
|
2283
|
+
}
|
|
2284
|
+
ConnectStep.displayName = "ConnectStep";
|
|
2285
|
+
|
|
2286
|
+
// src/components/steps/ProcessingStep.tsx
|
|
2287
|
+
import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef4, useState as useState2 } from "react";
|
|
2288
|
+
import { formatUnits } from "viem";
|
|
2289
|
+
|
|
2290
|
+
// src/components/ui/Button.tsx
|
|
2291
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2292
|
+
function Button({
|
|
2293
|
+
children,
|
|
2294
|
+
variant = "accent",
|
|
2295
|
+
size = "default",
|
|
2296
|
+
loading = false,
|
|
2297
|
+
loadingText,
|
|
2298
|
+
fullWidth = false,
|
|
2299
|
+
disabled,
|
|
2300
|
+
className = "",
|
|
2301
|
+
...props
|
|
2302
|
+
}) {
|
|
2303
|
+
const classes = [
|
|
2304
|
+
"rs-button",
|
|
2305
|
+
`rs-button--${variant}`,
|
|
2306
|
+
`rs-button--size-${size}`,
|
|
2307
|
+
fullWidth ? "rs-button--full-width" : "",
|
|
2308
|
+
className
|
|
2309
|
+
].filter(Boolean).join(" ");
|
|
2310
|
+
const showInlineLoadingText = loading && loadingText !== void 0;
|
|
2311
|
+
return /* @__PURE__ */ jsx16(
|
|
2312
|
+
"button",
|
|
2313
|
+
{
|
|
2314
|
+
className: classes,
|
|
2315
|
+
disabled: disabled || loading,
|
|
2316
|
+
"aria-busy": loading || void 0,
|
|
2317
|
+
...props,
|
|
2318
|
+
children: showInlineLoadingText ? /* @__PURE__ */ jsxs14("span", { className: "rs-button__loading-row", children: [
|
|
2319
|
+
/* @__PURE__ */ jsx16(Spinner, { className: "rs-spinner--sm" }),
|
|
2320
|
+
/* @__PURE__ */ jsx16("span", { children: loadingText })
|
|
2321
|
+
] }) : /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
2322
|
+
/* @__PURE__ */ jsx16("span", { className: loading ? "rs-button__content--hidden" : "", children }),
|
|
2323
|
+
loading && /* @__PURE__ */ jsx16("span", { className: "rs-button__spinner", children: /* @__PURE__ */ jsx16(Spinner, { className: "rs-spinner--sm" }) })
|
|
2324
|
+
] })
|
|
2325
|
+
}
|
|
2326
|
+
);
|
|
2327
|
+
}
|
|
2328
|
+
Button.displayName = "Button";
|
|
2329
|
+
|
|
2330
|
+
// src/components/ui/Tooltip.tsx
|
|
2331
|
+
import {
|
|
2332
|
+
useState,
|
|
2333
|
+
useRef as useRef3,
|
|
2334
|
+
useEffect as useEffect3,
|
|
2335
|
+
useCallback as useCallback2
|
|
2336
|
+
} from "react";
|
|
2337
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
2338
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2339
|
+
function Tooltip({ content, children, className }) {
|
|
2340
|
+
const [open, setOpen] = useState(false);
|
|
2341
|
+
const [position, setPosition] = useState(null);
|
|
2342
|
+
const triggerRef = useRef3(null);
|
|
2343
|
+
const bubbleRef = useRef3(null);
|
|
2344
|
+
const updatePosition = useCallback2(() => {
|
|
2345
|
+
const trigger = triggerRef.current;
|
|
2346
|
+
if (!trigger) return;
|
|
2347
|
+
const rect = trigger.getBoundingClientRect();
|
|
2348
|
+
setPosition({
|
|
2349
|
+
top: rect.top,
|
|
2350
|
+
left: rect.left + rect.width / 2
|
|
2351
|
+
});
|
|
2352
|
+
}, []);
|
|
2353
|
+
useEffect3(() => {
|
|
2354
|
+
if (!open) return;
|
|
2355
|
+
updatePosition();
|
|
2356
|
+
function handleOutside(event) {
|
|
2357
|
+
const target = event.target;
|
|
2358
|
+
if (!target) return;
|
|
2359
|
+
if (triggerRef.current?.contains(target)) return;
|
|
2360
|
+
if (bubbleRef.current?.contains(target)) return;
|
|
2361
|
+
setOpen(false);
|
|
2362
|
+
}
|
|
2363
|
+
function handleKey(event) {
|
|
2364
|
+
if (event.key === "Escape") setOpen(false);
|
|
2365
|
+
}
|
|
2366
|
+
document.addEventListener("mousedown", handleOutside);
|
|
2367
|
+
document.addEventListener("touchstart", handleOutside, { passive: true });
|
|
2368
|
+
document.addEventListener("keydown", handleKey);
|
|
2369
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
2370
|
+
window.addEventListener("resize", updatePosition);
|
|
2371
|
+
return () => {
|
|
2372
|
+
document.removeEventListener("mousedown", handleOutside);
|
|
2373
|
+
document.removeEventListener("touchstart", handleOutside);
|
|
2374
|
+
document.removeEventListener("keydown", handleKey);
|
|
2375
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
2376
|
+
window.removeEventListener("resize", updatePosition);
|
|
2377
|
+
};
|
|
2378
|
+
}, [open, updatePosition]);
|
|
2379
|
+
return /* @__PURE__ */ jsxs15(
|
|
2380
|
+
"span",
|
|
2381
|
+
{
|
|
2382
|
+
ref: triggerRef,
|
|
2383
|
+
className: `rs-tooltip ${className ?? ""}`,
|
|
2384
|
+
onMouseEnter: () => setOpen(true),
|
|
2385
|
+
onMouseLeave: () => setOpen(false),
|
|
2386
|
+
children: [
|
|
2387
|
+
/* @__PURE__ */ jsx17(
|
|
2388
|
+
"span",
|
|
2389
|
+
{
|
|
2390
|
+
className: "rs-tooltip-trigger",
|
|
2391
|
+
role: "button",
|
|
2392
|
+
tabIndex: 0,
|
|
2393
|
+
"aria-label": content,
|
|
2394
|
+
onClick: (event) => {
|
|
2395
|
+
event.stopPropagation();
|
|
2396
|
+
event.preventDefault();
|
|
2397
|
+
setOpen((value) => !value);
|
|
2398
|
+
},
|
|
2399
|
+
onKeyDown: (event) => {
|
|
2400
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
2401
|
+
event.preventDefault();
|
|
2402
|
+
setOpen((value) => !value);
|
|
2403
|
+
}
|
|
2404
|
+
},
|
|
2405
|
+
children
|
|
2406
|
+
}
|
|
2407
|
+
),
|
|
2408
|
+
open && position && typeof document !== "undefined" && createPortal2(
|
|
2409
|
+
/* @__PURE__ */ jsx17(
|
|
2410
|
+
"span",
|
|
2411
|
+
{
|
|
2412
|
+
ref: bubbleRef,
|
|
2413
|
+
className: "rs-tooltip-bubble",
|
|
2414
|
+
role: "tooltip",
|
|
2415
|
+
style: { top: position.top, left: position.left },
|
|
2416
|
+
children: content
|
|
2417
|
+
}
|
|
2418
|
+
),
|
|
2419
|
+
document.body
|
|
2420
|
+
)
|
|
2421
|
+
]
|
|
2422
|
+
}
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
Tooltip.displayName = "Tooltip";
|
|
2426
|
+
|
|
2427
|
+
// src/core/webhook.ts
|
|
2428
|
+
function isRecord(value) {
|
|
2429
|
+
return typeof value === "object" && value !== null;
|
|
2430
|
+
}
|
|
2431
|
+
function asString(value) {
|
|
2432
|
+
return typeof value === "string" ? value : void 0;
|
|
2433
|
+
}
|
|
2434
|
+
function asNumber(value) {
|
|
2435
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
2436
|
+
if (typeof value !== "string") return void 0;
|
|
2437
|
+
const trimmed = value.trim();
|
|
2438
|
+
if (!trimmed) return void 0;
|
|
2439
|
+
const caipMatch = trimmed.match(/^eip155:(\d+)$/);
|
|
2440
|
+
if (caipMatch?.[1]) {
|
|
2441
|
+
const parsed2 = Number(caipMatch[1]);
|
|
2442
|
+
return Number.isFinite(parsed2) ? parsed2 : void 0;
|
|
2443
|
+
}
|
|
2444
|
+
const parsed = Number(trimmed);
|
|
2445
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2446
|
+
}
|
|
2447
|
+
function asAmount(value) {
|
|
2448
|
+
if (typeof value === "string") return value;
|
|
2449
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2450
|
+
return value.toString();
|
|
2451
|
+
}
|
|
2452
|
+
return void 0;
|
|
2453
|
+
}
|
|
2454
|
+
function asAddress(value) {
|
|
2455
|
+
if (typeof value !== "string") return void 0;
|
|
2456
|
+
return /^0x[a-fA-F0-9]{40}$/.test(value) ? value : void 0;
|
|
2457
|
+
}
|
|
2458
|
+
function getEventTxHash(event) {
|
|
2459
|
+
if (!event?.type) return void 0;
|
|
2460
|
+
if (event.type === "deposit-received") {
|
|
2461
|
+
return asString(event.data?.transactionHash);
|
|
2462
|
+
}
|
|
2463
|
+
if (event.type === "bridge-started" || event.type === "bridge-complete") {
|
|
2464
|
+
const deposit = isRecord(event.data?.deposit) ? event.data.deposit : void 0;
|
|
2465
|
+
const source = isRecord(event.data?.source) ? event.data.source : void 0;
|
|
2466
|
+
return asString(deposit?.transactionHash) ?? asString(source?.transactionHash);
|
|
2467
|
+
}
|
|
2468
|
+
if (event.type === "bridge-failed" || event.type === "error") {
|
|
2469
|
+
const deposit = isRecord(event.data?.deposit) ? event.data.deposit : void 0;
|
|
2470
|
+
const source = isRecord(event.data?.source) ? event.data.source : void 0;
|
|
2471
|
+
return asString(deposit?.transactionHash) ?? asString(source?.transactionHash);
|
|
2472
|
+
}
|
|
2473
|
+
if (event.type === "post-bridge-swap-complete" || event.type === "post-bridge-swap-failed") {
|
|
2474
|
+
const deposit = isRecord(event.data?.deposit) ? event.data.deposit : void 0;
|
|
2475
|
+
return asString(deposit?.transactionHash);
|
|
2476
|
+
}
|
|
2477
|
+
return void 0;
|
|
2478
|
+
}
|
|
2479
|
+
function getEventSourceDetails(event) {
|
|
2480
|
+
if (!event?.type || !isRecord(event.data)) return {};
|
|
2481
|
+
if (event.type === "deposit-received") {
|
|
2482
|
+
return {
|
|
2483
|
+
chainId: asNumber(event.data.chain),
|
|
2484
|
+
amount: asAmount(event.data.amount),
|
|
2485
|
+
token: asAddress(event.data.token)
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
const source = isRecord(event.data.source) ? event.data.source : void 0;
|
|
2489
|
+
const deposit = isRecord(event.data.deposit) ? event.data.deposit : void 0;
|
|
2490
|
+
if (event.type === "bridge-started" || event.type === "bridge-complete" || event.type === "bridge-failed" || event.type === "error" || event.type === "post-bridge-swap-complete" || event.type === "post-bridge-swap-failed") {
|
|
2491
|
+
return {
|
|
2492
|
+
chainId: asNumber(source?.chain) ?? asNumber(deposit?.chain),
|
|
2493
|
+
amount: asAmount(source?.amount) ?? asAmount(deposit?.amount),
|
|
2494
|
+
token: asAddress(source?.asset) ?? asAddress(deposit?.asset) ?? asAddress(deposit?.token)
|
|
2495
|
+
};
|
|
2496
|
+
}
|
|
2497
|
+
return {};
|
|
2498
|
+
}
|
|
2499
|
+
function isDepositEvent(event) {
|
|
2500
|
+
return event?.type === "deposit-received" || event?.type === "bridge-started" || event?.type === "bridge-complete" || event?.type === "bridge-failed" || event?.type === "post-bridge-swap-complete" || event?.type === "post-bridge-swap-failed" || event?.type === "error";
|
|
2501
|
+
}
|
|
2502
|
+
function isFailedEvent(event) {
|
|
2503
|
+
return event?.type === "bridge-failed" || event?.type === "post-bridge-swap-failed" || event?.type === "error";
|
|
2504
|
+
}
|
|
2505
|
+
function isHexString(value) {
|
|
2506
|
+
return value.startsWith("0x") || value.startsWith("0X");
|
|
2507
|
+
}
|
|
2508
|
+
function txRefsMatch(a, b) {
|
|
2509
|
+
if (isHexString(a) && isHexString(b)) {
|
|
2510
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
2511
|
+
}
|
|
2512
|
+
return a === b;
|
|
2513
|
+
}
|
|
2514
|
+
function formatBridgeFailedMessage(event) {
|
|
2515
|
+
const eventData = event?.data ?? {};
|
|
2516
|
+
const code = typeof eventData.errorCode === "string" ? eventData.errorCode : void 0;
|
|
2517
|
+
const backendMessage = typeof eventData.message === "string" ? eventData.message.trim() : "";
|
|
2518
|
+
function toUserFacingFailure(raw) {
|
|
2519
|
+
const lower = raw.toLowerCase();
|
|
2520
|
+
if (lower.includes("insufficient funds")) {
|
|
2521
|
+
return "Deposit was received, but processing could not continue due to insufficient funds. Please retry.";
|
|
2522
|
+
}
|
|
2523
|
+
if (lower.includes("no valid quote available")) {
|
|
2524
|
+
return "No bridge route is currently available for this transfer. Please try again shortly.";
|
|
2525
|
+
}
|
|
2526
|
+
if (lower.includes("simulation failed")) {
|
|
2527
|
+
return "Transfer processing failed during simulation. Please retry.";
|
|
2528
|
+
}
|
|
2529
|
+
if (raw.length > 220) {
|
|
2530
|
+
return "Transfer processing failed. Please retry.";
|
|
2531
|
+
}
|
|
2532
|
+
return raw;
|
|
2533
|
+
}
|
|
2534
|
+
if (backendMessage.length > 0) {
|
|
2535
|
+
return { message: toUserFacingFailure(backendMessage), code };
|
|
2536
|
+
}
|
|
2537
|
+
if (code) {
|
|
2538
|
+
return { message: `Bridge failed (${code})`, code };
|
|
2539
|
+
}
|
|
2540
|
+
return { message: "Bridge failed" };
|
|
2541
|
+
}
|
|
2542
|
+
function failureMessageForEvent(event) {
|
|
2543
|
+
if (event?.type === "error") {
|
|
2544
|
+
const message = isRecord(event.data) ? asString(event.data.message) : void 0;
|
|
2545
|
+
return message ?? "Unknown error";
|
|
2546
|
+
}
|
|
2547
|
+
return formatBridgeFailedMessage(event).message;
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
// src/components/steps/ProcessingStep.tsx
|
|
2551
|
+
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2552
|
+
function SuccessBadge() {
|
|
2553
|
+
return /* @__PURE__ */ jsxs16(
|
|
2554
|
+
"svg",
|
|
2555
|
+
{
|
|
2556
|
+
width: "40",
|
|
2557
|
+
height: "40",
|
|
2558
|
+
viewBox: "0 0 40 40",
|
|
2559
|
+
fill: "none",
|
|
2560
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2561
|
+
"aria-hidden": "true",
|
|
2562
|
+
children: [
|
|
2563
|
+
/* @__PURE__ */ jsx18(
|
|
2564
|
+
"rect",
|
|
2565
|
+
{
|
|
2566
|
+
width: "40",
|
|
2567
|
+
height: "40",
|
|
2568
|
+
rx: "8",
|
|
2569
|
+
fill: "var(--rs-icon-wrapper-bg)"
|
|
2570
|
+
}
|
|
2571
|
+
),
|
|
2572
|
+
/* @__PURE__ */ jsx18(
|
|
2573
|
+
"path",
|
|
2574
|
+
{
|
|
2575
|
+
d: "M28 14L17 25L12 20",
|
|
2576
|
+
stroke: "var(--rs-icon-wrapper-icon)",
|
|
2577
|
+
strokeWidth: "2",
|
|
2578
|
+
strokeLinecap: "round",
|
|
2579
|
+
strokeLinejoin: "round"
|
|
2580
|
+
}
|
|
2581
|
+
)
|
|
2582
|
+
]
|
|
2583
|
+
}
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
function FailedBadge() {
|
|
2587
|
+
return /* @__PURE__ */ jsxs16(
|
|
2588
|
+
"svg",
|
|
2589
|
+
{
|
|
2590
|
+
width: "40",
|
|
2591
|
+
height: "40",
|
|
2592
|
+
viewBox: "0 0 40 40",
|
|
2593
|
+
fill: "none",
|
|
2594
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2595
|
+
"aria-hidden": "true",
|
|
2596
|
+
children: [
|
|
2597
|
+
/* @__PURE__ */ jsx18("rect", { width: "40", height: "40", rx: "8", fill: "#FB2C36" }),
|
|
2598
|
+
/* @__PURE__ */ jsx18(
|
|
2599
|
+
"path",
|
|
2600
|
+
{
|
|
2601
|
+
d: "M26 14L14 26M14 14L26 26",
|
|
2602
|
+
stroke: "white",
|
|
2603
|
+
strokeWidth: "2",
|
|
2604
|
+
strokeLinecap: "round",
|
|
2605
|
+
strokeLinejoin: "round"
|
|
2606
|
+
}
|
|
2607
|
+
)
|
|
2608
|
+
]
|
|
2609
|
+
}
|
|
2610
|
+
);
|
|
2611
|
+
}
|
|
2612
|
+
var INITIAL_POLL_INTERVAL = 3e3;
|
|
2613
|
+
var MAX_POLL_INTERVAL = 3e4;
|
|
2614
|
+
var BACKOFF_MULTIPLIER = 1.5;
|
|
2615
|
+
var ESCALATED_DELAY_MS = 10 * 60 * 1e3;
|
|
2616
|
+
var SOFT_DELAY_MS = {
|
|
2617
|
+
confirming: 90 * 1e3,
|
|
2618
|
+
received: 90 * 1e3,
|
|
2619
|
+
bridging: 4 * 60 * 1e3
|
|
2620
|
+
};
|
|
2621
|
+
var PHASE_TIMINGS_PREFIX = "rhinestone:phase-timings";
|
|
2622
|
+
var STABLECOIN_SYMBOLS = /* @__PURE__ */ new Set([
|
|
2623
|
+
"USDC",
|
|
2624
|
+
"USDT",
|
|
2625
|
+
"DAI",
|
|
2626
|
+
"FRAX",
|
|
2627
|
+
"PYUSD",
|
|
2628
|
+
"USDP",
|
|
2629
|
+
"TUSD",
|
|
2630
|
+
"GUSD",
|
|
2631
|
+
"USDS",
|
|
2632
|
+
"LUSD",
|
|
2633
|
+
"BUSD",
|
|
2634
|
+
"USDE"
|
|
2635
|
+
]);
|
|
2636
|
+
function maxFractionDigitsFor(symbol) {
|
|
2637
|
+
if (!symbol) return 6;
|
|
2638
|
+
return STABLECOIN_SYMBOLS.has(symbol.toUpperCase()) ? 3 : 6;
|
|
2639
|
+
}
|
|
2640
|
+
var PAYMENT_METHOD_LABELS = {
|
|
2641
|
+
creditcard: "Card",
|
|
2642
|
+
debitcard: "Card",
|
|
2643
|
+
card: "Card",
|
|
2644
|
+
"apple-pay": "Apple Pay",
|
|
2645
|
+
applepay: "Apple Pay",
|
|
2646
|
+
"google-pay": "Google Pay",
|
|
2647
|
+
googlepay: "Google Pay",
|
|
2648
|
+
"bank-transfer": "Bank Transfer",
|
|
2649
|
+
banktransfer: "Bank Transfer",
|
|
2650
|
+
sepa: "SEPA",
|
|
2651
|
+
"open-banking": "Open Banking",
|
|
2652
|
+
"krak-pay": "Kraken Pay",
|
|
2653
|
+
krakpay: "Kraken Pay",
|
|
2654
|
+
kraken: "Kraken",
|
|
2655
|
+
coinbase: "Coinbase",
|
|
2656
|
+
binance: "Binance"
|
|
2657
|
+
};
|
|
2658
|
+
function formatPaymentMethod(method) {
|
|
2659
|
+
const key = method.trim().toLowerCase();
|
|
2660
|
+
if (PAYMENT_METHOD_LABELS[key]) return PAYMENT_METHOD_LABELS[key];
|
|
2661
|
+
return key.replace(/[-_]+/g, " ").split(" ").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
2662
|
+
}
|
|
2663
|
+
function loadPhaseTimings(txHash) {
|
|
2664
|
+
if (typeof window === "undefined") return null;
|
|
2665
|
+
try {
|
|
2666
|
+
const raw = window.localStorage.getItem(`${PHASE_TIMINGS_PREFIX}:${txHash}`);
|
|
2667
|
+
if (!raw) return null;
|
|
2668
|
+
const parsed = JSON.parse(raw);
|
|
2669
|
+
if (typeof parsed.startedAt !== "number") return null;
|
|
2670
|
+
return parsed;
|
|
2671
|
+
} catch {
|
|
2672
|
+
return null;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
function savePhaseTimings(txHash, timings) {
|
|
2676
|
+
if (typeof window === "undefined") return;
|
|
2677
|
+
try {
|
|
2678
|
+
window.localStorage.setItem(
|
|
2679
|
+
`${PHASE_TIMINGS_PREFIX}:${txHash}`,
|
|
2680
|
+
JSON.stringify(timings)
|
|
2681
|
+
);
|
|
2682
|
+
} catch {
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
function isEventForTx(event, txHash) {
|
|
2686
|
+
const eventTxHash = getEventTxHash(event);
|
|
2687
|
+
if (!eventTxHash) return false;
|
|
2688
|
+
return txRefsMatch(eventTxHash, txHash);
|
|
2689
|
+
}
|
|
2690
|
+
function parseWebhookTimestamp(event) {
|
|
2691
|
+
if (typeof event?.time !== "string") return void 0;
|
|
2692
|
+
const timestamp = Date.parse(event.time);
|
|
2693
|
+
return Number.isFinite(timestamp) ? timestamp : void 0;
|
|
2694
|
+
}
|
|
2695
|
+
function syncPhaseTimings(previous, event) {
|
|
2696
|
+
if (!event?.type) return previous;
|
|
2697
|
+
const timestamp = parseWebhookTimestamp(event) ?? Date.now();
|
|
2698
|
+
const setReceived = (event.type === "deposit-received" || event.type === "bridge-started" || event.type === "bridge-complete" || event.type === "bridge-failed" || event.type === "post-bridge-swap-complete" || event.type === "post-bridge-swap-failed" || event.type === "error") && previous.receivedAt === void 0;
|
|
2699
|
+
const setBridging = (event.type === "bridge-started" || event.type === "bridge-complete" || event.type === "post-bridge-swap-complete") && previous.bridgingAt === void 0;
|
|
2700
|
+
const setCompleted = (event.type === "bridge-complete" || event.type === "post-bridge-swap-complete") && previous.completedAt === void 0;
|
|
2701
|
+
if (!setReceived && !setBridging && !setCompleted) return previous;
|
|
2702
|
+
return {
|
|
2703
|
+
...previous,
|
|
2704
|
+
...setReceived && { receivedAt: timestamp },
|
|
2705
|
+
...setBridging && { bridgingAt: timestamp },
|
|
2706
|
+
...setCompleted && { completedAt: timestamp }
|
|
2707
|
+
};
|
|
2708
|
+
}
|
|
2709
|
+
function formatTimer(seconds) {
|
|
2710
|
+
const safe = Math.max(0, seconds);
|
|
2711
|
+
const mins = Math.floor(safe / 60);
|
|
2712
|
+
const secs = safe % 60;
|
|
2713
|
+
return `${mins}:${String(secs).padStart(2, "0")}`;
|
|
2714
|
+
}
|
|
2715
|
+
function TickerChar({ value }) {
|
|
2716
|
+
const [current, setCurrent] = useState2(value);
|
|
2717
|
+
const [previous, setPrevious] = useState2(null);
|
|
2718
|
+
const [animKey, setAnimKey] = useState2(0);
|
|
2719
|
+
useEffect4(() => {
|
|
2720
|
+
if (value === current) return;
|
|
2721
|
+
setPrevious(current);
|
|
2722
|
+
setCurrent(value);
|
|
2723
|
+
setAnimKey((k) => k + 1);
|
|
2724
|
+
const timeout = setTimeout(() => setPrevious(null), 360);
|
|
2725
|
+
return () => clearTimeout(timeout);
|
|
2726
|
+
}, [value, current]);
|
|
2727
|
+
return /* @__PURE__ */ jsxs16("span", { className: "rs-ticker-slot", children: [
|
|
2728
|
+
previous !== null && /* @__PURE__ */ jsx18("span", { className: "rs-ticker-out", children: previous }, `out-${animKey}`),
|
|
2729
|
+
/* @__PURE__ */ jsx18("span", { className: "rs-ticker-in", children: current }, `in-${animKey}`)
|
|
2730
|
+
] });
|
|
2731
|
+
}
|
|
2732
|
+
function Ticker({ value }) {
|
|
2733
|
+
return /* @__PURE__ */ jsx18("span", { className: "rs-ticker", "aria-label": value, children: value.split("").map((char, index) => (
|
|
2734
|
+
// Position-stable key so each slot keeps its own animation state.
|
|
2735
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
2736
|
+
/* @__PURE__ */ jsx18(TickerChar, { value: char }, index)
|
|
2737
|
+
)) });
|
|
2738
|
+
}
|
|
2739
|
+
function getPhaseStartTime(phaseId, phaseTimings) {
|
|
2740
|
+
if (phaseId === "confirming") return phaseTimings.startedAt;
|
|
2741
|
+
if (phaseId === "received") {
|
|
2742
|
+
return phaseTimings.receivedAt ?? phaseTimings.startedAt;
|
|
2743
|
+
}
|
|
2744
|
+
return phaseTimings.bridgingAt ?? phaseTimings.receivedAt ?? phaseTimings.startedAt;
|
|
2745
|
+
}
|
|
2746
|
+
function getFailedPhaseId(phaseTimings) {
|
|
2747
|
+
if (phaseTimings.bridgingAt !== void 0) return "bridging";
|
|
2748
|
+
if (phaseTimings.receivedAt !== void 0) return "received";
|
|
2749
|
+
return "confirming";
|
|
2750
|
+
}
|
|
2751
|
+
function getCurrentPhaseId(state, phaseTimings) {
|
|
2752
|
+
if (state.type === "failed") {
|
|
2753
|
+
return getFailedPhaseId(phaseTimings);
|
|
2754
|
+
}
|
|
2755
|
+
if (state.type === "complete") {
|
|
2756
|
+
return void 0;
|
|
2757
|
+
}
|
|
2758
|
+
if (state.lastEvent?.type === "bridge-started" || state.lastEvent?.type === "bridge-complete")
|
|
2759
|
+
return "bridging";
|
|
2760
|
+
if (state.lastEvent?.type === "deposit-received") return "received";
|
|
2761
|
+
return "confirming";
|
|
2762
|
+
}
|
|
2763
|
+
function ProcessingStep({
|
|
2764
|
+
smartAccount,
|
|
2765
|
+
solanaDepositAddress,
|
|
2766
|
+
txHash,
|
|
2767
|
+
sourceChain,
|
|
2768
|
+
sourceToken,
|
|
2769
|
+
targetChain,
|
|
2770
|
+
amount,
|
|
2771
|
+
sourceSymbol: providedSourceSymbol,
|
|
2772
|
+
sourceDecimals: providedSourceDecimals,
|
|
2773
|
+
amountUsd,
|
|
2774
|
+
hasPostBridgeActions,
|
|
2775
|
+
service,
|
|
2776
|
+
directTransfer,
|
|
2777
|
+
flowLabel = "deposit",
|
|
2778
|
+
debug,
|
|
2779
|
+
targetToken,
|
|
2780
|
+
uiConfig,
|
|
2781
|
+
quotedFeeAmount,
|
|
2782
|
+
quotedFeeSymbol,
|
|
2783
|
+
balanceAfterUsd,
|
|
2784
|
+
isSwappedOrder,
|
|
2785
|
+
onClose,
|
|
2786
|
+
onNewDeposit,
|
|
2787
|
+
onRetry,
|
|
2788
|
+
onDepositComplete,
|
|
2789
|
+
onDepositFailed,
|
|
2790
|
+
onError
|
|
2791
|
+
}) {
|
|
2792
|
+
const startTimeRef = useRef4(Date.now());
|
|
2793
|
+
const pollIntervalRef = useRef4(INITIAL_POLL_INTERVAL);
|
|
2794
|
+
const pollTimeoutRef = useRef4(null);
|
|
2795
|
+
const escalatedDelayRef = useRef4(false);
|
|
2796
|
+
const processingContextRef = useLatestRef({
|
|
2797
|
+
amount,
|
|
2798
|
+
sourceChain,
|
|
2799
|
+
sourceToken,
|
|
2800
|
+
sourceDecimals: providedSourceDecimals,
|
|
2801
|
+
amountUsd,
|
|
2802
|
+
targetChain,
|
|
2803
|
+
targetToken,
|
|
2804
|
+
hasPostBridgeActions
|
|
2805
|
+
});
|
|
2806
|
+
const onDepositCompleteRef = useLatestRef(onDepositComplete);
|
|
2807
|
+
const onDepositFailedRef = useLatestRef(onDepositFailed);
|
|
2808
|
+
const onErrorRef = useLatestRef(onError);
|
|
2809
|
+
const [state, setState] = useState2(
|
|
2810
|
+
directTransfer ? { type: "complete" } : { type: "processing" }
|
|
2811
|
+
);
|
|
2812
|
+
const [elapsedSeconds, setElapsedSeconds] = useState2(0);
|
|
2813
|
+
const [phaseTimings, setPhaseTimings] = useState2(() => {
|
|
2814
|
+
const saved = loadPhaseTimings(txHash);
|
|
2815
|
+
if (saved) {
|
|
2816
|
+
startTimeRef.current = saved.startedAt;
|
|
2817
|
+
return saved;
|
|
2818
|
+
}
|
|
2819
|
+
return { startedAt: startTimeRef.current };
|
|
2820
|
+
});
|
|
2821
|
+
const [hasEscalatedDelay, setHasEscalatedDelay] = useState2(false);
|
|
2822
|
+
const updatePhaseTimings = useCallback3(
|
|
2823
|
+
(updater) => {
|
|
2824
|
+
setPhaseTimings((previous) => {
|
|
2825
|
+
const next = updater(previous);
|
|
2826
|
+
savePhaseTimings(txHash, next);
|
|
2827
|
+
return next;
|
|
2828
|
+
});
|
|
2829
|
+
},
|
|
2830
|
+
[txHash]
|
|
2831
|
+
);
|
|
2832
|
+
useEffect4(() => {
|
|
2833
|
+
if (!directTransfer) return;
|
|
2834
|
+
const completedAt = Date.now();
|
|
2835
|
+
updatePhaseTimings(() => ({
|
|
2836
|
+
startedAt: startTimeRef.current,
|
|
2837
|
+
completedAt,
|
|
2838
|
+
endedAt: completedAt
|
|
2839
|
+
}));
|
|
2840
|
+
debugLog(debug, "processing", "direct-transfer:complete", {
|
|
2841
|
+
txHash,
|
|
2842
|
+
flowLabel
|
|
2843
|
+
});
|
|
2844
|
+
const context = processingContextRef.current;
|
|
2845
|
+
onDepositCompleteRef.current?.(txHash, void 0, {
|
|
2846
|
+
amount: context.amount,
|
|
2847
|
+
sourceChain: context.sourceChain,
|
|
2848
|
+
sourceToken: context.sourceToken,
|
|
2849
|
+
sourceDecimals: context.sourceDecimals,
|
|
2850
|
+
amountUsd: context.amountUsd,
|
|
2851
|
+
targetChain: context.targetChain,
|
|
2852
|
+
targetToken: context.targetToken
|
|
2853
|
+
});
|
|
2854
|
+
}, [
|
|
2855
|
+
debug,
|
|
2856
|
+
directTransfer,
|
|
2857
|
+
flowLabel,
|
|
2858
|
+
onDepositCompleteRef,
|
|
2859
|
+
processingContextRef,
|
|
2860
|
+
txHash,
|
|
2861
|
+
updatePhaseTimings
|
|
2862
|
+
]);
|
|
2863
|
+
useEffect4(() => {
|
|
2864
|
+
if (directTransfer || state.type !== "processing") return;
|
|
2865
|
+
const updateElapsed = () => {
|
|
2866
|
+
setElapsedSeconds(
|
|
2867
|
+
Math.floor((Date.now() - startTimeRef.current) / 1e3)
|
|
2868
|
+
);
|
|
2869
|
+
};
|
|
2870
|
+
updateElapsed();
|
|
2871
|
+
const intervalId = setInterval(updateElapsed, 1e3);
|
|
2872
|
+
return () => clearInterval(intervalId);
|
|
2873
|
+
}, [directTransfer, state.type]);
|
|
2874
|
+
useEffect4(() => {
|
|
2875
|
+
if (state.type === "processing") return;
|
|
2876
|
+
const endedAt = state.type === "complete" ? phaseTimings.completedAt ?? Date.now() : Date.now();
|
|
2877
|
+
setElapsedSeconds(Math.floor((endedAt - startTimeRef.current) / 1e3));
|
|
2878
|
+
updatePhaseTimings(
|
|
2879
|
+
(previous) => previous.endedAt !== void 0 ? previous : { ...previous, endedAt }
|
|
2880
|
+
);
|
|
2881
|
+
}, [phaseTimings.completedAt, state.type, updatePhaseTimings]);
|
|
2882
|
+
useEffect4(() => {
|
|
2883
|
+
if (!state.lastEvent) return;
|
|
2884
|
+
updatePhaseTimings(
|
|
2885
|
+
(previous) => syncPhaseTimings(previous, state.lastEvent)
|
|
2886
|
+
);
|
|
2887
|
+
}, [state.lastEvent?.time, state.lastEvent?.type, updatePhaseTimings]);
|
|
2888
|
+
const [swappedFiatContext, setSwappedFiatContext] = useState2(null);
|
|
2889
|
+
useEffect4(() => {
|
|
2890
|
+
let cancelled = false;
|
|
2891
|
+
service.fetchSwappedOrderStatus(smartAccount).then((res) => {
|
|
2892
|
+
if (cancelled || !res) return;
|
|
2893
|
+
if (res.transactionId != null) {
|
|
2894
|
+
if (res.transactionId.toLowerCase() !== txHash.toLowerCase()) return;
|
|
2895
|
+
} else if (!isSwappedOrder) {
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
setSwappedFiatContext({
|
|
2899
|
+
paidAmountUsd: res.paidAmountUsd,
|
|
2900
|
+
paidAmountEur: res.paidAmountEur,
|
|
2901
|
+
onrampFeeUsd: res.onrampFeeUsd,
|
|
2902
|
+
paymentMethod: res.paymentMethod
|
|
2903
|
+
});
|
|
2904
|
+
}).catch(() => {
|
|
2905
|
+
});
|
|
2906
|
+
return () => {
|
|
2907
|
+
cancelled = true;
|
|
2908
|
+
};
|
|
2909
|
+
}, [service, smartAccount, txHash, isSwappedOrder]);
|
|
2910
|
+
useEffect4(() => {
|
|
2911
|
+
if (directTransfer) return;
|
|
2912
|
+
if (state.type !== "processing") {
|
|
2913
|
+
pollIntervalRef.current = INITIAL_POLL_INTERVAL;
|
|
2914
|
+
return;
|
|
2915
|
+
}
|
|
2916
|
+
let isMounted = true;
|
|
2917
|
+
async function pollStatus() {
|
|
2918
|
+
try {
|
|
2919
|
+
const account = sourceChain === "solana" && solanaDepositAddress ? solanaDepositAddress : smartAccount;
|
|
2920
|
+
debugLog(debug, "processing", "poll:request", {
|
|
2921
|
+
account,
|
|
2922
|
+
txHash,
|
|
2923
|
+
intervalMs: pollIntervalRef.current
|
|
2924
|
+
});
|
|
2925
|
+
const data = await service.fetchStatus(account, txHash);
|
|
2926
|
+
const lastEvent2 = data.lastEvent;
|
|
2927
|
+
const eventMatchesTx = isEventForTx(lastEvent2, txHash);
|
|
2928
|
+
const eventForCurrentTx = eventMatchesTx ? lastEvent2 : void 0;
|
|
2929
|
+
if (lastEvent2) {
|
|
2930
|
+
const eventData = lastEvent2.data;
|
|
2931
|
+
debugLog(debug, "processing", "poll:event", {
|
|
2932
|
+
type: lastEvent2.type,
|
|
2933
|
+
matchesTx: eventMatchesTx,
|
|
2934
|
+
intentId: eventData?.intentId
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
if (!isMounted) return;
|
|
2938
|
+
const awaitingPostBridgeSwap = processingContextRef.current.hasPostBridgeActions;
|
|
2939
|
+
if (eventForCurrentTx?.type === "post-bridge-swap-complete") {
|
|
2940
|
+
setState({ type: "complete", lastEvent: eventForCurrentTx });
|
|
2941
|
+
const swapTxHash = eventForCurrentTx.data?.swap?.transactionHash;
|
|
2942
|
+
debugLog(debug, "processing", "state:complete", {
|
|
2943
|
+
txHash,
|
|
2944
|
+
destinationTxHash: swapTxHash,
|
|
2945
|
+
event: eventForCurrentTx.type
|
|
2946
|
+
});
|
|
2947
|
+
const context = processingContextRef.current;
|
|
2948
|
+
onDepositCompleteRef.current?.(txHash, swapTxHash, {
|
|
2949
|
+
amount: context.amount,
|
|
2950
|
+
sourceChain: context.sourceChain,
|
|
2951
|
+
sourceToken: context.sourceToken,
|
|
2952
|
+
sourceDecimals: context.sourceDecimals,
|
|
2953
|
+
amountUsd: context.amountUsd,
|
|
2954
|
+
targetChain: context.targetChain,
|
|
2955
|
+
targetToken: context.targetToken
|
|
2956
|
+
});
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2959
|
+
if (eventForCurrentTx?.type === "post-bridge-swap-failed") {
|
|
2960
|
+
const formatted = formatBridgeFailedMessage(eventForCurrentTx);
|
|
2961
|
+
setState({
|
|
2962
|
+
type: "failed",
|
|
2963
|
+
message: formatted.message,
|
|
2964
|
+
lastEvent: eventForCurrentTx
|
|
2965
|
+
});
|
|
2966
|
+
debugLog(debug, "processing", "state:failed", {
|
|
2967
|
+
txHash,
|
|
2968
|
+
message: formatted.message,
|
|
2969
|
+
code: formatted.code
|
|
2970
|
+
});
|
|
2971
|
+
onDepositFailedRef.current?.(txHash, formatted.message);
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
if (eventForCurrentTx?.type === "bridge-complete" && !awaitingPostBridgeSwap) {
|
|
2975
|
+
setState({ type: "complete", lastEvent: eventForCurrentTx });
|
|
2976
|
+
const destinationTxHash2 = eventForCurrentTx.data?.destination?.transactionHash;
|
|
2977
|
+
debugLog(debug, "processing", "state:complete", {
|
|
2978
|
+
txHash,
|
|
2979
|
+
destinationTxHash: destinationTxHash2,
|
|
2980
|
+
event: eventForCurrentTx.type
|
|
2981
|
+
});
|
|
2982
|
+
const context = processingContextRef.current;
|
|
2983
|
+
onDepositCompleteRef.current?.(txHash, destinationTxHash2, {
|
|
2984
|
+
amount: context.amount,
|
|
2985
|
+
sourceChain: context.sourceChain,
|
|
2986
|
+
sourceToken: context.sourceToken,
|
|
2987
|
+
sourceDecimals: context.sourceDecimals,
|
|
2988
|
+
amountUsd: context.amountUsd,
|
|
2989
|
+
targetChain: context.targetChain,
|
|
2990
|
+
targetToken: context.targetToken
|
|
2991
|
+
});
|
|
2992
|
+
return;
|
|
2993
|
+
}
|
|
2994
|
+
if (eventForCurrentTx?.type === "bridge-failed") {
|
|
2995
|
+
const formatted = formatBridgeFailedMessage(eventForCurrentTx);
|
|
2996
|
+
setState({
|
|
2997
|
+
type: "failed",
|
|
2998
|
+
message: formatted.message,
|
|
2999
|
+
lastEvent: eventForCurrentTx
|
|
3000
|
+
});
|
|
3001
|
+
debugLog(debug, "processing", "state:failed", {
|
|
3002
|
+
txHash,
|
|
3003
|
+
message: formatted.message,
|
|
3004
|
+
code: formatted.code
|
|
3005
|
+
});
|
|
3006
|
+
onDepositFailedRef.current?.(txHash, formatted.message);
|
|
3007
|
+
return;
|
|
3008
|
+
}
|
|
3009
|
+
if (eventForCurrentTx?.type === "error") {
|
|
3010
|
+
const errorMessage = eventForCurrentTx.data?.message ?? "Unknown error";
|
|
3011
|
+
setState({
|
|
3012
|
+
type: "failed",
|
|
3013
|
+
message: errorMessage,
|
|
3014
|
+
lastEvent: eventForCurrentTx
|
|
3015
|
+
});
|
|
3016
|
+
debugLog(debug, "processing", "state:error-event", {
|
|
3017
|
+
txHash,
|
|
3018
|
+
message: errorMessage
|
|
3019
|
+
});
|
|
3020
|
+
onDepositFailedRef.current?.(txHash, errorMessage);
|
|
3021
|
+
return;
|
|
3022
|
+
}
|
|
3023
|
+
setState((previous) => ({
|
|
3024
|
+
type: "processing",
|
|
3025
|
+
lastEvent: eventForCurrentTx ?? previous.lastEvent
|
|
3026
|
+
}));
|
|
3027
|
+
scheduleNextPoll();
|
|
3028
|
+
} catch (error) {
|
|
3029
|
+
debugError(debug, "processing", "poll:failure", error, {
|
|
3030
|
+
smartAccount,
|
|
3031
|
+
txHash,
|
|
3032
|
+
intervalMs: pollIntervalRef.current
|
|
3033
|
+
});
|
|
3034
|
+
scheduleNextPoll();
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
function scheduleNextPoll() {
|
|
3038
|
+
if (!isMounted) return;
|
|
3039
|
+
pollTimeoutRef.current = setTimeout(() => {
|
|
3040
|
+
pollIntervalRef.current = Math.min(
|
|
3041
|
+
pollIntervalRef.current * BACKOFF_MULTIPLIER,
|
|
3042
|
+
MAX_POLL_INTERVAL
|
|
3043
|
+
);
|
|
3044
|
+
debugLog(debug, "processing", "poll:scheduled", {
|
|
3045
|
+
nextIntervalMs: pollIntervalRef.current
|
|
3046
|
+
});
|
|
3047
|
+
pollStatus();
|
|
3048
|
+
}, pollIntervalRef.current);
|
|
3049
|
+
}
|
|
3050
|
+
pollStatus();
|
|
3051
|
+
return () => {
|
|
3052
|
+
isMounted = false;
|
|
3053
|
+
if (pollTimeoutRef.current) {
|
|
3054
|
+
clearTimeout(pollTimeoutRef.current);
|
|
3055
|
+
}
|
|
3056
|
+
};
|
|
3057
|
+
}, [
|
|
3058
|
+
debug,
|
|
3059
|
+
directTransfer,
|
|
3060
|
+
onDepositCompleteRef,
|
|
3061
|
+
onDepositFailedRef,
|
|
3062
|
+
processingContextRef,
|
|
3063
|
+
service,
|
|
3064
|
+
smartAccount,
|
|
3065
|
+
solanaDepositAddress,
|
|
3066
|
+
sourceChain,
|
|
3067
|
+
state.type,
|
|
3068
|
+
txHash
|
|
3069
|
+
]);
|
|
3070
|
+
useEffect4(() => {
|
|
3071
|
+
if (directTransfer || state.type !== "processing") return;
|
|
3072
|
+
const timeoutId = setTimeout(() => {
|
|
3073
|
+
if (escalatedDelayRef.current) return;
|
|
3074
|
+
escalatedDelayRef.current = true;
|
|
3075
|
+
setHasEscalatedDelay(true);
|
|
3076
|
+
const message = "Transfer is taking longer than expected. Your funds are safe and processing will continue automatically.";
|
|
3077
|
+
debugLog(debug, "processing", "state:delay-escalated", {
|
|
3078
|
+
txHash,
|
|
3079
|
+
timeoutMs: ESCALATED_DELAY_MS
|
|
3080
|
+
});
|
|
3081
|
+
onErrorRef.current?.(message, "PROCESS_TIMEOUT");
|
|
3082
|
+
}, ESCALATED_DELAY_MS);
|
|
3083
|
+
return () => clearTimeout(timeoutId);
|
|
3084
|
+
}, [debug, directTransfer, onErrorRef, state.type, txHash]);
|
|
3085
|
+
const isComplete = state.type === "complete";
|
|
3086
|
+
const isFailed = state.type === "failed";
|
|
3087
|
+
const isProcessing = state.type === "processing";
|
|
3088
|
+
const lastEvent = state.lastEvent;
|
|
3089
|
+
const failureMessage = state.type === "failed" ? state.message : void 0;
|
|
3090
|
+
const timelineNowMs = phaseTimings.endedAt ?? Date.now();
|
|
3091
|
+
const flowNoun = flowLabel === "withdraw" ? "withdrawal" : "deposit";
|
|
3092
|
+
const flowCapitalized = flowLabel === "withdraw" ? "Withdrawal" : "Deposit";
|
|
3093
|
+
const isPostBridgeSwapEvent = lastEvent?.type === "post-bridge-swap-complete" || lastEvent?.type === "post-bridge-swap-failed";
|
|
3094
|
+
const destinationTxHash = isPostBridgeSwapEvent ? lastEvent?.data?.swap?.transactionHash || null : lastEvent?.data?.destination?.transactionHash || null;
|
|
3095
|
+
const sourceDetails = getEventSourceDetails(lastEvent);
|
|
3096
|
+
const displaySourceChain = sourceDetails.chainId ?? sourceChain;
|
|
3097
|
+
const displaySourceToken = sourceDetails.token ?? sourceToken;
|
|
3098
|
+
const displayAmount = sourceDetails.amount ?? amount;
|
|
3099
|
+
const sourceExplorerUrl = getExplorerTxUrl(displaySourceChain, txHash);
|
|
3100
|
+
const destExplorerUrl = destinationTxHash ? getExplorerTxUrl(targetChain, destinationTxHash) : null;
|
|
3101
|
+
const isEvmSourceToken = /^0x[a-fA-F0-9]{40}$/.test(displaySourceToken);
|
|
3102
|
+
const sourceSymbol = displaySourceChain === "solana" ? providedSourceSymbol ?? "SOL" : isEvmSourceToken ? getTokenSymbol(displaySourceToken, displaySourceChain) : providedSourceSymbol ?? "Token";
|
|
3103
|
+
const sourceDecimals = displaySourceChain === "solana" ? providedSourceDecimals ?? 9 : isEvmSourceToken ? getTokenDecimalsByAddress(
|
|
3104
|
+
displaySourceToken,
|
|
3105
|
+
displaySourceChain
|
|
3106
|
+
) : providedSourceDecimals ?? 18;
|
|
3107
|
+
const amountMaxDigits = maxFractionDigitsFor(sourceSymbol);
|
|
3108
|
+
const formattedReceivedAmount = (() => {
|
|
3109
|
+
try {
|
|
3110
|
+
const raw = formatUnits(BigInt(displayAmount), sourceDecimals);
|
|
3111
|
+
const numeric = Number(raw);
|
|
3112
|
+
if (!Number.isFinite(numeric)) return raw;
|
|
3113
|
+
return numeric.toLocaleString("en-US", {
|
|
3114
|
+
minimumFractionDigits: 2,
|
|
3115
|
+
maximumFractionDigits: amountMaxDigits
|
|
3116
|
+
});
|
|
3117
|
+
} catch {
|
|
3118
|
+
return Number(displayAmount).toLocaleString("en-US", {
|
|
3119
|
+
minimumFractionDigits: 2,
|
|
3120
|
+
maximumFractionDigits: amountMaxDigits
|
|
3121
|
+
});
|
|
3122
|
+
}
|
|
3123
|
+
})();
|
|
3124
|
+
const destinationAmountRaw = (() => {
|
|
3125
|
+
const dest = lastEvent?.data?.destination;
|
|
3126
|
+
if (!dest || dest.amount === void 0) return void 0;
|
|
3127
|
+
try {
|
|
3128
|
+
return BigInt(dest.amount);
|
|
3129
|
+
} catch {
|
|
3130
|
+
return void 0;
|
|
3131
|
+
}
|
|
3132
|
+
})();
|
|
3133
|
+
const sourceAmountRaw = (() => {
|
|
3134
|
+
try {
|
|
3135
|
+
return BigInt(displayAmount);
|
|
3136
|
+
} catch {
|
|
3137
|
+
return void 0;
|
|
3138
|
+
}
|
|
3139
|
+
})();
|
|
3140
|
+
const bridgingCostRaw = sourceAmountRaw !== void 0 && destinationAmountRaw !== void 0 && sourceAmountRaw > destinationAmountRaw ? sourceAmountRaw - destinationAmountRaw : void 0;
|
|
3141
|
+
const formattedDestinationAmount = destinationAmountRaw !== void 0 ? (() => {
|
|
3142
|
+
try {
|
|
3143
|
+
const raw = formatUnits(destinationAmountRaw, sourceDecimals);
|
|
3144
|
+
const numeric = Number(raw);
|
|
3145
|
+
if (!Number.isFinite(numeric)) return raw;
|
|
3146
|
+
return numeric.toLocaleString("en-US", {
|
|
3147
|
+
minimumFractionDigits: 2,
|
|
3148
|
+
maximumFractionDigits: amountMaxDigits
|
|
3149
|
+
});
|
|
3150
|
+
} catch {
|
|
3151
|
+
return void 0;
|
|
3152
|
+
}
|
|
3153
|
+
})() : void 0;
|
|
3154
|
+
const formattedBridgingCost = bridgingCostRaw !== void 0 ? (() => {
|
|
3155
|
+
try {
|
|
3156
|
+
const raw = formatUnits(bridgingCostRaw, sourceDecimals);
|
|
3157
|
+
const numeric = Number(raw);
|
|
3158
|
+
if (!Number.isFinite(numeric)) return raw;
|
|
3159
|
+
return numeric.toLocaleString("en-US", {
|
|
3160
|
+
minimumFractionDigits: 2,
|
|
3161
|
+
maximumFractionDigits: amountMaxDigits
|
|
3162
|
+
});
|
|
3163
|
+
} catch {
|
|
3164
|
+
return void 0;
|
|
3165
|
+
}
|
|
3166
|
+
})() : void 0;
|
|
3167
|
+
const currentPhaseId = getCurrentPhaseId(state, phaseTimings);
|
|
3168
|
+
const activePhaseStartedAt = currentPhaseId ? getPhaseStartTime(currentPhaseId, phaseTimings) : void 0;
|
|
3169
|
+
const activePhaseElapsedMs = isProcessing && activePhaseStartedAt !== void 0 ? timelineNowMs - activePhaseStartedAt : 0;
|
|
3170
|
+
const delayPhaseId = isProcessing && currentPhaseId && activePhaseElapsedMs >= SOFT_DELAY_MS[currentPhaseId] ? currentPhaseId : void 0;
|
|
3171
|
+
void delayPhaseId;
|
|
3172
|
+
void hasEscalatedDelay;
|
|
3173
|
+
const targetSymbol = (() => {
|
|
3174
|
+
const resolved = getTargetTokenSymbol(targetToken, targetChain);
|
|
3175
|
+
return resolved !== "Token" ? resolved : providedSourceSymbol ?? "USDC";
|
|
3176
|
+
})();
|
|
3177
|
+
const targetTokenIcon = getTokenIcon(targetSymbol);
|
|
3178
|
+
const sourceChainIcon = getChainIcon(displaySourceChain);
|
|
3179
|
+
const targetChainIcon = getChainIcon(targetChain);
|
|
3180
|
+
const sourceTokenIcon = getTokenIcon(sourceSymbol);
|
|
3181
|
+
const sourceChainName = getChainName(displaySourceChain);
|
|
3182
|
+
const targetChainName = getChainName(targetChain);
|
|
3183
|
+
const timerText = formatTimer(elapsedSeconds);
|
|
3184
|
+
const feeSponsored = uiConfig?.feeSponsored ?? false;
|
|
3185
|
+
const feeTooltip = uiConfig?.feeTooltip ?? (feeSponsored ? "Network fees are sponsored for this deposit." : "Network fees apply.");
|
|
3186
|
+
const stateTitle = isComplete ? `${flowCapitalized} successful` : isFailed ? `${flowCapitalized} failed` : "Processing...";
|
|
3187
|
+
const handleRetry = onRetry ?? onNewDeposit;
|
|
3188
|
+
const headerContent = isComplete ? /* @__PURE__ */ jsxs16("div", { className: "rs-body-header", children: [
|
|
3189
|
+
/* @__PURE__ */ jsx18(SuccessBadge, {}),
|
|
3190
|
+
/* @__PURE__ */ jsx18("div", { className: "rs-body-header-text", children: /* @__PURE__ */ jsx18("h2", { className: "rs-body-header-title", children: stateTitle }) })
|
|
3191
|
+
] }) : isFailed ? /* @__PURE__ */ jsxs16("div", { className: "rs-body-header", children: [
|
|
3192
|
+
/* @__PURE__ */ jsx18(FailedBadge, {}),
|
|
3193
|
+
/* @__PURE__ */ jsx18("div", { className: "rs-body-header-text", children: /* @__PURE__ */ jsx18("h2", { className: "rs-body-header-title", children: stateTitle }) })
|
|
3194
|
+
] }) : /* @__PURE__ */ jsx18(BodyHeader, { icon: /* @__PURE__ */ jsx18(WalletIcon, {}), title: stateTitle });
|
|
3195
|
+
return /* @__PURE__ */ jsxs16("div", { className: "rs-screen", children: [
|
|
3196
|
+
/* @__PURE__ */ jsxs16("div", { className: "rs-screen-body rs-screen-body--gap-32", children: [
|
|
3197
|
+
headerContent,
|
|
3198
|
+
/* @__PURE__ */ jsxs16("div", { className: "rs-review-details", children: [
|
|
3199
|
+
/* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3200
|
+
/* @__PURE__ */ jsx18("span", { children: "Source chain" }),
|
|
3201
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3202
|
+
/* @__PURE__ */ jsx18("span", { children: sourceChainName }),
|
|
3203
|
+
sourceChainIcon && /* @__PURE__ */ jsx18("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx18("img", { src: sourceChainIcon, alt: "" }) }),
|
|
3204
|
+
isSwappedOrder && sourceExplorerUrl && /* @__PURE__ */ jsx18(
|
|
3205
|
+
"a",
|
|
3206
|
+
{
|
|
3207
|
+
href: sourceExplorerUrl,
|
|
3208
|
+
target: "_blank",
|
|
3209
|
+
rel: "noopener noreferrer",
|
|
3210
|
+
className: "rs-review-detail-link",
|
|
3211
|
+
"aria-label": "View source transaction",
|
|
3212
|
+
children: /* @__PURE__ */ jsx18(ExternalLinkIcon, {})
|
|
3213
|
+
}
|
|
3214
|
+
)
|
|
3215
|
+
] })
|
|
3216
|
+
] }),
|
|
3217
|
+
/* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3218
|
+
/* @__PURE__ */ jsx18("span", { children: "Destination chain" }),
|
|
3219
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3220
|
+
/* @__PURE__ */ jsx18("span", { children: targetChainName }),
|
|
3221
|
+
targetChainIcon && /* @__PURE__ */ jsx18("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx18("img", { src: targetChainIcon, alt: "" }) }),
|
|
3222
|
+
isSwappedOrder && destExplorerUrl && /* @__PURE__ */ jsx18(
|
|
3223
|
+
"a",
|
|
3224
|
+
{
|
|
3225
|
+
href: destExplorerUrl,
|
|
3226
|
+
target: "_blank",
|
|
3227
|
+
rel: "noopener noreferrer",
|
|
3228
|
+
className: "rs-review-detail-link",
|
|
3229
|
+
"aria-label": "View destination transaction",
|
|
3230
|
+
children: /* @__PURE__ */ jsx18(ExternalLinkIcon, {})
|
|
3231
|
+
}
|
|
3232
|
+
)
|
|
3233
|
+
] })
|
|
3234
|
+
] }),
|
|
3235
|
+
/* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3236
|
+
/* @__PURE__ */ jsx18("span", { children: isProcessing ? "Time elapsed" : "Total time" }),
|
|
3237
|
+
/* @__PURE__ */ jsx18("span", { className: "rs-review-detail-value", children: /* @__PURE__ */ jsx18(Ticker, { value: timerText }) })
|
|
3238
|
+
] }),
|
|
3239
|
+
isSwappedOrder ? /* @__PURE__ */ jsxs16(Fragment4, { children: [
|
|
3240
|
+
swappedFiatContext?.paidAmountUsd != null && /* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3241
|
+
/* @__PURE__ */ jsx18("span", { children: "You pay" }),
|
|
3242
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3243
|
+
"$",
|
|
3244
|
+
swappedFiatContext.paidAmountUsd.toFixed(2),
|
|
3245
|
+
swappedFiatContext.paymentMethod && /* @__PURE__ */ jsxs16("span", { style: { color: "#71717b", marginLeft: 6 }, children: [
|
|
3246
|
+
"via",
|
|
3247
|
+
" ",
|
|
3248
|
+
formatPaymentMethod(swappedFiatContext.paymentMethod)
|
|
3249
|
+
] })
|
|
3250
|
+
] })
|
|
3251
|
+
] }),
|
|
3252
|
+
swappedFiatContext?.onrampFeeUsd != null && /* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3253
|
+
/* @__PURE__ */ jsx18("span", { children: "On-ramp fee" }),
|
|
3254
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3255
|
+
"$",
|
|
3256
|
+
swappedFiatContext.onrampFeeUsd.toFixed(2)
|
|
3257
|
+
] })
|
|
3258
|
+
] }),
|
|
3259
|
+
formattedBridgingCost && /* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3260
|
+
/* @__PURE__ */ jsx18("span", { children: "Bridging cost" }),
|
|
3261
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3262
|
+
/* @__PURE__ */ jsxs16("span", { children: [
|
|
3263
|
+
formattedBridgingCost,
|
|
3264
|
+
" ",
|
|
3265
|
+
sourceSymbol
|
|
3266
|
+
] }),
|
|
3267
|
+
sourceTokenIcon && /* @__PURE__ */ jsx18("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx18("img", { src: sourceTokenIcon, alt: "" }) })
|
|
3268
|
+
] })
|
|
3269
|
+
] })
|
|
3270
|
+
] }) : /* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3271
|
+
/* @__PURE__ */ jsx18("span", { children: isProcessing ? "You send" : "You sent" }),
|
|
3272
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3273
|
+
/* @__PURE__ */ jsxs16("span", { children: [
|
|
3274
|
+
formattedReceivedAmount,
|
|
3275
|
+
" ",
|
|
3276
|
+
sourceSymbol
|
|
3277
|
+
] }),
|
|
3278
|
+
sourceTokenIcon && /* @__PURE__ */ jsx18("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx18("img", { src: sourceTokenIcon, alt: "" }) })
|
|
3279
|
+
] })
|
|
3280
|
+
] }),
|
|
3281
|
+
/* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3282
|
+
/* @__PURE__ */ jsx18("span", { children: isProcessing ? "Receive" : "Received" }),
|
|
3283
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3284
|
+
/* @__PURE__ */ jsx18("span", { children: formattedDestinationAmount ? `${formattedDestinationAmount} ${targetSymbol}` : `~${formattedReceivedAmount} ${targetSymbol}` }),
|
|
3285
|
+
targetTokenIcon && /* @__PURE__ */ jsx18("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx18("img", { src: targetTokenIcon, alt: "" }) })
|
|
3286
|
+
] })
|
|
3287
|
+
] }),
|
|
3288
|
+
isFailed && balanceAfterUsd !== void 0 && /* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3289
|
+
/* @__PURE__ */ jsx18("span", { children: "Balance" }),
|
|
3290
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3291
|
+
"$",
|
|
3292
|
+
balanceAfterUsd.toFixed(2)
|
|
3293
|
+
] })
|
|
3294
|
+
] }),
|
|
3295
|
+
isSwappedOrder ? quotedFeeAmount !== void 0 && /* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3296
|
+
/* @__PURE__ */ jsx18("span", { children: "Fees" }),
|
|
3297
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3298
|
+
/* @__PURE__ */ jsxs16(
|
|
3299
|
+
"span",
|
|
3300
|
+
{
|
|
3301
|
+
style: feeSponsored ? { textDecoration: "line-through" } : void 0,
|
|
3302
|
+
children: [
|
|
3303
|
+
quotedFeeAmount,
|
|
3304
|
+
" ",
|
|
3305
|
+
quotedFeeSymbol ?? sourceSymbol
|
|
3306
|
+
]
|
|
3307
|
+
}
|
|
3308
|
+
),
|
|
3309
|
+
/* @__PURE__ */ jsx18(Tooltip, { content: feeTooltip, children: /* @__PURE__ */ jsx18(
|
|
3310
|
+
"span",
|
|
3311
|
+
{
|
|
3312
|
+
className: "rs-review-detail-info",
|
|
3313
|
+
"aria-label": "Fee info",
|
|
3314
|
+
children: /* @__PURE__ */ jsx18(InfoIcon, {})
|
|
3315
|
+
}
|
|
3316
|
+
) })
|
|
3317
|
+
] })
|
|
3318
|
+
] }) : (() => {
|
|
3319
|
+
const feeValue = quotedFeeAmount ?? formattedBridgingCost;
|
|
3320
|
+
if (feeValue === void 0) return null;
|
|
3321
|
+
return /* @__PURE__ */ jsxs16("div", { className: "rs-review-detail-row", children: [
|
|
3322
|
+
/* @__PURE__ */ jsx18("span", { children: "Fees" }),
|
|
3323
|
+
/* @__PURE__ */ jsxs16("span", { className: "rs-review-detail-value", children: [
|
|
3324
|
+
/* @__PURE__ */ jsxs16(
|
|
3325
|
+
"span",
|
|
3326
|
+
{
|
|
3327
|
+
style: feeSponsored ? { textDecoration: "line-through" } : void 0,
|
|
3328
|
+
children: [
|
|
3329
|
+
feeValue,
|
|
3330
|
+
" ",
|
|
3331
|
+
quotedFeeSymbol ?? sourceSymbol
|
|
3332
|
+
]
|
|
3333
|
+
}
|
|
3334
|
+
),
|
|
3335
|
+
/* @__PURE__ */ jsx18(Tooltip, { content: feeTooltip, children: /* @__PURE__ */ jsx18(
|
|
3336
|
+
"span",
|
|
3337
|
+
{
|
|
3338
|
+
className: "rs-review-detail-info",
|
|
3339
|
+
"aria-label": "Fee info",
|
|
3340
|
+
children: /* @__PURE__ */ jsx18(InfoIcon, {})
|
|
3341
|
+
}
|
|
3342
|
+
) })
|
|
3343
|
+
] })
|
|
3344
|
+
] });
|
|
3345
|
+
})()
|
|
3346
|
+
] }),
|
|
3347
|
+
isFailed && failureMessage && /* @__PURE__ */ jsx18(Callout, { variant: "error", children: failureMessage }),
|
|
3348
|
+
isProcessing && /* @__PURE__ */ jsx18(
|
|
3349
|
+
Button,
|
|
3350
|
+
{
|
|
3351
|
+
fullWidth: true,
|
|
3352
|
+
disabled: true,
|
|
3353
|
+
loading: true,
|
|
3354
|
+
loadingText: "Submitting transaction\u2026",
|
|
3355
|
+
children: "Submitting transaction\u2026"
|
|
3356
|
+
}
|
|
3357
|
+
),
|
|
3358
|
+
isComplete && /* @__PURE__ */ jsxs16("div", { className: "rs-screen-button-row", children: [
|
|
3359
|
+
onNewDeposit && /* @__PURE__ */ jsxs16(Button, { variant: "outline", onClick: onNewDeposit, fullWidth: true, children: [
|
|
3360
|
+
"New ",
|
|
3361
|
+
flowNoun
|
|
3362
|
+
] }),
|
|
3363
|
+
onClose && /* @__PURE__ */ jsx18(Button, { onClick: onClose, fullWidth: true, children: "Done" })
|
|
3364
|
+
] }),
|
|
3365
|
+
isFailed && /* @__PURE__ */ jsxs16("div", { className: "rs-screen-button-row", children: [
|
|
3366
|
+
onClose && /* @__PURE__ */ jsx18(Button, { variant: "outline", onClick: onClose, fullWidth: true, children: "Cancel" }),
|
|
3367
|
+
handleRetry && /* @__PURE__ */ jsx18(Button, { onClick: handleRetry, fullWidth: true, children: "Try again" })
|
|
3368
|
+
] })
|
|
3369
|
+
] }),
|
|
3370
|
+
/* @__PURE__ */ jsx18(PoweredBy, {})
|
|
3371
|
+
] });
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3374
|
+
// src/core/safe.ts
|
|
3375
|
+
import {
|
|
3376
|
+
concat,
|
|
3377
|
+
encodeFunctionData,
|
|
3378
|
+
erc20Abi,
|
|
3379
|
+
hashTypedData,
|
|
3380
|
+
pad,
|
|
3381
|
+
parseEventLogs,
|
|
3382
|
+
toHex,
|
|
3383
|
+
zeroAddress
|
|
3384
|
+
} from "viem";
|
|
3385
|
+
var SAFE_ABI = [
|
|
3386
|
+
{
|
|
3387
|
+
type: "function",
|
|
3388
|
+
name: "isOwner",
|
|
3389
|
+
stateMutability: "view",
|
|
3390
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
3391
|
+
outputs: [{ name: "", type: "bool" }]
|
|
3392
|
+
},
|
|
3393
|
+
{
|
|
3394
|
+
type: "function",
|
|
3395
|
+
name: "nonce",
|
|
3396
|
+
stateMutability: "view",
|
|
3397
|
+
inputs: [],
|
|
3398
|
+
outputs: [{ type: "uint256" }]
|
|
3399
|
+
},
|
|
3400
|
+
{
|
|
3401
|
+
type: "function",
|
|
3402
|
+
name: "execTransaction",
|
|
3403
|
+
stateMutability: "payable",
|
|
3404
|
+
inputs: [
|
|
3405
|
+
{ name: "to", type: "address" },
|
|
3406
|
+
{ name: "value", type: "uint256" },
|
|
3407
|
+
{ name: "data", type: "bytes" },
|
|
3408
|
+
{ name: "operation", type: "uint8" },
|
|
3409
|
+
{ name: "safeTxGas", type: "uint256" },
|
|
3410
|
+
{ name: "baseGas", type: "uint256" },
|
|
3411
|
+
{ name: "gasPrice", type: "uint256" },
|
|
3412
|
+
{ name: "gasToken", type: "address" },
|
|
3413
|
+
{ name: "refundReceiver", type: "address" },
|
|
3414
|
+
{ name: "signatures", type: "bytes" }
|
|
3415
|
+
],
|
|
3416
|
+
outputs: [{ name: "success", type: "bool" }]
|
|
3417
|
+
},
|
|
3418
|
+
{
|
|
3419
|
+
type: "event",
|
|
3420
|
+
name: "ExecutionSuccess",
|
|
3421
|
+
inputs: [
|
|
3422
|
+
{ name: "txHash", type: "bytes32", indexed: true },
|
|
3423
|
+
{ name: "payment", type: "uint256", indexed: false }
|
|
3424
|
+
],
|
|
3425
|
+
anonymous: false
|
|
3426
|
+
},
|
|
3427
|
+
{
|
|
3428
|
+
type: "event",
|
|
3429
|
+
name: "ExecutionFailure",
|
|
3430
|
+
inputs: [
|
|
3431
|
+
{ name: "txHash", type: "bytes32", indexed: true },
|
|
3432
|
+
{ name: "payment", type: "uint256", indexed: false }
|
|
3433
|
+
],
|
|
3434
|
+
anonymous: false
|
|
3435
|
+
}
|
|
3436
|
+
];
|
|
3437
|
+
async function executeSafeEthTransfer(params) {
|
|
3438
|
+
const {
|
|
3439
|
+
walletClient,
|
|
3440
|
+
publicClient,
|
|
3441
|
+
safeAddress,
|
|
3442
|
+
recipient,
|
|
3443
|
+
amount,
|
|
3444
|
+
chainId
|
|
3445
|
+
} = params;
|
|
3446
|
+
const account = walletClient.account;
|
|
3447
|
+
const chain = walletClient.chain;
|
|
3448
|
+
if (!account || !chain) {
|
|
3449
|
+
throw new Error("Wallet not connected");
|
|
3450
|
+
}
|
|
3451
|
+
if (chain.id !== chainId) {
|
|
3452
|
+
throw new Error(`Switch to ${getChainName(chainId)} to sign`);
|
|
3453
|
+
}
|
|
3454
|
+
const isOwner = await publicClient.readContract({
|
|
3455
|
+
address: safeAddress,
|
|
3456
|
+
abi: SAFE_ABI,
|
|
3457
|
+
functionName: "isOwner",
|
|
3458
|
+
args: [account.address]
|
|
3459
|
+
});
|
|
3460
|
+
if (!isOwner) {
|
|
3461
|
+
throw new Error("Connected wallet is not a Safe owner");
|
|
3462
|
+
}
|
|
3463
|
+
const safeTx = {
|
|
3464
|
+
to: recipient,
|
|
3465
|
+
value: amount,
|
|
3466
|
+
data: "0x",
|
|
3467
|
+
operation: 0,
|
|
3468
|
+
safeTxGas: 0n,
|
|
3469
|
+
baseGas: 0n,
|
|
3470
|
+
gasPrice: 0n,
|
|
3471
|
+
gasToken: zeroAddress,
|
|
3472
|
+
refundReceiver: zeroAddress
|
|
3473
|
+
};
|
|
3474
|
+
const signature = concat([
|
|
3475
|
+
pad(account.address, { size: 32 }),
|
|
3476
|
+
pad(toHex(0), { size: 32 }),
|
|
3477
|
+
toHex(1, { size: 1 })
|
|
3478
|
+
]);
|
|
3479
|
+
const txHash = await walletClient.writeContract({
|
|
3480
|
+
account,
|
|
3481
|
+
chain,
|
|
3482
|
+
address: safeAddress,
|
|
3483
|
+
abi: SAFE_ABI,
|
|
3484
|
+
functionName: "execTransaction",
|
|
3485
|
+
args: [
|
|
3486
|
+
safeTx.to,
|
|
3487
|
+
safeTx.value,
|
|
3488
|
+
safeTx.data,
|
|
3489
|
+
safeTx.operation,
|
|
3490
|
+
safeTx.safeTxGas,
|
|
3491
|
+
safeTx.baseGas,
|
|
3492
|
+
safeTx.gasPrice,
|
|
3493
|
+
safeTx.gasToken,
|
|
3494
|
+
safeTx.refundReceiver,
|
|
3495
|
+
signature
|
|
3496
|
+
]
|
|
3497
|
+
});
|
|
3498
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3499
|
+
hash: txHash
|
|
3500
|
+
});
|
|
3501
|
+
const safeLogs = receipt.logs.filter(
|
|
3502
|
+
(log) => log.address.toLowerCase() === safeAddress.toLowerCase()
|
|
3503
|
+
);
|
|
3504
|
+
const parsed = parseEventLogs({
|
|
3505
|
+
abi: SAFE_ABI,
|
|
3506
|
+
logs: safeLogs,
|
|
3507
|
+
strict: false
|
|
3508
|
+
});
|
|
3509
|
+
const failed = parsed.find((log) => log.eventName === "ExecutionFailure");
|
|
3510
|
+
if (failed) {
|
|
3511
|
+
throw new Error("Safe transaction failed");
|
|
3512
|
+
}
|
|
3513
|
+
const succeeded = parsed.find((log) => log.eventName === "ExecutionSuccess");
|
|
3514
|
+
if (!succeeded) {
|
|
3515
|
+
throw new Error("Safe transaction status unavailable");
|
|
3516
|
+
}
|
|
3517
|
+
return { txHash };
|
|
3518
|
+
}
|
|
3519
|
+
async function executeSafeErc20Transfer(params) {
|
|
3520
|
+
const {
|
|
3521
|
+
walletClient,
|
|
3522
|
+
publicClient,
|
|
3523
|
+
safeAddress,
|
|
3524
|
+
tokenAddress,
|
|
3525
|
+
recipient,
|
|
3526
|
+
amount,
|
|
3527
|
+
chainId
|
|
3528
|
+
} = params;
|
|
3529
|
+
const account = walletClient.account;
|
|
3530
|
+
const chain = walletClient.chain;
|
|
3531
|
+
if (!account || !chain) {
|
|
3532
|
+
throw new Error("Wallet not connected");
|
|
3533
|
+
}
|
|
3534
|
+
if (chain.id !== chainId) {
|
|
3535
|
+
throw new Error(`Switch to ${getChainName(chainId)} to sign`);
|
|
3536
|
+
}
|
|
3537
|
+
const isOwner = await publicClient.readContract({
|
|
3538
|
+
address: safeAddress,
|
|
3539
|
+
abi: SAFE_ABI,
|
|
3540
|
+
functionName: "isOwner",
|
|
3541
|
+
args: [account.address]
|
|
3542
|
+
});
|
|
3543
|
+
if (!isOwner) {
|
|
3544
|
+
throw new Error("Connected wallet is not a Safe owner");
|
|
3545
|
+
}
|
|
3546
|
+
const data = encodeFunctionData({
|
|
3547
|
+
abi: erc20Abi,
|
|
3548
|
+
functionName: "transfer",
|
|
3549
|
+
args: [recipient, amount]
|
|
3550
|
+
});
|
|
3551
|
+
const safeTx = {
|
|
3552
|
+
to: tokenAddress,
|
|
3553
|
+
value: 0n,
|
|
3554
|
+
data,
|
|
3555
|
+
operation: 0,
|
|
3556
|
+
safeTxGas: 0n,
|
|
3557
|
+
baseGas: 0n,
|
|
3558
|
+
gasPrice: 0n,
|
|
3559
|
+
gasToken: zeroAddress,
|
|
3560
|
+
refundReceiver: zeroAddress
|
|
3561
|
+
};
|
|
3562
|
+
const signature = concat([
|
|
3563
|
+
pad(account.address, { size: 32 }),
|
|
3564
|
+
pad(toHex(0), { size: 32 }),
|
|
3565
|
+
toHex(1, { size: 1 })
|
|
3566
|
+
]);
|
|
3567
|
+
const txHash = await walletClient.writeContract({
|
|
3568
|
+
account,
|
|
3569
|
+
chain,
|
|
3570
|
+
address: safeAddress,
|
|
3571
|
+
abi: SAFE_ABI,
|
|
3572
|
+
functionName: "execTransaction",
|
|
3573
|
+
args: [
|
|
3574
|
+
safeTx.to,
|
|
3575
|
+
safeTx.value,
|
|
3576
|
+
safeTx.data,
|
|
3577
|
+
safeTx.operation,
|
|
3578
|
+
safeTx.safeTxGas,
|
|
3579
|
+
safeTx.baseGas,
|
|
3580
|
+
safeTx.gasPrice,
|
|
3581
|
+
safeTx.gasToken,
|
|
3582
|
+
safeTx.refundReceiver,
|
|
3583
|
+
signature
|
|
3584
|
+
]
|
|
3585
|
+
});
|
|
3586
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3587
|
+
hash: txHash
|
|
3588
|
+
});
|
|
3589
|
+
const safeLogs = receipt.logs.filter(
|
|
3590
|
+
(log) => log.address.toLowerCase() === safeAddress.toLowerCase()
|
|
3591
|
+
);
|
|
3592
|
+
const parsed = parseEventLogs({
|
|
3593
|
+
abi: SAFE_ABI,
|
|
3594
|
+
logs: safeLogs,
|
|
3595
|
+
strict: false
|
|
3596
|
+
});
|
|
3597
|
+
const failed = parsed.find((log) => log.eventName === "ExecutionFailure");
|
|
3598
|
+
if (failed) {
|
|
3599
|
+
throw new Error("Safe transaction failed");
|
|
3600
|
+
}
|
|
3601
|
+
const succeeded = parsed.find((log) => log.eventName === "ExecutionSuccess");
|
|
3602
|
+
if (!succeeded) {
|
|
3603
|
+
throw new Error("Safe transaction status unavailable");
|
|
3604
|
+
}
|
|
3605
|
+
return { txHash };
|
|
3606
|
+
}
|
|
3607
|
+
var SAFE_TX_TYPES = {
|
|
3608
|
+
SafeTx: [
|
|
3609
|
+
{ name: "to", type: "address" },
|
|
3610
|
+
{ name: "value", type: "uint256" },
|
|
3611
|
+
{ name: "data", type: "bytes" },
|
|
3612
|
+
{ name: "operation", type: "uint8" },
|
|
3613
|
+
{ name: "safeTxGas", type: "uint256" },
|
|
3614
|
+
{ name: "baseGas", type: "uint256" },
|
|
3615
|
+
{ name: "gasPrice", type: "uint256" },
|
|
3616
|
+
{ name: "gasToken", type: "address" },
|
|
3617
|
+
{ name: "refundReceiver", type: "address" },
|
|
3618
|
+
{ name: "nonce", type: "uint256" }
|
|
3619
|
+
]
|
|
3620
|
+
};
|
|
3621
|
+
async function buildSafeTransaction(params) {
|
|
3622
|
+
const { publicClient, safeAddress, to, value, data, chainId } = params;
|
|
3623
|
+
const nonce = await publicClient.readContract({
|
|
3624
|
+
address: safeAddress,
|
|
3625
|
+
abi: SAFE_ABI,
|
|
3626
|
+
functionName: "nonce"
|
|
3627
|
+
});
|
|
3628
|
+
const message = {
|
|
3629
|
+
to,
|
|
3630
|
+
value,
|
|
3631
|
+
data,
|
|
3632
|
+
operation: 0,
|
|
3633
|
+
safeTxGas: 0n,
|
|
3634
|
+
baseGas: 0n,
|
|
3635
|
+
gasPrice: 0n,
|
|
3636
|
+
gasToken: zeroAddress,
|
|
3637
|
+
refundReceiver: zeroAddress,
|
|
3638
|
+
nonce
|
|
3639
|
+
};
|
|
3640
|
+
const safeTxHash = hashTypedData({
|
|
3641
|
+
domain: { chainId, verifyingContract: safeAddress },
|
|
3642
|
+
types: SAFE_TX_TYPES,
|
|
3643
|
+
primaryType: "SafeTx",
|
|
3644
|
+
message
|
|
3645
|
+
});
|
|
3646
|
+
return {
|
|
3647
|
+
chainId,
|
|
3648
|
+
safeAddress,
|
|
3649
|
+
safeTxHash,
|
|
3650
|
+
typedData: {
|
|
3651
|
+
domain: { chainId, verifyingContract: safeAddress },
|
|
3652
|
+
types: SAFE_TX_TYPES,
|
|
3653
|
+
primaryType: "SafeTx",
|
|
3654
|
+
message
|
|
3655
|
+
}
|
|
3656
|
+
};
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
// src/core/session-owner.ts
|
|
3660
|
+
import { isAddress } from "viem";
|
|
3661
|
+
import {
|
|
3662
|
+
generatePrivateKey,
|
|
3663
|
+
privateKeyToAccount
|
|
3664
|
+
} from "viem/accounts";
|
|
3665
|
+
var STORAGE_PREFIX = "rhinestone:session-owner";
|
|
3666
|
+
function storageKey(eoaAddress) {
|
|
3667
|
+
return `${STORAGE_PREFIX}:${eoaAddress.toLowerCase()}`;
|
|
3668
|
+
}
|
|
3669
|
+
function loadSessionOwnerFromStorage(eoaAddress) {
|
|
3670
|
+
if (typeof window === "undefined") return null;
|
|
3671
|
+
const raw = window.localStorage.getItem(storageKey(eoaAddress));
|
|
3672
|
+
if (!raw) return null;
|
|
3673
|
+
try {
|
|
3674
|
+
const parsed = JSON.parse(raw);
|
|
3675
|
+
if (!parsed.privateKey) return null;
|
|
3676
|
+
const account = privateKeyToAccount(parsed.privateKey);
|
|
3677
|
+
return {
|
|
3678
|
+
privateKey: parsed.privateKey,
|
|
3679
|
+
address: account.address
|
|
3680
|
+
};
|
|
3681
|
+
} catch {
|
|
3682
|
+
return null;
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
function saveSessionOwnerToStorage(eoaAddress, privateKey, address) {
|
|
3686
|
+
if (typeof window === "undefined") return;
|
|
3687
|
+
if (!isAddress(address)) return;
|
|
3688
|
+
const payload = {
|
|
3689
|
+
privateKey,
|
|
3690
|
+
address,
|
|
3691
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3692
|
+
};
|
|
3693
|
+
window.localStorage.setItem(storageKey(eoaAddress), JSON.stringify(payload));
|
|
3694
|
+
}
|
|
3695
|
+
function createSessionOwnerKey() {
|
|
3696
|
+
const privateKey = generatePrivateKey();
|
|
3697
|
+
const account = privateKeyToAccount(privateKey);
|
|
3698
|
+
return {
|
|
3699
|
+
privateKey,
|
|
3700
|
+
account,
|
|
3701
|
+
address: account.address
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3704
|
+
function accountFromPrivateKey(privateKey) {
|
|
3705
|
+
return privateKeyToAccount(privateKey);
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
export {
|
|
3709
|
+
Modal,
|
|
3710
|
+
WalletIcon,
|
|
3711
|
+
ExternalLinkIcon,
|
|
3712
|
+
CheckIcon,
|
|
3713
|
+
TransferCryptoIcon,
|
|
3714
|
+
ChevronLeftIcon,
|
|
3715
|
+
ChevronDownIcon,
|
|
3716
|
+
CloseIcon,
|
|
3717
|
+
HandCoinsIcon,
|
|
3718
|
+
HistoryIcon,
|
|
3719
|
+
InfoIcon,
|
|
3720
|
+
CopyIcon,
|
|
3721
|
+
ArrowUpRightIcon,
|
|
3722
|
+
AlertTriangleIcon,
|
|
3723
|
+
PercentIcon,
|
|
3724
|
+
ClockIcon,
|
|
3725
|
+
PlusCircleIcon,
|
|
3726
|
+
CircleArrowOutUpLeftIcon,
|
|
3727
|
+
BankIcon,
|
|
3728
|
+
Callout,
|
|
3729
|
+
BodyHeader,
|
|
3730
|
+
PoweredBy,
|
|
3731
|
+
Spinner,
|
|
3732
|
+
ConnectStep,
|
|
3733
|
+
useLatestRef,
|
|
3734
|
+
Button,
|
|
3735
|
+
debugLog,
|
|
3736
|
+
debugError,
|
|
3737
|
+
getAssetId,
|
|
3738
|
+
portfolioToAssets,
|
|
3739
|
+
isNativeAsset,
|
|
3740
|
+
buildSessionDetails,
|
|
3741
|
+
createDepositService,
|
|
3742
|
+
currencyFormatter,
|
|
3743
|
+
tokenFormatter,
|
|
3744
|
+
isUnsupportedChainSwitchError,
|
|
3745
|
+
formatUserError,
|
|
3746
|
+
Tooltip,
|
|
3747
|
+
getEventTxHash,
|
|
3748
|
+
getEventSourceDetails,
|
|
3749
|
+
isDepositEvent,
|
|
3750
|
+
isFailedEvent,
|
|
3751
|
+
txRefsMatch,
|
|
3752
|
+
failureMessageForEvent,
|
|
3753
|
+
ProcessingStep,
|
|
3754
|
+
SAFE_ABI,
|
|
3755
|
+
executeSafeEthTransfer,
|
|
3756
|
+
executeSafeErc20Transfer,
|
|
3757
|
+
buildSafeTransaction,
|
|
3758
|
+
getPublicClient,
|
|
3759
|
+
getHyperEvmReadClient,
|
|
3760
|
+
loadSessionOwnerFromStorage,
|
|
3761
|
+
saveSessionOwnerToStorage,
|
|
3762
|
+
createSessionOwnerKey,
|
|
3763
|
+
accountFromPrivateKey,
|
|
3764
|
+
applyTheme
|
|
3765
|
+
};
|