@oasisprotocol/privana-sdk 0.5.10 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-RDYNQUMV.js → chunk-OHSIARZ3.js} +385 -376
- package/dist/chunk-OHSIARZ3.js.map +1 -0
- package/dist/{chunk-KCXYYUUG.cjs → chunk-VNECVPMU.cjs} +385 -376
- package/dist/chunk-VNECVPMU.cjs.map +1 -0
- package/dist/index.cjs +346 -288
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +86 -29
- package/dist/index.js.map +1 -1
- package/dist/on-ramp.cjs +5 -5
- package/dist/on-ramp.d.cts +2 -2
- package/dist/on-ramp.d.ts +2 -2
- package/dist/on-ramp.js +1 -1
- package/dist/{pending-lock-DIL2jCaT.d.cts → pending-lock-rsz1iwZ-.d.cts} +1 -1
- package/dist/{pending-lock-DIL2jCaT.d.ts → pending-lock-rsz1iwZ-.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-KCXYYUUG.cjs.map +0 -1
- package/dist/chunk-RDYNQUMV.js.map +0 -1
|
@@ -245,12 +245,21 @@ var SIWE_MESSAGE_VALIDITY_MS = 24 * 60 * 60 * 1e3;
|
|
|
245
245
|
function buildSiweStatement(chainId) {
|
|
246
246
|
return `Sign in to Privana on chain ${chainId}`;
|
|
247
247
|
}
|
|
248
|
+
function pickSiweDomain(response) {
|
|
249
|
+
const allowed = response.domains;
|
|
250
|
+
if (!allowed?.length) throw new Error("SIWE domain response contains no domains");
|
|
251
|
+
const primary = allowed[0];
|
|
252
|
+
if (typeof window === "undefined" || !window.location?.host) return primary;
|
|
253
|
+
const host = window.location.host.toLowerCase();
|
|
254
|
+
return allowed.some((domain) => domain.toLowerCase() === host) ? host : primary;
|
|
255
|
+
}
|
|
248
256
|
async function buildSiweLoginMessage(api, params) {
|
|
249
257
|
const { address, chainId, apiUrl } = params;
|
|
250
|
-
const [
|
|
258
|
+
const [domainResponse, { nonce }] = await Promise.all([
|
|
251
259
|
api.getSiweDomain(),
|
|
252
260
|
api.getSiweNonce(address)
|
|
253
261
|
]);
|
|
262
|
+
const domain = pickSiweDomain(domainResponse);
|
|
254
263
|
const issuedAt = /* @__PURE__ */ new Date();
|
|
255
264
|
const expirationTime = new Date(issuedAt.getTime() + SIWE_MESSAGE_VALIDITY_MS);
|
|
256
265
|
const uri = typeof window !== "undefined" && window.location.origin ? window.location.origin : apiUrl;
|
|
@@ -584,7 +593,7 @@ var PrivanaClient = class _PrivanaClient {
|
|
|
584
593
|
return this.http.get(`/v1/accounting/withdraw/${index}`);
|
|
585
594
|
}
|
|
586
595
|
async getSiweDomain() {
|
|
587
|
-
return this.http.get("/v1/accounting/auth/
|
|
596
|
+
return this.http.get("/v1/accounting/auth/domains");
|
|
588
597
|
}
|
|
589
598
|
async getSiweNonce(userAddress) {
|
|
590
599
|
const user = normalizeAddress(userAddress);
|
|
@@ -1368,7 +1377,7 @@ async function ctrlHydrateViaRefresh(ctrl, record, address) {
|
|
|
1368
1377
|
}
|
|
1369
1378
|
}
|
|
1370
1379
|
|
|
1371
|
-
// src/sdk/
|
|
1380
|
+
// src/sdk/utils/private-read-token-store.ts
|
|
1372
1381
|
var cache = /* @__PURE__ */ new Map();
|
|
1373
1382
|
function createScopeKey(apiUrl, chainId, address) {
|
|
1374
1383
|
return `${apiUrl.replace(/\/$/, "")}:${chainId}:${address.toLowerCase()}`;
|
|
@@ -1993,395 +2002,129 @@ function useSafePrivanaContext() {
|
|
|
1993
2002
|
return react.useContext(PrivanaContext);
|
|
1994
2003
|
}
|
|
1995
2004
|
|
|
1996
|
-
// src/sdk/
|
|
1997
|
-
function
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
}
|
|
2010
|
-
function canUseBrowserStorage() {
|
|
2011
|
-
const probeKey = "privana:storage-probe";
|
|
2012
|
-
for (const storage of storageCandidates()) {
|
|
2005
|
+
// src/sdk/utils/deposit-finality.ts
|
|
2006
|
+
async function checkDepositWithFinalityRetry({
|
|
2007
|
+
checkDeposit,
|
|
2008
|
+
isStale,
|
|
2009
|
+
onRetry,
|
|
2010
|
+
timeoutMs,
|
|
2011
|
+
retryIntervalMs,
|
|
2012
|
+
startedAt = Date.now(),
|
|
2013
|
+
now = Date.now,
|
|
2014
|
+
sleep = defaultSleep
|
|
2015
|
+
}) {
|
|
2016
|
+
while (true) {
|
|
2017
|
+
if (isStale()) return { kind: "stale" };
|
|
2013
2018
|
try {
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2019
|
+
const response = await checkDeposit();
|
|
2020
|
+
if (!isInsufficientFinalityMessage(response.detail) || response.status !== "error") {
|
|
2021
|
+
return { kind: "response", response };
|
|
2022
|
+
}
|
|
2023
|
+
if (response.detail) onRetry(response.detail);
|
|
2024
|
+
} catch (error) {
|
|
2025
|
+
if (isStale()) return { kind: "stale" };
|
|
2026
|
+
if (!isInsufficientFinalityError(error)) throw error;
|
|
2027
|
+
onRetry(
|
|
2028
|
+
error instanceof AccountingApiError && error.detail ? error.detail : error instanceof Error ? error.message : String(error)
|
|
2029
|
+
);
|
|
2018
2030
|
}
|
|
2031
|
+
if (now() - startedAt > timeoutMs) return { kind: "timeout" };
|
|
2032
|
+
await sleep(retryIntervalMs);
|
|
2019
2033
|
}
|
|
2020
|
-
return false;
|
|
2021
2034
|
}
|
|
2022
|
-
function
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
try {
|
|
2026
|
-
storage.setItem(key, value);
|
|
2027
|
-
stored = true;
|
|
2028
|
-
} catch {
|
|
2029
|
-
}
|
|
2035
|
+
function isInsufficientFinalityError(error) {
|
|
2036
|
+
if (error instanceof AccountingApiError) {
|
|
2037
|
+
return isInsufficientFinalityMessage(error.detail) || isInsufficientFinalityMessage(error.message);
|
|
2030
2038
|
}
|
|
2031
|
-
return
|
|
2039
|
+
return error instanceof Error && isInsufficientFinalityMessage(error.message);
|
|
2032
2040
|
}
|
|
2033
|
-
function
|
|
2034
|
-
|
|
2035
|
-
try {
|
|
2036
|
-
const value = storage.getItem(key);
|
|
2037
|
-
if (value !== null) return value;
|
|
2038
|
-
} catch {
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
return null;
|
|
2041
|
+
function isInsufficientFinalityMessage(message) {
|
|
2042
|
+
return message?.includes("Insufficient finality") ?? false;
|
|
2042
2043
|
}
|
|
2043
|
-
function
|
|
2044
|
-
|
|
2045
|
-
try {
|
|
2046
|
-
storage.removeItem(key);
|
|
2047
|
-
} catch {
|
|
2048
|
-
}
|
|
2049
|
-
}
|
|
2044
|
+
function defaultSleep(milliseconds) {
|
|
2045
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
2050
2046
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2047
|
+
var INITIAL_AUTH_BACKOFF_MS = 5e3;
|
|
2048
|
+
var MAX_AUTH_BACKOFF_MS = 6e4;
|
|
2049
|
+
var privateReadFailureCache = /* @__PURE__ */ new Map();
|
|
2050
|
+
var privateReadInflight = /* @__PURE__ */ new Map();
|
|
2051
|
+
async function executeHostedAuthPrivateReadRequest({
|
|
2052
|
+
client,
|
|
2053
|
+
hostedAuthSession,
|
|
2054
|
+
refreshHostedAuthSession,
|
|
2055
|
+
request
|
|
2056
|
+
}) {
|
|
2057
|
+
const ensureHostedAuth = async (forceRefresh) => {
|
|
2058
|
+
if (!hostedAuthSession) {
|
|
2059
|
+
throw new HostedAuthRequiredError();
|
|
2060
|
+
}
|
|
2061
|
+
if (!forceRefresh && isHostedAuthSessionActive(hostedAuthSession)) {
|
|
2062
|
+
client.clearPrivateReadToken();
|
|
2063
|
+
client.setBearerToken(hostedAuthSession.accessToken);
|
|
2064
|
+
return hostedAuthSession.accessToken;
|
|
2065
|
+
}
|
|
2066
|
+
const refreshed = await refreshHostedAuthSession();
|
|
2067
|
+
client.clearPrivateReadToken();
|
|
2068
|
+
client.setBearerToken(refreshed.accessToken);
|
|
2069
|
+
return refreshed.accessToken;
|
|
2070
|
+
};
|
|
2071
|
+
await ensureHostedAuth(false);
|
|
2055
2072
|
try {
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2073
|
+
return await request(client);
|
|
2074
|
+
} catch (error) {
|
|
2075
|
+
if (!(error instanceof AccountingApiError) || error.statusCode !== 401) {
|
|
2076
|
+
throw error;
|
|
2077
|
+
}
|
|
2078
|
+
await ensureHostedAuth(true);
|
|
2079
|
+
return request(client);
|
|
2061
2080
|
}
|
|
2062
2081
|
}
|
|
2063
|
-
function
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
storage.setItem(key, value);
|
|
2068
|
-
} catch {
|
|
2069
|
-
return false;
|
|
2070
|
-
}
|
|
2071
|
-
try {
|
|
2072
|
-
storageCandidate("sessionStorage")?.removeItem(key);
|
|
2073
|
-
} catch {
|
|
2074
|
-
}
|
|
2075
|
-
return true;
|
|
2082
|
+
function clearPrivateReadScope(scopeKey, client) {
|
|
2083
|
+
deleteCachedPrivateReadToken(scopeKey);
|
|
2084
|
+
privateReadFailureCache.delete(scopeKey);
|
|
2085
|
+
client.clearPrivateReadToken();
|
|
2076
2086
|
}
|
|
2077
|
-
function
|
|
2087
|
+
async function executeSiwePrivateReadRequest({
|
|
2088
|
+
client,
|
|
2089
|
+
scopeKey,
|
|
2090
|
+
getToken,
|
|
2091
|
+
request
|
|
2092
|
+
}) {
|
|
2093
|
+
const run = (token2) => request(client.withPrivateReadToken(token2));
|
|
2094
|
+
const token = await getToken(false);
|
|
2078
2095
|
try {
|
|
2079
|
-
return
|
|
2080
|
-
} catch {
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
}
|
|
2084
|
-
function removeSharedBrowserStorageItem(key) {
|
|
2085
|
-
for (const storage of storageCandidates()) {
|
|
2086
|
-
try {
|
|
2087
|
-
storage.removeItem(key);
|
|
2088
|
-
} catch {
|
|
2096
|
+
return await run(token);
|
|
2097
|
+
} catch (error) {
|
|
2098
|
+
if (!(error instanceof AccountingApiError) || error.statusCode !== 401) {
|
|
2099
|
+
throw error;
|
|
2089
2100
|
}
|
|
2101
|
+
clearPrivateReadScope(scopeKey, client);
|
|
2102
|
+
return run(await getToken(true));
|
|
2090
2103
|
}
|
|
2091
2104
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
return amount * (BUFFER_SCALE - shave) / BUFFER_SCALE;
|
|
2103
|
-
}
|
|
2104
|
-
function clampLockAmount(amount, maxAmount) {
|
|
2105
|
-
return maxAmount !== void 0 && maxAmount < amount ? maxAmount : amount;
|
|
2106
|
-
}
|
|
2107
|
-
function requireServiceAddress(serviceAddress) {
|
|
2108
|
-
if (!serviceAddress) {
|
|
2109
|
-
throw new Error("Service address not configured");
|
|
2110
|
-
}
|
|
2111
|
-
return serviceAddress;
|
|
2112
|
-
}
|
|
2113
|
-
function requireDepositLockOwner(walletAddress, beneficiary) {
|
|
2114
|
-
if (!walletAddress) throw new Error("No wallet connected");
|
|
2115
|
-
if (!beneficiary) throw new Error("No authenticated deposit account");
|
|
2116
|
-
if (walletAddress.toLowerCase() !== beneficiary.toLowerCase()) {
|
|
2117
|
-
throw new Error("Connected wallet does not match the authenticated deposit account");
|
|
2118
|
-
}
|
|
2119
|
-
return beneficiary;
|
|
2120
|
-
}
|
|
2121
|
-
function walletClientAccountAddress(walletClient) {
|
|
2122
|
-
const account = walletClient.account;
|
|
2123
|
-
if (!account) return void 0;
|
|
2124
|
-
return typeof account === "string" ? account : account.address;
|
|
2105
|
+
function recordPrivateReadFailure(scopeKey) {
|
|
2106
|
+
const previous = privateReadFailureCache.get(scopeKey);
|
|
2107
|
+
const backoffMs = Math.min(
|
|
2108
|
+
previous ? previous.backoffMs * 2 : INITIAL_AUTH_BACKOFF_MS,
|
|
2109
|
+
MAX_AUTH_BACKOFF_MS
|
|
2110
|
+
);
|
|
2111
|
+
privateReadFailureCache.set(scopeKey, {
|
|
2112
|
+
backoffMs,
|
|
2113
|
+
retryAt: Date.now() + backoffMs
|
|
2114
|
+
});
|
|
2125
2115
|
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
tokenId,
|
|
2133
|
-
amount,
|
|
2134
|
-
lockDuration = DEFAULT_LOCK_DURATION_SECONDS
|
|
2135
|
-
}) {
|
|
2136
|
-
if (amount <= 0n) {
|
|
2137
|
-
throw new Error("Lock amount must be positive");
|
|
2138
|
-
}
|
|
2139
|
-
const signerAddress = walletClientAccountAddress(walletClient);
|
|
2140
|
-
if (!signerAddress || signerAddress.toLowerCase() !== userAddress.toLowerCase()) {
|
|
2141
|
-
throw new Error("Connected wallet does not match the authenticated deposit account");
|
|
2116
|
+
function ensureFailureBackoff(scopeKey) {
|
|
2117
|
+
const failure = privateReadFailureCache.get(scopeKey);
|
|
2118
|
+
if (!failure) return;
|
|
2119
|
+
if (failure.retryAt <= Date.now()) {
|
|
2120
|
+
privateReadFailureCache.delete(scopeKey);
|
|
2121
|
+
return;
|
|
2142
2122
|
}
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
verifyingContract: networkConfig.accountingContract,
|
|
2149
|
-
message: {
|
|
2150
|
-
serviceAddress,
|
|
2151
|
-
tokenId,
|
|
2152
|
-
amount,
|
|
2153
|
-
expiry,
|
|
2154
|
-
nonce: BigInt(nonce)
|
|
2155
|
-
}
|
|
2156
|
-
});
|
|
2157
|
-
return {
|
|
2158
|
-
service_address: serviceAddress,
|
|
2159
|
-
token_id: tokenId,
|
|
2160
|
-
amount: amount.toString(),
|
|
2161
|
-
expiry: expiry.toString(),
|
|
2162
|
-
nonce: String(nonce),
|
|
2163
|
-
signature
|
|
2164
|
-
};
|
|
2165
|
-
}
|
|
2166
|
-
var EXPIRY_SLACK_SECONDS = 60;
|
|
2167
|
-
function isSignedLockUsable(payload) {
|
|
2168
|
-
const expiry = Number(payload.expiry);
|
|
2169
|
-
if (!Number.isFinite(expiry)) return false;
|
|
2170
|
-
return expiry > Math.floor(Date.now() / 1e3) + EXPIRY_SLACK_SECONDS;
|
|
2171
|
-
}
|
|
2172
|
-
var PostDepositLockError = class _PostDepositLockError extends Error {
|
|
2173
|
-
constructor(message, reason, signedAmount, creditedAmount, options) {
|
|
2174
|
-
super(message, options);
|
|
2175
|
-
this.reason = reason;
|
|
2176
|
-
this.signedAmount = signedAmount;
|
|
2177
|
-
this.creditedAmount = creditedAmount;
|
|
2178
|
-
this.name = "PostDepositLockError";
|
|
2179
|
-
this.submissionMayHaveSucceeded = options?.submissionMayHaveSucceeded ?? false;
|
|
2180
|
-
Object.setPrototypeOf(this, _PostDepositLockError.prototype);
|
|
2181
|
-
}
|
|
2182
|
-
};
|
|
2183
|
-
async function submitPendingLock({
|
|
2184
|
-
client,
|
|
2185
|
-
payload,
|
|
2186
|
-
creditedAmount,
|
|
2187
|
-
beforeSubmit
|
|
2188
|
-
}) {
|
|
2189
|
-
let signedAmount;
|
|
2190
|
-
try {
|
|
2191
|
-
signedAmount = BigInt(payload.amount);
|
|
2192
|
-
} catch (err) {
|
|
2193
|
-
throw new PostDepositLockError(
|
|
2194
|
-
"Stored signed lock payload is malformed",
|
|
2195
|
-
"submission-failed",
|
|
2196
|
-
void 0,
|
|
2197
|
-
creditedAmount,
|
|
2198
|
-
{ cause: err }
|
|
2199
|
-
);
|
|
2200
|
-
}
|
|
2201
|
-
if (!isSignedLockUsable(payload)) {
|
|
2202
|
-
throw new PostDepositLockError(
|
|
2203
|
-
"Signed lock expired before the deposit was credited",
|
|
2204
|
-
"expired",
|
|
2205
|
-
signedAmount,
|
|
2206
|
-
creditedAmount
|
|
2207
|
-
);
|
|
2208
|
-
}
|
|
2209
|
-
if (creditedAmount !== void 0 && creditedAmount < signedAmount) {
|
|
2210
|
-
throw new PostDepositLockError(
|
|
2211
|
-
"Credited amount is below the signed lock amount",
|
|
2212
|
-
"credited-below-signed",
|
|
2213
|
-
signedAmount,
|
|
2214
|
-
creditedAmount
|
|
2215
|
-
);
|
|
2216
|
-
}
|
|
2217
|
-
try {
|
|
2218
|
-
beforeSubmit?.();
|
|
2219
|
-
return await client.lockFunds(payload);
|
|
2220
|
-
} catch (err) {
|
|
2221
|
-
throw new PostDepositLockError(
|
|
2222
|
-
err instanceof Error ? err.message : "Lock submission failed",
|
|
2223
|
-
"submission-failed",
|
|
2224
|
-
signedAmount,
|
|
2225
|
-
creditedAmount,
|
|
2226
|
-
{ cause: err }
|
|
2227
|
-
);
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
function pendingLockKey(userAddress, correlationId) {
|
|
2231
|
-
return `privana:pending-lock:${userAddress.toLowerCase()}:${correlationId}`;
|
|
2232
|
-
}
|
|
2233
|
-
function savePendingLock(userAddress, correlationId, payload) {
|
|
2234
|
-
const record = { payload, savedAt: Date.now() };
|
|
2235
|
-
const stored = setBrowserStorageItem(
|
|
2236
|
-
pendingLockKey(userAddress, correlationId),
|
|
2237
|
-
JSON.stringify(record)
|
|
2238
|
-
);
|
|
2239
|
-
if (!stored) {
|
|
2240
|
-
throw new Error("Unable to persist signed lock for recovery");
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
|
-
function loadPendingLock(userAddress, correlationId) {
|
|
2244
|
-
const key = pendingLockKey(userAddress, correlationId);
|
|
2245
|
-
try {
|
|
2246
|
-
const raw = getBrowserStorageItem(key);
|
|
2247
|
-
if (!raw) return void 0;
|
|
2248
|
-
const record = JSON.parse(raw);
|
|
2249
|
-
if (!record?.payload?.signature) {
|
|
2250
|
-
removeBrowserStorageItem(key);
|
|
2251
|
-
return void 0;
|
|
2252
|
-
}
|
|
2253
|
-
return record.payload;
|
|
2254
|
-
} catch {
|
|
2255
|
-
return void 0;
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
function clearPendingLock(userAddress, correlationId) {
|
|
2259
|
-
removeBrowserStorageItem(pendingLockKey(userAddress, correlationId));
|
|
2260
|
-
}
|
|
2261
|
-
|
|
2262
|
-
// src/sdk/hooks/deposit-finality.ts
|
|
2263
|
-
async function checkDepositWithFinalityRetry({
|
|
2264
|
-
checkDeposit,
|
|
2265
|
-
isStale,
|
|
2266
|
-
onRetry,
|
|
2267
|
-
timeoutMs,
|
|
2268
|
-
retryIntervalMs,
|
|
2269
|
-
startedAt = Date.now(),
|
|
2270
|
-
now = Date.now,
|
|
2271
|
-
sleep = defaultSleep
|
|
2272
|
-
}) {
|
|
2273
|
-
while (true) {
|
|
2274
|
-
if (isStale()) return { kind: "stale" };
|
|
2275
|
-
try {
|
|
2276
|
-
const response = await checkDeposit();
|
|
2277
|
-
if (!isInsufficientFinalityMessage(response.detail) || response.status !== "error") {
|
|
2278
|
-
return { kind: "response", response };
|
|
2279
|
-
}
|
|
2280
|
-
if (response.detail) onRetry(response.detail);
|
|
2281
|
-
} catch (error) {
|
|
2282
|
-
if (isStale()) return { kind: "stale" };
|
|
2283
|
-
if (!isInsufficientFinalityError(error)) throw error;
|
|
2284
|
-
onRetry(
|
|
2285
|
-
error instanceof AccountingApiError && error.detail ? error.detail : error instanceof Error ? error.message : String(error)
|
|
2286
|
-
);
|
|
2287
|
-
}
|
|
2288
|
-
if (now() - startedAt > timeoutMs) return { kind: "timeout" };
|
|
2289
|
-
await sleep(retryIntervalMs);
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
function isInsufficientFinalityError(error) {
|
|
2293
|
-
if (error instanceof AccountingApiError) {
|
|
2294
|
-
return isInsufficientFinalityMessage(error.detail) || isInsufficientFinalityMessage(error.message);
|
|
2295
|
-
}
|
|
2296
|
-
return error instanceof Error && isInsufficientFinalityMessage(error.message);
|
|
2297
|
-
}
|
|
2298
|
-
function isInsufficientFinalityMessage(message) {
|
|
2299
|
-
return message?.includes("Insufficient finality") ?? false;
|
|
2300
|
-
}
|
|
2301
|
-
function defaultSleep(milliseconds) {
|
|
2302
|
-
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
2303
|
-
}
|
|
2304
|
-
var INITIAL_AUTH_BACKOFF_MS = 5e3;
|
|
2305
|
-
var MAX_AUTH_BACKOFF_MS = 6e4;
|
|
2306
|
-
var privateReadFailureCache = /* @__PURE__ */ new Map();
|
|
2307
|
-
var privateReadInflight = /* @__PURE__ */ new Map();
|
|
2308
|
-
async function executeHostedAuthPrivateReadRequest({
|
|
2309
|
-
client,
|
|
2310
|
-
hostedAuthSession,
|
|
2311
|
-
refreshHostedAuthSession,
|
|
2312
|
-
request
|
|
2313
|
-
}) {
|
|
2314
|
-
const ensureHostedAuth = async (forceRefresh) => {
|
|
2315
|
-
if (!hostedAuthSession) {
|
|
2316
|
-
throw new HostedAuthRequiredError();
|
|
2317
|
-
}
|
|
2318
|
-
if (!forceRefresh && isHostedAuthSessionActive(hostedAuthSession)) {
|
|
2319
|
-
client.clearPrivateReadToken();
|
|
2320
|
-
client.setBearerToken(hostedAuthSession.accessToken);
|
|
2321
|
-
return hostedAuthSession.accessToken;
|
|
2322
|
-
}
|
|
2323
|
-
const refreshed = await refreshHostedAuthSession();
|
|
2324
|
-
client.clearPrivateReadToken();
|
|
2325
|
-
client.setBearerToken(refreshed.accessToken);
|
|
2326
|
-
return refreshed.accessToken;
|
|
2327
|
-
};
|
|
2328
|
-
await ensureHostedAuth(false);
|
|
2329
|
-
try {
|
|
2330
|
-
return await request(client);
|
|
2331
|
-
} catch (error) {
|
|
2332
|
-
if (!(error instanceof AccountingApiError) || error.statusCode !== 401) {
|
|
2333
|
-
throw error;
|
|
2334
|
-
}
|
|
2335
|
-
await ensureHostedAuth(true);
|
|
2336
|
-
return request(client);
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
function clearPrivateReadScope(scopeKey, client) {
|
|
2340
|
-
deleteCachedPrivateReadToken(scopeKey);
|
|
2341
|
-
privateReadFailureCache.delete(scopeKey);
|
|
2342
|
-
client.clearPrivateReadToken();
|
|
2343
|
-
}
|
|
2344
|
-
async function executeSiwePrivateReadRequest({
|
|
2345
|
-
client,
|
|
2346
|
-
scopeKey,
|
|
2347
|
-
getToken,
|
|
2348
|
-
request
|
|
2349
|
-
}) {
|
|
2350
|
-
const run = (token2) => request(client.withPrivateReadToken(token2));
|
|
2351
|
-
const token = await getToken(false);
|
|
2352
|
-
try {
|
|
2353
|
-
return await run(token);
|
|
2354
|
-
} catch (error) {
|
|
2355
|
-
if (!(error instanceof AccountingApiError) || error.statusCode !== 401) {
|
|
2356
|
-
throw error;
|
|
2357
|
-
}
|
|
2358
|
-
clearPrivateReadScope(scopeKey, client);
|
|
2359
|
-
return run(await getToken(true));
|
|
2360
|
-
}
|
|
2361
|
-
}
|
|
2362
|
-
function recordPrivateReadFailure(scopeKey) {
|
|
2363
|
-
const previous = privateReadFailureCache.get(scopeKey);
|
|
2364
|
-
const backoffMs = Math.min(
|
|
2365
|
-
previous ? previous.backoffMs * 2 : INITIAL_AUTH_BACKOFF_MS,
|
|
2366
|
-
MAX_AUTH_BACKOFF_MS
|
|
2367
|
-
);
|
|
2368
|
-
privateReadFailureCache.set(scopeKey, {
|
|
2369
|
-
backoffMs,
|
|
2370
|
-
retryAt: Date.now() + backoffMs
|
|
2371
|
-
});
|
|
2372
|
-
}
|
|
2373
|
-
function ensureFailureBackoff(scopeKey) {
|
|
2374
|
-
const failure = privateReadFailureCache.get(scopeKey);
|
|
2375
|
-
if (!failure) return;
|
|
2376
|
-
if (failure.retryAt <= Date.now()) {
|
|
2377
|
-
privateReadFailureCache.delete(scopeKey);
|
|
2378
|
-
return;
|
|
2379
|
-
}
|
|
2380
|
-
throw new Error(
|
|
2381
|
-
`Private-read authentication is temporarily paused after a recent failure. Retry in ${Math.ceil(
|
|
2382
|
-
(failure.retryAt - Date.now()) / 1e3
|
|
2383
|
-
)}s.`
|
|
2384
|
-
);
|
|
2123
|
+
throw new Error(
|
|
2124
|
+
`Private-read authentication is temporarily paused after a recent failure. Retry in ${Math.ceil(
|
|
2125
|
+
(failure.retryAt - Date.now()) / 1e3
|
|
2126
|
+
)}s.`
|
|
2127
|
+
);
|
|
2385
2128
|
}
|
|
2386
2129
|
function usePrivateReadRequest() {
|
|
2387
2130
|
const wagmiContext = react.useContext(wagmi.WagmiContext);
|
|
@@ -2705,6 +2448,272 @@ function creditedAmountFromResponse(response, requestedAmount) {
|
|
|
2705
2448
|
function isDefinitiveCandidateFailure(error) {
|
|
2706
2449
|
return error instanceof AccountingApiError && error.statusCode === 400;
|
|
2707
2450
|
}
|
|
2451
|
+
|
|
2452
|
+
// src/sdk/utils/browser-storage.ts
|
|
2453
|
+
function storageCandidate(name) {
|
|
2454
|
+
try {
|
|
2455
|
+
if (typeof window === "undefined") return void 0;
|
|
2456
|
+
return window[name] ?? void 0;
|
|
2457
|
+
} catch {
|
|
2458
|
+
return void 0;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
function storageCandidates() {
|
|
2462
|
+
return [storageCandidate("localStorage"), storageCandidate("sessionStorage")].filter(
|
|
2463
|
+
(storage) => storage !== void 0
|
|
2464
|
+
);
|
|
2465
|
+
}
|
|
2466
|
+
function canUseBrowserStorage() {
|
|
2467
|
+
const probeKey = "privana:storage-probe";
|
|
2468
|
+
for (const storage of storageCandidates()) {
|
|
2469
|
+
try {
|
|
2470
|
+
storage.setItem(probeKey, "1");
|
|
2471
|
+
storage.removeItem(probeKey);
|
|
2472
|
+
return true;
|
|
2473
|
+
} catch {
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
return false;
|
|
2477
|
+
}
|
|
2478
|
+
function setBrowserStorageItem(key, value) {
|
|
2479
|
+
let stored = false;
|
|
2480
|
+
for (const storage of storageCandidates()) {
|
|
2481
|
+
try {
|
|
2482
|
+
storage.setItem(key, value);
|
|
2483
|
+
stored = true;
|
|
2484
|
+
} catch {
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
return stored;
|
|
2488
|
+
}
|
|
2489
|
+
function getBrowserStorageItem(key) {
|
|
2490
|
+
for (const storage of storageCandidates()) {
|
|
2491
|
+
try {
|
|
2492
|
+
const value = storage.getItem(key);
|
|
2493
|
+
if (value !== null) return value;
|
|
2494
|
+
} catch {
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
return null;
|
|
2498
|
+
}
|
|
2499
|
+
function removeBrowserStorageItem(key) {
|
|
2500
|
+
for (const storage of storageCandidates()) {
|
|
2501
|
+
try {
|
|
2502
|
+
storage.removeItem(key);
|
|
2503
|
+
} catch {
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
function canUseSharedBrowserStorage() {
|
|
2508
|
+
const storage = storageCandidate("localStorage");
|
|
2509
|
+
if (!storage) return false;
|
|
2510
|
+
const probeKey = "privana:shared-storage-probe";
|
|
2511
|
+
try {
|
|
2512
|
+
storage.setItem(probeKey, "1");
|
|
2513
|
+
storage.removeItem(probeKey);
|
|
2514
|
+
return true;
|
|
2515
|
+
} catch {
|
|
2516
|
+
return false;
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
function setSharedBrowserStorageItem(key, value) {
|
|
2520
|
+
const storage = storageCandidate("localStorage");
|
|
2521
|
+
if (!storage) return false;
|
|
2522
|
+
try {
|
|
2523
|
+
storage.setItem(key, value);
|
|
2524
|
+
} catch {
|
|
2525
|
+
return false;
|
|
2526
|
+
}
|
|
2527
|
+
try {
|
|
2528
|
+
storageCandidate("sessionStorage")?.removeItem(key);
|
|
2529
|
+
} catch {
|
|
2530
|
+
}
|
|
2531
|
+
return true;
|
|
2532
|
+
}
|
|
2533
|
+
function getSharedBrowserStorageItem(key) {
|
|
2534
|
+
try {
|
|
2535
|
+
return storageCandidate("localStorage")?.getItem(key) ?? null;
|
|
2536
|
+
} catch {
|
|
2537
|
+
return null;
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
function removeSharedBrowserStorageItem(key) {
|
|
2541
|
+
for (const storage of storageCandidates()) {
|
|
2542
|
+
try {
|
|
2543
|
+
storage.removeItem(key);
|
|
2544
|
+
} catch {
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
// src/sdk/utils/pending-lock.ts
|
|
2550
|
+
var DEFAULT_LOCK_DURATION_SECONDS = 259200;
|
|
2551
|
+
var DEFAULT_ONRAMP_LOCK_BUFFER = 0.02;
|
|
2552
|
+
var BUFFER_SCALE = 1000000n;
|
|
2553
|
+
function applyLockBuffer(amount, buffer = DEFAULT_ONRAMP_LOCK_BUFFER) {
|
|
2554
|
+
if (!Number.isFinite(buffer) || buffer < 0 || buffer >= 1) {
|
|
2555
|
+
throw new Error(`Lock buffer must be in [0, 1), got ${buffer}`);
|
|
2556
|
+
}
|
|
2557
|
+
const shave = BigInt(Math.max(0, Math.ceil(buffer * Number(BUFFER_SCALE) - 1e-6)));
|
|
2558
|
+
return amount * (BUFFER_SCALE - shave) / BUFFER_SCALE;
|
|
2559
|
+
}
|
|
2560
|
+
function clampLockAmount(amount, maxAmount) {
|
|
2561
|
+
return maxAmount !== void 0 && maxAmount < amount ? maxAmount : amount;
|
|
2562
|
+
}
|
|
2563
|
+
function requireServiceAddress(serviceAddress) {
|
|
2564
|
+
if (!serviceAddress) {
|
|
2565
|
+
throw new Error("Service address not configured");
|
|
2566
|
+
}
|
|
2567
|
+
return serviceAddress;
|
|
2568
|
+
}
|
|
2569
|
+
function requireDepositLockOwner(walletAddress, beneficiary) {
|
|
2570
|
+
if (!walletAddress) throw new Error("No wallet connected");
|
|
2571
|
+
if (!beneficiary) throw new Error("No authenticated deposit account");
|
|
2572
|
+
if (walletAddress.toLowerCase() !== beneficiary.toLowerCase()) {
|
|
2573
|
+
throw new Error("Connected wallet does not match the authenticated deposit account");
|
|
2574
|
+
}
|
|
2575
|
+
return beneficiary;
|
|
2576
|
+
}
|
|
2577
|
+
function walletClientAccountAddress(walletClient) {
|
|
2578
|
+
const account = walletClient.account;
|
|
2579
|
+
if (!account) return void 0;
|
|
2580
|
+
return typeof account === "string" ? account : account.address;
|
|
2581
|
+
}
|
|
2582
|
+
async function createSignedLockRequest({
|
|
2583
|
+
client,
|
|
2584
|
+
walletClient,
|
|
2585
|
+
userAddress,
|
|
2586
|
+
networkConfig,
|
|
2587
|
+
serviceAddress,
|
|
2588
|
+
tokenId,
|
|
2589
|
+
amount,
|
|
2590
|
+
lockDuration = DEFAULT_LOCK_DURATION_SECONDS
|
|
2591
|
+
}) {
|
|
2592
|
+
if (amount <= 0n) {
|
|
2593
|
+
throw new Error("Lock amount must be positive");
|
|
2594
|
+
}
|
|
2595
|
+
const signerAddress = walletClientAccountAddress(walletClient);
|
|
2596
|
+
if (!signerAddress || signerAddress.toLowerCase() !== userAddress.toLowerCase()) {
|
|
2597
|
+
throw new Error("Connected wallet does not match the authenticated deposit account");
|
|
2598
|
+
}
|
|
2599
|
+
const expiry = BigInt(Math.floor(Date.now() / 1e3) + lockDuration);
|
|
2600
|
+
const { nonce } = await client.getLockNonce(userAddress);
|
|
2601
|
+
const signature = await signLockMessage({
|
|
2602
|
+
walletClient,
|
|
2603
|
+
chainId: networkConfig.chainId,
|
|
2604
|
+
verifyingContract: networkConfig.accountingContract,
|
|
2605
|
+
message: {
|
|
2606
|
+
serviceAddress,
|
|
2607
|
+
tokenId,
|
|
2608
|
+
amount,
|
|
2609
|
+
expiry,
|
|
2610
|
+
nonce: BigInt(nonce)
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
return {
|
|
2614
|
+
service_address: serviceAddress,
|
|
2615
|
+
token_id: tokenId,
|
|
2616
|
+
amount: amount.toString(),
|
|
2617
|
+
expiry: expiry.toString(),
|
|
2618
|
+
nonce: String(nonce),
|
|
2619
|
+
signature
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2622
|
+
var EXPIRY_SLACK_SECONDS = 60;
|
|
2623
|
+
function isSignedLockUsable(payload) {
|
|
2624
|
+
const expiry = Number(payload.expiry);
|
|
2625
|
+
if (!Number.isFinite(expiry)) return false;
|
|
2626
|
+
return expiry > Math.floor(Date.now() / 1e3) + EXPIRY_SLACK_SECONDS;
|
|
2627
|
+
}
|
|
2628
|
+
var PostDepositLockError = class _PostDepositLockError extends Error {
|
|
2629
|
+
constructor(message, reason, signedAmount, creditedAmount, options) {
|
|
2630
|
+
super(message, options);
|
|
2631
|
+
this.reason = reason;
|
|
2632
|
+
this.signedAmount = signedAmount;
|
|
2633
|
+
this.creditedAmount = creditedAmount;
|
|
2634
|
+
this.name = "PostDepositLockError";
|
|
2635
|
+
this.submissionMayHaveSucceeded = options?.submissionMayHaveSucceeded ?? false;
|
|
2636
|
+
Object.setPrototypeOf(this, _PostDepositLockError.prototype);
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
async function submitPendingLock({
|
|
2640
|
+
client,
|
|
2641
|
+
payload,
|
|
2642
|
+
creditedAmount,
|
|
2643
|
+
beforeSubmit
|
|
2644
|
+
}) {
|
|
2645
|
+
let signedAmount;
|
|
2646
|
+
try {
|
|
2647
|
+
signedAmount = BigInt(payload.amount);
|
|
2648
|
+
} catch (err) {
|
|
2649
|
+
throw new PostDepositLockError(
|
|
2650
|
+
"Stored signed lock payload is malformed",
|
|
2651
|
+
"submission-failed",
|
|
2652
|
+
void 0,
|
|
2653
|
+
creditedAmount,
|
|
2654
|
+
{ cause: err }
|
|
2655
|
+
);
|
|
2656
|
+
}
|
|
2657
|
+
if (!isSignedLockUsable(payload)) {
|
|
2658
|
+
throw new PostDepositLockError(
|
|
2659
|
+
"Signed lock expired before the deposit was credited",
|
|
2660
|
+
"expired",
|
|
2661
|
+
signedAmount,
|
|
2662
|
+
creditedAmount
|
|
2663
|
+
);
|
|
2664
|
+
}
|
|
2665
|
+
if (creditedAmount !== void 0 && creditedAmount < signedAmount) {
|
|
2666
|
+
throw new PostDepositLockError(
|
|
2667
|
+
"Credited amount is below the signed lock amount",
|
|
2668
|
+
"credited-below-signed",
|
|
2669
|
+
signedAmount,
|
|
2670
|
+
creditedAmount
|
|
2671
|
+
);
|
|
2672
|
+
}
|
|
2673
|
+
try {
|
|
2674
|
+
beforeSubmit?.();
|
|
2675
|
+
return await client.lockFunds(payload);
|
|
2676
|
+
} catch (err) {
|
|
2677
|
+
throw new PostDepositLockError(
|
|
2678
|
+
err instanceof Error ? err.message : "Lock submission failed",
|
|
2679
|
+
"submission-failed",
|
|
2680
|
+
signedAmount,
|
|
2681
|
+
creditedAmount,
|
|
2682
|
+
{ cause: err }
|
|
2683
|
+
);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
function pendingLockKey(userAddress, correlationId) {
|
|
2687
|
+
return `privana:pending-lock:${userAddress.toLowerCase()}:${correlationId}`;
|
|
2688
|
+
}
|
|
2689
|
+
function savePendingLock(userAddress, correlationId, payload) {
|
|
2690
|
+
const record = { payload, savedAt: Date.now() };
|
|
2691
|
+
const stored = setBrowserStorageItem(
|
|
2692
|
+
pendingLockKey(userAddress, correlationId),
|
|
2693
|
+
JSON.stringify(record)
|
|
2694
|
+
);
|
|
2695
|
+
if (!stored) {
|
|
2696
|
+
throw new Error("Unable to persist signed lock for recovery");
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
function loadPendingLock(userAddress, correlationId) {
|
|
2700
|
+
const key = pendingLockKey(userAddress, correlationId);
|
|
2701
|
+
try {
|
|
2702
|
+
const raw = getBrowserStorageItem(key);
|
|
2703
|
+
if (!raw) return void 0;
|
|
2704
|
+
const record = JSON.parse(raw);
|
|
2705
|
+
if (!record?.payload?.signature) {
|
|
2706
|
+
removeBrowserStorageItem(key);
|
|
2707
|
+
return void 0;
|
|
2708
|
+
}
|
|
2709
|
+
return record.payload;
|
|
2710
|
+
} catch {
|
|
2711
|
+
return void 0;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
function clearPendingLock(userAddress, correlationId) {
|
|
2715
|
+
removeBrowserStorageItem(pendingLockKey(userAddress, correlationId));
|
|
2716
|
+
}
|
|
2708
2717
|
var BYTES32_PATTERN = /^0x[0-9a-fA-F]{64}$/;
|
|
2709
2718
|
var PROVIDER_ASSET_CODE_PATTERN = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
2710
2719
|
var TRANSAK_MINIMUM_TARGET_PERCENT = 105n;
|
|
@@ -6163,5 +6172,5 @@ exports.useSafePrivanaContext = useSafePrivanaContext;
|
|
|
6163
6172
|
exports.useSiweAuth = useSiweAuth;
|
|
6164
6173
|
exports.useTransakOnRamp = useTransakOnRamp;
|
|
6165
6174
|
exports.waitForTransactionReceipt = waitForTransactionReceipt;
|
|
6166
|
-
//# sourceMappingURL=chunk-
|
|
6167
|
-
//# sourceMappingURL=chunk-
|
|
6175
|
+
//# sourceMappingURL=chunk-VNECVPMU.cjs.map
|
|
6176
|
+
//# sourceMappingURL=chunk-VNECVPMU.cjs.map
|