@rhinestone/deposit-modal 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +12 -12
  2. package/bridge-transcript.json +14 -0
  3. package/dist/ClaimModal-GFDBVCN5.cjs +11 -0
  4. package/dist/ClaimModal-VQS3AKEL.mjs +11 -0
  5. package/dist/{DepositModalReown-Q2JMLT47.mjs → DepositModalReown-KOJ7VEY7.mjs} +30 -29
  6. package/dist/{DepositModalReown-46LPVFB6.cjs → DepositModalReown-R37SSHUF.cjs} +79 -78
  7. package/dist/WithdrawModal-QVXHMH43.mjs +12 -0
  8. package/dist/WithdrawModal-RY2NMFLE.cjs +12 -0
  9. package/dist/{caip-D8aRWdEY.d.cts → caip-BMi-9t09.d.cts} +27 -3
  10. package/dist/{caip-D8aRWdEY.d.ts → caip-BMi-9t09.d.ts} +27 -3
  11. package/dist/{chunk-V2OBGLJD.cjs → chunk-3OOY67VJ.cjs} +1103 -253
  12. package/dist/{chunk-6KG642KH.cjs → chunk-5P2PVLS4.cjs} +1 -1
  13. package/dist/{chunk-IN77XSWO.mjs → chunk-5VONFVQN.mjs} +976 -126
  14. package/dist/chunk-6HUHVXCY.cjs +704 -0
  15. package/dist/{chunk-PKML3XVS.mjs → chunk-7UQ6OUEE.mjs} +166 -45
  16. package/dist/{chunk-K4H6TGBE.mjs → chunk-CXBMIZUF.mjs} +1 -1
  17. package/dist/{chunk-MTFJSE52.cjs → chunk-DTVZUBHI.cjs} +236 -115
  18. package/dist/{chunk-VUATPOWJ.mjs → chunk-EAGMY3QE.mjs} +74 -17
  19. package/dist/{chunk-RN5VKWI6.mjs → chunk-FTWKAJGN.mjs} +1 -1
  20. package/dist/{chunk-KTUVSKR4.mjs → chunk-IKUYZA7A.mjs} +265 -523
  21. package/dist/{chunk-DW2EQA2U.cjs → chunk-LVCD6RHF.cjs} +2 -2
  22. package/dist/{chunk-4KCSH4T3.cjs → chunk-OY5N3E6L.cjs} +86 -29
  23. package/dist/chunk-QJ7RCCU7.mjs +704 -0
  24. package/dist/chunk-UCBUCX2W.mjs +3169 -0
  25. package/dist/{chunk-YCQZJQO6.cjs → chunk-VPDCGD2D.cjs} +622 -880
  26. package/dist/{chunk-7ICSHJFE.cjs → chunk-X4D52IZK.cjs} +1501 -176
  27. package/dist/claim.cjs +5 -7
  28. package/dist/claim.d.cts +3 -3
  29. package/dist/claim.d.ts +3 -3
  30. package/dist/claim.mjs +4 -6
  31. package/dist/{constants-DBrWhJWE.d.cts → constants-BT4ctuHo.d.cts} +7 -6
  32. package/dist/{constants-DBrWhJWE.d.ts → constants-D21ZdNOP.d.ts} +7 -6
  33. package/dist/constants.cjs +3 -3
  34. package/dist/constants.d.cts +3 -3
  35. package/dist/constants.d.ts +3 -3
  36. package/dist/constants.mjs +2 -2
  37. package/dist/deposit.cjs +7 -8
  38. package/dist/deposit.d.cts +3 -3
  39. package/dist/deposit.d.ts +3 -3
  40. package/dist/deposit.mjs +6 -7
  41. package/dist/embed.cjs +135 -44
  42. package/dist/embed.d.cts +2 -2
  43. package/dist/embed.d.ts +2 -2
  44. package/dist/embed.mjs +116 -25
  45. package/dist/index.cjs +13 -11
  46. package/dist/index.d.cts +3 -3
  47. package/dist/index.d.ts +3 -3
  48. package/dist/index.mjs +12 -10
  49. package/dist/polymarket.cjs +6 -6
  50. package/dist/polymarket.mjs +2 -2
  51. package/dist/styles.css +132 -100
  52. package/dist/{types-Bho4OA03.d.ts → types-CQ0NHqCy.d.ts} +146 -11
  53. package/dist/{types-DP_3KwlD.d.cts → types-CxiLX5sv.d.cts} +146 -11
  54. package/dist/withdraw.cjs +6 -6
  55. package/dist/withdraw.d.cts +4 -4
  56. package/dist/withdraw.d.ts +4 -4
  57. package/dist/withdraw.mjs +5 -5
  58. package/package.json +7 -6
  59. package/dist/ClaimModal-IKTJU43Y.cjs +0 -13
  60. package/dist/ClaimModal-SEKNWRMA.mjs +0 -13
  61. package/dist/WithdrawModal-5ICGD4HJ.mjs +0 -12
  62. package/dist/WithdrawModal-VP6PMIRO.cjs +0 -12
  63. package/dist/chunk-7SR7ENVJ.mjs +0 -1844
  64. package/dist/chunk-CFKC77SV.mjs +0 -1062
  65. package/dist/chunk-LZFRL2AL.cjs +0 -81
  66. package/dist/chunk-WUJIKCFU.mjs +0 -81
  67. package/dist/chunk-X2IPAPXU.cjs +0 -163
  68. package/dist/chunk-YWMWI7PZ.cjs +0 -1062
  69. package/dist/chunk-ZEO6ADM3.mjs +0 -163
@@ -1,1844 +0,0 @@
1
- import {
2
- BodyHeader,
3
- Button,
4
- Callout,
5
- CdnIcon,
6
- CheckIcon,
7
- ChevronDownIcon,
8
- ExternalLinkIcon,
9
- InfoIcon,
10
- PoweredBy,
11
- TokenIcon,
12
- WalletIcon,
13
- debugError,
14
- debugLog,
15
- estimateReceiveAmount,
16
- formatFeeUsd,
17
- formatQuotedReceive,
18
- formatRawTokenAmount,
19
- formatTokenAmount,
20
- isRecognizedToken,
21
- quoteFeeGroups,
22
- quoteFeeRow,
23
- resolveTokenDisplay,
24
- tokenAmountToNumber,
25
- useExplorerTxUrl,
26
- useLatestRef
27
- } from "./chunk-IN77XSWO.mjs";
28
- import {
29
- NATIVE_TOKEN_ADDRESS,
30
- SOLANA_MAINNET_CAIP2,
31
- cdnIconUrl,
32
- getChainIcon,
33
- getChainName,
34
- targetChainToCaip2,
35
- targetChainToChainId,
36
- toEvmCaip2
37
- } from "./chunk-VUATPOWJ.mjs";
38
-
39
- // src/core/removed-props.ts
40
- var MANAGED_ACCOUNT_REMOVALS = {
41
- signerAddress: "removed \u2014 accounts are service-managed, so there is no session signer. See MIGRATION.md.",
42
- sessionChainIds: "removed \u2014 accounts are service-managed, so there is no smart session. See MIGRATION.md.",
43
- dappAddress: "removed \u2014 the modal reads the address off `walletClient.account` instead, which cannot disagree with the wallet your app has connected.",
44
- dappWalletClient: "renamed to `walletClient`.",
45
- dappPublicClient: "renamed to `publicClient`.",
46
- allowedRoutes: "removed \u2014 which chains and tokens you accept is the project's `depositWhitelist`, enforced by the processor. Offering a subset means a separate API key.",
47
- rhinestoneApiKey: "removed \u2014 it was never read. The API key belongs on your backend proxy, which attaches it to the upstream request; the modal runs in the browser."
48
- };
49
- var REMOVED_PROPS = {
50
- DepositModal: {
51
- ...MANAGED_ACCOUNT_REMOVALS,
52
- dappImports: "renamed to `assetMigrations`.",
53
- initialDappImport: "renamed to `initialAssetMigration`.",
54
- fiatOnrampMethods: "replaced by `fiatMethods`, a boolean map keyed by Swapped payment group (`creditcard`, `bank-transfer`, `apple-pay`). Omit it to offer all of them.",
55
- enableSolana: "removed \u2014 Solana sources follow the project's `depositWhitelist`."
56
- },
57
- WithdrawModal: {
58
- ...MANAGED_ACCOUNT_REMOVALS,
59
- safeAddress: "renamed to `accountAddress`, and it need not be a Safe.",
60
- onSignTransaction: "replaced by `onSendTransaction`, which performs one transfer and returns its hash. The modal no longer submits the transaction itself.",
61
- reownAppId: "removed \u2014 WithdrawModal never connects a wallet; your app performs the transfer via `onSendTransaction`.",
62
- onRequestConnect: "removed \u2014 WithdrawModal never connects a wallet."
63
- }
64
- };
65
- var REMOVED_UI_CONFIG_KEYS = {
66
- checkLiquidity: "removed \u2014 the modal no longer surfaces a liquidity cap. Route capacity is enforced when the deposit is processed.",
67
- showHistoryButton: "removed \u2014 deposit history is always offered, from the modal's home screen. There is no way to hide it."
68
- };
69
- var warned = /* @__PURE__ */ new Set();
70
- function warnOnce(component, key, replacement) {
71
- const id = `${component}.${key}`;
72
- if (warned.has(id)) return;
73
- warned.add(id);
74
- console.error(
75
- `[rhinestone] <${component}> was passed \`${key}\`, which no longer exists: ${replacement}`
76
- );
77
- }
78
- function warnRemovedProps(component, props) {
79
- const table = REMOVED_PROPS[component];
80
- if (!table) return;
81
- for (const key of Object.keys(table)) {
82
- if (Object.prototype.hasOwnProperty.call(props, key)) {
83
- warnOnce(component, key, table[key]);
84
- }
85
- }
86
- const uiConfig = props.uiConfig;
87
- if (uiConfig && typeof uiConfig === "object") {
88
- for (const key of Object.keys(REMOVED_UI_CONFIG_KEYS)) {
89
- if (Object.prototype.hasOwnProperty.call(uiConfig, key)) {
90
- warnOnce(
91
- component,
92
- `uiConfig.${key}`,
93
- REMOVED_UI_CONFIG_KEYS[key]
94
- );
95
- }
96
- }
97
- }
98
- }
99
-
100
- // src/core/analytics-target.ts
101
- var HEX_TOKEN = /^0x(?:[0-9a-f]{32}|[0-9a-f]{40})$/i;
102
- var BASE58_TOKEN = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
103
- function analyticsTargetProperties(input) {
104
- const properties = {};
105
- const chain = resolveChain(input.targetChain);
106
- const token = resolveToken(input.targetToken);
107
- if (chain) properties.target_chain = chain;
108
- if (token) properties.target_token = token;
109
- return properties;
110
- }
111
- function resolveChain(targetChain) {
112
- if (targetChain === void 0) return void 0;
113
- if (typeof targetChain === "number" && !(targetChain > 0 && Number.isInteger(targetChain))) {
114
- return void 0;
115
- }
116
- return targetChainToCaip2(targetChain);
117
- }
118
- function resolveToken(targetToken) {
119
- const raw = targetToken?.trim();
120
- if (!raw) return void 0;
121
- if (HEX_TOKEN.test(raw)) return raw.toLowerCase();
122
- if (BASE58_TOKEN.test(raw)) return raw;
123
- return raw.toLowerCase() === "native" ? "native" : void 0;
124
- }
125
-
126
- // src/core/submission-uncertainty.ts
127
- var MARKER = "__rhinestoneSubmissionUncertain";
128
- function markSubmissionUncertain(error) {
129
- Object.defineProperty(error, MARKER, {
130
- value: true,
131
- enumerable: false
132
- });
133
- return error;
134
- }
135
- function isSubmissionUncertain(error) {
136
- let current = error;
137
- for (let depth = 0; depth < 10; depth += 1) {
138
- if (typeof current !== "object" || current === null) return false;
139
- if (current[MARKER] === true) return true;
140
- current = current.cause;
141
- }
142
- return false;
143
- }
144
-
145
- // src/components/steps/ProcessingStep.tsx
146
- import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
147
-
148
- // src/components/ui/Tooltip.tsx
149
- import {
150
- useState,
151
- useRef,
152
- useEffect,
153
- useCallback
154
- } from "react";
155
- import { createPortal } from "react-dom";
156
- import { jsx, jsxs } from "react/jsx-runtime";
157
- function Tooltip({ content, children, className }) {
158
- const [open, setOpen] = useState(false);
159
- const [position, setPosition] = useState(null);
160
- const triggerRef = useRef(null);
161
- const bubbleRef = useRef(null);
162
- const updatePosition = useCallback(() => {
163
- const trigger = triggerRef.current;
164
- if (!trigger) return;
165
- const rect = trigger.getBoundingClientRect();
166
- setPosition({
167
- top: rect.top,
168
- left: rect.left + rect.width / 2
169
- });
170
- }, []);
171
- useEffect(() => {
172
- if (!open) return;
173
- updatePosition();
174
- function handleOutside(event) {
175
- const target = event.target;
176
- if (!target) return;
177
- if (triggerRef.current?.contains(target)) return;
178
- if (bubbleRef.current?.contains(target)) return;
179
- setOpen(false);
180
- }
181
- function handleKey(event) {
182
- if (event.key === "Escape") setOpen(false);
183
- }
184
- document.addEventListener("mousedown", handleOutside);
185
- document.addEventListener("touchstart", handleOutside, { passive: true });
186
- document.addEventListener("keydown", handleKey);
187
- window.addEventListener("scroll", updatePosition, true);
188
- window.addEventListener("resize", updatePosition);
189
- return () => {
190
- document.removeEventListener("mousedown", handleOutside);
191
- document.removeEventListener("touchstart", handleOutside);
192
- document.removeEventListener("keydown", handleKey);
193
- window.removeEventListener("scroll", updatePosition, true);
194
- window.removeEventListener("resize", updatePosition);
195
- };
196
- }, [open, updatePosition]);
197
- return /* @__PURE__ */ jsxs(
198
- "span",
199
- {
200
- ref: triggerRef,
201
- className: `rs-tooltip ${className ?? ""}`,
202
- onMouseEnter: () => setOpen(true),
203
- onMouseLeave: () => setOpen(false),
204
- children: [
205
- /* @__PURE__ */ jsx(
206
- "span",
207
- {
208
- className: "rs-tooltip-trigger",
209
- role: "button",
210
- tabIndex: 0,
211
- "aria-label": content,
212
- onClick: (event) => {
213
- event.stopPropagation();
214
- event.preventDefault();
215
- setOpen((value) => !value);
216
- },
217
- onKeyDown: (event) => {
218
- if (event.key === "Enter" || event.key === " ") {
219
- event.preventDefault();
220
- setOpen((value) => !value);
221
- }
222
- },
223
- children
224
- }
225
- ),
226
- open && position && typeof document !== "undefined" && createPortal(
227
- /* @__PURE__ */ jsx(
228
- "span",
229
- {
230
- ref: bubbleRef,
231
- className: "rs-tooltip-bubble",
232
- role: "tooltip",
233
- style: { top: position.top, left: position.left },
234
- children: content
235
- }
236
- ),
237
- document.body
238
- )
239
- ]
240
- }
241
- );
242
- }
243
- Tooltip.displayName = "Tooltip";
244
-
245
- // src/components/ui/FeesAccordion.tsx
246
- import { useState as useState2 } from "react";
247
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
248
- function FeesAccordion({ total, rows, tooltip }) {
249
- const [open, setOpen] = useState2(false);
250
- const expandable = rows.length > 0;
251
- return /* @__PURE__ */ jsxs2("div", { className: "rs-fees-accordion", children: [
252
- /* @__PURE__ */ jsxs2("div", { className: "rs-fees-accordion-summary", children: [
253
- /* @__PURE__ */ jsxs2("span", { className: "rs-fees-accordion-label", children: [
254
- /* @__PURE__ */ jsx2("span", { children: "Fees" }),
255
- tooltip && /* @__PURE__ */ jsx2(Tooltip, { content: tooltip, children: /* @__PURE__ */ jsx2("span", { className: "rs-review-detail-info", "aria-label": "Fees info", children: /* @__PURE__ */ jsx2(InfoIcon, {}) }) })
256
- ] }),
257
- /* @__PURE__ */ jsxs2(
258
- "button",
259
- {
260
- type: "button",
261
- className: "rs-fees-accordion-toggle",
262
- "aria-label": "Fees",
263
- "aria-expanded": expandable ? open : void 0,
264
- disabled: !expandable,
265
- onClick: () => expandable && setOpen((o) => !o),
266
- children: [
267
- /* @__PURE__ */ jsx2("span", { children: total }),
268
- expandable && /* @__PURE__ */ jsx2(
269
- "span",
270
- {
271
- className: `rs-fees-accordion-chevron${open ? " rs-fees-accordion-chevron--open" : ""}`,
272
- "aria-hidden": "true",
273
- children: /* @__PURE__ */ jsx2(ChevronDownIcon, {})
274
- }
275
- )
276
- ]
277
- }
278
- )
279
- ] }),
280
- expandable && open && /* @__PURE__ */ jsx2("div", { className: "rs-fees-accordion-rows", children: rows.map((row, i) => /* @__PURE__ */ jsxs2("div", { className: "rs-fees-accordion-row", children: [
281
- /* @__PURE__ */ jsx2("span", { children: row.label }),
282
- /* @__PURE__ */ jsxs2("span", { className: "rs-review-detail-value", children: [
283
- /* @__PURE__ */ jsx2(
284
- "span",
285
- {
286
- className: row.strike ? "rs-review-detail-value--strike" : void 0,
287
- children: row.value
288
- }
289
- ),
290
- row.tooltip && /* @__PURE__ */ jsx2(Tooltip, { content: row.tooltip, children: /* @__PURE__ */ jsx2(
291
- "span",
292
- {
293
- className: "rs-review-detail-info",
294
- "aria-label": `${row.label} info`,
295
- children: /* @__PURE__ */ jsx2(InfoIcon, {})
296
- }
297
- ) })
298
- ] })
299
- ] }, `${row.label}-${i}`)) })
300
- ] });
301
- }
302
- FeesAccordion.displayName = "FeesAccordion";
303
-
304
- // src/components/steps/SwappedReceipt.tsx
305
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
306
- function SwappedReceipt({
307
- onrampMethod,
308
- onrampMethodIcon,
309
- amountPaid,
310
- depositedAmount,
311
- depositedSymbol,
312
- feesTotal,
313
- feeRows,
314
- feesTooltip,
315
- newLabel,
316
- onNewDeposit,
317
- onClose
318
- }) {
319
- return /* @__PURE__ */ jsxs3("div", { className: "rs-screen", children: [
320
- /* @__PURE__ */ jsxs3("div", { className: "rs-screen-body rs-screen-body--gap-32", children: [
321
- /* @__PURE__ */ jsx3(BodyHeader, { icon: /* @__PURE__ */ jsx3(CheckIcon, {}), title: "Deposit successful" }),
322
- /* @__PURE__ */ jsxs3("div", { className: "rs-review-details", children: [
323
- onrampMethod && /* @__PURE__ */ jsxs3("div", { className: "rs-review-detail-row", children: [
324
- /* @__PURE__ */ jsx3("span", { children: "Onramp method" }),
325
- /* @__PURE__ */ jsxs3("span", { className: "rs-review-detail-value", children: [
326
- /* @__PURE__ */ jsx3("span", { children: onrampMethod }),
327
- onrampMethodIcon && /* @__PURE__ */ jsx3("span", { className: "rs-review-detail-icon rs-review-detail-icon--square", children: /* @__PURE__ */ jsx3(CdnIcon, { src: onrampMethodIcon }) })
328
- ] })
329
- ] }),
330
- amountPaid && /* @__PURE__ */ jsxs3("div", { className: "rs-review-detail-row", children: [
331
- /* @__PURE__ */ jsx3("span", { children: "Amount paid" }),
332
- /* @__PURE__ */ jsx3("span", { className: "rs-review-detail-value", children: amountPaid })
333
- ] }),
334
- /* @__PURE__ */ jsxs3("div", { className: "rs-review-detail-row", children: [
335
- /* @__PURE__ */ jsx3("span", { children: "Deposited amount" }),
336
- /* @__PURE__ */ jsxs3("span", { className: "rs-review-detail-value", children: [
337
- /* @__PURE__ */ jsx3("span", { children: depositedAmount }),
338
- depositedSymbol && /* @__PURE__ */ jsx3("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx3(TokenIcon, { symbol: depositedSymbol }) })
339
- ] })
340
- ] }),
341
- feesTotal != null && /* @__PURE__ */ jsx3(
342
- FeesAccordion,
343
- {
344
- total: feesTotal,
345
- rows: feeRows ?? [],
346
- tooltip: feesTooltip ?? void 0
347
- }
348
- )
349
- ] }),
350
- /* @__PURE__ */ jsxs3("div", { className: "rs-screen-button-row", children: [
351
- onNewDeposit && /* @__PURE__ */ jsx3(Button, { variant: "outline", onClick: onNewDeposit, fullWidth: true, children: newLabel }),
352
- onClose && /* @__PURE__ */ jsx3(Button, { onClick: onClose, fullWidth: true, children: "Done" })
353
- ] })
354
- ] }),
355
- /* @__PURE__ */ jsx3(PoweredBy, {})
356
- ] });
357
- }
358
- SwappedReceipt.displayName = "SwappedReceipt";
359
-
360
- // src/components/ui/ExchangeLogos.tsx
361
- import { jsx as jsx4 } from "react/jsx-runtime";
362
- var EXCHANGE_SLUGS = [
363
- "binance",
364
- "bitfinex",
365
- "btcturk",
366
- "bybit",
367
- "coinbase",
368
- "gateio",
369
- "htx",
370
- "kraken",
371
- "kucoin"
372
- ];
373
- var EXCHANGE_LOGO_SOURCES = Object.fromEntries(
374
- EXCHANGE_SLUGS.map((slug) => [slug, cdnIconUrl("exchanges", slug)])
375
- );
376
- var EXCHANGE_LOGO_ALIASES = {
377
- gate: "gateio"
378
- };
379
- function normalize(value) {
380
- return value.toLowerCase().replace(/[^a-z0-9]/g, "");
381
- }
382
- function getLogoSource(value) {
383
- const key = normalize(value);
384
- return EXCHANGE_LOGO_SOURCES[EXCHANGE_LOGO_ALIASES[key] ?? key] ?? null;
385
- }
386
- function getExchangeLogoSrc(name, connection) {
387
- return getLogoSource(name) ?? getLogoSource(connection ?? "");
388
- }
389
- function getExchangeLogo(name, connection) {
390
- const src = getExchangeLogoSrc(name, connection);
391
- return src ? /* @__PURE__ */ jsx4(CdnIcon, { src }) : null;
392
- }
393
-
394
- // src/core/webhook.ts
395
- function isRecord(value) {
396
- return typeof value === "object" && value !== null;
397
- }
398
- function asString(value) {
399
- return typeof value === "string" ? value : void 0;
400
- }
401
- function asNumber(value) {
402
- if (typeof value === "number" && Number.isFinite(value)) return value;
403
- if (typeof value !== "string") return void 0;
404
- const trimmed = value.trim();
405
- if (!trimmed) return void 0;
406
- const caipMatch = trimmed.match(/^eip155:(\d+)$/);
407
- if (caipMatch?.[1]) {
408
- const parsed2 = Number(caipMatch[1]);
409
- return Number.isFinite(parsed2) ? parsed2 : void 0;
410
- }
411
- const parsed = Number(trimmed);
412
- return Number.isFinite(parsed) ? parsed : void 0;
413
- }
414
- function asAmount(value) {
415
- if (typeof value === "string") return value;
416
- if (typeof value === "number" && Number.isFinite(value)) {
417
- return value.toString();
418
- }
419
- return void 0;
420
- }
421
- function asAddress(value) {
422
- if (typeof value !== "string") return void 0;
423
- return /^0x[a-fA-F0-9]{40}$/.test(value) ? value : void 0;
424
- }
425
- function getEventTxHash(event) {
426
- if (!event?.type) return void 0;
427
- if (event.type === "deposit-received") {
428
- return asString(event.data?.transactionHash);
429
- }
430
- if (event.type === "bridge-started" || event.type === "bridge-complete") {
431
- const deposit = isRecord(event.data?.deposit) ? event.data.deposit : void 0;
432
- const source = isRecord(event.data?.source) ? event.data.source : void 0;
433
- return asString(deposit?.transactionHash) ?? asString(source?.transactionHash);
434
- }
435
- if (event.type === "bridge-failed" || event.type === "error") {
436
- const deposit = isRecord(event.data?.deposit) ? event.data.deposit : void 0;
437
- const source = isRecord(event.data?.source) ? event.data.source : void 0;
438
- return asString(deposit?.transactionHash) ?? asString(source?.transactionHash);
439
- }
440
- return void 0;
441
- }
442
- function getEventSourceDetails(event) {
443
- if (!event?.type || !isRecord(event.data)) return {};
444
- if (event.type === "deposit-received") {
445
- return {
446
- chainId: asNumber(event.data.chain),
447
- amount: asAmount(event.data.amount),
448
- token: asAddress(event.data.token)
449
- };
450
- }
451
- const source = isRecord(event.data.source) ? event.data.source : void 0;
452
- const deposit = isRecord(event.data.deposit) ? event.data.deposit : void 0;
453
- if (event.type === "bridge-started" || event.type === "bridge-complete" || event.type === "bridge-failed" || event.type === "error") {
454
- return {
455
- chainId: asNumber(source?.chain) ?? asNumber(deposit?.chain),
456
- amount: asAmount(source?.amount) ?? asAmount(deposit?.amount),
457
- token: asAddress(source?.asset) ?? asAddress(deposit?.asset) ?? asAddress(deposit?.token)
458
- };
459
- }
460
- return {};
461
- }
462
- function asChain(value) {
463
- if (typeof value === "string") {
464
- const trimmed = value.trim();
465
- if (trimmed.startsWith("solana")) return "solana";
466
- if (trimmed.startsWith("hypercore")) return "hypercore";
467
- }
468
- return asNumber(value);
469
- }
470
- function getEventDestinationDetails(event) {
471
- if (!event?.type || !isRecord(event.data)) return {};
472
- if (event.type !== "bridge-started" && event.type !== "bridge-complete") {
473
- return {};
474
- }
475
- const destination = isRecord(event.data.destination) ? event.data.destination : void 0;
476
- if (!destination) return {};
477
- const token = asString(destination.asset);
478
- return {
479
- chainId: asChain(destination.chain),
480
- amount: asAmount(destination.amount),
481
- token: token?.trim() || void 0
482
- };
483
- }
484
- function isDepositEvent(event) {
485
- return event?.type === "deposit-received" || event?.type === "bridge-started" || event?.type === "bridge-complete" || event?.type === "bridge-failed" || event?.type === "error";
486
- }
487
- function isFailedEvent(event) {
488
- return event?.type === "bridge-failed" || event?.type === "error";
489
- }
490
- function isHexString(value) {
491
- return value.startsWith("0x") || value.startsWith("0X");
492
- }
493
- function txRefsMatch(a, b) {
494
- if (isHexString(a) && isHexString(b)) {
495
- return a.toLowerCase() === b.toLowerCase();
496
- }
497
- return a === b;
498
- }
499
- function formatBridgeFailedMessage(event) {
500
- const eventData = event?.data ?? {};
501
- const code = typeof eventData.errorCode === "string" ? eventData.errorCode : void 0;
502
- const backendMessage = typeof eventData.message === "string" ? eventData.message.trim() : "";
503
- function toUserFacingFailure(raw) {
504
- const lower = raw.toLowerCase();
505
- if (lower.includes("insufficient funds")) {
506
- return "Deposit was received, but processing could not continue due to insufficient funds. Please retry.";
507
- }
508
- if (lower.includes("no valid quote available")) {
509
- return "No bridge route is currently available for this transfer. Please try again shortly.";
510
- }
511
- if (lower.includes("simulation failed")) {
512
- return "Transfer processing failed during simulation. Please retry.";
513
- }
514
- if (raw.length > 220) {
515
- return "Transfer processing failed. Please retry.";
516
- }
517
- return raw;
518
- }
519
- if (backendMessage.length > 0) {
520
- return { message: toUserFacingFailure(backendMessage), code };
521
- }
522
- if (code) {
523
- return { message: `Bridge failed (${code})`, code };
524
- }
525
- return { message: "Bridge failed" };
526
- }
527
- function failureMessageForEvent(event) {
528
- if (event?.type === "error") {
529
- const message = isRecord(event.data) ? asString(event.data.message) : void 0;
530
- return message ?? "Unknown error";
531
- }
532
- return formatBridgeFailedMessage(event).message;
533
- }
534
-
535
- // src/core/useTokenPrices.ts
536
- import { useEffect as useEffect2, useState as useState3 } from "react";
537
- var PRICE_SERVICE_NATIVE = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
538
- var WRAPPED_SOL_MINT = "So11111111111111111111111111111111111111112";
539
- var SOL_SYSTEM_PROGRAM = "11111111111111111111111111111111";
540
- function isSolanaNativeAddress(address) {
541
- if (!address) return true;
542
- const lower = address.toLowerCase();
543
- return lower === "native" || lower === SOL_SYSTEM_PROGRAM;
544
- }
545
- function isNativeAddress(address) {
546
- if (!address) return true;
547
- return address.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
548
- }
549
- function caip2For(token) {
550
- if (token.chainId === "solana") {
551
- const mint = isSolanaNativeAddress(token.address) ? WRAPPED_SOL_MINT : token.address;
552
- return mint ? `${SOLANA_MAINNET_CAIP2}:${mint}` : void 0;
553
- }
554
- if (typeof token.chainId !== "number") return void 0;
555
- const address = isNativeAddress(token.address) ? PRICE_SERVICE_NATIVE : token.address;
556
- return `${toEvmCaip2(token.chainId)}:${address.toLowerCase()}`;
557
- }
558
- async function fetchTokenPriceUsd(service, token) {
559
- const caip2 = caip2For(token);
560
- if (!caip2) return null;
561
- const prices = await service.fetchPricesByAddress([caip2]);
562
- const price = prices[caip2];
563
- return typeof price === "number" && price > 0 ? price : null;
564
- }
565
- function useTokenPrices(service, tokens) {
566
- const [prices, setPrices] = useState3({});
567
- const defined = tokens.filter((t) => Boolean(t?.symbol));
568
- const entries = [
569
- ...new Map(
570
- defined.flatMap((token) => {
571
- const caip2 = caip2For(token);
572
- return caip2 ? [{ symbol: token.symbol.toUpperCase(), caip2 }] : [];
573
- }).map((entry) => [entry.caip2, entry])
574
- ).values()
575
- ].sort((a, b) => a.caip2.localeCompare(b.caip2));
576
- const key = JSON.stringify(entries);
577
- useEffect2(() => {
578
- if (key === "[]") return;
579
- let cancelled = false;
580
- const parsed = JSON.parse(key);
581
- const caip2Ids = [...new Set(parsed.map((e) => e.caip2))];
582
- service.fetchPricesByAddress(caip2Ids).then((byAddress) => {
583
- if (cancelled) return;
584
- const merged = {};
585
- for (const entry of parsed) {
586
- const price = byAddress[entry.caip2];
587
- if (typeof price === "number") merged[entry.symbol] = price;
588
- }
589
- setPrices(merged);
590
- }).catch(() => {
591
- });
592
- return () => {
593
- cancelled = true;
594
- };
595
- }, [service, key]);
596
- return prices;
597
- }
598
-
599
- // src/components/steps/ProcessingStep.tsx
600
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
601
- function SuccessBadge() {
602
- return /* @__PURE__ */ jsxs4(
603
- "svg",
604
- {
605
- width: "40",
606
- height: "40",
607
- viewBox: "0 0 40 40",
608
- fill: "none",
609
- xmlns: "http://www.w3.org/2000/svg",
610
- "aria-hidden": "true",
611
- children: [
612
- /* @__PURE__ */ jsx5(
613
- "rect",
614
- {
615
- width: "40",
616
- height: "40",
617
- rx: "8",
618
- fill: "var(--rs-icon-wrapper-bg)"
619
- }
620
- ),
621
- /* @__PURE__ */ jsx5(
622
- "path",
623
- {
624
- d: "M28 14L17 25L12 20",
625
- stroke: "var(--rs-icon-wrapper-icon)",
626
- strokeWidth: "2",
627
- strokeLinecap: "round",
628
- strokeLinejoin: "round"
629
- }
630
- )
631
- ]
632
- }
633
- );
634
- }
635
- function FailedBadge() {
636
- return /* @__PURE__ */ jsxs4(
637
- "svg",
638
- {
639
- width: "40",
640
- height: "40",
641
- viewBox: "0 0 40 40",
642
- fill: "none",
643
- xmlns: "http://www.w3.org/2000/svg",
644
- "aria-hidden": "true",
645
- children: [
646
- /* @__PURE__ */ jsx5("rect", { width: "40", height: "40", rx: "8", fill: "#FB2C36" }),
647
- /* @__PURE__ */ jsx5(
648
- "path",
649
- {
650
- d: "M26 14L14 26M14 14L26 26",
651
- stroke: "white",
652
- strokeWidth: "2",
653
- strokeLinecap: "round",
654
- strokeLinejoin: "round"
655
- }
656
- )
657
- ]
658
- }
659
- );
660
- }
661
- var INITIAL_POLL_INTERVAL = 3e3;
662
- var MAX_POLL_INTERVAL = 3e4;
663
- var BACKOFF_MULTIPLIER = 1.5;
664
- var SWAPPED_RECEIPT_STATUS_RETRY_INTERVAL_MS = 1e3;
665
- var SWAPPED_FIAT_STATUS_MAX_ATTEMPTS = 12;
666
- var ESCALATED_DELAY_MS = 10 * 60 * 1e3;
667
- var SOFT_DELAY_MS = {
668
- confirming: 90 * 1e3,
669
- received: 90 * 1e3,
670
- bridging: 4 * 60 * 1e3
671
- };
672
- var PHASE_TIMINGS_PREFIX = "rhinestone:phase-timings";
673
- var PAYMENT_METHOD_LABELS = {
674
- creditcard: "Card",
675
- debitcard: "Card",
676
- card: "Card",
677
- "apple-pay": "Apple Pay",
678
- applepay: "Apple Pay",
679
- "google-pay": "Google Pay",
680
- googlepay: "Google Pay",
681
- "bank-transfer": "Bank Transfer",
682
- banktransfer: "Bank Transfer",
683
- sepa: "SEPA",
684
- "open-banking": "Open Banking",
685
- "krak-pay": "Kraken Pay",
686
- krakpay: "Kraken Pay",
687
- kraken: "Kraken",
688
- coinbase: "Coinbase",
689
- binance: "Binance"
690
- };
691
- function formatPaymentMethod(method) {
692
- const key = method.trim().toLowerCase();
693
- if (PAYMENT_METHOD_LABELS[key]) return PAYMENT_METHOD_LABELS[key];
694
- return key.replace(/[-_]+/g, " ").split(" ").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
695
- }
696
- function buildQuoteFeeRows(breakdown, sponsoredTooltip) {
697
- if (!breakdown) return { rows: [], chargeableUsd: 0 };
698
- const rows = [];
699
- let chargeableUsd = 0;
700
- for (const group of quoteFeeGroups(breakdown)) {
701
- if (group.chargedUsd > 0) {
702
- chargeableUsd += group.chargedUsd;
703
- rows.push({ label: group.label, value: formatFeeUsd(group.chargedUsd) });
704
- } else if (group.sponsoredUsd > 0) {
705
- rows.push({
706
- label: group.label,
707
- value: formatFeeUsd(group.sponsoredUsd),
708
- strike: true,
709
- tooltip: sponsoredTooltip
710
- });
711
- }
712
- }
713
- return { rows, chargeableUsd };
714
- }
715
- function toSwappedFiatContext(status) {
716
- return {
717
- orderCrypto: status.orderCrypto,
718
- orderCryptoAmount: status.orderCryptoAmount,
719
- paidAmountUsd: status.paidAmountUsd,
720
- paidAmountEur: status.paidAmountEur,
721
- onrampFeeUsd: status.onrampFeeUsd,
722
- paymentMethod: status.paymentMethod
723
- };
724
- }
725
- function hasFiatReceiptPricing(status) {
726
- return status.paidAmountUsd != null && status.onrampFeeUsd != null;
727
- }
728
- function loadPhaseTimings(txHash) {
729
- if (typeof window === "undefined") return null;
730
- try {
731
- const raw = window.localStorage.getItem(`${PHASE_TIMINGS_PREFIX}:${txHash}`);
732
- if (!raw) return null;
733
- const parsed = JSON.parse(raw);
734
- if (typeof parsed.startedAt !== "number") return null;
735
- return parsed;
736
- } catch {
737
- return null;
738
- }
739
- }
740
- function savePhaseTimings(txHash, timings) {
741
- if (typeof window === "undefined") return;
742
- try {
743
- window.localStorage.setItem(
744
- `${PHASE_TIMINGS_PREFIX}:${txHash}`,
745
- JSON.stringify(timings)
746
- );
747
- } catch {
748
- }
749
- }
750
- function isEventForTx(event, txHash) {
751
- const eventTxHash = getEventTxHash(event);
752
- if (!eventTxHash) return false;
753
- return txRefsMatch(eventTxHash, txHash);
754
- }
755
- function parseWebhookTimestamp(event) {
756
- if (typeof event?.time !== "string") return void 0;
757
- const timestamp = Date.parse(event.time);
758
- return Number.isFinite(timestamp) ? timestamp : void 0;
759
- }
760
- function syncPhaseTimings(previous, event) {
761
- if (!event?.type) return previous;
762
- const timestamp = parseWebhookTimestamp(event) ?? Date.now();
763
- const setReceived = (event.type === "deposit-received" || event.type === "bridge-started" || event.type === "bridge-complete" || event.type === "bridge-failed" || event.type === "error") && previous.receivedAt === void 0;
764
- const setBridging = (event.type === "bridge-started" || event.type === "bridge-complete") && previous.bridgingAt === void 0;
765
- const setCompleted = event.type === "bridge-complete" && previous.completedAt === void 0;
766
- if (!setReceived && !setBridging && !setCompleted) return previous;
767
- return {
768
- ...previous,
769
- ...setReceived && { receivedAt: timestamp },
770
- ...setBridging && { bridgingAt: timestamp },
771
- ...setCompleted && { completedAt: timestamp }
772
- };
773
- }
774
- function formatTimer(seconds) {
775
- const safe = Math.max(0, seconds);
776
- const mins = Math.floor(safe / 60);
777
- const secs = safe % 60;
778
- return `${mins}:${String(secs).padStart(2, "0")}`;
779
- }
780
- function TickerChar({ value }) {
781
- const [current, setCurrent] = useState4(value);
782
- const [previous, setPrevious] = useState4(null);
783
- const [animKey, setAnimKey] = useState4(0);
784
- useEffect3(() => {
785
- if (value === current) return;
786
- setPrevious(current);
787
- setCurrent(value);
788
- setAnimKey((k) => k + 1);
789
- const timeout = setTimeout(() => setPrevious(null), 360);
790
- return () => clearTimeout(timeout);
791
- }, [value, current]);
792
- return /* @__PURE__ */ jsxs4("span", { className: "rs-ticker-slot", children: [
793
- previous !== null && /* @__PURE__ */ jsx5("span", { className: "rs-ticker-out", children: previous }, `out-${animKey}`),
794
- /* @__PURE__ */ jsx5("span", { className: "rs-ticker-in", children: current }, `in-${animKey}`)
795
- ] });
796
- }
797
- function Ticker({ value }) {
798
- return /* @__PURE__ */ jsx5("span", { className: "rs-ticker", "aria-label": value, children: value.split("").map((char, index) => (
799
- // Position-stable key so each slot keeps its own animation state.
800
- // eslint-disable-next-line react/no-array-index-key
801
- /* @__PURE__ */ jsx5(TickerChar, { value: char }, index)
802
- )) });
803
- }
804
- function getPhaseStartTime(phaseId, phaseTimings) {
805
- if (phaseId === "confirming") return phaseTimings.startedAt;
806
- if (phaseId === "received") {
807
- return phaseTimings.receivedAt ?? phaseTimings.startedAt;
808
- }
809
- return phaseTimings.bridgingAt ?? phaseTimings.receivedAt ?? phaseTimings.startedAt;
810
- }
811
- function getFailedPhaseId(phaseTimings) {
812
- if (phaseTimings.bridgingAt !== void 0) return "bridging";
813
- if (phaseTimings.receivedAt !== void 0) return "received";
814
- return "confirming";
815
- }
816
- function getCurrentPhaseId(state, phaseTimings) {
817
- if (state.type === "failed") {
818
- return getFailedPhaseId(phaseTimings);
819
- }
820
- if (state.type === "complete") {
821
- return void 0;
822
- }
823
- if (state.lastEvent?.type === "bridge-started" || state.lastEvent?.type === "bridge-complete")
824
- return "bridging";
825
- if (state.lastEvent?.type === "deposit-received") return "received";
826
- return "confirming";
827
- }
828
- function getCompletionDestinationTxHash(event) {
829
- const eventData = event?.data;
830
- const swapTxHash = eventData?.swap?.transactionHash;
831
- const destinationTxHash = eventData?.destination?.transactionHash;
832
- return swapTxHash ?? destinationTxHash;
833
- }
834
- function getTerminalProcessingOutcome(event) {
835
- if (event?.type === "post-bridge-swap-complete") {
836
- return {
837
- type: "complete",
838
- destinationTxHash: getCompletionDestinationTxHash(event)
839
- };
840
- }
841
- if (event?.type === "post-bridge-swap-failed") {
842
- return {
843
- type: "failed",
844
- message: formatBridgeFailedMessage(event).message
845
- };
846
- }
847
- if (event?.type === "bridge-complete") {
848
- return {
849
- type: "complete",
850
- destinationTxHash: getCompletionDestinationTxHash(event)
851
- };
852
- }
853
- if (event?.type === "bridge-failed") {
854
- return {
855
- type: "failed",
856
- message: formatBridgeFailedMessage(event).message
857
- };
858
- }
859
- if (event?.type === "error") {
860
- return {
861
- type: "failed",
862
- message: event.data?.message ?? "Unknown error"
863
- };
864
- }
865
- return null;
866
- }
867
- function getInitialProcessingState(event, txHash) {
868
- if (!event || !isEventForTx(event, txHash)) return null;
869
- const terminal = getTerminalProcessingOutcome(event);
870
- if (terminal?.type === "complete") {
871
- return { type: "complete", lastEvent: event };
872
- }
873
- if (terminal?.type === "failed") {
874
- return { type: "failed", message: terminal.message, lastEvent: event };
875
- }
876
- if (isDepositEvent(event)) {
877
- return { type: "processing", lastEvent: event };
878
- }
879
- return null;
880
- }
881
- function getSettledSourceTxHash(event, fallbackTxHash) {
882
- return event?.data?.source?.transactionHash ?? fallbackTxHash;
883
- }
884
- function ProcessingStep({
885
- smartAccount,
886
- solanaDepositAddress,
887
- txHash,
888
- sourceChain,
889
- sourceToken,
890
- targetChain,
891
- amount,
892
- sourceSymbol: providedSourceSymbol,
893
- sourceDecimals: providedSourceDecimals,
894
- amountUsd,
895
- service,
896
- directTransfer,
897
- sourceTxHashPending,
898
- initialEvent,
899
- flowLabel = "deposit",
900
- debug,
901
- targetToken,
902
- uiConfig,
903
- quotedFeeAmount,
904
- quotedFeeSymbol,
905
- quote,
906
- balanceAfterUsd,
907
- isSwappedOrder,
908
- swappedContext,
909
- onClose,
910
- onNewDeposit,
911
- onRetry,
912
- onDepositComplete,
913
- onDepositFailed,
914
- onSourceTransactionObserved,
915
- onError
916
- }) {
917
- const startTimeRef = useRef2(Date.now());
918
- const pollIntervalRef = useRef2(INITIAL_POLL_INTERVAL);
919
- const pollTimeoutRef = useRef2(null);
920
- const escalatedDelayRef = useRef2(false);
921
- const processingContextRef = useLatestRef({
922
- amount,
923
- sourceChain,
924
- sourceToken,
925
- sourceDecimals: providedSourceDecimals,
926
- amountUsd,
927
- targetChain,
928
- targetToken
929
- });
930
- const onDepositCompleteRef = useLatestRef(onDepositComplete);
931
- const onDepositFailedRef = useLatestRef(onDepositFailed);
932
- const onErrorRef = useLatestRef(onError);
933
- const explorerTxUrl = useExplorerTxUrl();
934
- const initialTerminalEventRef = useRef2(null);
935
- const [state, setState] = useState4(() => {
936
- if (directTransfer) return { type: "complete" };
937
- const initial = getInitialProcessingState(
938
- initialEvent,
939
- txHash
940
- );
941
- if (initial) {
942
- if (initial.type !== "processing") {
943
- initialTerminalEventRef.current = initial.lastEvent ?? null;
944
- }
945
- return initial;
946
- }
947
- return { type: "processing" };
948
- });
949
- const [elapsedSeconds, setElapsedSeconds] = useState4(0);
950
- const [phaseTimings, setPhaseTimings] = useState4(() => {
951
- const saved = loadPhaseTimings(txHash);
952
- if (saved) {
953
- startTimeRef.current = saved.startedAt;
954
- return saved;
955
- }
956
- return { startedAt: startTimeRef.current };
957
- });
958
- const [hasEscalatedDelay, setHasEscalatedDelay] = useState4(false);
959
- const updatePhaseTimings = useCallback2(
960
- (updater) => {
961
- setPhaseTimings((previous) => {
962
- const next = updater(previous);
963
- savePhaseTimings(txHash, next);
964
- return next;
965
- });
966
- },
967
- [txHash]
968
- );
969
- useEffect3(() => {
970
- if (!directTransfer) return;
971
- const completedAt = Date.now();
972
- updatePhaseTimings(() => ({
973
- startedAt: startTimeRef.current,
974
- completedAt,
975
- endedAt: completedAt
976
- }));
977
- debugLog(debug, "processing", "direct-transfer:complete", {
978
- txHash,
979
- flowLabel
980
- });
981
- const context = processingContextRef.current;
982
- onDepositCompleteRef.current?.(txHash, void 0, {
983
- amount: context.amount,
984
- sourceChain: context.sourceChain,
985
- sourceToken: context.sourceToken,
986
- sourceDecimals: context.sourceDecimals,
987
- amountUsd: context.amountUsd,
988
- targetChain: context.targetChain,
989
- targetToken: context.targetToken
990
- });
991
- }, [
992
- debug,
993
- directTransfer,
994
- flowLabel,
995
- onDepositCompleteRef,
996
- processingContextRef,
997
- txHash,
998
- updatePhaseTimings
999
- ]);
1000
- useEffect3(() => {
1001
- const event = initialTerminalEventRef.current;
1002
- if (!event) return;
1003
- initialTerminalEventRef.current = null;
1004
- const outcome = getTerminalProcessingOutcome(event);
1005
- if (!outcome) return;
1006
- if (outcome.type === "complete") {
1007
- const context = processingContextRef.current;
1008
- const sourceTxHash2 = getSettledSourceTxHash(event, txHash);
1009
- debugLog(debug, "processing", "initial-event:complete", {
1010
- txHash,
1011
- sourceTxHash: sourceTxHash2,
1012
- destinationTxHash: outcome.destinationTxHash,
1013
- event: event.type
1014
- });
1015
- onDepositCompleteRef.current?.(
1016
- sourceTxHash2,
1017
- outcome.destinationTxHash,
1018
- {
1019
- amount: context.amount,
1020
- sourceChain: context.sourceChain,
1021
- sourceToken: context.sourceToken,
1022
- sourceDecimals: context.sourceDecimals,
1023
- amountUsd: context.amountUsd,
1024
- targetChain: context.targetChain,
1025
- targetToken: context.targetToken
1026
- }
1027
- );
1028
- return;
1029
- }
1030
- debugLog(debug, "processing", "initial-event:failed", {
1031
- txHash,
1032
- message: outcome.message,
1033
- event: event.type
1034
- });
1035
- onDepositFailedRef.current?.(txHash, outcome.message);
1036
- }, [
1037
- debug,
1038
- onDepositCompleteRef,
1039
- onDepositFailedRef,
1040
- processingContextRef,
1041
- txHash
1042
- ]);
1043
- useEffect3(() => {
1044
- if (directTransfer || state.type !== "processing") return;
1045
- const updateElapsed = () => {
1046
- setElapsedSeconds(
1047
- Math.floor((Date.now() - startTimeRef.current) / 1e3)
1048
- );
1049
- };
1050
- updateElapsed();
1051
- const intervalId = setInterval(updateElapsed, 1e3);
1052
- return () => clearInterval(intervalId);
1053
- }, [directTransfer, state.type]);
1054
- useEffect3(() => {
1055
- if (state.type === "processing") return;
1056
- const endedAt = state.type === "complete" ? phaseTimings.completedAt ?? Date.now() : Date.now();
1057
- setElapsedSeconds(Math.floor((endedAt - startTimeRef.current) / 1e3));
1058
- updatePhaseTimings(
1059
- (previous) => previous.endedAt !== void 0 ? previous : { ...previous, endedAt }
1060
- );
1061
- }, [phaseTimings.completedAt, state.type, updatePhaseTimings]);
1062
- useEffect3(() => {
1063
- if (!state.lastEvent) return;
1064
- updatePhaseTimings(
1065
- (previous) => syncPhaseTimings(previous, state.lastEvent)
1066
- );
1067
- }, [state.lastEvent?.time, state.lastEvent?.type, updatePhaseTimings]);
1068
- const [swappedFiatContext, setSwappedFiatContext] = useState4(null);
1069
- useEffect3(() => {
1070
- let cancelled = false;
1071
- let timeoutId;
1072
- const isFiatSwappedOrder = isSwappedOrder && swappedContext?.variant === "fiat";
1073
- const expectedOrderUuid = swappedContext?.expectedOrderUuid ?? null;
1074
- setSwappedFiatContext(null);
1075
- if (isFiatSwappedOrder && !expectedOrderUuid) {
1076
- return () => {
1077
- cancelled = true;
1078
- };
1079
- }
1080
- let attempts = 0;
1081
- const matchesCurrentDeposit = (res) => {
1082
- if (isFiatSwappedOrder) {
1083
- return res.orderId === expectedOrderUuid;
1084
- }
1085
- return res.transactionId != null && res.transactionId.toLowerCase() === txHash.toLowerCase();
1086
- };
1087
- const scheduleRetry = () => {
1088
- if (cancelled || !isFiatSwappedOrder || attempts >= SWAPPED_FIAT_STATUS_MAX_ATTEMPTS) {
1089
- return;
1090
- }
1091
- timeoutId = setTimeout(
1092
- fetchContext,
1093
- SWAPPED_RECEIPT_STATUS_RETRY_INTERVAL_MS
1094
- );
1095
- };
1096
- async function fetchContext() {
1097
- attempts += 1;
1098
- try {
1099
- const res = await service.fetchSwappedOrderStatus(smartAccount);
1100
- if (cancelled) return;
1101
- if (!res || !matchesCurrentDeposit(res)) {
1102
- scheduleRetry();
1103
- return;
1104
- }
1105
- setSwappedFiatContext(toSwappedFiatContext(res));
1106
- if (isFiatSwappedOrder && !hasFiatReceiptPricing(res)) {
1107
- scheduleRetry();
1108
- }
1109
- } catch {
1110
- scheduleRetry();
1111
- }
1112
- }
1113
- fetchContext();
1114
- return () => {
1115
- cancelled = true;
1116
- if (timeoutId !== void 0) clearTimeout(timeoutId);
1117
- };
1118
- }, [
1119
- service,
1120
- smartAccount,
1121
- txHash,
1122
- isSwappedOrder,
1123
- swappedContext?.variant,
1124
- swappedContext?.expectedOrderUuid
1125
- ]);
1126
- useEffect3(() => {
1127
- if (directTransfer) return;
1128
- if (state.type !== "processing") {
1129
- pollIntervalRef.current = INITIAL_POLL_INTERVAL;
1130
- return;
1131
- }
1132
- let isMounted = true;
1133
- async function pollStatus() {
1134
- try {
1135
- const account = sourceChain === "solana" && solanaDepositAddress ? solanaDepositAddress : smartAccount;
1136
- debugLog(debug, "processing", "poll:request", {
1137
- account,
1138
- txHash,
1139
- intervalMs: pollIntervalRef.current
1140
- });
1141
- const data = await service.fetchStatus(account, txHash);
1142
- const lastEvent2 = data.lastEvent;
1143
- const eventMatchesTx = isEventForTx(lastEvent2, txHash);
1144
- const eventForCurrentTx = eventMatchesTx ? lastEvent2 : void 0;
1145
- if (lastEvent2) {
1146
- const eventData = lastEvent2.data;
1147
- debugLog(debug, "processing", "poll:event", {
1148
- type: lastEvent2.type,
1149
- matchesTx: eventMatchesTx,
1150
- intentId: eventData?.intentId
1151
- });
1152
- }
1153
- if (!isMounted) return;
1154
- if (eventForCurrentTx?.type === "bridge-complete") {
1155
- setState({ type: "complete", lastEvent: eventForCurrentTx });
1156
- const sourceTxHash2 = getSettledSourceTxHash(
1157
- eventForCurrentTx,
1158
- txHash
1159
- );
1160
- const destinationTxHash2 = eventForCurrentTx.data?.destination?.transactionHash;
1161
- debugLog(debug, "processing", "state:complete", {
1162
- txHash,
1163
- sourceTxHash: sourceTxHash2,
1164
- destinationTxHash: destinationTxHash2,
1165
- event: eventForCurrentTx.type
1166
- });
1167
- const context = processingContextRef.current;
1168
- onDepositCompleteRef.current?.(sourceTxHash2, destinationTxHash2, {
1169
- amount: context.amount,
1170
- sourceChain: context.sourceChain,
1171
- sourceToken: context.sourceToken,
1172
- sourceDecimals: context.sourceDecimals,
1173
- amountUsd: context.amountUsd,
1174
- targetChain: context.targetChain,
1175
- targetToken: context.targetToken
1176
- });
1177
- return;
1178
- }
1179
- if (eventForCurrentTx?.type === "bridge-failed") {
1180
- const formatted = formatBridgeFailedMessage(eventForCurrentTx);
1181
- setState({
1182
- type: "failed",
1183
- message: formatted.message,
1184
- lastEvent: eventForCurrentTx
1185
- });
1186
- debugLog(debug, "processing", "state:failed", {
1187
- txHash,
1188
- message: formatted.message,
1189
- code: formatted.code
1190
- });
1191
- onDepositFailedRef.current?.(txHash, formatted.message);
1192
- return;
1193
- }
1194
- if (eventForCurrentTx?.type === "error") {
1195
- const errorMessage = eventForCurrentTx.data?.message ?? "Unknown error";
1196
- setState({
1197
- type: "failed",
1198
- message: errorMessage,
1199
- lastEvent: eventForCurrentTx
1200
- });
1201
- debugLog(debug, "processing", "state:error-event", {
1202
- txHash,
1203
- message: errorMessage
1204
- });
1205
- onDepositFailedRef.current?.(txHash, errorMessage);
1206
- return;
1207
- }
1208
- setState((previous) => ({
1209
- type: "processing",
1210
- lastEvent: eventForCurrentTx ?? previous.lastEvent
1211
- }));
1212
- scheduleNextPoll();
1213
- } catch (error) {
1214
- debugError(debug, "processing", "poll:failure", error, {
1215
- smartAccount,
1216
- txHash,
1217
- intervalMs: pollIntervalRef.current
1218
- });
1219
- scheduleNextPoll();
1220
- }
1221
- }
1222
- function scheduleNextPoll() {
1223
- if (!isMounted) return;
1224
- pollTimeoutRef.current = setTimeout(() => {
1225
- pollIntervalRef.current = Math.min(
1226
- pollIntervalRef.current * BACKOFF_MULTIPLIER,
1227
- MAX_POLL_INTERVAL
1228
- );
1229
- debugLog(debug, "processing", "poll:scheduled", {
1230
- nextIntervalMs: pollIntervalRef.current
1231
- });
1232
- pollStatus();
1233
- }, pollIntervalRef.current);
1234
- }
1235
- pollStatus();
1236
- return () => {
1237
- isMounted = false;
1238
- if (pollTimeoutRef.current) {
1239
- clearTimeout(pollTimeoutRef.current);
1240
- }
1241
- };
1242
- }, [
1243
- debug,
1244
- directTransfer,
1245
- onDepositCompleteRef,
1246
- onDepositFailedRef,
1247
- processingContextRef,
1248
- service,
1249
- smartAccount,
1250
- solanaDepositAddress,
1251
- sourceChain,
1252
- state.type,
1253
- txHash
1254
- ]);
1255
- useEffect3(() => {
1256
- if (directTransfer || state.type !== "processing") return;
1257
- const timeoutId = setTimeout(() => {
1258
- if (escalatedDelayRef.current) return;
1259
- escalatedDelayRef.current = true;
1260
- setHasEscalatedDelay(true);
1261
- const message = "Transfer is taking longer than expected. Your funds are safe and processing will continue automatically.";
1262
- debugLog(debug, "processing", "state:delay-escalated", {
1263
- txHash,
1264
- timeoutMs: ESCALATED_DELAY_MS
1265
- });
1266
- onErrorRef.current?.(message, "PROCESS_TIMEOUT");
1267
- }, ESCALATED_DELAY_MS);
1268
- return () => clearTimeout(timeoutId);
1269
- }, [debug, directTransfer, onErrorRef, state.type, txHash]);
1270
- const isComplete = state.type === "complete";
1271
- const isFailed = state.type === "failed";
1272
- const isProcessing = state.type === "processing";
1273
- const lastEvent = state.lastEvent;
1274
- const failureMessage = state.type === "failed" ? state.message : void 0;
1275
- const timelineNowMs = phaseTimings.endedAt ?? Date.now();
1276
- const flowNoun = flowLabel === "withdraw" ? "withdrawal" : "deposit";
1277
- const flowCapitalized = flowLabel === "withdraw" ? "Withdrawal" : "Deposit";
1278
- const destinationTxHash = lastEvent?.data?.destination?.transactionHash || null;
1279
- const sourceTxHash = getSettledSourceTxHash(lastEvent, txHash);
1280
- const sourceDetails = getEventSourceDetails(lastEvent);
1281
- const displaySourceChain = sourceDetails.chainId ?? sourceChain;
1282
- const displaySourceToken = sourceDetails.token ?? sourceToken;
1283
- const displayAmount = sourceDetails.amount ?? amount;
1284
- const targetChainId = targetChainToChainId(targetChain);
1285
- const settledSourceTxHash = lastEvent?.data?.source?.transactionHash;
1286
- const sourceExplorerHash = settledSourceTxHash ?? (sourceTxHashPending ? null : sourceTxHash);
1287
- useEffect3(() => {
1288
- if (sourceTxHashPending && settledSourceTxHash) {
1289
- onSourceTransactionObserved?.(settledSourceTxHash);
1290
- }
1291
- }, [onSourceTransactionObserved, settledSourceTxHash, sourceTxHashPending]);
1292
- const sourceExplorerUrl = sourceExplorerHash ? explorerTxUrl(displaySourceChain, sourceExplorerHash) : null;
1293
- const destExplorerUrl = destinationTxHash ? explorerTxUrl(targetChainId, destinationTxHash) : null;
1294
- const sourceDisplay = (() => {
1295
- const resolved = resolveTokenDisplay(displaySourceToken, displaySourceChain, {
1296
- symbol: providedSourceSymbol ?? (displaySourceChain === "solana" ? "SOL" : "Token"),
1297
- decimals: providedSourceDecimals
1298
- });
1299
- return {
1300
- symbol: resolved.symbol,
1301
- decimals: resolved.decimals ?? (displaySourceChain === "solana" ? 9 : 18)
1302
- };
1303
- })();
1304
- const sourceSymbol = sourceDisplay.symbol;
1305
- const formattedSentAmount = formatRawTokenAmount(displayAmount, sourceDisplay) ?? // Not raw base units (e.g. a decimal string) — render the number as-is.
1306
- formatTokenAmount(Number(displayAmount), sourceDisplay.symbol) ?? displayAmount;
1307
- const eventDestination = getEventDestinationDetails(lastEvent);
1308
- const targetDisplay = resolveTokenDisplay(
1309
- eventDestination.token ?? targetToken,
1310
- eventDestination.chainId ?? targetChainId,
1311
- // Dapp deposits default to USDC targets; mirror the source symbol last so
1312
- // unresolvable same-token routes still label sensibly.
1313
- { symbol: providedSourceSymbol ?? "USDC" }
1314
- );
1315
- const targetSymbol = targetDisplay.symbol;
1316
- const formattedDestinationAmount = eventDestination.amount !== void 0 ? formatRawTokenAmount(eventDestination.amount, targetDisplay) : void 0;
1317
- const amountUsdNumber = amountUsd !== void 0 && Number(amountUsd) > 0 ? Number(amountUsd) : void 0;
1318
- const prices = useTokenPrices(service, [
1319
- {
1320
- chainId: displaySourceChain,
1321
- address: displaySourceToken,
1322
- symbol: sourceDisplay.symbol
1323
- },
1324
- {
1325
- chainId: eventDestination.chainId ?? targetChainId,
1326
- address: eventDestination.token ?? targetToken,
1327
- symbol: targetDisplay.symbol
1328
- }
1329
- ]);
1330
- const estimatedReceiveAmount = (() => {
1331
- const estimate = estimateReceiveAmount({
1332
- sourceAmount: tokenAmountToNumber(displayAmount, sourceDisplay.decimals),
1333
- sourceSymbol: sourceDisplay.symbol,
1334
- targetSymbol: targetDisplay.symbol,
1335
- sourceAmountUsd: amountUsdNumber,
1336
- sourceTrusted: isRecognizedToken(displaySourceToken, displaySourceChain),
1337
- prices
1338
- });
1339
- return estimate !== void 0 ? formatTokenAmount(estimate, targetDisplay.symbol) : void 0;
1340
- })();
1341
- const quotedReceive = quote ? formatQuotedReceive(quote.output, targetSymbol) : void 0;
1342
- const receiveDisplay = formattedDestinationAmount ? (
1343
- // The delivered amount, once the bridge reports it — actual, not an
1344
- // estimate, so it wins over the quote and carries no "~".
1345
- `${formattedDestinationAmount} ${targetSymbol}`
1346
- ) : quotedReceive ?? (estimatedReceiveAmount !== void 0 ? `~${estimatedReceiveAmount} ${targetSymbol}` : amountUsdNumber !== void 0 ? `~$${amountUsdNumber.toFixed(2)}` : (
1347
- // Cross-token with no price and no USD context (rare QR case):
1348
- // an honest dash beats a wrong number.
1349
- "\u2014"
1350
- ));
1351
- const formattedBridgingCost = (() => {
1352
- if (sourceSymbol.toUpperCase() !== targetSymbol.toUpperCase()) {
1353
- return void 0;
1354
- }
1355
- const sentValue = tokenAmountToNumber(displayAmount, sourceDisplay.decimals);
1356
- const receivedValue = eventDestination.amount !== void 0 ? tokenAmountToNumber(eventDestination.amount, targetDisplay.decimals) : void 0;
1357
- if (sentValue === void 0 || receivedValue === void 0 || sentValue <= receivedValue) {
1358
- return void 0;
1359
- }
1360
- const formatted = formatTokenAmount(sentValue - receivedValue, sourceSymbol);
1361
- if (formatted === void 0 || Number(formatted.replace(/,/g, "")) === 0) {
1362
- return void 0;
1363
- }
1364
- return formatted;
1365
- })();
1366
- const currentPhaseId = getCurrentPhaseId(state, phaseTimings);
1367
- const activePhaseStartedAt = currentPhaseId ? getPhaseStartTime(currentPhaseId, phaseTimings) : void 0;
1368
- const activePhaseElapsedMs = isProcessing && activePhaseStartedAt !== void 0 ? timelineNowMs - activePhaseStartedAt : 0;
1369
- const delayPhaseId = isProcessing && currentPhaseId && activePhaseElapsedMs >= SOFT_DELAY_MS[currentPhaseId] ? currentPhaseId : void 0;
1370
- void delayPhaseId;
1371
- void hasEscalatedDelay;
1372
- const sourceChainIcon = getChainIcon(displaySourceChain);
1373
- const targetChainIcon = getChainIcon(targetChainId);
1374
- const sourceChainName = getChainName(displaySourceChain);
1375
- const targetChainName = getChainName(targetChainId);
1376
- const timerText = formatTimer(elapsedSeconds);
1377
- const feeSponsored = uiConfig?.feeSponsored ?? false;
1378
- const feeTooltip = uiConfig?.feeTooltip ?? (feeSponsored ? "Network fees are sponsored for this deposit." : "Network fees apply.");
1379
- const stateTitle = isComplete ? `${flowCapitalized} successful` : isFailed ? `${flowCapitalized} failed` : "Processing...";
1380
- const handleRetry = onRetry ?? onNewDeposit;
1381
- const headerContent = isComplete ? /* @__PURE__ */ jsxs4("div", { className: "rs-body-header", children: [
1382
- /* @__PURE__ */ jsx5(SuccessBadge, {}),
1383
- /* @__PURE__ */ jsx5("div", { className: "rs-body-header-text", children: /* @__PURE__ */ jsx5("h2", { className: "rs-body-header-title", children: stateTitle }) })
1384
- ] }) : isFailed ? /* @__PURE__ */ jsxs4("div", { className: "rs-body-header", children: [
1385
- /* @__PURE__ */ jsx5(FailedBadge, {}),
1386
- /* @__PURE__ */ jsx5("div", { className: "rs-body-header-text", children: /* @__PURE__ */ jsx5("h2", { className: "rs-body-header-title", children: stateTitle }) })
1387
- ] }) : /* @__PURE__ */ jsx5(BodyHeader, { icon: /* @__PURE__ */ jsx5(WalletIcon, {}), title: stateTitle });
1388
- if (isComplete && isSwappedOrder) {
1389
- const effectivePaymentMethod = swappedContext?.method ?? swappedFiatContext?.paymentMethod ?? null;
1390
- const onrampMethod = effectivePaymentMethod ? formatPaymentMethod(effectivePaymentMethod) : null;
1391
- const onrampMethodIcon = swappedContext?.variant === "connect" && effectivePaymentMethod ? getExchangeLogoSrc(
1392
- onrampMethod ?? effectivePaymentMethod,
1393
- effectivePaymentMethod
1394
- ) : null;
1395
- const connectPaidAmount = swappedContext?.variant === "connect" ? `${formattedSentAmount} ${sourceSymbol}` : null;
1396
- const amountPaid = connectPaidAmount ?? (swappedFiatContext?.paidAmountUsd != null ? `$${swappedFiatContext.paidAmountUsd.toFixed(2)}` : null);
1397
- const depositedAmount = receiveDisplay;
1398
- const onrampFeeUsd = swappedFiatContext?.onrampFeeUsd ?? null;
1399
- const sponsoredFeeTooltip = "This fee is sponsored for this deposit.";
1400
- const feesTooltip = feeSponsored ? "On-ramp fees are charged by the payment provider. Market and network fees are sponsored for this deposit." : "Fees include on-ramp, market, and network costs for this deposit.";
1401
- const { rows: quoteFeeRows, chargeableUsd } = buildQuoteFeeRows(
1402
- quote?.fees.breakdown,
1403
- sponsoredFeeTooltip
1404
- );
1405
- const feeRows = [];
1406
- if (onrampFeeUsd != null) {
1407
- feeRows.push({
1408
- label: "On-ramp fee",
1409
- value: formatFeeUsd(onrampFeeUsd)
1410
- });
1411
- }
1412
- const hasFallbackFee = quoteFeeRows.length === 0 && quotedFeeAmount !== void 0 && Number.isFinite(Number(quotedFeeAmount));
1413
- if (quoteFeeRows.length > 0) {
1414
- feeRows.push(...quoteFeeRows);
1415
- } else if (hasFallbackFee) {
1416
- feeRows.push({
1417
- label: "Network fee",
1418
- value: formatFeeUsd(Number(quotedFeeAmount)),
1419
- strike: feeSponsored,
1420
- tooltip: feeSponsored ? sponsoredFeeTooltip : void 0
1421
- });
1422
- }
1423
- const fallbackChargeableUsd = hasFallbackFee && !feeSponsored ? Number(quotedFeeAmount) : 0;
1424
- const feesTotal = feeRows.length > 0 ? formatFeeUsd(
1425
- (onrampFeeUsd ?? 0) + chargeableUsd + fallbackChargeableUsd
1426
- ) : null;
1427
- return /* @__PURE__ */ jsx5(
1428
- SwappedReceipt,
1429
- {
1430
- onrampMethod,
1431
- onrampMethodIcon,
1432
- amountPaid,
1433
- depositedAmount,
1434
- depositedSymbol: targetSymbol,
1435
- feesTotal,
1436
- feeRows,
1437
- feesTooltip,
1438
- newLabel: `New ${flowNoun}`,
1439
- onNewDeposit,
1440
- onClose: onClose ? () => onClose("success_done") : void 0
1441
- }
1442
- );
1443
- }
1444
- return /* @__PURE__ */ jsxs4("div", { className: "rs-screen", children: [
1445
- /* @__PURE__ */ jsxs4("div", { className: "rs-screen-body rs-screen-body--gap-32", children: [
1446
- headerContent,
1447
- /* @__PURE__ */ jsxs4("div", { className: "rs-review-details", children: [
1448
- /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1449
- /* @__PURE__ */ jsx5("span", { children: "Source chain" }),
1450
- /* @__PURE__ */ jsxs4("span", { className: "rs-review-detail-value", children: [
1451
- /* @__PURE__ */ jsx5("span", { children: sourceChainName }),
1452
- sourceChainIcon && /* @__PURE__ */ jsx5("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx5(CdnIcon, { src: sourceChainIcon }) }),
1453
- sourceExplorerUrl && /* @__PURE__ */ jsx5(
1454
- "a",
1455
- {
1456
- href: sourceExplorerUrl,
1457
- target: "_blank",
1458
- rel: "noopener noreferrer",
1459
- className: "rs-review-detail-link",
1460
- "aria-label": "View source transaction",
1461
- children: /* @__PURE__ */ jsx5(ExternalLinkIcon, {})
1462
- }
1463
- )
1464
- ] })
1465
- ] }),
1466
- /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1467
- /* @__PURE__ */ jsx5("span", { children: "Destination chain" }),
1468
- /* @__PURE__ */ jsxs4("span", { className: "rs-review-detail-value", children: [
1469
- /* @__PURE__ */ jsx5("span", { children: targetChainName }),
1470
- targetChainIcon && /* @__PURE__ */ jsx5("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx5(CdnIcon, { src: targetChainIcon }) }),
1471
- destExplorerUrl && /* @__PURE__ */ jsx5(
1472
- "a",
1473
- {
1474
- href: destExplorerUrl,
1475
- target: "_blank",
1476
- rel: "noopener noreferrer",
1477
- className: "rs-review-detail-link",
1478
- "aria-label": "View destination transaction",
1479
- children: /* @__PURE__ */ jsx5(ExternalLinkIcon, {})
1480
- }
1481
- )
1482
- ] })
1483
- ] }),
1484
- /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1485
- /* @__PURE__ */ jsx5("span", { children: isProcessing ? "Time elapsed" : "Total time" }),
1486
- /* @__PURE__ */ jsx5("span", { className: "rs-review-detail-value", children: /* @__PURE__ */ jsx5(Ticker, { value: timerText }) })
1487
- ] }),
1488
- isSwappedOrder ? /* @__PURE__ */ jsxs4(Fragment, { children: [
1489
- swappedFiatContext?.paidAmountUsd != null && /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1490
- /* @__PURE__ */ jsx5("span", { children: "You pay" }),
1491
- /* @__PURE__ */ jsxs4("span", { className: "rs-review-detail-value", children: [
1492
- "$",
1493
- swappedFiatContext.paidAmountUsd.toFixed(2),
1494
- swappedFiatContext.paymentMethod && /* @__PURE__ */ jsxs4("span", { style: { color: "#71717b", marginLeft: 6 }, children: [
1495
- "via",
1496
- " ",
1497
- formatPaymentMethod(swappedFiatContext.paymentMethod)
1498
- ] })
1499
- ] })
1500
- ] }),
1501
- swappedFiatContext?.onrampFeeUsd != null && /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1502
- /* @__PURE__ */ jsx5("span", { children: "On-ramp fee" }),
1503
- /* @__PURE__ */ jsxs4("span", { className: "rs-review-detail-value", children: [
1504
- "$",
1505
- swappedFiatContext.onrampFeeUsd.toFixed(2)
1506
- ] })
1507
- ] }),
1508
- formattedBridgingCost && /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1509
- /* @__PURE__ */ jsx5("span", { children: "Bridging cost" }),
1510
- /* @__PURE__ */ jsxs4("span", { className: "rs-review-detail-value", children: [
1511
- /* @__PURE__ */ jsxs4("span", { children: [
1512
- formattedBridgingCost,
1513
- " ",
1514
- sourceSymbol
1515
- ] }),
1516
- /* @__PURE__ */ jsx5("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx5(TokenIcon, { symbol: sourceSymbol }) })
1517
- ] })
1518
- ] })
1519
- ] }) : /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1520
- /* @__PURE__ */ jsx5("span", { children: isProcessing ? "You send" : "You sent" }),
1521
- /* @__PURE__ */ jsxs4("span", { className: "rs-review-detail-value", children: [
1522
- /* @__PURE__ */ jsxs4("span", { children: [
1523
- formattedSentAmount,
1524
- " ",
1525
- sourceSymbol
1526
- ] }),
1527
- /* @__PURE__ */ jsx5("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx5(TokenIcon, { symbol: sourceSymbol }) })
1528
- ] })
1529
- ] }),
1530
- /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1531
- /* @__PURE__ */ jsx5("span", { children: isProcessing ? "Receive" : "Received" }),
1532
- /* @__PURE__ */ jsxs4("span", { className: "rs-review-detail-value", children: [
1533
- /* @__PURE__ */ jsx5("span", { children: receiveDisplay }),
1534
- /* @__PURE__ */ jsx5("span", { className: "rs-review-detail-icon", children: /* @__PURE__ */ jsx5(TokenIcon, { symbol: targetSymbol }) })
1535
- ] })
1536
- ] }),
1537
- isFailed && balanceAfterUsd !== void 0 && /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1538
- /* @__PURE__ */ jsx5("span", { children: "Balance" }),
1539
- /* @__PURE__ */ jsxs4("span", { className: "rs-review-detail-value", children: [
1540
- "$",
1541
- balanceAfterUsd.toFixed(2)
1542
- ] })
1543
- ] }),
1544
- isSwappedOrder ? quotedFeeAmount !== void 0 && /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1545
- /* @__PURE__ */ jsx5("span", { children: "Fees" }),
1546
- /* @__PURE__ */ jsxs4("span", { className: "rs-review-detail-value", children: [
1547
- /* @__PURE__ */ jsxs4(
1548
- "span",
1549
- {
1550
- style: feeSponsored ? { textDecoration: "line-through" } : void 0,
1551
- children: [
1552
- quotedFeeAmount,
1553
- " ",
1554
- quotedFeeSymbol ?? sourceSymbol
1555
- ]
1556
- }
1557
- ),
1558
- /* @__PURE__ */ jsx5(Tooltip, { content: feeTooltip, children: /* @__PURE__ */ jsx5(
1559
- "span",
1560
- {
1561
- className: "rs-review-detail-info",
1562
- "aria-label": "Fee info",
1563
- children: /* @__PURE__ */ jsx5(InfoIcon, {})
1564
- }
1565
- ) })
1566
- ] })
1567
- ] }) : (() => {
1568
- const { display, struck } = quoteFeeRow(quote?.fees);
1569
- if (display !== null) {
1570
- return /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1571
- /* @__PURE__ */ jsx5("span", { children: "Fees" }),
1572
- /* @__PURE__ */ jsx5("span", { className: "rs-review-detail-value", children: /* @__PURE__ */ jsx5(
1573
- "span",
1574
- {
1575
- style: struck ? { textDecoration: "line-through" } : void 0,
1576
- children: display
1577
- }
1578
- ) })
1579
- ] });
1580
- }
1581
- const feeValue = quotedFeeAmount ?? formattedBridgingCost;
1582
- if (feeValue === void 0) return null;
1583
- return /* @__PURE__ */ jsxs4("div", { className: "rs-review-detail-row", children: [
1584
- /* @__PURE__ */ jsx5("span", { children: "Fees" }),
1585
- /* @__PURE__ */ jsx5("span", { className: "rs-review-detail-value", children: /* @__PURE__ */ jsxs4(
1586
- "span",
1587
- {
1588
- style: feeSponsored ? { textDecoration: "line-through" } : void 0,
1589
- children: [
1590
- feeValue,
1591
- " ",
1592
- quotedFeeSymbol ?? sourceSymbol
1593
- ]
1594
- }
1595
- ) })
1596
- ] });
1597
- })()
1598
- ] }),
1599
- isFailed && failureMessage && /* @__PURE__ */ jsx5(Callout, { variant: "error", children: failureMessage }),
1600
- isProcessing && /* @__PURE__ */ jsx5(
1601
- Button,
1602
- {
1603
- fullWidth: true,
1604
- disabled: true,
1605
- loading: true,
1606
- loadingText: "Submitting transaction\u2026",
1607
- children: "Submitting transaction\u2026"
1608
- }
1609
- ),
1610
- isComplete && /* @__PURE__ */ jsxs4("div", { className: "rs-screen-button-row", children: [
1611
- onNewDeposit && /* @__PURE__ */ jsxs4(Button, { variant: "outline", onClick: onNewDeposit, fullWidth: true, children: [
1612
- "New ",
1613
- flowNoun
1614
- ] }),
1615
- onClose && /* @__PURE__ */ jsx5(Button, { onClick: () => onClose("success_done"), fullWidth: true, children: "Done" })
1616
- ] }),
1617
- isFailed && /* @__PURE__ */ jsxs4("div", { className: "rs-screen-button-row", children: [
1618
- onClose && /* @__PURE__ */ jsx5(
1619
- Button,
1620
- {
1621
- variant: "outline",
1622
- onClick: () => onClose("failure_cancel"),
1623
- fullWidth: true,
1624
- children: "Cancel"
1625
- }
1626
- ),
1627
- handleRetry && /* @__PURE__ */ jsx5(Button, { onClick: handleRetry, fullWidth: true, children: "Try again" })
1628
- ] })
1629
- ] }),
1630
- /* @__PURE__ */ jsx5(PoweredBy, {})
1631
- ] });
1632
- }
1633
-
1634
- // src/core/account-setup.ts
1635
- import { concat, isAddress, keccak256, toHex } from "viem";
1636
- var SetupError = class extends Error {
1637
- constructor(message, category) {
1638
- super(message);
1639
- this.category = category;
1640
- this.name = "SetupError";
1641
- }
1642
- };
1643
- function stableStringify(value) {
1644
- if (value === null || typeof value !== "object") {
1645
- return JSON.stringify(value);
1646
- }
1647
- if (Array.isArray(value)) {
1648
- return `[${value.map(stableStringify).join(",")}]`;
1649
- }
1650
- const obj = value;
1651
- const keys = Object.keys(obj).sort();
1652
- const entries = keys.filter((k) => obj[k] !== void 0).map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
1653
- return `{${entries.join(",")}}`;
1654
- }
1655
- function normalizeKeyToken(value) {
1656
- if (value === void 0) return void 0;
1657
- return value.startsWith("0x") ? value.toLowerCase() : value;
1658
- }
1659
- var SOLANA_ADDRESS_PATTERN = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
1660
- function recipientMatchesTarget(recipient, targetChain) {
1661
- return targetChainToCaip2(targetChain).startsWith("solana:") ? SOLANA_ADDRESS_PATTERN.test(recipient) : isAddress(recipient);
1662
- }
1663
- function deriveAccountSalt(input) {
1664
- return keccak256(
1665
- concat([
1666
- keccak256(toHex(normalizeKeyToken(input.recipient) ?? "")),
1667
- keccak256(toHex(targetChainToCaip2(input.targetChain))),
1668
- keccak256(toHex(normalizeKeyToken(input.targetToken) ?? ""))
1669
- ])
1670
- );
1671
- }
1672
- function computeRequestKey(input) {
1673
- return stableStringify({
1674
- targetChain: input.targetChain,
1675
- targetToken: normalizeKeyToken(input.targetToken),
1676
- recipient: normalizeKeyToken(input.recipient),
1677
- outputTokenRules: input.outputTokenRules ?? null,
1678
- rejectUnmapped: input.rejectUnmapped ?? null,
1679
- forceRegister: input.forceRegister ?? false
1680
- });
1681
- }
1682
- function computeCacheKey(input) {
1683
- return stableStringify({
1684
- targetChain: input.targetChain,
1685
- targetToken: normalizeKeyToken(input.targetToken),
1686
- recipient: normalizeKeyToken(input.recipient),
1687
- outputTokenRules: input.outputTokenRules ?? null,
1688
- rejectUnmapped: input.rejectUnmapped ?? null
1689
- });
1690
- }
1691
- function computeIsCacheable(input) {
1692
- return !input.forceRegister;
1693
- }
1694
- var SETUP_REQUEST_DEDUPE_TTL_MS = 3e4;
1695
- var setupRequestDedupe = /* @__PURE__ */ new Map();
1696
- function readDedupedAccountSetupResult(requestKey) {
1697
- const now = Date.now();
1698
- const existing = setupRequestDedupe.get(requestKey);
1699
- if (existing?.settled && existing.expiresAt > now && existing.result) {
1700
- return existing.result;
1701
- }
1702
- return null;
1703
- }
1704
- async function runAccountSetup(input, deps) {
1705
- const { service, debug } = deps;
1706
- const cacheKey = computeCacheKey(input);
1707
- if (!input.recipient) {
1708
- throw new SetupError(
1709
- "A recipient is required to open a deposit account.",
1710
- "client"
1711
- );
1712
- }
1713
- if (!recipientMatchesTarget(input.recipient, input.targetChain)) {
1714
- throw new SetupError(
1715
- input.targetChain === "solana" ? "A Solana destination needs a base58 recipient address." : "An EVM destination needs a 0x recipient address.",
1716
- "client"
1717
- );
1718
- }
1719
- try {
1720
- const salt = deriveAccountSalt(input);
1721
- debugLog(debug, "account-setup", "setup:start", {
1722
- salt,
1723
- recipient: input.recipient,
1724
- targetChain: input.targetChain,
1725
- forceRegister: input.forceRegister ?? false
1726
- });
1727
- const result = await service.registerManagedAccount({
1728
- salt,
1729
- target: {
1730
- chain: targetChainToCaip2(input.targetChain),
1731
- token: input.targetToken,
1732
- recipient: input.recipient,
1733
- ...input.outputTokenRules?.length && {
1734
- outputTokenRules: input.outputTokenRules
1735
- },
1736
- ...input.rejectUnmapped != null && {
1737
- rejectUnmapped: input.rejectUnmapped
1738
- }
1739
- }
1740
- });
1741
- return {
1742
- smartAccount: result.evmDepositAddress,
1743
- solanaDepositAddress: result.solanaDepositAddress,
1744
- cacheKey
1745
- };
1746
- } catch (error) {
1747
- debugError(debug, "account-setup", "setup:failed", error, {
1748
- recipient: input.recipient
1749
- });
1750
- if (error instanceof SetupError) throw error;
1751
- throw mapError(error);
1752
- }
1753
- }
1754
- function runAccountSetupDeduped(input, deps, requestKey) {
1755
- const now = Date.now();
1756
- const existing = setupRequestDedupe.get(requestKey);
1757
- if (existing && (!existing.settled || existing.expiresAt > now)) {
1758
- debugLog(deps.debug, "account-setup", "setup:dedupe-hit", {
1759
- recipient: input.recipient,
1760
- forceRegister: input.forceRegister ?? false
1761
- });
1762
- return existing.promise;
1763
- }
1764
- const entry = {
1765
- promise: runAccountSetup(input, deps),
1766
- settled: false,
1767
- expiresAt: Number.POSITIVE_INFINITY,
1768
- result: null
1769
- };
1770
- setupRequestDedupe.set(requestKey, entry);
1771
- entry.promise.then((result) => {
1772
- entry.settled = true;
1773
- entry.result = result;
1774
- entry.expiresAt = Date.now() + SETUP_REQUEST_DEDUPE_TTL_MS;
1775
- const cleanupTimer = setTimeout(() => {
1776
- if (setupRequestDedupe.get(requestKey) === entry) {
1777
- setupRequestDedupe.delete(requestKey);
1778
- }
1779
- }, SETUP_REQUEST_DEDUPE_TTL_MS);
1780
- if (typeof cleanupTimer === "object" && cleanupTimer && "unref" in cleanupTimer && typeof cleanupTimer.unref === "function") {
1781
- cleanupTimer.unref();
1782
- }
1783
- }).catch(() => {
1784
- if (setupRequestDedupe.get(requestKey) === entry) {
1785
- setupRequestDedupe.delete(requestKey);
1786
- }
1787
- });
1788
- return entry.promise;
1789
- }
1790
- function mapError(error) {
1791
- if (error instanceof TypeError) {
1792
- return new SetupError(
1793
- "Couldn't reach the deposit service. Please check your connection.",
1794
- "network"
1795
- );
1796
- }
1797
- if (error instanceof Error) {
1798
- const statusMatch = error.message.match(/\b(4\d{2}|5\d{2})\b/);
1799
- if (statusMatch) {
1800
- const status = Number(statusMatch[1]);
1801
- if (status >= 500) {
1802
- return new SetupError(
1803
- "Deposit service is temporarily unavailable. Please try again shortly.",
1804
- "server"
1805
- );
1806
- }
1807
- return new SetupError(error.message, "client");
1808
- }
1809
- return new SetupError(
1810
- "Deposit service is temporarily unavailable. Please try again shortly.",
1811
- "unknown"
1812
- );
1813
- }
1814
- return new SetupError(
1815
- "Deposit service is temporarily unavailable. Please try again shortly.",
1816
- "unknown"
1817
- );
1818
- }
1819
-
1820
- export {
1821
- getExchangeLogoSrc,
1822
- getExchangeLogo,
1823
- Tooltip,
1824
- markSubmissionUncertain,
1825
- isSubmissionUncertain,
1826
- getEventTxHash,
1827
- getEventSourceDetails,
1828
- isDepositEvent,
1829
- isFailedEvent,
1830
- txRefsMatch,
1831
- failureMessageForEvent,
1832
- fetchTokenPriceUsd,
1833
- useTokenPrices,
1834
- ProcessingStep,
1835
- SetupError,
1836
- deriveAccountSalt,
1837
- computeRequestKey,
1838
- computeCacheKey,
1839
- computeIsCacheable,
1840
- readDedupedAccountSetupResult,
1841
- runAccountSetupDeduped,
1842
- warnRemovedProps,
1843
- analyticsTargetProperties
1844
- };