@oasisprotocol/privana-sdk 0.3.0 → 0.4.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/README.md +137 -17
- package/dist/chunk-RDMJGMI3.cjs +1757 -0
- package/dist/chunk-RDMJGMI3.cjs.map +1 -0
- package/dist/chunk-ZVNC4FTJ.js +1704 -0
- package/dist/chunk-ZVNC4FTJ.js.map +1 -0
- package/dist/index.cjs +323 -1718
- 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 +61 -60
- package/dist/index.d.ts +61 -60
- package/dist/index.js +52 -1576
- package/dist/index.js.map +1 -1
- package/dist/on-ramp-DIAOtdeU.d.cts +115 -0
- package/dist/on-ramp-DIAOtdeU.d.ts +115 -0
- package/dist/on-ramp.cjs +861 -0
- package/dist/on-ramp.cjs.map +1 -0
- package/dist/on-ramp.d.cts +137 -0
- package/dist/on-ramp.d.ts +137 -0
- package/dist/on-ramp.js +858 -0
- package/dist/on-ramp.js.map +1 -0
- package/package.json +12 -1
package/dist/on-ramp.js
ADDED
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { usePrivanaContext, usePrivateReadRequest, useDepositVerification, getTransactionReceipt, Button } from './chunk-ZVNC4FTJ.js';
|
|
3
|
+
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
4
|
+
import { parseAbiItem, zeroAddress, parseUnits, decodeEventLog, formatUnits } from 'viem';
|
|
5
|
+
import { useConfig, useAccount } from 'wagmi';
|
|
6
|
+
import { MoonPayBuyWidget } from '@moonpay/moonpay-react';
|
|
7
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
var DEFAULT_DELIVERY_TIMEOUT_MS = 12e4;
|
|
10
|
+
var DEFAULT_VERIFICATION_TIMEOUT_MS = 10 * 6e4;
|
|
11
|
+
var DEFAULT_FINALITY_RETRY_INTERVAL_MS = 15e3;
|
|
12
|
+
var ERC20_TRANSFER_EVENT = parseAbiItem(
|
|
13
|
+
"event Transfer(address indexed from, address indexed to, uint256 value)"
|
|
14
|
+
);
|
|
15
|
+
function useFiatOnRamp(options) {
|
|
16
|
+
const { tokenId, onCredited, onError, onDebugEvent } = options;
|
|
17
|
+
const deliveryTimeout = options.deliveryTimeout ?? DEFAULT_DELIVERY_TIMEOUT_MS;
|
|
18
|
+
const deliveryPollInterval = options.deliveryPollInterval ?? 3e3;
|
|
19
|
+
const verificationTimeout = options.verificationTimeout ?? DEFAULT_VERIFICATION_TIMEOUT_MS;
|
|
20
|
+
const finalityRetryInterval = options.finalityRetryInterval ?? DEFAULT_FINALITY_RETRY_INTERVAL_MS;
|
|
21
|
+
const { client, enabledTokens } = usePrivanaContext();
|
|
22
|
+
const { executePrivateRead, privateReadReady } = usePrivateReadRequest();
|
|
23
|
+
const wagmiConfig = useConfig();
|
|
24
|
+
const selectedToken = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
|
|
25
|
+
const [status, setStatus] = useState("idle");
|
|
26
|
+
const [pending, setPending] = useState([]);
|
|
27
|
+
const [error, setError] = useState(null);
|
|
28
|
+
const [depositAddress, setDepositAddress] = useState();
|
|
29
|
+
const [minDepositBaseUnits, setMinDepositBaseUnits] = useState();
|
|
30
|
+
const [activeIntentId, setActiveIntentId] = useState(null);
|
|
31
|
+
const onCreditedRef = useRef(onCredited);
|
|
32
|
+
const onErrorRef = useRef(onError);
|
|
33
|
+
const onDebugEventRef = useRef(onDebugEvent);
|
|
34
|
+
const statusRef = useRef(status);
|
|
35
|
+
const activeIntentIdRef = useRef(null);
|
|
36
|
+
const activeVerificationRecordRef = useRef(null);
|
|
37
|
+
const activeVerificationKeyRef = useRef(null);
|
|
38
|
+
const triggeredVerificationKeysRef = useRef(/* @__PURE__ */ new Set());
|
|
39
|
+
const closeReconcilePromiseRef = useRef(null);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
onCreditedRef.current = onCredited;
|
|
42
|
+
onErrorRef.current = onError;
|
|
43
|
+
onDebugEventRef.current = onDebugEvent;
|
|
44
|
+
}, [onCredited, onError, onDebugEvent]);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
statusRef.current = status;
|
|
47
|
+
}, [status]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
activeIntentIdRef.current = activeIntentId;
|
|
50
|
+
}, [activeIntentId]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
activeIntentIdRef.current = null;
|
|
53
|
+
setActiveIntentId(null);
|
|
54
|
+
}, [tokenId]);
|
|
55
|
+
const emitDebug = useCallback(
|
|
56
|
+
(event, payload) => {
|
|
57
|
+
onDebugEventRef.current?.({
|
|
58
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
59
|
+
event,
|
|
60
|
+
status: statusRef.current,
|
|
61
|
+
tokenId,
|
|
62
|
+
payload
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
[tokenId]
|
|
66
|
+
);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
emitDebug("private-read-state", { privateReadReady });
|
|
69
|
+
}, [emitDebug, privateReadReady]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (!privateReadReady) {
|
|
72
|
+
emitDebug("deposit-address:skip", { reason: "private-read-not-ready" });
|
|
73
|
+
setDepositAddress(void 0);
|
|
74
|
+
setMinDepositBaseUnits(void 0);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
let cancelled = false;
|
|
78
|
+
void (async () => {
|
|
79
|
+
try {
|
|
80
|
+
emitDebug("deposit-address:request");
|
|
81
|
+
const resp = await executePrivateRead(() => client.getDepositAddress());
|
|
82
|
+
if (cancelled) return;
|
|
83
|
+
setDepositAddress(resp.deposit_address);
|
|
84
|
+
const token = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
|
|
85
|
+
const mins = token ? resp.min_deposit?.[String(token.chainId)] : void 0;
|
|
86
|
+
if (mins?.erc20) setMinDepositBaseUnits(BigInt(mins.erc20));
|
|
87
|
+
emitDebug("deposit-address:success", {
|
|
88
|
+
depositAddress: resp.deposit_address,
|
|
89
|
+
selectedToken: token ? summariseToken(token) : null,
|
|
90
|
+
minDepositBaseUnits: mins?.erc20 ?? null
|
|
91
|
+
});
|
|
92
|
+
} catch (err) {
|
|
93
|
+
if (!cancelled) {
|
|
94
|
+
emitDebug("deposit-address:error", errorPayload(err));
|
|
95
|
+
console.warn("Failed to fetch Privana deposit address:", err);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
})();
|
|
99
|
+
return () => {
|
|
100
|
+
cancelled = true;
|
|
101
|
+
};
|
|
102
|
+
}, [client, emitDebug, enabledTokens, executePrivateRead, privateReadReady, tokenId]);
|
|
103
|
+
const refreshPending = useCallback(async () => {
|
|
104
|
+
if (!privateReadReady) {
|
|
105
|
+
emitDebug("pending:skip", { reason: "private-read-not-ready" });
|
|
106
|
+
setPending([]);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
emitDebug("pending:request");
|
|
111
|
+
const { pending: rows } = await executePrivateRead(() => client.getPendingOnRamps());
|
|
112
|
+
setPending(rows);
|
|
113
|
+
emitDebug("pending:success", {
|
|
114
|
+
count: rows.length,
|
|
115
|
+
rows: rows.map(summariseOnRampRecord)
|
|
116
|
+
});
|
|
117
|
+
} catch (err) {
|
|
118
|
+
emitDebug("pending:error", errorPayload(err));
|
|
119
|
+
console.warn("Failed to load pending on-ramps:", err);
|
|
120
|
+
}
|
|
121
|
+
}, [client, emitDebug, executePrivateRead, privateReadReady]);
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
refreshPending();
|
|
124
|
+
}, [refreshPending]);
|
|
125
|
+
const clearActiveVerification = useCallback(() => {
|
|
126
|
+
const key = activeVerificationKeyRef.current;
|
|
127
|
+
if (key) triggeredVerificationKeysRef.current.delete(key);
|
|
128
|
+
activeVerificationKeyRef.current = null;
|
|
129
|
+
activeVerificationRecordRef.current = null;
|
|
130
|
+
}, []);
|
|
131
|
+
const { verify } = useDepositVerification({
|
|
132
|
+
pollTimeout: verificationTimeout,
|
|
133
|
+
pollInterval: options.verificationPollInterval,
|
|
134
|
+
finalityRetryInterval,
|
|
135
|
+
onCredited: (depositTxHash) => {
|
|
136
|
+
const record = activeVerificationRecordRef.current;
|
|
137
|
+
emitDebug("verification:credited", {
|
|
138
|
+
depositTxHash,
|
|
139
|
+
record: record ? summariseOnRampRecord(record) : null
|
|
140
|
+
});
|
|
141
|
+
setStatus("credited");
|
|
142
|
+
void (async () => {
|
|
143
|
+
try {
|
|
144
|
+
if (record && depositTxHash.startsWith("0x")) {
|
|
145
|
+
emitDebug("onramp:mark-deposit-triggered-request", {
|
|
146
|
+
transactionId: record.transaction_id,
|
|
147
|
+
depositTxHash
|
|
148
|
+
});
|
|
149
|
+
const updated = await executePrivateRead(
|
|
150
|
+
() => client.updateOnRamp(record.transaction_id, {
|
|
151
|
+
deposit_tx_hash: depositTxHash
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
emitDebug("onramp:mark-deposit-triggered-success", {
|
|
155
|
+
record: summariseOnRampRecord(updated)
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
} catch (err) {
|
|
159
|
+
emitDebug("onramp:mark-deposit-triggered-error", errorPayload(err));
|
|
160
|
+
console.warn("Failed to mark on-ramp row complete:", err);
|
|
161
|
+
} finally {
|
|
162
|
+
clearActiveVerification();
|
|
163
|
+
if (record && activeIntentIdRef.current === record.transaction_id) {
|
|
164
|
+
activeIntentIdRef.current = null;
|
|
165
|
+
setActiveIntentId(null);
|
|
166
|
+
}
|
|
167
|
+
void refreshPending();
|
|
168
|
+
}
|
|
169
|
+
})();
|
|
170
|
+
onCreditedRef.current?.(depositTxHash);
|
|
171
|
+
},
|
|
172
|
+
onCheckTimeout: (depositTxHash) => {
|
|
173
|
+
const err = new Error(
|
|
174
|
+
"Privana verification is still pending. Retry from the pending on-ramp list if it does not complete."
|
|
175
|
+
);
|
|
176
|
+
emitDebug("verification:timeout", { depositTxHash, message: err.message });
|
|
177
|
+
clearActiveVerification();
|
|
178
|
+
setStatus("failed");
|
|
179
|
+
setError(err);
|
|
180
|
+
void refreshPending();
|
|
181
|
+
onErrorRef.current?.(err);
|
|
182
|
+
},
|
|
183
|
+
onError: (err) => {
|
|
184
|
+
emitDebug("verification:error", errorPayload(err));
|
|
185
|
+
clearActiveVerification();
|
|
186
|
+
setStatus("failed");
|
|
187
|
+
setError(err);
|
|
188
|
+
onErrorRef.current?.(err);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
const prepareOnRampIntent = useCallback(
|
|
192
|
+
async ({
|
|
193
|
+
currencyCode,
|
|
194
|
+
baseCurrencyCode,
|
|
195
|
+
baseCurrencyAmount
|
|
196
|
+
}) => {
|
|
197
|
+
try {
|
|
198
|
+
setError(null);
|
|
199
|
+
const token = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
|
|
200
|
+
if (!token) throw new Error(`Unknown token: ${tokenId}`);
|
|
201
|
+
if (!depositAddress) throw new Error("Privana deposit address is not ready");
|
|
202
|
+
emitDebug("intent:create-request", {
|
|
203
|
+
tokenId,
|
|
204
|
+
chainId: token.chainId,
|
|
205
|
+
currencyCode,
|
|
206
|
+
baseCurrencyCode: baseCurrencyCode ?? null,
|
|
207
|
+
baseCurrencyAmount: baseCurrencyAmount ?? null,
|
|
208
|
+
depositAddress
|
|
209
|
+
});
|
|
210
|
+
const record = await executePrivateRead(
|
|
211
|
+
() => client.createOnRampIntent({
|
|
212
|
+
wallet_address: depositAddress,
|
|
213
|
+
token_id: tokenId,
|
|
214
|
+
chain_id: token.chainId,
|
|
215
|
+
moonpay_currency_code: currencyCode,
|
|
216
|
+
base_currency_code: baseCurrencyCode,
|
|
217
|
+
base_currency_amount: baseCurrencyAmount
|
|
218
|
+
})
|
|
219
|
+
);
|
|
220
|
+
activeIntentIdRef.current = record.transaction_id;
|
|
221
|
+
setActiveIntentId(record.transaction_id);
|
|
222
|
+
emitDebug("intent:create-success", {
|
|
223
|
+
record: summariseOnRampRecord(record)
|
|
224
|
+
});
|
|
225
|
+
return record;
|
|
226
|
+
} catch (err) {
|
|
227
|
+
const e = err instanceof Error ? err : new Error("Failed to create on-ramp intent");
|
|
228
|
+
setStatus("failed");
|
|
229
|
+
setError(e);
|
|
230
|
+
emitDebug("intent:create-error", errorPayload(e));
|
|
231
|
+
onErrorRef.current?.(e);
|
|
232
|
+
throw e;
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
[client, depositAddress, emitDebug, enabledTokens, executePrivateRead, tokenId]
|
|
236
|
+
);
|
|
237
|
+
const registerOnRampTokenMapping = useCallback(
|
|
238
|
+
async (moonpayTransactionId) => {
|
|
239
|
+
const token = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
|
|
240
|
+
if (!token) {
|
|
241
|
+
emitDebug("register-token-mapping:skip", {
|
|
242
|
+
moonpayTransactionId,
|
|
243
|
+
reason: "selected-token-not-found"
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const transactionId = activeIntentIdRef.current ?? moonpayTransactionId;
|
|
248
|
+
try {
|
|
249
|
+
emitDebug("register-token-mapping:request", {
|
|
250
|
+
transactionId,
|
|
251
|
+
moonpayTransactionId,
|
|
252
|
+
tokenId,
|
|
253
|
+
chainId: token.chainId
|
|
254
|
+
});
|
|
255
|
+
const record = await executePrivateRead(
|
|
256
|
+
() => client.updateOnRamp(transactionId, {
|
|
257
|
+
token_id: tokenId,
|
|
258
|
+
chain_id: token.chainId,
|
|
259
|
+
moonpay_transaction_id: transactionId === moonpayTransactionId ? void 0 : moonpayTransactionId
|
|
260
|
+
})
|
|
261
|
+
);
|
|
262
|
+
emitDebug("register-token-mapping:success", {
|
|
263
|
+
transactionId,
|
|
264
|
+
moonpayTransactionId,
|
|
265
|
+
record: summariseOnRampRecord(record)
|
|
266
|
+
});
|
|
267
|
+
} catch (err) {
|
|
268
|
+
emitDebug("register-token-mapping:error", {
|
|
269
|
+
transactionId,
|
|
270
|
+
moonpayTransactionId,
|
|
271
|
+
...errorPayload(err)
|
|
272
|
+
});
|
|
273
|
+
console.warn("Failed to register on-ramp token mapping:", err);
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
[client, emitDebug, enabledTokens, executePrivateRead, tokenId]
|
|
277
|
+
);
|
|
278
|
+
const handleTransactionCreated = useCallback(
|
|
279
|
+
async (props) => {
|
|
280
|
+
emitDebug("moonpay:onTransactionCreated", summariseMoonPayEventProps(props));
|
|
281
|
+
await registerOnRampTokenMapping(props.id);
|
|
282
|
+
},
|
|
283
|
+
[emitDebug, registerOnRampTokenMapping]
|
|
284
|
+
);
|
|
285
|
+
const signUrl = useCallback(
|
|
286
|
+
async (url) => {
|
|
287
|
+
setError(null);
|
|
288
|
+
try {
|
|
289
|
+
emitDebug("moonpay:onUrlSignatureRequested", summariseMoonPayUrl(url));
|
|
290
|
+
const { signature } = await executePrivateRead(() => client.signOnRampUrl({ url }));
|
|
291
|
+
setStatus("awaiting-purchase");
|
|
292
|
+
emitDebug("sign-url:success", {
|
|
293
|
+
signatureLength: signature.length
|
|
294
|
+
});
|
|
295
|
+
return signature;
|
|
296
|
+
} catch (err) {
|
|
297
|
+
const e = err instanceof Error ? err : new Error("Failed to sign on-ramp URL");
|
|
298
|
+
setStatus("failed");
|
|
299
|
+
setError(e);
|
|
300
|
+
emitDebug("sign-url:error", errorPayload(e));
|
|
301
|
+
onErrorRef.current?.(e);
|
|
302
|
+
throw err;
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
[client, emitDebug, executePrivateRead]
|
|
306
|
+
);
|
|
307
|
+
const waitForOnChainHash = useCallback(
|
|
308
|
+
async (transactionId) => {
|
|
309
|
+
const startTime = Date.now();
|
|
310
|
+
emitDebug("delivery-poll:start", {
|
|
311
|
+
transactionId,
|
|
312
|
+
deliveryTimeout,
|
|
313
|
+
deliveryPollInterval
|
|
314
|
+
});
|
|
315
|
+
while (Date.now() - startTime < deliveryTimeout) {
|
|
316
|
+
try {
|
|
317
|
+
const { pending: rows } = await executePrivateRead(() => client.getPendingOnRamps());
|
|
318
|
+
setPending(rows);
|
|
319
|
+
const record = rows.find((r) => matchesOnRampTransaction(r, transactionId));
|
|
320
|
+
emitDebug("delivery-poll:tick", {
|
|
321
|
+
transactionId,
|
|
322
|
+
count: rows.length,
|
|
323
|
+
matchingRecord: record ? summariseOnRampRecord(record) : null
|
|
324
|
+
});
|
|
325
|
+
if (record?.on_chain_tx_hash && record.quote_currency_amount) {
|
|
326
|
+
emitDebug("delivery-poll:success", {
|
|
327
|
+
transactionId,
|
|
328
|
+
record: summariseOnRampRecord(record)
|
|
329
|
+
});
|
|
330
|
+
return record;
|
|
331
|
+
}
|
|
332
|
+
} catch (err) {
|
|
333
|
+
emitDebug("delivery-poll:error", {
|
|
334
|
+
transactionId,
|
|
335
|
+
...errorPayload(err)
|
|
336
|
+
});
|
|
337
|
+
console.warn("Polling pending on-ramps failed:", err);
|
|
338
|
+
}
|
|
339
|
+
await new Promise((r) => setTimeout(r, deliveryPollInterval));
|
|
340
|
+
}
|
|
341
|
+
emitDebug("delivery-poll:timeout", { transactionId });
|
|
342
|
+
return null;
|
|
343
|
+
},
|
|
344
|
+
[client, deliveryPollInterval, deliveryTimeout, emitDebug, executePrivateRead]
|
|
345
|
+
);
|
|
346
|
+
const triggerVerification = useCallback(
|
|
347
|
+
async (record) => {
|
|
348
|
+
const verificationKey = getOnRampVerificationKey(record);
|
|
349
|
+
if (triggeredVerificationKeysRef.current.has(verificationKey)) {
|
|
350
|
+
emitDebug("verification:skip-duplicate", {
|
|
351
|
+
verificationKey,
|
|
352
|
+
record: summariseOnRampRecord(record)
|
|
353
|
+
});
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
triggeredVerificationKeysRef.current.add(verificationKey);
|
|
357
|
+
activeVerificationKeyRef.current = verificationKey;
|
|
358
|
+
activeVerificationRecordRef.current = record;
|
|
359
|
+
emitDebug("verification:start", {
|
|
360
|
+
verificationKey,
|
|
361
|
+
record: summariseOnRampRecord(record)
|
|
362
|
+
});
|
|
363
|
+
try {
|
|
364
|
+
if (!record.on_chain_tx_hash || !record.quote_currency_amount) {
|
|
365
|
+
throw new Error("On-ramp record missing on-chain tx hash or delivered amount");
|
|
366
|
+
}
|
|
367
|
+
if (record.chain_id === void 0 || !record.wallet_address) {
|
|
368
|
+
throw new Error("On-ramp record missing chain id or wallet address");
|
|
369
|
+
}
|
|
370
|
+
const recordTokenId = record.token_id;
|
|
371
|
+
if (!recordTokenId) {
|
|
372
|
+
throw new Error("On-ramp record missing token id");
|
|
373
|
+
}
|
|
374
|
+
const token = enabledTokens.find((t) => t.id.toLowerCase() === recordTokenId.toLowerCase());
|
|
375
|
+
if (!token) throw new Error(`Unknown token: ${recordTokenId}`);
|
|
376
|
+
if (token.chainId !== record.chain_id) {
|
|
377
|
+
throw new Error(
|
|
378
|
+
`Token ${recordTokenId} is on chain ${token.chainId} but record is on chain ${record.chain_id}`
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
const amount = await resolveDeliveredAmount({
|
|
382
|
+
onChainTxHash: record.on_chain_tx_hash,
|
|
383
|
+
chainId: record.chain_id,
|
|
384
|
+
walletAddress: record.wallet_address,
|
|
385
|
+
token,
|
|
386
|
+
fallbackAmount: record.quote_currency_amount,
|
|
387
|
+
wagmiConfig,
|
|
388
|
+
emitDebug
|
|
389
|
+
});
|
|
390
|
+
if (minDepositBaseUnits !== void 0 && amount < minDepositBaseUnits) {
|
|
391
|
+
emitDebug("verification:below-minimum", {
|
|
392
|
+
quoteCurrencyAmount: record.quote_currency_amount,
|
|
393
|
+
minDepositBaseUnits: String(minDepositBaseUnits)
|
|
394
|
+
});
|
|
395
|
+
throw new Error(
|
|
396
|
+
`Delivered amount (${record.quote_currency_amount}) is below the minimum deposit.`
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
setStatus("verifying");
|
|
400
|
+
emitDebug("verification:check-deposit-request", {
|
|
401
|
+
hash: record.on_chain_tx_hash,
|
|
402
|
+
chainId: record.chain_id,
|
|
403
|
+
amount: amount.toString()
|
|
404
|
+
});
|
|
405
|
+
await verify({
|
|
406
|
+
hash: record.on_chain_tx_hash,
|
|
407
|
+
chainId: record.chain_id,
|
|
408
|
+
amount
|
|
409
|
+
});
|
|
410
|
+
} catch (err) {
|
|
411
|
+
triggeredVerificationKeysRef.current.delete(verificationKey);
|
|
412
|
+
if (activeVerificationKeyRef.current === verificationKey) {
|
|
413
|
+
activeVerificationKeyRef.current = null;
|
|
414
|
+
activeVerificationRecordRef.current = null;
|
|
415
|
+
}
|
|
416
|
+
throw err;
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
[emitDebug, enabledTokens, minDepositBaseUnits, verify, wagmiConfig]
|
|
420
|
+
);
|
|
421
|
+
const handleTransactionCompleted = useCallback(
|
|
422
|
+
async (props) => {
|
|
423
|
+
emitDebug("moonpay:onTransactionCompleted", summariseMoonPayEventProps(props));
|
|
424
|
+
try {
|
|
425
|
+
setStatus("awaiting-delivery");
|
|
426
|
+
await registerOnRampTokenMapping(props.id);
|
|
427
|
+
const transactionId = activeIntentIdRef.current ?? props.id;
|
|
428
|
+
const record = await waitForOnChainHash(transactionId);
|
|
429
|
+
if (!record) {
|
|
430
|
+
const err = new Error(
|
|
431
|
+
"Backend has not yet confirmed delivery. You can finish from the pending list."
|
|
432
|
+
);
|
|
433
|
+
emitDebug("moonpay:completed-without-backend-row", {
|
|
434
|
+
transactionId,
|
|
435
|
+
moonpayTransactionId: props.id,
|
|
436
|
+
message: err.message
|
|
437
|
+
});
|
|
438
|
+
setStatus("failed");
|
|
439
|
+
setError(err);
|
|
440
|
+
onErrorRef.current?.(err);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
await triggerVerification(record);
|
|
444
|
+
} catch (err) {
|
|
445
|
+
const e = err instanceof Error ? err : new Error("Verification failed");
|
|
446
|
+
emitDebug("moonpay:onTransactionCompleted-error", errorPayload(e));
|
|
447
|
+
setStatus("failed");
|
|
448
|
+
setError(e);
|
|
449
|
+
onErrorRef.current?.(e);
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
[emitDebug, registerOnRampTokenMapping, triggerVerification, waitForOnChainHash]
|
|
453
|
+
);
|
|
454
|
+
const handleWidgetClosed = useCallback(async () => {
|
|
455
|
+
if (closeReconcilePromiseRef.current) return closeReconcilePromiseRef.current;
|
|
456
|
+
closeReconcilePromiseRef.current = (async () => {
|
|
457
|
+
const previousStatus = statusRef.current;
|
|
458
|
+
const transactionId = activeIntentIdRef.current;
|
|
459
|
+
emitDebug("moonpay:widget-closed-reconcile", {
|
|
460
|
+
previousStatus,
|
|
461
|
+
transactionId
|
|
462
|
+
});
|
|
463
|
+
if (!transactionId) {
|
|
464
|
+
await refreshPending();
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
setStatus("awaiting-delivery");
|
|
469
|
+
const record = await waitForOnChainHash(transactionId);
|
|
470
|
+
if (record) {
|
|
471
|
+
await triggerVerification(record);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
await refreshPending();
|
|
475
|
+
if (previousStatus === "awaiting-purchase" || previousStatus === "awaiting-delivery") {
|
|
476
|
+
setStatus("idle");
|
|
477
|
+
}
|
|
478
|
+
} catch (err) {
|
|
479
|
+
const e = err instanceof Error ? err : new Error("On-ramp reconciliation failed");
|
|
480
|
+
emitDebug("moonpay:widget-closed-reconcile-error", errorPayload(e));
|
|
481
|
+
setStatus("failed");
|
|
482
|
+
setError(e);
|
|
483
|
+
onErrorRef.current?.(e);
|
|
484
|
+
}
|
|
485
|
+
})();
|
|
486
|
+
try {
|
|
487
|
+
await closeReconcilePromiseRef.current;
|
|
488
|
+
} finally {
|
|
489
|
+
closeReconcilePromiseRef.current = null;
|
|
490
|
+
}
|
|
491
|
+
}, [emitDebug, refreshPending, triggerVerification, waitForOnChainHash]);
|
|
492
|
+
const finishPendingVerification = useCallback(
|
|
493
|
+
async (record) => {
|
|
494
|
+
try {
|
|
495
|
+
emitDebug("pending:finish-verification", {
|
|
496
|
+
record: summariseOnRampRecord(record)
|
|
497
|
+
});
|
|
498
|
+
await triggerVerification(record);
|
|
499
|
+
} catch (err) {
|
|
500
|
+
const e = err instanceof Error ? err : new Error("Verification failed");
|
|
501
|
+
emitDebug("pending:finish-verification-error", errorPayload(e));
|
|
502
|
+
setStatus("failed");
|
|
503
|
+
setError(e);
|
|
504
|
+
onErrorRef.current?.(e);
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
[emitDebug, triggerVerification]
|
|
508
|
+
);
|
|
509
|
+
return {
|
|
510
|
+
status,
|
|
511
|
+
activeIntentId,
|
|
512
|
+
pending,
|
|
513
|
+
error,
|
|
514
|
+
depositAddress,
|
|
515
|
+
minDepositBaseUnits,
|
|
516
|
+
selectedToken,
|
|
517
|
+
prepareOnRampIntent,
|
|
518
|
+
signUrl,
|
|
519
|
+
handleTransactionCreated,
|
|
520
|
+
handleTransactionCompleted,
|
|
521
|
+
finishPendingVerification,
|
|
522
|
+
handleWidgetClosed,
|
|
523
|
+
refreshPending
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
function summariseToken(token) {
|
|
527
|
+
return {
|
|
528
|
+
tokenId: token.id,
|
|
529
|
+
chainId: token.chainId,
|
|
530
|
+
symbol: token.symbol ?? null,
|
|
531
|
+
decimals: token.decimals ?? null
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function summariseOnRampRecord(record) {
|
|
535
|
+
return {
|
|
536
|
+
transaction_id: record.transaction_id,
|
|
537
|
+
external_transaction_id: record.external_transaction_id ?? null,
|
|
538
|
+
moonpay_transaction_id: record.moonpay_transaction_id ?? null,
|
|
539
|
+
status: record.status,
|
|
540
|
+
wallet_address: record.wallet_address,
|
|
541
|
+
token_id: record.token_id,
|
|
542
|
+
chain_id: record.chain_id,
|
|
543
|
+
moonpay_currency_code: record.moonpay_currency_code ?? null,
|
|
544
|
+
quote_currency_amount: record.quote_currency_amount ?? null,
|
|
545
|
+
on_chain_tx_hash: record.on_chain_tx_hash ?? null,
|
|
546
|
+
deposit_tx_hash: record.deposit_tx_hash ?? null,
|
|
547
|
+
deposit_triggered_at: record.deposit_triggered_at ?? null,
|
|
548
|
+
credited_at: record.credited_at ?? null
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
async function resolveDeliveredAmount({
|
|
552
|
+
onChainTxHash,
|
|
553
|
+
chainId,
|
|
554
|
+
walletAddress,
|
|
555
|
+
token,
|
|
556
|
+
fallbackAmount,
|
|
557
|
+
wagmiConfig,
|
|
558
|
+
emitDebug
|
|
559
|
+
}) {
|
|
560
|
+
if (token.contract === zeroAddress) {
|
|
561
|
+
return parseUnits(fallbackAmount, token.decimals);
|
|
562
|
+
}
|
|
563
|
+
let receiptError;
|
|
564
|
+
try {
|
|
565
|
+
const receipt = await getTransactionReceipt(wagmiConfig, {
|
|
566
|
+
hash: onChainTxHash,
|
|
567
|
+
chainId
|
|
568
|
+
});
|
|
569
|
+
let delivered = 0n;
|
|
570
|
+
for (const log of receipt.logs) {
|
|
571
|
+
if (log.address.toLowerCase() !== token.contract.toLowerCase()) continue;
|
|
572
|
+
try {
|
|
573
|
+
const decoded = decodeEventLog({
|
|
574
|
+
abi: [ERC20_TRANSFER_EVENT],
|
|
575
|
+
data: log.data,
|
|
576
|
+
topics: log.topics
|
|
577
|
+
});
|
|
578
|
+
if (decoded.eventName !== "Transfer") continue;
|
|
579
|
+
const to = decoded.args.to.toLowerCase();
|
|
580
|
+
if (to !== walletAddress.toLowerCase()) continue;
|
|
581
|
+
delivered += decoded.args.value;
|
|
582
|
+
} catch {
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (delivered > 0n) {
|
|
586
|
+
emitDebug("verification:amount-from-receipt", {
|
|
587
|
+
amount: delivered.toString(),
|
|
588
|
+
tokenAddress: token.contract,
|
|
589
|
+
walletAddress,
|
|
590
|
+
moonpayQuoteCurrencyAmount: fallbackAmount
|
|
591
|
+
});
|
|
592
|
+
return delivered;
|
|
593
|
+
}
|
|
594
|
+
emitDebug("verification:amount-from-receipt-missing", {
|
|
595
|
+
tokenAddress: token.contract,
|
|
596
|
+
walletAddress,
|
|
597
|
+
moonpayQuoteCurrencyAmount: fallbackAmount
|
|
598
|
+
});
|
|
599
|
+
} catch (err) {
|
|
600
|
+
emitDebug("verification:amount-from-receipt-error", errorPayload(err));
|
|
601
|
+
receiptError = err;
|
|
602
|
+
}
|
|
603
|
+
const errorDetail = receiptError instanceof Error ? receiptError.message : receiptError === void 0 ? `no ${token.symbol} Transfer to ${walletAddress} found` : String(receiptError);
|
|
604
|
+
throw new Error(
|
|
605
|
+
`Unable to derive delivered ${token.symbol} amount from ${onChainTxHash}: ${errorDetail}`
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
function matchesOnRampTransaction(record, transactionId) {
|
|
609
|
+
return record.transaction_id === transactionId || record.external_transaction_id === transactionId || record.moonpay_transaction_id === transactionId;
|
|
610
|
+
}
|
|
611
|
+
function getOnRampVerificationKey(record) {
|
|
612
|
+
return record.on_chain_tx_hash ?? record.transaction_id;
|
|
613
|
+
}
|
|
614
|
+
function summariseMoonPayEventProps(props) {
|
|
615
|
+
return {
|
|
616
|
+
id: props.id,
|
|
617
|
+
externalTransactionId: props.externalTransactionId,
|
|
618
|
+
status: props.status,
|
|
619
|
+
walletAddress: props.walletAddress,
|
|
620
|
+
walletAddressTag: props.walletAddressTag,
|
|
621
|
+
baseCurrencyAmount: props.baseCurrencyAmount,
|
|
622
|
+
quoteCurrencyAmount: props.quoteCurrencyAmount,
|
|
623
|
+
baseCurrency: props.baseCurrency,
|
|
624
|
+
quoteCurrency: props.quoteCurrency,
|
|
625
|
+
createdAt: props.createdAt
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
function summariseMoonPayUrl(url) {
|
|
629
|
+
try {
|
|
630
|
+
const parsed = new URL(url);
|
|
631
|
+
const params = parsed.searchParams;
|
|
632
|
+
return {
|
|
633
|
+
origin: parsed.origin,
|
|
634
|
+
pathname: parsed.pathname,
|
|
635
|
+
apiKeyPrefix: params.get("apiKey")?.slice(0, 8) ?? null,
|
|
636
|
+
currencyCode: params.get("currencyCode"),
|
|
637
|
+
baseCurrencyCode: params.get("baseCurrencyCode"),
|
|
638
|
+
baseCurrencyAmount: params.get("baseCurrencyAmount"),
|
|
639
|
+
walletAddress: params.get("walletAddress"),
|
|
640
|
+
externalCustomerId: params.get("externalCustomerId"),
|
|
641
|
+
externalTransactionId: params.get("externalTransactionId"),
|
|
642
|
+
redirectURL: params.get("redirectURL"),
|
|
643
|
+
signaturePresent: params.has("signature")
|
|
644
|
+
};
|
|
645
|
+
} catch {
|
|
646
|
+
return { parseError: true, length: url.length };
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function errorPayload(err) {
|
|
650
|
+
if (err instanceof Error) {
|
|
651
|
+
return {
|
|
652
|
+
name: err.name,
|
|
653
|
+
message: err.message,
|
|
654
|
+
stack: err.stack?.split("\n").slice(0, 4).join("\n")
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
return { message: String(err) };
|
|
658
|
+
}
|
|
659
|
+
function FiatOnRampForm({
|
|
660
|
+
tokenId,
|
|
661
|
+
currencyCode,
|
|
662
|
+
baseCurrencyCode = "usd",
|
|
663
|
+
defaultBaseCurrencyAmount = "100",
|
|
664
|
+
tokenSymbol,
|
|
665
|
+
onCredited,
|
|
666
|
+
onError,
|
|
667
|
+
onDebugEvent
|
|
668
|
+
}) {
|
|
669
|
+
const { address } = useAccount();
|
|
670
|
+
const [visible, setVisible] = useState(false);
|
|
671
|
+
const [isPreparing, setIsPreparing] = useState(false);
|
|
672
|
+
const {
|
|
673
|
+
status,
|
|
674
|
+
activeIntentId,
|
|
675
|
+
pending,
|
|
676
|
+
error,
|
|
677
|
+
depositAddress,
|
|
678
|
+
minDepositBaseUnits,
|
|
679
|
+
selectedToken,
|
|
680
|
+
prepareOnRampIntent,
|
|
681
|
+
signUrl,
|
|
682
|
+
handleTransactionCreated,
|
|
683
|
+
handleTransactionCompleted,
|
|
684
|
+
finishPendingVerification,
|
|
685
|
+
handleWidgetClosed
|
|
686
|
+
} = useFiatOnRamp({ tokenId, onCredited, onError, onDebugEvent });
|
|
687
|
+
const decimals = selectedToken?.decimals;
|
|
688
|
+
const displaySymbol = tokenSymbol ?? selectedToken?.symbol ?? currencyCode.toUpperCase();
|
|
689
|
+
const emitFormDebug = useCallback(
|
|
690
|
+
(event, payload) => {
|
|
691
|
+
onDebugEvent?.({
|
|
692
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
693
|
+
event,
|
|
694
|
+
status,
|
|
695
|
+
tokenId,
|
|
696
|
+
payload
|
|
697
|
+
});
|
|
698
|
+
},
|
|
699
|
+
[onDebugEvent, status, tokenId]
|
|
700
|
+
);
|
|
701
|
+
const minFiatGate = minDepositBaseUnits !== void 0 && decimals !== void 0 ? Number(formatUnits(minDepositBaseUnits, decimals)) * 1.05 : void 0;
|
|
702
|
+
const isBelowMin = minFiatGate !== void 0 && Number(defaultBaseCurrencyAmount) < minFiatGate;
|
|
703
|
+
const isBusy = isPreparing || status === "awaiting-purchase" || status === "awaiting-delivery" || status === "verifying";
|
|
704
|
+
const blockReasons = useMemo(
|
|
705
|
+
() => [
|
|
706
|
+
!address ? "wallet-not-connected" : null,
|
|
707
|
+
!depositAddress ? "deposit-address-not-loaded" : null,
|
|
708
|
+
isBusy ? `busy:${isPreparing ? "preparing" : status}` : null,
|
|
709
|
+
isBelowMin ? "below-minimum" : null
|
|
710
|
+
].filter((reason) => Boolean(reason)),
|
|
711
|
+
[address, depositAddress, isBelowMin, isBusy, isPreparing, status]
|
|
712
|
+
);
|
|
713
|
+
const canBuy = blockReasons.length === 0;
|
|
714
|
+
const handleOpen = useCallback(async () => {
|
|
715
|
+
if (!canBuy) {
|
|
716
|
+
emitFormDebug("form:open-blocked", {
|
|
717
|
+
reasons: blockReasons,
|
|
718
|
+
currencyCode,
|
|
719
|
+
tokenSymbol: displaySymbol,
|
|
720
|
+
tokenDecimals: decimals ?? null,
|
|
721
|
+
baseCurrencyCode,
|
|
722
|
+
defaultBaseCurrencyAmount,
|
|
723
|
+
depositAddress: depositAddress ?? null,
|
|
724
|
+
walletAddress: address ?? null,
|
|
725
|
+
status
|
|
726
|
+
});
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
setIsPreparing(true);
|
|
730
|
+
emitFormDebug("form:open-click", {
|
|
731
|
+
currencyCode,
|
|
732
|
+
tokenSymbol: displaySymbol,
|
|
733
|
+
tokenDecimals: decimals ?? null,
|
|
734
|
+
baseCurrencyCode,
|
|
735
|
+
defaultBaseCurrencyAmount,
|
|
736
|
+
depositAddress: depositAddress ?? null,
|
|
737
|
+
walletConnected: Boolean(address)
|
|
738
|
+
});
|
|
739
|
+
try {
|
|
740
|
+
const intent = await prepareOnRampIntent({
|
|
741
|
+
currencyCode,
|
|
742
|
+
baseCurrencyCode,
|
|
743
|
+
baseCurrencyAmount: defaultBaseCurrencyAmount
|
|
744
|
+
});
|
|
745
|
+
emitFormDebug("form:intent-ready", {
|
|
746
|
+
transactionId: intent.transaction_id,
|
|
747
|
+
externalTransactionId: intent.external_transaction_id ?? null
|
|
748
|
+
});
|
|
749
|
+
setVisible(true);
|
|
750
|
+
} catch (err) {
|
|
751
|
+
const error2 = err instanceof Error ? err : new Error("Failed to prepare MoonPay on-ramp");
|
|
752
|
+
emitFormDebug("form:intent-error", {
|
|
753
|
+
name: error2.name,
|
|
754
|
+
message: error2.message
|
|
755
|
+
});
|
|
756
|
+
} finally {
|
|
757
|
+
setIsPreparing(false);
|
|
758
|
+
}
|
|
759
|
+
}, [
|
|
760
|
+
address,
|
|
761
|
+
baseCurrencyCode,
|
|
762
|
+
blockReasons,
|
|
763
|
+
canBuy,
|
|
764
|
+
currencyCode,
|
|
765
|
+
decimals,
|
|
766
|
+
displaySymbol,
|
|
767
|
+
defaultBaseCurrencyAmount,
|
|
768
|
+
depositAddress,
|
|
769
|
+
emitFormDebug,
|
|
770
|
+
prepareOnRampIntent,
|
|
771
|
+
status
|
|
772
|
+
]);
|
|
773
|
+
const handleClose = useCallback(async () => {
|
|
774
|
+
emitFormDebug("moonpay:onClose");
|
|
775
|
+
setVisible(false);
|
|
776
|
+
await handleWidgetClosed();
|
|
777
|
+
}, [emitFormDebug, handleWidgetClosed]);
|
|
778
|
+
const handleCloseOverlay = useCallback(async () => {
|
|
779
|
+
emitFormDebug("moonpay:onCloseOverlay");
|
|
780
|
+
setVisible(false);
|
|
781
|
+
await handleWidgetClosed();
|
|
782
|
+
}, [emitFormDebug, handleWidgetClosed]);
|
|
783
|
+
const handleReady = useCallback(async () => {
|
|
784
|
+
emitFormDebug("moonpay:onReady");
|
|
785
|
+
}, [emitFormDebug]);
|
|
786
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
787
|
+
/* @__PURE__ */ jsx(Button, { type: "button", onClick: handleOpen, children: isPreparing ? "Preparing MoonPay\u2026" : isBusy ? statusLabel(status) : `Buy ${displaySymbol}` }),
|
|
788
|
+
!canBuy && !isBusy && /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground text-xs", children: [
|
|
789
|
+
"Button blocked: ",
|
|
790
|
+
blockReasons.join(", ")
|
|
791
|
+
] }),
|
|
792
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", role: "alert", children: error.message }),
|
|
793
|
+
isBelowMin && minFiatGate !== void 0 && /* @__PURE__ */ jsxs("p", { className: "text-destructive text-sm", role: "alert", children: [
|
|
794
|
+
"Minimum purchase is ~$",
|
|
795
|
+
minFiatGate.toFixed(2),
|
|
796
|
+
"."
|
|
797
|
+
] }),
|
|
798
|
+
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: "Finish verification" }),
|
|
800
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: "MoonPay delivered your purchase, but Privana hasn't verified it yet. Resume verification now to credit your balance." }),
|
|
801
|
+
pending.map((record) => /* @__PURE__ */ jsxs(
|
|
802
|
+
Button,
|
|
803
|
+
{
|
|
804
|
+
type: "button",
|
|
805
|
+
variant: "outline",
|
|
806
|
+
size: "sm",
|
|
807
|
+
disabled: isBusy,
|
|
808
|
+
onClick: () => finishPendingVerification(record),
|
|
809
|
+
children: [
|
|
810
|
+
record.quote_currency_amount ?? "?",
|
|
811
|
+
" ",
|
|
812
|
+
displaySymbol
|
|
813
|
+
]
|
|
814
|
+
},
|
|
815
|
+
record.transaction_id
|
|
816
|
+
))
|
|
817
|
+
] }),
|
|
818
|
+
visible && depositAddress && activeIntentId && /* @__PURE__ */ jsx(
|
|
819
|
+
MoonPayBuyWidget,
|
|
820
|
+
{
|
|
821
|
+
variant: "overlay",
|
|
822
|
+
visible: true,
|
|
823
|
+
baseCurrencyCode,
|
|
824
|
+
baseCurrencyAmount: defaultBaseCurrencyAmount,
|
|
825
|
+
currencyCode,
|
|
826
|
+
walletAddress: depositAddress,
|
|
827
|
+
externalCustomerId: address?.toLowerCase(),
|
|
828
|
+
externalTransactionId: activeIntentId,
|
|
829
|
+
onClose: handleClose,
|
|
830
|
+
onCloseOverlay: handleCloseOverlay,
|
|
831
|
+
onReady: handleReady,
|
|
832
|
+
onUrlSignatureRequested: signUrl,
|
|
833
|
+
onTransactionCreated: handleTransactionCreated,
|
|
834
|
+
onTransactionCompleted: handleTransactionCompleted
|
|
835
|
+
}
|
|
836
|
+
)
|
|
837
|
+
] });
|
|
838
|
+
}
|
|
839
|
+
function statusLabel(status) {
|
|
840
|
+
switch (status) {
|
|
841
|
+
case "awaiting-purchase":
|
|
842
|
+
return "Complete your purchase\u2026";
|
|
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
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
export { FiatOnRampForm, useFiatOnRamp };
|
|
857
|
+
//# sourceMappingURL=on-ramp.js.map
|
|
858
|
+
//# sourceMappingURL=on-ramp.js.map
|