@rhinestone/deposit-modal 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/{DepositModalReown-IFN6YLZJ.cjs → DepositModalReown-TVO3ZQQK.cjs} +17 -17
- package/dist/{DepositModalReown-R7UROJPJ.mjs → DepositModalReown-VCY65DJX.mjs} +4 -4
- package/dist/WithdrawModal-GR3HLYXU.cjs +11 -0
- package/dist/{WithdrawModal-XKSSHVEJ.mjs → WithdrawModal-RMCYJJ4S.mjs} +3 -3
- package/dist/bearer-transport-CVHNBgoV.d.cts +14 -0
- package/dist/bearer-transport-CVHNBgoV.d.ts +14 -0
- package/dist/{chunk-24N4KMJX.cjs → chunk-34JVJZEZ.cjs} +71 -71
- package/dist/{chunk-3HCCR54R.mjs → chunk-3TEVMP76.mjs} +1 -1
- package/dist/chunk-ADEWTBSN.cjs +19 -0
- package/dist/{chunk-EA5CE2UZ.mjs → chunk-AIZKGFVA.mjs} +675 -233
- package/dist/{chunk-F6ROQ6RJ.mjs → chunk-EKC2IRQG.mjs} +185 -69
- package/dist/{chunk-V466XQSG.cjs → chunk-JV5OGG53.cjs} +630 -514
- package/dist/{chunk-H4O22O7Y.cjs → chunk-N3AAT6BS.cjs} +826 -384
- package/dist/{chunk-5CM3ZQ5U.cjs → chunk-N6JJG57R.cjs} +1 -1
- package/dist/chunk-O4JZKKCZ.cjs +41 -0
- package/dist/chunk-PP4QE32S.mjs +19 -0
- package/dist/{chunk-HMB2XQGD.mjs → chunk-R2B6VD4E.mjs} +1 -1
- package/dist/chunk-X4EUXBHJ.mjs +41 -0
- package/dist/compliance.cjs +14 -0
- package/dist/compliance.d.cts +10 -0
- package/dist/compliance.d.ts +10 -0
- package/dist/compliance.mjs +14 -0
- package/dist/constants.cjs +3 -3
- package/dist/constants.d.cts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.mjs +3 -3
- package/dist/deposit.cjs +4 -4
- package/dist/deposit.d.cts +2 -2
- package/dist/deposit.d.ts +2 -2
- package/dist/deposit.mjs +3 -3
- package/dist/embed.cjs +16 -16
- package/dist/embed.d.cts +1 -1
- package/dist/embed.d.ts +1 -1
- package/dist/embed.mjs +8 -8
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +4 -4
- package/dist/offramp.cjs +25 -0
- package/dist/offramp.d.cts +28 -0
- package/dist/offramp.d.ts +28 -0
- package/dist/offramp.mjs +25 -0
- package/dist/onramp.cjs +27 -0
- package/dist/onramp.d.cts +37 -0
- package/dist/onramp.d.ts +37 -0
- package/dist/onramp.mjs +27 -0
- package/dist/ramp-types-C6aUz4SZ.d.cts +43 -0
- package/dist/ramp-types-C6aUz4SZ.d.ts +43 -0
- package/dist/server.cjs +54 -2
- package/dist/server.d.cts +13 -1
- package/dist/server.d.ts +13 -1
- package/dist/server.mjs +52 -0
- package/dist/styles.css +22 -0
- package/dist/{types-B9SbZQIG.d.ts → types-CZYypODQ.d.ts} +3 -2
- package/dist/{types-SwGGf0eH.d.cts → types-CbUs4boj.d.cts} +3 -2
- package/dist/withdraw.cjs +4 -4
- package/dist/withdraw.d.cts +2 -2
- package/dist/withdraw.d.ts +2 -2
- package/dist/withdraw.mjs +3 -3
- package/package.json +35 -5
- package/dist/WithdrawModal-SZEAI473.cjs +0 -11
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MODAL_VERSION,
|
|
3
|
-
serviceHeaders
|
|
4
|
-
} from "./chunk-3HCCR54R.mjs";
|
|
5
1
|
import {
|
|
6
2
|
CHAIN_DISPLAY_ORDER,
|
|
7
3
|
HYPERCORE_CHAIN_ID,
|
|
@@ -36,6 +32,10 @@ import {
|
|
|
36
32
|
toEvmCaip2,
|
|
37
33
|
tokenIconUrl
|
|
38
34
|
} from "./chunk-EAGMY3QE.mjs";
|
|
35
|
+
import {
|
|
36
|
+
MODAL_VERSION,
|
|
37
|
+
serviceHeaders
|
|
38
|
+
} from "./chunk-3TEVMP76.mjs";
|
|
39
39
|
|
|
40
40
|
// src/components/ui/Modal.tsx
|
|
41
41
|
import {
|
|
@@ -650,12 +650,37 @@ function parseQuotePreview(raw) {
|
|
|
650
650
|
);
|
|
651
651
|
if (Math.abs(totalUsd - breakdownTotalUsd) > 1e-6) return null;
|
|
652
652
|
const breakdown = Object.fromEntries(parsedEntries);
|
|
653
|
+
const timing = parseDepositTiming(obj.timing);
|
|
653
654
|
return {
|
|
654
655
|
settlementLayer: obj.settlementLayer,
|
|
655
656
|
expiresAt: obj.expiresAt,
|
|
656
657
|
estimatedFillTimeSeconds: obj.estimatedFillTimeSeconds,
|
|
657
658
|
fees: { total: { usd: totalUsd }, breakdown },
|
|
658
|
-
output
|
|
659
|
+
output,
|
|
660
|
+
...timing ? { timing } : {}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
function parseDepositTiming(raw) {
|
|
664
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
665
|
+
const o = raw;
|
|
666
|
+
const seconds = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
667
|
+
const expectedSeconds = seconds(o.expectedSeconds);
|
|
668
|
+
const softDelaySeconds = seconds(o.softDelaySeconds);
|
|
669
|
+
const escalatedDelaySeconds = seconds(o.escalatedDelaySeconds);
|
|
670
|
+
if (expectedSeconds === void 0 || softDelaySeconds === void 0 || escalatedDelaySeconds === void 0) {
|
|
671
|
+
return void 0;
|
|
672
|
+
}
|
|
673
|
+
return { expectedSeconds, softDelaySeconds, escalatedDelaySeconds };
|
|
674
|
+
}
|
|
675
|
+
function normalizeHistoryTiming(raw) {
|
|
676
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
677
|
+
return raw;
|
|
678
|
+
}
|
|
679
|
+
const { timing: rawTiming, ...row } = raw;
|
|
680
|
+
const timing = parseDepositTiming(rawTiming);
|
|
681
|
+
return {
|
|
682
|
+
...row,
|
|
683
|
+
...timing ? { timing } : {}
|
|
659
684
|
};
|
|
660
685
|
}
|
|
661
686
|
function parseQuoteOutput(raw) {
|
|
@@ -711,6 +736,31 @@ function resolveAppFeeBps(appFees, sourceChainCaip2, sourceToken) {
|
|
|
711
736
|
const bps = override ?? appFees?.feeBps ?? 0;
|
|
712
737
|
return bps > 0 ? bps : 0;
|
|
713
738
|
}
|
|
739
|
+
function isRawAmount(value) {
|
|
740
|
+
return typeof value === "string" && /^\d+$/.test(value);
|
|
741
|
+
}
|
|
742
|
+
function isDepositPolicyRefusal(value) {
|
|
743
|
+
return typeof value === "object" && value !== null && "policyRefusal" in value;
|
|
744
|
+
}
|
|
745
|
+
var DEPOSIT_POLICY_REFUSAL_CODES = [
|
|
746
|
+
"TOKEN-3",
|
|
747
|
+
"BALANCE-3",
|
|
748
|
+
"BALANCE-4"
|
|
749
|
+
];
|
|
750
|
+
var DEPOSIT_PREFLIGHT_TIMEOUT_MS = 1e4;
|
|
751
|
+
async function readPolicyRefusal(response) {
|
|
752
|
+
if (response.status !== 422) return null;
|
|
753
|
+
try {
|
|
754
|
+
const body = await response.json();
|
|
755
|
+
if (!body || typeof body.code !== "string" || !DEPOSIT_POLICY_REFUSAL_CODES.includes(body.code)) {
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
const message = typeof body.error === "string" && body.error !== "" ? body.error : "This deposit is not accepted.";
|
|
759
|
+
return { policyRefusal: { code: body.code, message } };
|
|
760
|
+
} catch {
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
714
764
|
var PermitDepositSubmitError = class extends Error {
|
|
715
765
|
constructor(message, safeToTransfer) {
|
|
716
766
|
super(message);
|
|
@@ -737,29 +787,60 @@ function createDepositService(baseUrl, options) {
|
|
|
737
787
|
if (value.length <= 20) return value;
|
|
738
788
|
return `${value.slice(0, 10)}...${value.slice(-8)}`;
|
|
739
789
|
}
|
|
790
|
+
function withPreflightTimeout(label, fallback, run) {
|
|
791
|
+
const controller = new AbortController();
|
|
792
|
+
return new Promise((resolve) => {
|
|
793
|
+
const timer = setTimeout(() => {
|
|
794
|
+
debugLog(debug, scope, `${label}:timeout`, {
|
|
795
|
+
timeoutMs: DEPOSIT_PREFLIGHT_TIMEOUT_MS
|
|
796
|
+
});
|
|
797
|
+
controller.abort();
|
|
798
|
+
resolve(fallback);
|
|
799
|
+
}, DEPOSIT_PREFLIGHT_TIMEOUT_MS);
|
|
800
|
+
run(controller.signal).then(
|
|
801
|
+
(value) => {
|
|
802
|
+
clearTimeout(timer);
|
|
803
|
+
resolve(value);
|
|
804
|
+
},
|
|
805
|
+
() => {
|
|
806
|
+
clearTimeout(timer);
|
|
807
|
+
resolve(fallback);
|
|
808
|
+
}
|
|
809
|
+
);
|
|
810
|
+
});
|
|
811
|
+
}
|
|
740
812
|
const PORTFOLIO_CACHE_TTL_MS = 3e4;
|
|
741
813
|
const portfolioCache = /* @__PURE__ */ new Map();
|
|
742
814
|
let topTokensPromise = null;
|
|
743
815
|
let chainsPromise = null;
|
|
744
|
-
function
|
|
745
|
-
if (typeof value !== "object" || value === null
|
|
816
|
+
function parseDepositOptions(value) {
|
|
817
|
+
if (typeof value !== "object" || value === null) return null;
|
|
818
|
+
const { restricted, tokens: rawTokens } = value;
|
|
819
|
+
if (typeof restricted !== "boolean") return null;
|
|
820
|
+
if (typeof rawTokens !== "object" || rawTokens === null || Array.isArray(rawTokens)) {
|
|
746
821
|
return null;
|
|
747
822
|
}
|
|
748
|
-
const
|
|
749
|
-
for (const [chainKey, list] of Object.entries(
|
|
823
|
+
const tokens = {};
|
|
824
|
+
for (const [chainKey, list] of Object.entries(rawTokens)) {
|
|
750
825
|
if (!Array.isArray(list)) return null;
|
|
751
|
-
const
|
|
826
|
+
const parsed = [];
|
|
752
827
|
for (const entry of list) {
|
|
753
828
|
if (typeof entry !== "object" || entry === null) return null;
|
|
754
|
-
const { symbol, address, decimals } = entry;
|
|
829
|
+
const { symbol, address, decimals, minAmount, maxAmount } = entry;
|
|
755
830
|
if (typeof symbol !== "string" || typeof address !== "string" || typeof decimals !== "number") {
|
|
756
831
|
return null;
|
|
757
832
|
}
|
|
758
|
-
|
|
833
|
+
parsed.push({
|
|
834
|
+
symbol,
|
|
835
|
+
address,
|
|
836
|
+
decimals,
|
|
837
|
+
...isRawAmount(minAmount) && { minAmount },
|
|
838
|
+
...isRawAmount(maxAmount) && { maxAmount }
|
|
839
|
+
});
|
|
759
840
|
}
|
|
760
|
-
|
|
841
|
+
tokens[chainKey] = parsed;
|
|
761
842
|
}
|
|
762
|
-
return
|
|
843
|
+
return { restricted, tokens };
|
|
763
844
|
}
|
|
764
845
|
async function requestTopTokens() {
|
|
765
846
|
try {
|
|
@@ -771,21 +852,21 @@ function createDepositService(baseUrl, options) {
|
|
|
771
852
|
debugLog(debug, scope, "getTopTokens:unavailable", {
|
|
772
853
|
status: response.status
|
|
773
854
|
});
|
|
774
|
-
return
|
|
855
|
+
return { ok: false };
|
|
775
856
|
}
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
if (!tokens) {
|
|
857
|
+
const options2 = parseDepositOptions(await response.json());
|
|
858
|
+
if (!options2) {
|
|
779
859
|
debugLog(debug, scope, "getTopTokens:malformed");
|
|
780
|
-
return
|
|
860
|
+
return { ok: false };
|
|
781
861
|
}
|
|
782
862
|
debugLog(debug, scope, "getTopTokens:success", {
|
|
783
|
-
chains: Object.keys(tokens).length
|
|
863
|
+
chains: Object.keys(options2.tokens).length,
|
|
864
|
+
restricted: options2.restricted
|
|
784
865
|
});
|
|
785
|
-
return
|
|
866
|
+
return { ok: true, options: options2 };
|
|
786
867
|
} catch (error) {
|
|
787
868
|
debugError(debug, scope, "getTopTokens:error", error);
|
|
788
|
-
return
|
|
869
|
+
return { ok: false };
|
|
789
870
|
}
|
|
790
871
|
}
|
|
791
872
|
async function requestChains() {
|
|
@@ -1197,7 +1278,7 @@ function createDepositService(baseUrl, options) {
|
|
|
1197
1278
|
);
|
|
1198
1279
|
}
|
|
1199
1280
|
const data = await response.json();
|
|
1200
|
-
const deposits = Array.isArray(data?.deposits) ? data.deposits : [];
|
|
1281
|
+
const deposits = Array.isArray(data?.deposits) ? data.deposits.map(normalizeHistoryTiming) : [];
|
|
1201
1282
|
const nextCursor = data?.nextCursor ?? data?.next_cursor ?? null;
|
|
1202
1283
|
debugLog(debug, scope, "fetchDepositHistory:success", {
|
|
1203
1284
|
recipient: shortRef(params.recipient),
|
|
@@ -1278,36 +1359,46 @@ function createDepositService(baseUrl, options) {
|
|
|
1278
1359
|
sourceToken: params.sourceToken,
|
|
1279
1360
|
amount: params.amount
|
|
1280
1361
|
});
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
debugLog(debug, scope, "getQuotePreview:unavailable", {
|
|
1290
|
-
status: response.status
|
|
1362
|
+
return withPreflightTimeout("getQuotePreview", null, async (signal) => {
|
|
1363
|
+
try {
|
|
1364
|
+
const response = await fetch(url, {
|
|
1365
|
+
method: "POST",
|
|
1366
|
+
headers: serviceHeaders(),
|
|
1367
|
+
body: JSON.stringify(body),
|
|
1368
|
+
cache: "no-store",
|
|
1369
|
+
signal
|
|
1291
1370
|
});
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1371
|
+
if (!response.ok) {
|
|
1372
|
+
const refusal = await readPolicyRefusal(response);
|
|
1373
|
+
if (refusal) {
|
|
1374
|
+
debugLog(debug, scope, "getQuotePreview:refused", {
|
|
1375
|
+
code: refusal.policyRefusal.code
|
|
1376
|
+
});
|
|
1377
|
+
return refusal;
|
|
1378
|
+
}
|
|
1379
|
+
debugLog(debug, scope, "getQuotePreview:unavailable", {
|
|
1380
|
+
status: response.status
|
|
1381
|
+
});
|
|
1382
|
+
return null;
|
|
1383
|
+
}
|
|
1384
|
+
const quote = parseQuotePreview(await response.json());
|
|
1385
|
+
if (!quote) {
|
|
1386
|
+
debugLog(debug, scope, "getQuotePreview:unavailable", {
|
|
1387
|
+
status: response.status,
|
|
1388
|
+
reason: "malformed"
|
|
1389
|
+
});
|
|
1390
|
+
return null;
|
|
1391
|
+
}
|
|
1392
|
+
debugLog(debug, scope, "getQuotePreview:success", {
|
|
1393
|
+
settlementLayer: quote.settlementLayer,
|
|
1394
|
+
totalUsd: quote.fees.total.usd
|
|
1299
1395
|
});
|
|
1396
|
+
return quote;
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
debugError(debug, scope, "getQuotePreview:error", error);
|
|
1300
1399
|
return null;
|
|
1301
1400
|
}
|
|
1302
|
-
|
|
1303
|
-
settlementLayer: quote.settlementLayer,
|
|
1304
|
-
totalUsd: quote.fees.total.usd
|
|
1305
|
-
});
|
|
1306
|
-
return quote;
|
|
1307
|
-
} catch (error) {
|
|
1308
|
-
debugError(debug, scope, "getQuotePreview:error", error);
|
|
1309
|
-
return null;
|
|
1310
|
-
}
|
|
1401
|
+
});
|
|
1311
1402
|
},
|
|
1312
1403
|
async preparePermitDeposit(params) {
|
|
1313
1404
|
const url = apiUrl("/deposits/permit/prepare");
|
|
@@ -1318,88 +1409,98 @@ function createDepositService(baseUrl, options) {
|
|
|
1318
1409
|
token: params.token,
|
|
1319
1410
|
amount: params.amount
|
|
1320
1411
|
});
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
debugLog(debug, scope, "preparePermitDeposit:unavailable", {
|
|
1330
|
-
status: response.status
|
|
1331
|
-
});
|
|
1332
|
-
return null;
|
|
1333
|
-
}
|
|
1334
|
-
const data = await response.json();
|
|
1335
|
-
if (!data || typeof data.available !== "boolean") {
|
|
1336
|
-
debugLog(debug, scope, "preparePermitDeposit:unavailable", {
|
|
1337
|
-
status: response.status,
|
|
1338
|
-
reason: "malformed"
|
|
1412
|
+
return withPreflightTimeout("preparePermitDeposit", null, async (signal) => {
|
|
1413
|
+
try {
|
|
1414
|
+
const response = await fetch(url, {
|
|
1415
|
+
method: "POST",
|
|
1416
|
+
headers: serviceHeaders(),
|
|
1417
|
+
body: JSON.stringify({ ...params, kind: "auto" }),
|
|
1418
|
+
cache: "no-store",
|
|
1419
|
+
signal
|
|
1339
1420
|
});
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1421
|
+
if (!response.ok) {
|
|
1422
|
+
const refusal = await readPolicyRefusal(response);
|
|
1423
|
+
if (refusal) {
|
|
1424
|
+
debugLog(debug, scope, "preparePermitDeposit:refused", {
|
|
1425
|
+
code: refusal.policyRefusal.code
|
|
1426
|
+
});
|
|
1427
|
+
return refusal;
|
|
1428
|
+
}
|
|
1429
|
+
debugLog(debug, scope, "preparePermitDeposit:unavailable", {
|
|
1430
|
+
status: response.status
|
|
1431
|
+
});
|
|
1345
1432
|
return null;
|
|
1346
1433
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
reason,
|
|
1360
|
-
...parsedApproval && { approval: parsedApproval }
|
|
1361
|
-
};
|
|
1362
|
-
}
|
|
1363
|
-
const authorization = data.authorization;
|
|
1364
|
-
if (!authorization || typeof authorization.typedData !== "object" || authorization.typedData === null || typeof authorization.nonce !== "string") {
|
|
1365
|
-
return null;
|
|
1366
|
-
}
|
|
1367
|
-
if (authorization.kind === "erc3009" && typeof authorization.validAfter === "string" && typeof authorization.validBefore === "string") {
|
|
1368
|
-
debugLog(debug, scope, "preparePermitDeposit:success", {
|
|
1369
|
-
kind: authorization.kind,
|
|
1370
|
-
nonce: authorization.nonce
|
|
1371
|
-
});
|
|
1372
|
-
return {
|
|
1373
|
-
available: true,
|
|
1374
|
-
authorization: {
|
|
1375
|
-
kind: "erc3009",
|
|
1376
|
-
typedData: authorization.typedData,
|
|
1377
|
-
nonce: authorization.nonce,
|
|
1378
|
-
validAfter: authorization.validAfter,
|
|
1379
|
-
validBefore: authorization.validBefore
|
|
1434
|
+
const data = await response.json();
|
|
1435
|
+
if (!data || typeof data.available !== "boolean") {
|
|
1436
|
+
debugLog(debug, scope, "preparePermitDeposit:unavailable", {
|
|
1437
|
+
status: response.status,
|
|
1438
|
+
reason: "malformed"
|
|
1439
|
+
});
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
if (data.available === false) {
|
|
1443
|
+
const reason = data.reason;
|
|
1444
|
+
if (reason !== "permit2-approval-required" && reason !== "unsupported-token" && reason !== "unsupported-owner") {
|
|
1445
|
+
return null;
|
|
1380
1446
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1447
|
+
debugLog(debug, scope, "preparePermitDeposit:unavailable", {
|
|
1448
|
+
status: response.status,
|
|
1449
|
+
reason
|
|
1450
|
+
});
|
|
1451
|
+
const approval = data.approval;
|
|
1452
|
+
const parsedApproval = approval && typeof approval.token === "string" && typeof approval.spender === "string" && typeof approval.minimumAmount === "string" ? {
|
|
1453
|
+
token: approval.token,
|
|
1454
|
+
spender: approval.spender,
|
|
1455
|
+
minimumAmount: approval.minimumAmount
|
|
1456
|
+
} : void 0;
|
|
1457
|
+
return {
|
|
1458
|
+
available: false,
|
|
1459
|
+
reason,
|
|
1460
|
+
...parsedApproval && { approval: parsedApproval }
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
const authorization = data.authorization;
|
|
1464
|
+
if (!authorization || typeof authorization.typedData !== "object" || authorization.typedData === null || typeof authorization.nonce !== "string") {
|
|
1465
|
+
return null;
|
|
1466
|
+
}
|
|
1467
|
+
if (authorization.kind === "erc3009" && typeof authorization.validAfter === "string" && typeof authorization.validBefore === "string") {
|
|
1468
|
+
debugLog(debug, scope, "preparePermitDeposit:success", {
|
|
1391
1469
|
kind: authorization.kind,
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1470
|
+
nonce: authorization.nonce
|
|
1471
|
+
});
|
|
1472
|
+
return {
|
|
1473
|
+
available: true,
|
|
1474
|
+
authorization: {
|
|
1475
|
+
kind: "erc3009",
|
|
1476
|
+
typedData: authorization.typedData,
|
|
1477
|
+
nonce: authorization.nonce,
|
|
1478
|
+
validAfter: authorization.validAfter,
|
|
1479
|
+
validBefore: authorization.validBefore
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
if ((authorization.kind === "erc2612" || authorization.kind === "permit2") && typeof authorization.deadline === "string") {
|
|
1484
|
+
debugLog(debug, scope, "preparePermitDeposit:success", {
|
|
1485
|
+
kind: authorization.kind,
|
|
1486
|
+
nonce: authorization.nonce
|
|
1487
|
+
});
|
|
1488
|
+
return {
|
|
1489
|
+
available: true,
|
|
1490
|
+
authorization: {
|
|
1491
|
+
kind: authorization.kind,
|
|
1492
|
+
typedData: authorization.typedData,
|
|
1493
|
+
nonce: authorization.nonce,
|
|
1494
|
+
deadline: authorization.deadline
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
return null;
|
|
1499
|
+
} catch (error) {
|
|
1500
|
+
debugError(debug, scope, "preparePermitDeposit:error", error);
|
|
1501
|
+
return null;
|
|
1397
1502
|
}
|
|
1398
|
-
|
|
1399
|
-
} catch (error) {
|
|
1400
|
-
debugError(debug, scope, "preparePermitDeposit:error", error);
|
|
1401
|
-
return null;
|
|
1402
|
-
}
|
|
1503
|
+
});
|
|
1403
1504
|
},
|
|
1404
1505
|
async submitPermitDeposit(params) {
|
|
1405
1506
|
const url = apiUrl("/deposits/permit");
|
|
@@ -1708,9 +1809,11 @@ function createDepositService(baseUrl, options) {
|
|
|
1708
1809
|
},
|
|
1709
1810
|
getTopTokens() {
|
|
1710
1811
|
if (!topTokensPromise) {
|
|
1711
|
-
const attempt = requestTopTokens().then((
|
|
1712
|
-
if (!
|
|
1713
|
-
|
|
1812
|
+
const attempt = requestTopTokens().then((result) => {
|
|
1813
|
+
if (!result.ok && topTokensPromise === attempt) {
|
|
1814
|
+
topTokensPromise = null;
|
|
1815
|
+
}
|
|
1816
|
+
return result;
|
|
1714
1817
|
});
|
|
1715
1818
|
topTokensPromise = attempt;
|
|
1716
1819
|
}
|
|
@@ -1939,6 +2042,7 @@ function useStableRpcUrls(rpcUrls) {
|
|
|
1939
2042
|
// src/core/chain-catalog-context.tsx
|
|
1940
2043
|
import {
|
|
1941
2044
|
createContext as createContext2,
|
|
2045
|
+
useCallback as useCallback2,
|
|
1942
2046
|
useContext as useContext2,
|
|
1943
2047
|
useEffect as useEffect2,
|
|
1944
2048
|
useMemo as useMemo2,
|
|
@@ -1946,6 +2050,59 @@ import {
|
|
|
1946
2050
|
useSyncExternalStore
|
|
1947
2051
|
} from "react";
|
|
1948
2052
|
|
|
2053
|
+
// src/core/deposit-bounds.ts
|
|
2054
|
+
import { formatUnits } from "viem";
|
|
2055
|
+
function findListed(options, chain, token) {
|
|
2056
|
+
const normalize2 = (value) => chain === "solana" ? value : value.toLowerCase();
|
|
2057
|
+
return options.tokens[String(chain)]?.find(
|
|
2058
|
+
(listed) => normalize2(listed.address) === normalize2(token)
|
|
2059
|
+
);
|
|
2060
|
+
}
|
|
2061
|
+
function isExcludedByRestrictedList(options, chain, token) {
|
|
2062
|
+
return Boolean(options?.restricted && !findListed(options, chain, token));
|
|
2063
|
+
}
|
|
2064
|
+
function findDepositBounds(options, chain, token) {
|
|
2065
|
+
if (!options?.restricted) return void 0;
|
|
2066
|
+
const entry = findListed(options, chain, token);
|
|
2067
|
+
if (!entry || entry.minAmount === void 0 && entry.maxAmount === void 0) {
|
|
2068
|
+
return void 0;
|
|
2069
|
+
}
|
|
2070
|
+
return {
|
|
2071
|
+
symbol: entry.symbol,
|
|
2072
|
+
decimals: entry.decimals,
|
|
2073
|
+
...entry.minAmount !== void 0 && { minAmount: BigInt(entry.minAmount) },
|
|
2074
|
+
...entry.maxAmount !== void 0 && { maxAmount: BigInt(entry.maxAmount) }
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
function boundMessage(bounds, bound) {
|
|
2078
|
+
const raw = bounds?.[bound];
|
|
2079
|
+
if (!bounds || raw === void 0) return null;
|
|
2080
|
+
const amount = new Intl.NumberFormat("en-US", {
|
|
2081
|
+
maximumFractionDigits: Math.min(bounds.decimals, 20)
|
|
2082
|
+
}).format(Number(formatUnits(raw, bounds.decimals)));
|
|
2083
|
+
return `${bound === "minAmount" ? "Minimum" : "Maximum"} deposit is ${amount} ${bounds.symbol}`;
|
|
2084
|
+
}
|
|
2085
|
+
function depositBoundsViolation(bounds, units) {
|
|
2086
|
+
if (!bounds || units === null || units <= 0n) return null;
|
|
2087
|
+
if (bounds.minAmount !== void 0 && units < bounds.minAmount) {
|
|
2088
|
+
return boundMessage(bounds, "minAmount");
|
|
2089
|
+
}
|
|
2090
|
+
if (bounds.maxAmount !== void 0 && units > bounds.maxAmount) {
|
|
2091
|
+
return boundMessage(bounds, "maxAmount");
|
|
2092
|
+
}
|
|
2093
|
+
return null;
|
|
2094
|
+
}
|
|
2095
|
+
function describePolicyRefusal(refusal, bounds) {
|
|
2096
|
+
switch (refusal.code) {
|
|
2097
|
+
case "BALANCE-4":
|
|
2098
|
+
return boundMessage(bounds, "minAmount") ?? "This amount is below the minimum deposit";
|
|
2099
|
+
case "BALANCE-3":
|
|
2100
|
+
return boundMessage(bounds, "maxAmount") ?? "This amount is above the maximum deposit";
|
|
2101
|
+
default:
|
|
2102
|
+
return "This token isn't accepted for deposits";
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
1949
2106
|
// src/core/public-client.ts
|
|
1950
2107
|
import { createPublicClient, http } from "viem";
|
|
1951
2108
|
var clientCache = /* @__PURE__ */ new Map();
|
|
@@ -2110,6 +2267,9 @@ var EMPTY = {
|
|
|
2110
2267
|
viemChains: null,
|
|
2111
2268
|
isLoading: true,
|
|
2112
2269
|
shortlist: null,
|
|
2270
|
+
depositOptions: { status: "loading" },
|
|
2271
|
+
retryDepositOptions: () => {
|
|
2272
|
+
},
|
|
2113
2273
|
clientCapableChainIds: null
|
|
2114
2274
|
};
|
|
2115
2275
|
var ChainCatalogContext = createContext2(EMPTY);
|
|
@@ -2267,6 +2427,48 @@ function useTargetTokens() {
|
|
|
2267
2427
|
};
|
|
2268
2428
|
}, [catalog, shortlist]);
|
|
2269
2429
|
}
|
|
2430
|
+
function createDepositOffer(state, retry) {
|
|
2431
|
+
const options = state.status === "ready" ? state.options : null;
|
|
2432
|
+
const restricted = options?.restricted ?? false;
|
|
2433
|
+
const listed = (chain) => options?.tokens[String(chain)];
|
|
2434
|
+
const isChainOffered = (chain) => {
|
|
2435
|
+
if (!options) return false;
|
|
2436
|
+
const entry = listed(chain);
|
|
2437
|
+
return restricted ? (entry?.length ?? 0) > 0 : entry?.length !== 0;
|
|
2438
|
+
};
|
|
2439
|
+
return {
|
|
2440
|
+
status: state.status,
|
|
2441
|
+
restricted,
|
|
2442
|
+
retry,
|
|
2443
|
+
tokensFor: (chain) => {
|
|
2444
|
+
if (!options) return [];
|
|
2445
|
+
const entry = listed(chain);
|
|
2446
|
+
return restricted ? entry ?? [] : entry;
|
|
2447
|
+
},
|
|
2448
|
+
isChainOffered
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
function useDepositOffer() {
|
|
2452
|
+
const { depositOptions, retryDepositOptions } = useChainCatalog();
|
|
2453
|
+
return useMemo2(
|
|
2454
|
+
() => createDepositOffer(depositOptions, retryDepositOptions),
|
|
2455
|
+
[depositOptions, retryDepositOptions]
|
|
2456
|
+
);
|
|
2457
|
+
}
|
|
2458
|
+
function useDepositBounds() {
|
|
2459
|
+
const { depositOptions } = useChainCatalog();
|
|
2460
|
+
return useMemo2(() => {
|
|
2461
|
+
const options = depositOptions.status === "ready" ? depositOptions.options : null;
|
|
2462
|
+
return (chain, token) => findDepositBounds(options, chain, token);
|
|
2463
|
+
}, [depositOptions]);
|
|
2464
|
+
}
|
|
2465
|
+
function useRestrictedListExclusion() {
|
|
2466
|
+
const { depositOptions } = useChainCatalog();
|
|
2467
|
+
return useMemo2(() => {
|
|
2468
|
+
const options = depositOptions.status === "ready" ? depositOptions.options : null;
|
|
2469
|
+
return (chain, token) => isExcludedByRestrictedList(options, chain, token);
|
|
2470
|
+
}, [depositOptions]);
|
|
2471
|
+
}
|
|
2270
2472
|
function useIsDepositChain() {
|
|
2271
2473
|
const ids = useDepositChainIds();
|
|
2272
2474
|
const { clientCapableChainIds } = useChainCatalog();
|
|
@@ -2359,19 +2561,36 @@ function ChainCatalogProvider({
|
|
|
2359
2561
|
const chains = toViemChains(catalog, overrides, viemChains);
|
|
2360
2562
|
if (chains.length > 0) setRuntimeChains(chains);
|
|
2361
2563
|
}, [catalog, viemChains, overrides]);
|
|
2362
|
-
const [
|
|
2564
|
+
const [depositOptions, setDepositOptions] = useState({
|
|
2565
|
+
status: "loading"
|
|
2566
|
+
});
|
|
2567
|
+
const [depositOptionsAttempt, setDepositOptionsAttempt] = useState(0);
|
|
2363
2568
|
useEffect2(() => {
|
|
2364
2569
|
let active = true;
|
|
2365
|
-
|
|
2366
|
-
service.getTopTokens().then(
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2570
|
+
setDepositOptions({ status: "loading" });
|
|
2571
|
+
service.getTopTokens().then(
|
|
2572
|
+
(result) => {
|
|
2573
|
+
if (!active) return;
|
|
2574
|
+
if (!result?.ok) {
|
|
2575
|
+
setDepositOptions({ status: "error" });
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
publishShortlistTokens(result.options.tokens);
|
|
2579
|
+
setDepositOptions({ status: "ready", options: result.options });
|
|
2580
|
+
},
|
|
2581
|
+
() => {
|
|
2582
|
+
if (active) setDepositOptions({ status: "error" });
|
|
2583
|
+
}
|
|
2584
|
+
);
|
|
2371
2585
|
return () => {
|
|
2372
2586
|
active = false;
|
|
2373
2587
|
};
|
|
2374
|
-
}, [service]);
|
|
2588
|
+
}, [service, depositOptionsAttempt]);
|
|
2589
|
+
const retryDepositOptions = useCallback2(
|
|
2590
|
+
() => setDepositOptionsAttempt((attempt) => attempt + 1),
|
|
2591
|
+
[]
|
|
2592
|
+
);
|
|
2593
|
+
const shortlist = depositOptions.status === "ready" ? depositOptions.options.tokens : null;
|
|
2375
2594
|
const clientCapableChainIds = useMemo2(() => {
|
|
2376
2595
|
if (!catalog) return null;
|
|
2377
2596
|
return new Set(
|
|
@@ -2384,9 +2603,19 @@ function ChainCatalogProvider({
|
|
|
2384
2603
|
viemChains,
|
|
2385
2604
|
isLoading,
|
|
2386
2605
|
shortlist,
|
|
2606
|
+
depositOptions,
|
|
2607
|
+
retryDepositOptions,
|
|
2387
2608
|
clientCapableChainIds
|
|
2388
2609
|
}),
|
|
2389
|
-
[
|
|
2610
|
+
[
|
|
2611
|
+
catalog,
|
|
2612
|
+
viemChains,
|
|
2613
|
+
isLoading,
|
|
2614
|
+
shortlist,
|
|
2615
|
+
depositOptions,
|
|
2616
|
+
retryDepositOptions,
|
|
2617
|
+
clientCapableChainIds
|
|
2618
|
+
]
|
|
2390
2619
|
);
|
|
2391
2620
|
return /* @__PURE__ */ jsx3(ChainCatalogContext.Provider, { value, children });
|
|
2392
2621
|
}
|
|
@@ -2404,7 +2633,7 @@ function useLatestRef(value) {
|
|
|
2404
2633
|
// src/core/analytics.tsx
|
|
2405
2634
|
import {
|
|
2406
2635
|
createContext as createContext3,
|
|
2407
|
-
useCallback as
|
|
2636
|
+
useCallback as useCallback3,
|
|
2408
2637
|
useContext as useContext3,
|
|
2409
2638
|
useEffect as useEffect4,
|
|
2410
2639
|
useMemo as useMemo3,
|
|
@@ -2792,7 +3021,7 @@ function AnalyticsSession(props) {
|
|
|
2792
3021
|
nowRef.current = props.now ?? (() => /* @__PURE__ */ new Date());
|
|
2793
3022
|
const modal = props.modal;
|
|
2794
3023
|
const reportedFailuresRef = useRef3(/* @__PURE__ */ new Set());
|
|
2795
|
-
const emitFailure =
|
|
3024
|
+
const emitFailure = useCallback3(
|
|
2796
3025
|
({ reason, status, droppedEvents }) => {
|
|
2797
3026
|
if (reportedFailuresRef.current.has(reason)) return;
|
|
2798
3027
|
reportedFailuresRef.current.add(reason);
|
|
@@ -2842,7 +3071,7 @@ function AnalyticsSession(props) {
|
|
|
2842
3071
|
ingestRef.current = createIngest();
|
|
2843
3072
|
}
|
|
2844
3073
|
const pendingDisposalRef = useRef3(null);
|
|
2845
|
-
const enqueue =
|
|
3074
|
+
const enqueue = useCallback3(
|
|
2846
3075
|
(event) => {
|
|
2847
3076
|
try {
|
|
2848
3077
|
ingestRef.current?.transport?.enqueue(
|
|
@@ -2877,7 +3106,7 @@ function AnalyticsSession(props) {
|
|
|
2877
3106
|
}, 0);
|
|
2878
3107
|
};
|
|
2879
3108
|
}, [emitFailure]);
|
|
2880
|
-
const emitDeposit =
|
|
3109
|
+
const emitDeposit = useCallback3(
|
|
2881
3110
|
(...[payload, fundingMethod]) => {
|
|
2882
3111
|
if (modal !== "deposit") return;
|
|
2883
3112
|
const event = {
|
|
@@ -2896,7 +3125,7 @@ function AnalyticsSession(props) {
|
|
|
2896
3125
|
},
|
|
2897
3126
|
[enqueue, modal, nowRef, onEventRef]
|
|
2898
3127
|
);
|
|
2899
|
-
const emitWithdraw =
|
|
3128
|
+
const emitWithdraw = useCallback3(
|
|
2900
3129
|
(payload) => {
|
|
2901
3130
|
if (modal !== "withdraw") return;
|
|
2902
3131
|
const event = {
|
|
@@ -2912,7 +3141,7 @@ function AnalyticsSession(props) {
|
|
|
2912
3141
|
},
|
|
2913
3142
|
[enqueue, modal, nowRef, onEventRef]
|
|
2914
3143
|
);
|
|
2915
|
-
const emitClaim =
|
|
3144
|
+
const emitClaim = useCallback3(
|
|
2916
3145
|
(payload) => {
|
|
2917
3146
|
if (modal !== "claim") return;
|
|
2918
3147
|
const event = {
|
|
@@ -2986,17 +3215,38 @@ function recoveryFailureCause(sourceChainName) {
|
|
|
2986
3215
|
}
|
|
2987
3216
|
|
|
2988
3217
|
// src/core/history-status.ts
|
|
2989
|
-
|
|
3218
|
+
var UNKNOWN = ["pending", false, []];
|
|
3219
|
+
var STATUS_FACTS = [
|
|
3220
|
+
// `delayed` is held up but still coming on the corridor's schedule.
|
|
3221
|
+
["pending", true, ["pending", "delayed"]],
|
|
3222
|
+
// Nothing emits `submitted` or `bridging`; they are here so the badge and
|
|
3223
|
+
// the arrival line cannot diverge should a row ever carry them.
|
|
3224
|
+
["processing", true, ["processing", "submitted", "bridging"]],
|
|
3225
|
+
["completed", false, ["completed", "complete", "bridge-complete", "processed"]],
|
|
3226
|
+
// `rejected` is recoverable upstream exactly like `failed`, so it must not
|
|
3227
|
+
// read as Pending — a row offering recovery would otherwise claim to still
|
|
3228
|
+
// be on its way.
|
|
3229
|
+
["failed", false, ["failed", "error", "bridge-failed", "rejected"]],
|
|
3230
|
+
// Where a recovered deposit lands. Terminal and not recoverable again.
|
|
3231
|
+
["refunded", false, ["refunded"]],
|
|
3232
|
+
// These read Pending but are not arriving: the deposit is bound for a refund
|
|
3233
|
+
// or for manual reconciliation rather than for the recipient, so an arrival
|
|
3234
|
+
// expectation would be a claim about a corridor it never enters.
|
|
3235
|
+
["pending", false, ["ignored", "expecting_refund", "reconciliation_required"]]
|
|
3236
|
+
];
|
|
3237
|
+
function lookup(raw) {
|
|
2990
3238
|
const lower = raw.toLowerCase();
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3239
|
+
return STATUS_FACTS.find((facts) => facts[2].includes(lower)) ?? UNKNOWN;
|
|
3240
|
+
}
|
|
3241
|
+
function normalizeStatus(raw, isSpam) {
|
|
3242
|
+
const [badge] = lookup(raw);
|
|
3243
|
+
return isSpam && badge === "failed" ? "spam" : badge;
|
|
3244
|
+
}
|
|
3245
|
+
function isInFlight(status) {
|
|
3246
|
+
return status === "pending" || status === "processing";
|
|
3247
|
+
}
|
|
3248
|
+
function expectsArrival(raw) {
|
|
3249
|
+
return lookup(raw)[1];
|
|
3000
3250
|
}
|
|
3001
3251
|
var STATUS_LABEL = {
|
|
3002
3252
|
pending: "Pending",
|
|
@@ -3016,20 +3266,37 @@ function filterVisibleDeposits(deposits) {
|
|
|
3016
3266
|
}
|
|
3017
3267
|
|
|
3018
3268
|
// src/core/history-badge.ts
|
|
3019
|
-
|
|
3269
|
+
var RECOVERY_INDICATOR_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
3270
|
+
function deriveHistoryBadgeState(deposits, {
|
|
3271
|
+
recipient,
|
|
3272
|
+
canRecover,
|
|
3273
|
+
evaluatedAt = Date.now()
|
|
3274
|
+
}) {
|
|
3020
3275
|
const visible = filterVisibleDeposits(deposits);
|
|
3021
3276
|
if (canRecover) {
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
)
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3277
|
+
let count = 0;
|
|
3278
|
+
let nextExpiryAt = null;
|
|
3279
|
+
for (const row of visible) {
|
|
3280
|
+
if (toRecoverableDeposit(row, { recipient }) === null) continue;
|
|
3281
|
+
const expiryAt = Date.parse(row.createdAt ?? "") + RECOVERY_INDICATOR_WINDOW_MS;
|
|
3282
|
+
if (!(evaluatedAt < expiryAt)) continue;
|
|
3283
|
+
count += 1;
|
|
3284
|
+
nextExpiryAt = nextExpiryAt === null ? expiryAt : Math.min(nextExpiryAt, expiryAt);
|
|
3285
|
+
}
|
|
3286
|
+
if (count > 0) {
|
|
3287
|
+
return {
|
|
3288
|
+
badge: { state: "needs-you", count },
|
|
3289
|
+
nextExpiryAt
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
const inProgress = visible.filter(
|
|
3294
|
+
(row) => isInFlight(normalizeStatus(row.status, row.isSpam))
|
|
3295
|
+
).length;
|
|
3296
|
+
return {
|
|
3297
|
+
badge: inProgress > 0 ? { state: "progress", count: inProgress } : null,
|
|
3298
|
+
nextExpiryAt: null
|
|
3299
|
+
};
|
|
3033
3300
|
}
|
|
3034
3301
|
function historyBadgeAriaLabel(badge) {
|
|
3035
3302
|
if (!badge) return "Deposit history";
|
|
@@ -3069,11 +3336,11 @@ function HistoryEntryButton({
|
|
|
3069
3336
|
}
|
|
3070
3337
|
|
|
3071
3338
|
// src/components/history/HistoryOverlay.tsx
|
|
3072
|
-
import { useCallback as
|
|
3339
|
+
import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo6, useRef as useRef7, useState as useState8 } from "react";
|
|
3073
3340
|
|
|
3074
3341
|
// src/components/history/DepositHistoryPanel.tsx
|
|
3075
3342
|
import {
|
|
3076
|
-
useCallback as
|
|
3343
|
+
useCallback as useCallback4,
|
|
3077
3344
|
useEffect as useEffect5,
|
|
3078
3345
|
useId,
|
|
3079
3346
|
useMemo as useMemo4,
|
|
@@ -3351,8 +3618,83 @@ function TokenIcon({ symbol, className, fallback }) {
|
|
|
3351
3618
|
}
|
|
3352
3619
|
TokenIcon.displayName = "TokenIcon";
|
|
3353
3620
|
|
|
3621
|
+
// src/core/deposit-timing.ts
|
|
3622
|
+
var FALLBACK_SOFT_DELAY_MS = {
|
|
3623
|
+
confirming: 90 * 1e3,
|
|
3624
|
+
received: 90 * 1e3,
|
|
3625
|
+
bridging: 4 * 60 * 1e3
|
|
3626
|
+
};
|
|
3627
|
+
var FALLBACK_ESCALATED_DELAY_MS = 10 * 60 * 1e3;
|
|
3628
|
+
var MIN_SOFT_DELAY_MS = 90 * 1e3;
|
|
3629
|
+
function formatExpectedDuration(seconds) {
|
|
3630
|
+
if (!Number.isFinite(seconds) || seconds <= 0) return "a few seconds";
|
|
3631
|
+
if (seconds < 60) {
|
|
3632
|
+
const whole = Math.round(seconds);
|
|
3633
|
+
return `~${whole} second${whole === 1 ? "" : "s"}`;
|
|
3634
|
+
}
|
|
3635
|
+
return `~${Math.max(1, Math.round(seconds / 60))} min`;
|
|
3636
|
+
}
|
|
3637
|
+
function resolveServedThresholds(timing) {
|
|
3638
|
+
const softMs = Math.max(timing.softDelaySeconds * 1e3, MIN_SOFT_DELAY_MS);
|
|
3639
|
+
return {
|
|
3640
|
+
softMs,
|
|
3641
|
+
escalatedMs: Math.max(timing.escalatedDelaySeconds * 1e3, softMs)
|
|
3642
|
+
};
|
|
3643
|
+
}
|
|
3644
|
+
function resolveServedTimingState(timing, totalElapsedMs) {
|
|
3645
|
+
const { softMs, escalatedMs } = resolveServedThresholds(timing);
|
|
3646
|
+
if (totalElapsedMs >= escalatedMs) return "escalated";
|
|
3647
|
+
if (totalElapsedMs >= softMs) return "soft";
|
|
3648
|
+
return "expected";
|
|
3649
|
+
}
|
|
3650
|
+
function resolvePendingTimingState({
|
|
3651
|
+
timing,
|
|
3652
|
+
totalElapsedMs,
|
|
3653
|
+
phaseId,
|
|
3654
|
+
phaseElapsedMs
|
|
3655
|
+
}) {
|
|
3656
|
+
if (timing) return resolveServedTimingState(timing, totalElapsedMs);
|
|
3657
|
+
if (totalElapsedMs >= FALLBACK_ESCALATED_DELAY_MS) return "escalated";
|
|
3658
|
+
if (phaseId && phaseElapsedMs >= FALLBACK_SOFT_DELAY_MS[phaseId]) {
|
|
3659
|
+
return "soft";
|
|
3660
|
+
}
|
|
3661
|
+
return "expected";
|
|
3662
|
+
}
|
|
3663
|
+
var STATE_ORDER = {
|
|
3664
|
+
expected: 0,
|
|
3665
|
+
soft: 1,
|
|
3666
|
+
escalated: 2
|
|
3667
|
+
};
|
|
3668
|
+
function advancePendingTimingState(previous, next) {
|
|
3669
|
+
return STATE_ORDER[next] > STATE_ORDER[previous] ? next : previous;
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3672
|
+
// src/core/history-timing.ts
|
|
3673
|
+
function resolveHistoryTiming({
|
|
3674
|
+
status,
|
|
3675
|
+
timing,
|
|
3676
|
+
createdAt,
|
|
3677
|
+
nowMs
|
|
3678
|
+
}) {
|
|
3679
|
+
if (!expectsArrival(status) || !timing || !createdAt) {
|
|
3680
|
+
return void 0;
|
|
3681
|
+
}
|
|
3682
|
+
const createdAtMs = Date.parse(createdAt);
|
|
3683
|
+
if (!Number.isFinite(createdAtMs)) return void 0;
|
|
3684
|
+
const elapsedMs = Math.max(0, nowMs - createdAtMs);
|
|
3685
|
+
const state = resolveServedTimingState(timing, elapsedMs);
|
|
3686
|
+
const { softMs, escalatedMs } = resolveServedThresholds(timing);
|
|
3687
|
+
const nextTransitionAtMs = state === "expected" ? createdAtMs + softMs : state === "soft" ? createdAtMs + escalatedMs : void 0;
|
|
3688
|
+
return {
|
|
3689
|
+
state,
|
|
3690
|
+
expectedDuration: formatExpectedDuration(timing.expectedSeconds),
|
|
3691
|
+
...nextTransitionAtMs !== void 0 ? { nextTransitionAtMs } : {}
|
|
3692
|
+
};
|
|
3693
|
+
}
|
|
3694
|
+
|
|
3354
3695
|
// src/components/history/DepositHistoryPanel.tsx
|
|
3355
3696
|
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3697
|
+
var MAX_TIMER_DELAY_MS = 2147e6;
|
|
3356
3698
|
function shortenHash(hash) {
|
|
3357
3699
|
if (hash.length <= 14) return hash;
|
|
3358
3700
|
return `${hash.slice(0, 6)}\u2026${hash.slice(-4)}`;
|
|
@@ -3458,7 +3800,7 @@ function DepositHistoryPanel({
|
|
|
3458
3800
|
useEffect5(() => {
|
|
3459
3801
|
rootRef.current?.toggleAttribute("inert", Boolean(obscured));
|
|
3460
3802
|
}, [obscured]);
|
|
3461
|
-
const handleKeyDown =
|
|
3803
|
+
const handleKeyDown = useCallback4(
|
|
3462
3804
|
(e) => {
|
|
3463
3805
|
if (obscured) return;
|
|
3464
3806
|
if (e.key === "Escape") {
|
|
@@ -3472,6 +3814,48 @@ function DepositHistoryPanel({
|
|
|
3472
3814
|
() => filterVisibleDeposits(deposits),
|
|
3473
3815
|
[deposits]
|
|
3474
3816
|
);
|
|
3817
|
+
const [nowMs, setNowMs] = useState3(() => Date.now());
|
|
3818
|
+
const timingPresentations = useMemo4(
|
|
3819
|
+
() => visibleDeposits.map(
|
|
3820
|
+
(deposit) => resolveHistoryTiming({
|
|
3821
|
+
status: deposit.status,
|
|
3822
|
+
timing: deposit.timing,
|
|
3823
|
+
createdAt: deposit.createdAt,
|
|
3824
|
+
nowMs
|
|
3825
|
+
})
|
|
3826
|
+
),
|
|
3827
|
+
[nowMs, visibleDeposits]
|
|
3828
|
+
);
|
|
3829
|
+
useEffect5(() => {
|
|
3830
|
+
setNowMs(Date.now());
|
|
3831
|
+
}, [visibleDeposits]);
|
|
3832
|
+
useEffect5(() => {
|
|
3833
|
+
const nextBoundary = timingPresentations.reduce(
|
|
3834
|
+
(earliest, presentation) => {
|
|
3835
|
+
const next = presentation?.nextTransitionAtMs;
|
|
3836
|
+
if (next === void 0) return earliest;
|
|
3837
|
+
return earliest === void 0 || next < earliest ? next : earliest;
|
|
3838
|
+
},
|
|
3839
|
+
void 0
|
|
3840
|
+
);
|
|
3841
|
+
if (nextBoundary === void 0) return;
|
|
3842
|
+
let timeout;
|
|
3843
|
+
const updateAtBoundary = () => {
|
|
3844
|
+
const current = Date.now();
|
|
3845
|
+
if (current < nextBoundary) {
|
|
3846
|
+
timeout = setTimeout(
|
|
3847
|
+
updateAtBoundary,
|
|
3848
|
+
Math.min(nextBoundary - current, MAX_TIMER_DELAY_MS)
|
|
3849
|
+
);
|
|
3850
|
+
return;
|
|
3851
|
+
}
|
|
3852
|
+
setNowMs(current);
|
|
3853
|
+
};
|
|
3854
|
+
updateAtBoundary();
|
|
3855
|
+
return () => {
|
|
3856
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
3857
|
+
};
|
|
3858
|
+
}, [timingPresentations]);
|
|
3475
3859
|
return /* @__PURE__ */ jsxs8(
|
|
3476
3860
|
"div",
|
|
3477
3861
|
{
|
|
@@ -3548,6 +3932,7 @@ function DepositHistoryPanel({
|
|
|
3548
3932
|
HistoryCard,
|
|
3549
3933
|
{
|
|
3550
3934
|
deposit,
|
|
3935
|
+
timing: timingPresentations[i],
|
|
3551
3936
|
position: i,
|
|
3552
3937
|
recipient,
|
|
3553
3938
|
onRecover,
|
|
@@ -3575,6 +3960,7 @@ function DepositHistoryPanel({
|
|
|
3575
3960
|
}
|
|
3576
3961
|
function HistoryCard({
|
|
3577
3962
|
deposit,
|
|
3963
|
+
timing,
|
|
3578
3964
|
position,
|
|
3579
3965
|
recipient,
|
|
3580
3966
|
onRecover,
|
|
@@ -3682,6 +4068,16 @@ function HistoryCard({
|
|
|
3682
4068
|
}
|
|
3683
4069
|
)
|
|
3684
4070
|
] }),
|
|
4071
|
+
timing && /* @__PURE__ */ jsx13(
|
|
4072
|
+
"span",
|
|
4073
|
+
{
|
|
4074
|
+
className: "rs-history-card-timing",
|
|
4075
|
+
"data-state": timing.state,
|
|
4076
|
+
"aria-live": "polite",
|
|
4077
|
+
"aria-atomic": "true",
|
|
4078
|
+
children: timing.state === "expected" ? `Usually arrives in ${timing.expectedDuration}` : timing.state === "soft" ? "Taking longer than usual" : "Taking much longer than usual"
|
|
4079
|
+
}
|
|
4080
|
+
),
|
|
3685
4081
|
recoverable && /* @__PURE__ */ jsxs8(
|
|
3686
4082
|
Button,
|
|
3687
4083
|
{
|
|
@@ -3705,7 +4101,7 @@ function HistoryCard({
|
|
|
3705
4101
|
DepositHistoryPanel.displayName = "DepositHistoryPanel";
|
|
3706
4102
|
|
|
3707
4103
|
// src/components/history/HistoryFallbackFlow.tsx
|
|
3708
|
-
import { useCallback as
|
|
4104
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef5, useState as useState5 } from "react";
|
|
3709
4105
|
|
|
3710
4106
|
// src/components/ui/ListRow.tsx
|
|
3711
4107
|
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
@@ -3889,10 +4285,10 @@ function RecoveryLookupScreen({
|
|
|
3889
4285
|
RecoveryLookupScreen.displayName = "RecoveryLookupScreen";
|
|
3890
4286
|
|
|
3891
4287
|
// src/components/recovery/recovery-display.ts
|
|
3892
|
-
import { formatUnits as
|
|
4288
|
+
import { formatUnits as formatUnits3 } from "viem";
|
|
3893
4289
|
|
|
3894
4290
|
// src/core/token-amount.ts
|
|
3895
|
-
import { formatUnits } from "viem";
|
|
4291
|
+
import { formatUnits as formatUnits2 } from "viem";
|
|
3896
4292
|
var STABLECOIN_SYMBOLS = /* @__PURE__ */ new Set([
|
|
3897
4293
|
"USDC",
|
|
3898
4294
|
"USDT",
|
|
@@ -3956,7 +4352,7 @@ function formatTokenAmount(amount, symbol) {
|
|
|
3956
4352
|
function tokenAmountToNumber(rawAmount, decimals) {
|
|
3957
4353
|
if (decimals === void 0) return void 0;
|
|
3958
4354
|
try {
|
|
3959
|
-
const value = Number(
|
|
4355
|
+
const value = Number(formatUnits2(BigInt(rawAmount), decimals));
|
|
3960
4356
|
return Number.isFinite(value) ? value : void 0;
|
|
3961
4357
|
} catch {
|
|
3962
4358
|
return void 0;
|
|
@@ -4043,18 +4439,18 @@ function describeRecoveryDeposit(params) {
|
|
|
4043
4439
|
const decimals = getTokenDecimalsByAddress(token, params.chainId);
|
|
4044
4440
|
const symbol = getTokenSymbol(token, params.chainId);
|
|
4045
4441
|
const raw = toBigInt(params.amountRaw);
|
|
4046
|
-
const human = Number(
|
|
4442
|
+
const human = Number(formatUnits3(raw, decimals));
|
|
4047
4443
|
return {
|
|
4048
4444
|
symbol,
|
|
4049
4445
|
decimals,
|
|
4050
|
-
amount: formatTokenAmount(human, symbol) ??
|
|
4446
|
+
amount: formatTokenAmount(human, symbol) ?? formatUnits3(raw, decimals),
|
|
4051
4447
|
chainName: getChainName(params.chainId),
|
|
4052
4448
|
chainIcon: getChainIcon(params.chainId)
|
|
4053
4449
|
};
|
|
4054
4450
|
}
|
|
4055
4451
|
function formatRecoveryAmount(amountRaw, decimals, symbol) {
|
|
4056
4452
|
const raw = toBigInt(amountRaw);
|
|
4057
|
-
return formatTokenAmount(Number(
|
|
4453
|
+
return formatTokenAmount(Number(formatUnits3(raw, decimals)), symbol) ?? formatUnits3(raw, decimals);
|
|
4058
4454
|
}
|
|
4059
4455
|
function toBigInt(value) {
|
|
4060
4456
|
try {
|
|
@@ -4542,7 +4938,7 @@ function HistoryFallbackFlow({
|
|
|
4542
4938
|
epochRef.current += 1;
|
|
4543
4939
|
};
|
|
4544
4940
|
}, [recipient]);
|
|
4545
|
-
const reportSelection =
|
|
4941
|
+
const reportSelection = useCallback5(
|
|
4546
4942
|
(match, autoSelected) => {
|
|
4547
4943
|
analytics.selectDeposit({
|
|
4548
4944
|
depositId: match.depositId,
|
|
@@ -4552,7 +4948,7 @@ function HistoryFallbackFlow({
|
|
|
4552
4948
|
},
|
|
4553
4949
|
[analytics]
|
|
4554
4950
|
);
|
|
4555
|
-
const
|
|
4951
|
+
const lookup2 = useCallback5(
|
|
4556
4952
|
async (txHash) => {
|
|
4557
4953
|
const epoch = epochRef.current;
|
|
4558
4954
|
const isCurrentLookup = () => epochRef.current === epoch;
|
|
@@ -4595,7 +4991,7 @@ function HistoryFallbackFlow({
|
|
|
4595
4991
|
},
|
|
4596
4992
|
[analytics, service, recipient, onFound, reportSelection]
|
|
4597
4993
|
);
|
|
4598
|
-
const selectMatch =
|
|
4994
|
+
const selectMatch = useCallback5(
|
|
4599
4995
|
(match) => {
|
|
4600
4996
|
analytics.completeStep("select");
|
|
4601
4997
|
reportSelection(match, false);
|
|
@@ -4603,7 +4999,7 @@ function HistoryFallbackFlow({
|
|
|
4603
4999
|
},
|
|
4604
5000
|
[analytics, onFound, reportSelection]
|
|
4605
5001
|
);
|
|
4606
|
-
const handleKeyDown =
|
|
5002
|
+
const handleKeyDown = useCallback5(
|
|
4607
5003
|
(e) => {
|
|
4608
5004
|
if (e.key === "Escape") {
|
|
4609
5005
|
e.stopPropagation();
|
|
@@ -4682,7 +5078,7 @@ function HistoryFallbackFlow({
|
|
|
4682
5078
|
hint: "The transaction hash from the wallet or exchange you sent from.",
|
|
4683
5079
|
loading,
|
|
4684
5080
|
error,
|
|
4685
|
-
onSubmit:
|
|
5081
|
+
onSubmit: lookup2,
|
|
4686
5082
|
onInvalid: () => analytics.friction("lookup_transaction_hash_invalid", "lookup")
|
|
4687
5083
|
}
|
|
4688
5084
|
)
|
|
@@ -4698,7 +5094,7 @@ function newestFirst(rows) {
|
|
|
4698
5094
|
HistoryFallbackFlow.displayName = "HistoryFallbackFlow";
|
|
4699
5095
|
|
|
4700
5096
|
// src/components/history/HistoryRecoveryFlow.tsx
|
|
4701
|
-
import { useCallback as
|
|
5097
|
+
import { useCallback as useCallback6, useEffect as useEffect9, useRef as useRef6, useState as useState7 } from "react";
|
|
4702
5098
|
|
|
4703
5099
|
// src/components/recovery/RecoveryOutcomeScreen.tsx
|
|
4704
5100
|
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
@@ -4902,7 +5298,7 @@ function HistoryRecoveryFlow({
|
|
|
4902
5298
|
token: deposit.token,
|
|
4903
5299
|
amountRaw: deposit.amountRaw
|
|
4904
5300
|
});
|
|
4905
|
-
const submit =
|
|
5301
|
+
const submit = useCallback6(
|
|
4906
5302
|
async (destination) => {
|
|
4907
5303
|
setPhase("submitting");
|
|
4908
5304
|
onSubmittingChange(true);
|
|
@@ -4974,7 +5370,7 @@ function HistoryRecoveryFlow({
|
|
|
4974
5370
|
symbol
|
|
4975
5371
|
]
|
|
4976
5372
|
);
|
|
4977
|
-
const retry =
|
|
5373
|
+
const retry = useCallback6(() => {
|
|
4978
5374
|
const reason = lastFailureRef.current;
|
|
4979
5375
|
if (reason) {
|
|
4980
5376
|
lastFailureRef.current = null;
|
|
@@ -5071,7 +5467,7 @@ function HistoryOverlay({
|
|
|
5071
5467
|
const [fallbackOpen, setFallbackOpen] = useState8(false);
|
|
5072
5468
|
const [attempt, setAttempt] = useState8(null);
|
|
5073
5469
|
const claimControllerRef = useRef7(null);
|
|
5074
|
-
const beginAttempt =
|
|
5470
|
+
const beginAttempt = useCallback7(
|
|
5075
5471
|
(entrySource) => {
|
|
5076
5472
|
const id = randomUuid();
|
|
5077
5473
|
const started = {
|
|
@@ -5089,7 +5485,7 @@ function HistoryOverlay({
|
|
|
5089
5485
|
},
|
|
5090
5486
|
[analytics]
|
|
5091
5487
|
);
|
|
5092
|
-
const endAttempt =
|
|
5488
|
+
const endAttempt = useCallback7(
|
|
5093
5489
|
(source, reason) => {
|
|
5094
5490
|
const claim = claimControllerRef.current;
|
|
5095
5491
|
claim?.abandon(reason);
|
|
@@ -5099,12 +5495,12 @@ function HistoryOverlay({
|
|
|
5099
5495
|
},
|
|
5100
5496
|
[]
|
|
5101
5497
|
);
|
|
5102
|
-
const openFallback =
|
|
5498
|
+
const openFallback = useCallback7(() => {
|
|
5103
5499
|
const started = beginAttempt("history_fallback");
|
|
5104
5500
|
analytics.fallbackOpened(started.id);
|
|
5105
5501
|
setFallbackOpen(true);
|
|
5106
5502
|
}, [analytics, beginAttempt]);
|
|
5107
|
-
const closeFallback =
|
|
5503
|
+
const closeFallback = useCallback7(
|
|
5108
5504
|
(via) => {
|
|
5109
5505
|
endAttempt(via === "escape" ? "escape" : "history_back", "back");
|
|
5110
5506
|
setFallbackOpen(false);
|
|
@@ -5117,7 +5513,7 @@ function HistoryOverlay({
|
|
|
5117
5513
|
setFallbackOpen(false);
|
|
5118
5514
|
if (fallbackOpen && !recovering) endAttempt("host_controlled", "back");
|
|
5119
5515
|
}, [canLookup, fallbackOpen, recovering, endAttempt]);
|
|
5120
|
-
const startRecoveryFromRow =
|
|
5516
|
+
const startRecoveryFromRow = useCallback7(
|
|
5121
5517
|
(deposit) => {
|
|
5122
5518
|
const started = beginAttempt("history_row");
|
|
5123
5519
|
analytics.recoveryStarted(started.id, "history_row", deposit.depositId);
|
|
@@ -5125,7 +5521,7 @@ function HistoryOverlay({
|
|
|
5125
5521
|
},
|
|
5126
5522
|
[analytics, beginAttempt, controller]
|
|
5127
5523
|
);
|
|
5128
|
-
const startRecoveryFromLookup =
|
|
5524
|
+
const startRecoveryFromLookup = useCallback7(
|
|
5129
5525
|
(deposit) => {
|
|
5130
5526
|
if (attempt) {
|
|
5131
5527
|
analytics.recoveryStarted(
|
|
@@ -5139,11 +5535,11 @@ function HistoryOverlay({
|
|
|
5139
5535
|
},
|
|
5140
5536
|
[analytics, attempt, controller]
|
|
5141
5537
|
);
|
|
5142
|
-
const cancelRecovery =
|
|
5538
|
+
const cancelRecovery = useCallback7(() => {
|
|
5143
5539
|
endAttempt("history_back", "back");
|
|
5144
5540
|
controller.cancelRecovery();
|
|
5145
5541
|
}, [controller, endAttempt]);
|
|
5146
|
-
const closeModal =
|
|
5542
|
+
const closeModal = useCallback7(() => {
|
|
5147
5543
|
endAttempt("header_button", "modal_close");
|
|
5148
5544
|
onCloseModal();
|
|
5149
5545
|
}, [endAttempt, onCloseModal]);
|
|
@@ -5234,7 +5630,8 @@ function HistoryOverlay({
|
|
|
5234
5630
|
}
|
|
5235
5631
|
|
|
5236
5632
|
// src/components/history/useHistoryPanel.ts
|
|
5237
|
-
import { useCallback as
|
|
5633
|
+
import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo7, useRef as useRef8, useState as useState9 } from "react";
|
|
5634
|
+
var MAX_TIMEOUT_MS = 2147483647;
|
|
5238
5635
|
function sortByCreatedAtDesc(items) {
|
|
5239
5636
|
return [...items].sort((a, b) => {
|
|
5240
5637
|
const at = a.createdAt ? Date.parse(a.createdAt) : 0;
|
|
@@ -5262,7 +5659,7 @@ function useHistoryPanel({
|
|
|
5262
5659
|
const requestRef = useRef8(0);
|
|
5263
5660
|
const [recovering, setRecovering] = useState9(null);
|
|
5264
5661
|
const [recoverySubmitting, setRecoverySubmitting] = useState9(false);
|
|
5265
|
-
const fetchHistory =
|
|
5662
|
+
const fetchHistory = useCallback8(
|
|
5266
5663
|
async (mode = "initial") => {
|
|
5267
5664
|
if (!recipient) return;
|
|
5268
5665
|
const isInitial = mode === "initial";
|
|
@@ -5312,27 +5709,40 @@ function useHistoryPanel({
|
|
|
5312
5709
|
},
|
|
5313
5710
|
[recipient, service]
|
|
5314
5711
|
);
|
|
5315
|
-
const
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
);
|
|
5319
|
-
const
|
|
5712
|
+
const badgeState2 = deriveHistoryBadgeState(deposits, {
|
|
5713
|
+
recipient,
|
|
5714
|
+
canRecover
|
|
5715
|
+
});
|
|
5716
|
+
const badge = badgeState2.badge;
|
|
5717
|
+
useEffect11(() => {
|
|
5718
|
+
if (!isOpen || badgeState2.nextExpiryAt === null) return;
|
|
5719
|
+
const delay = Math.min(
|
|
5720
|
+
badgeState2.nextExpiryAt - Date.now(),
|
|
5721
|
+
MAX_TIMEOUT_MS
|
|
5722
|
+
);
|
|
5723
|
+
const timeout = setTimeout(
|
|
5724
|
+
() => setDeposits((current) => current.slice()),
|
|
5725
|
+
delay
|
|
5726
|
+
);
|
|
5727
|
+
return () => clearTimeout(timeout);
|
|
5728
|
+
}, [isOpen, badgeState2.nextExpiryAt]);
|
|
5729
|
+
const openPanel = useCallback8(() => {
|
|
5320
5730
|
setIsPanelOpen(true);
|
|
5321
5731
|
analytics?.visitOpened(badge);
|
|
5322
5732
|
if (!loadedRef.current || staleRef.current) {
|
|
5323
5733
|
fetchHistory("initial");
|
|
5324
5734
|
}
|
|
5325
5735
|
}, [analytics, badge, fetchHistory]);
|
|
5326
|
-
const closePanel =
|
|
5736
|
+
const closePanel = useCallback8(() => {
|
|
5327
5737
|
if (recovering) return;
|
|
5328
5738
|
setIsPanelOpen(false);
|
|
5329
5739
|
analytics?.visitEnded();
|
|
5330
5740
|
}, [analytics, recovering]);
|
|
5331
|
-
const loadMore =
|
|
5741
|
+
const loadMore = useCallback8(() => {
|
|
5332
5742
|
analytics?.loadMore(filterVisibleDeposits(deposits).length);
|
|
5333
5743
|
fetchHistory("more");
|
|
5334
5744
|
}, [analytics, deposits, fetchHistory]);
|
|
5335
|
-
const markStale =
|
|
5745
|
+
const markStale = useCallback8(() => {
|
|
5336
5746
|
staleRef.current = true;
|
|
5337
5747
|
if (isOpen) {
|
|
5338
5748
|
fetchHistory("initial");
|
|
@@ -5368,10 +5778,10 @@ function useHistoryPanel({
|
|
|
5368
5778
|
setRecovering(null);
|
|
5369
5779
|
setRecoverySubmitting(false);
|
|
5370
5780
|
}, [isOpen, analytics]);
|
|
5371
|
-
const invalidate =
|
|
5781
|
+
const invalidate = useCallback8(() => {
|
|
5372
5782
|
staleRef.current = true;
|
|
5373
5783
|
}, []);
|
|
5374
|
-
const cancelRecovery =
|
|
5784
|
+
const cancelRecovery = useCallback8(() => setRecovering(null), []);
|
|
5375
5785
|
return useMemo7(
|
|
5376
5786
|
() => ({
|
|
5377
5787
|
deposits,
|
|
@@ -5805,14 +6215,14 @@ function isSubmissionUncertain(error) {
|
|
|
5805
6215
|
}
|
|
5806
6216
|
|
|
5807
6217
|
// src/components/steps/ProcessingStep.tsx
|
|
5808
|
-
import { useCallback as
|
|
6218
|
+
import { useCallback as useCallback10, useEffect as useEffect14, useRef as useRef10, useState as useState13 } from "react";
|
|
5809
6219
|
|
|
5810
6220
|
// src/components/ui/Tooltip.tsx
|
|
5811
6221
|
import {
|
|
5812
6222
|
useState as useState10,
|
|
5813
6223
|
useRef as useRef9,
|
|
5814
6224
|
useEffect as useEffect12,
|
|
5815
|
-
useCallback as
|
|
6225
|
+
useCallback as useCallback9
|
|
5816
6226
|
} from "react";
|
|
5817
6227
|
import { createPortal as createPortal2 } from "react-dom";
|
|
5818
6228
|
import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
@@ -5821,7 +6231,7 @@ function Tooltip({ content, children, className }) {
|
|
|
5821
6231
|
const [position, setPosition] = useState10(null);
|
|
5822
6232
|
const triggerRef = useRef9(null);
|
|
5823
6233
|
const bubbleRef = useRef9(null);
|
|
5824
|
-
const updatePosition =
|
|
6234
|
+
const updatePosition = useCallback9(() => {
|
|
5825
6235
|
const trigger = triggerRef.current;
|
|
5826
6236
|
if (!trigger) return;
|
|
5827
6237
|
const rect = trigger.getBoundingClientRect();
|
|
@@ -6325,12 +6735,6 @@ var MAX_POLL_INTERVAL = 3e4;
|
|
|
6325
6735
|
var BACKOFF_MULTIPLIER = 1.5;
|
|
6326
6736
|
var SWAPPED_RECEIPT_STATUS_RETRY_INTERVAL_MS = 1e3;
|
|
6327
6737
|
var SWAPPED_FIAT_STATUS_MAX_ATTEMPTS = 12;
|
|
6328
|
-
var ESCALATED_DELAY_MS = 10 * 60 * 1e3;
|
|
6329
|
-
var SOFT_DELAY_MS = {
|
|
6330
|
-
confirming: 90 * 1e3,
|
|
6331
|
-
received: 90 * 1e3,
|
|
6332
|
-
bridging: 4 * 60 * 1e3
|
|
6333
|
-
};
|
|
6334
6738
|
var PHASE_TIMINGS_PREFIX = "rhinestone:phase-timings";
|
|
6335
6739
|
var PAYMENT_METHOD_LABELS = {
|
|
6336
6740
|
creditcard: "Card",
|
|
@@ -6610,7 +7014,6 @@ function ProcessingStep({
|
|
|
6610
7014
|
}
|
|
6611
7015
|
return { type: "processing" };
|
|
6612
7016
|
});
|
|
6613
|
-
const [elapsedSeconds, setElapsedSeconds] = useState13(0);
|
|
6614
7017
|
const [phaseTimings, setPhaseTimings] = useState13(() => {
|
|
6615
7018
|
const saved = loadPhaseTimings(txHash);
|
|
6616
7019
|
if (saved) {
|
|
@@ -6619,8 +7022,16 @@ function ProcessingStep({
|
|
|
6619
7022
|
}
|
|
6620
7023
|
return { startedAt: startTimeRef.current };
|
|
6621
7024
|
});
|
|
6622
|
-
const [
|
|
6623
|
-
|
|
7025
|
+
const [elapsedSeconds, setElapsedSeconds] = useState13(
|
|
7026
|
+
() => Math.max(0, Math.floor((Date.now() - startTimeRef.current) / 1e3))
|
|
7027
|
+
);
|
|
7028
|
+
const [latchedTiming, setLatchedTiming] = useState13(quote?.timing);
|
|
7029
|
+
if (latchedTiming === void 0 && quote?.timing !== void 0) {
|
|
7030
|
+
setLatchedTiming(quote.timing);
|
|
7031
|
+
}
|
|
7032
|
+
const timingStateRef = useRef10("expected");
|
|
7033
|
+
const servedTimingLatchRef = useRef10(false);
|
|
7034
|
+
const updatePhaseTimings = useCallback10(
|
|
6624
7035
|
(updater) => {
|
|
6625
7036
|
setPhaseTimings((previous) => {
|
|
6626
7037
|
const next = updater(previous);
|
|
@@ -6925,21 +7336,6 @@ function ProcessingStep({
|
|
|
6925
7336
|
state.type,
|
|
6926
7337
|
txHash
|
|
6927
7338
|
]);
|
|
6928
|
-
useEffect14(() => {
|
|
6929
|
-
if (directTransfer || state.type !== "processing") return;
|
|
6930
|
-
const timeoutId = setTimeout(() => {
|
|
6931
|
-
if (escalatedDelayRef.current) return;
|
|
6932
|
-
escalatedDelayRef.current = true;
|
|
6933
|
-
setHasEscalatedDelay(true);
|
|
6934
|
-
const message = "Transfer is taking longer than expected. Your funds are safe and processing will continue automatically.";
|
|
6935
|
-
debugLog(debug, "processing", "state:delay-escalated", {
|
|
6936
|
-
txHash,
|
|
6937
|
-
timeoutMs: ESCALATED_DELAY_MS
|
|
6938
|
-
});
|
|
6939
|
-
onErrorRef.current?.(message, "PROCESS_TIMEOUT");
|
|
6940
|
-
}, ESCALATED_DELAY_MS);
|
|
6941
|
-
return () => clearTimeout(timeoutId);
|
|
6942
|
-
}, [debug, directTransfer, onErrorRef, state.type, txHash]);
|
|
6943
7339
|
const isComplete = state.type === "complete";
|
|
6944
7340
|
const isFailed = state.type === "failed";
|
|
6945
7341
|
const isProcessing = state.type === "processing";
|
|
@@ -7039,9 +7435,43 @@ function ProcessingStep({
|
|
|
7039
7435
|
const currentPhaseId = getCurrentPhaseId(state, phaseTimings);
|
|
7040
7436
|
const activePhaseStartedAt = currentPhaseId ? getPhaseStartTime(currentPhaseId, phaseTimings) : void 0;
|
|
7041
7437
|
const activePhaseElapsedMs = isProcessing && activePhaseStartedAt !== void 0 ? timelineNowMs - activePhaseStartedAt : 0;
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7438
|
+
if (latchedTiming !== void 0 && !servedTimingLatchRef.current) {
|
|
7439
|
+
servedTimingLatchRef.current = true;
|
|
7440
|
+
timingStateRef.current = "expected";
|
|
7441
|
+
}
|
|
7442
|
+
const timingState = isProcessing ? advancePendingTimingState(
|
|
7443
|
+
timingStateRef.current,
|
|
7444
|
+
resolvePendingTimingState({
|
|
7445
|
+
timing: latchedTiming,
|
|
7446
|
+
totalElapsedMs: elapsedSeconds * 1e3,
|
|
7447
|
+
phaseId: currentPhaseId,
|
|
7448
|
+
phaseElapsedMs: activePhaseElapsedMs
|
|
7449
|
+
})
|
|
7450
|
+
) : timingStateRef.current;
|
|
7451
|
+
timingStateRef.current = timingState;
|
|
7452
|
+
const expectedDuration = latchedTiming && timingState === "expected" ? formatExpectedDuration(latchedTiming.expectedSeconds) : void 0;
|
|
7453
|
+
useEffect14(() => {
|
|
7454
|
+
if (directTransfer || !isProcessing || timingState !== "escalated") return;
|
|
7455
|
+
if (escalatedDelayRef.current) return;
|
|
7456
|
+
escalatedDelayRef.current = true;
|
|
7457
|
+
debugLog(debug, "processing", "state:delay-escalated", {
|
|
7458
|
+
txHash,
|
|
7459
|
+
timeoutMs: latchedTiming ? resolveServedThresholds(latchedTiming).escalatedMs : FALLBACK_ESCALATED_DELAY_MS,
|
|
7460
|
+
served: latchedTiming !== void 0
|
|
7461
|
+
});
|
|
7462
|
+
onErrorRef.current?.(
|
|
7463
|
+
"Transfer is taking longer than expected. Your funds are safe and processing will continue automatically.",
|
|
7464
|
+
"PROCESS_TIMEOUT"
|
|
7465
|
+
);
|
|
7466
|
+
}, [
|
|
7467
|
+
debug,
|
|
7468
|
+
directTransfer,
|
|
7469
|
+
isProcessing,
|
|
7470
|
+
latchedTiming,
|
|
7471
|
+
onErrorRef,
|
|
7472
|
+
timingState,
|
|
7473
|
+
txHash
|
|
7474
|
+
]);
|
|
7045
7475
|
const sourceChainIcon = getChainIcon(displaySourceChain);
|
|
7046
7476
|
const targetChainIcon = getChainIcon(targetChainId);
|
|
7047
7477
|
const sourceChainName = getChainName(displaySourceChain);
|
|
@@ -7270,6 +7700,11 @@ function ProcessingStep({
|
|
|
7270
7700
|
})()
|
|
7271
7701
|
] }),
|
|
7272
7702
|
isFailed && failureMessage && /* @__PURE__ */ jsx27(Callout, { variant: "error", children: failureMessage }),
|
|
7703
|
+
isProcessing && expectedDuration !== void 0 && /* @__PURE__ */ jsxs20("p", { className: "rs-pending-expectation", children: [
|
|
7704
|
+
"Usually arrives in ",
|
|
7705
|
+
expectedDuration
|
|
7706
|
+
] }),
|
|
7707
|
+
isProcessing && latchedTiming !== void 0 && timingState !== "expected" && /* @__PURE__ */ jsx27(Callout, { variant: "warning", children: timingState === "soft" ? "Taking longer than usual. Your funds are safe and this will complete automatically." : "This is taking much longer than usual. Your funds are safe and processing continues automatically." }),
|
|
7273
7708
|
isProcessing && /* @__PURE__ */ jsx27(
|
|
7274
7709
|
Button,
|
|
7275
7710
|
{
|
|
@@ -7529,8 +7964,11 @@ export {
|
|
|
7529
7964
|
SwappedConnectFeeChangedError,
|
|
7530
7965
|
SUPPORTED_CONNECT_EXCHANGE_COUNT,
|
|
7531
7966
|
resolveAppFeeBps,
|
|
7967
|
+
isDepositPolicyRefusal,
|
|
7532
7968
|
PermitDepositSubmitError,
|
|
7533
7969
|
createDepositService,
|
|
7970
|
+
depositBoundsViolation,
|
|
7971
|
+
describePolicyRefusal,
|
|
7534
7972
|
rpcUrlFor,
|
|
7535
7973
|
RpcUrlsProvider,
|
|
7536
7974
|
useRpcUrls,
|
|
@@ -7543,6 +7981,9 @@ export {
|
|
|
7543
7981
|
useDepositChainIds,
|
|
7544
7982
|
useDestinationChains,
|
|
7545
7983
|
useTargetTokens,
|
|
7984
|
+
useDepositOffer,
|
|
7985
|
+
useDepositBounds,
|
|
7986
|
+
useRestrictedListExclusion,
|
|
7546
7987
|
useIsDepositChain,
|
|
7547
7988
|
useChainName,
|
|
7548
7989
|
useReadClient,
|
|
@@ -7559,6 +8000,7 @@ export {
|
|
|
7559
8000
|
isUnsupportedChainSwitchError,
|
|
7560
8001
|
formatUserError,
|
|
7561
8002
|
Tooltip,
|
|
8003
|
+
formatExpectedDuration,
|
|
7562
8004
|
markSubmissionUncertain,
|
|
7563
8005
|
isSubmissionUncertain,
|
|
7564
8006
|
formatTokenAmount,
|