@oasisprotocol/privana-sdk 0.4.0 → 0.4.1
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/{chunk-RDMJGMI3.cjs → chunk-CRJNDCDT.cjs} +19 -3
- package/dist/chunk-CRJNDCDT.cjs.map +1 -0
- package/dist/{chunk-ZVNC4FTJ.js → chunk-ZXFQJ5MG.js} +19 -4
- package/dist/chunk-ZXFQJ5MG.js.map +1 -0
- package/dist/index.cjs +150 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +3 -13
- package/dist/index.js.map +1 -1
- package/dist/on-ramp.cjs +149 -52
- package/dist/on-ramp.cjs.map +1 -1
- package/dist/on-ramp.d.cts +6 -0
- package/dist/on-ramp.d.ts +6 -0
- package/dist/on-ramp.js +146 -49
- package/dist/on-ramp.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-RDMJGMI3.cjs.map +0 -1
- package/dist/chunk-ZVNC4FTJ.js.map +0 -1
package/dist/on-ramp.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { usePrivanaContext, usePrivateReadRequest, useDepositVerification,
|
|
2
|
+
import { usePrivanaContext, usePrivateReadRequest, useDepositVerification, waitForTransactionReceipt, Skeleton, Button } from './chunk-ZXFQJ5MG.js';
|
|
3
3
|
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
4
4
|
import { parseAbiItem, zeroAddress, parseUnits, decodeEventLog, formatUnits } from 'viem';
|
|
5
5
|
import { useConfig, useAccount } from 'wagmi';
|
|
6
|
+
import { Loader2 } from 'lucide-react';
|
|
6
7
|
import { MoonPayBuyWidget } from '@moonpay/moonpay-react';
|
|
7
8
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
8
9
|
|
|
@@ -28,6 +29,7 @@ function useFiatOnRamp(options) {
|
|
|
28
29
|
const [depositAddress, setDepositAddress] = useState();
|
|
29
30
|
const [minDepositBaseUnits, setMinDepositBaseUnits] = useState();
|
|
30
31
|
const [activeIntentId, setActiveIntentId] = useState(null);
|
|
32
|
+
const [finalityProgress, setFinalityProgress] = useState({});
|
|
31
33
|
const onCreditedRef = useRef(onCredited);
|
|
32
34
|
const onErrorRef = useRef(onError);
|
|
33
35
|
const onDebugEventRef = useRef(onDebugEvent);
|
|
@@ -37,6 +39,7 @@ function useFiatOnRamp(options) {
|
|
|
37
39
|
const activeVerificationKeyRef = useRef(null);
|
|
38
40
|
const triggeredVerificationKeysRef = useRef(/* @__PURE__ */ new Set());
|
|
39
41
|
const closeReconcilePromiseRef = useRef(null);
|
|
42
|
+
const purchaseInitiatedRef = useRef(false);
|
|
40
43
|
useEffect(() => {
|
|
41
44
|
onCreditedRef.current = onCredited;
|
|
42
45
|
onErrorRef.current = onError;
|
|
@@ -132,13 +135,32 @@ function useFiatOnRamp(options) {
|
|
|
132
135
|
pollTimeout: verificationTimeout,
|
|
133
136
|
pollInterval: options.verificationPollInterval,
|
|
134
137
|
finalityRetryInterval,
|
|
138
|
+
onCheckRetry: (message) => {
|
|
139
|
+
const record = activeVerificationRecordRef.current;
|
|
140
|
+
if (!record) return;
|
|
141
|
+
emitDebug("verification:check-retry", {
|
|
142
|
+
message,
|
|
143
|
+
record: summariseOnRampRecord(record)
|
|
144
|
+
});
|
|
145
|
+
setFinalityProgress((prev) => ({ ...prev, [record.transaction_id]: message }));
|
|
146
|
+
},
|
|
135
147
|
onCredited: (depositTxHash) => {
|
|
136
148
|
const record = activeVerificationRecordRef.current;
|
|
137
149
|
emitDebug("verification:credited", {
|
|
138
150
|
depositTxHash,
|
|
139
151
|
record: record ? summariseOnRampRecord(record) : null
|
|
140
152
|
});
|
|
141
|
-
|
|
153
|
+
if (record && activeIntentIdRef.current === record.transaction_id) {
|
|
154
|
+
setStatus("credited");
|
|
155
|
+
}
|
|
156
|
+
if (record) {
|
|
157
|
+
setFinalityProgress((prev) => {
|
|
158
|
+
if (!(record.transaction_id in prev)) return prev;
|
|
159
|
+
const next = { ...prev };
|
|
160
|
+
delete next[record.transaction_id];
|
|
161
|
+
return next;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
142
164
|
void (async () => {
|
|
143
165
|
try {
|
|
144
166
|
if (record && depositTxHash.startsWith("0x")) {
|
|
@@ -170,21 +192,27 @@ function useFiatOnRamp(options) {
|
|
|
170
192
|
onCreditedRef.current?.(depositTxHash);
|
|
171
193
|
},
|
|
172
194
|
onCheckTimeout: (depositTxHash) => {
|
|
195
|
+
const record = activeVerificationRecordRef.current;
|
|
173
196
|
const err = new Error(
|
|
174
197
|
"Privana verification is still pending. Retry from the pending on-ramp list if it does not complete."
|
|
175
198
|
);
|
|
176
199
|
emitDebug("verification:timeout", { depositTxHash, message: err.message });
|
|
177
200
|
clearActiveVerification();
|
|
178
|
-
|
|
179
|
-
|
|
201
|
+
if (!record || activeIntentIdRef.current === record.transaction_id) {
|
|
202
|
+
setStatus("failed");
|
|
203
|
+
setError(err);
|
|
204
|
+
}
|
|
180
205
|
void refreshPending();
|
|
181
206
|
onErrorRef.current?.(err);
|
|
182
207
|
},
|
|
183
208
|
onError: (err) => {
|
|
209
|
+
const record = activeVerificationRecordRef.current;
|
|
184
210
|
emitDebug("verification:error", errorPayload(err));
|
|
185
211
|
clearActiveVerification();
|
|
186
|
-
|
|
187
|
-
|
|
212
|
+
if (!record || activeIntentIdRef.current === record.transaction_id) {
|
|
213
|
+
setStatus("failed");
|
|
214
|
+
setError(err);
|
|
215
|
+
}
|
|
188
216
|
onErrorRef.current?.(err);
|
|
189
217
|
}
|
|
190
218
|
});
|
|
@@ -196,6 +224,7 @@ function useFiatOnRamp(options) {
|
|
|
196
224
|
}) => {
|
|
197
225
|
try {
|
|
198
226
|
setError(null);
|
|
227
|
+
purchaseInitiatedRef.current = false;
|
|
199
228
|
const token = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
|
|
200
229
|
if (!token) throw new Error(`Unknown token: ${tokenId}`);
|
|
201
230
|
if (!depositAddress) throw new Error("Privana deposit address is not ready");
|
|
@@ -278,6 +307,7 @@ function useFiatOnRamp(options) {
|
|
|
278
307
|
const handleTransactionCreated = useCallback(
|
|
279
308
|
async (props) => {
|
|
280
309
|
emitDebug("moonpay:onTransactionCreated", summariseMoonPayEventProps(props));
|
|
310
|
+
purchaseInitiatedRef.current = true;
|
|
281
311
|
await registerOnRampTokenMapping(props.id);
|
|
282
312
|
},
|
|
283
313
|
[emitDebug, registerOnRampTokenMapping]
|
|
@@ -356,6 +386,12 @@ function useFiatOnRamp(options) {
|
|
|
356
386
|
triggeredVerificationKeysRef.current.add(verificationKey);
|
|
357
387
|
activeVerificationKeyRef.current = verificationKey;
|
|
358
388
|
activeVerificationRecordRef.current = record;
|
|
389
|
+
setFinalityProgress((prev) => {
|
|
390
|
+
if (!(record.transaction_id in prev)) return prev;
|
|
391
|
+
const next = { ...prev };
|
|
392
|
+
delete next[record.transaction_id];
|
|
393
|
+
return next;
|
|
394
|
+
});
|
|
359
395
|
emitDebug("verification:start", {
|
|
360
396
|
verificationKey,
|
|
361
397
|
record: summariseOnRampRecord(record)
|
|
@@ -396,7 +432,9 @@ function useFiatOnRamp(options) {
|
|
|
396
432
|
`Delivered amount (${record.quote_currency_amount}) is below the minimum deposit.`
|
|
397
433
|
);
|
|
398
434
|
}
|
|
399
|
-
|
|
435
|
+
if (activeIntentIdRef.current === record.transaction_id) {
|
|
436
|
+
setStatus("verifying");
|
|
437
|
+
}
|
|
400
438
|
emitDebug("verification:check-deposit-request", {
|
|
401
439
|
hash: record.on_chain_tx_hash,
|
|
402
440
|
chainId: record.chain_id,
|
|
@@ -464,6 +502,17 @@ function useFiatOnRamp(options) {
|
|
|
464
502
|
await refreshPending();
|
|
465
503
|
return;
|
|
466
504
|
}
|
|
505
|
+
if (!purchaseInitiatedRef.current) {
|
|
506
|
+
emitDebug("moonpay:widget-closed-without-purchase", {
|
|
507
|
+
previousStatus,
|
|
508
|
+
transactionId
|
|
509
|
+
});
|
|
510
|
+
await refreshPending();
|
|
511
|
+
if (previousStatus === "awaiting-purchase" || previousStatus === "awaiting-delivery") {
|
|
512
|
+
setStatus("idle");
|
|
513
|
+
}
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
467
516
|
try {
|
|
468
517
|
setStatus("awaiting-delivery");
|
|
469
518
|
const record = await waitForOnChainHash(transactionId);
|
|
@@ -502,15 +551,39 @@ function useFiatOnRamp(options) {
|
|
|
502
551
|
setStatus("failed");
|
|
503
552
|
setError(e);
|
|
504
553
|
onErrorRef.current?.(e);
|
|
554
|
+
throw e;
|
|
505
555
|
}
|
|
506
556
|
},
|
|
507
557
|
[emitDebug, triggerVerification]
|
|
508
558
|
);
|
|
559
|
+
const triggerVerificationRef = useRef(triggerVerification);
|
|
560
|
+
useEffect(() => {
|
|
561
|
+
triggerVerificationRef.current = triggerVerification;
|
|
562
|
+
}, [triggerVerification]);
|
|
563
|
+
useEffect(() => {
|
|
564
|
+
let cancelled = false;
|
|
565
|
+
void (async () => {
|
|
566
|
+
for (const record of pending) {
|
|
567
|
+
if (cancelled) break;
|
|
568
|
+
if (!record.on_chain_tx_hash || !record.quote_currency_amount) continue;
|
|
569
|
+
const key = getOnRampVerificationKey(record);
|
|
570
|
+
if (triggeredVerificationKeysRef.current.has(key)) continue;
|
|
571
|
+
try {
|
|
572
|
+
await triggerVerificationRef.current(record);
|
|
573
|
+
} catch {
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
})();
|
|
577
|
+
return () => {
|
|
578
|
+
cancelled = true;
|
|
579
|
+
};
|
|
580
|
+
}, [pending]);
|
|
509
581
|
return {
|
|
510
582
|
status,
|
|
511
583
|
activeIntentId,
|
|
512
584
|
pending,
|
|
513
585
|
error,
|
|
586
|
+
finalityProgress,
|
|
514
587
|
depositAddress,
|
|
515
588
|
minDepositBaseUnits,
|
|
516
589
|
selectedToken,
|
|
@@ -562,9 +635,11 @@ async function resolveDeliveredAmount({
|
|
|
562
635
|
}
|
|
563
636
|
let receiptError;
|
|
564
637
|
try {
|
|
565
|
-
const receipt = await
|
|
638
|
+
const receipt = await waitForTransactionReceipt(wagmiConfig, {
|
|
566
639
|
hash: onChainTxHash,
|
|
567
|
-
chainId
|
|
640
|
+
chainId,
|
|
641
|
+
timeout: 6e4,
|
|
642
|
+
pollingInterval: 4e3
|
|
568
643
|
});
|
|
569
644
|
let delivered = 0n;
|
|
570
645
|
for (const log of receipt.logs) {
|
|
@@ -669,11 +744,13 @@ function FiatOnRampForm({
|
|
|
669
744
|
const { address } = useAccount();
|
|
670
745
|
const [visible, setVisible] = useState(false);
|
|
671
746
|
const [isPreparing, setIsPreparing] = useState(false);
|
|
747
|
+
const [rowError, setRowError] = useState(null);
|
|
672
748
|
const {
|
|
673
749
|
status,
|
|
674
750
|
activeIntentId,
|
|
675
751
|
pending,
|
|
676
752
|
error,
|
|
753
|
+
finalityProgress,
|
|
677
754
|
depositAddress,
|
|
678
755
|
minDepositBaseUnits,
|
|
679
756
|
selectedToken,
|
|
@@ -700,15 +777,17 @@ function FiatOnRampForm({
|
|
|
700
777
|
);
|
|
701
778
|
const minFiatGate = minDepositBaseUnits !== void 0 && decimals !== void 0 ? Number(formatUnits(minDepositBaseUnits, decimals)) * 1.05 : void 0;
|
|
702
779
|
const isBelowMin = minFiatGate !== void 0 && Number(defaultBaseCurrencyAmount) < minFiatGate;
|
|
703
|
-
const isBusy = isPreparing || status === "awaiting-purchase"
|
|
780
|
+
const isBusy = isPreparing || status === "awaiting-purchase";
|
|
781
|
+
const isInitializing = !!address && !depositAddress;
|
|
704
782
|
const blockReasons = useMemo(
|
|
705
783
|
() => [
|
|
706
784
|
!address ? "wallet-not-connected" : null,
|
|
707
785
|
!depositAddress ? "deposit-address-not-loaded" : null,
|
|
708
786
|
isBusy ? `busy:${isPreparing ? "preparing" : status}` : null,
|
|
787
|
+
visible ? "widget-open" : null,
|
|
709
788
|
isBelowMin ? "below-minimum" : null
|
|
710
789
|
].filter((reason) => Boolean(reason)),
|
|
711
|
-
[address, depositAddress, isBelowMin, isBusy, isPreparing, status]
|
|
790
|
+
[address, depositAddress, isBelowMin, isBusy, isPreparing, status, visible]
|
|
712
791
|
);
|
|
713
792
|
const canBuy = blockReasons.length === 0;
|
|
714
793
|
const handleOpen = useCallback(async () => {
|
|
@@ -783,11 +862,10 @@ function FiatOnRampForm({
|
|
|
783
862
|
const handleReady = useCallback(async () => {
|
|
784
863
|
emitFormDebug("moonpay:onReady");
|
|
785
864
|
}, [emitFormDebug]);
|
|
786
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
787
|
-
/* @__PURE__ */ jsx(
|
|
788
|
-
|
|
789
|
-
"
|
|
790
|
-
blockReasons.join(", ")
|
|
865
|
+
return /* @__PURE__ */ jsxs("div", { "data-privana": true, className: "flex flex-col gap-4", children: [
|
|
866
|
+
isInitializing ? /* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full rounded-md" }) : /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleOpen, disabled: !canBuy, children: [
|
|
867
|
+
(isBusy || visible) && /* @__PURE__ */ jsx(Loader2, { className: "animate-spin", "aria-hidden": true }),
|
|
868
|
+
"Buy"
|
|
791
869
|
] }),
|
|
792
870
|
error && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", role: "alert", children: error.message }),
|
|
793
871
|
isBelowMin && minFiatGate !== void 0 && /* @__PURE__ */ jsxs("p", { className: "text-destructive text-sm", role: "alert", children: [
|
|
@@ -796,24 +874,54 @@ function FiatOnRampForm({
|
|
|
796
874
|
"."
|
|
797
875
|
] }),
|
|
798
876
|
pending.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [
|
|
799
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
877
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Pending payments" }),
|
|
878
|
+
pending.map((record) => {
|
|
879
|
+
const progress = parseFinalityProgress(finalityProgress[record.transaction_id]);
|
|
880
|
+
const hasProgress = !!finalityProgress[record.transaction_id];
|
|
881
|
+
const isStalled = !hasProgress && Date.now() / 1e3 - (record.updated_at ?? 0) > 60;
|
|
882
|
+
const showRetry = rowError?.id === record.transaction_id || isStalled;
|
|
883
|
+
return /* @__PURE__ */ jsxs(
|
|
884
|
+
"div",
|
|
885
|
+
{
|
|
886
|
+
className: "border-border flex flex-col gap-1 rounded-md border p-2",
|
|
887
|
+
children: [
|
|
888
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
889
|
+
/* @__PURE__ */ jsxs("p", { className: "text-muted-foreground flex items-center gap-1 text-xs", children: [
|
|
890
|
+
/* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin", "aria-hidden": true }),
|
|
891
|
+
progress ?? "Verifying\u2026"
|
|
892
|
+
] }),
|
|
893
|
+
/* @__PURE__ */ jsxs("p", { className: "text-muted-foreground text-xs", children: [
|
|
894
|
+
record.quote_currency_amount ?? "?",
|
|
895
|
+
" ",
|
|
896
|
+
displaySymbol
|
|
897
|
+
] })
|
|
898
|
+
] }),
|
|
899
|
+
rowError?.id === record.transaction_id && /* @__PURE__ */ jsx("p", { className: "text-destructive text-xs", children: rowError.message }),
|
|
900
|
+
showRetry && /* @__PURE__ */ jsx(
|
|
901
|
+
Button,
|
|
902
|
+
{
|
|
903
|
+
type: "button",
|
|
904
|
+
variant: "outline",
|
|
905
|
+
size: "sm",
|
|
906
|
+
onClick: async () => {
|
|
907
|
+
setRowError(null);
|
|
908
|
+
try {
|
|
909
|
+
await finishPendingVerification(record);
|
|
910
|
+
} catch (err) {
|
|
911
|
+
setRowError({
|
|
912
|
+
id: record.transaction_id,
|
|
913
|
+
message: err instanceof Error ? err.message : "Verification failed"
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
},
|
|
917
|
+
children: "Retry"
|
|
918
|
+
}
|
|
919
|
+
)
|
|
920
|
+
]
|
|
921
|
+
},
|
|
922
|
+
record.transaction_id
|
|
923
|
+
);
|
|
924
|
+
})
|
|
817
925
|
] }),
|
|
818
926
|
visible && depositAddress && activeIntentId && /* @__PURE__ */ jsx(
|
|
819
927
|
MoonPayBuyWidget,
|
|
@@ -836,21 +944,10 @@ function FiatOnRampForm({
|
|
|
836
944
|
)
|
|
837
945
|
] });
|
|
838
946
|
}
|
|
839
|
-
function
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
case "awaiting-delivery":
|
|
844
|
-
return "Waiting for on-chain delivery\u2026";
|
|
845
|
-
case "verifying":
|
|
846
|
-
return "Verifying with Privana\u2026";
|
|
847
|
-
case "credited":
|
|
848
|
-
return "Credited";
|
|
849
|
-
case "failed":
|
|
850
|
-
return "Failed";
|
|
851
|
-
default:
|
|
852
|
-
return "";
|
|
853
|
-
}
|
|
947
|
+
function parseFinalityProgress(message) {
|
|
948
|
+
if (!message) return null;
|
|
949
|
+
const match = message.match(/(\d+\/\d+)\s+confirmations/i);
|
|
950
|
+
return match ? `${match[1]} confirmations` : null;
|
|
854
951
|
}
|
|
855
952
|
|
|
856
953
|
export { FiatOnRampForm, useFiatOnRamp };
|