@oasisprotocol/privana-sdk 0.5.1 → 0.5.3
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 +34 -0
- package/dist/{chunk-4IW4V7YJ.cjs → chunk-7KEQHWZB.cjs} +909 -240
- package/dist/chunk-7KEQHWZB.cjs.map +1 -0
- package/dist/{chunk-SAJ7K5WT.js → chunk-DZURIIRE.js} +905 -243
- package/dist/chunk-DZURIIRE.js.map +1 -0
- package/dist/index.cjs +1465 -365
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +2 -2
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +26 -13
- package/dist/index.d.ts +26 -13
- package/dist/index.js +1227 -135
- package/dist/index.js.map +1 -1
- package/dist/on-ramp.cjs +3 -3
- package/dist/on-ramp.d.cts +5 -4
- package/dist/on-ramp.d.ts +5 -4
- package/dist/on-ramp.js +1 -1
- package/dist/{pending-lock-BdkuMjmY.d.cts → pending-lock-f4bWWTyN.d.cts} +41 -7
- package/dist/{pending-lock-BdkuMjmY.d.ts → pending-lock-f4bWWTyN.d.ts} +41 -7
- package/package.json +1 -1
- package/dist/chunk-4IW4V7YJ.cjs.map +0 -1
- package/dist/chunk-SAJ7K5WT.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { createSiweMessage } from 'viem/siwe';
|
|
3
|
+
import { createContext, useContext, useRef, useCallback, useSyncExternalStore, useMemo, useState, useEffect } from 'react';
|
|
3
4
|
import { WagmiContext, useConfig, useChainId, useSwitchChain, useAccount, useWalletClient } from 'wagmi';
|
|
4
5
|
import { watchAccount, getAccount, getWalletClient } from 'wagmi/actions';
|
|
5
|
-
import { createSiweMessage } from 'viem/siwe';
|
|
6
6
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
7
|
import { parseAbiItem, zeroAddress, walletActions, hexToString, parseUnits, decodeEventLog, formatUnits, createClient, custom } from 'viem';
|
|
8
8
|
import { useQueryClient } from '@tanstack/react-query';
|
|
@@ -11,7 +11,7 @@ import { cva } from 'class-variance-authority';
|
|
|
11
11
|
import { clsx } from 'clsx';
|
|
12
12
|
import { twMerge } from 'tailwind-merge';
|
|
13
13
|
import { parseAccount, getAddress } from 'viem/utils';
|
|
14
|
-
import { getTransactionReceipt as getTransactionReceipt$1, waitForTransactionReceipt as waitForTransactionReceipt$1, getTransaction, call } from 'viem/actions';
|
|
14
|
+
import { getBlockNumber as getBlockNumber$1, getTransactionReceipt as getTransactionReceipt$1, waitForTransactionReceipt as waitForTransactionReceipt$1, getTransaction, call } from 'viem/actions';
|
|
15
15
|
import { Loader2, CircleCheckIcon } from 'lucide-react';
|
|
16
16
|
import { MoonPayBuyWidget } from '@moonpay/moonpay-react';
|
|
17
17
|
|
|
@@ -95,8 +95,11 @@ function getExplorerAddressUrl(chainId, address) {
|
|
|
95
95
|
return `${chain.explorerUrl}/address/${address}#tokentxns`;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
// src/sdk/auth/auth-clock-skew.ts
|
|
99
|
+
var AUTH_CLOCK_SKEW_MS = 3e4;
|
|
100
|
+
|
|
98
101
|
// src/sdk/auth/hosted-auth.ts
|
|
99
|
-
var HOSTED_AUTH_CLOCK_SKEW_MS =
|
|
102
|
+
var HOSTED_AUTH_CLOCK_SKEW_MS = AUTH_CLOCK_SKEW_MS;
|
|
100
103
|
var PKCE_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
101
104
|
var DEFAULT_RANDOM_LENGTH = 64;
|
|
102
105
|
var HOSTED_AUTH_CALLBACK_QUERY_KEYS = ["code", "error", "error_description", "state"];
|
|
@@ -218,11 +221,32 @@ function isHostedAuthSessionActive(session, now = Date.now(), skewMs = HOSTED_AU
|
|
|
218
221
|
function isHostedAuthRefreshActive(session, now = Date.now(), skewMs = HOSTED_AUTH_CLOCK_SKEW_MS) {
|
|
219
222
|
return session.refreshExpiresAt > now + skewMs;
|
|
220
223
|
}
|
|
221
|
-
|
|
222
|
-
// src/sdk/auth/siwe.ts
|
|
224
|
+
var SIWE_MESSAGE_VALIDITY_MS = 24 * 60 * 60 * 1e3;
|
|
223
225
|
function buildSiweStatement(chainId) {
|
|
224
226
|
return `Sign in to Privana on chain ${chainId}`;
|
|
225
227
|
}
|
|
228
|
+
async function buildSiweLoginMessage(api, params) {
|
|
229
|
+
const { address, chainId, apiUrl } = params;
|
|
230
|
+
const [{ domain }, { nonce }] = await Promise.all([
|
|
231
|
+
api.getSiweDomain(),
|
|
232
|
+
api.getSiweNonce(address)
|
|
233
|
+
]);
|
|
234
|
+
const issuedAt = /* @__PURE__ */ new Date();
|
|
235
|
+
const expirationTime = new Date(issuedAt.getTime() + SIWE_MESSAGE_VALIDITY_MS);
|
|
236
|
+
const uri = typeof window !== "undefined" && window.location.origin ? window.location.origin : apiUrl;
|
|
237
|
+
const message = createSiweMessage({
|
|
238
|
+
address,
|
|
239
|
+
chainId,
|
|
240
|
+
domain,
|
|
241
|
+
expirationTime,
|
|
242
|
+
issuedAt,
|
|
243
|
+
nonce,
|
|
244
|
+
statement: buildSiweStatement(chainId),
|
|
245
|
+
uri,
|
|
246
|
+
version: "1"
|
|
247
|
+
});
|
|
248
|
+
return { message, expirationTime };
|
|
249
|
+
}
|
|
226
250
|
|
|
227
251
|
// src/sdk/client/errors.ts
|
|
228
252
|
var AccountingApiError = class _AccountingApiError extends Error {
|
|
@@ -347,9 +371,10 @@ var HttpClient = class {
|
|
|
347
371
|
var PRIVATE_READ_TOKEN_HEADER = "X-SIWE-Token";
|
|
348
372
|
var MAX_BATCH_BALANCE_TOKEN_IDS = 100;
|
|
349
373
|
var MAX_HISTORY_PAGE_SIZE = 100;
|
|
350
|
-
var PrivanaClient = class {
|
|
374
|
+
var PrivanaClient = class _PrivanaClient {
|
|
351
375
|
constructor(config) {
|
|
352
376
|
this.http = new HttpClient(config);
|
|
377
|
+
this.baseConfig = { ...config, headers: { ...config.headers } };
|
|
353
378
|
}
|
|
354
379
|
getBaseUrl() {
|
|
355
380
|
return this.http.getBaseUrl();
|
|
@@ -619,10 +644,23 @@ var PrivanaClient = class {
|
|
|
619
644
|
async getPendingOnRamps() {
|
|
620
645
|
return this.http.get("/v1/accounting/onramp/pending");
|
|
621
646
|
}
|
|
647
|
+
/**
|
|
648
|
+
* @deprecated This mutates the shared client's headers and displaces its Authorization
|
|
649
|
+
* (JWT bearer) header, which breaks concurrent JWT-authenticated requests. Use
|
|
650
|
+
* {@link PrivanaClient.withPrivateReadToken} instead, which returns a scoped client that
|
|
651
|
+
* authenticates the private read without touching the shared client's headers. Will be
|
|
652
|
+
* removed in a future release.
|
|
653
|
+
*/
|
|
622
654
|
setPrivateReadToken(token) {
|
|
623
655
|
this.http.removeHeader("Authorization");
|
|
624
656
|
this.http.setHeader(PRIVATE_READ_TOKEN_HEADER, token);
|
|
625
657
|
}
|
|
658
|
+
/**
|
|
659
|
+
* @deprecated The shared client no longer carries a private-read token between requests;
|
|
660
|
+
* private reads now run on a scoped client from {@link PrivanaClient.withPrivateReadToken}.
|
|
661
|
+
* Returns whatever X-SIWE-Token is currently set on the shared client's headers. Will be
|
|
662
|
+
* removed in a future release.
|
|
663
|
+
*/
|
|
626
664
|
getPrivateReadToken() {
|
|
627
665
|
return this.http.getHeader(PRIVATE_READ_TOKEN_HEADER);
|
|
628
666
|
}
|
|
@@ -636,6 +674,26 @@ var PrivanaClient = class {
|
|
|
636
674
|
clearBearerToken() {
|
|
637
675
|
this.http.removeHeader("Authorization");
|
|
638
676
|
}
|
|
677
|
+
/**
|
|
678
|
+
* Returns a scoped client whose requests authenticate with the given SIWE private-read
|
|
679
|
+
* token (X-SIWE-Token) instead of the client-wide JWT bearer. The scoped client shares
|
|
680
|
+
* baseUrl/timeout/base headers but owns a separate header set, so private reads issued
|
|
681
|
+
* through it never displace the real client's Authorization header: concurrent
|
|
682
|
+
* JWT-authenticated requests on the real client keep their bearer, and a bearer installed
|
|
683
|
+
* mid-flight (auto-login/hydration) is never erased by a private read.
|
|
684
|
+
*/
|
|
685
|
+
withPrivateReadToken(token) {
|
|
686
|
+
const headers = {};
|
|
687
|
+
for (const [name, value] of Object.entries(this.baseConfig.headers ?? {})) {
|
|
688
|
+
const lower = name.toLowerCase();
|
|
689
|
+
if (lower === "authorization" || lower === PRIVATE_READ_TOKEN_HEADER.toLowerCase()) {
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
headers[name] = value;
|
|
693
|
+
}
|
|
694
|
+
headers[PRIVATE_READ_TOKEN_HEADER] = token;
|
|
695
|
+
return new _PrivanaClient({ ...this.baseConfig, headers });
|
|
696
|
+
}
|
|
639
697
|
};
|
|
640
698
|
|
|
641
699
|
// src/sdk/signatures/eip712-types.ts
|
|
@@ -865,8 +923,421 @@ function useSafeAccount() {
|
|
|
865
923
|
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
866
924
|
}
|
|
867
925
|
|
|
926
|
+
// src/sdk/auth/siwe-persistence.ts
|
|
927
|
+
var PERSISTED_SIWE_AUTH_RECORD_VERSION = 2;
|
|
928
|
+
var warnedBlockedStorage = false;
|
|
929
|
+
function warnBlockedStorageOnce() {
|
|
930
|
+
if (warnedBlockedStorage) return;
|
|
931
|
+
warnedBlockedStorage = true;
|
|
932
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
933
|
+
console.warn(
|
|
934
|
+
"Privana SIWE auth: localStorage is unavailable or blocked; sessions will stay in memory only."
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
function createSiweAuthStorageKey(apiUrl, chainId) {
|
|
939
|
+
const normalizedApiUrl = apiUrl.replace(/\/$/, "");
|
|
940
|
+
return ["privana", "siwe-auth", normalizedApiUrl, String(chainId)].join(":");
|
|
941
|
+
}
|
|
942
|
+
function buildPersistedSiweAuthRecordFromLogin(response, now, siweTokenExpiresAt) {
|
|
943
|
+
return {
|
|
944
|
+
version: PERSISTED_SIWE_AUTH_RECORD_VERSION,
|
|
945
|
+
tokens: {
|
|
946
|
+
siwe_token: response.siwe_token,
|
|
947
|
+
jwt_access_token: response.jwt_access_token,
|
|
948
|
+
jwt_refresh_token: response.jwt_refresh_token,
|
|
949
|
+
address: response.address
|
|
950
|
+
},
|
|
951
|
+
accessTokenExpiresAt: now + response.jwt_expires_in * 1e3,
|
|
952
|
+
refreshTokenExpiresAt: now + response.jwt_refresh_expires_in * 1e3,
|
|
953
|
+
siweTokenExpiresAt,
|
|
954
|
+
updatedAt: now
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
function applyRefreshToPersistedSiweAuthRecord(previous, response, now = Date.now()) {
|
|
958
|
+
return {
|
|
959
|
+
...previous,
|
|
960
|
+
tokens: {
|
|
961
|
+
...previous.tokens,
|
|
962
|
+
jwt_access_token: response.token,
|
|
963
|
+
jwt_refresh_token: response.refresh_token
|
|
964
|
+
},
|
|
965
|
+
accessTokenExpiresAt: now + response.expires_in * 1e3,
|
|
966
|
+
refreshTokenExpiresAt: now + response.refresh_expires_in * 1e3,
|
|
967
|
+
updatedAt: now
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
function isPersistedSiweAuthRecord(value) {
|
|
971
|
+
if (typeof value !== "object" || value === null) return false;
|
|
972
|
+
const record = value;
|
|
973
|
+
if (record.version !== PERSISTED_SIWE_AUTH_RECORD_VERSION) return false;
|
|
974
|
+
if (typeof record.accessTokenExpiresAt !== "number") return false;
|
|
975
|
+
if (typeof record.refreshTokenExpiresAt !== "number") return false;
|
|
976
|
+
if (typeof record.siweTokenExpiresAt !== "number") return false;
|
|
977
|
+
if (typeof record.updatedAt !== "number") return false;
|
|
978
|
+
const tokens = record.tokens;
|
|
979
|
+
if (typeof tokens !== "object" || tokens === null) return false;
|
|
980
|
+
const tokenFields = tokens;
|
|
981
|
+
return typeof tokenFields.siwe_token === "string" && typeof tokenFields.jwt_access_token === "string" && typeof tokenFields.jwt_refresh_token === "string" && typeof tokenFields.address === "string" && tokenFields.address.length > 0;
|
|
982
|
+
}
|
|
983
|
+
function isPersistedSiweAuthAccessActive(record, now = Date.now()) {
|
|
984
|
+
return record.accessTokenExpiresAt > now + AUTH_CLOCK_SKEW_MS;
|
|
985
|
+
}
|
|
986
|
+
function isPersistedSiweAuthRefreshActive(record, now = Date.now()) {
|
|
987
|
+
return record.refreshTokenExpiresAt > now + AUTH_CLOCK_SKEW_MS;
|
|
988
|
+
}
|
|
989
|
+
function parsePersistedSiweAuthRecord(raw, now = Date.now()) {
|
|
990
|
+
let parsed;
|
|
991
|
+
try {
|
|
992
|
+
parsed = JSON.parse(raw);
|
|
993
|
+
} catch {
|
|
994
|
+
return null;
|
|
995
|
+
}
|
|
996
|
+
if (!isPersistedSiweAuthRecord(parsed)) return null;
|
|
997
|
+
if (!isPersistedSiweAuthRefreshActive(parsed, now)) return null;
|
|
998
|
+
return parsed;
|
|
999
|
+
}
|
|
1000
|
+
function readPersistedSiweAuth(storage, key, now = Date.now()) {
|
|
1001
|
+
let raw;
|
|
1002
|
+
try {
|
|
1003
|
+
raw = storage.getItem(key);
|
|
1004
|
+
} catch {
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1007
|
+
if (raw == null) return null;
|
|
1008
|
+
const record = parsePersistedSiweAuthRecord(raw, now);
|
|
1009
|
+
if (!record) removePersistedSiweAuth(storage, key);
|
|
1010
|
+
return record;
|
|
1011
|
+
}
|
|
1012
|
+
function writePersistedSiweAuth(storage, key, record) {
|
|
1013
|
+
try {
|
|
1014
|
+
storage.setItem(key, JSON.stringify(record));
|
|
1015
|
+
} catch {
|
|
1016
|
+
warnBlockedStorageOnce();
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
function removePersistedSiweAuth(storage, key) {
|
|
1020
|
+
try {
|
|
1021
|
+
storage.removeItem(key);
|
|
1022
|
+
} catch {
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
var localStorageProbe;
|
|
1026
|
+
function getSiweAuthLocalStorage() {
|
|
1027
|
+
if (localStorageProbe !== void 0) return localStorageProbe;
|
|
1028
|
+
if (typeof window === "undefined") return null;
|
|
1029
|
+
try {
|
|
1030
|
+
const storage = window.localStorage;
|
|
1031
|
+
storage.getItem("__privana_siwe_probe__");
|
|
1032
|
+
localStorageProbe = storage;
|
|
1033
|
+
} catch {
|
|
1034
|
+
warnBlockedStorageOnce();
|
|
1035
|
+
localStorageProbe = null;
|
|
1036
|
+
}
|
|
1037
|
+
return localStorageProbe;
|
|
1038
|
+
}
|
|
1039
|
+
function isAdoptableRecord(record, currentAddress, currentUpdatedAt) {
|
|
1040
|
+
if (!record || !currentAddress) return false;
|
|
1041
|
+
if (record.tokens.address.toLowerCase() !== currentAddress.toLowerCase()) return false;
|
|
1042
|
+
return record.updatedAt > (currentUpdatedAt ?? -1);
|
|
1043
|
+
}
|
|
1044
|
+
function resolveHydrationAction(record, connectedAddress, now = Date.now()) {
|
|
1045
|
+
if (!connectedAddress) return { type: "dormant" };
|
|
1046
|
+
if (!record) return { type: "dormant" };
|
|
1047
|
+
if (record.tokens.address.toLowerCase() !== connectedAddress.toLowerCase()) {
|
|
1048
|
+
return { type: "remove" };
|
|
1049
|
+
}
|
|
1050
|
+
if (isPersistedSiweAuthAccessActive(record, now)) return { type: "restore", record };
|
|
1051
|
+
if (isPersistedSiweAuthRefreshActive(record, now)) return { type: "refresh", record };
|
|
1052
|
+
return { type: "remove" };
|
|
1053
|
+
}
|
|
1054
|
+
function resolveStorageEvent(newValue, currentAddress, currentUpdatedAt, now = Date.now()) {
|
|
1055
|
+
if (!currentAddress) return { type: "ignore" };
|
|
1056
|
+
if (newValue == null) return { type: "logout" };
|
|
1057
|
+
const record = parsePersistedSiweAuthRecord(newValue, now);
|
|
1058
|
+
if (!record) return { type: "ignore" };
|
|
1059
|
+
if (!isAdoptableRecord(record, currentAddress, currentUpdatedAt)) return { type: "ignore" };
|
|
1060
|
+
return { type: "adopt", record };
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/sdk/auth/siwe-auth-policy.ts
|
|
1064
|
+
function resolveActiveSessionAddress(...candidates) {
|
|
1065
|
+
return candidates.find((candidate) => !!candidate) ?? null;
|
|
1066
|
+
}
|
|
1067
|
+
function resolveAutoLoginEffectAction(input) {
|
|
1068
|
+
const {
|
|
1069
|
+
status,
|
|
1070
|
+
isConnected,
|
|
1071
|
+
address,
|
|
1072
|
+
sessionAddress,
|
|
1073
|
+
autoLogin,
|
|
1074
|
+
isLoading,
|
|
1075
|
+
isHydrating,
|
|
1076
|
+
autoAttemptedAddress,
|
|
1077
|
+
authenticatingAddress
|
|
1078
|
+
} = input;
|
|
1079
|
+
const activeAddress2 = resolveActiveSessionAddress(sessionAddress, authenticatingAddress);
|
|
1080
|
+
if (status === "connecting" || status === "reconnecting") return { type: "wait" };
|
|
1081
|
+
if (!isConnected && activeAddress2) return { type: "reset-on-disconnect" };
|
|
1082
|
+
if (isConnected && address && activeAddress2 && address.toLowerCase() !== activeAddress2.toLowerCase()) {
|
|
1083
|
+
return { type: "clear-on-mismatch" };
|
|
1084
|
+
}
|
|
1085
|
+
if (!autoLogin) return { type: "noop" };
|
|
1086
|
+
if (isHydrating) return { type: "wait" };
|
|
1087
|
+
if (isConnected && address && !sessionAddress && !isLoading && autoAttemptedAddress !== address) {
|
|
1088
|
+
return { type: "auto-login" };
|
|
1089
|
+
}
|
|
1090
|
+
return { type: "noop" };
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// src/sdk/auth/auth-lifecycle.ts
|
|
1094
|
+
function initialAuthLifecycleState() {
|
|
1095
|
+
return {
|
|
1096
|
+
generation: 0,
|
|
1097
|
+
refreshData: null,
|
|
1098
|
+
currentRecord: null,
|
|
1099
|
+
autoAttemptedAddress: null,
|
|
1100
|
+
authenticatingAddress: null,
|
|
1101
|
+
hydratedAddress: null
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
function reduceAuthLifecycle(state, event) {
|
|
1105
|
+
switch (event.type) {
|
|
1106
|
+
case "reset":
|
|
1107
|
+
return {
|
|
1108
|
+
generation: state.generation + 1,
|
|
1109
|
+
refreshData: null,
|
|
1110
|
+
currentRecord: null,
|
|
1111
|
+
autoAttemptedAddress: null,
|
|
1112
|
+
authenticatingAddress: null,
|
|
1113
|
+
hydratedAddress: null
|
|
1114
|
+
};
|
|
1115
|
+
case "commit":
|
|
1116
|
+
return {
|
|
1117
|
+
...state,
|
|
1118
|
+
generation: state.generation + 1,
|
|
1119
|
+
refreshData: {
|
|
1120
|
+
refreshToken: event.record.tokens.jwt_refresh_token,
|
|
1121
|
+
refreshExpiresAt: event.record.refreshTokenExpiresAt
|
|
1122
|
+
},
|
|
1123
|
+
currentRecord: event.record
|
|
1124
|
+
};
|
|
1125
|
+
case "refresh":
|
|
1126
|
+
return {
|
|
1127
|
+
...state,
|
|
1128
|
+
refreshData: event.refreshData,
|
|
1129
|
+
currentRecord: event.record
|
|
1130
|
+
};
|
|
1131
|
+
case "setAutoAttemptedAddress":
|
|
1132
|
+
return { ...state, autoAttemptedAddress: event.address };
|
|
1133
|
+
case "setAuthenticatingAddress":
|
|
1134
|
+
return { ...state, authenticatingAddress: event.address };
|
|
1135
|
+
case "setHydratedAddress":
|
|
1136
|
+
return { ...state, hydratedAddress: event.address };
|
|
1137
|
+
case "clearCurrentRecord":
|
|
1138
|
+
return { ...state, currentRecord: null };
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// src/sdk/auth/auth-lifecycle-effects.ts
|
|
1143
|
+
function armPrivateRead(ports, input) {
|
|
1144
|
+
ports.cache.set(input.scopeKey, input.siweToken, input.siweExpiry);
|
|
1145
|
+
}
|
|
1146
|
+
function applyAccessRotation(ports, rotation) {
|
|
1147
|
+
ports.client.setBearerToken(rotation.accessToken);
|
|
1148
|
+
ports.react.setTokens(
|
|
1149
|
+
(prev) => prev ? {
|
|
1150
|
+
...prev,
|
|
1151
|
+
jwt_access_token: rotation.accessToken,
|
|
1152
|
+
jwt_refresh_token: rotation.refreshToken
|
|
1153
|
+
} : prev
|
|
1154
|
+
);
|
|
1155
|
+
ports.react.setAccessTokenExpiresAt(rotation.accessTokenExpiresAt);
|
|
1156
|
+
}
|
|
1157
|
+
function publishSession(ports, record) {
|
|
1158
|
+
ports.client.setBearerToken(record.tokens.jwt_access_token);
|
|
1159
|
+
ports.react.setSession({ address: record.tokens.address });
|
|
1160
|
+
ports.react.setTokens({
|
|
1161
|
+
siwe_token: record.tokens.siwe_token,
|
|
1162
|
+
jwt_access_token: record.tokens.jwt_access_token,
|
|
1163
|
+
jwt_refresh_token: record.tokens.jwt_refresh_token,
|
|
1164
|
+
address: record.tokens.address
|
|
1165
|
+
});
|
|
1166
|
+
ports.react.setAccessTokenExpiresAt(record.accessTokenExpiresAt);
|
|
1167
|
+
}
|
|
1168
|
+
function persistRecordIfEnabled(ports, record) {
|
|
1169
|
+
if (!ports.persistJwt) return;
|
|
1170
|
+
if (record) ports.storage.write(record);
|
|
1171
|
+
else ports.storage.remove();
|
|
1172
|
+
}
|
|
1173
|
+
function clearSessionEffects(ports) {
|
|
1174
|
+
ports.client.clearPrivateReadToken();
|
|
1175
|
+
ports.client.clearBearerToken();
|
|
1176
|
+
ports.react.setSession(null);
|
|
1177
|
+
ports.react.setTokens(null);
|
|
1178
|
+
ports.react.setAccessTokenExpiresAt(null);
|
|
1179
|
+
ports.react.setIsLoading(false);
|
|
1180
|
+
ports.react.setIsHydrating(false);
|
|
1181
|
+
ports.react.setError(null);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// src/sdk/auth/siwe-auth-controller.ts
|
|
1185
|
+
function activeAddress(ctrl) {
|
|
1186
|
+
return resolveActiveSessionAddress(
|
|
1187
|
+
ctrl.getSessionAddress(),
|
|
1188
|
+
ctrl.getState().currentRecord?.tokens.address
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
function ctrlReset(ctrl, removeStorage) {
|
|
1192
|
+
ctrl.loginInFlight = false;
|
|
1193
|
+
ctrl.loginOwnerGeneration = -1;
|
|
1194
|
+
ctrl.hydrateOwnerGeneration = -1;
|
|
1195
|
+
ctrl.refreshPromise = null;
|
|
1196
|
+
ctrl.dispatch({ type: "reset" });
|
|
1197
|
+
clearSessionEffects(ctrl.ports);
|
|
1198
|
+
if (removeStorage) persistRecordIfEnabled(ctrl.ports, null);
|
|
1199
|
+
}
|
|
1200
|
+
function ctrlRestoreSession(ctrl, record) {
|
|
1201
|
+
ctrl.dispatch({ type: "commit", record });
|
|
1202
|
+
armPrivateRead(ctrl.ports, {
|
|
1203
|
+
siweToken: record.tokens.siwe_token,
|
|
1204
|
+
siweExpiry: record.siweTokenExpiresAt,
|
|
1205
|
+
scopeKey: ctrl.ports.makeScopeKey(record.tokens.address)
|
|
1206
|
+
});
|
|
1207
|
+
publishSession(ctrl.ports, record);
|
|
1208
|
+
}
|
|
1209
|
+
function ctrlAdoptNewerRecordIfAvailable(ctrl) {
|
|
1210
|
+
if (!ctrl.config.persistJwt) return false;
|
|
1211
|
+
const record = ctrl.ports.storage.read();
|
|
1212
|
+
if (!record) return false;
|
|
1213
|
+
const current = activeAddress(ctrl);
|
|
1214
|
+
if (!isAdoptableRecord(record, current, ctrl.getState().currentRecord?.updatedAt ?? null)) {
|
|
1215
|
+
return false;
|
|
1216
|
+
}
|
|
1217
|
+
ctrlRestoreSession(ctrl, record);
|
|
1218
|
+
return true;
|
|
1219
|
+
}
|
|
1220
|
+
async function ctrlLogin(ctrl) {
|
|
1221
|
+
const address = ctrl.config.address;
|
|
1222
|
+
if (!address) throw new Error("No wallet connected");
|
|
1223
|
+
if (ctrl.loginInFlight) return;
|
|
1224
|
+
ctrl.loginInFlight = true;
|
|
1225
|
+
ctrl.dispatch({ type: "setAutoAttemptedAddress", address });
|
|
1226
|
+
ctrl.dispatch({ type: "setAuthenticatingAddress", address });
|
|
1227
|
+
ctrl.ports.react.setIsLoading(true);
|
|
1228
|
+
ctrl.ports.react.setError(null);
|
|
1229
|
+
const generation = ctrl.getState().generation;
|
|
1230
|
+
ctrl.loginOwnerGeneration = generation;
|
|
1231
|
+
const { chainId, apiUrl } = ctrl.config;
|
|
1232
|
+
const api = ctrl.api;
|
|
1233
|
+
try {
|
|
1234
|
+
const { message, expirationTime } = await buildSiweLoginMessage(api, {
|
|
1235
|
+
address,
|
|
1236
|
+
chainId,
|
|
1237
|
+
apiUrl
|
|
1238
|
+
});
|
|
1239
|
+
const signature = await ctrl.signer.signSiweMessage({ account: address, message });
|
|
1240
|
+
const res = await api.loginWithSiwe({ siwe_message: message, signature });
|
|
1241
|
+
if (ctrl.getState().generation !== generation) return;
|
|
1242
|
+
const record = buildPersistedSiweAuthRecordFromLogin(res, Date.now(), expirationTime.getTime());
|
|
1243
|
+
ctrlRestoreSession(ctrl, record);
|
|
1244
|
+
persistRecordIfEnabled(ctrl.ports, record);
|
|
1245
|
+
} catch (err) {
|
|
1246
|
+
if (ctrl.loginOwnerGeneration === generation) {
|
|
1247
|
+
ctrl.ports.react.setError(err instanceof Error ? err : new Error("Sign-in failed"));
|
|
1248
|
+
}
|
|
1249
|
+
throw err;
|
|
1250
|
+
} finally {
|
|
1251
|
+
if (ctrl.loginOwnerGeneration === generation) {
|
|
1252
|
+
ctrl.ports.react.setIsLoading(false);
|
|
1253
|
+
ctrl.loginInFlight = false;
|
|
1254
|
+
ctrl.loginOwnerGeneration = -1;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
async function ctrlLogout(ctrl) {
|
|
1259
|
+
const refreshToken = ctrl.getState().refreshData?.refreshToken;
|
|
1260
|
+
try {
|
|
1261
|
+
if (refreshToken) {
|
|
1262
|
+
try {
|
|
1263
|
+
await ctrl.api.logoutJwtSession({ refresh_token: refreshToken, revoke_all: true });
|
|
1264
|
+
} catch {
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
} finally {
|
|
1268
|
+
ctrlReset(ctrl, ctrl.config.persistJwt);
|
|
1269
|
+
ctrl.dispatch({ type: "setAutoAttemptedAddress", address: ctrl.config.address ?? null });
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
async function ctrlRefreshAccessToken(ctrl) {
|
|
1273
|
+
const data = ctrl.getState().refreshData;
|
|
1274
|
+
if (!data) return;
|
|
1275
|
+
if (Date.now() >= data.refreshExpiresAt - AUTH_CLOCK_SKEW_MS) {
|
|
1276
|
+
ctrlReset(ctrl, ctrl.config.persistJwt);
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
if (ctrl.refreshPromise) return ctrl.refreshPromise;
|
|
1280
|
+
const generation = ctrl.getState().generation;
|
|
1281
|
+
const promise = (async () => {
|
|
1282
|
+
try {
|
|
1283
|
+
if (ctrlAdoptNewerRecordIfAvailable(ctrl)) return;
|
|
1284
|
+
const res = await ctrl.api.refreshJwtSession({ refresh_token: data.refreshToken });
|
|
1285
|
+
if (ctrl.getState().generation !== generation) return;
|
|
1286
|
+
const refreshedAt = Date.now();
|
|
1287
|
+
const refreshData = {
|
|
1288
|
+
refreshToken: res.refresh_token,
|
|
1289
|
+
refreshExpiresAt: refreshedAt + res.refresh_expires_in * 1e3
|
|
1290
|
+
};
|
|
1291
|
+
const previous = ctrl.getState().currentRecord;
|
|
1292
|
+
if (previous && ctrl.config.persistJwt) {
|
|
1293
|
+
const nextRecord = applyRefreshToPersistedSiweAuthRecord(previous, res, refreshedAt);
|
|
1294
|
+
ctrl.dispatch({ type: "refresh", refreshData, record: nextRecord });
|
|
1295
|
+
ctrl.ports.storage.write(nextRecord);
|
|
1296
|
+
} else {
|
|
1297
|
+
ctrl.dispatch({ type: "refresh", refreshData, record: previous });
|
|
1298
|
+
}
|
|
1299
|
+
applyAccessRotation(ctrl.ports, {
|
|
1300
|
+
accessToken: res.token,
|
|
1301
|
+
refreshToken: res.refresh_token,
|
|
1302
|
+
accessTokenExpiresAt: refreshedAt + res.expires_in * 1e3
|
|
1303
|
+
});
|
|
1304
|
+
} catch {
|
|
1305
|
+
if (ctrl.getState().generation !== generation) return;
|
|
1306
|
+
if (!ctrlAdoptNewerRecordIfAvailable(ctrl)) ctrlReset(ctrl, ctrl.config.persistJwt);
|
|
1307
|
+
}
|
|
1308
|
+
})();
|
|
1309
|
+
ctrl.refreshPromise = promise;
|
|
1310
|
+
try {
|
|
1311
|
+
await promise;
|
|
1312
|
+
} finally {
|
|
1313
|
+
if (ctrl.refreshPromise === promise) ctrl.refreshPromise = null;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
async function ctrlHydrateViaRefresh(ctrl, record, address) {
|
|
1317
|
+
ctrl.dispatch({ type: "setAutoAttemptedAddress", address });
|
|
1318
|
+
ctrl.dispatch({ type: "setAuthenticatingAddress", address });
|
|
1319
|
+
ctrl.ports.react.setIsHydrating(true);
|
|
1320
|
+
ctrl.dispatch({ type: "commit", record });
|
|
1321
|
+
const hydrationGen = ctrl.getState().generation;
|
|
1322
|
+
ctrl.hydrateOwnerGeneration = hydrationGen;
|
|
1323
|
+
try {
|
|
1324
|
+
await ctrlRefreshAccessToken(ctrl).catch(() => {
|
|
1325
|
+
});
|
|
1326
|
+
const fresh = ctrl.getState().currentRecord;
|
|
1327
|
+
if (fresh && fresh.tokens.address.toLowerCase() === address.toLowerCase() && isPersistedSiweAuthAccessActive(fresh)) {
|
|
1328
|
+
ctrlRestoreSession(ctrl, fresh);
|
|
1329
|
+
} else if (ctrl.getState().generation === hydrationGen) {
|
|
1330
|
+
ctrl.dispatch({ type: "setAutoAttemptedAddress", address: null });
|
|
1331
|
+
}
|
|
1332
|
+
} finally {
|
|
1333
|
+
if (ctrl.hydrateOwnerGeneration === hydrationGen) {
|
|
1334
|
+
ctrl.ports.react.setIsHydrating(false);
|
|
1335
|
+
ctrl.hydrateOwnerGeneration = -1;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
868
1340
|
// src/sdk/hooks/private-read-token-store.ts
|
|
869
|
-
var AUTH_CLOCK_SKEW_MS = 3e4;
|
|
870
1341
|
var cache = /* @__PURE__ */ new Map();
|
|
871
1342
|
function createScopeKey(apiUrl, chainId, address) {
|
|
872
1343
|
return `${apiUrl.replace(/\/$/, "")}:${chainId}:${address.toLowerCase()}`;
|
|
@@ -886,164 +1357,240 @@ function setCachedPrivateReadToken(scopeKey, token, expiresAt) {
|
|
|
886
1357
|
function deleteCachedPrivateReadToken(scopeKey) {
|
|
887
1358
|
cache.delete(scopeKey);
|
|
888
1359
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
children,
|
|
894
|
-
client,
|
|
895
|
-
networkConfig,
|
|
896
|
-
autoLogin = true
|
|
897
|
-
}) {
|
|
1360
|
+
|
|
1361
|
+
// src/sdk/hooks/use-siwe-auth-lifecycle.ts
|
|
1362
|
+
function useSiweAuthLifecycle(deps) {
|
|
1363
|
+
const { storageKey, apiUrl, chainId, persistJwt, autoLogin, client, api } = deps;
|
|
898
1364
|
const wagmiContext = useContext(WagmiContext);
|
|
899
1365
|
const { address, isConnected, status } = useSafeAccount();
|
|
900
1366
|
const [session, setSession] = useState(null);
|
|
901
1367
|
const [tokens, setTokens] = useState(null);
|
|
902
1368
|
const [isLoading, setIsLoading] = useState(false);
|
|
1369
|
+
const [isHydrating, setIsHydrating] = useState(false);
|
|
903
1370
|
const [error, setError] = useState(null);
|
|
904
1371
|
const [accessTokenExpiresAt, setAccessTokenExpiresAt] = useState(null);
|
|
905
|
-
const
|
|
906
|
-
const
|
|
907
|
-
const
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
const refreshToken = refreshDataRef.current?.refreshToken;
|
|
922
|
-
try {
|
|
923
|
-
if (refreshToken) {
|
|
924
|
-
await client.logoutJwtSession({ refresh_token: refreshToken });
|
|
1372
|
+
const sessionRef = useRef(null);
|
|
1373
|
+
const prevPersistJwtRef = useRef(void 0);
|
|
1374
|
+
const authRef = useRef(initialAuthLifecycleState());
|
|
1375
|
+
const storageAdapter = useMemo(
|
|
1376
|
+
() => ({
|
|
1377
|
+
read: () => {
|
|
1378
|
+
const ls = getSiweAuthLocalStorage();
|
|
1379
|
+
return ls ? readPersistedSiweAuth(ls, storageKey) : null;
|
|
1380
|
+
},
|
|
1381
|
+
write: (record) => {
|
|
1382
|
+
const ls = getSiweAuthLocalStorage();
|
|
1383
|
+
if (ls) writePersistedSiweAuth(ls, storageKey, record);
|
|
1384
|
+
},
|
|
1385
|
+
remove: () => {
|
|
1386
|
+
const ls = getSiweAuthLocalStorage();
|
|
1387
|
+
if (ls) removePersistedSiweAuth(ls, storageKey);
|
|
925
1388
|
}
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
const res = await client.refreshJwtSession({ refresh_token: data.refreshToken });
|
|
1002
|
-
const refreshedAt = Date.now();
|
|
1003
|
-
client.setBearerToken(res.token);
|
|
1004
|
-
refreshDataRef.current = {
|
|
1005
|
-
refreshToken: res.refresh_token,
|
|
1006
|
-
refreshExpiresAt: refreshedAt + res.refresh_expires_in * 1e3
|
|
1007
|
-
};
|
|
1008
|
-
setTokens(
|
|
1009
|
-
(prev) => prev ? { ...prev, jwt_access_token: res.token, jwt_refresh_token: res.refresh_token } : prev
|
|
1010
|
-
);
|
|
1011
|
-
setAccessTokenExpiresAt(refreshedAt + res.expires_in * 1e3);
|
|
1012
|
-
} catch {
|
|
1013
|
-
clearSession();
|
|
1014
|
-
} finally {
|
|
1015
|
-
refreshInFlight.current = false;
|
|
1016
|
-
}
|
|
1017
|
-
}, [client, clearSession]);
|
|
1389
|
+
}),
|
|
1390
|
+
[storageKey]
|
|
1391
|
+
);
|
|
1392
|
+
const scopeInputsRef = useRef({ apiUrl, chainId });
|
|
1393
|
+
scopeInputsRef.current.apiUrl = apiUrl;
|
|
1394
|
+
scopeInputsRef.current.chainId = chainId;
|
|
1395
|
+
const wagmiContextRef = useRef(wagmiContext);
|
|
1396
|
+
wagmiContextRef.current = wagmiContext;
|
|
1397
|
+
const portsRef = useRef(null);
|
|
1398
|
+
if (!portsRef.current) {
|
|
1399
|
+
portsRef.current = {
|
|
1400
|
+
persistJwt,
|
|
1401
|
+
client,
|
|
1402
|
+
storage: storageAdapter,
|
|
1403
|
+
cache: { set: setCachedPrivateReadToken },
|
|
1404
|
+
react: {
|
|
1405
|
+
setSession,
|
|
1406
|
+
setTokens,
|
|
1407
|
+
setAccessTokenExpiresAt,
|
|
1408
|
+
setIsLoading,
|
|
1409
|
+
setIsHydrating,
|
|
1410
|
+
setError
|
|
1411
|
+
},
|
|
1412
|
+
makeScopeKey: (addr) => createScopeKey(scopeInputsRef.current.apiUrl, scopeInputsRef.current.chainId, addr)
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
const ports = portsRef.current;
|
|
1416
|
+
ports.persistJwt = persistJwt;
|
|
1417
|
+
ports.client = client;
|
|
1418
|
+
ports.storage = storageAdapter;
|
|
1419
|
+
const ctrlRef = useRef(null);
|
|
1420
|
+
if (!ctrlRef.current) {
|
|
1421
|
+
ctrlRef.current = {
|
|
1422
|
+
config: { address, chainId, apiUrl, persistJwt },
|
|
1423
|
+
ports,
|
|
1424
|
+
api,
|
|
1425
|
+
signer: {
|
|
1426
|
+
signSiweMessage: async ({ account, message }) => {
|
|
1427
|
+
const ctx = wagmiContextRef.current;
|
|
1428
|
+
if (!ctx) throw new Error("WagmiProvider is required for SIWE auth");
|
|
1429
|
+
const walletClient = await getWalletClient(ctx);
|
|
1430
|
+
if (!walletClient) throw new Error("No wallet client available");
|
|
1431
|
+
return walletClient.signMessage({
|
|
1432
|
+
account: walletClient.account ?? account,
|
|
1433
|
+
message
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
},
|
|
1437
|
+
loginInFlight: false,
|
|
1438
|
+
loginOwnerGeneration: -1,
|
|
1439
|
+
hydrateOwnerGeneration: -1,
|
|
1440
|
+
refreshPromise: null,
|
|
1441
|
+
getState: () => authRef.current,
|
|
1442
|
+
getSessionAddress: () => sessionRef.current?.address ?? null,
|
|
1443
|
+
dispatch: (event) => {
|
|
1444
|
+
authRef.current = reduceAuthLifecycle(authRef.current, event);
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
const ctrl = ctrlRef.current;
|
|
1449
|
+
ctrl.config = { address, chainId, apiUrl, persistJwt };
|
|
1450
|
+
ctrl.api = api;
|
|
1451
|
+
const login = useCallback(() => ctrlLogin(ctrl), [ctrl]);
|
|
1452
|
+
const logout = useCallback(() => ctrlLogout(ctrl), [ctrl]);
|
|
1453
|
+
const refreshAccessToken = useCallback(() => ctrlRefreshAccessToken(ctrl), [ctrl]);
|
|
1454
|
+
const resetSession = useCallback(() => ctrlReset(ctrl, false), [ctrl]);
|
|
1455
|
+
const clearSession = useCallback(() => ctrlReset(ctrl, ctrl.config.persistJwt), [ctrl]);
|
|
1456
|
+
const restoreSession = useCallback(
|
|
1457
|
+
(record) => ctrlRestoreSession(ctrl, record),
|
|
1458
|
+
[ctrl]
|
|
1459
|
+
);
|
|
1460
|
+
const hydrateViaRefresh = useCallback(
|
|
1461
|
+
(record, addr) => ctrlHydrateViaRefresh(ctrl, record, addr),
|
|
1462
|
+
[ctrl]
|
|
1463
|
+
);
|
|
1018
1464
|
useEffect(() => {
|
|
1019
1465
|
if (accessTokenExpiresAt == null) return;
|
|
1020
|
-
const delay = Math.max(accessTokenExpiresAt -
|
|
1466
|
+
const delay = Math.max(accessTokenExpiresAt - AUTH_CLOCK_SKEW_MS - Date.now(), 0);
|
|
1021
1467
|
const timer = setTimeout(() => {
|
|
1022
1468
|
void refreshAccessToken();
|
|
1023
1469
|
}, delay);
|
|
1024
1470
|
return () => clearTimeout(timer);
|
|
1025
1471
|
}, [accessTokenExpiresAt, refreshAccessToken]);
|
|
1472
|
+
const scopeRef = useRef(void 0);
|
|
1026
1473
|
useEffect(() => {
|
|
1027
|
-
|
|
1474
|
+
const prev = scopeRef.current;
|
|
1475
|
+
scopeRef.current = { apiUrl, chainId, client };
|
|
1476
|
+
const scopeChanged = !!prev && !(prev.apiUrl === apiUrl && prev.chainId === chainId && prev.client === client);
|
|
1477
|
+
if (scopeChanged) {
|
|
1478
|
+
ctrlReset(ctrl, false);
|
|
1479
|
+
}
|
|
1480
|
+
if (!persistJwt) return;
|
|
1028
1481
|
if (status === "connecting" || status === "reconnecting") return;
|
|
1029
|
-
if (!isConnected
|
|
1030
|
-
|
|
1031
|
-
|
|
1482
|
+
if (!isConnected || !address) return;
|
|
1483
|
+
if (ctrl.getState().hydratedAddress === address) return;
|
|
1484
|
+
ctrl.dispatch({ type: "setHydratedAddress", address });
|
|
1485
|
+
const action = resolveHydrationAction(storageAdapter.read(), address);
|
|
1486
|
+
switch (action.type) {
|
|
1487
|
+
case "restore":
|
|
1488
|
+
restoreSession(action.record);
|
|
1489
|
+
ctrl.dispatch({ type: "setAutoAttemptedAddress", address });
|
|
1490
|
+
break;
|
|
1491
|
+
case "refresh":
|
|
1492
|
+
void hydrateViaRefresh(action.record, address);
|
|
1493
|
+
break;
|
|
1494
|
+
case "remove":
|
|
1495
|
+
storageAdapter.remove();
|
|
1496
|
+
break;
|
|
1032
1497
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1498
|
+
}, [
|
|
1499
|
+
apiUrl,
|
|
1500
|
+
chainId,
|
|
1501
|
+
client,
|
|
1502
|
+
persistJwt,
|
|
1503
|
+
status,
|
|
1504
|
+
isConnected,
|
|
1505
|
+
address,
|
|
1506
|
+
storageAdapter,
|
|
1507
|
+
ctrl,
|
|
1508
|
+
restoreSession,
|
|
1509
|
+
hydrateViaRefresh
|
|
1510
|
+
]);
|
|
1511
|
+
useEffect(() => {
|
|
1512
|
+
const action = resolveAutoLoginEffectAction({
|
|
1513
|
+
status,
|
|
1514
|
+
isConnected,
|
|
1515
|
+
address: address ?? null,
|
|
1516
|
+
sessionAddress: session?.address ?? null,
|
|
1517
|
+
autoLogin,
|
|
1518
|
+
isLoading,
|
|
1519
|
+
isHydrating,
|
|
1520
|
+
autoAttemptedAddress: ctrl.getState().autoAttemptedAddress,
|
|
1521
|
+
authenticatingAddress: ctrl.getState().authenticatingAddress
|
|
1522
|
+
});
|
|
1523
|
+
switch (action.type) {
|
|
1524
|
+
case "wait":
|
|
1525
|
+
case "noop":
|
|
1526
|
+
return;
|
|
1527
|
+
case "reset-on-disconnect":
|
|
1528
|
+
resetSession();
|
|
1529
|
+
return;
|
|
1530
|
+
case "clear-on-mismatch":
|
|
1531
|
+
clearSession();
|
|
1532
|
+
return;
|
|
1533
|
+
case "auto-login":
|
|
1534
|
+
void login().catch(() => {
|
|
1535
|
+
});
|
|
1536
|
+
return;
|
|
1036
1537
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1538
|
+
}, [
|
|
1539
|
+
autoLogin,
|
|
1540
|
+
status,
|
|
1541
|
+
isConnected,
|
|
1542
|
+
address,
|
|
1543
|
+
session,
|
|
1544
|
+
isLoading,
|
|
1545
|
+
isHydrating,
|
|
1546
|
+
ctrl,
|
|
1547
|
+
login,
|
|
1548
|
+
clearSession,
|
|
1549
|
+
resetSession
|
|
1550
|
+
]);
|
|
1551
|
+
useEffect(() => {
|
|
1552
|
+
if (!persistJwt || typeof window === "undefined") return;
|
|
1553
|
+
function onStorage(event) {
|
|
1554
|
+
if (event.key !== storageKey) return;
|
|
1555
|
+
const currentAddress = resolveActiveSessionAddress(
|
|
1556
|
+
sessionRef.current?.address,
|
|
1557
|
+
ctrl.getState().currentRecord?.tokens.address,
|
|
1558
|
+
ctrl.getState().authenticatingAddress
|
|
1559
|
+
);
|
|
1560
|
+
const action = resolveStorageEvent(
|
|
1561
|
+
event.newValue,
|
|
1562
|
+
currentAddress,
|
|
1563
|
+
ctrl.getState().currentRecord?.updatedAt ?? null
|
|
1564
|
+
);
|
|
1565
|
+
switch (action.type) {
|
|
1566
|
+
case "logout":
|
|
1567
|
+
resetSession();
|
|
1568
|
+
if (currentAddress)
|
|
1569
|
+
ctrl.dispatch({ type: "setAutoAttemptedAddress", address: currentAddress });
|
|
1570
|
+
break;
|
|
1571
|
+
case "adopt":
|
|
1572
|
+
restoreSession(action.record);
|
|
1573
|
+
break;
|
|
1574
|
+
}
|
|
1041
1575
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1576
|
+
window.addEventListener("storage", onStorage);
|
|
1577
|
+
return () => window.removeEventListener("storage", onStorage);
|
|
1578
|
+
}, [persistJwt, storageKey, ctrl, restoreSession, resetSession]);
|
|
1579
|
+
useEffect(() => {
|
|
1580
|
+
const prev = prevPersistJwtRef.current;
|
|
1581
|
+
prevPersistJwtRef.current = persistJwt;
|
|
1582
|
+
if (!(prev === true && !persistJwt)) return;
|
|
1583
|
+
ctrl.dispatch({ type: "clearCurrentRecord" });
|
|
1584
|
+
storageAdapter.remove();
|
|
1585
|
+
}, [persistJwt, storageAdapter, ctrl]);
|
|
1586
|
+
useEffect(() => {
|
|
1587
|
+
sessionRef.current = session;
|
|
1588
|
+
}, [session]);
|
|
1589
|
+
return useMemo(
|
|
1044
1590
|
() => ({
|
|
1045
1591
|
isAuthenticated: !!session,
|
|
1046
|
-
isLoading,
|
|
1592
|
+
isLoading: isLoading || isHydrating,
|
|
1593
|
+
isHydrating,
|
|
1047
1594
|
error,
|
|
1048
1595
|
session,
|
|
1049
1596
|
accessToken: tokens?.jwt_access_token,
|
|
@@ -1051,8 +1598,38 @@ function SiweAuthProvider({
|
|
|
1051
1598
|
login,
|
|
1052
1599
|
logout
|
|
1053
1600
|
}),
|
|
1054
|
-
[session, isLoading, error, tokens, login, logout]
|
|
1601
|
+
[session, isLoading, isHydrating, error, tokens, login, logout]
|
|
1055
1602
|
);
|
|
1603
|
+
}
|
|
1604
|
+
var SiweAuthContext = createContext(null);
|
|
1605
|
+
function SiweAuthProvider({
|
|
1606
|
+
children,
|
|
1607
|
+
client,
|
|
1608
|
+
networkConfig,
|
|
1609
|
+
autoLogin = true,
|
|
1610
|
+
persistJwt = false
|
|
1611
|
+
}) {
|
|
1612
|
+
const storageKey = useMemo(
|
|
1613
|
+
() => createSiweAuthStorageKey(networkConfig.apiUrl, networkConfig.chainId),
|
|
1614
|
+
[networkConfig.apiUrl, networkConfig.chainId]
|
|
1615
|
+
);
|
|
1616
|
+
const lifecycleClient = useMemo(
|
|
1617
|
+
() => ({
|
|
1618
|
+
setBearerToken: (t) => client.setBearerToken(t),
|
|
1619
|
+
clearBearerToken: () => client.clearBearerToken(),
|
|
1620
|
+
clearPrivateReadToken: () => client.clearPrivateReadToken()
|
|
1621
|
+
}),
|
|
1622
|
+
[client]
|
|
1623
|
+
);
|
|
1624
|
+
const value = useSiweAuthLifecycle({
|
|
1625
|
+
storageKey,
|
|
1626
|
+
apiUrl: networkConfig.apiUrl,
|
|
1627
|
+
chainId: networkConfig.chainId,
|
|
1628
|
+
persistJwt,
|
|
1629
|
+
autoLogin,
|
|
1630
|
+
client: lifecycleClient,
|
|
1631
|
+
api: client
|
|
1632
|
+
});
|
|
1056
1633
|
return /* @__PURE__ */ jsx(SiweAuthContext.Provider, { value, children });
|
|
1057
1634
|
}
|
|
1058
1635
|
function useSiweAuth() {
|
|
@@ -1364,6 +1941,7 @@ function PrivanaProvider({
|
|
|
1364
1941
|
client,
|
|
1365
1942
|
networkConfig,
|
|
1366
1943
|
autoLogin: typeof siweAuth === "object" ? siweAuth.autoLogin : void 0,
|
|
1944
|
+
persistJwt: typeof siweAuth === "object" ? siweAuth.persistJwt : void 0,
|
|
1367
1945
|
children
|
|
1368
1946
|
}
|
|
1369
1947
|
) : children });
|
|
@@ -1434,6 +2012,47 @@ function removeBrowserStorageItem(key) {
|
|
|
1434
2012
|
}
|
|
1435
2013
|
}
|
|
1436
2014
|
}
|
|
2015
|
+
function canUseSharedBrowserStorage() {
|
|
2016
|
+
const storage = storageCandidate("localStorage");
|
|
2017
|
+
if (!storage) return false;
|
|
2018
|
+
const probeKey = "privana:shared-storage-probe";
|
|
2019
|
+
try {
|
|
2020
|
+
storage.setItem(probeKey, "1");
|
|
2021
|
+
storage.removeItem(probeKey);
|
|
2022
|
+
return true;
|
|
2023
|
+
} catch {
|
|
2024
|
+
return false;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
function setSharedBrowserStorageItem(key, value) {
|
|
2028
|
+
const storage = storageCandidate("localStorage");
|
|
2029
|
+
if (!storage) return false;
|
|
2030
|
+
try {
|
|
2031
|
+
storage.setItem(key, value);
|
|
2032
|
+
} catch {
|
|
2033
|
+
return false;
|
|
2034
|
+
}
|
|
2035
|
+
try {
|
|
2036
|
+
storageCandidate("sessionStorage")?.removeItem(key);
|
|
2037
|
+
} catch {
|
|
2038
|
+
}
|
|
2039
|
+
return true;
|
|
2040
|
+
}
|
|
2041
|
+
function getSharedBrowserStorageItem(key) {
|
|
2042
|
+
try {
|
|
2043
|
+
return storageCandidate("localStorage")?.getItem(key) ?? null;
|
|
2044
|
+
} catch {
|
|
2045
|
+
return null;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
function removeSharedBrowserStorageItem(key) {
|
|
2049
|
+
for (const storage of storageCandidates()) {
|
|
2050
|
+
try {
|
|
2051
|
+
storage.removeItem(key);
|
|
2052
|
+
} catch {
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
1437
2056
|
|
|
1438
2057
|
// src/sdk/hooks/pending-lock.ts
|
|
1439
2058
|
var DEFAULT_LOCK_DURATION_SECONDS = 259200;
|
|
@@ -1449,6 +2068,25 @@ function applyLockBuffer(amount, buffer = DEFAULT_ONRAMP_LOCK_BUFFER) {
|
|
|
1449
2068
|
function clampLockAmount(amount, maxAmount) {
|
|
1450
2069
|
return maxAmount !== void 0 && maxAmount < amount ? maxAmount : amount;
|
|
1451
2070
|
}
|
|
2071
|
+
function requireServiceAddress(serviceAddress) {
|
|
2072
|
+
if (!serviceAddress) {
|
|
2073
|
+
throw new Error("Service address not configured");
|
|
2074
|
+
}
|
|
2075
|
+
return serviceAddress;
|
|
2076
|
+
}
|
|
2077
|
+
function requireDepositLockOwner(walletAddress, beneficiary) {
|
|
2078
|
+
if (!walletAddress) throw new Error("No wallet connected");
|
|
2079
|
+
if (!beneficiary) throw new Error("No authenticated deposit account");
|
|
2080
|
+
if (walletAddress.toLowerCase() !== beneficiary.toLowerCase()) {
|
|
2081
|
+
throw new Error("Connected wallet does not match the authenticated deposit account");
|
|
2082
|
+
}
|
|
2083
|
+
return beneficiary;
|
|
2084
|
+
}
|
|
2085
|
+
function walletClientAccountAddress(walletClient) {
|
|
2086
|
+
const account = walletClient.account;
|
|
2087
|
+
if (!account) return void 0;
|
|
2088
|
+
return typeof account === "string" ? account : account.address;
|
|
2089
|
+
}
|
|
1452
2090
|
async function createSignedLockRequest({
|
|
1453
2091
|
client,
|
|
1454
2092
|
walletClient,
|
|
@@ -1462,6 +2100,10 @@ async function createSignedLockRequest({
|
|
|
1462
2100
|
if (amount <= 0n) {
|
|
1463
2101
|
throw new Error("Lock amount must be positive");
|
|
1464
2102
|
}
|
|
2103
|
+
const signerAddress = walletClientAccountAddress(walletClient);
|
|
2104
|
+
if (!signerAddress || signerAddress.toLowerCase() !== userAddress.toLowerCase()) {
|
|
2105
|
+
throw new Error("Connected wallet does not match the authenticated deposit account");
|
|
2106
|
+
}
|
|
1465
2107
|
const expiry = BigInt(Math.floor(Date.now() / 1e3) + lockDuration);
|
|
1466
2108
|
const { nonce } = await client.getLockNonce(userAddress);
|
|
1467
2109
|
const signature = await signLockMessage({
|
|
@@ -1498,13 +2140,15 @@ var PostDepositLockError = class _PostDepositLockError extends Error {
|
|
|
1498
2140
|
this.signedAmount = signedAmount;
|
|
1499
2141
|
this.creditedAmount = creditedAmount;
|
|
1500
2142
|
this.name = "PostDepositLockError";
|
|
2143
|
+
this.submissionMayHaveSucceeded = options?.submissionMayHaveSucceeded ?? false;
|
|
1501
2144
|
Object.setPrototypeOf(this, _PostDepositLockError.prototype);
|
|
1502
2145
|
}
|
|
1503
2146
|
};
|
|
1504
2147
|
async function submitPendingLock({
|
|
1505
2148
|
client,
|
|
1506
2149
|
payload,
|
|
1507
|
-
creditedAmount
|
|
2150
|
+
creditedAmount,
|
|
2151
|
+
beforeSubmit
|
|
1508
2152
|
}) {
|
|
1509
2153
|
let signedAmount;
|
|
1510
2154
|
try {
|
|
@@ -1528,13 +2172,14 @@ async function submitPendingLock({
|
|
|
1528
2172
|
}
|
|
1529
2173
|
if (creditedAmount !== void 0 && creditedAmount < signedAmount) {
|
|
1530
2174
|
throw new PostDepositLockError(
|
|
1531
|
-
|
|
2175
|
+
"Credited amount is below the signed lock amount",
|
|
1532
2176
|
"credited-below-signed",
|
|
1533
2177
|
signedAmount,
|
|
1534
2178
|
creditedAmount
|
|
1535
2179
|
);
|
|
1536
2180
|
}
|
|
1537
2181
|
try {
|
|
2182
|
+
beforeSubmit?.();
|
|
1538
2183
|
return await client.lockFunds(payload);
|
|
1539
2184
|
} catch (err) {
|
|
1540
2185
|
throw new PostDepositLockError(
|
|
@@ -1579,7 +2224,6 @@ function clearPendingLock(userAddress, correlationId) {
|
|
|
1579
2224
|
}
|
|
1580
2225
|
var INITIAL_AUTH_BACKOFF_MS = 5e3;
|
|
1581
2226
|
var MAX_AUTH_BACKOFF_MS = 6e4;
|
|
1582
|
-
var DEFAULT_SIWE_AUTH_VALIDITY_MS = 24 * 60 * 60 * 1e3;
|
|
1583
2227
|
var privateReadFailureCache = /* @__PURE__ */ new Map();
|
|
1584
2228
|
var privateReadInflight = /* @__PURE__ */ new Map();
|
|
1585
2229
|
async function executeHostedAuthPrivateReadRequest({
|
|
@@ -1604,13 +2248,13 @@ async function executeHostedAuthPrivateReadRequest({
|
|
|
1604
2248
|
};
|
|
1605
2249
|
await ensureHostedAuth(false);
|
|
1606
2250
|
try {
|
|
1607
|
-
return await request();
|
|
2251
|
+
return await request(client);
|
|
1608
2252
|
} catch (error) {
|
|
1609
2253
|
if (!(error instanceof AccountingApiError) || error.statusCode !== 401) {
|
|
1610
2254
|
throw error;
|
|
1611
2255
|
}
|
|
1612
2256
|
await ensureHostedAuth(true);
|
|
1613
|
-
return request();
|
|
2257
|
+
return request(client);
|
|
1614
2258
|
}
|
|
1615
2259
|
}
|
|
1616
2260
|
function clearPrivateReadScope(scopeKey, client) {
|
|
@@ -1618,6 +2262,24 @@ function clearPrivateReadScope(scopeKey, client) {
|
|
|
1618
2262
|
privateReadFailureCache.delete(scopeKey);
|
|
1619
2263
|
client.clearPrivateReadToken();
|
|
1620
2264
|
}
|
|
2265
|
+
async function executeSiwePrivateReadRequest({
|
|
2266
|
+
client,
|
|
2267
|
+
scopeKey,
|
|
2268
|
+
getToken,
|
|
2269
|
+
request
|
|
2270
|
+
}) {
|
|
2271
|
+
const run = (token2) => request(client.withPrivateReadToken(token2));
|
|
2272
|
+
const token = await getToken(false);
|
|
2273
|
+
try {
|
|
2274
|
+
return await run(token);
|
|
2275
|
+
} catch (error) {
|
|
2276
|
+
if (!(error instanceof AccountingApiError) || error.statusCode !== 401) {
|
|
2277
|
+
throw error;
|
|
2278
|
+
}
|
|
2279
|
+
clearPrivateReadScope(scopeKey, client);
|
|
2280
|
+
return run(await getToken(true));
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
1621
2283
|
function recordPrivateReadFailure(scopeKey) {
|
|
1622
2284
|
const previous = privateReadFailureCache.get(scopeKey);
|
|
1623
2285
|
const backoffMs = Math.min(
|
|
@@ -1664,46 +2326,26 @@ function usePrivateReadRequest() {
|
|
|
1664
2326
|
if (!walletAddress) {
|
|
1665
2327
|
throw new Error("No wallet connected");
|
|
1666
2328
|
}
|
|
1667
|
-
const walletClient = await getWalletClient(wagmiContext);
|
|
1668
|
-
if (!walletClient) {
|
|
1669
|
-
throw new Error("No wallet client available");
|
|
1670
|
-
}
|
|
1671
2329
|
const apiUrl = networkConfig.apiUrl;
|
|
1672
2330
|
const scopeKey = createScopeKey(apiUrl, networkConfig.chainId, walletAddress);
|
|
1673
2331
|
const getToken = async (forceRefresh) => {
|
|
1674
2332
|
const inflight = privateReadInflight.get(scopeKey);
|
|
1675
|
-
if (inflight)
|
|
1676
|
-
const token = await inflight;
|
|
1677
|
-
client.setPrivateReadToken(token);
|
|
1678
|
-
return token;
|
|
1679
|
-
}
|
|
2333
|
+
if (inflight) return inflight;
|
|
1680
2334
|
if (!forceRefresh) {
|
|
1681
2335
|
const cached = getCachedPrivateReadToken(scopeKey);
|
|
1682
|
-
if (cached)
|
|
1683
|
-
client.setPrivateReadToken(cached);
|
|
1684
|
-
return cached;
|
|
1685
|
-
}
|
|
2336
|
+
if (cached) return cached;
|
|
1686
2337
|
}
|
|
1687
2338
|
ensureFailureBackoff(scopeKey);
|
|
1688
2339
|
const authPromise = (async () => {
|
|
1689
2340
|
try {
|
|
1690
|
-
const
|
|
1691
|
-
|
|
1692
|
-
client
|
|
1693
|
-
|
|
1694
|
-
const
|
|
1695
|
-
const expirationTime = new Date(issuedAt.getTime() + DEFAULT_SIWE_AUTH_VALIDITY_MS);
|
|
1696
|
-
const uri = typeof window !== "undefined" && window.location.origin ? window.location.origin : apiUrl;
|
|
1697
|
-
const message = createSiweMessage({
|
|
2341
|
+
const walletClient = await getWalletClient(wagmiContext);
|
|
2342
|
+
if (!walletClient) {
|
|
2343
|
+
throw new Error("No wallet client available");
|
|
2344
|
+
}
|
|
2345
|
+
const { message, expirationTime } = await buildSiweLoginMessage(client, {
|
|
1698
2346
|
address: walletAddress,
|
|
1699
2347
|
chainId: networkConfig.chainId,
|
|
1700
|
-
|
|
1701
|
-
expirationTime,
|
|
1702
|
-
issuedAt,
|
|
1703
|
-
nonce: nonceResponse.nonce,
|
|
1704
|
-
statement: buildSiweStatement(networkConfig.chainId),
|
|
1705
|
-
uri,
|
|
1706
|
-
version: "1"
|
|
2348
|
+
apiUrl
|
|
1707
2349
|
});
|
|
1708
2350
|
const signature = await walletClient.signMessage({
|
|
1709
2351
|
account: walletClient.account ?? walletAddress,
|
|
@@ -1715,7 +2357,6 @@ function usePrivateReadRequest() {
|
|
|
1715
2357
|
});
|
|
1716
2358
|
setCachedPrivateReadToken(scopeKey, login.siwe_token, expirationTime.getTime());
|
|
1717
2359
|
privateReadFailureCache.delete(scopeKey);
|
|
1718
|
-
client.setPrivateReadToken(login.siwe_token);
|
|
1719
2360
|
return login.siwe_token;
|
|
1720
2361
|
} catch (error) {
|
|
1721
2362
|
const authError = error instanceof Error ? error : new Error("Failed to authenticate private reads");
|
|
@@ -1729,17 +2370,7 @@ function usePrivateReadRequest() {
|
|
|
1729
2370
|
privateReadInflight.set(scopeKey, authPromise);
|
|
1730
2371
|
return authPromise;
|
|
1731
2372
|
};
|
|
1732
|
-
|
|
1733
|
-
try {
|
|
1734
|
-
return await request();
|
|
1735
|
-
} catch (error) {
|
|
1736
|
-
if (!(error instanceof AccountingApiError) || error.statusCode !== 401) {
|
|
1737
|
-
throw error;
|
|
1738
|
-
}
|
|
1739
|
-
clearPrivateReadScope(scopeKey, client);
|
|
1740
|
-
await getToken(true);
|
|
1741
|
-
return request();
|
|
1742
|
-
}
|
|
2373
|
+
return executeSiwePrivateReadRequest({ client, scopeKey, getToken, request });
|
|
1743
2374
|
},
|
|
1744
2375
|
[
|
|
1745
2376
|
client,
|
|
@@ -1766,7 +2397,6 @@ function usePrivateReadRequest() {
|
|
|
1766
2397
|
|
|
1767
2398
|
// src/sdk/hooks/use-deposit-verification.ts
|
|
1768
2399
|
function useDepositVerification(options = {}) {
|
|
1769
|
-
const { client } = usePrivanaContext();
|
|
1770
2400
|
const queryClient = useQueryClient();
|
|
1771
2401
|
const { executePrivateRead } = usePrivateReadRequest();
|
|
1772
2402
|
const pollInterval = options.pollInterval ?? 5e3;
|
|
@@ -1775,6 +2405,7 @@ function useDepositVerification(options = {}) {
|
|
|
1775
2405
|
const [isVerifying, setIsVerifying] = useState(false);
|
|
1776
2406
|
const [didTimeout, setDidTimeout] = useState(false);
|
|
1777
2407
|
const [verificationFailed, setVerificationFailed] = useState(false);
|
|
2408
|
+
const [canCheckAnotherTransfer, setCanCheckAnotherTransfer] = useState(false);
|
|
1778
2409
|
const [error, setError] = useState(null);
|
|
1779
2410
|
const [txHash, setTxHash] = useState();
|
|
1780
2411
|
const generationRef = useRef(0);
|
|
@@ -1810,14 +2441,16 @@ function useDepositVerification(options = {}) {
|
|
|
1810
2441
|
const isStale = () => generation !== generationRef.current;
|
|
1811
2442
|
const { hash, chainId, amount, logIndex } = ctx;
|
|
1812
2443
|
setVerificationFailed(false);
|
|
2444
|
+
setCanCheckAnotherTransfer(false);
|
|
1813
2445
|
setError(null);
|
|
1814
2446
|
setDidTimeout(false);
|
|
1815
2447
|
setIsVerifying(true);
|
|
1816
2448
|
const pollStartTime = Date.now();
|
|
1817
|
-
const markVerificationFailed = (err) => {
|
|
2449
|
+
const markVerificationFailed = (err, canCheckAnother = false) => {
|
|
1818
2450
|
setIsVerifying(false);
|
|
1819
2451
|
setError(err);
|
|
1820
2452
|
setVerificationFailed(true);
|
|
2453
|
+
setCanCheckAnotherTransfer(canCheckAnother);
|
|
1821
2454
|
onErrorRef.current?.(err);
|
|
1822
2455
|
};
|
|
1823
2456
|
const markVerificationTimedOut = () => {
|
|
@@ -1833,7 +2466,7 @@ function useDepositVerification(options = {}) {
|
|
|
1833
2466
|
if (isStale()) return;
|
|
1834
2467
|
try {
|
|
1835
2468
|
const result = await executePrivateRead(
|
|
1836
|
-
() =>
|
|
2469
|
+
(readClient) => readClient.checkDeposit({
|
|
1837
2470
|
chain_id: chainId,
|
|
1838
2471
|
tx_hash: hash,
|
|
1839
2472
|
amount: amount.toString(),
|
|
@@ -1868,11 +2501,12 @@ function useDepositVerification(options = {}) {
|
|
|
1868
2501
|
}
|
|
1869
2502
|
if (isStale()) return;
|
|
1870
2503
|
if (triggerResult.status === "credited") {
|
|
2504
|
+
const creditedAmount = creditedAmountFromResponse(triggerResult, amount);
|
|
1871
2505
|
setIsVerifying(false);
|
|
1872
2506
|
verificationContextRef.current = null;
|
|
1873
2507
|
queryClient.invalidateQueries({ queryKey: ["accounting-balance"] });
|
|
1874
2508
|
queryClient.invalidateQueries({ queryKey: ["accounting-history"] });
|
|
1875
|
-
onCreditedRef.current?.(hash, triggerResult);
|
|
2509
|
+
onCreditedRef.current?.(hash, triggerResult, creditedAmount);
|
|
1876
2510
|
return;
|
|
1877
2511
|
}
|
|
1878
2512
|
if (triggerResult.status === "error") {
|
|
@@ -1892,16 +2526,29 @@ function useDepositVerification(options = {}) {
|
|
|
1892
2526
|
return true;
|
|
1893
2527
|
}
|
|
1894
2528
|
try {
|
|
1895
|
-
const result = await executePrivateRead(
|
|
2529
|
+
const result = await executePrivateRead(
|
|
2530
|
+
(readClient) => readClient.getDepositStatus(depositId)
|
|
2531
|
+
);
|
|
1896
2532
|
if (isStale()) return true;
|
|
1897
2533
|
consecutiveFailures = 0;
|
|
1898
2534
|
if (result.status === "credited") {
|
|
2535
|
+
let creditedAmount;
|
|
2536
|
+
try {
|
|
2537
|
+
creditedAmount = creditedAmountFromResponse(result, amount);
|
|
2538
|
+
} catch (err) {
|
|
2539
|
+
stopPolling();
|
|
2540
|
+
markVerificationFailed(
|
|
2541
|
+
err instanceof Error ? err : new Error("Deposit credited amount is invalid"),
|
|
2542
|
+
false
|
|
2543
|
+
);
|
|
2544
|
+
return true;
|
|
2545
|
+
}
|
|
1899
2546
|
stopPolling();
|
|
1900
2547
|
setIsVerifying(false);
|
|
1901
2548
|
verificationContextRef.current = null;
|
|
1902
2549
|
queryClient.invalidateQueries({ queryKey: ["accounting-balance"] });
|
|
1903
2550
|
queryClient.invalidateQueries({ queryKey: ["accounting-history"] });
|
|
1904
|
-
onCreditedRef.current?.(hash, result);
|
|
2551
|
+
onCreditedRef.current?.(hash, result, creditedAmount);
|
|
1905
2552
|
return true;
|
|
1906
2553
|
}
|
|
1907
2554
|
if (result.status === "error") {
|
|
@@ -1933,20 +2580,11 @@ function useDepositVerification(options = {}) {
|
|
|
1933
2580
|
} catch (err) {
|
|
1934
2581
|
if (isStale()) return;
|
|
1935
2582
|
stopPolling();
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
);
|
|
2583
|
+
const error2 = err instanceof Error ? err : new Error("Deposit verification failed");
|
|
2584
|
+
markVerificationFailed(error2, isDefinitiveCandidateFailure(error2));
|
|
1939
2585
|
}
|
|
1940
2586
|
},
|
|
1941
|
-
[
|
|
1942
|
-
client,
|
|
1943
|
-
executePrivateRead,
|
|
1944
|
-
finalityRetryInterval,
|
|
1945
|
-
pollInterval,
|
|
1946
|
-
pollTimeout,
|
|
1947
|
-
queryClient,
|
|
1948
|
-
stopPolling
|
|
1949
|
-
]
|
|
2587
|
+
[executePrivateRead, finalityRetryInterval, pollInterval, pollTimeout, queryClient, stopPolling]
|
|
1950
2588
|
);
|
|
1951
2589
|
const verify = useCallback(
|
|
1952
2590
|
async (ctx) => {
|
|
@@ -1977,12 +2615,14 @@ function useDepositVerification(options = {}) {
|
|
|
1977
2615
|
setIsVerifying(false);
|
|
1978
2616
|
setDidTimeout(false);
|
|
1979
2617
|
setVerificationFailed(false);
|
|
2618
|
+
setCanCheckAnotherTransfer(false);
|
|
1980
2619
|
setError(null);
|
|
1981
2620
|
}, [stopPolling]);
|
|
1982
2621
|
return {
|
|
1983
2622
|
isVerifying,
|
|
1984
2623
|
didTimeout,
|
|
1985
2624
|
verificationFailed,
|
|
2625
|
+
canCheckAnotherTransfer,
|
|
1986
2626
|
error,
|
|
1987
2627
|
txHash,
|
|
1988
2628
|
verify,
|
|
@@ -1990,6 +2630,19 @@ function useDepositVerification(options = {}) {
|
|
|
1990
2630
|
reset
|
|
1991
2631
|
};
|
|
1992
2632
|
}
|
|
2633
|
+
function creditedAmountFromResponse(response, requestedAmount) {
|
|
2634
|
+
if (response.amount == null) return requestedAmount;
|
|
2635
|
+
try {
|
|
2636
|
+
const amount = BigInt(response.amount);
|
|
2637
|
+
if (amount <= 0n) throw new Error("non-positive amount");
|
|
2638
|
+
return amount;
|
|
2639
|
+
} catch (err) {
|
|
2640
|
+
throw new Error("Deposit API returned an invalid credited amount", { cause: err });
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
function isDefinitiveCandidateFailure(error) {
|
|
2644
|
+
return error instanceof AccountingApiError && error.statusCode === 400;
|
|
2645
|
+
}
|
|
1993
2646
|
function sleep(ms) {
|
|
1994
2647
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1995
2648
|
}
|
|
@@ -2307,6 +2960,12 @@ async function getConnectorClient(config, parameters = {}) {
|
|
|
2307
2960
|
transport: (opts) => custom(provider)({ ...opts, retryCount: 0 })
|
|
2308
2961
|
});
|
|
2309
2962
|
}
|
|
2963
|
+
function getBlockNumber(config, parameters = {}) {
|
|
2964
|
+
const { chainId, ...rest } = parameters;
|
|
2965
|
+
const client = config.getClient({ chainId });
|
|
2966
|
+
const action = getAction(client, getBlockNumber$1, "getBlockNumber");
|
|
2967
|
+
return action(rest);
|
|
2968
|
+
}
|
|
2310
2969
|
|
|
2311
2970
|
// ../../node_modules/@wagmi/core/dist/esm/actions/getChainId.js
|
|
2312
2971
|
function getChainId2(config) {
|
|
@@ -2425,7 +3084,9 @@ function useFiatOnRamp(options) {
|
|
|
2425
3084
|
const { address } = useAccount();
|
|
2426
3085
|
const { data: walletClient } = useWalletClient();
|
|
2427
3086
|
const { client, enabledTokens, networkConfig, serviceAddress } = usePrivanaContext();
|
|
2428
|
-
const { executePrivateRead, privateReadReady } = usePrivateReadRequest();
|
|
3087
|
+
const { executePrivateRead, privateReadAddress, privateReadReady } = usePrivateReadRequest();
|
|
3088
|
+
const privateReadAddressRef = useRef(privateReadAddress);
|
|
3089
|
+
privateReadAddressRef.current = privateReadAddress;
|
|
2429
3090
|
const { ensureCorrectChain } = useEnsureCorrectChain();
|
|
2430
3091
|
const wagmiConfig = useConfig();
|
|
2431
3092
|
const queryClient = useQueryClient();
|
|
@@ -2447,7 +3108,6 @@ function useFiatOnRamp(options) {
|
|
|
2447
3108
|
const activeIntentIdRef = useRef(null);
|
|
2448
3109
|
const activeVerificationRecordRef = useRef(null);
|
|
2449
3110
|
const activeVerificationKeyRef = useRef(null);
|
|
2450
|
-
const activeVerificationAmountRef = useRef(null);
|
|
2451
3111
|
const lockOwnerRef = useRef(null);
|
|
2452
3112
|
const triggeredVerificationKeysRef = useRef(/* @__PURE__ */ new Set());
|
|
2453
3113
|
const activeVerificationDoneRef = useRef(null);
|
|
@@ -2496,7 +3156,7 @@ function useFiatOnRamp(options) {
|
|
|
2496
3156
|
void (async () => {
|
|
2497
3157
|
try {
|
|
2498
3158
|
emitDebug("deposit-address:request");
|
|
2499
|
-
const resp = await executePrivateRead(() =>
|
|
3159
|
+
const resp = await executePrivateRead((readClient) => readClient.getDepositAddress());
|
|
2500
3160
|
if (cancelled) return;
|
|
2501
3161
|
setDepositAddress(resp.deposit_address);
|
|
2502
3162
|
const token = enabledTokens.find((t) => t.id.toLowerCase() === tokenId.toLowerCase());
|
|
@@ -2526,7 +3186,9 @@ function useFiatOnRamp(options) {
|
|
|
2526
3186
|
}
|
|
2527
3187
|
try {
|
|
2528
3188
|
emitDebug("pending:request");
|
|
2529
|
-
const { pending: rows } = await executePrivateRead(
|
|
3189
|
+
const { pending: rows } = await executePrivateRead(
|
|
3190
|
+
(readClient) => readClient.getPendingOnRamps()
|
|
3191
|
+
);
|
|
2530
3192
|
setPending(rows);
|
|
2531
3193
|
emitDebug("pending:success", {
|
|
2532
3194
|
count: rows.length,
|
|
@@ -2536,7 +3198,7 @@ function useFiatOnRamp(options) {
|
|
|
2536
3198
|
emitDebug("pending:error", errorPayload(err));
|
|
2537
3199
|
console.warn("Failed to load pending on-ramps:", err);
|
|
2538
3200
|
}
|
|
2539
|
-
}, [
|
|
3201
|
+
}, [emitDebug, executePrivateRead, privateReadReady]);
|
|
2540
3202
|
useEffect(() => {
|
|
2541
3203
|
refreshPending();
|
|
2542
3204
|
}, [refreshPending]);
|
|
@@ -2546,13 +3208,12 @@ function useFiatOnRamp(options) {
|
|
|
2546
3208
|
if (key) triggeredVerificationKeysRef.current.delete(key);
|
|
2547
3209
|
activeVerificationKeyRef.current = null;
|
|
2548
3210
|
activeVerificationRecordRef.current = null;
|
|
2549
|
-
activeVerificationAmountRef.current = null;
|
|
2550
3211
|
setActiveVerificationId(null);
|
|
2551
3212
|
activeVerificationDoneRef.current?.();
|
|
2552
3213
|
activeVerificationDoneRef.current = null;
|
|
2553
3214
|
}, []);
|
|
2554
3215
|
const submitPendingLockAfterCredit = useCallback(
|
|
2555
|
-
async (transactionId, userAddress) => {
|
|
3216
|
+
async (transactionId, userAddress, creditedAmount) => {
|
|
2556
3217
|
const signedLock = loadPendingLock(userAddress, transactionId);
|
|
2557
3218
|
if (!signedLock) {
|
|
2558
3219
|
clearPendingLock(userAddress, transactionId);
|
|
@@ -2565,7 +3226,6 @@ function useFiatOnRamp(options) {
|
|
|
2565
3226
|
(onLockFailedRef.current ?? onErrorRef.current)?.(error2);
|
|
2566
3227
|
return;
|
|
2567
3228
|
}
|
|
2568
|
-
const creditedAmount = activeVerificationAmountRef.current ?? void 0;
|
|
2569
3229
|
try {
|
|
2570
3230
|
const result = await submitPendingLock({ client, payload: signedLock, creditedAmount });
|
|
2571
3231
|
queryClient.invalidateQueries({ queryKey: ["accounting-balance"] });
|
|
@@ -2611,7 +3271,7 @@ function useFiatOnRamp(options) {
|
|
|
2611
3271
|
});
|
|
2612
3272
|
setFinalityProgress((prev) => ({ ...prev, [record.transaction_id]: message }));
|
|
2613
3273
|
},
|
|
2614
|
-
onCredited: (depositTxHash) => {
|
|
3274
|
+
onCredited: (depositTxHash, _response, creditedAmount) => {
|
|
2615
3275
|
const record = activeVerificationRecordRef.current;
|
|
2616
3276
|
const verificationKey = record ? getOnRampVerificationKey(record) : null;
|
|
2617
3277
|
emitDebug("verification:credited", {
|
|
@@ -2628,9 +3288,9 @@ function useFiatOnRamp(options) {
|
|
|
2628
3288
|
delete next[record.transaction_id];
|
|
2629
3289
|
return next;
|
|
2630
3290
|
});
|
|
2631
|
-
const lockOwner = lockOwnerRef.current ??
|
|
3291
|
+
const lockOwner = lockOwnerRef.current ?? privateReadAddress;
|
|
2632
3292
|
if (lockOwner) {
|
|
2633
|
-
void submitPendingLockAfterCredit(record.transaction_id, lockOwner);
|
|
3293
|
+
void submitPendingLockAfterCredit(record.transaction_id, lockOwner, creditedAmount);
|
|
2634
3294
|
} else if (postDepositLock) {
|
|
2635
3295
|
emitDebug("lock:owner-unavailable", { transactionId: record.transaction_id });
|
|
2636
3296
|
(onLockFailedRef.current ?? onErrorRef.current)?.(
|
|
@@ -2649,7 +3309,7 @@ function useFiatOnRamp(options) {
|
|
|
2649
3309
|
depositTxHash
|
|
2650
3310
|
});
|
|
2651
3311
|
const updated = await executePrivateRead(
|
|
2652
|
-
() =>
|
|
3312
|
+
(readClient) => readClient.updateOnRamp(record.transaction_id, {
|
|
2653
3313
|
deposit_tx_hash: depositTxHash
|
|
2654
3314
|
})
|
|
2655
3315
|
);
|
|
@@ -2710,8 +3370,10 @@ function useFiatOnRamp(options) {
|
|
|
2710
3370
|
if (!token) throw new Error(`Unknown token: ${tokenId}`);
|
|
2711
3371
|
if (!depositAddress) throw new Error("Privana deposit address is not ready");
|
|
2712
3372
|
let lockAmount;
|
|
3373
|
+
let lockOwner;
|
|
2713
3374
|
if (postDepositLock) {
|
|
2714
3375
|
if (!address || !walletClient) throw new Error("Wallet not connected");
|
|
3376
|
+
lockOwner = requireDepositLockOwner(address, privateReadAddress);
|
|
2715
3377
|
if (!quoteCurrencyAmount) {
|
|
2716
3378
|
throw new Error(
|
|
2717
3379
|
"postDepositLock requires quoteCurrencyAmount to derive the lock amount"
|
|
@@ -2740,7 +3402,7 @@ function useFiatOnRamp(options) {
|
|
|
2740
3402
|
depositAddress
|
|
2741
3403
|
});
|
|
2742
3404
|
const record = await executePrivateRead(
|
|
2743
|
-
() =>
|
|
3405
|
+
(readClient) => readClient.createOnRampIntent({
|
|
2744
3406
|
wallet_address: depositAddress,
|
|
2745
3407
|
token_id: tokenId,
|
|
2746
3408
|
chain_id: token.chainId,
|
|
@@ -2749,22 +3411,25 @@ function useFiatOnRamp(options) {
|
|
|
2749
3411
|
base_currency_amount: baseCurrencyAmount
|
|
2750
3412
|
})
|
|
2751
3413
|
);
|
|
2752
|
-
if (postDepositLock &&
|
|
3414
|
+
if (postDepositLock && lockOwner && lockAmount !== void 0) {
|
|
2753
3415
|
const signingWalletClient = await getWalletClient3(wagmiConfig, {
|
|
2754
3416
|
chainId: networkConfig.chainId
|
|
2755
3417
|
});
|
|
2756
3418
|
const signedLock = await createSignedLockRequest({
|
|
2757
3419
|
client,
|
|
2758
3420
|
walletClient: signingWalletClient,
|
|
2759
|
-
userAddress:
|
|
3421
|
+
userAddress: lockOwner,
|
|
2760
3422
|
networkConfig,
|
|
2761
3423
|
serviceAddress: requireServiceAddress(postDepositLock.serviceAddress ?? serviceAddress),
|
|
2762
3424
|
tokenId,
|
|
2763
3425
|
amount: lockAmount,
|
|
2764
3426
|
lockDuration: postDepositLock.lockDuration
|
|
2765
3427
|
});
|
|
2766
|
-
|
|
2767
|
-
|
|
3428
|
+
if (privateReadAddressRef.current?.toLowerCase() !== lockOwner.toLowerCase()) {
|
|
3429
|
+
throw new Error("Authenticated deposit account changed while signing");
|
|
3430
|
+
}
|
|
3431
|
+
savePendingLock(lockOwner, record.transaction_id, signedLock);
|
|
3432
|
+
lockOwnerRef.current = lockOwner;
|
|
2768
3433
|
emitDebug("intent:lock-signed", {
|
|
2769
3434
|
transactionId: record.transaction_id,
|
|
2770
3435
|
amount: signedLock.amount,
|
|
@@ -2796,6 +3461,7 @@ function useFiatOnRamp(options) {
|
|
|
2796
3461
|
executePrivateRead,
|
|
2797
3462
|
networkConfig,
|
|
2798
3463
|
postDepositLock,
|
|
3464
|
+
privateReadAddress,
|
|
2799
3465
|
serviceAddress,
|
|
2800
3466
|
tokenId,
|
|
2801
3467
|
wagmiConfig,
|
|
@@ -2821,7 +3487,7 @@ function useFiatOnRamp(options) {
|
|
|
2821
3487
|
chainId: token.chainId
|
|
2822
3488
|
});
|
|
2823
3489
|
const record = await executePrivateRead(
|
|
2824
|
-
() =>
|
|
3490
|
+
(readClient) => readClient.updateOnRamp(transactionId, {
|
|
2825
3491
|
token_id: tokenId,
|
|
2826
3492
|
chain_id: token.chainId,
|
|
2827
3493
|
moonpay_transaction_id: transactionId === moonpayTransactionId ? void 0 : moonpayTransactionId
|
|
@@ -2841,7 +3507,7 @@ function useFiatOnRamp(options) {
|
|
|
2841
3507
|
console.warn("Failed to register on-ramp token mapping:", err);
|
|
2842
3508
|
}
|
|
2843
3509
|
},
|
|
2844
|
-
[
|
|
3510
|
+
[emitDebug, enabledTokens, executePrivateRead, tokenId]
|
|
2845
3511
|
);
|
|
2846
3512
|
const handleTransactionCreated = useCallback(
|
|
2847
3513
|
async (props) => {
|
|
@@ -2856,7 +3522,9 @@ function useFiatOnRamp(options) {
|
|
|
2856
3522
|
setError(null);
|
|
2857
3523
|
try {
|
|
2858
3524
|
emitDebug("moonpay:onUrlSignatureRequested", summariseMoonPayUrl(url));
|
|
2859
|
-
const { signature } = await executePrivateRead(
|
|
3525
|
+
const { signature } = await executePrivateRead(
|
|
3526
|
+
(readClient) => readClient.signOnRampUrl({ url })
|
|
3527
|
+
);
|
|
2860
3528
|
setStatus("awaiting-purchase");
|
|
2861
3529
|
emitDebug("sign-url:success", {
|
|
2862
3530
|
signatureLength: signature.length
|
|
@@ -2871,7 +3539,7 @@ function useFiatOnRamp(options) {
|
|
|
2871
3539
|
throw err;
|
|
2872
3540
|
}
|
|
2873
3541
|
},
|
|
2874
|
-
[
|
|
3542
|
+
[emitDebug, executePrivateRead]
|
|
2875
3543
|
);
|
|
2876
3544
|
const waitForOnChainHash = useCallback(
|
|
2877
3545
|
async (transactionId) => {
|
|
@@ -2883,7 +3551,9 @@ function useFiatOnRamp(options) {
|
|
|
2883
3551
|
});
|
|
2884
3552
|
while (Date.now() - startTime < deliveryTimeout) {
|
|
2885
3553
|
try {
|
|
2886
|
-
const { pending: rows } = await executePrivateRead(
|
|
3554
|
+
const { pending: rows } = await executePrivateRead(
|
|
3555
|
+
(readClient) => readClient.getPendingOnRamps()
|
|
3556
|
+
);
|
|
2887
3557
|
setPending(rows);
|
|
2888
3558
|
const record = rows.find((r) => matchesOnRampTransaction(r, transactionId));
|
|
2889
3559
|
emitDebug("delivery-poll:tick", {
|
|
@@ -2910,7 +3580,7 @@ function useFiatOnRamp(options) {
|
|
|
2910
3580
|
emitDebug("delivery-poll:timeout", { transactionId });
|
|
2911
3581
|
return null;
|
|
2912
3582
|
},
|
|
2913
|
-
[
|
|
3583
|
+
[deliveryPollInterval, deliveryTimeout, emitDebug, executePrivateRead]
|
|
2914
3584
|
);
|
|
2915
3585
|
const triggerVerification = useCallback(
|
|
2916
3586
|
async (record) => {
|
|
@@ -2978,7 +3648,6 @@ function useFiatOnRamp(options) {
|
|
|
2978
3648
|
`Delivered amount (${record.quote_currency_amount}) is below the minimum deposit.`
|
|
2979
3649
|
);
|
|
2980
3650
|
}
|
|
2981
|
-
activeVerificationAmountRef.current = amount;
|
|
2982
3651
|
if (activeIntentIdRef.current === record.transaction_id) {
|
|
2983
3652
|
setStatus("verifying");
|
|
2984
3653
|
}
|
|
@@ -2997,7 +3666,6 @@ function useFiatOnRamp(options) {
|
|
|
2997
3666
|
if (activeVerificationKeyRef.current === verificationKey) {
|
|
2998
3667
|
activeVerificationKeyRef.current = null;
|
|
2999
3668
|
activeVerificationRecordRef.current = null;
|
|
3000
|
-
activeVerificationAmountRef.current = null;
|
|
3001
3669
|
setActiveVerificationId(null);
|
|
3002
3670
|
}
|
|
3003
3671
|
throw err;
|
|
@@ -3243,12 +3911,6 @@ function matchesOnRampTransaction(record, transactionId) {
|
|
|
3243
3911
|
function getOnRampVerificationKey(record) {
|
|
3244
3912
|
return record.on_chain_tx_hash ?? record.transaction_id;
|
|
3245
3913
|
}
|
|
3246
|
-
function requireServiceAddress(serviceAddress) {
|
|
3247
|
-
if (!serviceAddress) {
|
|
3248
|
-
throw new Error("Service address not configured");
|
|
3249
|
-
}
|
|
3250
|
-
return serviceAddress;
|
|
3251
|
-
}
|
|
3252
3914
|
function summariseMoonPayEventProps(props) {
|
|
3253
3915
|
return {
|
|
3254
3916
|
id: props.id,
|
|
@@ -3682,7 +4344,7 @@ function FiatOnRampForm({
|
|
|
3682
4344
|
] }),
|
|
3683
4345
|
lockPending && /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-sm", children: [
|
|
3684
4346
|
/* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }),
|
|
3685
|
-
"Purchase credited
|
|
4347
|
+
"Purchase credited. Locking your funds\u2026"
|
|
3686
4348
|
] }),
|
|
3687
4349
|
autoStart ? !visible && isPrePurchase && /* @__PURE__ */ jsx(Skeleton, { className: "h-[656px] w-full rounded-md" }) : isInitializing ? /* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full rounded-md" }) : /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleOpen, disabled: !canBuy, children: [
|
|
3688
4350
|
(isBusy || visible) && /* @__PURE__ */ jsx(Loader2, { className: "animate-spin", "aria-hidden": true }),
|
|
@@ -3711,6 +4373,6 @@ function parseFinalityProgress(message) {
|
|
|
3711
4373
|
return match ? `${match[1]} confirmations` : null;
|
|
3712
4374
|
}
|
|
3713
4375
|
|
|
3714
|
-
export { AccountingApiError, Button, DEFAULT_LOCK_DURATION_SECONDS, DEFAULT_ONRAMP_LOCK_BUFFER, FiatOnRampForm, 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, canUseBrowserStorage, clampLockAmount, clearHostedAuthPendingTransaction, clearPendingLock, cn, createDomain, createHostedAuthPendingStorageKey, createHostedAuthState, createHostedAuthStorageKey, createLockExpiry, createPkceChallenge, createPkceVerifier, createSignedLockRequest, formatCountdown, formatTimeRemaining, formatTokenAmount, getAccountingContract, getApiUrl, getBrowserStorageItem, getChainById, getChainId, getExplorerAddressUrl, getExplorerLabel, getTransactionReceipt, getWalletClient3 as getWalletClient, isHostedAuthRefreshActive, isHostedAuthSessionActive, isSignedLockUsable, loadPendingLock, normalizeAddress, normalizeHex, parseHostedAuthCallback, parseTokenAmount, persistHostedAuthPendingTransaction, readHostedAuthPendingTransaction, readStoredHostedAuthSession, removeBrowserStorageItem, savePendingLock, setBrowserStorageItem, shortenAddress, signLockMessage, signModifyLockMessage, signTransferLockedMessage, signTransferMessage, signWithdrawFromLockMessage, signWithdrawMessage, stripHostedAuthCallbackParams, submitPendingLock, syncHostedAuthSessionToClient, useDepositVerification, useEnsureCorrectChain, useFiatOnRamp, usePrivanaContext, usePrivateReadRequest, useSafeAccount, useSafePrivanaContext, useSiweAuth, waitForTransactionReceipt };
|
|
3715
|
-
//# sourceMappingURL=chunk-
|
|
3716
|
-
//# sourceMappingURL=chunk-
|
|
4376
|
+
export { AccountingApiError, Button, DEFAULT_LOCK_DURATION_SECONDS, DEFAULT_ONRAMP_LOCK_BUFFER, FiatOnRampForm, 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, canUseBrowserStorage, canUseSharedBrowserStorage, clampLockAmount, clearHostedAuthPendingTransaction, clearPendingLock, cn, createDomain, createHostedAuthPendingStorageKey, createHostedAuthState, createHostedAuthStorageKey, createLockExpiry, createPkceChallenge, createPkceVerifier, createSignedLockRequest, formatCountdown, formatTimeRemaining, formatTokenAmount, getAccountingContract, getApiUrl, getBlockNumber, getBrowserStorageItem, getChainById, getChainId, getExplorerAddressUrl, getExplorerLabel, getSharedBrowserStorageItem, getTransactionReceipt, getWalletClient3 as getWalletClient, isHostedAuthRefreshActive, isHostedAuthSessionActive, isSignedLockUsable, loadPendingLock, normalizeAddress, normalizeHex, parseHostedAuthCallback, parseTokenAmount, persistHostedAuthPendingTransaction, readHostedAuthPendingTransaction, readStoredHostedAuthSession, removeBrowserStorageItem, removeSharedBrowserStorageItem, requireDepositLockOwner, requireServiceAddress, savePendingLock, setBrowserStorageItem, setSharedBrowserStorageItem, shortenAddress, signLockMessage, signModifyLockMessage, signTransferLockedMessage, signTransferMessage, signWithdrawFromLockMessage, signWithdrawMessage, stripHostedAuthCallbackParams, submitPendingLock, syncHostedAuthSessionToClient, useDepositVerification, useEnsureCorrectChain, useFiatOnRamp, usePrivanaContext, usePrivateReadRequest, useSafeAccount, useSafePrivanaContext, useSiweAuth, waitForTransactionReceipt };
|
|
4377
|
+
//# sourceMappingURL=chunk-DZURIIRE.js.map
|
|
4378
|
+
//# sourceMappingURL=chunk-DZURIIRE.js.map
|