@oasisprotocol/privana-sdk 0.4.4 → 0.5.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.
package/dist/on-ramp.cjs CHANGED
@@ -1,981 +1,17 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var chunkNMRIKUDN_cjs = require('./chunk-NMRIKUDN.cjs');
5
- var react = require('react');
6
- var viem = require('viem');
7
- var wagmi = require('wagmi');
8
- var lucideReact = require('lucide-react');
9
- var moonpayReact = require('@moonpay/moonpay-react');
10
- var jsxRuntime = require('react/jsx-runtime');
4
+ var chunkACLJPC75_cjs = require('./chunk-ACLJPC75.cjs');
11
5
 
12
- var DEFAULT_DELIVERY_TIMEOUT_MS = 12e4;
13
- var DEFAULT_VERIFICATION_TIMEOUT_MS = 10 * 6e4;
14
- var DEFAULT_FINALITY_RETRY_INTERVAL_MS = 15e3;
15
- var ERC20_TRANSFER_EVENT = viem.parseAbiItem(
16
- "event Transfer(address indexed from, address indexed to, uint256 value)"
17
- );
18
- function useFiatOnRamp(options) {
19
- const { tokenId, onCredited, onError, onDebugEvent } = options;
20
- const deliveryTimeout = options.deliveryTimeout ?? DEFAULT_DELIVERY_TIMEOUT_MS;
21
- const deliveryPollInterval = options.deliveryPollInterval ?? 3e3;
22
- const verificationTimeout = options.verificationTimeout ?? DEFAULT_VERIFICATION_TIMEOUT_MS;
23
- const finalityRetryInterval = options.finalityRetryInterval ?? DEFAULT_FINALITY_RETRY_INTERVAL_MS;
24
- const { client, enabledTokens } = chunkNMRIKUDN_cjs.usePrivanaContext();
25
- const { executePrivateRead, privateReadReady } = chunkNMRIKUDN_cjs.usePrivateReadRequest();
26
- const wagmiConfig = wagmi.useConfig();
27
- const selectedToken = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
28
- const [status, setStatus] = react.useState("idle");
29
- const [pending, setPending] = react.useState([]);
30
- const [error, setError] = react.useState(null);
31
- const [depositAddress, setDepositAddress] = react.useState();
32
- const [minDepositBaseUnits, setMinDepositBaseUnits] = react.useState();
33
- const [activeIntentId, setActiveIntentId] = react.useState(null);
34
- const [finalityProgress, setFinalityProgress] = react.useState({});
35
- const onCreditedRef = react.useRef(onCredited);
36
- const onErrorRef = react.useRef(onError);
37
- const onDebugEventRef = react.useRef(onDebugEvent);
38
- const statusRef = react.useRef(status);
39
- const activeIntentIdRef = react.useRef(null);
40
- const activeVerificationRecordRef = react.useRef(null);
41
- const activeVerificationKeyRef = react.useRef(null);
42
- const triggeredVerificationKeysRef = react.useRef(/* @__PURE__ */ new Set());
43
- const closeReconcilePromiseRef = react.useRef(null);
44
- const purchaseInitiatedRef = react.useRef(false);
45
- react.useEffect(() => {
46
- onCreditedRef.current = onCredited;
47
- onErrorRef.current = onError;
48
- onDebugEventRef.current = onDebugEvent;
49
- }, [onCredited, onError, onDebugEvent]);
50
- react.useEffect(() => {
51
- statusRef.current = status;
52
- }, [status]);
53
- react.useEffect(() => {
54
- activeIntentIdRef.current = activeIntentId;
55
- }, [activeIntentId]);
56
- react.useEffect(() => {
57
- activeIntentIdRef.current = null;
58
- setActiveIntentId(null);
59
- }, [tokenId]);
60
- const emitDebug = react.useCallback(
61
- (event, payload) => {
62
- onDebugEventRef.current?.({
63
- at: (/* @__PURE__ */ new Date()).toISOString(),
64
- event,
65
- status: statusRef.current,
66
- tokenId,
67
- payload
68
- });
69
- },
70
- [tokenId]
71
- );
72
- react.useEffect(() => {
73
- emitDebug("private-read-state", { privateReadReady });
74
- }, [emitDebug, privateReadReady]);
75
- react.useEffect(() => {
76
- if (!privateReadReady) {
77
- emitDebug("deposit-address:skip", { reason: "private-read-not-ready" });
78
- setDepositAddress(void 0);
79
- setMinDepositBaseUnits(void 0);
80
- return;
81
- }
82
- let cancelled = false;
83
- void (async () => {
84
- try {
85
- emitDebug("deposit-address:request");
86
- const resp = await executePrivateRead(() => client.getDepositAddress());
87
- if (cancelled) return;
88
- setDepositAddress(resp.deposit_address);
89
- const token = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
90
- const mins = token ? resp.min_deposit?.[String(token.chainId)] : void 0;
91
- if (mins?.erc20) setMinDepositBaseUnits(BigInt(mins.erc20));
92
- emitDebug("deposit-address:success", {
93
- depositAddress: resp.deposit_address,
94
- selectedToken: token ? summariseToken(token) : null,
95
- minDepositBaseUnits: mins?.erc20 ?? null
96
- });
97
- } catch (err) {
98
- if (!cancelled) {
99
- emitDebug("deposit-address:error", errorPayload(err));
100
- console.warn("Failed to fetch Privana deposit address:", err);
101
- }
102
- }
103
- })();
104
- return () => {
105
- cancelled = true;
106
- };
107
- }, [client, emitDebug, enabledTokens, executePrivateRead, privateReadReady, tokenId]);
108
- const refreshPending = react.useCallback(async () => {
109
- if (!privateReadReady) {
110
- emitDebug("pending:skip", { reason: "private-read-not-ready" });
111
- setPending([]);
112
- return;
113
- }
114
- try {
115
- emitDebug("pending:request");
116
- const { pending: rows } = await executePrivateRead(() => client.getPendingOnRamps());
117
- setPending(rows);
118
- emitDebug("pending:success", {
119
- count: rows.length,
120
- rows: rows.map(summariseOnRampRecord)
121
- });
122
- } catch (err) {
123
- emitDebug("pending:error", errorPayload(err));
124
- console.warn("Failed to load pending on-ramps:", err);
125
- }
126
- }, [client, emitDebug, executePrivateRead, privateReadReady]);
127
- react.useEffect(() => {
128
- refreshPending();
129
- }, [refreshPending]);
130
- const clearActiveVerification = react.useCallback(() => {
131
- const key = activeVerificationKeyRef.current;
132
- if (key) triggeredVerificationKeysRef.current.delete(key);
133
- activeVerificationKeyRef.current = null;
134
- activeVerificationRecordRef.current = null;
135
- }, []);
136
- const { verify } = chunkNMRIKUDN_cjs.useDepositVerification({
137
- pollTimeout: verificationTimeout,
138
- pollInterval: options.verificationPollInterval,
139
- finalityRetryInterval,
140
- onCheckRetry: (message) => {
141
- const record = activeVerificationRecordRef.current;
142
- if (!record) return;
143
- emitDebug("verification:check-retry", {
144
- message,
145
- record: summariseOnRampRecord(record)
146
- });
147
- setFinalityProgress((prev) => ({ ...prev, [record.transaction_id]: message }));
148
- },
149
- onCredited: (depositTxHash) => {
150
- const record = activeVerificationRecordRef.current;
151
- emitDebug("verification:credited", {
152
- depositTxHash,
153
- record: record ? summariseOnRampRecord(record) : null
154
- });
155
- if (record && activeIntentIdRef.current === record.transaction_id) {
156
- setStatus("credited");
157
- }
158
- if (record) {
159
- setFinalityProgress((prev) => {
160
- if (!(record.transaction_id in prev)) return prev;
161
- const next = { ...prev };
162
- delete next[record.transaction_id];
163
- return next;
164
- });
165
- }
166
- void (async () => {
167
- try {
168
- if (record && depositTxHash.startsWith("0x")) {
169
- emitDebug("onramp:mark-deposit-triggered-request", {
170
- transactionId: record.transaction_id,
171
- depositTxHash
172
- });
173
- const updated = await executePrivateRead(
174
- () => client.updateOnRamp(record.transaction_id, {
175
- deposit_tx_hash: depositTxHash
176
- })
177
- );
178
- emitDebug("onramp:mark-deposit-triggered-success", {
179
- record: summariseOnRampRecord(updated)
180
- });
181
- }
182
- } catch (err) {
183
- emitDebug("onramp:mark-deposit-triggered-error", errorPayload(err));
184
- console.warn("Failed to mark on-ramp row complete:", err);
185
- } finally {
186
- clearActiveVerification();
187
- if (record && activeIntentIdRef.current === record.transaction_id) {
188
- activeIntentIdRef.current = null;
189
- setActiveIntentId(null);
190
- }
191
- void refreshPending();
192
- }
193
- })();
194
- onCreditedRef.current?.(depositTxHash);
195
- },
196
- onCheckTimeout: (depositTxHash) => {
197
- const record = activeVerificationRecordRef.current;
198
- const err = new Error(
199
- "Privana verification is still pending. Retry from the pending on-ramp list if it does not complete."
200
- );
201
- emitDebug("verification:timeout", { depositTxHash, message: err.message });
202
- clearActiveVerification();
203
- if (!record || activeIntentIdRef.current === record.transaction_id) {
204
- setStatus("failed");
205
- setError(err);
206
- }
207
- void refreshPending();
208
- onErrorRef.current?.(err);
209
- },
210
- onError: (err) => {
211
- const record = activeVerificationRecordRef.current;
212
- emitDebug("verification:error", errorPayload(err));
213
- clearActiveVerification();
214
- if (!record || activeIntentIdRef.current === record.transaction_id) {
215
- setStatus("failed");
216
- setError(err);
217
- }
218
- onErrorRef.current?.(err);
219
- }
220
- });
221
- const prepareOnRampIntent = react.useCallback(
222
- async ({
223
- currencyCode,
224
- baseCurrencyCode,
225
- baseCurrencyAmount
226
- }) => {
227
- try {
228
- setError(null);
229
- purchaseInitiatedRef.current = false;
230
- const token = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
231
- if (!token) throw new Error(`Unknown token: ${tokenId}`);
232
- if (!depositAddress) throw new Error("Privana deposit address is not ready");
233
- emitDebug("intent:create-request", {
234
- tokenId,
235
- chainId: token.chainId,
236
- currencyCode,
237
- baseCurrencyCode: baseCurrencyCode ?? null,
238
- baseCurrencyAmount: baseCurrencyAmount ?? null,
239
- depositAddress
240
- });
241
- const record = await executePrivateRead(
242
- () => client.createOnRampIntent({
243
- wallet_address: depositAddress,
244
- token_id: tokenId,
245
- chain_id: token.chainId,
246
- moonpay_currency_code: currencyCode,
247
- base_currency_code: baseCurrencyCode,
248
- base_currency_amount: baseCurrencyAmount
249
- })
250
- );
251
- activeIntentIdRef.current = record.transaction_id;
252
- setActiveIntentId(record.transaction_id);
253
- emitDebug("intent:create-success", {
254
- record: summariseOnRampRecord(record)
255
- });
256
- return record;
257
- } catch (err) {
258
- const e = err instanceof Error ? err : new Error("Failed to create on-ramp intent");
259
- setStatus("failed");
260
- setError(e);
261
- emitDebug("intent:create-error", errorPayload(e));
262
- onErrorRef.current?.(e);
263
- throw e;
264
- }
265
- },
266
- [client, depositAddress, emitDebug, enabledTokens, executePrivateRead, tokenId]
267
- );
268
- const registerOnRampTokenMapping = react.useCallback(
269
- async (moonpayTransactionId) => {
270
- const token = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
271
- if (!token) {
272
- emitDebug("register-token-mapping:skip", {
273
- moonpayTransactionId,
274
- reason: "selected-token-not-found"
275
- });
276
- return;
277
- }
278
- const transactionId = activeIntentIdRef.current ?? moonpayTransactionId;
279
- try {
280
- emitDebug("register-token-mapping:request", {
281
- transactionId,
282
- moonpayTransactionId,
283
- tokenId,
284
- chainId: token.chainId
285
- });
286
- const record = await executePrivateRead(
287
- () => client.updateOnRamp(transactionId, {
288
- token_id: tokenId,
289
- chain_id: token.chainId,
290
- moonpay_transaction_id: transactionId === moonpayTransactionId ? void 0 : moonpayTransactionId
291
- })
292
- );
293
- emitDebug("register-token-mapping:success", {
294
- transactionId,
295
- moonpayTransactionId,
296
- record: summariseOnRampRecord(record)
297
- });
298
- } catch (err) {
299
- emitDebug("register-token-mapping:error", {
300
- transactionId,
301
- moonpayTransactionId,
302
- ...errorPayload(err)
303
- });
304
- console.warn("Failed to register on-ramp token mapping:", err);
305
- }
306
- },
307
- [client, emitDebug, enabledTokens, executePrivateRead, tokenId]
308
- );
309
- const handleTransactionCreated = react.useCallback(
310
- async (props) => {
311
- emitDebug("moonpay:onTransactionCreated", summariseMoonPayEventProps(props));
312
- purchaseInitiatedRef.current = true;
313
- await registerOnRampTokenMapping(props.id);
314
- },
315
- [emitDebug, registerOnRampTokenMapping]
316
- );
317
- const signUrl = react.useCallback(
318
- async (url) => {
319
- setError(null);
320
- try {
321
- emitDebug("moonpay:onUrlSignatureRequested", summariseMoonPayUrl(url));
322
- const { signature } = await executePrivateRead(() => client.signOnRampUrl({ url }));
323
- setStatus("awaiting-purchase");
324
- emitDebug("sign-url:success", {
325
- signatureLength: signature.length
326
- });
327
- return signature;
328
- } catch (err) {
329
- const e = err instanceof Error ? err : new Error("Failed to sign on-ramp URL");
330
- setStatus("failed");
331
- setError(e);
332
- emitDebug("sign-url:error", errorPayload(e));
333
- onErrorRef.current?.(e);
334
- throw err;
335
- }
336
- },
337
- [client, emitDebug, executePrivateRead]
338
- );
339
- const waitForOnChainHash = react.useCallback(
340
- async (transactionId) => {
341
- const startTime = Date.now();
342
- emitDebug("delivery-poll:start", {
343
- transactionId,
344
- deliveryTimeout,
345
- deliveryPollInterval
346
- });
347
- while (Date.now() - startTime < deliveryTimeout) {
348
- try {
349
- const { pending: rows } = await executePrivateRead(() => client.getPendingOnRamps());
350
- setPending(rows);
351
- const record = rows.find((r) => matchesOnRampTransaction(r, transactionId));
352
- emitDebug("delivery-poll:tick", {
353
- transactionId,
354
- count: rows.length,
355
- matchingRecord: record ? summariseOnRampRecord(record) : null
356
- });
357
- if (record?.on_chain_tx_hash && record.quote_currency_amount) {
358
- emitDebug("delivery-poll:success", {
359
- transactionId,
360
- record: summariseOnRampRecord(record)
361
- });
362
- return record;
363
- }
364
- } catch (err) {
365
- emitDebug("delivery-poll:error", {
366
- transactionId,
367
- ...errorPayload(err)
368
- });
369
- console.warn("Polling pending on-ramps failed:", err);
370
- }
371
- await new Promise((r) => setTimeout(r, deliveryPollInterval));
372
- }
373
- emitDebug("delivery-poll:timeout", { transactionId });
374
- return null;
375
- },
376
- [client, deliveryPollInterval, deliveryTimeout, emitDebug, executePrivateRead]
377
- );
378
- const triggerVerification = react.useCallback(
379
- async (record) => {
380
- const verificationKey = getOnRampVerificationKey(record);
381
- if (triggeredVerificationKeysRef.current.has(verificationKey)) {
382
- emitDebug("verification:skip-duplicate", {
383
- verificationKey,
384
- record: summariseOnRampRecord(record)
385
- });
386
- return;
387
- }
388
- triggeredVerificationKeysRef.current.add(verificationKey);
389
- activeVerificationKeyRef.current = verificationKey;
390
- activeVerificationRecordRef.current = record;
391
- setFinalityProgress((prev) => {
392
- if (!(record.transaction_id in prev)) return prev;
393
- const next = { ...prev };
394
- delete next[record.transaction_id];
395
- return next;
396
- });
397
- emitDebug("verification:start", {
398
- verificationKey,
399
- record: summariseOnRampRecord(record)
400
- });
401
- try {
402
- if (!record.on_chain_tx_hash || !record.quote_currency_amount) {
403
- throw new Error("On-ramp record missing on-chain tx hash or delivered amount");
404
- }
405
- if (record.chain_id === void 0 || !record.wallet_address) {
406
- throw new Error("On-ramp record missing chain id or wallet address");
407
- }
408
- const recordTokenId = record.token_id;
409
- if (!recordTokenId) {
410
- throw new Error("On-ramp record missing token id");
411
- }
412
- const token = enabledTokens.find((t) => t.id.toLowerCase() === recordTokenId.toLowerCase());
413
- if (!token) throw new Error(`Unknown token: ${recordTokenId}`);
414
- if (token.chainId !== record.chain_id) {
415
- throw new Error(
416
- `Token ${recordTokenId} is on chain ${token.chainId} but record is on chain ${record.chain_id}`
417
- );
418
- }
419
- const amount = await resolveDeliveredAmount({
420
- onChainTxHash: record.on_chain_tx_hash,
421
- chainId: record.chain_id,
422
- walletAddress: record.wallet_address,
423
- token,
424
- fallbackAmount: record.quote_currency_amount,
425
- wagmiConfig,
426
- emitDebug
427
- });
428
- if (minDepositBaseUnits !== void 0 && amount < minDepositBaseUnits) {
429
- emitDebug("verification:below-minimum", {
430
- quoteCurrencyAmount: record.quote_currency_amount,
431
- minDepositBaseUnits: String(minDepositBaseUnits)
432
- });
433
- throw new Error(
434
- `Delivered amount (${record.quote_currency_amount}) is below the minimum deposit.`
435
- );
436
- }
437
- if (activeIntentIdRef.current === record.transaction_id) {
438
- setStatus("verifying");
439
- }
440
- emitDebug("verification:check-deposit-request", {
441
- hash: record.on_chain_tx_hash,
442
- chainId: record.chain_id,
443
- amount: amount.toString()
444
- });
445
- await verify({
446
- hash: record.on_chain_tx_hash,
447
- chainId: record.chain_id,
448
- amount
449
- });
450
- } catch (err) {
451
- triggeredVerificationKeysRef.current.delete(verificationKey);
452
- if (activeVerificationKeyRef.current === verificationKey) {
453
- activeVerificationKeyRef.current = null;
454
- activeVerificationRecordRef.current = null;
455
- }
456
- throw err;
457
- }
458
- },
459
- [emitDebug, enabledTokens, minDepositBaseUnits, verify, wagmiConfig]
460
- );
461
- const handleTransactionCompleted = react.useCallback(
462
- async (props) => {
463
- emitDebug("moonpay:onTransactionCompleted", summariseMoonPayEventProps(props));
464
- try {
465
- setStatus("awaiting-delivery");
466
- await registerOnRampTokenMapping(props.id);
467
- const transactionId = activeIntentIdRef.current ?? props.id;
468
- const record = await waitForOnChainHash(transactionId);
469
- if (!record) {
470
- const err = new Error(
471
- "Backend has not yet confirmed delivery. You can finish from the pending list."
472
- );
473
- emitDebug("moonpay:completed-without-backend-row", {
474
- transactionId,
475
- moonpayTransactionId: props.id,
476
- message: err.message
477
- });
478
- setStatus("failed");
479
- setError(err);
480
- onErrorRef.current?.(err);
481
- return;
482
- }
483
- await triggerVerification(record);
484
- } catch (err) {
485
- const e = err instanceof Error ? err : new Error("Verification failed");
486
- emitDebug("moonpay:onTransactionCompleted-error", errorPayload(e));
487
- setStatus("failed");
488
- setError(e);
489
- onErrorRef.current?.(e);
490
- }
491
- },
492
- [emitDebug, registerOnRampTokenMapping, triggerVerification, waitForOnChainHash]
493
- );
494
- const handleWidgetClosed = react.useCallback(async () => {
495
- if (closeReconcilePromiseRef.current) return closeReconcilePromiseRef.current;
496
- closeReconcilePromiseRef.current = (async () => {
497
- const previousStatus = statusRef.current;
498
- const transactionId = activeIntentIdRef.current;
499
- emitDebug("moonpay:widget-closed-reconcile", {
500
- previousStatus,
501
- transactionId
502
- });
503
- if (!transactionId) {
504
- await refreshPending();
505
- return;
506
- }
507
- if (!purchaseInitiatedRef.current) {
508
- emitDebug("moonpay:widget-closed-without-purchase", {
509
- previousStatus,
510
- transactionId
511
- });
512
- await refreshPending();
513
- if (previousStatus === "awaiting-purchase" || previousStatus === "awaiting-delivery") {
514
- setStatus("idle");
515
- }
516
- return;
517
- }
518
- try {
519
- setStatus("awaiting-delivery");
520
- const record = await waitForOnChainHash(transactionId);
521
- if (record) {
522
- await triggerVerification(record);
523
- return;
524
- }
525
- await refreshPending();
526
- if (previousStatus === "awaiting-purchase" || previousStatus === "awaiting-delivery") {
527
- setStatus("idle");
528
- }
529
- } catch (err) {
530
- const e = err instanceof Error ? err : new Error("On-ramp reconciliation failed");
531
- emitDebug("moonpay:widget-closed-reconcile-error", errorPayload(e));
532
- setStatus("failed");
533
- setError(e);
534
- onErrorRef.current?.(e);
535
- }
536
- })();
537
- try {
538
- await closeReconcilePromiseRef.current;
539
- } finally {
540
- closeReconcilePromiseRef.current = null;
541
- }
542
- }, [emitDebug, refreshPending, triggerVerification, waitForOnChainHash]);
543
- const finishPendingVerification = react.useCallback(
544
- async (record) => {
545
- try {
546
- emitDebug("pending:finish-verification", {
547
- record: summariseOnRampRecord(record)
548
- });
549
- await triggerVerification(record);
550
- } catch (err) {
551
- const e = err instanceof Error ? err : new Error("Verification failed");
552
- emitDebug("pending:finish-verification-error", errorPayload(e));
553
- setStatus("failed");
554
- setError(e);
555
- onErrorRef.current?.(e);
556
- throw e;
557
- }
558
- },
559
- [emitDebug, triggerVerification]
560
- );
561
- const triggerVerificationRef = react.useRef(triggerVerification);
562
- react.useEffect(() => {
563
- triggerVerificationRef.current = triggerVerification;
564
- }, [triggerVerification]);
565
- react.useEffect(() => {
566
- let cancelled = false;
567
- void (async () => {
568
- for (const record of pending) {
569
- if (cancelled) break;
570
- if (!record.on_chain_tx_hash || !record.quote_currency_amount) continue;
571
- const key = getOnRampVerificationKey(record);
572
- if (triggeredVerificationKeysRef.current.has(key)) continue;
573
- try {
574
- await triggerVerificationRef.current(record);
575
- } catch {
576
- }
577
- }
578
- })();
579
- return () => {
580
- cancelled = true;
581
- };
582
- }, [pending]);
583
- return {
584
- status,
585
- activeIntentId,
586
- pending,
587
- error,
588
- finalityProgress,
589
- depositAddress,
590
- minDepositBaseUnits,
591
- selectedToken,
592
- prepareOnRampIntent,
593
- signUrl,
594
- handleTransactionCreated,
595
- handleTransactionCompleted,
596
- finishPendingVerification,
597
- handleWidgetClosed,
598
- refreshPending
599
- };
600
- }
601
- function summariseToken(token) {
602
- return {
603
- tokenId: token.id,
604
- chainId: token.chainId,
605
- symbol: token.symbol ?? null,
606
- decimals: token.decimals ?? null
607
- };
608
- }
609
- function summariseOnRampRecord(record) {
610
- return {
611
- transaction_id: record.transaction_id,
612
- external_transaction_id: record.external_transaction_id ?? null,
613
- moonpay_transaction_id: record.moonpay_transaction_id ?? null,
614
- status: record.status,
615
- wallet_address: record.wallet_address,
616
- token_id: record.token_id,
617
- chain_id: record.chain_id,
618
- moonpay_currency_code: record.moonpay_currency_code ?? null,
619
- quote_currency_amount: record.quote_currency_amount ?? null,
620
- on_chain_tx_hash: record.on_chain_tx_hash ?? null,
621
- deposit_tx_hash: record.deposit_tx_hash ?? null,
622
- deposit_triggered_at: record.deposit_triggered_at ?? null,
623
- credited_at: record.credited_at ?? null
624
- };
625
- }
626
- async function resolveDeliveredAmount({
627
- onChainTxHash,
628
- chainId,
629
- walletAddress,
630
- token,
631
- fallbackAmount,
632
- wagmiConfig,
633
- emitDebug
634
- }) {
635
- if (token.contract === viem.zeroAddress) {
636
- return viem.parseUnits(fallbackAmount, token.decimals);
637
- }
638
- let receiptError;
639
- try {
640
- const receipt = await chunkNMRIKUDN_cjs.waitForTransactionReceipt(wagmiConfig, {
641
- hash: onChainTxHash,
642
- chainId,
643
- timeout: 6e4,
644
- pollingInterval: 4e3
645
- });
646
- let delivered = 0n;
647
- for (const log of receipt.logs) {
648
- if (log.address.toLowerCase() !== token.contract.toLowerCase()) continue;
649
- try {
650
- const decoded = viem.decodeEventLog({
651
- abi: [ERC20_TRANSFER_EVENT],
652
- data: log.data,
653
- topics: log.topics
654
- });
655
- if (decoded.eventName !== "Transfer") continue;
656
- const to = decoded.args.to.toLowerCase();
657
- if (to !== walletAddress.toLowerCase()) continue;
658
- delivered += decoded.args.value;
659
- } catch {
660
- }
661
- }
662
- if (delivered > 0n) {
663
- emitDebug("verification:amount-from-receipt", {
664
- amount: delivered.toString(),
665
- tokenAddress: token.contract,
666
- walletAddress,
667
- moonpayQuoteCurrencyAmount: fallbackAmount
668
- });
669
- return delivered;
670
- }
671
- emitDebug("verification:amount-from-receipt-missing", {
672
- tokenAddress: token.contract,
673
- walletAddress,
674
- moonpayQuoteCurrencyAmount: fallbackAmount
675
- });
676
- } catch (err) {
677
- emitDebug("verification:amount-from-receipt-error", errorPayload(err));
678
- receiptError = err;
679
- }
680
- const errorDetail = receiptError instanceof Error ? receiptError.message : receiptError === void 0 ? `no ${token.symbol} Transfer to ${walletAddress} found` : String(receiptError);
681
- throw new Error(
682
- `Unable to derive delivered ${token.symbol} amount from ${onChainTxHash}: ${errorDetail}`
683
- );
684
- }
685
- function matchesOnRampTransaction(record, transactionId) {
686
- return record.transaction_id === transactionId || record.external_transaction_id === transactionId || record.moonpay_transaction_id === transactionId;
687
- }
688
- function getOnRampVerificationKey(record) {
689
- return record.on_chain_tx_hash ?? record.transaction_id;
690
- }
691
- function summariseMoonPayEventProps(props) {
692
- return {
693
- id: props.id,
694
- externalTransactionId: props.externalTransactionId,
695
- status: props.status,
696
- walletAddress: props.walletAddress,
697
- walletAddressTag: props.walletAddressTag,
698
- baseCurrencyAmount: props.baseCurrencyAmount,
699
- quoteCurrencyAmount: props.quoteCurrencyAmount,
700
- baseCurrency: props.baseCurrency,
701
- quoteCurrency: props.quoteCurrency,
702
- createdAt: props.createdAt
703
- };
704
- }
705
- function summariseMoonPayUrl(url) {
706
- try {
707
- const parsed = new URL(url);
708
- const params = parsed.searchParams;
709
- return {
710
- origin: parsed.origin,
711
- pathname: parsed.pathname,
712
- apiKeyPrefix: params.get("apiKey")?.slice(0, 8) ?? null,
713
- currencyCode: params.get("currencyCode"),
714
- baseCurrencyCode: params.get("baseCurrencyCode"),
715
- baseCurrencyAmount: params.get("baseCurrencyAmount"),
716
- walletAddress: params.get("walletAddress"),
717
- externalCustomerId: params.get("externalCustomerId"),
718
- externalTransactionId: params.get("externalTransactionId"),
719
- redirectURL: params.get("redirectURL"),
720
- signaturePresent: params.has("signature")
721
- };
722
- } catch {
723
- return { parseError: true, length: url.length };
724
- }
725
- }
726
- function errorPayload(err) {
727
- if (err instanceof Error) {
728
- return {
729
- name: err.name,
730
- message: err.message,
731
- stack: err.stack?.split("\n").slice(0, 4).join("\n")
732
- };
733
- }
734
- return { message: String(err) };
735
- }
736
- function FiatOnRampForm({
737
- tokenId,
738
- currencyCode,
739
- baseCurrencyCode = "usd",
740
- defaultBaseCurrencyAmount = "100",
741
- tokenSymbol,
742
- theme,
743
- themeId,
744
- colorCode,
745
- variant = "overlay",
746
- lockAmount,
747
- paymentMethod,
748
- onCredited,
749
- onError,
750
- onDebugEvent
751
- }) {
752
- const { address } = wagmi.useAccount();
753
- const [visible, setVisible] = react.useState(false);
754
- const [isPreparing, setIsPreparing] = react.useState(false);
755
- const [rowError, setRowError] = react.useState(null);
756
- const {
757
- status,
758
- activeIntentId,
759
- pending,
760
- error,
761
- finalityProgress,
762
- depositAddress,
763
- minDepositBaseUnits,
764
- selectedToken,
765
- prepareOnRampIntent,
766
- signUrl,
767
- handleTransactionCreated,
768
- handleTransactionCompleted,
769
- finishPendingVerification,
770
- handleWidgetClosed
771
- } = useFiatOnRamp({ tokenId, onCredited, onError, onDebugEvent });
772
- const decimals = selectedToken?.decimals;
773
- const displaySymbol = tokenSymbol ?? selectedToken?.symbol ?? currencyCode.toUpperCase();
774
- const overlayNode = react.useMemo(
775
- () => variant === "overlay" ? buildOverlayNode() : void 0,
776
- [variant]
777
- );
778
- const emitFormDebug = react.useCallback(
779
- (event, payload) => {
780
- onDebugEvent?.({
781
- at: (/* @__PURE__ */ new Date()).toISOString(),
782
- event,
783
- status,
784
- tokenId,
785
- payload
786
- });
787
- },
788
- [onDebugEvent, status, tokenId]
789
- );
790
- const minFiatGate = minDepositBaseUnits !== void 0 && decimals !== void 0 ? Number(viem.formatUnits(minDepositBaseUnits, decimals)) * 1.05 : void 0;
791
- const isBelowMin = minFiatGate !== void 0 && Number(defaultBaseCurrencyAmount) < minFiatGate;
792
- const isBusy = isPreparing || status === "awaiting-purchase";
793
- const isInitializing = !!address && !depositAddress;
794
- const blockReasons = react.useMemo(
795
- () => [
796
- !address ? "wallet-not-connected" : null,
797
- !depositAddress ? "deposit-address-not-loaded" : null,
798
- isBusy ? `busy:${isPreparing ? "preparing" : status}` : null,
799
- visible ? "widget-open" : null,
800
- isBelowMin ? "below-minimum" : null
801
- ].filter((reason) => Boolean(reason)),
802
- [address, depositAddress, isBelowMin, isBusy, isPreparing, status, visible]
803
- );
804
- const canBuy = blockReasons.length === 0;
805
- const handleOpen = react.useCallback(async () => {
806
- if (!canBuy) {
807
- emitFormDebug("form:open-blocked", {
808
- reasons: blockReasons,
809
- currencyCode,
810
- tokenSymbol: displaySymbol,
811
- tokenDecimals: decimals ?? null,
812
- baseCurrencyCode,
813
- defaultBaseCurrencyAmount,
814
- depositAddress: depositAddress ?? null,
815
- walletAddress: address ?? null,
816
- status
817
- });
818
- return;
819
- }
820
- setIsPreparing(true);
821
- emitFormDebug("form:open-click", {
822
- currencyCode,
823
- tokenSymbol: displaySymbol,
824
- tokenDecimals: decimals ?? null,
825
- baseCurrencyCode,
826
- defaultBaseCurrencyAmount,
827
- depositAddress: depositAddress ?? null,
828
- walletConnected: Boolean(address)
829
- });
830
- try {
831
- const intent = await prepareOnRampIntent({
832
- currencyCode,
833
- baseCurrencyCode,
834
- baseCurrencyAmount: defaultBaseCurrencyAmount
835
- });
836
- emitFormDebug("form:intent-ready", {
837
- transactionId: intent.transaction_id,
838
- externalTransactionId: intent.external_transaction_id ?? null
839
- });
840
- setVisible(true);
841
- } catch (err) {
842
- const error2 = err instanceof Error ? err : new Error("Failed to prepare MoonPay on-ramp");
843
- emitFormDebug("form:intent-error", {
844
- name: error2.name,
845
- message: error2.message
846
- });
847
- } finally {
848
- setIsPreparing(false);
849
- }
850
- }, [
851
- address,
852
- baseCurrencyCode,
853
- blockReasons,
854
- canBuy,
855
- currencyCode,
856
- decimals,
857
- displaySymbol,
858
- defaultBaseCurrencyAmount,
859
- depositAddress,
860
- emitFormDebug,
861
- prepareOnRampIntent,
862
- status
863
- ]);
864
- const handleClose = react.useCallback(async () => {
865
- emitFormDebug("moonpay:onClose");
866
- setVisible(false);
867
- await handleWidgetClosed();
868
- }, [emitFormDebug, handleWidgetClosed]);
869
- const handleCloseOverlay = react.useCallback(async () => {
870
- emitFormDebug("moonpay:onCloseOverlay");
871
- setVisible(false);
872
- await handleWidgetClosed();
873
- }, [emitFormDebug, handleWidgetClosed]);
874
- const handleReady = react.useCallback(async () => {
875
- emitFormDebug("moonpay:onReady");
876
- }, [emitFormDebug]);
877
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-privana": true, className: "flex flex-col gap-4", children: [
878
- isInitializing ? /* @__PURE__ */ jsxRuntime.jsx(chunkNMRIKUDN_cjs.Skeleton, { className: "h-9 w-full rounded-md" }) : /* @__PURE__ */ jsxRuntime.jsxs(chunkNMRIKUDN_cjs.Button, { type: "button", onClick: handleOpen, disabled: !canBuy, children: [
879
- (isBusy || visible) && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "animate-spin", "aria-hidden": true }),
880
- "Buy"
881
- ] }),
882
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-destructive text-sm", role: "alert", children: error.message }),
883
- isBelowMin && minFiatGate !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-destructive text-sm", role: "alert", children: [
884
- "Minimum purchase is ~$",
885
- minFiatGate.toFixed(2),
886
- "."
887
- ] }),
888
- pending.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [
889
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: "Pending payments" }),
890
- pending.map((record) => {
891
- const progress = parseFinalityProgress(finalityProgress[record.transaction_id]);
892
- const hasProgress = !!finalityProgress[record.transaction_id];
893
- const isStalled = !hasProgress && Date.now() / 1e3 - (record.updated_at ?? 0) > 60;
894
- const showRetry = rowError?.id === record.transaction_id || isStalled;
895
- return /* @__PURE__ */ jsxRuntime.jsxs(
896
- "div",
897
- {
898
- className: "border-border flex flex-col gap-1 rounded-md border p-2",
899
- children: [
900
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
901
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground flex items-center gap-1 text-xs", children: [
902
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "size-3 animate-spin", "aria-hidden": true }),
903
- progress ?? "Verifying\u2026"
904
- ] }),
905
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground text-xs", children: [
906
- record.quote_currency_amount ?? "?",
907
- " ",
908
- displaySymbol
909
- ] })
910
- ] }),
911
- rowError?.id === record.transaction_id && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-destructive text-xs", children: rowError.message }),
912
- showRetry && /* @__PURE__ */ jsxRuntime.jsx(
913
- chunkNMRIKUDN_cjs.Button,
914
- {
915
- type: "button",
916
- variant: "outline",
917
- size: "sm",
918
- onClick: async () => {
919
- setRowError(null);
920
- try {
921
- await finishPendingVerification(record);
922
- } catch (err) {
923
- setRowError({
924
- id: record.transaction_id,
925
- message: err instanceof Error ? err.message : "Verification failed"
926
- });
927
- }
928
- },
929
- children: "Retry"
930
- }
931
- )
932
- ]
933
- },
934
- record.transaction_id
935
- );
936
- })
937
- ] }),
938
- visible && depositAddress && activeIntentId && /* @__PURE__ */ jsxRuntime.jsx(
939
- moonpayReact.MoonPayBuyWidget,
940
- {
941
- variant,
942
- visible: true,
943
- theme,
944
- themeId,
945
- colorCode,
946
- overlayNode,
947
- baseCurrencyCode,
948
- baseCurrencyAmount: defaultBaseCurrencyAmount,
949
- lockAmount: lockAmount ? "true" : void 0,
950
- paymentMethod,
951
- currencyCode,
952
- walletAddress: depositAddress,
953
- externalCustomerId: address?.toLowerCase(),
954
- externalTransactionId: activeIntentId,
955
- onClose: handleClose,
956
- onCloseOverlay: handleCloseOverlay,
957
- onReady: handleReady,
958
- onUrlSignatureRequested: signUrl,
959
- onTransactionCreated: handleTransactionCreated,
960
- onTransactionCompleted: handleTransactionCompleted
961
- }
962
- )
963
- ] });
964
- }
965
- function parseFinalityProgress(message) {
966
- if (!message) return null;
967
- const match = message.match(/(\d+\/\d+)\s+confirmations/i);
968
- return match ? `${match[1]} confirmations` : null;
969
- }
970
- function buildOverlayNode() {
971
- if (typeof document === "undefined") return void 0;
972
- const wrap = document.createElement("div");
973
- wrap.style.cssText = "display:flex;flex-direction:column;align-items:center;gap:12px";
974
- wrap.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="139" height="20.61" viewBox="0 0 139 20.61"><path d="M15.08,20.61L10.49,20.61L10.49,0.00L17.96,0.00L18.81,0.02L19.62,0.10L20.38,0.22L21.11,0.39L21.79,0.60L22.43,0.87L23.03,1.18L23.58,1.53L24.08,1.92L24.53,2.35L24.93,2.82L25.29,3.33L25.59,3.88L25.84,4.47L26.03,5.09L26.17,5.75L26.25,6.44L26.28,7.17L26.28,7.17L26.28,7.62L26.25,8.33L26.17,9.01L26.03,9.66L25.84,10.27L25.59,10.86L25.29,11.41L24.93,11.93L24.53,12.40L24.08,12.83L23.58,13.23L23.03,13.58L22.43,13.89L21.79,14.15L21.11,14.37L20.38,14.54L19.62,14.66L18.81,14.73L17.96,14.76L17.96,14.76L15.08,14.76L15.08,20.61ZM18.19,3.98L15.08,3.98L15.08,10.78L18.19,10.78L18.53,10.77L18.87,10.73L19.18,10.68L19.48,10.59L19.76,10.49L20.02,10.36L20.27,10.21L20.50,10.04L20.70,9.85L20.89,9.64L21.06,9.42L21.21,9.17L21.34,8.91L21.45,8.63L21.53,8.34L21.59,8.04L21.62,7.72L21.63,7.39L21.63,7.39L21.62,7.05L21.59,6.72L21.53,6.40L21.45,6.10L21.34,5.82L21.21,5.56L21.06,5.31L20.89,5.09L20.70,4.88L20.50,4.69L20.27,4.53L20.02,4.38L19.76,4.26L19.48,4.16L19.18,4.08L18.87,4.02L18.53,3.99L18.19,3.98L18.19,3.98ZM31.76,20.61L27.16,20.61L27.16,0.00L35.20,0.00L36.03,0.02L36.82,0.09L37.58,0.19L38.30,0.34L38.98,0.53L39.62,0.77L40.23,1.05L40.78,1.37L41.28,1.73L41.74,2.13L42.15,2.57L42.51,3.05L42.82,3.58L43.07,4.15L43.26,4.76L43.40,5.42L43.49,6.12L43.52,6.86L43.52,6.86L43.52,7.31L43.49,8.04L43.40,8.73L43.26,9.38L43.07,9.98L42.82,10.54L42.51,11.06L42.51,11.06L42.15,11.53L41.75,11.97L41.30,12.36L40.80,12.72L40.26,13.03L39.68,13.30L39.68,13.30L44.89,20.61L39.57,20.61L35.12,14.06L31.76,14.06L31.76,20.61ZM35.56,3.89L31.76,3.89L31.76,10.44L35.56,10.44L35.89,10.43L36.21,10.40L36.51,10.34L36.79,10.26L37.06,10.16L37.31,10.04L37.55,9.89L37.77,9.73L37.97,9.55L38.15,9.35L38.32,9.13L38.46,8.89L38.59,8.64L38.69,8.37L38.77,8.09L38.82,7.79L38.86,7.49L38.87,7.17L38.86,6.85L38.82,6.54L38.77,6.25L38.69,5.97L38.59,5.70L38.46,5.45L38.32,5.21L38.15,4.99L37.97,4.79L37.77,4.61L37.55,4.44L37.31,4.30L37.06,4.17L36.79,4.07L36.51,3.99L36.21,3.94L35.89,3.90L35.56,3.89L35.56,3.89ZM50.25,20.61L45.66,20.61L45.66,0.17L50.25,0.17L50.25,20.61ZM64.88,20.61L57.41,20.61L51.19,0.17L55.92,0.17L60.71,16.88L61.66,16.88L66.09,0.17L70.68,0.17L64.88,20.61ZM71.44,20.61L66.85,20.61L73.60,0.17L81.02,0.17L88.02,20.61L83.26,20.61L81.61,15.54L73.07,15.54L71.44,20.61ZM74.27,11.73L80.35,11.73L77.80,3.92L76.76,3.92L74.27,11.73ZM92.94,20.61L88.68,20.61L88.68,0.17L96.21,0.17L103.91,16.88L104.30,16.88L104.30,0.17L108.62,0.17L108.62,20.61L101.03,20.61L93.33,3.89L92.94,3.89L92.94,20.61ZM113.87,20.61L109.28,20.61L116.02,0.17L123.44,0.17L130.44,20.61L125.68,20.61L124.03,15.54L115.49,15.54L113.87,20.61ZM116.70,11.73L122.77,11.73L120.22,3.92L119.19,3.92L116.70,11.73Z" fill="#ffffff"/></svg><span>Your secure checkout is loading</span>`;
975
- return wrap;
976
- }
977
6
 
978
- exports.FiatOnRampForm = FiatOnRampForm;
979
- exports.useFiatOnRamp = useFiatOnRamp;
7
+
8
+ Object.defineProperty(exports, "FiatOnRampForm", {
9
+ enumerable: true,
10
+ get: function () { return chunkACLJPC75_cjs.FiatOnRampForm; }
11
+ });
12
+ Object.defineProperty(exports, "useFiatOnRamp", {
13
+ enumerable: true,
14
+ get: function () { return chunkACLJPC75_cjs.useFiatOnRamp; }
15
+ });
980
16
  //# sourceMappingURL=on-ramp.cjs.map
981
17
  //# sourceMappingURL=on-ramp.cjs.map