@oasisprotocol/privana-sdk 0.5.0 → 0.5.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-ACLJPC75.cjs → chunk-4IW4V7YJ.cjs} +929 -20
- package/dist/chunk-4IW4V7YJ.cjs.map +1 -0
- package/dist/{chunk-54FC6TO3.js → chunk-SAJ7K5WT.js} +900 -22
- package/dist/chunk-SAJ7K5WT.js.map +1 -0
- package/dist/index.cjs +711 -506
- 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 +56 -339
- package/dist/index.d.ts +56 -339
- package/dist/index.js +419 -301
- package/dist/index.js.map +1 -1
- package/dist/on-ramp.cjs +3 -3
- package/dist/on-ramp.d.cts +40 -4
- package/dist/on-ramp.d.ts +40 -4
- package/dist/on-ramp.js +1 -1
- package/dist/pending-lock-BdkuMjmY.d.cts +600 -0
- package/dist/pending-lock-BdkuMjmY.d.ts +600 -0
- package/package.json +1 -1
- package/dist/chunk-54FC6TO3.js.map +0 -1
- package/dist/chunk-ACLJPC75.cjs.map +0 -1
- package/dist/on-ramp-XLXsy0aI.d.cts +0 -130
- package/dist/on-ramp-XLXsy0aI.d.ts +0 -130
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { usePrivanaContext, createHostedAuthPendingStorageKey, clearHostedAuthPendingTransaction, HostedAuthRequiredError, HostedAuthError, createPkceVerifier, createPkceChallenge, createHostedAuthState, persistHostedAuthPendingTransaction, parseHostedAuthCallback, readHostedAuthPendingTransaction, HostedAuthStateMismatchError, buildHostedAuthSession, useSafePrivanaContext, usePrivateReadRequest, formatTokenAmount, useDepositVerification, getTransactionReceipt, waitForTransactionReceipt, cn, Button, stripHostedAuthCallbackParams, AccountingApiError,
|
|
3
|
-
export { AccountingApiError, Button, HOSTED_AUTH_CLOCK_SKEW_MS, HostedAuthError, HostedAuthRequiredError, HostedAuthStateMismatchError, HttpClient, NETWORK_CONFIG, NetworkError, PrivanaClient, PrivanaProvider, SUPPORTED_CHAINS, SiweAuthProvider, Skeleton, ValidationError, applyRefreshResponse, buildHostedAuthSession, buildSiweStatement, buttonVariants, clearHostedAuthPendingTransaction, createHostedAuthPendingStorageKey, createHostedAuthState, createHostedAuthStorageKey, createPkceChallenge, createPkceVerifier, getAccountingContract, getApiUrl, getChainById, getChainId, getExplorerAddressUrl, getExplorerLabel, isHostedAuthRefreshActive, isHostedAuthSessionActive, normalizeAddress, normalizeHex, parseHostedAuthCallback, persistHostedAuthPendingTransaction, readHostedAuthPendingTransaction, readStoredHostedAuthSession, stripHostedAuthCallbackParams, syncHostedAuthSessionToClient, useDepositVerification, usePrivanaContext, useSafeAccount, useSafePrivanaContext, useSiweAuth } from './chunk-
|
|
2
|
+
import { usePrivanaContext, createHostedAuthPendingStorageKey, clearHostedAuthPendingTransaction, HostedAuthRequiredError, HostedAuthError, createPkceVerifier, createPkceChallenge, createHostedAuthState, persistHostedAuthPendingTransaction, parseHostedAuthCallback, readHostedAuthPendingTransaction, HostedAuthStateMismatchError, buildHostedAuthSession, useSafePrivanaContext, usePrivateReadRequest, formatTokenAmount, submitPendingLock, PostDepositLockError, useDepositVerification, useEnsureCorrectChain, getTransactionReceipt, waitForTransactionReceipt, canUseBrowserStorage, clampLockAmount, getWalletClient, createSignedLockRequest, signWithdrawMessage, signLockMessage, signTransferMessage, signTransferLockedMessage, signModifyLockMessage, cn, Button, stripHostedAuthCallbackParams, AccountingApiError, getBrowserStorageItem, removeBrowserStorageItem, isSignedLockUsable, setBrowserStorageItem, shortenAddress, formatTimeRemaining, parseTokenAmount, getExplorerAddressUrl, getExplorerLabel, Skeleton, formatCountdown, FiatOnRampForm } from './chunk-SAJ7K5WT.js';
|
|
3
|
+
export { AccountingApiError, Button, DEFAULT_LOCK_DURATION_SECONDS, DEFAULT_ONRAMP_LOCK_BUFFER, HOSTED_AUTH_CLOCK_SKEW_MS, HostedAuthError, HostedAuthRequiredError, HostedAuthStateMismatchError, HttpClient, LOCK_TYPES, MODIFY_LOCK_TYPES, NETWORK_CONFIG, NetworkError, PostDepositLockError, PrivanaClient, PrivanaProvider, SUPPORTED_CHAINS, SiweAuthProvider, Skeleton, TRANSFER_LOCKED_TYPES, TRANSFER_TYPES, ValidationError, WITHDRAW_FROM_LOCK_TYPES, WITHDRAW_TYPES, applyLockBuffer, applyRefreshResponse, buildHostedAuthSession, buildSiweStatement, buttonVariants, clampLockAmount, clearHostedAuthPendingTransaction, clearPendingLock, createDomain, createHostedAuthPendingStorageKey, createHostedAuthState, createHostedAuthStorageKey, createLockExpiry, createPkceChallenge, createPkceVerifier, createSignedLockRequest, getAccountingContract, getApiUrl, getChainById, getChainId, getExplorerAddressUrl, getExplorerLabel, isHostedAuthRefreshActive, isHostedAuthSessionActive, isSignedLockUsable, loadPendingLock, normalizeAddress, normalizeHex, parseHostedAuthCallback, persistHostedAuthPendingTransaction, readHostedAuthPendingTransaction, readStoredHostedAuthSession, savePendingLock, signLockMessage, signModifyLockMessage, signTransferLockedMessage, signTransferMessage, signWithdrawFromLockMessage, signWithdrawMessage, stripHostedAuthCallbackParams, submitPendingLock, syncHostedAuthSessionToClient, useDepositVerification, usePrivanaContext, useSafeAccount, useSafePrivanaContext, useSiweAuth } from './chunk-SAJ7K5WT.js';
|
|
4
4
|
import { useState, useRef, useMemo, useCallback, useContext, useEffect, useId } from 'react';
|
|
5
5
|
import { QueryClientContext, useQuery, useQueryClient, useMutation, keepPreviousData } from '@tanstack/react-query';
|
|
6
|
-
import { useAccount, useWalletClient, useConfig, useWriteContract, useSendTransaction,
|
|
6
|
+
import { useAccount, useWalletClient, useConfig, useWriteContract, useSendTransaction, useBalance as useBalance$1, useReadContract } from 'wagmi';
|
|
7
7
|
import { zeroAddress, erc20Abi } from 'viem';
|
|
8
8
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
9
9
|
import { XIcon } from 'lucide-react';
|
|
@@ -12,202 +12,6 @@ import { toast } from 'sonner';
|
|
|
12
12
|
import { QRCodeSVG } from 'qrcode.react';
|
|
13
13
|
import { MoonPayProvider, MoonPayContext } from '@moonpay/moonpay-react';
|
|
14
14
|
|
|
15
|
-
// src/sdk/signatures/eip712-types.ts
|
|
16
|
-
function createDomain(chainId, verifyingContract) {
|
|
17
|
-
return {
|
|
18
|
-
name: "AccountingModule",
|
|
19
|
-
version: "1",
|
|
20
|
-
chainId,
|
|
21
|
-
verifyingContract
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
var LOCK_TYPES = {
|
|
25
|
-
Lock: [
|
|
26
|
-
{ name: "serviceAddress", type: "address" },
|
|
27
|
-
{ name: "tokenId", type: "bytes32" },
|
|
28
|
-
{ name: "amount", type: "uint256" },
|
|
29
|
-
{ name: "expiry", type: "uint256" },
|
|
30
|
-
{ name: "nonce", type: "uint256" }
|
|
31
|
-
]
|
|
32
|
-
};
|
|
33
|
-
var TRANSFER_TYPES = {
|
|
34
|
-
Transfer: [
|
|
35
|
-
{ name: "toAddress", type: "address" },
|
|
36
|
-
{ name: "tokenId", type: "bytes32" },
|
|
37
|
-
{ name: "amount", type: "uint256" },
|
|
38
|
-
{ name: "nonce", type: "uint256" }
|
|
39
|
-
]
|
|
40
|
-
};
|
|
41
|
-
var TRANSFER_LOCKED_TYPES = {
|
|
42
|
-
TransferLocked: [
|
|
43
|
-
{ name: "userAddress", type: "address" },
|
|
44
|
-
{ name: "toAddress", type: "address" },
|
|
45
|
-
{ name: "lockId", type: "uint256" },
|
|
46
|
-
{ name: "amount", type: "uint256" },
|
|
47
|
-
{ name: "nonce", type: "uint256" },
|
|
48
|
-
{ name: "serviceAddress", type: "address" }
|
|
49
|
-
]
|
|
50
|
-
};
|
|
51
|
-
var WITHDRAW_TYPES = {
|
|
52
|
-
Withdraw: [
|
|
53
|
-
{ name: "tokenId", type: "bytes32" },
|
|
54
|
-
{ name: "amount", type: "uint256" },
|
|
55
|
-
{ name: "nonce", type: "uint256" }
|
|
56
|
-
]
|
|
57
|
-
};
|
|
58
|
-
var MODIFY_LOCK_TYPES = {
|
|
59
|
-
ModifyLock: [
|
|
60
|
-
{ name: "lockId", type: "uint256" },
|
|
61
|
-
{ name: "amount", type: "uint256" },
|
|
62
|
-
{ name: "newExpiry", type: "uint256" },
|
|
63
|
-
{ name: "nonce", type: "uint256" }
|
|
64
|
-
]
|
|
65
|
-
};
|
|
66
|
-
var WITHDRAW_FROM_LOCK_TYPES = {
|
|
67
|
-
WithdrawFromLock: [
|
|
68
|
-
{ name: "userAddress", type: "address" },
|
|
69
|
-
{ name: "toAddress", type: "address" },
|
|
70
|
-
{ name: "lockId", type: "uint256" },
|
|
71
|
-
{ name: "amount", type: "uint256" },
|
|
72
|
-
{ name: "nonce", type: "uint256" }
|
|
73
|
-
]
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// src/sdk/signatures/sign-lock.ts
|
|
77
|
-
async function signLockMessage({
|
|
78
|
-
walletClient,
|
|
79
|
-
chainId,
|
|
80
|
-
verifyingContract,
|
|
81
|
-
message
|
|
82
|
-
}) {
|
|
83
|
-
const account = walletClient.account;
|
|
84
|
-
if (!account) {
|
|
85
|
-
throw new Error("No account connected to wallet client");
|
|
86
|
-
}
|
|
87
|
-
const domain = createDomain(chainId, verifyingContract);
|
|
88
|
-
const signature = await walletClient.signTypedData({
|
|
89
|
-
account,
|
|
90
|
-
domain,
|
|
91
|
-
types: LOCK_TYPES,
|
|
92
|
-
primaryType: "Lock",
|
|
93
|
-
message
|
|
94
|
-
});
|
|
95
|
-
return signature;
|
|
96
|
-
}
|
|
97
|
-
function createLockExpiry(minutesFromNow = 60) {
|
|
98
|
-
return BigInt(Math.floor(Date.now() / 1e3) + minutesFromNow * 60);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// src/sdk/signatures/sign-modify-lock.ts
|
|
102
|
-
async function signModifyLockMessage({
|
|
103
|
-
walletClient,
|
|
104
|
-
chainId,
|
|
105
|
-
verifyingContract,
|
|
106
|
-
message
|
|
107
|
-
}) {
|
|
108
|
-
const account = walletClient.account;
|
|
109
|
-
if (!account) {
|
|
110
|
-
throw new Error("No account connected to wallet client");
|
|
111
|
-
}
|
|
112
|
-
const domain = createDomain(chainId, verifyingContract);
|
|
113
|
-
const signature = await walletClient.signTypedData({
|
|
114
|
-
account,
|
|
115
|
-
domain,
|
|
116
|
-
types: MODIFY_LOCK_TYPES,
|
|
117
|
-
primaryType: "ModifyLock",
|
|
118
|
-
message
|
|
119
|
-
});
|
|
120
|
-
return signature;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// src/sdk/signatures/sign-transfer.ts
|
|
124
|
-
async function signTransferMessage({
|
|
125
|
-
walletClient,
|
|
126
|
-
chainId,
|
|
127
|
-
verifyingContract,
|
|
128
|
-
message
|
|
129
|
-
}) {
|
|
130
|
-
const account = walletClient.account;
|
|
131
|
-
if (!account) {
|
|
132
|
-
throw new Error("No account connected to wallet client");
|
|
133
|
-
}
|
|
134
|
-
const domain = createDomain(chainId, verifyingContract);
|
|
135
|
-
const signature = await walletClient.signTypedData({
|
|
136
|
-
account,
|
|
137
|
-
domain,
|
|
138
|
-
types: TRANSFER_TYPES,
|
|
139
|
-
primaryType: "Transfer",
|
|
140
|
-
message
|
|
141
|
-
});
|
|
142
|
-
return signature;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// src/sdk/signatures/sign-transfer-locked.ts
|
|
146
|
-
async function signTransferLockedMessage({
|
|
147
|
-
walletClient,
|
|
148
|
-
chainId,
|
|
149
|
-
verifyingContract,
|
|
150
|
-
message
|
|
151
|
-
}) {
|
|
152
|
-
const account = walletClient.account;
|
|
153
|
-
if (!account) {
|
|
154
|
-
throw new Error("No account connected to wallet client");
|
|
155
|
-
}
|
|
156
|
-
const domain = createDomain(chainId, verifyingContract);
|
|
157
|
-
const signature = await walletClient.signTypedData({
|
|
158
|
-
account,
|
|
159
|
-
domain,
|
|
160
|
-
types: TRANSFER_LOCKED_TYPES,
|
|
161
|
-
primaryType: "TransferLocked",
|
|
162
|
-
message
|
|
163
|
-
});
|
|
164
|
-
return signature;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// src/sdk/signatures/sign-withdraw.ts
|
|
168
|
-
async function signWithdrawMessage({
|
|
169
|
-
walletClient,
|
|
170
|
-
chainId,
|
|
171
|
-
verifyingContract,
|
|
172
|
-
message
|
|
173
|
-
}) {
|
|
174
|
-
const account = walletClient.account;
|
|
175
|
-
if (!account) {
|
|
176
|
-
throw new Error("No account connected to wallet client");
|
|
177
|
-
}
|
|
178
|
-
const domain = createDomain(chainId, verifyingContract);
|
|
179
|
-
const signature = await walletClient.signTypedData({
|
|
180
|
-
account,
|
|
181
|
-
domain,
|
|
182
|
-
types: WITHDRAW_TYPES,
|
|
183
|
-
primaryType: "Withdraw",
|
|
184
|
-
message
|
|
185
|
-
});
|
|
186
|
-
return signature;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// src/sdk/signatures/sign-withdraw-from-lock.ts
|
|
190
|
-
async function signWithdrawFromLockMessage({
|
|
191
|
-
walletClient,
|
|
192
|
-
chainId,
|
|
193
|
-
verifyingContract,
|
|
194
|
-
message
|
|
195
|
-
}) {
|
|
196
|
-
const account = walletClient.account;
|
|
197
|
-
if (!account) {
|
|
198
|
-
throw new Error("No account connected to wallet client");
|
|
199
|
-
}
|
|
200
|
-
const domain = createDomain(chainId, verifyingContract);
|
|
201
|
-
const signature = await walletClient.signTypedData({
|
|
202
|
-
account,
|
|
203
|
-
domain,
|
|
204
|
-
types: WITHDRAW_FROM_LOCK_TYPES,
|
|
205
|
-
primaryType: "WithdrawFromLock",
|
|
206
|
-
message
|
|
207
|
-
});
|
|
208
|
-
return signature;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
15
|
// src/sdk/hooks/use-privana-client.ts
|
|
212
16
|
function usePrivanaClient() {
|
|
213
17
|
const { client } = usePrivanaContext();
|
|
@@ -468,75 +272,22 @@ function useBatchBalances(options) {
|
|
|
468
272
|
refetch: query.refetch
|
|
469
273
|
};
|
|
470
274
|
}
|
|
471
|
-
function useEnsureCorrectChain() {
|
|
472
|
-
const config = useConfig();
|
|
473
|
-
const chainId = useChainId();
|
|
474
|
-
const { switchChainAsync } = useSwitchChain();
|
|
475
|
-
const waitUntilOnChain = useCallback(
|
|
476
|
-
async (expectedChainId, timeoutMs, pollIntervalMs = 250) => {
|
|
477
|
-
const startedAt = Date.now();
|
|
478
|
-
while (Date.now() - startedAt < timeoutMs) {
|
|
479
|
-
const currentChainId = getChainId2(config);
|
|
480
|
-
if (currentChainId === expectedChainId) return true;
|
|
481
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
482
|
-
}
|
|
483
|
-
return false;
|
|
484
|
-
},
|
|
485
|
-
[config]
|
|
486
|
-
);
|
|
487
|
-
const ensureCorrectChain = useCallback(
|
|
488
|
-
async (targetChainId) => {
|
|
489
|
-
const currentChainId = getChainId2(config);
|
|
490
|
-
if (currentChainId === targetChainId) return false;
|
|
491
|
-
let switchErrorMessage;
|
|
492
|
-
try {
|
|
493
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
494
|
-
setTimeout(() => reject(new Error("Chain switch timeout")), 3e3);
|
|
495
|
-
});
|
|
496
|
-
await Promise.race([switchChainAsync({ chainId: targetChainId }), timeoutPromise]);
|
|
497
|
-
} catch (error) {
|
|
498
|
-
if (error instanceof Error && error.message.includes("Unsupported Chain")) {
|
|
499
|
-
console.warn("Got 'Unsupported Chain' error, chain may have switched anyway.");
|
|
500
|
-
} else {
|
|
501
|
-
switchErrorMessage = error instanceof Error ? error.message : "Unknown chain switch error";
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
const settled = await waitUntilOnChain(targetChainId, 2e4);
|
|
505
|
-
if (settled) return true;
|
|
506
|
-
if (switchErrorMessage) {
|
|
507
|
-
throw new Error(
|
|
508
|
-
`Failed to switch to chain (Chain ID: ${targetChainId}): ${switchErrorMessage}`
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
throw new Error(`Chain switch did not settle in time (expected ${targetChainId}).`);
|
|
512
|
-
},
|
|
513
|
-
[config, switchChainAsync, waitUntilOnChain]
|
|
514
|
-
);
|
|
515
|
-
const isOnChain = useCallback((targetChainId) => chainId === targetChainId, [chainId]);
|
|
516
|
-
return {
|
|
517
|
-
chainId,
|
|
518
|
-
ensureCorrectChain,
|
|
519
|
-
isOnChain
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// src/sdk/hooks/use-deposit.ts
|
|
524
275
|
var STALE_MS = 30 * 60 * 1e3;
|
|
525
276
|
function storageKey(address) {
|
|
526
277
|
return `privana:pending-deposit:${address.toLowerCase()}`;
|
|
527
278
|
}
|
|
528
279
|
function savePendingDeposit(address, data) {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
280
|
+
const stored = setBrowserStorageItem(storageKey(address), JSON.stringify(data));
|
|
281
|
+
if (!stored && data.signedLock) {
|
|
282
|
+
throw new Error("Unable to persist pending locked deposit for recovery");
|
|
532
283
|
}
|
|
533
284
|
}
|
|
534
285
|
function loadPendingDeposit(address) {
|
|
535
286
|
try {
|
|
536
|
-
const raw =
|
|
287
|
+
const raw = getBrowserStorageItem(storageKey(address));
|
|
537
288
|
if (!raw) return null;
|
|
538
289
|
const data = JSON.parse(raw);
|
|
539
|
-
if (Date.now() - data.savedAt > STALE_MS) {
|
|
290
|
+
if (Date.now() - data.savedAt > STALE_MS && !(data.signedLock && isSignedLockUsable(data.signedLock))) {
|
|
540
291
|
clearPendingDeposit(address);
|
|
541
292
|
return null;
|
|
542
293
|
}
|
|
@@ -545,15 +296,20 @@ function loadPendingDeposit(address) {
|
|
|
545
296
|
return null;
|
|
546
297
|
}
|
|
547
298
|
}
|
|
548
|
-
function clearPendingDeposit(address) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
299
|
+
function clearPendingDeposit(address, onlyForTxHash) {
|
|
300
|
+
if (onlyForTxHash) {
|
|
301
|
+
try {
|
|
302
|
+
const raw = getBrowserStorageItem(storageKey(address));
|
|
303
|
+
const recordTxHash = raw ? JSON.parse(raw).txHash : void 0;
|
|
304
|
+
if (recordTxHash && recordTxHash !== onlyForTxHash) return;
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
552
307
|
}
|
|
308
|
+
removeBrowserStorageItem(storageKey(address));
|
|
553
309
|
}
|
|
554
310
|
function useDeposit(options = {}) {
|
|
555
311
|
const { address } = useAccount();
|
|
556
|
-
const { client, enabledTokens, getChainById: getChainById2 } = usePrivanaContext();
|
|
312
|
+
const { client, enabledTokens, getChainById: getChainById2, networkConfig, serviceAddress } = usePrivanaContext();
|
|
557
313
|
const { data: walletClient } = useWalletClient();
|
|
558
314
|
const queryClient = useQueryClient();
|
|
559
315
|
const config = useConfig();
|
|
@@ -570,21 +326,54 @@ function useDeposit(options = {}) {
|
|
|
570
326
|
const onDepositAddressReceivedRef = useRef(options.onDepositAddressReceived);
|
|
571
327
|
const onDepositSuccessRef = useRef(options.onDepositSuccess);
|
|
572
328
|
const onCreditedRef = useRef(options.onCredited);
|
|
329
|
+
const onLockSubmittedRef = useRef(options.onLockSubmitted);
|
|
330
|
+
const onLockFailedRef = useRef(options.onLockFailed);
|
|
573
331
|
const onCheckTimeoutRef = useRef(options.onCheckTimeout);
|
|
574
332
|
const onErrorRef = useRef(options.onError);
|
|
575
333
|
useEffect(() => {
|
|
576
334
|
onDepositAddressReceivedRef.current = options.onDepositAddressReceived;
|
|
577
335
|
onDepositSuccessRef.current = options.onDepositSuccess;
|
|
578
336
|
onCreditedRef.current = options.onCredited;
|
|
337
|
+
onLockSubmittedRef.current = options.onLockSubmitted;
|
|
338
|
+
onLockFailedRef.current = options.onLockFailed;
|
|
579
339
|
onCheckTimeoutRef.current = options.onCheckTimeout;
|
|
580
340
|
onErrorRef.current = options.onError;
|
|
581
341
|
}, [
|
|
582
342
|
options.onDepositAddressReceived,
|
|
583
343
|
options.onDepositSuccess,
|
|
584
344
|
options.onCredited,
|
|
345
|
+
options.onLockSubmitted,
|
|
346
|
+
options.onLockFailed,
|
|
585
347
|
options.onCheckTimeout,
|
|
586
348
|
options.onError
|
|
587
349
|
]);
|
|
350
|
+
const pendingLockRef = useRef(null);
|
|
351
|
+
const submitPendingLockAfterCredit = useCallback(
|
|
352
|
+
async (signedLock, response) => {
|
|
353
|
+
try {
|
|
354
|
+
const result = await submitPendingLock({
|
|
355
|
+
client,
|
|
356
|
+
payload: signedLock,
|
|
357
|
+
creditedAmount: response.amount != null ? BigInt(response.amount) : void 0
|
|
358
|
+
});
|
|
359
|
+
queryClient.invalidateQueries({ queryKey: ["accounting-balance"] });
|
|
360
|
+
queryClient.invalidateQueries({ queryKey: ["accounting-locked-funds"] });
|
|
361
|
+
queryClient.invalidateQueries({ queryKey: ["accounting-total-locked-balance"] });
|
|
362
|
+
queryClient.invalidateQueries({ queryKey: ["accounting-history"] });
|
|
363
|
+
onLockSubmittedRef.current?.(result);
|
|
364
|
+
} catch (err) {
|
|
365
|
+
const error2 = err instanceof PostDepositLockError ? err : new PostDepositLockError(
|
|
366
|
+
err instanceof Error ? err.message : "Lock submission failed",
|
|
367
|
+
"submission-failed",
|
|
368
|
+
BigInt(signedLock.amount),
|
|
369
|
+
void 0,
|
|
370
|
+
{ cause: err }
|
|
371
|
+
);
|
|
372
|
+
(onLockFailedRef.current ?? onErrorRef.current)?.(error2);
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
[client, queryClient]
|
|
376
|
+
);
|
|
588
377
|
const {
|
|
589
378
|
isVerifying,
|
|
590
379
|
didTimeout,
|
|
@@ -597,8 +386,16 @@ function useDeposit(options = {}) {
|
|
|
597
386
|
pollTimeout: options.pollTimeout,
|
|
598
387
|
onCredited: (hash, response) => {
|
|
599
388
|
verificationContextRef.current = null;
|
|
600
|
-
|
|
601
|
-
|
|
389
|
+
const signedLock = pendingLockRef.current;
|
|
390
|
+
pendingLockRef.current = null;
|
|
391
|
+
if (signedLock) {
|
|
392
|
+
void submitPendingLockAfterCredit(signedLock, response).finally(() => {
|
|
393
|
+
if (address) clearPendingDeposit(address, hash);
|
|
394
|
+
});
|
|
395
|
+
} else if (address) {
|
|
396
|
+
clearPendingDeposit(address);
|
|
397
|
+
}
|
|
398
|
+
onCreditedRef.current?.(hash, response, signedLock !== null);
|
|
602
399
|
},
|
|
603
400
|
onCheckTimeout: (hash) => {
|
|
604
401
|
onCheckTimeoutRef.current?.(hash);
|
|
@@ -649,6 +446,7 @@ function useDeposit(options = {}) {
|
|
|
649
446
|
resetVerification();
|
|
650
447
|
if (address) clearPendingDeposit(address);
|
|
651
448
|
verificationContextRef.current = null;
|
|
449
|
+
pendingLockRef.current = null;
|
|
652
450
|
setDepositAddress(null);
|
|
653
451
|
setTxHash(void 0);
|
|
654
452
|
setIsSwitchingChain(false);
|
|
@@ -668,6 +466,7 @@ function useDeposit(options = {}) {
|
|
|
668
466
|
setTxHash(hash);
|
|
669
467
|
setDepositAddress(persisted.depositAddress);
|
|
670
468
|
setIsWaitingForConfirmation(true);
|
|
469
|
+
pendingLockRef.current = persisted.signedLock ?? null;
|
|
671
470
|
const ctx = {
|
|
672
471
|
hash,
|
|
673
472
|
chainId: persisted.chainId,
|
|
@@ -724,13 +523,6 @@ function useDeposit(options = {}) {
|
|
|
724
523
|
if (!sourceChain) throw new Error(`Chain ${token.chainId} not configured`);
|
|
725
524
|
const addrResponse = await addressMutation.mutateAsync();
|
|
726
525
|
if (isStale()) return;
|
|
727
|
-
setIsSwitchingChain(true);
|
|
728
|
-
try {
|
|
729
|
-
await ensureCorrectChain(sourceChain.id);
|
|
730
|
-
} finally {
|
|
731
|
-
if (!isStale()) setIsSwitchingChain(false);
|
|
732
|
-
}
|
|
733
|
-
if (isStale()) return;
|
|
734
526
|
const depositAddr = addrResponse.deposit_address;
|
|
735
527
|
if (!depositAddr || depositAddr === zeroAddress) {
|
|
736
528
|
throw new Error("Invalid deposit address received from API");
|
|
@@ -743,6 +535,43 @@ function useDeposit(options = {}) {
|
|
|
743
535
|
`Amount is below the minimum deposit (${minAmountStr}) for ${isNative ? "native" : "ERC-20"} on chain ${sourceChain.id}`
|
|
744
536
|
);
|
|
745
537
|
}
|
|
538
|
+
if (params.postDepositLock && !canUseBrowserStorage()) {
|
|
539
|
+
throw new Error("Browser storage is required for locked deposit recovery");
|
|
540
|
+
}
|
|
541
|
+
const lockAmount = clampLockAmount(params.amount, params.postDepositLock?.maxAmount);
|
|
542
|
+
let signedLock;
|
|
543
|
+
if (params.postDepositLock) {
|
|
544
|
+
setIsSwitchingChain(true);
|
|
545
|
+
try {
|
|
546
|
+
await ensureCorrectChain(networkConfig.chainId);
|
|
547
|
+
} finally {
|
|
548
|
+
if (!isStale()) setIsSwitchingChain(false);
|
|
549
|
+
}
|
|
550
|
+
if (isStale()) return;
|
|
551
|
+
const signingWalletClient = await getWalletClient(config, {
|
|
552
|
+
chainId: networkConfig.chainId
|
|
553
|
+
});
|
|
554
|
+
signedLock = await createSignedLockRequest({
|
|
555
|
+
client,
|
|
556
|
+
walletClient: signingWalletClient,
|
|
557
|
+
userAddress: address,
|
|
558
|
+
networkConfig,
|
|
559
|
+
serviceAddress: requireServiceAddress(
|
|
560
|
+
params.postDepositLock.serviceAddress ?? serviceAddress
|
|
561
|
+
),
|
|
562
|
+
tokenId: params.tokenId,
|
|
563
|
+
amount: lockAmount,
|
|
564
|
+
lockDuration: params.postDepositLock.lockDuration
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
if (isStale()) return;
|
|
568
|
+
setIsSwitchingChain(true);
|
|
569
|
+
try {
|
|
570
|
+
await ensureCorrectChain(sourceChain.id);
|
|
571
|
+
} finally {
|
|
572
|
+
if (!isStale()) setIsSwitchingChain(false);
|
|
573
|
+
}
|
|
574
|
+
if (isStale()) return;
|
|
746
575
|
const hash = token.contract === zeroAddress ? await sendTransactionAsync({
|
|
747
576
|
to: depositAddr,
|
|
748
577
|
value: params.amount,
|
|
@@ -762,13 +591,19 @@ function useDeposit(options = {}) {
|
|
|
762
591
|
amount: params.amount
|
|
763
592
|
};
|
|
764
593
|
verificationContextRef.current = ctx;
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
594
|
+
pendingLockRef.current = signedLock ?? null;
|
|
595
|
+
try {
|
|
596
|
+
savePendingDeposit(address, {
|
|
597
|
+
txHash: hash,
|
|
598
|
+
chainId: sourceChain.id,
|
|
599
|
+
amount: params.amount.toString(),
|
|
600
|
+
depositAddress: addrResponse,
|
|
601
|
+
signedLock,
|
|
602
|
+
savedAt: Date.now()
|
|
603
|
+
});
|
|
604
|
+
} catch (err) {
|
|
605
|
+
console.warn("Failed to persist pending deposit after transfer broadcast:", err);
|
|
606
|
+
}
|
|
772
607
|
try {
|
|
773
608
|
setIsWaitingForConfirmation(true);
|
|
774
609
|
try {
|
|
@@ -803,12 +638,15 @@ function useDeposit(options = {}) {
|
|
|
803
638
|
address,
|
|
804
639
|
walletClient,
|
|
805
640
|
addressMutation,
|
|
641
|
+
client,
|
|
806
642
|
getChainById2,
|
|
807
643
|
config,
|
|
808
644
|
confirmations,
|
|
809
645
|
enabledTokens,
|
|
810
646
|
ensureCorrectChain,
|
|
647
|
+
networkConfig,
|
|
811
648
|
queryClient,
|
|
649
|
+
serviceAddress,
|
|
812
650
|
writeContractAsync,
|
|
813
651
|
sendTransactionAsync,
|
|
814
652
|
reset,
|
|
@@ -843,6 +681,12 @@ function useDeposit(options = {}) {
|
|
|
843
681
|
reset
|
|
844
682
|
};
|
|
845
683
|
}
|
|
684
|
+
function requireServiceAddress(serviceAddress) {
|
|
685
|
+
if (!serviceAddress) {
|
|
686
|
+
throw new Error("Service address not configured");
|
|
687
|
+
}
|
|
688
|
+
return serviceAddress;
|
|
689
|
+
}
|
|
846
690
|
function useDepositAddress(options = {}) {
|
|
847
691
|
const queryClient = useContext(QueryClientContext);
|
|
848
692
|
const accountingContext = useSafePrivanaContext();
|
|
@@ -1370,6 +1214,59 @@ function useExpiredLocks(options = {}) {
|
|
|
1370
1214
|
refetch: query.refetch
|
|
1371
1215
|
};
|
|
1372
1216
|
}
|
|
1217
|
+
function statusCodeOf(error) {
|
|
1218
|
+
return error instanceof AccountingApiError ? error.statusCode : void 0;
|
|
1219
|
+
}
|
|
1220
|
+
function usePendingDeposits(options = {}) {
|
|
1221
|
+
const queryClient = useContext(QueryClientContext);
|
|
1222
|
+
const accountingContext = useSafePrivanaContext();
|
|
1223
|
+
const hasProviders = !!queryClient && !!accountingContext;
|
|
1224
|
+
const client = accountingContext?.client;
|
|
1225
|
+
const { executePrivateRead, privateReadAddress, privateReadQueryScope, privateReadReady } = usePrivateReadRequest();
|
|
1226
|
+
const { chainId, tokenAddress, lookbackBlocks } = options;
|
|
1227
|
+
const query = useQuery({
|
|
1228
|
+
queryKey: ["accounting-pending-deposits", ...privateReadQueryScope, chainId, tokenAddress],
|
|
1229
|
+
queryFn: async () => {
|
|
1230
|
+
if (!privateReadAddress) throw new Error("No authenticated account available");
|
|
1231
|
+
if (!chainId) throw new Error("No chain ID provided");
|
|
1232
|
+
if (!client) throw new Error("No accounting client");
|
|
1233
|
+
try {
|
|
1234
|
+
return await executePrivateRead(
|
|
1235
|
+
() => client.getPendingDeposits({
|
|
1236
|
+
chain_id: chainId,
|
|
1237
|
+
token_address: tokenAddress,
|
|
1238
|
+
lookback_blocks: lookbackBlocks
|
|
1239
|
+
})
|
|
1240
|
+
);
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
if (statusCodeOf(error) === 404) {
|
|
1243
|
+
return { pending: [], scanned_from_block: 0, scanned_to_block: 0 };
|
|
1244
|
+
}
|
|
1245
|
+
throw error;
|
|
1246
|
+
}
|
|
1247
|
+
},
|
|
1248
|
+
enabled: hasProviders && (options.enabled ?? true) && privateReadReady && !!privateReadAddress && !!chainId && !!client,
|
|
1249
|
+
// A 503 means discovery is not configured for this chain server-side —
|
|
1250
|
+
// polling can't fix that, so stop until a manual refetch succeeds.
|
|
1251
|
+
refetchInterval: (query2) => statusCodeOf(query2.state.error) === 503 ? false : options.refetchInterval ?? 3e4,
|
|
1252
|
+
staleTime: 25e3,
|
|
1253
|
+
retry: (failureCount, error) => {
|
|
1254
|
+
const status = statusCodeOf(error);
|
|
1255
|
+
if (status === 429 || status === 404 || status === 503) return false;
|
|
1256
|
+
return failureCount < 2;
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
return {
|
|
1260
|
+
pending: query.data?.pending ?? [],
|
|
1261
|
+
scannedToBlock: query.data?.scanned_to_block,
|
|
1262
|
+
isFetching: query.isFetching,
|
|
1263
|
+
isError: query.isError,
|
|
1264
|
+
error: query.error,
|
|
1265
|
+
isRateLimited: statusCodeOf(query.error) === 429,
|
|
1266
|
+
isUnavailable: statusCodeOf(query.error) === 503,
|
|
1267
|
+
refetch: async () => (await query.refetch()).data
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1373
1270
|
function usePendingWithdrawals(options = {}) {
|
|
1374
1271
|
const { address, isConnected } = useAccount();
|
|
1375
1272
|
const { client, pollingInterval } = usePrivanaContext();
|
|
@@ -3285,7 +3182,10 @@ function TokenSelectorView2({ selectedTokenId, onSelect }) {
|
|
|
3285
3182
|
function CreditCardWidgetView({
|
|
3286
3183
|
token,
|
|
3287
3184
|
amount,
|
|
3288
|
-
allowance
|
|
3185
|
+
allowance,
|
|
3186
|
+
onCredited,
|
|
3187
|
+
onLockSubmitted,
|
|
3188
|
+
onLockFailed
|
|
3289
3189
|
}) {
|
|
3290
3190
|
const moonpayCurrencyCode = token?.moonpayCurrencyCode;
|
|
3291
3191
|
const containerRef = useRef(null);
|
|
@@ -3300,10 +3200,18 @@ function CreditCardWidgetView({
|
|
|
3300
3200
|
{
|
|
3301
3201
|
tokenId: token.id,
|
|
3302
3202
|
currencyCode: moonpayCurrencyCode,
|
|
3303
|
-
|
|
3203
|
+
quoteCurrencyAmount: amount || void 0,
|
|
3204
|
+
lockAmount: true,
|
|
3304
3205
|
variant: "embedded",
|
|
3305
3206
|
autoStart: true,
|
|
3306
|
-
theme: widgetTheme
|
|
3207
|
+
theme: widgetTheme,
|
|
3208
|
+
postDepositLock: allowance ? {
|
|
3209
|
+
maxAmount: BigInt(allowance.value),
|
|
3210
|
+
lockDuration: allowance.lockDuration
|
|
3211
|
+
} : void 0,
|
|
3212
|
+
onCredited,
|
|
3213
|
+
onLockSubmitted,
|
|
3214
|
+
onLockFailed
|
|
3307
3215
|
}
|
|
3308
3216
|
) : /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground text-sm", children: [
|
|
3309
3217
|
token?.symbol ?? "This token",
|
|
@@ -3605,7 +3513,7 @@ function DepositView({
|
|
|
3605
3513
|
className: "bg-secondary text-foreground hover:bg-secondary/80 cursor-pointer rounded px-3 py-2.5 text-xs font-semibold transition-colors",
|
|
3606
3514
|
children: "MAX"
|
|
3607
3515
|
}
|
|
3608
|
-
) : isCreditCard ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: "USD" }) : selectedToken && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: selectedToken.symbol })
|
|
3516
|
+
) : isCreditCard ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: selectedToken?.symbol ?? "USD" }) : selectedToken && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: selectedToken.symbol })
|
|
3609
3517
|
]
|
|
3610
3518
|
}
|
|
3611
3519
|
),
|
|
@@ -3659,14 +3567,105 @@ function SummaryRow({ value, label }) {
|
|
|
3659
3567
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label })
|
|
3660
3568
|
] });
|
|
3661
3569
|
}
|
|
3570
|
+
var LISTENING_WINDOW_MS = 36e5;
|
|
3571
|
+
function remainingSeconds(deadline) {
|
|
3572
|
+
return Math.max(0, Math.round((deadline - Date.now()) / 1e3));
|
|
3573
|
+
}
|
|
3574
|
+
function useCountdown(deadline) {
|
|
3575
|
+
const [secondsLeft, setSecondsLeft] = useState(() => remainingSeconds(deadline));
|
|
3576
|
+
useEffect(() => {
|
|
3577
|
+
if (remainingSeconds(deadline) <= 0) return;
|
|
3578
|
+
const id = setInterval(() => {
|
|
3579
|
+
const left = remainingSeconds(deadline);
|
|
3580
|
+
setSecondsLeft(left);
|
|
3581
|
+
if (left <= 0) clearInterval(id);
|
|
3582
|
+
}, 1e3);
|
|
3583
|
+
return () => clearInterval(id);
|
|
3584
|
+
}, [deadline]);
|
|
3585
|
+
return secondsLeft;
|
|
3586
|
+
}
|
|
3587
|
+
function AwaitingDepositStatus({
|
|
3588
|
+
title,
|
|
3589
|
+
subtitle,
|
|
3590
|
+
remaining
|
|
3591
|
+
}) {
|
|
3592
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 text-center", children: [
|
|
3593
|
+
/* @__PURE__ */ jsx("div", { className: "text-foreground", children: /* @__PURE__ */ jsx(Spinner, { size: 32 }) }),
|
|
3594
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
3595
|
+
/* @__PURE__ */ jsx("h3", { className: "text-foreground text-xl leading-6 font-medium", children: title }),
|
|
3596
|
+
/* @__PURE__ */ jsxs("p", { className: "text-muted-foreground text-sm leading-[18px]", children: [
|
|
3597
|
+
subtitle,
|
|
3598
|
+
remaining && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3599
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
3600
|
+
"Remaining time: ",
|
|
3601
|
+
remaining
|
|
3602
|
+
] })
|
|
3603
|
+
] })
|
|
3604
|
+
] })
|
|
3605
|
+
] });
|
|
3606
|
+
}
|
|
3662
3607
|
function ExternalDepositView({
|
|
3663
3608
|
token,
|
|
3664
|
-
amount
|
|
3609
|
+
amount,
|
|
3610
|
+
onCredited
|
|
3665
3611
|
}) {
|
|
3666
3612
|
const { getChainById: getChainById2 } = usePrivanaContext();
|
|
3667
3613
|
const { depositAddress, isReady, isLoading } = useDepositAddress();
|
|
3668
3614
|
const chain = token ? getChainById2(token.chainId) : void 0;
|
|
3669
3615
|
const [copied, setCopied] = useState(false);
|
|
3616
|
+
const [deadline] = useState(() => Date.now() + LISTENING_WINDOW_MS);
|
|
3617
|
+
const secondsLeft = useCountdown(deadline);
|
|
3618
|
+
const listening = secondsLeft > 0;
|
|
3619
|
+
const [nothingFound, setNothingFound] = useState(false);
|
|
3620
|
+
const [credited, setCredited] = useState(null);
|
|
3621
|
+
const verification = useDepositVerification({
|
|
3622
|
+
onCredited: (txHash) => {
|
|
3623
|
+
if (onCredited) {
|
|
3624
|
+
onCredited();
|
|
3625
|
+
return;
|
|
3626
|
+
}
|
|
3627
|
+
setCredited({ txHash });
|
|
3628
|
+
}
|
|
3629
|
+
});
|
|
3630
|
+
const { isVerifying, verificationFailed, didTimeout, verify } = verification;
|
|
3631
|
+
const scanPaused = isVerifying || verificationFailed || didTimeout || !!credited;
|
|
3632
|
+
const {
|
|
3633
|
+
pending,
|
|
3634
|
+
isFetching: isScanning,
|
|
3635
|
+
isError: isScanError,
|
|
3636
|
+
isRateLimited,
|
|
3637
|
+
isUnavailable,
|
|
3638
|
+
refetch: refetchPendingDeposits
|
|
3639
|
+
} = usePendingDeposits({
|
|
3640
|
+
chainId: token?.chainId,
|
|
3641
|
+
enabled: isReady && !!depositAddress && !!token,
|
|
3642
|
+
refetchInterval: listening && !scanPaused ? 3e4 : false
|
|
3643
|
+
});
|
|
3644
|
+
const processedRef = useRef(/* @__PURE__ */ new Set());
|
|
3645
|
+
useEffect(() => {
|
|
3646
|
+
if (scanPaused) return;
|
|
3647
|
+
const next = [...pending].sort((a, b) => a.block_number - b.block_number).find((d) => !processedRef.current.has(`${d.tx_hash}:${d.log_index}`));
|
|
3648
|
+
if (!next) return;
|
|
3649
|
+
processedRef.current.add(`${next.tx_hash}:${next.log_index}`);
|
|
3650
|
+
void verify({
|
|
3651
|
+
hash: next.tx_hash,
|
|
3652
|
+
chainId: next.chain_id,
|
|
3653
|
+
amount: BigInt(next.amount),
|
|
3654
|
+
logIndex: next.log_index
|
|
3655
|
+
});
|
|
3656
|
+
}, [pending, scanPaused, verify]);
|
|
3657
|
+
const nothingFoundTimerRef = useRef(void 0);
|
|
3658
|
+
useEffect(() => () => clearTimeout(nothingFoundTimerRef.current), []);
|
|
3659
|
+
const handleRefresh = () => {
|
|
3660
|
+
processedRef.current.clear();
|
|
3661
|
+
void refetchPendingDeposits().then((result) => {
|
|
3662
|
+
if (!result || result.pending.length === 0) {
|
|
3663
|
+
setNothingFound(true);
|
|
3664
|
+
clearTimeout(nothingFoundTimerRef.current);
|
|
3665
|
+
nothingFoundTimerRef.current = setTimeout(() => setNothingFound(false), 4e3);
|
|
3666
|
+
}
|
|
3667
|
+
});
|
|
3668
|
+
};
|
|
3670
3669
|
const handleCopy = () => {
|
|
3671
3670
|
if (!depositAddress) return;
|
|
3672
3671
|
void navigator.clipboard.writeText(depositAddress);
|
|
@@ -3704,6 +3703,55 @@ function ExternalDepositView({
|
|
|
3704
3703
|
}
|
|
3705
3704
|
)
|
|
3706
3705
|
] })
|
|
3706
|
+
] }),
|
|
3707
|
+
credited ? /* @__PURE__ */ jsx(
|
|
3708
|
+
TransactionSuccessView,
|
|
3709
|
+
{
|
|
3710
|
+
title: "Deposit Credited",
|
|
3711
|
+
message: `Transfer ${shortenAddress(credited.txHash)} has been credited to your Privana balance.`,
|
|
3712
|
+
onDone: () => {
|
|
3713
|
+
setCredited(null);
|
|
3714
|
+
verification.reset();
|
|
3715
|
+
void refetchPendingDeposits();
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
) : verificationFailed || didTimeout ? /* @__PURE__ */ jsx(
|
|
3719
|
+
TransactionErrorView,
|
|
3720
|
+
{
|
|
3721
|
+
title: "Could not verify deposit",
|
|
3722
|
+
message: didTimeout ? "Verification timed out \u2014 your deposit may still be credited in the background." : verification.error?.message ?? "Your transfer was found but could not be verified.",
|
|
3723
|
+
onRetry: () => void verification.retryVerification(),
|
|
3724
|
+
onDismiss: () => verification.reset(),
|
|
3725
|
+
isRetrying: isVerifying
|
|
3726
|
+
}
|
|
3727
|
+
) : isVerifying ? /* @__PURE__ */ jsx(
|
|
3728
|
+
AwaitingDepositStatus,
|
|
3729
|
+
{
|
|
3730
|
+
title: "Deposit detected",
|
|
3731
|
+
subtitle: "Crediting the incoming transaction to your Privana balance..."
|
|
3732
|
+
}
|
|
3733
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3734
|
+
/* @__PURE__ */ jsx(
|
|
3735
|
+
AwaitingDepositStatus,
|
|
3736
|
+
{
|
|
3737
|
+
title: "Awaiting Deposit",
|
|
3738
|
+
subtitle: listening ? "We are listening for your incoming transaction for the next hour." : "We've stopped listening automatically. Use the button below to check for your deposit.",
|
|
3739
|
+
remaining: listening ? formatCountdown(secondsLeft) : void 0
|
|
3740
|
+
}
|
|
3741
|
+
),
|
|
3742
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
3743
|
+
/* @__PURE__ */ jsx(
|
|
3744
|
+
"button",
|
|
3745
|
+
{
|
|
3746
|
+
type: "button",
|
|
3747
|
+
onClick: handleRefresh,
|
|
3748
|
+
disabled: !depositAddress || isScanning,
|
|
3749
|
+
className: "bg-secondary text-foreground hover:bg-secondary/80 flex h-10 w-full cursor-pointer items-center justify-center rounded-[10px] px-3 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50",
|
|
3750
|
+
children: "Refresh deposit status"
|
|
3751
|
+
}
|
|
3752
|
+
),
|
|
3753
|
+
isRateLimited ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-center text-sm", children: "Checked too recently \u2014 try again in a moment." }) : isUnavailable ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-center text-sm", children: "Deposit discovery is unavailable for this chain." }) : isScanError ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-center text-sm", children: "Chain temporarily unreachable \u2014 retrying automatically." }) : nothingFound ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-center text-sm", children: "No incoming transaction found yet." }) : null
|
|
3754
|
+
] })
|
|
3707
3755
|
] })
|
|
3708
3756
|
] });
|
|
3709
3757
|
}
|
|
@@ -3716,6 +3764,7 @@ function DepositModalContent({
|
|
|
3716
3764
|
onConnectWallet,
|
|
3717
3765
|
onDeposit,
|
|
3718
3766
|
onDepositSuccess,
|
|
3767
|
+
onLockFailed,
|
|
3719
3768
|
onClose,
|
|
3720
3769
|
onCloseBlockedChange,
|
|
3721
3770
|
onExit
|
|
@@ -3747,6 +3796,21 @@ function DepositModalContent({
|
|
|
3747
3796
|
const [showSuccess, setShowSuccess] = useState(false);
|
|
3748
3797
|
const [showTimeout, setShowTimeout] = useState(false);
|
|
3749
3798
|
const [cancelled, setCancelled] = useState(false);
|
|
3799
|
+
const [isSubmittingLock, setIsSubmittingLock] = useState(false);
|
|
3800
|
+
const [lockFailedMessage, setLockFailedMessage] = useState(null);
|
|
3801
|
+
const finishDeposit = () => {
|
|
3802
|
+
setAmount("");
|
|
3803
|
+
if (onDepositSuccess) {
|
|
3804
|
+
resetDeposit();
|
|
3805
|
+
onDepositSuccess();
|
|
3806
|
+
} else {
|
|
3807
|
+
setShowSuccess(true);
|
|
3808
|
+
}
|
|
3809
|
+
};
|
|
3810
|
+
const finishCardPurchase = () => {
|
|
3811
|
+
setAmount("");
|
|
3812
|
+
onDepositSuccess?.();
|
|
3813
|
+
};
|
|
3750
3814
|
const {
|
|
3751
3815
|
txHash,
|
|
3752
3816
|
isGettingAddress,
|
|
@@ -3761,14 +3825,24 @@ function DepositModalContent({
|
|
|
3761
3825
|
retryVerification,
|
|
3762
3826
|
reset: resetDeposit
|
|
3763
3827
|
} = useDeposit({
|
|
3764
|
-
onCredited: () => {
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
onDepositSuccess();
|
|
3769
|
-
} else {
|
|
3770
|
-
setShowSuccess(true);
|
|
3828
|
+
onCredited: (_txHash, _response, lockPending) => {
|
|
3829
|
+
if (lockPending) {
|
|
3830
|
+
setIsSubmittingLock(true);
|
|
3831
|
+
return;
|
|
3771
3832
|
}
|
|
3833
|
+
finishDeposit();
|
|
3834
|
+
},
|
|
3835
|
+
onLockSubmitted: () => {
|
|
3836
|
+
setIsSubmittingLock(false);
|
|
3837
|
+
finishDeposit();
|
|
3838
|
+
},
|
|
3839
|
+
// The deposit credited; only the policy lock failed. Success must not
|
|
3840
|
+
// fire (the host would act on unlocked funds) — show the dedicated
|
|
3841
|
+
// error view and let the host re-prompt for a fresh lock.
|
|
3842
|
+
onLockFailed: (err) => {
|
|
3843
|
+
setIsSubmittingLock(false);
|
|
3844
|
+
setLockFailedMessage(err.message);
|
|
3845
|
+
onLockFailed?.(err);
|
|
3772
3846
|
},
|
|
3773
3847
|
onCheckTimeout: () => {
|
|
3774
3848
|
setAmount("");
|
|
@@ -3801,7 +3875,11 @@ function DepositModalContent({
|
|
|
3801
3875
|
setCancelled(false);
|
|
3802
3876
|
deposit({
|
|
3803
3877
|
tokenId: token.id,
|
|
3804
|
-
amount: parseTokenAmount(args.amount, token.decimals)
|
|
3878
|
+
amount: parseTokenAmount(args.amount, token.decimals),
|
|
3879
|
+
postDepositLock: allowance ? {
|
|
3880
|
+
maxAmount: BigInt(allowance.value),
|
|
3881
|
+
lockDuration: allowance.lockDuration
|
|
3882
|
+
} : void 0
|
|
3805
3883
|
}).catch((err) => {
|
|
3806
3884
|
toast.error(err instanceof Error ? err.message : "Deposit failed");
|
|
3807
3885
|
});
|
|
@@ -3811,26 +3889,32 @@ function DepositModalContent({
|
|
|
3811
3889
|
const depositSteps = [
|
|
3812
3890
|
{
|
|
3813
3891
|
label: "Getting deposit address",
|
|
3814
|
-
status: isGettingAddress ? "active" : isSwitchingChain || isSendingTransaction || isWaitingForConfirmation || isWaitingForProcessing ? "completed" : "pending"
|
|
3892
|
+
status: isGettingAddress ? "active" : isSwitchingChain || isSendingTransaction || isWaitingForConfirmation || isWaitingForProcessing || isSubmittingLock ? "completed" : "pending"
|
|
3815
3893
|
},
|
|
3816
3894
|
{
|
|
3817
3895
|
label: `Switching to ${targetChain?.name ?? "deposit chain"}`,
|
|
3818
|
-
status: isSwitchingChain ? "active" : isSendingTransaction || isWaitingForConfirmation || isWaitingForProcessing ? "completed" : "pending"
|
|
3896
|
+
status: isSwitchingChain ? "active" : isSendingTransaction || isWaitingForConfirmation || isWaitingForProcessing || isSubmittingLock ? "completed" : "pending"
|
|
3819
3897
|
},
|
|
3820
3898
|
{
|
|
3821
3899
|
label: "Confirm in wallet",
|
|
3822
|
-
status: isSendingTransaction ? "active" : isWaitingForConfirmation || isWaitingForProcessing ? "completed" : "pending"
|
|
3900
|
+
status: isSendingTransaction ? "active" : isWaitingForConfirmation || isWaitingForProcessing || isSubmittingLock ? "completed" : "pending"
|
|
3823
3901
|
},
|
|
3824
3902
|
{
|
|
3825
3903
|
label: "Confirming transaction",
|
|
3826
|
-
status: isWaitingForConfirmation ? "active" : isWaitingForProcessing ? "completed" : "pending"
|
|
3904
|
+
status: isWaitingForConfirmation ? "active" : isWaitingForProcessing || isSubmittingLock ? "completed" : "pending"
|
|
3827
3905
|
},
|
|
3828
3906
|
{
|
|
3829
3907
|
label: "Verifying deposit \u2014 may take up to a few minutes",
|
|
3830
|
-
status: isWaitingForProcessing ? "active" : "pending"
|
|
3831
|
-
}
|
|
3908
|
+
status: isWaitingForProcessing ? "active" : isSubmittingLock ? "completed" : "pending"
|
|
3909
|
+
},
|
|
3910
|
+
...allowance ? [
|
|
3911
|
+
{
|
|
3912
|
+
label: `Locking funds for ${appName}`,
|
|
3913
|
+
status: isSubmittingLock ? "active" : "pending"
|
|
3914
|
+
}
|
|
3915
|
+
] : []
|
|
3832
3916
|
];
|
|
3833
|
-
const flowView = showSuccess ? "deposit-success" : showTimeout ? "deposit-timeout" : verificationFailed ? "deposit-error" : isPending && !cancelled ? "depositing" : null;
|
|
3917
|
+
const flowView = showSuccess ? "deposit-success" : lockFailedMessage ? "lock-error" : showTimeout ? "deposit-timeout" : verificationFailed ? "deposit-error" : isPending && !cancelled || isSubmittingLock ? "depositing" : null;
|
|
3834
3918
|
const activeView = flowView ?? view;
|
|
3835
3919
|
const handleDepositDone = () => {
|
|
3836
3920
|
setShowSuccess(false);
|
|
@@ -3842,6 +3926,12 @@ function DepositModalContent({
|
|
|
3842
3926
|
setCancelled(true);
|
|
3843
3927
|
resetDeposit();
|
|
3844
3928
|
};
|
|
3929
|
+
const handleLockFailedDone = () => {
|
|
3930
|
+
setLockFailedMessage(null);
|
|
3931
|
+
setAmount("");
|
|
3932
|
+
setCancelled(false);
|
|
3933
|
+
resetDeposit();
|
|
3934
|
+
};
|
|
3845
3935
|
const handleDismissVerificationError = () => {
|
|
3846
3936
|
setAmount("");
|
|
3847
3937
|
setCancelled(false);
|
|
@@ -3895,6 +3985,14 @@ function DepositModalContent({
|
|
|
3895
3985
|
onDone: handleDepositDone
|
|
3896
3986
|
}
|
|
3897
3987
|
),
|
|
3988
|
+
activeView === "lock-error" && /* @__PURE__ */ jsx(
|
|
3989
|
+
TransactionWarningView,
|
|
3990
|
+
{
|
|
3991
|
+
title: "Deposit credited, lock failed",
|
|
3992
|
+
message: `Your deposit was credited but locking the funds for ${appName} failed: ${lockFailedMessage}`,
|
|
3993
|
+
onDone: handleLockFailedDone
|
|
3994
|
+
}
|
|
3995
|
+
),
|
|
3898
3996
|
activeView === "deposit-timeout" && /* @__PURE__ */ jsx(
|
|
3899
3997
|
TransactionWarningView,
|
|
3900
3998
|
{
|
|
@@ -3988,8 +4086,28 @@ function DepositModalContent({
|
|
|
3988
4086
|
}
|
|
3989
4087
|
}
|
|
3990
4088
|
),
|
|
3991
|
-
activeView === "external-deposit" && /* @__PURE__ */ jsx(
|
|
3992
|
-
|
|
4089
|
+
activeView === "external-deposit" && /* @__PURE__ */ jsx(
|
|
4090
|
+
ExternalDepositView,
|
|
4091
|
+
{
|
|
4092
|
+
token: selectedToken,
|
|
4093
|
+
amount,
|
|
4094
|
+
onCredited: allowance ? void 0 : onDepositSuccess
|
|
4095
|
+
}
|
|
4096
|
+
),
|
|
4097
|
+
activeView === "credit-card-widget" && /* @__PURE__ */ jsx(MoonPayGate, { enabled: true, children: /* @__PURE__ */ jsx(
|
|
4098
|
+
CreditCardWidgetView,
|
|
4099
|
+
{
|
|
4100
|
+
token: selectedToken,
|
|
4101
|
+
amount,
|
|
4102
|
+
allowance,
|
|
4103
|
+
onCredited: allowance ? void 0 : finishCardPurchase,
|
|
4104
|
+
onLockSubmitted: finishCardPurchase,
|
|
4105
|
+
onLockFailed: (err) => {
|
|
4106
|
+
setLockFailedMessage(err.message);
|
|
4107
|
+
onLockFailed?.(err);
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
) })
|
|
3993
4111
|
] });
|
|
3994
4112
|
}
|
|
3995
4113
|
function DepositModal({ open, onClose, ...handlers }) {
|
|
@@ -4780,6 +4898,6 @@ function WalletInlineModal({ className, ...handlers }) {
|
|
|
4780
4898
|
);
|
|
4781
4899
|
}
|
|
4782
4900
|
|
|
4783
|
-
export { DepositInlineModal, DepositModal,
|
|
4901
|
+
export { DepositInlineModal, DepositModal, PrivanaButton, PrivanaIcon, PrivanaInlineModal, PrivanaModal, WalletInlineModal, WalletModal, WithdrawInlineModal, WithdrawModal, getChainIcon, getTokenIcon, useBalance, useBatchBalances, useDeposit, useDepositAddress, useExpiredLocks, useHistory, useHostedRedirectAuth, useLockFunds, useLockedFunds, useModifyLock, usePendingDeposits, usePendingWithdrawals, usePrivanaClient, useTokenInfo, useTokenList, useTotalLockedBalance, useTransfer, useUnlockFunds, useWithdraw };
|
|
4784
4902
|
//# sourceMappingURL=index.js.map
|
|
4785
4903
|
//# sourceMappingURL=index.js.map
|