@orderly.network/onramper-plugin 0.0.2-alpha.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 (81) hide show
  1. package/README.md +113 -0
  2. package/dist/chunk-QKQ47GQZ.js +11 -0
  3. package/dist/chunk-QKQ47GQZ.js.map +1 -0
  4. package/dist/chunk-XNMUIY72.mjs +9 -0
  5. package/dist/chunk-XNMUIY72.mjs.map +1 -0
  6. package/dist/de-GYR62SPI.js +36 -0
  7. package/dist/de-GYR62SPI.js.map +1 -0
  8. package/dist/de-XNITMI2B.mjs +34 -0
  9. package/dist/de-XNITMI2B.mjs.map +1 -0
  10. package/dist/en-IAAY2CDT.js +36 -0
  11. package/dist/en-IAAY2CDT.js.map +1 -0
  12. package/dist/en-UCLS4WAV.mjs +34 -0
  13. package/dist/en-UCLS4WAV.mjs.map +1 -0
  14. package/dist/es-76I5JYOI.mjs +34 -0
  15. package/dist/es-76I5JYOI.mjs.map +1 -0
  16. package/dist/es-B7VYU5TQ.js +36 -0
  17. package/dist/es-B7VYU5TQ.js.map +1 -0
  18. package/dist/fr-5G5K3WZB.js +36 -0
  19. package/dist/fr-5G5K3WZB.js.map +1 -0
  20. package/dist/fr-YBDFJ5YI.mjs +34 -0
  21. package/dist/fr-YBDFJ5YI.mjs.map +1 -0
  22. package/dist/id-RPSFROFO.js +36 -0
  23. package/dist/id-RPSFROFO.js.map +1 -0
  24. package/dist/id-TWEEXYGV.mjs +34 -0
  25. package/dist/id-TWEEXYGV.mjs.map +1 -0
  26. package/dist/index.d.mts +158 -0
  27. package/dist/index.d.ts +158 -0
  28. package/dist/index.js +1863 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/index.mjs +1851 -0
  31. package/dist/index.mjs.map +1 -0
  32. package/dist/it-64LYBKQP.js +36 -0
  33. package/dist/it-64LYBKQP.js.map +1 -0
  34. package/dist/it-ESHQ6ZEW.mjs +34 -0
  35. package/dist/it-ESHQ6ZEW.mjs.map +1 -0
  36. package/dist/ja-AMRP6CIA.js +36 -0
  37. package/dist/ja-AMRP6CIA.js.map +1 -0
  38. package/dist/ja-TTBR7X25.mjs +34 -0
  39. package/dist/ja-TTBR7X25.mjs.map +1 -0
  40. package/dist/ko-BTMZAQ7J.js +36 -0
  41. package/dist/ko-BTMZAQ7J.js.map +1 -0
  42. package/dist/ko-PUJOVSVE.mjs +34 -0
  43. package/dist/ko-PUJOVSVE.mjs.map +1 -0
  44. package/dist/nl-BZB2BRQ7.js +36 -0
  45. package/dist/nl-BZB2BRQ7.js.map +1 -0
  46. package/dist/nl-SENDBWKK.mjs +34 -0
  47. package/dist/nl-SENDBWKK.mjs.map +1 -0
  48. package/dist/pl-GGYFSVAJ.mjs +34 -0
  49. package/dist/pl-GGYFSVAJ.mjs.map +1 -0
  50. package/dist/pl-ORAYIBD7.js +36 -0
  51. package/dist/pl-ORAYIBD7.js.map +1 -0
  52. package/dist/pt-RWI3GBG6.js +36 -0
  53. package/dist/pt-RWI3GBG6.js.map +1 -0
  54. package/dist/pt-S4FOVI3A.mjs +34 -0
  55. package/dist/pt-S4FOVI3A.mjs.map +1 -0
  56. package/dist/ru-AP7GWUPY.js +36 -0
  57. package/dist/ru-AP7GWUPY.js.map +1 -0
  58. package/dist/ru-ZF4FQ3S2.mjs +34 -0
  59. package/dist/ru-ZF4FQ3S2.mjs.map +1 -0
  60. package/dist/styles.css +1 -0
  61. package/dist/tc-RZ4GC2Z6.js +36 -0
  62. package/dist/tc-RZ4GC2Z6.js.map +1 -0
  63. package/dist/tc-TQ4TWJLG.mjs +34 -0
  64. package/dist/tc-TQ4TWJLG.mjs.map +1 -0
  65. package/dist/tr-E6HTGNQA.js +36 -0
  66. package/dist/tr-E6HTGNQA.js.map +1 -0
  67. package/dist/tr-RM566BKR.mjs +34 -0
  68. package/dist/tr-RM566BKR.mjs.map +1 -0
  69. package/dist/uk-6FVT4QXK.js +36 -0
  70. package/dist/uk-6FVT4QXK.js.map +1 -0
  71. package/dist/uk-K25BMMM3.mjs +34 -0
  72. package/dist/uk-K25BMMM3.mjs.map +1 -0
  73. package/dist/vi-KGVGSDIB.js +36 -0
  74. package/dist/vi-KGVGSDIB.js.map +1 -0
  75. package/dist/vi-MTEYJND7.mjs +34 -0
  76. package/dist/vi-MTEYJND7.mjs.map +1 -0
  77. package/dist/zh-A3GFFBKX.js +36 -0
  78. package/dist/zh-A3GFFBKX.js.map +1 -0
  79. package/dist/zh-JDOWM3XW.mjs +34 -0
  80. package/dist/zh-JDOWM3XW.mjs.map +1 -0
  81. package/package.json +47 -0
package/dist/index.js ADDED
@@ -0,0 +1,1863 @@
1
+ 'use strict';
2
+
3
+ var chunkQKQ47GQZ_js = require('./chunk-QKQ47GQZ.js');
4
+ var react = require('react');
5
+ var i18n = require('@orderly.network/i18n');
6
+ var ui = require('@orderly.network/ui');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+ var ethers = require('ethers');
9
+ var hooks = require('@orderly.network/hooks');
10
+ var types = require('@orderly.network/types');
11
+ var utils = require('@orderly.network/utils');
12
+ var pluginCore = require('@orderly.network/plugin-core');
13
+
14
+ // src/constants.ts
15
+ var FIAT_CURRENCIES = [
16
+ "USD",
17
+ "EUR",
18
+ "CNY",
19
+ "HKD",
20
+ "TWD",
21
+ "KRW",
22
+ "JPY"
23
+ ];
24
+ var PRESET_AMOUNTS = {
25
+ USD: [100, 500, 1e3, 5e3],
26
+ EUR: [100, 500, 1e3, 5e3],
27
+ CNY: [1e3, 5e3, 1e4, 5e4],
28
+ HKD: [1e3, 5e3, 1e4, 5e4],
29
+ TWD: [5e3, 2e4, 5e4, 2e5],
30
+ KRW: [2e5, 5e5, 1e6, 5e6],
31
+ JPY: [2e4, 5e4, 1e5, 5e5]
32
+ };
33
+ var ONRAMPER_WIDGET_BASE = "https://buy.onramper.com";
34
+ function formatCompact(n) {
35
+ const suffixes = [
36
+ [1e9, "B"],
37
+ [1e6, "M"],
38
+ [1e3, "K"]
39
+ ];
40
+ for (const [threshold, suffix] of suffixes) {
41
+ if (n >= threshold) {
42
+ const val = n / threshold;
43
+ return (val % 1 === 0 ? val.toString() : val.toFixed(1)) + suffix;
44
+ }
45
+ }
46
+ return n.toLocaleString();
47
+ }
48
+ var ExchangeIcon = (props) => /* @__PURE__ */ jsxRuntime.jsx(
49
+ "svg",
50
+ {
51
+ width: "12",
52
+ height: "12",
53
+ viewBox: "0 0 12 12",
54
+ fill: "currentColor",
55
+ xmlns: "http://www.w3.org/2000/svg",
56
+ ...props,
57
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10.997 8.004a.5.5 0 0 0-.14-.36l-1.86-1.843-.703.703.984 1h-7.28a.5.5 0 0 0 0 1h7.28l-.984 1 .703.703 1.86-1.844a.5.5 0 0 0 .14-.36m-.5-4a.5.5 0 0 0-.5-.5H2.716l.984-1-.703-.703-1.859 1.843a.515.515 0 0 0 0 .719l1.86 1.844.702-.703-.984-1h7.281a.5.5 0 0 0 .5-.5" })
58
+ }
59
+ );
60
+ var ArrowDownIcon = (props) => /* @__PURE__ */ jsxRuntime.jsx(
61
+ "svg",
62
+ {
63
+ width: "20",
64
+ height: "21",
65
+ viewBox: "0 0 20 21",
66
+ fill: "currentColor",
67
+ xmlns: "http://www.w3.org/2000/svg",
68
+ ...props,
69
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9.994 5.51a.83.83 0 0 0-.832.833v6.295l-2.498-2.471-1.17 1.17 3.902 3.929a.84.84 0 0 0 .599.244.84.84 0 0 0 .597-.244l3.903-3.928-1.171-1.171-2.498 2.471V6.343a.83.83 0 0 0-.832-.833" })
70
+ }
71
+ );
72
+ var BuyCryptoIcon = (props) => /* @__PURE__ */ jsxRuntime.jsx(
73
+ "svg",
74
+ {
75
+ width: "11",
76
+ height: "11",
77
+ viewBox: "0 0 11 11",
78
+ fill: "none",
79
+ xmlns: "http://www.w3.org/2000/svg",
80
+ ...props,
81
+ children: /* @__PURE__ */ jsxRuntime.jsx(
82
+ "path",
83
+ {
84
+ fillRule: "evenodd",
85
+ clipRule: "evenodd",
86
+ d: "M0 2.33333C0 1.04475 1.04475 0 2.33333 0H8.16667C9.45525 0 10.5 1.04475 10.5 2.33333V8.16667C10.5 9.45525 9.45525 10.5 8.16667 10.5H2.33333C1.04475 10.5 0 9.45525 0 8.16667V2.33333ZM7 5.83333H5.83333V7C5.83333 7.322 5.572 7.58333 5.25 7.58333C4.928 7.58333 4.66667 7.322 4.66667 7V5.83333H3.5C3.178 5.83333 2.91667 5.572 2.91667 5.25C2.91667 4.928 3.178 4.66667 3.5 4.66667H4.66667V3.5C4.66667 3.178 4.928 2.91667 5.25 2.91667C5.572 2.91667 5.83333 3.178 5.83333 3.5V4.66667H7C7.322 4.66667 7.58333 4.928 7.58333 5.25C7.58333 5.572 7.322 5.83333 7 5.83333Z",
87
+ fill: "currentColor"
88
+ }
89
+ )
90
+ }
91
+ );
92
+ var ChainSelect = (props) => {
93
+ const { chains, value, wrongNetwork, loading, disabled } = props;
94
+ const [open, setOpen] = react.useState(false);
95
+ const { t } = i18n.useTranslation();
96
+ const selectable = wrongNetwork || chains?.length > 1;
97
+ const chainIcon = wrongNetwork ? /* @__PURE__ */ jsxRuntime.jsx(
98
+ ui.Flex,
99
+ {
100
+ width: 18,
101
+ height: 18,
102
+ intensity: 100,
103
+ r: "full",
104
+ justify: "center",
105
+ itemAlign: "center",
106
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 80, children: "U" })
107
+ }
108
+ ) : /* @__PURE__ */ jsxRuntime.jsx(ui.ChainIcon, { className: "oui-size-[18px]", chainId: value?.id });
109
+ const chainName = wrongNetwork ? t("onramp.unknownNetwork") : value?.info?.network_infos?.name;
110
+ const currentChain = chains.find((chain) => chain.chain_id === value?.id);
111
+ const extendedCurrentChain = currentChain;
112
+ const isCurrentChainSupported = !currentChain || extendedCurrentChain?.isSupported !== false;
113
+ const renderRightIcon = () => {
114
+ if (loading) {
115
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { size: "sm" });
116
+ }
117
+ if (selectable) {
118
+ return /* @__PURE__ */ jsxRuntime.jsx(ExchangeIcon, { className: "oui-text-base-contrast-54" });
119
+ }
120
+ };
121
+ const trigger = /* @__PURE__ */ jsxRuntime.jsxs(
122
+ ui.Flex,
123
+ {
124
+ intensity: 500,
125
+ className: ui.cn(
126
+ "oui-rounded-b-sm oui-rounded-t-xl oui-border oui-border-line",
127
+ disabled ? "oui-cursor-not-allowed" : selectable ? "oui-cursor-pointer" : "oui-cursor-auto",
128
+ props.className
129
+ ),
130
+ height: 54,
131
+ px: 3,
132
+ justify: "between",
133
+ itemAlign: "center",
134
+ children: [
135
+ /* @__PURE__ */ jsxRuntime.jsxs(
136
+ ui.Flex,
137
+ {
138
+ direction: "column",
139
+ itemAlign: "start",
140
+ justify: "center",
141
+ className: "oui-gap-0",
142
+ children: [
143
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { className: "oui-h-[16px] oui-items-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 54, children: t("transfer.network") }) }),
144
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gapX: 1, itemAlign: "center", className: "oui-mt-0.5 oui-h-[20px]", children: [
145
+ chainIcon,
146
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "sm", intensity: 80, className: "oui-leading-none", children: chainName }),
147
+ !isCurrentChainSupported && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "danger", size: "xs", children: t("common.notSupported") })
148
+ ] })
149
+ ]
150
+ }
151
+ ),
152
+ renderRightIcon()
153
+ ]
154
+ }
155
+ );
156
+ const content = chains.map((chain, index) => {
157
+ const extendedChain = chain;
158
+ const isActive = chain.chain_id === value?.id;
159
+ const isSupported = extendedChain.isSupported !== false;
160
+ return /* @__PURE__ */ jsxRuntime.jsxs(
161
+ ui.Flex,
162
+ {
163
+ px: 2,
164
+ r: "base",
165
+ justify: "between",
166
+ itemAlign: "center",
167
+ className: ui.cn(
168
+ "oui-deposit-network-select-item",
169
+ "oui-h-[30px]",
170
+ isSupported ? "oui-cursor-pointer hover:oui-bg-base-5" : "oui-cursor-not-allowed",
171
+ isActive && "oui-bg-base-5",
172
+ index !== 0 && "oui-mt-[2px]"
173
+ ),
174
+ onClick: async () => {
175
+ if (!isSupported) return;
176
+ setOpen(false);
177
+ await props.onValueChange(chain);
178
+ },
179
+ children: [
180
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gapX: 1, itemAlign: "center", children: [
181
+ /* @__PURE__ */ jsxRuntime.jsx(
182
+ ui.ChainIcon,
183
+ {
184
+ className: ui.cn("oui-size-[18px]", !isSupported && "oui-opacity-50"),
185
+ chainId: chain.chain_id
186
+ }
187
+ ),
188
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: isSupported ? 54 : 36, children: chain.name }),
189
+ !isSupported && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "danger", size: "xs", children: t("common.notSupported") })
190
+ ] }),
191
+ isActive && /* @__PURE__ */ jsxRuntime.jsx(
192
+ ui.Box,
193
+ {
194
+ width: 4,
195
+ height: 4,
196
+ r: "full",
197
+ className: "oui-deposit-network-select-active-dot oui-bg-[linear-gradient(270deg,#59B0FE_0%,#26FEFE_100%)]"
198
+ }
199
+ )
200
+ ]
201
+ },
202
+ chain.chain_id
203
+ );
204
+ });
205
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuRoot, { open: selectable ? open : false, onOpenChange: setOpen, children: [
206
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuTrigger, { disabled, asChild: true, children: trigger }),
207
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuPortal, { children: /* @__PURE__ */ jsxRuntime.jsx(
208
+ ui.DropdownMenuContent,
209
+ {
210
+ onCloseAutoFocus: (e) => e.preventDefault(),
211
+ align: "start",
212
+ sideOffset: 2,
213
+ className: ui.cn(
214
+ "oui-deposit-token-select-dropdown-menu-content",
215
+ "oui-bg-base-8 oui-p-1",
216
+ "oui-w-[var(--radix-dropdown-menu-trigger-width)]",
217
+ "oui-select-none oui-rounded-md"
218
+ ),
219
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.ScrollArea, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oui-max-h-[110px]", children: content }) })
220
+ }
221
+ ) })
222
+ ] });
223
+ };
224
+ var ExchangeDivider = ({ icon }) => {
225
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { height: 40, gapX: 3, className: "oui-w-full", children: [
226
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { height: 1, className: "oui-flex-1 oui-bg-base-contrast-12" }),
227
+ icon || /* @__PURE__ */ jsxRuntime.jsx(ArrowDownIcon, { className: "oui-text-primary-light" }),
228
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { height: 1, className: "oui-flex-1 oui-bg-base-contrast-12" })
229
+ ] });
230
+ };
231
+ var HistoryDropdown = ({
232
+ transactions,
233
+ pendingTransactions,
234
+ containerRef
235
+ }) => {
236
+ const { t } = i18n.useTranslation();
237
+ if (transactions.length === 0) {
238
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { justify: "between", itemAlign: "center", className: "oui-w-full", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "sm", intensity: 98, weight: "semibold", children: t("onramp.youSpend") }) });
239
+ }
240
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuRoot, { children: [
241
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "between", itemAlign: "center", className: "oui-w-full", children: [
242
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "sm", intensity: 98, children: t("onramp.youSpend") }),
243
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
244
+ ui.Flex,
245
+ {
246
+ gap: 1,
247
+ itemAlign: "center",
248
+ className: "oui-group oui-cursor-pointer",
249
+ children: [
250
+ pendingTransactions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { itemAlign: "center", className: "oui-gap-1.5", children: [
251
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { size: "sm", className: "oui-text-primaryLight" }),
252
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 54, children: pendingTransactions.length === 1 ? t("onramp.pendingPurchase", {
253
+ count: pendingTransactions.length
254
+ }) : t("onramp.pendingPurchases", {
255
+ count: pendingTransactions.length
256
+ }) })
257
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xs", intensity: 54, children: t("onramp.history") }),
258
+ /* @__PURE__ */ jsxRuntime.jsx(
259
+ ui.CaretDownIcon,
260
+ {
261
+ size: 10,
262
+ className: "oui-text-base-contrast-54 oui-transition-transform group-data-[state=open]:oui-rotate-180",
263
+ opacity: 1
264
+ }
265
+ )
266
+ ]
267
+ }
268
+ ) })
269
+ ] }),
270
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuPortal, { children: /* @__PURE__ */ jsxRuntime.jsx(
271
+ ui.DropdownMenuContent,
272
+ {
273
+ align: "end",
274
+ className: "oui-text-semibold oui-custom-scrollbar oui-z-50 oui-max-h-[105px] oui-overflow-y-auto oui-rounded-lg oui-border oui-border-line-12 oui-bg-base-8 oui-p-1 oui-shadow-lg",
275
+ style: containerRef?.current ? { width: containerRef.current.offsetWidth } : void 0,
276
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { direction: "column", gap: 1, children: transactions.map((tx) => /* @__PURE__ */ jsxRuntime.jsx(
277
+ ui.DropdownMenuItem,
278
+ {
279
+ className: "oui-flex oui-w-full oui-cursor-default oui-items-start oui-justify-between oui-rounded-md oui-px-2 oui-py-0.5 oui-outline-none focus:oui-bg-base-6 data-[disabled]:oui-pointer-events-none data-[disabled]:oui-opacity-50",
280
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oui-grid oui-w-full oui-grid-cols-[0.5fr_1fr_1fr_1.2fr_minmax(auto,max-content)] oui-items-center oui-gap-1", children: [
281
+ /* @__PURE__ */ jsxRuntime.jsx(
282
+ ui.Text,
283
+ {
284
+ size: "xs",
285
+ weight: "semibold",
286
+ className: "oui-truncate oui-whitespace-nowrap oui-uppercase oui-text-base-contrast-80",
287
+ children: tx.sourceCurrency
288
+ }
289
+ ),
290
+ /* @__PURE__ */ jsxRuntime.jsx(
291
+ ui.Text,
292
+ {
293
+ size: "xs",
294
+ weight: "semibold",
295
+ className: "oui-truncate oui-whitespace-nowrap",
296
+ children: tx.inAmount
297
+ }
298
+ ),
299
+ /* @__PURE__ */ jsxRuntime.jsx(
300
+ ui.Text,
301
+ {
302
+ size: "xs",
303
+ intensity: 54,
304
+ className: "oui-truncate oui-whitespace-nowrap oui-capitalize",
305
+ children: tx.onramp
306
+ }
307
+ ),
308
+ /* @__PURE__ */ jsxRuntime.jsx(
309
+ ui.Text,
310
+ {
311
+ size: "xs",
312
+ weight: "semibold",
313
+ className: ui.cn(
314
+ "oui-truncate oui-whitespace-nowrap oui-capitalize",
315
+ tx.status === "failed" || tx.status === "canceled" ? "oui-text-danger" : tx.status === "pending" || tx.status === "new" ? "oui-text-warning" : "oui-text-success"
316
+ ),
317
+ children: tx.status === "paid" ? t("onramp.statusCompleted") : tx.status
318
+ }
319
+ ),
320
+ /* @__PURE__ */ jsxRuntime.jsx(
321
+ ui.Text,
322
+ {
323
+ size: "xs",
324
+ intensity: 36,
325
+ className: "oui-whitespace-nowrap oui-text-right",
326
+ children: new Date(tx.statusDate).toLocaleString("en-US", {
327
+ month: "short",
328
+ day: "numeric",
329
+ hour: "2-digit",
330
+ minute: "2-digit"
331
+ })
332
+ }
333
+ )
334
+ ] })
335
+ },
336
+ tx.transactionId
337
+ )) })
338
+ }
339
+ ) })
340
+ ] });
341
+ };
342
+ var PartnerSelect = ({
343
+ partners,
344
+ value,
345
+ onValueChange,
346
+ containerRef
347
+ }) => {
348
+ const { t } = i18n.useTranslation();
349
+ const [open, setOpen] = react.useState(false);
350
+ const selectable = partners.length > 1;
351
+ const triggerRef = react.useRef(null);
352
+ const getAlignOffset = () => {
353
+ if (!containerRef?.current || !triggerRef.current) return 0;
354
+ const containerRect = containerRef.current.getBoundingClientRect();
355
+ const triggerRect = triggerRef.current.getBoundingClientRect();
356
+ return triggerRect.right - containerRect.right;
357
+ };
358
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuRoot, { open: selectable ? open : false, onOpenChange: setOpen, children: [
359
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuTrigger, { children: /* @__PURE__ */ jsxRuntime.jsxs(
360
+ ui.Flex,
361
+ {
362
+ ref: triggerRef,
363
+ itemAlign: "center",
364
+ gap: 1,
365
+ className: "oui-cursor-pointer oui-select-none",
366
+ children: [
367
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 54, weight: "semibold", children: t("onramp.by") }),
368
+ /* @__PURE__ */ jsxRuntime.jsx(
369
+ "img",
370
+ {
371
+ alt: value.name,
372
+ src: `https://cdn.onramper.com/icons/onramps/${value.id.toLowerCase()}-colored.svg`,
373
+ className: "oui-size-4 oui-rounded-full oui-object-cover"
374
+ }
375
+ ),
376
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 54, weight: "semibold", children: value.name }),
377
+ selectable && /* @__PURE__ */ jsxRuntime.jsx(
378
+ ui.CaretDownIcon,
379
+ {
380
+ size: 10,
381
+ className: "oui-text-base-contrast-54",
382
+ opacity: 1
383
+ }
384
+ )
385
+ ]
386
+ }
387
+ ) }),
388
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuPortal, { children: /* @__PURE__ */ jsxRuntime.jsx(
389
+ ui.DropdownMenuContent,
390
+ {
391
+ onCloseAutoFocus: (e) => e.preventDefault(),
392
+ align: "end",
393
+ sideOffset: 10,
394
+ alignOffset: getAlignOffset(),
395
+ className: ui.cn(
396
+ "oui-z-50 oui-bg-base-8 oui-p-1",
397
+ "oui-min-w-[270px] oui-select-none oui-rounded-[6px]",
398
+ "oui-border oui-border-line-6"
399
+ ),
400
+ style: containerRef?.current ? { width: containerRef.current.offsetWidth } : void 0,
401
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.ScrollArea, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oui-flex oui-max-h-[220px] oui-flex-col oui-gap-1", children: partners.map((partner) => {
402
+ const isActive = partner.id === value.id;
403
+ const diffPercentage = value.payout === 0 ? 0 : (partner.payout - value.payout) / value.payout * 100;
404
+ return /* @__PURE__ */ jsxRuntime.jsxs(
405
+ ui.Flex,
406
+ {
407
+ p: 2,
408
+ justify: "between",
409
+ itemAlign: "center",
410
+ className: ui.cn(
411
+ "oui-min-h-[56px] oui-cursor-pointer oui-rounded-[4px] oui-transition-colors hover:oui-bg-base-6",
412
+ isActive ? "oui-bg-base-5" : "oui-bg-transparent"
413
+ ),
414
+ onClick: () => {
415
+ onValueChange(partner);
416
+ setOpen(false);
417
+ },
418
+ children: [
419
+ /* @__PURE__ */ jsxRuntime.jsxs(
420
+ ui.Flex,
421
+ {
422
+ direction: "column",
423
+ itemAlign: "start",
424
+ justify: "center",
425
+ gap: 1,
426
+ className: "oui-flex-[3]",
427
+ children: [
428
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 1, itemAlign: "center", children: [
429
+ /* @__PURE__ */ jsxRuntime.jsx(
430
+ "img",
431
+ {
432
+ alt: partner.name,
433
+ src: `https://cdn.onramper.com/icons/onramps/${partner.id.toLowerCase()}-colored.svg`,
434
+ className: "oui-size-4 oui-rounded-full oui-object-cover",
435
+ onError: (e) => {
436
+ e.target.src = "https://cdn.onramper.com/icons/tokens/usdc.svg";
437
+ }
438
+ }
439
+ ),
440
+ /* @__PURE__ */ jsxRuntime.jsx(
441
+ ui.Text,
442
+ {
443
+ size: "sm",
444
+ intensity: 54,
445
+ weight: "semibold",
446
+ className: "oui-leading-5 oui-tracking-[0.42px]",
447
+ children: partner.name
448
+ }
449
+ )
450
+ ] }),
451
+ partner.recommendations && partner.recommendations.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { className: "oui-flex-wrap oui-gap-1", children: partner.recommendations.map((rec, i) => /* @__PURE__ */ jsxRuntime.jsx(
452
+ ui.Flex,
453
+ {
454
+ px: 1,
455
+ py: 1,
456
+ className: ui.cn(
457
+ "oui-rounded-[4px] oui-bg-gradient-to-r",
458
+ "oui-from-[rgb(var(--oui-gradient-brand-start)_/_0.12)]",
459
+ "oui-to-[rgb(var(--oui-gradient-brand-end)_/_0.12)]"
460
+ ),
461
+ children: /* @__PURE__ */ jsxRuntime.jsx(
462
+ ui.Text.gradient,
463
+ {
464
+ size: "3xs",
465
+ weight: "semibold",
466
+ color: "brand",
467
+ className: "oui-leading-[10px] oui-tracking-[0.3px]",
468
+ children: rec
469
+ }
470
+ )
471
+ },
472
+ i
473
+ )) }) })
474
+ ]
475
+ }
476
+ ),
477
+ /* @__PURE__ */ jsxRuntime.jsx(
478
+ ui.Flex,
479
+ {
480
+ direction: "column",
481
+ itemAlign: "end",
482
+ justify: "center",
483
+ gap: 1,
484
+ className: "oui-flex-[2] oui-text-xs oui-font-semibold oui-tracking-[0.36px]",
485
+ children: isActive ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
486
+ /* @__PURE__ */ jsxRuntime.jsx(
487
+ ui.Text,
488
+ {
489
+ intensity: 36,
490
+ className: "oui-text-right oui-leading-[15px]",
491
+ children: t("onramp.youReceive")
492
+ }
493
+ ),
494
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { intensity: 98, className: "oui-leading-[15px]", children: [
495
+ partner.payout.toFixed(2),
496
+ " USDC"
497
+ ] })
498
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
499
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { intensity: 54, className: "oui-leading-[15px]", children: [
500
+ partner.payout.toFixed(2),
501
+ " USDC"
502
+ ] }),
503
+ /* @__PURE__ */ jsxRuntime.jsxs(
504
+ ui.Text,
505
+ {
506
+ className: ui.cn(
507
+ "oui-text-right oui-leading-[15px]",
508
+ diffPercentage < 0 ? "oui-text-danger" : diffPercentage > 0 ? "oui-text-success" : "oui-text-base-contrast-54"
509
+ ),
510
+ children: [
511
+ diffPercentage > 0 ? "+" : "",
512
+ diffPercentage.toFixed(2),
513
+ "%"
514
+ ]
515
+ }
516
+ )
517
+ ] })
518
+ }
519
+ )
520
+ ]
521
+ },
522
+ partner.id
523
+ );
524
+ }) }) })
525
+ }
526
+ ) })
527
+ ] });
528
+ };
529
+ var PaymentMethodSelect = (props) => {
530
+ const { t } = i18n.useTranslation();
531
+ const { methods, value, disabled, loading } = props;
532
+ const [open, setOpen] = react.useState(false);
533
+ const selectable = methods.length > 1;
534
+ const renderRightIcon = () => {
535
+ if (selectable) {
536
+ return /* @__PURE__ */ jsxRuntime.jsx(ExchangeIcon, { className: "oui-text-base-contrast-54" });
537
+ }
538
+ };
539
+ const trigger = /* @__PURE__ */ jsxRuntime.jsxs(
540
+ ui.Flex,
541
+ {
542
+ intensity: 500,
543
+ className: ui.cn(
544
+ "oui-rounded-[4px] oui-rounded-t-xl oui-border oui-border-line",
545
+ disabled ? "oui-cursor-not-allowed" : selectable ? "oui-cursor-pointer" : "oui-cursor-auto",
546
+ props.className
547
+ ),
548
+ height: 54,
549
+ px: 3,
550
+ justify: "between",
551
+ itemAlign: "center",
552
+ children: [
553
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
554
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 54, children: t("onramp.payWith") }) }),
555
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { gapX: 1, itemAlign: "center", children: loading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
556
+ value.icon && /* @__PURE__ */ jsxRuntime.jsx(
557
+ "img",
558
+ {
559
+ src: value.icon,
560
+ alt: value.name,
561
+ width: 16,
562
+ height: 16,
563
+ className: "oui-rounded-lg oui-bg-secondary oui-object-contain oui-p-px"
564
+ }
565
+ ),
566
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "sm", intensity: 80, children: value.name })
567
+ ] }) })
568
+ ] }),
569
+ renderRightIcon()
570
+ ]
571
+ }
572
+ );
573
+ const content = methods.map((method, index) => {
574
+ const isActive = method.id === value.id;
575
+ return /* @__PURE__ */ jsxRuntime.jsxs(
576
+ ui.Flex,
577
+ {
578
+ px: 2,
579
+ r: "base",
580
+ justify: "between",
581
+ itemAlign: "center",
582
+ className: ui.cn(
583
+ "oui-h-[30px] oui-cursor-pointer hover:oui-bg-base-5",
584
+ isActive && "oui-bg-base-5",
585
+ index !== 0 && "oui-mt-[2px]"
586
+ ),
587
+ onClick: () => {
588
+ setOpen(false);
589
+ props.onValueChange(method);
590
+ },
591
+ children: [
592
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gapX: 1, itemAlign: "center", children: [
593
+ method.icon && /* @__PURE__ */ jsxRuntime.jsx(
594
+ "img",
595
+ {
596
+ src: method.icon,
597
+ alt: method.name,
598
+ width: 14,
599
+ height: 14,
600
+ className: "oui-rounded-lg oui-bg-secondary oui-object-contain oui-p-px"
601
+ }
602
+ ),
603
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: isActive ? 80 : 54, children: method.name })
604
+ ] }),
605
+ isActive && /* @__PURE__ */ jsxRuntime.jsx(
606
+ ui.Box,
607
+ {
608
+ width: 4,
609
+ height: 4,
610
+ r: "full",
611
+ className: "oui-bg-[linear-gradient(270deg,#59B0FE_0%,#26FEFE_100%)]"
612
+ }
613
+ )
614
+ ]
615
+ },
616
+ method.id
617
+ );
618
+ });
619
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuRoot, { open: selectable ? open : false, onOpenChange: setOpen, children: [
620
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuTrigger, { disabled, asChild: true, children: trigger }),
621
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuPortal, { children: /* @__PURE__ */ jsxRuntime.jsx(
622
+ ui.DropdownMenuContent,
623
+ {
624
+ onCloseAutoFocus: (e) => e.preventDefault(),
625
+ align: "start",
626
+ sideOffset: 2,
627
+ className: ui.cn(
628
+ "oui-z-50 oui-bg-base-8 oui-p-1",
629
+ "oui-w-[var(--radix-dropdown-menu-trigger-width)]",
630
+ "oui-select-none oui-rounded-md"
631
+ ),
632
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.ScrollArea, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oui-max-h-[145px]", children: content }) })
633
+ }
634
+ ) })
635
+ ] });
636
+ };
637
+ var QuantityInput = (props) => {
638
+ const { t } = i18n.useTranslation();
639
+ const {
640
+ classNames,
641
+ label,
642
+ value,
643
+ onValueChange,
644
+ loading,
645
+ placeholder,
646
+ suffix,
647
+ error,
648
+ ...rest
649
+ } = props;
650
+ const inputRef = react.useRef(null);
651
+ const hasError = !!error;
652
+ const prefix = /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { children: [
653
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { className: "oui-absolute oui-top-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 54, children: label || t("onramp.quantity") }) }),
654
+ loading && /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { className: "oui-absolute oui-bottom-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { size: "sm" }) })
655
+ ] });
656
+ const _placeholder = placeholder ?? (loading ? "" : "0");
657
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { children: [
658
+ /* @__PURE__ */ jsxRuntime.jsx(
659
+ ui.Input,
660
+ {
661
+ ref: inputRef,
662
+ autoComplete: "off",
663
+ placeholder: _placeholder,
664
+ color: hasError ? "danger" : "default",
665
+ prefix,
666
+ suffix,
667
+ value,
668
+ onValueChange: (value2) => {
669
+ onValueChange?.(value2);
670
+ },
671
+ formatters: [
672
+ ui.inputFormatter.numberFormatter,
673
+ ui.inputFormatter.dpFormatter(2),
674
+ ui.inputFormatter.currencyFormatter
675
+ ],
676
+ ...rest,
677
+ classNames: {
678
+ ...classNames,
679
+ root: ui.cn(
680
+ "oui-relative oui-h-[54px] oui-px-3",
681
+ "oui-rounded-lg oui-border oui-border-line",
682
+ props.readOnly ? "oui-border-none oui-bg-base-6 focus-within:oui-outline-0" : "oui-bg-base-5",
683
+ classNames?.root
684
+ ),
685
+ input: ui.cn(
686
+ "oui-absolute oui-top-[25px] oui-h-[20px]",
687
+ classNames?.input
688
+ )
689
+ }
690
+ }
691
+ ),
692
+ hasError && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", className: "oui-mt-1 oui-px-1 oui-text-danger", children: error })
693
+ ] });
694
+ };
695
+ var CurrencySuffix = ({
696
+ currencies,
697
+ selected,
698
+ onSelect
699
+ }) => {
700
+ const [open, setOpen] = react.useState(false);
701
+ const selectable = currencies.length > 1;
702
+ const trigger = /* @__PURE__ */ jsxRuntime.jsxs(
703
+ ui.Flex,
704
+ {
705
+ itemAlign: "center",
706
+ gap: 1,
707
+ className: ui.cn(selectable && "oui-cursor-pointer"),
708
+ children: [
709
+ /* @__PURE__ */ jsxRuntime.jsx(
710
+ "img",
711
+ {
712
+ src: `https://cdn.onramper.com/icons/tokens/${selected.toLowerCase()}.svg`,
713
+ alt: selected,
714
+ className: "oui-size-4 oui-rounded-full"
715
+ }
716
+ ),
717
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "sm", intensity: 80, weight: "semibold", children: selected }),
718
+ selectable && /* @__PURE__ */ jsxRuntime.jsx(
719
+ ui.CaretDownIcon,
720
+ {
721
+ size: 10,
722
+ className: "oui-text-base-contrast-54",
723
+ opacity: 1
724
+ }
725
+ )
726
+ ]
727
+ }
728
+ );
729
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oui-absolute oui-right-3 oui-flex oui-h-[20px] oui-items-center", children: selectable ? /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuRoot, { open, onOpenChange: setOpen, children: [
730
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuTrigger, { asChild: true, children: trigger }),
731
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuPortal, { children: /* @__PURE__ */ jsxRuntime.jsx(
732
+ ui.DropdownMenuContent,
733
+ {
734
+ onCloseAutoFocus: (e) => e.preventDefault(),
735
+ align: "end",
736
+ sideOffset: 2,
737
+ className: ui.cn(
738
+ "oui-z-50 oui-bg-base-8 oui-p-1",
739
+ "oui-min-w-[80px]",
740
+ "oui-select-none oui-rounded-md"
741
+ ),
742
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.ScrollArea, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oui-max-h-[110px]", children: currencies.map((currency) => {
743
+ const isActive = currency === selected;
744
+ return /* @__PURE__ */ jsxRuntime.jsx(
745
+ ui.Flex,
746
+ {
747
+ px: 2,
748
+ r: "base",
749
+ justify: "between",
750
+ itemAlign: "center",
751
+ className: ui.cn(
752
+ "oui-h-[30px] oui-cursor-pointer hover:oui-bg-base-5",
753
+ isActive && "oui-bg-base-5",
754
+ "oui-mt-[2px]"
755
+ ),
756
+ onClick: () => {
757
+ setOpen(false);
758
+ onSelect(currency);
759
+ },
760
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 2, itemAlign: "center", children: [
761
+ /* @__PURE__ */ jsxRuntime.jsx(
762
+ "img",
763
+ {
764
+ src: `https://cdn.onramper.com/icons/tokens/${currency.toLowerCase()}.svg`,
765
+ alt: currency,
766
+ className: "oui-size-4 oui-rounded-full"
767
+ }
768
+ ),
769
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: isActive ? 80 : 54, children: currency })
770
+ ] })
771
+ },
772
+ currency
773
+ );
774
+ }) }) })
775
+ }
776
+ ) })
777
+ ] }) : trigger });
778
+ };
779
+ var TokenSuffix = ({ symbol }) => {
780
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oui-absolute oui-right-3 oui-flex oui-h-[20px] oui-items-center", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { itemAlign: "center", gap: 1, children: [
781
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TokenIcon, { name: symbol, size: "xs" }),
782
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "sm", intensity: 80, weight: "semibold", children: symbol })
783
+ ] }) });
784
+ };
785
+ var RADIUS = 6;
786
+ var TRACK_STROKE = 1.5;
787
+ var ARC_STROKE = 2;
788
+ var SIZE = (RADIUS + ARC_STROKE) * 2;
789
+ var CIRCUMFERENCE = 2 * Math.PI * RADIUS;
790
+ function createTickStore(duration) {
791
+ let elapsed = 0;
792
+ const listeners = /* @__PURE__ */ new Set();
793
+ let timer = null;
794
+ function start() {
795
+ stop();
796
+ elapsed = 0;
797
+ timer = setInterval(() => {
798
+ elapsed += 1;
799
+ if (elapsed >= duration) stop();
800
+ listeners.forEach((l) => l());
801
+ }, 1e3);
802
+ }
803
+ function stop() {
804
+ if (timer) {
805
+ clearInterval(timer);
806
+ timer = null;
807
+ }
808
+ }
809
+ return {
810
+ getSnapshot: () => elapsed,
811
+ subscribe: (cb) => {
812
+ listeners.add(cb);
813
+ if (listeners.size === 1) start();
814
+ return () => {
815
+ listeners.delete(cb);
816
+ if (listeners.size === 0) stop();
817
+ };
818
+ },
819
+ reset: () => start()
820
+ };
821
+ }
822
+ var store = createTickStore(30);
823
+ var QuoteCountdown = ({
824
+ duration = 30,
825
+ isValidating = false
826
+ }) => {
827
+ const prevValidatingRef = react.useRef(isValidating);
828
+ if (prevValidatingRef.current && !isValidating) {
829
+ store.reset();
830
+ }
831
+ prevValidatingRef.current = isValidating;
832
+ const subscribe = react.useCallback((cb) => store.subscribe(cb), []);
833
+ const getSnapshot = react.useCallback(() => store.getSnapshot(), []);
834
+ const elapsed = react.useSyncExternalStore(subscribe, getSnapshot);
835
+ const prevElapsedRef = react.useRef(elapsed);
836
+ const isResetting = elapsed < prevElapsedRef.current;
837
+ prevElapsedRef.current = elapsed;
838
+ const finished = elapsed >= duration;
839
+ const progress = finished ? 1 : elapsed / duration;
840
+ const dashOffset = CIRCUMFERENCE * (1 - progress);
841
+ const transitionDuration = isResetting ? "0.6s" : "0.2s";
842
+ return /* @__PURE__ */ jsxRuntime.jsxs(
843
+ "svg",
844
+ {
845
+ width: SIZE,
846
+ height: SIZE,
847
+ viewBox: `0 0 ${SIZE} ${SIZE}`,
848
+ className: "oui-shrink-0",
849
+ style: { transform: "rotate(-90deg)" },
850
+ children: [
851
+ /* @__PURE__ */ jsxRuntime.jsx(
852
+ "circle",
853
+ {
854
+ cx: SIZE / 2,
855
+ cy: SIZE / 2,
856
+ r: RADIUS,
857
+ fill: "none",
858
+ stroke: "rgba(255,255,255,0.1)",
859
+ strokeWidth: TRACK_STROKE
860
+ }
861
+ ),
862
+ /* @__PURE__ */ jsxRuntime.jsx(
863
+ "circle",
864
+ {
865
+ cx: SIZE / 2,
866
+ cy: SIZE / 2,
867
+ r: RADIUS,
868
+ fill: "none",
869
+ stroke: "rgb(var(--oui-color-primary-light))",
870
+ strokeWidth: ARC_STROKE,
871
+ strokeLinecap: "round",
872
+ strokeDasharray: CIRCUMFERENCE,
873
+ strokeDashoffset: dashOffset,
874
+ style: {
875
+ transition: `stroke-dashoffset ${transitionDuration} ease-in-out`
876
+ }
877
+ }
878
+ )
879
+ ]
880
+ }
881
+ );
882
+ };
883
+ var OnrampFormUI = (props) => {
884
+ const { t } = i18n.useTranslation();
885
+ const {
886
+ paymentMethods,
887
+ selectedPaymentMethod,
888
+ onPaymentMethodChange,
889
+ fiatCurrencies,
890
+ selectedCurrency,
891
+ onCurrencyChange,
892
+ spendAmount,
893
+ onSpendAmountChange,
894
+ presetAmounts,
895
+ chains,
896
+ selectedChain,
897
+ onChainChange,
898
+ wallet,
899
+ address,
900
+ receiveQuantity,
901
+ receiveQuantityPlaceholder,
902
+ partners,
903
+ selectedPartner,
904
+ onPartnerChange,
905
+ exchangeRateText,
906
+ isQuoteLoading,
907
+ onContinue,
908
+ isContinueDisabled,
909
+ iframeDialogOpen,
910
+ onramperIframeUrl,
911
+ quoteIsValidating,
912
+ spendAmountError,
913
+ transactions,
914
+ pendingTransactions
915
+ } = props;
916
+ const formRef = react.useRef(null);
917
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { id: "oui-onramp-form", className: ui.textVariants({ weight: "semibold" }), children: [
918
+ /* @__PURE__ */ jsxRuntime.jsxs(
919
+ ui.Flex,
920
+ {
921
+ ref: formRef,
922
+ direction: "column",
923
+ className: "oui-w-full oui-tracking-[0.03em]",
924
+ children: [
925
+ /* @__PURE__ */ jsxRuntime.jsxs(
926
+ ui.Flex,
927
+ {
928
+ direction: "column",
929
+ itemAlign: "start",
930
+ className: "oui-w-full",
931
+ gap: 3,
932
+ children: [
933
+ /* @__PURE__ */ jsxRuntime.jsx(
934
+ HistoryDropdown,
935
+ {
936
+ transactions,
937
+ pendingTransactions,
938
+ containerRef: formRef
939
+ }
940
+ ),
941
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", className: "oui-w-full", gap: 2, children: [
942
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { className: "oui-w-full", children: [
943
+ /* @__PURE__ */ jsxRuntime.jsx(
944
+ PaymentMethodSelect,
945
+ {
946
+ methods: selectedPaymentMethod ? paymentMethods : [],
947
+ value: selectedPaymentMethod ?? {
948
+ id: "_",
949
+ name: t("onramp.noPaymentMethod")
950
+ },
951
+ onValueChange: onPaymentMethodChange,
952
+ disabled: !selectedPaymentMethod,
953
+ loading: isQuoteLoading && !selectedPaymentMethod
954
+ }
955
+ ),
956
+ /* @__PURE__ */ jsxRuntime.jsx(
957
+ QuantityInput,
958
+ {
959
+ value: spendAmount,
960
+ onValueChange: onSpendAmountChange,
961
+ placeholder: presetAmounts[0].toString(),
962
+ error: spendAmountError,
963
+ suffix: /* @__PURE__ */ jsxRuntime.jsx(
964
+ CurrencySuffix,
965
+ {
966
+ currencies: fiatCurrencies,
967
+ selected: selectedCurrency,
968
+ onSelect: (c) => onCurrencyChange(c)
969
+ }
970
+ ),
971
+ classNames: {
972
+ root: "oui-mt-1 oui-rounded-t-[4px] oui-rounded-b-xl"
973
+ }
974
+ }
975
+ )
976
+ ] }),
977
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { gap: 3, className: "oui-w-full", children: presetAmounts.map((amount) => /* @__PURE__ */ jsxRuntime.jsx(
978
+ ui.Flex,
979
+ {
980
+ justify: "center",
981
+ itemAlign: "center",
982
+ className: ui.cn(
983
+ "oui-h-6 oui-flex-1 oui-cursor-pointer oui-rounded",
984
+ "oui-bg-base-6 oui-transition-colors hover:oui-bg-base-5"
985
+ ),
986
+ onClick: () => onSpendAmountChange(amount.toString()),
987
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 36, weight: "semibold", children: formatCompact(amount) })
988
+ },
989
+ amount
990
+ )) })
991
+ ] })
992
+ ]
993
+ }
994
+ ),
995
+ /* @__PURE__ */ jsxRuntime.jsx(ExchangeDivider, {}),
996
+ /* @__PURE__ */ jsxRuntime.jsxs(
997
+ ui.Flex,
998
+ {
999
+ direction: "column",
1000
+ itemAlign: "start",
1001
+ className: "oui-w-full",
1002
+ gap: 3,
1003
+ children: [
1004
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "between", className: "oui-w-full", children: [
1005
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "sm", intensity: 98, weight: "semibold", children: t("onramp.youReceive") }),
1006
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gapX: 1, children: [
1007
+ /* @__PURE__ */ jsxRuntime.jsx(ui.WalletIcon, { size: "2xs", name: wallet?.label ?? "" }),
1008
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text.formatted, { size: "sm", intensity: 54, rule: "address", children: address })
1009
+ ] })
1010
+ ] }),
1011
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", className: "oui-w-full", gap: 1, children: [
1012
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { className: "oui-w-full", gap: 1, children: [
1013
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { className: "oui-min-w-0 oui-flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
1014
+ ChainSelect,
1015
+ {
1016
+ chains,
1017
+ value: selectedChain,
1018
+ onValueChange: onChainChange,
1019
+ wrongNetwork: !selectedChain,
1020
+ className: "oui-w-full oui-rounded-[4px] oui-rounded-tl-xl"
1021
+ }
1022
+ ) }),
1023
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { className: "oui-min-w-0 oui-flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
1024
+ QuantityInput,
1025
+ {
1026
+ readOnly: true,
1027
+ loading: isQuoteLoading,
1028
+ value: isQuoteLoading ? "" : receiveQuantity,
1029
+ placeholder: isQuoteLoading ? "" : receiveQuantityPlaceholder,
1030
+ suffix: /* @__PURE__ */ jsxRuntime.jsx(TokenSuffix, { symbol: "USDC" }),
1031
+ classNames: {
1032
+ root: ui.cn(
1033
+ "oui-rounded-[4px] oui-rounded-b-sm oui-rounded-tr-xl",
1034
+ "oui-border-none oui-bg-base-6 focus-within:oui-outline-0"
1035
+ )
1036
+ }
1037
+ }
1038
+ ) })
1039
+ ] }),
1040
+ /* @__PURE__ */ jsxRuntime.jsxs(
1041
+ ui.Flex,
1042
+ {
1043
+ justify: "between",
1044
+ itemAlign: "center",
1045
+ className: ui.cn(
1046
+ "oui-w-full oui-rounded-b-xl oui-rounded-t-[4px]",
1047
+ "oui-bg-base-6 oui-px-3 oui-py-2"
1048
+ ),
1049
+ children: [
1050
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { itemAlign: "center", gap: 1, children: [
1051
+ exchangeRateText && /* @__PURE__ */ jsxRuntime.jsx(
1052
+ QuoteCountdown,
1053
+ {
1054
+ duration: 30,
1055
+ isValidating: quoteIsValidating
1056
+ }
1057
+ ),
1058
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 54, weight: "semibold", children: exchangeRateText || "\u2014" })
1059
+ ] }),
1060
+ selectedPartner ? /* @__PURE__ */ jsxRuntime.jsx(
1061
+ PartnerSelect,
1062
+ {
1063
+ partners,
1064
+ value: selectedPartner,
1065
+ onValueChange: onPartnerChange,
1066
+ containerRef: formRef
1067
+ }
1068
+ ) : isQuoteLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", intensity: 36, children: t("onramp.noPartner") })
1069
+ ]
1070
+ }
1071
+ )
1072
+ ] })
1073
+ ]
1074
+ }
1075
+ ),
1076
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { justify: "center", className: "oui-w-full oui-pt-7", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { className: "oui-w-full lg:oui-w-auto lg:oui-min-w-[184px]", children: /* @__PURE__ */ jsxRuntime.jsx(
1077
+ ui.Button,
1078
+ {
1079
+ fullWidth: true,
1080
+ size: { initial: "md", lg: "lg" },
1081
+ onClick: onContinue,
1082
+ disabled: isContinueDisabled,
1083
+ children: t("onramp.continue")
1084
+ }
1085
+ ) }) })
1086
+ ]
1087
+ }
1088
+ ),
1089
+ iframeDialogOpen && /* @__PURE__ */ jsxRuntime.jsx(ui.DialogPortal, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oui-fixed oui-inset-0 oui-z-50 oui-flex oui-flex-col oui-bg-black/80", children: /* @__PURE__ */ jsxRuntime.jsx(
1090
+ "iframe",
1091
+ {
1092
+ src: onramperIframeUrl,
1093
+ title: t("onramp.iframeTitle"),
1094
+ allow: "accelerometer; autoplay; camera; gyroscope; payment; microphone",
1095
+ className: "oui-w-full oui-flex-1 oui-border-none"
1096
+ }
1097
+ ) }) })
1098
+ ] });
1099
+ };
1100
+ var OnrampConfigContext = react.createContext(null);
1101
+ function OnrampConfigProvider({
1102
+ config,
1103
+ children
1104
+ }) {
1105
+ return /* @__PURE__ */ jsxRuntime.jsx(OnrampConfigContext.Provider, { value: config, children });
1106
+ }
1107
+ function useOnrampConfig() {
1108
+ const config = react.useContext(OnrampConfigContext);
1109
+ if (!config) {
1110
+ throw new Error("useOnrampConfig must be used within OnrampConfigProvider");
1111
+ }
1112
+ return config;
1113
+ }
1114
+ function arrangeStringAlphabetically(inputString) {
1115
+ const inputObject = {};
1116
+ inputString.split("&").forEach((pair) => {
1117
+ const [key, value] = pair.split("=");
1118
+ const nestedPairs = value.split(",");
1119
+ inputObject[key] = {};
1120
+ nestedPairs.forEach((nestedPair) => {
1121
+ const [nestedKey, nestedValue] = nestedPair.split(":");
1122
+ inputObject[key][nestedKey] = nestedValue;
1123
+ });
1124
+ });
1125
+ for (const key in inputObject) {
1126
+ inputObject[key] = Object.fromEntries(
1127
+ Object.entries(inputObject[key]).sort()
1128
+ );
1129
+ }
1130
+ const sortedKeys = Object.keys(inputObject).sort();
1131
+ const parts = [];
1132
+ for (const key of sortedKeys) {
1133
+ const nested = Object.entries(inputObject[key]).map(([k, v]) => `${k}:${v}`).join(",");
1134
+ parts.push(`${key}=${nested}`);
1135
+ }
1136
+ return parts.join("&");
1137
+ }
1138
+ function generateOnramperSignature(secretKey, data) {
1139
+ const encoder = new TextEncoder();
1140
+ const sig = ethers.computeHmac(
1141
+ "sha256",
1142
+ encoder.encode(secretKey),
1143
+ encoder.encode(data)
1144
+ );
1145
+ return sig.startsWith("0x") ? sig.slice(2) : sig;
1146
+ }
1147
+
1148
+ // src/utils/buildOnramperUrl.ts
1149
+ function buildOnramperIframeUrl({
1150
+ spendAmount,
1151
+ selectedCurrency,
1152
+ onramperToken,
1153
+ selectedPaymentMethod,
1154
+ selectedPartner,
1155
+ address,
1156
+ apiKey,
1157
+ secretKey
1158
+ }) {
1159
+ const params = new URLSearchParams();
1160
+ params.set("themeName", "dark");
1161
+ params.set("apiKey", apiKey);
1162
+ params.set("mode", "buy");
1163
+ params.set("skipTransactionScreen", "true");
1164
+ params.set("txnRedirect", "true");
1165
+ if (spendAmount) params.set("txnAmount", spendAmount);
1166
+ if (selectedCurrency) params.set("txnFiat", selectedCurrency.toLowerCase());
1167
+ if (onramperToken) params.set("txnCrypto", onramperToken);
1168
+ if (selectedPaymentMethod)
1169
+ params.set("txnPaymentMethod", selectedPaymentMethod.id);
1170
+ if (selectedPartner) params.set("txnOnramp", selectedPartner.id);
1171
+ if (address && onramperToken) {
1172
+ const walletsValue = `${onramperToken}:${address}`.toLowerCase();
1173
+ params.set("wallets", walletsValue);
1174
+ if (secretKey) {
1175
+ const signContent = arrangeStringAlphabetically(
1176
+ `wallets=${walletsValue}`
1177
+ );
1178
+ const signature = generateOnramperSignature(secretKey, signContent);
1179
+ params.set("signature", signature);
1180
+ }
1181
+ }
1182
+ return `${ONRAMPER_WIDGET_BASE}?${params.toString()}`;
1183
+ }
1184
+
1185
+ // src/hooks/useOnrampCheckout.ts
1186
+ function useOnrampCheckout({
1187
+ spendAmount,
1188
+ selectedCurrency,
1189
+ spendAmountError,
1190
+ onramperToken,
1191
+ selectedPartner,
1192
+ selectedPaymentMethod,
1193
+ address,
1194
+ isLoading
1195
+ }) {
1196
+ const { t } = i18n.useTranslation();
1197
+ const { apiKey, secretKey } = useOnrampConfig();
1198
+ const [iframeDialogOpen, setIframeDialogOpen] = react.useState(false);
1199
+ const onramperIframeUrl = react.useMemo(
1200
+ () => buildOnramperIframeUrl({
1201
+ spendAmount,
1202
+ selectedCurrency,
1203
+ onramperToken,
1204
+ selectedPaymentMethod,
1205
+ selectedPartner,
1206
+ address,
1207
+ apiKey,
1208
+ secretKey
1209
+ }),
1210
+ [
1211
+ spendAmount,
1212
+ selectedCurrency,
1213
+ onramperToken,
1214
+ selectedPaymentMethod,
1215
+ selectedPartner,
1216
+ address,
1217
+ apiKey,
1218
+ secretKey
1219
+ ]
1220
+ );
1221
+ const isContinueDisabled = react.useMemo(
1222
+ () => !spendAmount || isNaN(parseFloat(spendAmount)) || parseFloat(spendAmount) <= 0 || !selectedCurrency || !onramperToken || !selectedPartner || !selectedPaymentMethod || !address || isLoading || !!spendAmountError,
1223
+ [
1224
+ spendAmount,
1225
+ selectedCurrency,
1226
+ spendAmountError,
1227
+ onramperToken,
1228
+ selectedPartner,
1229
+ selectedPaymentMethod,
1230
+ address,
1231
+ isLoading
1232
+ ]
1233
+ );
1234
+ const onContinue = react.useCallback(() => {
1235
+ const missing = [];
1236
+ const num = parseFloat(spendAmount);
1237
+ if (!spendAmount || isNaN(num) || num <= 0)
1238
+ missing.push(t("onramp.missing.spendAmount"));
1239
+ if (!selectedCurrency)
1240
+ missing.push(t("onramp.missing.currency"));
1241
+ if (!onramperToken)
1242
+ missing.push(t("onramp.missing.network"));
1243
+ if (!selectedPartner)
1244
+ missing.push(t("onramp.missing.partner"));
1245
+ if (!selectedPaymentMethod)
1246
+ missing.push(t("onramp.missing.paymentMethod"));
1247
+ if (!address)
1248
+ missing.push(t("onramp.missing.address"));
1249
+ if (missing.length > 0) {
1250
+ const msg = t("onramp.missingRequiredInfo", {
1251
+ list: missing.join(", ")
1252
+ });
1253
+ ui.toast.error(msg);
1254
+ return;
1255
+ }
1256
+ setIframeDialogOpen(true);
1257
+ }, [
1258
+ spendAmount,
1259
+ selectedCurrency,
1260
+ onramperToken,
1261
+ selectedPartner,
1262
+ selectedPaymentMethod,
1263
+ address,
1264
+ t
1265
+ ]);
1266
+ return {
1267
+ onramperIframeUrl,
1268
+ iframeDialogOpen,
1269
+ setIframeDialogOpen,
1270
+ isContinueDisabled,
1271
+ onContinue
1272
+ };
1273
+ }
1274
+ var ONRAMPER_BASE = "https://api.onramper.com/quotes";
1275
+ var CHAIN_TO_ONRAMPER_TOKEN = {
1276
+ 1: "usdc_ethereum",
1277
+ // Ethereum
1278
+ 10: "usdc_optimism",
1279
+ // Optimism
1280
+ 56: "usdc_bsc",
1281
+ // BNB Smart Chain
1282
+ 100: "usdc_gnosis",
1283
+ // Gnosis
1284
+ 137: "usdc_polygon",
1285
+ // Polygon
1286
+ 143: "usdc_monad",
1287
+ // Monad Mainnet
1288
+ 324: "usdc_zksync",
1289
+ // zkSync Era
1290
+ 2020: "usdc_ronin",
1291
+ // Ronin
1292
+ 8453: "usdc_base",
1293
+ // Base
1294
+ 42161: "usdc_arbitrum",
1295
+ // Arbitrum One
1296
+ 42220: "usdc_celo",
1297
+ // Celo
1298
+ 43114: "usdc_avaxc",
1299
+ // Avalanche C-Chain
1300
+ 57073: "usdc_ink",
1301
+ // Ink
1302
+ 59144: "usdc_linea",
1303
+ // Linea
1304
+ 900900900: "usdc_solana"
1305
+ // Solana Mainnet
1306
+ };
1307
+ var ONRAMP_SUPPORTED_CHAIN_IDS = new Set(
1308
+ Object.keys(CHAIN_TO_ONRAMPER_TOKEN).map(Number)
1309
+ );
1310
+ function getOnramperToken(chainId) {
1311
+ return CHAIN_TO_ONRAMPER_TOKEN[chainId];
1312
+ }
1313
+ var onrampFetcher = async (url, apiKey) => {
1314
+ const res = await fetch(url, {
1315
+ headers: {
1316
+ accept: "application/json",
1317
+ Authorization: apiKey
1318
+ }
1319
+ });
1320
+ if (!res.ok) {
1321
+ throw new Error(`Onramper API error: ${res.status}`);
1322
+ }
1323
+ return res.json();
1324
+ };
1325
+ var buildQuoteUrl = (currency, amount, onramperToken) => `${ONRAMPER_BASE}/${currency.toLowerCase()}/${onramperToken}?amount=${amount}`;
1326
+ function filterValidRamps(items) {
1327
+ return items.filter((item) => !item.errors);
1328
+ }
1329
+ function toPartners(items) {
1330
+ return items.map((item) => ({
1331
+ id: item.ramp,
1332
+ name: item.ramp.charAt(0).toUpperCase() + item.ramp.slice(1),
1333
+ rate: item.rate ?? 0,
1334
+ payout: item.payout ?? 0,
1335
+ recommendations: item.recommendations ?? []
1336
+ }));
1337
+ }
1338
+ function toPaymentMethods(items) {
1339
+ const seen = /* @__PURE__ */ new Set();
1340
+ const methods = [];
1341
+ for (const item of items) {
1342
+ if (!item.availablePaymentMethods) continue;
1343
+ for (const pm of item.availablePaymentMethods) {
1344
+ if (!seen.has(pm.paymentTypeId)) {
1345
+ seen.add(pm.paymentTypeId);
1346
+ methods.push({
1347
+ id: pm.paymentTypeId,
1348
+ name: pm.name,
1349
+ icon: pm.icon
1350
+ });
1351
+ }
1352
+ }
1353
+ }
1354
+ return methods;
1355
+ }
1356
+ function getPaymentMethodsForPartner(items, partnerId) {
1357
+ const item = items.find((i) => i.ramp === partnerId);
1358
+ if (!item?.availablePaymentMethods) return [];
1359
+ return item.availablePaymentMethods.map((pm) => ({
1360
+ id: pm.paymentTypeId,
1361
+ name: pm.name,
1362
+ icon: pm.icon
1363
+ }));
1364
+ }
1365
+ function extractAllLimits(items) {
1366
+ const result = {};
1367
+ for (const item of items) {
1368
+ if (!item.availablePaymentMethods) continue;
1369
+ for (const pm of item.availablePaymentMethods) {
1370
+ const agg = pm.details?.limits?.aggregatedLimit;
1371
+ if (agg && !result[pm.paymentTypeId]) {
1372
+ result[pm.paymentTypeId] = agg;
1373
+ }
1374
+ }
1375
+ }
1376
+ return result;
1377
+ }
1378
+ var DEFAULT_AMOUNT = 100;
1379
+ function useOnrampQuotes(currency, amount, onramperToken) {
1380
+ const { apiKey } = useOnrampConfig();
1381
+ const token = onramperToken || "";
1382
+ const num = amount ? parseFloat(amount) : NaN;
1383
+ const effectiveAmount = !isNaN(num) && num > 0 ? num : DEFAULT_AMOUNT;
1384
+ const url = buildQuoteUrl(currency, effectiveAmount, token);
1385
+ const { data, error, isLoading, isValidating } = hooks.useSWR(
1386
+ `onramp-quote-${currency.toLowerCase()}-${effectiveAmount}-${token}`,
1387
+ () => onrampFetcher(url, apiKey),
1388
+ {
1389
+ refreshInterval: 3e4,
1390
+ revalidateOnFocus: false,
1391
+ dedupingInterval: 1e3,
1392
+ keepPreviousData: true
1393
+ }
1394
+ );
1395
+ const validRamps = react.useMemo(
1396
+ () => data ? filterValidRamps(data) : [],
1397
+ [data]
1398
+ );
1399
+ const isAvailable = validRamps.length > 0 && !error;
1400
+ const partners = react.useMemo(() => toPartners(validRamps), [validRamps]);
1401
+ const paymentMethods = react.useMemo(
1402
+ () => toPaymentMethods(validRamps),
1403
+ [validRamps]
1404
+ );
1405
+ const latestLimits = react.useMemo(
1406
+ () => extractAllLimits(validRamps),
1407
+ [validRamps]
1408
+ );
1409
+ const [accumulatedLimits, setAccumulatedLimits] = react.useState({});
1410
+ react.useEffect(() => {
1411
+ if (Object.keys(latestLimits).length > 0) {
1412
+ setAccumulatedLimits((prev) => ({ ...prev, ...latestLimits }));
1413
+ }
1414
+ }, [latestLimits]);
1415
+ return {
1416
+ isAvailable,
1417
+ partners,
1418
+ paymentMethods,
1419
+ paymentMethodLimits: accumulatedLimits,
1420
+ isLoading,
1421
+ error,
1422
+ validRamps,
1423
+ isValidating,
1424
+ getPaymentMethodsForPartner: (partnerId) => getPaymentMethodsForPartner(validRamps, partnerId)
1425
+ };
1426
+ }
1427
+ function useOnrampAvailability(currency) {
1428
+ return useOnrampQuotes(currency);
1429
+ }
1430
+ var PENDING_STATUSES = /* @__PURE__ */ new Set(["pending", "new"]);
1431
+ function useOnrampTransactionStatus(walletAddress) {
1432
+ const { workerUrl } = useOnrampConfig();
1433
+ const { data, error } = hooks.useSWR(
1434
+ walletAddress && workerUrl ? `${workerUrl}?walletAddress=${walletAddress}` : null,
1435
+ async (url) => {
1436
+ const res = await fetch(url);
1437
+ if (!res.ok) {
1438
+ if (res.status === 404) return [];
1439
+ throw new Error("Failed to fetch transaction status");
1440
+ }
1441
+ return res.json();
1442
+ },
1443
+ {
1444
+ refreshInterval: (latestData) => {
1445
+ if (!walletAddress) return 0;
1446
+ if (latestData && latestData.some((tx) => PENDING_STATUSES.has(tx.status))) {
1447
+ return 1e4;
1448
+ }
1449
+ return 3e4;
1450
+ },
1451
+ revalidateOnFocus: true
1452
+ }
1453
+ );
1454
+ const transactions = react.useMemo(() => data || [], [data]);
1455
+ const pendingTransactions = react.useMemo(
1456
+ () => transactions.filter((tx) => PENDING_STATUSES.has(tx.status)),
1457
+ [transactions]
1458
+ );
1459
+ const historyTransactions = react.useMemo(
1460
+ () => transactions.filter((tx) => !PENDING_STATUSES.has(tx.status)),
1461
+ [transactions]
1462
+ );
1463
+ return {
1464
+ transactions,
1465
+ pendingTransactions,
1466
+ historyTransactions,
1467
+ error,
1468
+ isLoading: !data && !error && !!walletAddress
1469
+ };
1470
+ }
1471
+ function usePartnerPaymentSelection(partners, getPaymentMethodsForPartner2) {
1472
+ const [selectedPartnerId, setSelectedPartnerId] = react.useState(
1473
+ null
1474
+ );
1475
+ const [selectedPaymentMethodId, setSelectedPaymentMethodId] = react.useState(null);
1476
+ const selectedPartner = react.useMemo(() => {
1477
+ if (partners.length === 0) return null;
1478
+ if (selectedPartnerId) {
1479
+ const found = partners.find((p) => p.id === selectedPartnerId);
1480
+ if (found) return found;
1481
+ }
1482
+ return partners[0];
1483
+ }, [partners, selectedPartnerId]);
1484
+ const onPartnerChange = react.useCallback((partner) => {
1485
+ setSelectedPartnerId(partner.id);
1486
+ setSelectedPaymentMethodId(null);
1487
+ }, []);
1488
+ const paymentMethods = react.useMemo(() => {
1489
+ if (!selectedPartner) return [];
1490
+ return getPaymentMethodsForPartner2(selectedPartner.id);
1491
+ }, [selectedPartner, getPaymentMethodsForPartner2]);
1492
+ const selectedPaymentMethod = react.useMemo(() => {
1493
+ if (paymentMethods.length === 0) return null;
1494
+ if (selectedPaymentMethodId) {
1495
+ const found = paymentMethods.find(
1496
+ (m) => m.id === selectedPaymentMethodId
1497
+ );
1498
+ if (found) return found;
1499
+ }
1500
+ return paymentMethods[0];
1501
+ }, [paymentMethods, selectedPaymentMethodId]);
1502
+ const onPaymentMethodChange = react.useCallback((method) => {
1503
+ setSelectedPaymentMethodId(method.id);
1504
+ }, []);
1505
+ return {
1506
+ selectedPartner,
1507
+ onPartnerChange,
1508
+ paymentMethods,
1509
+ selectedPaymentMethod,
1510
+ onPaymentMethodChange
1511
+ };
1512
+ }
1513
+ function useReceiveDisplay({
1514
+ spendAmount,
1515
+ spendAmountError,
1516
+ selectedPartner,
1517
+ selectedCurrency,
1518
+ effectiveSpendAmountForQuote
1519
+ }) {
1520
+ const receiveQuantity = react.useMemo(() => {
1521
+ if (spendAmountError) return "";
1522
+ const num = parseFloat(spendAmount);
1523
+ if (isNaN(num) || num <= 0 || !selectedPartner) return "";
1524
+ if (selectedPartner.payout > 0) return selectedPartner.payout.toFixed(2);
1525
+ return "0";
1526
+ }, [spendAmount, spendAmountError, selectedPartner]);
1527
+ const receiveQuantityPlaceholder = react.useMemo(() => {
1528
+ if (!selectedPartner) return "";
1529
+ if (selectedPartner.payout > 0) return selectedPartner.payout.toFixed(2);
1530
+ if (!selectedPartner.rate) return "";
1531
+ const num = parseFloat(effectiveSpendAmountForQuote);
1532
+ if (!isNaN(num) && num > 0) return (num / selectedPartner.rate).toFixed(2);
1533
+ return "";
1534
+ }, [selectedPartner, effectiveSpendAmountForQuote]);
1535
+ const exchangeRateText = react.useMemo(() => {
1536
+ if (!selectedPartner?.rate) return "";
1537
+ return `1 USDC \u2248 ${selectedPartner.rate.toFixed(4)} ${selectedCurrency}`;
1538
+ }, [selectedPartner, selectedCurrency]);
1539
+ return { receiveQuantity, receiveQuantityPlaceholder, exchangeRateText };
1540
+ }
1541
+ function useSpendAmount() {
1542
+ const [selectedCurrency, setSelectedCurrency] = react.useState("USD");
1543
+ const [spendAmount, setSpendAmount] = react.useState("");
1544
+ const [debouncedSpendAmount, setDebouncedSpendAmount] = react.useState(
1545
+ PRESET_AMOUNTS["USD"][0].toString()
1546
+ );
1547
+ react.useEffect(() => {
1548
+ const handler = setTimeout(() => {
1549
+ setDebouncedSpendAmount(spendAmount);
1550
+ }, 500);
1551
+ return () => clearTimeout(handler);
1552
+ }, [spendAmount]);
1553
+ const effectiveSpendAmountForQuote = react.useMemo(() => {
1554
+ const num = parseFloat(debouncedSpendAmount);
1555
+ if (!isNaN(num) && num > 0) return debouncedSpendAmount;
1556
+ return PRESET_AMOUNTS[selectedCurrency][0].toString();
1557
+ }, [debouncedSpendAmount, selectedCurrency]);
1558
+ const onCurrencyChange = (currency) => {
1559
+ setSelectedCurrency(currency);
1560
+ setSpendAmount("");
1561
+ setDebouncedSpendAmount(PRESET_AMOUNTS[currency][0].toString());
1562
+ };
1563
+ return {
1564
+ fiatCurrencies: FIAT_CURRENCIES,
1565
+ selectedCurrency,
1566
+ onCurrencyChange,
1567
+ spendAmount,
1568
+ onSpendAmountChange: setSpendAmount,
1569
+ presetAmounts: PRESET_AMOUNTS[selectedCurrency],
1570
+ effectiveSpendAmountForQuote
1571
+ };
1572
+ }
1573
+ function useWalletAddress() {
1574
+ const { wallet, connectedChain } = hooks.useWalletConnector();
1575
+ const { state: accountState, account } = hooks.useAccount();
1576
+ const address = react.useMemo(() => {
1577
+ let addr = accountState.address;
1578
+ if (connectedChain?.id && types.ABSTRACT_CHAIN_ID_MAP.has(parseInt(connectedChain.id))) {
1579
+ addr = account.getAdditionalInfo()?.AGWAddress;
1580
+ }
1581
+ return addr;
1582
+ }, [accountState, account, connectedChain]);
1583
+ return { wallet, address };
1584
+ }
1585
+ function useChainSelect() {
1586
+ const { t } = i18n.useTranslation();
1587
+ const networkId = hooks.useConfig("networkId");
1588
+ const [linkDeviceStorage] = hooks.useLocalStorage("orderly_link_device", {});
1589
+ const { connectedChain, settingChain, setChain } = hooks.useWalletConnector();
1590
+ const [allChains, { findByChainId }] = hooks.useChains(networkId, {
1591
+ pick: "network_infos",
1592
+ filter: (chain) => chain.network_infos?.bridge_enable || chain.network_infos?.bridgeless
1593
+ });
1594
+ const chains = react.useMemo(
1595
+ () => allChains.filter((c) => ONRAMP_SUPPORTED_CHAIN_IDS.has(c.chain_id)),
1596
+ [allChains]
1597
+ );
1598
+ const currentChain = react.useMemo(() => {
1599
+ const chainId = connectedChain ? utils.praseChainIdToNumber(connectedChain.id) : parseInt(linkDeviceStorage?.chainId);
1600
+ if (!chainId) return null;
1601
+ const chain = findByChainId(chainId);
1602
+ return {
1603
+ ...connectedChain,
1604
+ id: chainId,
1605
+ info: chain
1606
+ };
1607
+ }, [findByChainId, connectedChain, linkDeviceStorage]);
1608
+ const onChainChange = react.useCallback(
1609
+ async (chain) => {
1610
+ const chainInfo = findByChainId(chain.chain_id);
1611
+ if (!connectedChain) {
1612
+ return;
1613
+ }
1614
+ const currentChainId = utils.praseChainIdToNumber(connectedChain.id);
1615
+ if (!chainInfo || chainInfo.network_infos?.chain_id === currentChainId) {
1616
+ return Promise.resolve();
1617
+ }
1618
+ return setChain({
1619
+ chainId: utils.int2hex(Number(chainInfo.network_infos?.chain_id))
1620
+ }).then((switched) => {
1621
+ if (switched) {
1622
+ ui.toast.success(t("connector.networkSwitched"));
1623
+ } else {
1624
+ ui.toast.error(t("connector.switchChain.failed"));
1625
+ }
1626
+ }).catch((error) => {
1627
+ if (error && error.message) {
1628
+ ui.toast.error(
1629
+ `${t("connector.switchChain.failed")}: ${error.message}`
1630
+ );
1631
+ }
1632
+ });
1633
+ },
1634
+ [connectedChain, setChain, findByChainId, t]
1635
+ );
1636
+ return {
1637
+ chains,
1638
+ currentChain,
1639
+ settingChain,
1640
+ onChainChange
1641
+ };
1642
+ }
1643
+
1644
+ // src/components/onrampForm/onrampForm.script.tsx
1645
+ var useOnrampFormScript = () => {
1646
+ const { t } = i18n.useTranslation();
1647
+ const spend = useSpendAmount();
1648
+ const { chains, currentChain, onChainChange } = useChainSelect();
1649
+ const onramperToken = react.useMemo(
1650
+ () => currentChain ? getOnramperToken(currentChain.id) : void 0,
1651
+ [currentChain]
1652
+ );
1653
+ const {
1654
+ isAvailable,
1655
+ partners,
1656
+ isLoading,
1657
+ getPaymentMethodsForPartner: getPaymentMethodsForPartner2,
1658
+ paymentMethodLimits,
1659
+ isValidating
1660
+ } = useOnrampQuotes(
1661
+ spend.selectedCurrency,
1662
+ spend.effectiveSpendAmountForQuote,
1663
+ onramperToken
1664
+ );
1665
+ const selection = usePartnerPaymentSelection(
1666
+ partners,
1667
+ getPaymentMethodsForPartner2
1668
+ );
1669
+ const spendAmountError = react.useMemo(() => {
1670
+ if (!spend.spendAmount || !selection.selectedPaymentMethod) return "";
1671
+ const num = parseFloat(spend.spendAmount);
1672
+ if (isNaN(num) || num <= 0) return "";
1673
+ const limits = paymentMethodLimits[selection.selectedPaymentMethod.id];
1674
+ if (!limits) return "";
1675
+ if (num < limits.min || num > limits.max) {
1676
+ return t("onramp.amountBetween", {
1677
+ currency: spend.selectedCurrency,
1678
+ min: limits.min,
1679
+ max: limits.max
1680
+ });
1681
+ }
1682
+ return "";
1683
+ }, [
1684
+ spend.spendAmount,
1685
+ spend.selectedCurrency,
1686
+ selection.selectedPaymentMethod,
1687
+ paymentMethodLimits,
1688
+ t
1689
+ ]);
1690
+ const { wallet, address } = useWalletAddress();
1691
+ const {
1692
+ transactions,
1693
+ pendingTransactions,
1694
+ historyTransactions,
1695
+ isLoading: isStatusLoading
1696
+ } = useOnrampTransactionStatus(address ?? null);
1697
+ const { receiveQuantity, receiveQuantityPlaceholder, exchangeRateText } = useReceiveDisplay({
1698
+ spendAmount: spend.spendAmount,
1699
+ spendAmountError,
1700
+ selectedPartner: selection.selectedPartner,
1701
+ selectedCurrency: spend.selectedCurrency,
1702
+ effectiveSpendAmountForQuote: spend.effectiveSpendAmountForQuote
1703
+ });
1704
+ const checkout = useOnrampCheckout({
1705
+ spendAmount: spend.spendAmount,
1706
+ selectedCurrency: spend.selectedCurrency,
1707
+ spendAmountError,
1708
+ onramperToken,
1709
+ selectedPartner: selection.selectedPartner,
1710
+ selectedPaymentMethod: selection.selectedPaymentMethod,
1711
+ address,
1712
+ isLoading
1713
+ });
1714
+ return {
1715
+ paymentMethods: selection.paymentMethods,
1716
+ selectedPaymentMethod: selection.selectedPaymentMethod,
1717
+ onPaymentMethodChange: selection.onPaymentMethodChange,
1718
+ fiatCurrencies: spend.fiatCurrencies,
1719
+ selectedCurrency: spend.selectedCurrency,
1720
+ onCurrencyChange: spend.onCurrencyChange,
1721
+ spendAmount: spend.spendAmount,
1722
+ onSpendAmountChange: spend.onSpendAmountChange,
1723
+ presetAmounts: spend.presetAmounts,
1724
+ chains,
1725
+ selectedChain: currentChain,
1726
+ onChainChange,
1727
+ wallet,
1728
+ address,
1729
+ receiveQuantity,
1730
+ receiveQuantityPlaceholder,
1731
+ partners,
1732
+ selectedPartner: selection.selectedPartner,
1733
+ onPartnerChange: selection.onPartnerChange,
1734
+ exchangeRateText,
1735
+ quoteIsValidating: isValidating,
1736
+ isAvailable,
1737
+ isQuoteLoading: isLoading,
1738
+ spendAmountError,
1739
+ ...checkout,
1740
+ statusData: pendingTransactions.length > 0 ? pendingTransactions[0] : historyTransactions.length > 0 ? historyTransactions[0] : null,
1741
+ transactions,
1742
+ pendingTransactions,
1743
+ historyTransactions,
1744
+ isStatusLoading
1745
+ };
1746
+ };
1747
+ var OnrampForm = (props) => {
1748
+ const state = useOnrampFormScript();
1749
+ return /* @__PURE__ */ jsxRuntime.jsx(OnrampFormUI, { ...state, ...props });
1750
+ };
1751
+
1752
+ // src/i18n/module.ts
1753
+ var OnrampLocales = {
1754
+ "onramp.pluginName": "Buy Crypto (Onramper)",
1755
+ "onramp.tabTitle": "Buy Crypto",
1756
+ "onramp.noPaymentMethod": "No available payment method",
1757
+ "onramp.youReceive": "You receive",
1758
+ "onramp.youSpend": "You spend",
1759
+ "onramp.noPartner": "No available partner",
1760
+ "onramp.continue": "Continue",
1761
+ "onramp.iframeTitle": "Onramper Widget",
1762
+ "onramp.amountBetween": "Amount should be in between {{currency}} {{min}} and {{currency}} {{max}}",
1763
+ "onramp.by": "by",
1764
+ "onramp.payWith": "Pay with",
1765
+ "onramp.history": "History",
1766
+ "onramp.pendingPurchase": "{{count}} pending purchase",
1767
+ "onramp.pendingPurchases": "{{count}} pending purchases",
1768
+ "onramp.statusCompleted": "completed",
1769
+ "onramp.quantity": "Quantity",
1770
+ "onramp.unknownNetwork": "Unknown",
1771
+ "onramp.missingRequiredInfo": "Missing required info: {{list}}",
1772
+ "onramp.missing.spendAmount": "spend amount",
1773
+ "onramp.missing.currency": "fiat currency",
1774
+ "onramp.missing.network": "network (chain)",
1775
+ "onramp.missing.partner": "partner",
1776
+ "onramp.missing.paymentMethod": "payment method",
1777
+ "onramp.missing.address": "wallet address"
1778
+ };
1779
+
1780
+ // import("./locales/**/*.json") in src/i18n/provider.tsx
1781
+ var globImport_locales_json = chunkQKQ47GQZ_js.__glob({
1782
+ "./locales/de.json": () => import('./de-GYR62SPI.js'),
1783
+ "./locales/en.json": () => import('./en-IAAY2CDT.js'),
1784
+ "./locales/es.json": () => import('./es-B7VYU5TQ.js'),
1785
+ "./locales/fr.json": () => import('./fr-5G5K3WZB.js'),
1786
+ "./locales/id.json": () => import('./id-RPSFROFO.js'),
1787
+ "./locales/it.json": () => import('./it-64LYBKQP.js'),
1788
+ "./locales/ja.json": () => import('./ja-AMRP6CIA.js'),
1789
+ "./locales/ko.json": () => import('./ko-BTMZAQ7J.js'),
1790
+ "./locales/nl.json": () => import('./nl-BZB2BRQ7.js'),
1791
+ "./locales/pl.json": () => import('./pl-ORAYIBD7.js'),
1792
+ "./locales/pt.json": () => import('./pt-RWI3GBG6.js'),
1793
+ "./locales/ru.json": () => import('./ru-AP7GWUPY.js'),
1794
+ "./locales/tc.json": () => import('./tc-RZ4GC2Z6.js'),
1795
+ "./locales/tr.json": () => import('./tr-E6HTGNQA.js'),
1796
+ "./locales/uk.json": () => import('./uk-6FVT4QXK.js'),
1797
+ "./locales/vi.json": () => import('./vi-KGVGSDIB.js'),
1798
+ "./locales/zh.json": () => import('./zh-A3GFFBKX.js')
1799
+ });
1800
+
1801
+ // src/i18n/provider.tsx
1802
+ i18n.preloadDefaultResource(OnrampLocales);
1803
+ var resources = (lang) => {
1804
+ return globImport_locales_json(`./locales/${lang}.json`).then(
1805
+ (res) => res.default
1806
+ );
1807
+ };
1808
+ var OnrampLocaleProvider = (props) => {
1809
+ return /* @__PURE__ */ jsxRuntime.jsx(i18n.ExternalLocaleProvider, { resources, children: props.children });
1810
+ };
1811
+ var defaultIcon = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oui-flex oui-items-center oui-justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(BuyCryptoIcon, { width: 11, height: 11, className: "oui-mt-0.5 oui-ml-0.5" }) });
1812
+ function registerOnrampPlugin(options) {
1813
+ return (SDK) => {
1814
+ SDK.registerPlugin({
1815
+ id: "orderly-onramp",
1816
+ name: i18n.i18n.t("onramp.pluginName"),
1817
+ version: "1.0.0",
1818
+ interceptors: [
1819
+ pluginCore.createInterceptor("Transfer.DepositAndWithdraw", (Original, props) => {
1820
+ if (!options.apiKey || !options.secretKey) {
1821
+ return /* @__PURE__ */ jsxRuntime.jsx(Original, { ...props });
1822
+ }
1823
+ const onrampTab = {
1824
+ id: "onramp",
1825
+ title: options.title ?? i18n.i18n.t("onramp.tabTitle"),
1826
+ icon: options.icon ?? defaultIcon,
1827
+ component: (props2) => /* @__PURE__ */ jsxRuntime.jsx(
1828
+ OnrampConfigProvider,
1829
+ {
1830
+ config: {
1831
+ apiKey: options.apiKey,
1832
+ secretKey: options.secretKey,
1833
+ workerUrl: options.workerUrl
1834
+ },
1835
+ children: /* @__PURE__ */ jsxRuntime.jsx(OnrampForm, { ...props2 })
1836
+ }
1837
+ ),
1838
+ order: 30
1839
+ };
1840
+ const extraTabs = [
1841
+ ...Array.isArray(props.extraTabs) ? props.extraTabs : [],
1842
+ onrampTab
1843
+ ];
1844
+ return /* @__PURE__ */ jsxRuntime.jsx(OnrampLocaleProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(Original, { ...props, extraTabs }) });
1845
+ })
1846
+ ]
1847
+ });
1848
+ };
1849
+ }
1850
+
1851
+ exports.ArrowDownIcon = ArrowDownIcon;
1852
+ exports.BuyCryptoIcon = BuyCryptoIcon;
1853
+ exports.ExchangeIcon = ExchangeIcon;
1854
+ exports.FIAT_CURRENCIES = FIAT_CURRENCIES;
1855
+ exports.OnrampForm = OnrampForm;
1856
+ exports.OnrampFormUI = OnrampFormUI;
1857
+ exports.PRESET_AMOUNTS = PRESET_AMOUNTS;
1858
+ exports.formatCompact = formatCompact;
1859
+ exports.registerOnrampPlugin = registerOnrampPlugin;
1860
+ exports.useOnrampAvailability = useOnrampAvailability;
1861
+ exports.useOnrampFormScript = useOnrampFormScript;
1862
+ //# sourceMappingURL=index.js.map
1863
+ //# sourceMappingURL=index.js.map