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