@swype-org/react-sdk 0.2.174 → 0.2.186
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 +36 -0
- package/dist/index.cjs +1239 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +130 -20
- package/dist/index.d.ts +130 -20
- package/dist/index.js +1240 -118
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var buffer = require('buffer');
|
|
3
4
|
var react = require('react');
|
|
4
5
|
var reactAuth = require('@privy-io/react-auth');
|
|
5
6
|
var wagmi = require('wagmi');
|
|
@@ -17,6 +18,10 @@ var __export = (target, all) => {
|
|
|
17
18
|
for (var name in all)
|
|
18
19
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
20
|
};
|
|
21
|
+
var g = globalThis;
|
|
22
|
+
if (typeof g.Buffer === "undefined") {
|
|
23
|
+
g.Buffer = buffer.Buffer;
|
|
24
|
+
}
|
|
20
25
|
|
|
21
26
|
// src/theme.ts
|
|
22
27
|
var darkTheme = {
|
|
@@ -609,7 +614,7 @@ function resolveSelectSourceOption(choices, options, chainName, tokenSymbol, rec
|
|
|
609
614
|
}
|
|
610
615
|
var BLINK_PRIVY_APP_ID = "cmlil87uv004n0ck0blwumwek";
|
|
611
616
|
var wagmiConfig = wagmi.createConfig({
|
|
612
|
-
chains: [chains.mainnet, chains.arbitrum, chains.base, chains.polygon, chains.bsc, chains.megaeth, chains.monad, chains.baseSepolia],
|
|
617
|
+
chains: [chains.mainnet, chains.arbitrum, chains.base, chains.polygon, chains.bsc, chains.megaeth, chains.monad, chains.hyperliquid, chains.baseSepolia],
|
|
613
618
|
connectors: [
|
|
614
619
|
connectors.injected({ unstable_shimAsyncInject: 2e3 }),
|
|
615
620
|
// `preference: 'all'` (default) lets the Coinbase Wallet SDK prefer an
|
|
@@ -629,6 +634,7 @@ var wagmiConfig = wagmi.createConfig({
|
|
|
629
634
|
[chains.bsc.id]: wagmi.http(),
|
|
630
635
|
[chains.megaeth.id]: wagmi.http(),
|
|
631
636
|
[chains.monad.id]: wagmi.http(),
|
|
637
|
+
[chains.hyperliquid.id]: wagmi.http(),
|
|
632
638
|
[chains.baseSepolia.id]: wagmi.http()
|
|
633
639
|
}
|
|
634
640
|
});
|
|
@@ -1201,7 +1207,7 @@ async function fetchTransfer(apiBaseUrl, token, transferId, authorizationSession
|
|
|
1201
1207
|
if (!res.ok) await throwApiError(res);
|
|
1202
1208
|
return await res.json();
|
|
1203
1209
|
}
|
|
1204
|
-
async function signTransfer(apiBaseUrl, token, transferId,
|
|
1210
|
+
async function signTransfer(apiBaseUrl, token, transferId, signedTransfer, authorizationSessionToken) {
|
|
1205
1211
|
if (!token && !authorizationSessionToken) {
|
|
1206
1212
|
throw new Error("Missing auth credentials for transfer signing.");
|
|
1207
1213
|
}
|
|
@@ -1212,7 +1218,9 @@ async function signTransfer(apiBaseUrl, token, transferId, signedUserOp, authori
|
|
|
1212
1218
|
...token ? { Authorization: `Bearer ${token}` } : {},
|
|
1213
1219
|
...authorizationSessionToken ? { "x-authorization-session-token": authorizationSessionToken } : {}
|
|
1214
1220
|
},
|
|
1215
|
-
body: JSON.stringify(
|
|
1221
|
+
body: JSON.stringify(
|
|
1222
|
+
signedTransfer.chainFamily === "svm" ? { signedTransfer } : { signedUserOp: signedTransfer }
|
|
1223
|
+
)
|
|
1216
1224
|
});
|
|
1217
1225
|
if (!res.ok) await throwApiError(res);
|
|
1218
1226
|
return await res.json();
|
|
@@ -1326,7 +1334,14 @@ async function probeActionCompletion(apiBaseUrl, actionId) {
|
|
|
1326
1334
|
if (res.status === 422 && code === "DEPOSIT_TX_NOT_FOUND") {
|
|
1327
1335
|
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
1328
1336
|
}
|
|
1329
|
-
|
|
1337
|
+
const approvalNotDetectedCodes = /* @__PURE__ */ new Set([
|
|
1338
|
+
"APPROVE_NOT_DETECTED",
|
|
1339
|
+
"APPROVE_SPL_NOT_DETECTED",
|
|
1340
|
+
"SPL_DELEGATE_MISSING",
|
|
1341
|
+
"SPL_DELEGATE_INSUFFICIENT",
|
|
1342
|
+
"SPL_DELEGATE_WRONG_OWNER"
|
|
1343
|
+
]);
|
|
1344
|
+
if (res.status === 422 && code && approvalNotDetectedCodes.has(code)) {
|
|
1330
1345
|
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
1331
1346
|
}
|
|
1332
1347
|
if (res.status === 422 && code === "INVALID_TRANSFER_STATE") {
|
|
@@ -1354,6 +1369,8 @@ async function pollTransferTick(params) {
|
|
|
1354
1369
|
}
|
|
1355
1370
|
|
|
1356
1371
|
// src/hooks/useTransferPolling.ts
|
|
1372
|
+
var FAST_POLL_COUNT = 8;
|
|
1373
|
+
var FAST_POLL_INTERVAL_MS = 1e3;
|
|
1357
1374
|
function useTransferPolling(intervalMs = 3e3, overrideGetAccessToken) {
|
|
1358
1375
|
const { apiBaseUrl } = useBlinkConfig();
|
|
1359
1376
|
const { getAccessToken: privyGetAccessToken } = reactAuth.usePrivy();
|
|
@@ -1361,12 +1378,15 @@ function useTransferPolling(intervalMs = 3e3, overrideGetAccessToken) {
|
|
|
1361
1378
|
const [transfer, setTransfer] = react.useState(null);
|
|
1362
1379
|
const [error, setError] = react.useState(null);
|
|
1363
1380
|
const [isPolling, setIsPolling] = react.useState(false);
|
|
1364
|
-
const
|
|
1381
|
+
const timeoutRef = react.useRef(null);
|
|
1365
1382
|
const transferIdRef = react.useRef(null);
|
|
1383
|
+
const tickCountRef = react.useRef(0);
|
|
1384
|
+
const runningRef = react.useRef(false);
|
|
1366
1385
|
const stopPolling = react.useCallback(() => {
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1386
|
+
runningRef.current = false;
|
|
1387
|
+
if (timeoutRef.current) {
|
|
1388
|
+
clearTimeout(timeoutRef.current);
|
|
1389
|
+
timeoutRef.current = null;
|
|
1370
1390
|
}
|
|
1371
1391
|
setIsPolling(false);
|
|
1372
1392
|
}, []);
|
|
@@ -1391,16 +1411,27 @@ function useTransferPolling(intervalMs = 3e3, overrideGetAccessToken) {
|
|
|
1391
1411
|
stopPolling();
|
|
1392
1412
|
}
|
|
1393
1413
|
}, [apiBaseUrl, getAccessToken, stopPolling]);
|
|
1414
|
+
const scheduleNext = react.useCallback(() => {
|
|
1415
|
+
if (!runningRef.current) return;
|
|
1416
|
+
const next = tickCountRef.current < FAST_POLL_COUNT ? Math.min(FAST_POLL_INTERVAL_MS, intervalMs) : intervalMs;
|
|
1417
|
+
timeoutRef.current = setTimeout(async () => {
|
|
1418
|
+
tickCountRef.current += 1;
|
|
1419
|
+
await poll();
|
|
1420
|
+
scheduleNext();
|
|
1421
|
+
}, next);
|
|
1422
|
+
}, [intervalMs, poll]);
|
|
1394
1423
|
const startPolling = react.useCallback(
|
|
1395
1424
|
(transferId) => {
|
|
1396
1425
|
stopPolling();
|
|
1397
1426
|
transferIdRef.current = transferId;
|
|
1427
|
+
tickCountRef.current = 0;
|
|
1428
|
+
runningRef.current = true;
|
|
1398
1429
|
setIsPolling(true);
|
|
1399
1430
|
setError(null);
|
|
1400
1431
|
poll();
|
|
1401
|
-
|
|
1432
|
+
scheduleNext();
|
|
1402
1433
|
},
|
|
1403
|
-
[poll,
|
|
1434
|
+
[poll, scheduleNext, stopPolling]
|
|
1404
1435
|
);
|
|
1405
1436
|
react.useEffect(() => () => stopPolling(), [stopPolling]);
|
|
1406
1437
|
return { transfer, error, isPolling, startPolling, stopPolling };
|
|
@@ -1653,7 +1684,7 @@ async function resolvePermit2BatchMetadata(options) {
|
|
|
1653
1684
|
approveAction = null,
|
|
1654
1685
|
signAction,
|
|
1655
1686
|
fetchAuthorizationSession: fetchAuthorizationSession2 = fetchAuthorizationSession,
|
|
1656
|
-
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
1687
|
+
sleep: sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
1657
1688
|
logPrefix = "[blink-sdk][permit2-batch]",
|
|
1658
1689
|
waitForAwaitingLimit = false
|
|
1659
1690
|
} = options;
|
|
@@ -1693,7 +1724,7 @@ async function resolvePermit2BatchMetadata(options) {
|
|
|
1693
1724
|
`SIGN_PERMIT2 metadata is incomplete for batch execution. Missing: ${missingFields.join(", ")}.`
|
|
1694
1725
|
);
|
|
1695
1726
|
}
|
|
1696
|
-
await
|
|
1727
|
+
await sleep2(BATCH_SIGN_PERMIT2_POLL_MS);
|
|
1697
1728
|
const refreshedSession = await fetchAuthorizationSession2(apiBaseUrl, sessionId);
|
|
1698
1729
|
const refreshedSignAction = refreshedSession.actions.find((action) => action.id === signAction.id);
|
|
1699
1730
|
if (!refreshedSignAction) {
|
|
@@ -1747,7 +1778,7 @@ function isTxSettlementTimeoutError(err) {
|
|
|
1747
1778
|
async function waitForTransactionReceipt(walletClient, txHash, logContext, options = {}) {
|
|
1748
1779
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_TX_RECEIPT_POLL_INTERVAL_MS;
|
|
1749
1780
|
const maxAttempts = options.maxAttempts ?? DEFAULT_TX_RECEIPT_MAX_ATTEMPTS;
|
|
1750
|
-
const
|
|
1781
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1751
1782
|
const info = options.logger?.info ?? ((message) => console.info(message));
|
|
1752
1783
|
const warn = options.logger?.warn ?? ((message) => console.warn(message));
|
|
1753
1784
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
@@ -1767,7 +1798,7 @@ async function waitForTransactionReceipt(walletClient, txHash, logContext, optio
|
|
|
1767
1798
|
`[blink-sdk][tx-receipt] eth_getTransactionReceipt errored; will retry. context=${logContext}, txHash=${txHash}, error=${err instanceof Error ? err.message : String(err)}`
|
|
1768
1799
|
);
|
|
1769
1800
|
}
|
|
1770
|
-
await
|
|
1801
|
+
await sleep2(pollIntervalMs);
|
|
1771
1802
|
}
|
|
1772
1803
|
const waitedMs = pollIntervalMs * maxAttempts;
|
|
1773
1804
|
if (options.strict) {
|
|
@@ -1810,7 +1841,7 @@ function parseNonce(raw) {
|
|
|
1810
1841
|
async function waitForNonceAdvance(walletClient, sender, expectedMinNonce, logContext, options = {}) {
|
|
1811
1842
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_TX_RECEIPT_POLL_INTERVAL_MS;
|
|
1812
1843
|
const maxAttempts = options.maxAttempts ?? DEFAULT_TX_RECEIPT_MAX_ATTEMPTS;
|
|
1813
|
-
const
|
|
1844
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1814
1845
|
const info = options.logger?.info ?? ((message) => console.info(message));
|
|
1815
1846
|
const warn = options.logger?.warn ?? ((message) => console.warn(message));
|
|
1816
1847
|
let lastObservedNonce = null;
|
|
@@ -1829,7 +1860,7 @@ async function waitForNonceAdvance(walletClient, sender, expectedMinNonce, logCo
|
|
|
1829
1860
|
`[blink-sdk][tx-nonce] eth_getTransactionCount errored; will retry. context=${logContext}, sender=${sender}, error=${err instanceof Error ? err.message : String(err)}`
|
|
1830
1861
|
);
|
|
1831
1862
|
}
|
|
1832
|
-
await
|
|
1863
|
+
await sleep2(pollIntervalMs);
|
|
1833
1864
|
}
|
|
1834
1865
|
const waitedMs = pollIntervalMs * maxAttempts;
|
|
1835
1866
|
throw new TxSettlementTimeoutError({
|
|
@@ -1852,7 +1883,7 @@ async function waitForTransactionSettled(params) {
|
|
|
1852
1883
|
logContext,
|
|
1853
1884
|
pollIntervalMs,
|
|
1854
1885
|
maxAttempts,
|
|
1855
|
-
sleep,
|
|
1886
|
+
sleep: sleep2,
|
|
1856
1887
|
logger
|
|
1857
1888
|
} = params;
|
|
1858
1889
|
const expectedMinNonce = preSendNonce + 1;
|
|
@@ -1867,7 +1898,7 @@ async function waitForTransactionSettled(params) {
|
|
|
1867
1898
|
await waitForTransactionReceipt(walletClient, txHash, logContext, {
|
|
1868
1899
|
pollIntervalMs: resolvedPollIntervalMs,
|
|
1869
1900
|
maxAttempts: resolvedMaxAttempts,
|
|
1870
|
-
sleep,
|
|
1901
|
+
sleep: sleep2,
|
|
1871
1902
|
logger: sharedLogger,
|
|
1872
1903
|
strict: true
|
|
1873
1904
|
});
|
|
@@ -1889,7 +1920,7 @@ async function waitForTransactionSettled(params) {
|
|
|
1889
1920
|
{
|
|
1890
1921
|
pollIntervalMs: resolvedPollIntervalMs,
|
|
1891
1922
|
maxAttempts: resolvedMaxAttempts,
|
|
1892
|
-
sleep,
|
|
1923
|
+
sleep: sleep2,
|
|
1893
1924
|
logger: sharedLogger
|
|
1894
1925
|
}
|
|
1895
1926
|
);
|
|
@@ -1927,13 +1958,13 @@ async function waitForWalletNonceAgreement(params) {
|
|
|
1927
1958
|
pollIntervalMs,
|
|
1928
1959
|
maxAttempts,
|
|
1929
1960
|
minWaitMs,
|
|
1930
|
-
sleep,
|
|
1961
|
+
sleep: sleep2,
|
|
1931
1962
|
logger
|
|
1932
1963
|
} = params;
|
|
1933
1964
|
const resolvedPollIntervalMs = pollIntervalMs ?? DEFAULT_WALLET_NONCE_AGREEMENT_POLL_INTERVAL_MS;
|
|
1934
1965
|
const resolvedMaxAttempts = maxAttempts ?? DEFAULT_WALLET_NONCE_AGREEMENT_MAX_ATTEMPTS;
|
|
1935
1966
|
const resolvedMinWaitMs = minWaitMs ?? DEFAULT_WALLET_NONCE_AGREEMENT_MIN_WAIT_MS;
|
|
1936
|
-
const resolvedSleep =
|
|
1967
|
+
const resolvedSleep = sleep2 ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1937
1968
|
const info = logger?.info ?? ((message) => console.info(message));
|
|
1938
1969
|
const warn = logger?.warn ?? ((message) => console.warn(message));
|
|
1939
1970
|
const startedAt = Date.now();
|
|
@@ -2019,7 +2050,7 @@ async function pollWalletCallsStatus(walletClient, callsId, options = {}) {
|
|
|
2019
2050
|
const maxConsecutiveErrors = options.maxConsecutiveErrors ?? DEFAULT_MAX_CONSECUTIVE_ERRORS;
|
|
2020
2051
|
const errorGracePeriodMs = options.errorGracePeriodMs ?? DEFAULT_ERROR_GRACE_PERIOD_MS;
|
|
2021
2052
|
const logEveryN = Math.max(1, options.logEveryNAttempts ?? DEFAULT_LOG_EVERY_N_ATTEMPTS);
|
|
2022
|
-
const
|
|
2053
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
2023
2054
|
const now = options.now ?? (() => Date.now());
|
|
2024
2055
|
const info = options.logger?.info ?? ((m) => console.info(m));
|
|
2025
2056
|
const warn = options.logger?.warn ?? ((m) => console.warn(m));
|
|
@@ -2028,7 +2059,7 @@ async function pollWalletCallsStatus(walletClient, callsId, options = {}) {
|
|
|
2028
2059
|
let consecutiveErrors = 0;
|
|
2029
2060
|
let lastStatus;
|
|
2030
2061
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
2031
|
-
await
|
|
2062
|
+
await sleep2(pollIntervalMs);
|
|
2032
2063
|
let status;
|
|
2033
2064
|
try {
|
|
2034
2065
|
status = await withTimeout(
|
|
@@ -2210,6 +2241,10 @@ function buildTargetMatchers(target) {
|
|
|
2210
2241
|
aliases.add("rabby");
|
|
2211
2242
|
aliases.add("io.rabby");
|
|
2212
2243
|
}
|
|
2244
|
+
if (value.includes("phantom")) {
|
|
2245
|
+
aliases.add("phantom");
|
|
2246
|
+
aliases.add("app.phantom");
|
|
2247
|
+
}
|
|
2213
2248
|
if (value.includes("injected")) {
|
|
2214
2249
|
aliases.add("injected");
|
|
2215
2250
|
}
|
|
@@ -2247,6 +2282,252 @@ function resolveWalletConnector(connectors, target) {
|
|
|
2247
2282
|
return metaMaskConnector ?? connectors[0];
|
|
2248
2283
|
}
|
|
2249
2284
|
|
|
2285
|
+
// src/solanaWalletRuntime.ts
|
|
2286
|
+
function asSigner(adapter) {
|
|
2287
|
+
return adapter;
|
|
2288
|
+
}
|
|
2289
|
+
var PHANTOM_SOLANA_CONNECT_TIMEOUT_MESSAGE = "PHANTOM_SOLANA_CONNECT_TIMEOUT";
|
|
2290
|
+
var APPROVE_SPL_CONFIRMATION_TIMEOUT_MESSAGE = "APPROVE_SPL_CONFIRMATION_TIMEOUT";
|
|
2291
|
+
var APPROVE_SPL_ONCHAIN_FAILURE_PREFIX = "Solana transaction failed:";
|
|
2292
|
+
var DEFAULT_CONFIRM_TIMEOUT_MS = 45e3;
|
|
2293
|
+
var DEFAULT_POLL_INTERVAL_MS2 = 1e3;
|
|
2294
|
+
var DEFAULT_COMMITMENT = "confirmed";
|
|
2295
|
+
var cachedAdapter = null;
|
|
2296
|
+
var cachedProviderKey = null;
|
|
2297
|
+
function providerKey(selection) {
|
|
2298
|
+
const providerName = selection.providerName?.trim();
|
|
2299
|
+
if (!providerName) {
|
|
2300
|
+
throw new Error("Solana wallet metadata is missing providerName.");
|
|
2301
|
+
}
|
|
2302
|
+
return `${selection.providerId ?? ""}:${providerName.toLowerCase()}`;
|
|
2303
|
+
}
|
|
2304
|
+
function assertSupportedProvider(selection) {
|
|
2305
|
+
const name = selection.providerName?.trim().toLowerCase();
|
|
2306
|
+
if (name !== "phantom") {
|
|
2307
|
+
throw new Error(`Unsupported Solana wallet provider: ${selection.providerName ?? "unknown"}.`);
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
async function createAdapter(selection) {
|
|
2311
|
+
assertSupportedProvider(selection);
|
|
2312
|
+
const { PhantomWalletAdapter } = await import('@solana/wallet-adapter-phantom');
|
|
2313
|
+
return new PhantomWalletAdapter();
|
|
2314
|
+
}
|
|
2315
|
+
function readPublicKey(adapter) {
|
|
2316
|
+
const publicKey = adapter.publicKey?.toBase58();
|
|
2317
|
+
if (!publicKey) {
|
|
2318
|
+
throw new Error("Solana wallet did not return a public key.");
|
|
2319
|
+
}
|
|
2320
|
+
return publicKey;
|
|
2321
|
+
}
|
|
2322
|
+
async function connectSolanaWallet(selection, options) {
|
|
2323
|
+
const key = providerKey(selection);
|
|
2324
|
+
if (!cachedAdapter || cachedProviderKey !== key) {
|
|
2325
|
+
cachedAdapter = await createAdapter(selection);
|
|
2326
|
+
cachedProviderKey = key;
|
|
2327
|
+
}
|
|
2328
|
+
if (!cachedAdapter.connected) {
|
|
2329
|
+
const adapter = cachedAdapter;
|
|
2330
|
+
const connectPromise = adapter.connect();
|
|
2331
|
+
const timeoutMs = options?.timeoutMs;
|
|
2332
|
+
if (typeof timeoutMs === "number" && timeoutMs > 0) {
|
|
2333
|
+
let timeoutHandle = null;
|
|
2334
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
2335
|
+
timeoutHandle = setTimeout(() => {
|
|
2336
|
+
reject(new Error(PHANTOM_SOLANA_CONNECT_TIMEOUT_MESSAGE));
|
|
2337
|
+
}, timeoutMs);
|
|
2338
|
+
});
|
|
2339
|
+
try {
|
|
2340
|
+
await Promise.race([connectPromise, timeoutPromise]);
|
|
2341
|
+
} finally {
|
|
2342
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
2343
|
+
}
|
|
2344
|
+
} else {
|
|
2345
|
+
await connectPromise;
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
return {
|
|
2349
|
+
adapter: cachedAdapter,
|
|
2350
|
+
publicKey: readPublicKey(cachedAdapter)
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
async function signSolanaTransaction(selection, unsignedTxBase64, expectedOwnerPubkey) {
|
|
2354
|
+
const { adapter, publicKey } = await connectSolanaWallet(selection);
|
|
2355
|
+
if (publicKey !== expectedOwnerPubkey) {
|
|
2356
|
+
throw new Error(
|
|
2357
|
+
`Connected Solana wallet ${publicKey} does not match the required source wallet ${expectedOwnerPubkey}. Please switch accounts in Phantom and retry.`
|
|
2358
|
+
);
|
|
2359
|
+
}
|
|
2360
|
+
const signer = asSigner(adapter);
|
|
2361
|
+
if (typeof signer.signTransaction !== "function") {
|
|
2362
|
+
throw new Error("Selected Solana wallet does not support transaction signing.");
|
|
2363
|
+
}
|
|
2364
|
+
const transaction = await deserializeTransaction(unsignedTxBase64);
|
|
2365
|
+
const signedTransaction = await signer.signTransaction(transaction);
|
|
2366
|
+
return {
|
|
2367
|
+
ownerPubkey: publicKey,
|
|
2368
|
+
signedTxBase64: bytesToBase64(serializeTransaction(signedTransaction))
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
async function supportsSignAllSolanaTransactions(selection) {
|
|
2372
|
+
const { adapter } = await connectSolanaWallet(selection);
|
|
2373
|
+
return typeof asSigner(adapter).signAllTransactions === "function";
|
|
2374
|
+
}
|
|
2375
|
+
async function signAllSolanaTransactions(selection, unsignedTxsBase64, expectedOwnerPubkey) {
|
|
2376
|
+
if (!Array.isArray(unsignedTxsBase64) || unsignedTxsBase64.length === 0) {
|
|
2377
|
+
throw new Error("signAllSolanaTransactions requires at least one unsigned transaction.");
|
|
2378
|
+
}
|
|
2379
|
+
const { adapter, publicKey } = await connectSolanaWallet(selection);
|
|
2380
|
+
if (publicKey !== expectedOwnerPubkey) {
|
|
2381
|
+
throw new Error(
|
|
2382
|
+
`Connected Solana wallet ${publicKey} does not match the required source wallet ${expectedOwnerPubkey}. Please switch accounts in Phantom and retry.`
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
const signer = asSigner(adapter);
|
|
2386
|
+
if (typeof signer.signAllTransactions !== "function") {
|
|
2387
|
+
throw new Error("Selected Solana wallet does not support signAllTransactions.");
|
|
2388
|
+
}
|
|
2389
|
+
const transactions = [];
|
|
2390
|
+
for (const txBase64 of unsignedTxsBase64) {
|
|
2391
|
+
transactions.push(await deserializeTransaction(txBase64));
|
|
2392
|
+
}
|
|
2393
|
+
const signed = await signer.signAllTransactions(transactions);
|
|
2394
|
+
if (!Array.isArray(signed) || signed.length !== transactions.length) {
|
|
2395
|
+
throw new Error(
|
|
2396
|
+
`signAllTransactions returned ${Array.isArray(signed) ? signed.length : "non-array"} transactions; expected ${transactions.length}.`
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
return {
|
|
2400
|
+
ownerPubkey: publicKey,
|
|
2401
|
+
signedTxsBase64: signed.map((tx) => bytesToBase64(serializeTransaction(tx)))
|
|
2402
|
+
};
|
|
2403
|
+
}
|
|
2404
|
+
async function deserializeTransaction(unsignedTxBase64) {
|
|
2405
|
+
const { Transaction, VersionedTransaction } = await import('@solana/web3.js');
|
|
2406
|
+
const bytes = base64ToBytes(unsignedTxBase64);
|
|
2407
|
+
try {
|
|
2408
|
+
return Transaction.from(bytes);
|
|
2409
|
+
} catch {
|
|
2410
|
+
return VersionedTransaction.deserialize(bytes);
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
function serializeTransaction(transaction) {
|
|
2414
|
+
if ("version" in transaction) {
|
|
2415
|
+
return transaction.serialize();
|
|
2416
|
+
}
|
|
2417
|
+
return transaction.serialize({
|
|
2418
|
+
requireAllSignatures: false,
|
|
2419
|
+
verifySignatures: false
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
function base64ToBytes(value) {
|
|
2423
|
+
if (typeof globalThis.atob === "function") {
|
|
2424
|
+
const binary = globalThis.atob(value);
|
|
2425
|
+
const bytes = new Uint8Array(binary.length);
|
|
2426
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2427
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2428
|
+
}
|
|
2429
|
+
return bytes;
|
|
2430
|
+
}
|
|
2431
|
+
const bufferCtor = globalThis.Buffer;
|
|
2432
|
+
if (bufferCtor) {
|
|
2433
|
+
return bufferCtor.from(value, "base64");
|
|
2434
|
+
}
|
|
2435
|
+
throw new Error("Base64 decoding is not available in this environment.");
|
|
2436
|
+
}
|
|
2437
|
+
function bytesToBase64(bytes) {
|
|
2438
|
+
if (typeof globalThis.btoa === "function") {
|
|
2439
|
+
let binary = "";
|
|
2440
|
+
for (const byte of bytes) {
|
|
2441
|
+
binary += String.fromCharCode(byte);
|
|
2442
|
+
}
|
|
2443
|
+
return globalThis.btoa(binary);
|
|
2444
|
+
}
|
|
2445
|
+
const bufferCtor = globalThis.Buffer;
|
|
2446
|
+
if (bufferCtor) {
|
|
2447
|
+
return bufferCtor.from(bytes).toString("base64");
|
|
2448
|
+
}
|
|
2449
|
+
throw new Error("Base64 encoding is not available in this environment.");
|
|
2450
|
+
}
|
|
2451
|
+
async function signAndSendApproveSplViaWallet(params) {
|
|
2452
|
+
const rpcUrl = params.rpcUrl?.trim();
|
|
2453
|
+
if (!rpcUrl) {
|
|
2454
|
+
throw new Error("signAndSendApproveSplViaWallet requires an rpcUrl.");
|
|
2455
|
+
}
|
|
2456
|
+
const unsignedTxBase64 = params.unsignedTxBase64?.trim();
|
|
2457
|
+
if (!unsignedTxBase64) {
|
|
2458
|
+
throw new Error("signAndSendApproveSplViaWallet requires unsignedTxBase64.");
|
|
2459
|
+
}
|
|
2460
|
+
const commitment = params.commitment ?? DEFAULT_COMMITMENT;
|
|
2461
|
+
const { adapter, publicKey } = await connectSolanaWallet(params.selection);
|
|
2462
|
+
if (publicKey !== params.expectedOwnerPubkey) {
|
|
2463
|
+
throw new Error(
|
|
2464
|
+
`Connected Solana wallet ${publicKey} does not match the required source wallet ${params.expectedOwnerPubkey}. Please switch accounts in Phantom and retry.`
|
|
2465
|
+
);
|
|
2466
|
+
}
|
|
2467
|
+
if (!adapter.sendTransaction) {
|
|
2468
|
+
throw new Error("Selected Solana wallet does not support sendTransaction.");
|
|
2469
|
+
}
|
|
2470
|
+
const transaction = await deserializeTransaction(unsignedTxBase64);
|
|
2471
|
+
const connection = await defaultConnectionFactory(rpcUrl, commitment);
|
|
2472
|
+
const signature = await adapter.sendTransaction(
|
|
2473
|
+
transaction,
|
|
2474
|
+
connection,
|
|
2475
|
+
{
|
|
2476
|
+
preflightCommitment: commitment,
|
|
2477
|
+
maxRetries: 3
|
|
2478
|
+
}
|
|
2479
|
+
);
|
|
2480
|
+
return { ownerPubkey: publicKey, signature };
|
|
2481
|
+
}
|
|
2482
|
+
async function confirmApproveSplViaPolling(params) {
|
|
2483
|
+
const rpcUrl = params.rpcUrl?.trim();
|
|
2484
|
+
if (!rpcUrl) {
|
|
2485
|
+
throw new Error("confirmApproveSplViaPolling requires an rpcUrl.");
|
|
2486
|
+
}
|
|
2487
|
+
const signature = params.signature?.trim();
|
|
2488
|
+
if (!signature) {
|
|
2489
|
+
throw new Error("confirmApproveSplViaPolling requires a signature.");
|
|
2490
|
+
}
|
|
2491
|
+
const confirmTimeoutMs = params.confirmTimeoutMs ?? DEFAULT_CONFIRM_TIMEOUT_MS;
|
|
2492
|
+
const pollIntervalMs = params.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
2493
|
+
const commitment = params.commitment ?? DEFAULT_COMMITMENT;
|
|
2494
|
+
const connection = await defaultConnectionFactory(rpcUrl, commitment);
|
|
2495
|
+
const slot = await pollForConfirmation({
|
|
2496
|
+
connection,
|
|
2497
|
+
signature,
|
|
2498
|
+
confirmTimeoutMs,
|
|
2499
|
+
pollIntervalMs
|
|
2500
|
+
});
|
|
2501
|
+
return { signature, slot };
|
|
2502
|
+
}
|
|
2503
|
+
async function defaultConnectionFactory(rpcUrl, commitment) {
|
|
2504
|
+
const { Connection } = await import('@solana/web3.js');
|
|
2505
|
+
return new Connection(rpcUrl, commitment);
|
|
2506
|
+
}
|
|
2507
|
+
async function pollForConfirmation(args) {
|
|
2508
|
+
const { connection, signature, confirmTimeoutMs, pollIntervalMs } = args;
|
|
2509
|
+
const expiresAt = Date.now() + confirmTimeoutMs;
|
|
2510
|
+
while (Date.now() <= expiresAt) {
|
|
2511
|
+
const result = await connection.getSignatureStatuses(
|
|
2512
|
+
[signature],
|
|
2513
|
+
{ searchTransactionHistory: true }
|
|
2514
|
+
);
|
|
2515
|
+
const status = result.value[0];
|
|
2516
|
+
if (status?.err) {
|
|
2517
|
+
throw new Error(`${APPROVE_SPL_ONCHAIN_FAILURE_PREFIX} ${JSON.stringify(status.err)}`);
|
|
2518
|
+
}
|
|
2519
|
+
const confirmationStatus = status?.confirmationStatus;
|
|
2520
|
+
if (confirmationStatus === "confirmed" || confirmationStatus === "finalized") {
|
|
2521
|
+
return status?.slot;
|
|
2522
|
+
}
|
|
2523
|
+
await sleep(pollIntervalMs);
|
|
2524
|
+
}
|
|
2525
|
+
throw new Error(APPROVE_SPL_CONFIRMATION_TIMEOUT_MESSAGE);
|
|
2526
|
+
}
|
|
2527
|
+
function sleep(ms) {
|
|
2528
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2250
2531
|
// src/hooks/authorizationExecutor.ts
|
|
2251
2532
|
var WALLET_CLIENT_MAX_ATTEMPTS = 25;
|
|
2252
2533
|
var WALLET_CLIENT_POLL_MS = 400;
|
|
@@ -2258,6 +2539,7 @@ var EXECUTE_BRIDGE_TX_TIMEOUT_MS = 12e4;
|
|
|
2258
2539
|
var EXECUTE_BRIDGE_TX_TIMEOUT_MESSAGE = "EXECUTE_BRIDGE_TX_TIMEOUT";
|
|
2259
2540
|
var WALLET_PROMPT_TIMEOUT_MS = 9e4;
|
|
2260
2541
|
var WALLET_PROMPT_TIMEOUT_MESSAGE = "WALLET_PROMPT_TIMEOUT";
|
|
2542
|
+
var PHANTOM_SOLANA_CONNECT_TIMEOUT_MS = 8e3;
|
|
2261
2543
|
var BATCH_RECEIPT_POLL_INTERVAL_MS = 1500;
|
|
2262
2544
|
var BATCH_RECEIPT_MAX_ATTEMPTS = 20;
|
|
2263
2545
|
var BATCHABLE_ACTION_TYPES = /* @__PURE__ */ new Set(["APPROVE_PERMIT2", "SIGN_PERMIT2", "EXECUTE_BRIDGE"]);
|
|
@@ -2371,13 +2653,110 @@ function parseSignTypedDataPayload(typedData) {
|
|
|
2371
2653
|
};
|
|
2372
2654
|
}
|
|
2373
2655
|
function isBatchableAction(action) {
|
|
2656
|
+
if (action.type === "EXECUTE_BRIDGE" && action.metadata?.chainFamily === "svm") {
|
|
2657
|
+
return false;
|
|
2658
|
+
}
|
|
2374
2659
|
return BATCHABLE_ACTION_TYPES.has(action.type);
|
|
2375
2660
|
}
|
|
2661
|
+
function isSvmCombinedFirstTransferPair(pending) {
|
|
2662
|
+
if (pending.length < 2) return false;
|
|
2663
|
+
const [first, second] = pending;
|
|
2664
|
+
if (first.type !== "APPROVE_SPL") return false;
|
|
2665
|
+
if (second.type !== "EXECUTE_BRIDGE") return false;
|
|
2666
|
+
if (second.metadata?.chainFamily !== "svm") return false;
|
|
2667
|
+
if (first.metadata?.awaitingLimit) return false;
|
|
2668
|
+
const approveTx = first.metadata?.unsignedApproveTxBase64;
|
|
2669
|
+
const bridgeTx = second.metadata?.unsignedTxBase64;
|
|
2670
|
+
if (typeof approveTx !== "string" || approveTx.trim() === "") return false;
|
|
2671
|
+
if (typeof bridgeTx !== "string" || bridgeTx.trim() === "") return false;
|
|
2672
|
+
const approveOwner = first.metadata?.ownerPubkey;
|
|
2673
|
+
const bridgeOwner = second.metadata?.ownerPubkey ?? second.metadata?.senderAddress;
|
|
2674
|
+
if (typeof approveOwner !== "string" || approveOwner.trim() === "") return false;
|
|
2675
|
+
if (typeof bridgeOwner !== "string" || bridgeOwner.trim() === "") return false;
|
|
2676
|
+
if (approveOwner !== bridgeOwner) return false;
|
|
2677
|
+
return true;
|
|
2678
|
+
}
|
|
2679
|
+
function getSolanaWalletSelection(action) {
|
|
2680
|
+
const providerName = action.metadata?.providerName;
|
|
2681
|
+
if (typeof providerName !== "string" || providerName.trim() === "") {
|
|
2682
|
+
throw new Error(`${action.type} metadata is missing providerName.`);
|
|
2683
|
+
}
|
|
2684
|
+
const providerId = action.metadata?.providerId;
|
|
2685
|
+
return {
|
|
2686
|
+
...typeof providerId === "string" && providerId.trim() !== "" ? { providerId } : {},
|
|
2687
|
+
providerName
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2690
|
+
function isPhantomEvmProviderAvailable() {
|
|
2691
|
+
if (typeof window === "undefined") return false;
|
|
2692
|
+
const phantom = window.phantom;
|
|
2693
|
+
return Boolean(phantom?.ethereum?.isPhantom);
|
|
2694
|
+
}
|
|
2695
|
+
async function phantomEvmHasAuthorizedAccount() {
|
|
2696
|
+
if (typeof window === "undefined") return false;
|
|
2697
|
+
const provider = window.phantom?.ethereum;
|
|
2698
|
+
if (!provider?.isPhantom) return false;
|
|
2699
|
+
try {
|
|
2700
|
+
const accounts = await provider.request({ method: "eth_accounts" });
|
|
2701
|
+
return Array.isArray(accounts) && accounts.length > 0;
|
|
2702
|
+
} catch {
|
|
2703
|
+
return false;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
async function attemptPhantomEvmConnect(connectors, connectAsync) {
|
|
2707
|
+
let connector = resolveWalletConnector(connectors, { providerName: "Phantom" });
|
|
2708
|
+
if (!connector) {
|
|
2709
|
+
const ethereum = typeof window !== "undefined" ? window.ethereum : void 0;
|
|
2710
|
+
if (ethereum?.isPhantom) {
|
|
2711
|
+
connector = resolveWalletConnector(connectors, { wagmiConnectorId: "injected" });
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
if (!connector) return null;
|
|
2715
|
+
const result = await connectAsync({ connector });
|
|
2716
|
+
const address = result.accounts[0];
|
|
2717
|
+
if (!address) return null;
|
|
2718
|
+
return { address, chainId: result.chainId };
|
|
2719
|
+
}
|
|
2376
2720
|
function getPendingActions(session, completedIds) {
|
|
2377
2721
|
return session.actions.filter((a) => a.status === "PENDING" && !completedIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
|
|
2378
2722
|
}
|
|
2379
2723
|
async function executeOpenProvider(action, wagmiConfig2, connectors, connectAsync) {
|
|
2380
2724
|
try {
|
|
2725
|
+
if (action.metadata?.chainFamily === "svm") {
|
|
2726
|
+
const selection = getSolanaWalletSelection(action);
|
|
2727
|
+
const evmProbed = isPhantomEvmProviderAvailable() && await phantomEvmHasAuthorizedAccount();
|
|
2728
|
+
const svmPromise = connectSolanaWallet(selection, {
|
|
2729
|
+
timeoutMs: evmProbed ? PHANTOM_SOLANA_CONNECT_TIMEOUT_MS : void 0
|
|
2730
|
+
});
|
|
2731
|
+
const evmPromise = evmProbed ? attemptPhantomEvmConnect(connectors, connectAsync) : Promise.resolve(null);
|
|
2732
|
+
const [svmSettled, evmSettled] = await Promise.allSettled([svmPromise, evmPromise]);
|
|
2733
|
+
const svmPublicKey = svmSettled.status === "fulfilled" ? svmSettled.value.publicKey : null;
|
|
2734
|
+
const evmAddress = evmSettled.status === "fulfilled" && evmSettled.value ? evmSettled.value.address : null;
|
|
2735
|
+
if (!svmPublicKey && !evmAddress) {
|
|
2736
|
+
const svmReason = svmSettled.status === "rejected" ? svmSettled.reason instanceof Error ? svmSettled.reason.message : String(svmSettled.reason) : "Failed to connect Phantom.";
|
|
2737
|
+
const friendlyMsg = svmReason === PHANTOM_SOLANA_CONNECT_TIMEOUT_MESSAGE ? "Phantom did not respond. Please try again." : svmReason;
|
|
2738
|
+
return actionError(
|
|
2739
|
+
action,
|
|
2740
|
+
isUserRejection(friendlyMsg) ? "You rejected the Solana wallet connection request. Please connect Phantom to continue." : friendlyMsg
|
|
2741
|
+
);
|
|
2742
|
+
}
|
|
2743
|
+
if (evmAddress) {
|
|
2744
|
+
await refreshWalletCapabilities(wagmiConfig2, "open-provider:phantom-dual");
|
|
2745
|
+
}
|
|
2746
|
+
const messageParts = [];
|
|
2747
|
+
if (svmPublicKey) messageParts.push(`SVM: ${svmPublicKey}`);
|
|
2748
|
+
if (evmAddress) messageParts.push(`EVM: ${evmAddress}`);
|
|
2749
|
+
return actionSuccess(
|
|
2750
|
+
action,
|
|
2751
|
+
`Connected to ${selection.providerName}. ${messageParts.join(", ")}`,
|
|
2752
|
+
{
|
|
2753
|
+
accounts: svmPublicKey ? [svmPublicKey, ...evmAddress ? [evmAddress] : []] : evmAddress ? [evmAddress] : [],
|
|
2754
|
+
chainFamily: "svm",
|
|
2755
|
+
...svmPublicKey ? { userSolanaWalletPubkey: svmPublicKey } : {},
|
|
2756
|
+
...evmAddress ? { evmAddress } : {}
|
|
2757
|
+
}
|
|
2758
|
+
);
|
|
2759
|
+
}
|
|
2381
2760
|
const account = core.getAccount(wagmiConfig2);
|
|
2382
2761
|
const targetId = action.metadata?.wagmiConnectorId;
|
|
2383
2762
|
const connector = resolveWalletConnector(connectors, { wagmiConnectorId: targetId });
|
|
@@ -2503,9 +2882,16 @@ async function executeOpenProvider(action, wagmiConfig2, connectors, connectAsyn
|
|
|
2503
2882
|
{ accounts: [...result.accounts], chainId: hexChainId }
|
|
2504
2883
|
);
|
|
2505
2884
|
} catch (err) {
|
|
2885
|
+
const msg = err instanceof Error ? err.message : "Failed to connect wallet";
|
|
2886
|
+
if (action.metadata?.chainFamily === "svm") {
|
|
2887
|
+
return actionError(
|
|
2888
|
+
action,
|
|
2889
|
+
isUserRejection(msg) ? "You rejected the Solana wallet connection request. Please connect Phantom to continue." : msg
|
|
2890
|
+
);
|
|
2891
|
+
}
|
|
2506
2892
|
return actionError(
|
|
2507
2893
|
action,
|
|
2508
|
-
|
|
2894
|
+
msg
|
|
2509
2895
|
);
|
|
2510
2896
|
}
|
|
2511
2897
|
}
|
|
@@ -2583,6 +2969,334 @@ async function executeSwitchChain(action, wagmiConfig2, switchChainAsync) {
|
|
|
2583
2969
|
);
|
|
2584
2970
|
}
|
|
2585
2971
|
}
|
|
2972
|
+
async function executeApproveSplSolana(action, options) {
|
|
2973
|
+
try {
|
|
2974
|
+
const selection = getSolanaWalletSelection(action);
|
|
2975
|
+
const ownerPubkey = action.metadata?.ownerPubkey;
|
|
2976
|
+
const unsignedApproveTxBase64 = action.metadata?.unsignedApproveTxBase64;
|
|
2977
|
+
const tokenSymbol = action.metadata?.tokenSymbol;
|
|
2978
|
+
const clientRpcUrl = action.metadata?.clientRpcUrl;
|
|
2979
|
+
if (action.metadata?.awaitingLimit) {
|
|
2980
|
+
return actionError(
|
|
2981
|
+
action,
|
|
2982
|
+
"APPROVE_SPL action is still waiting for a One-Tap allowance amount."
|
|
2983
|
+
);
|
|
2984
|
+
}
|
|
2985
|
+
if (typeof ownerPubkey !== "string" || ownerPubkey.trim() === "") {
|
|
2986
|
+
return actionError(action, "APPROVE_SPL metadata is missing ownerPubkey.");
|
|
2987
|
+
}
|
|
2988
|
+
if (typeof unsignedApproveTxBase64 !== "string" || unsignedApproveTxBase64.trim() === "") {
|
|
2989
|
+
return actionError(
|
|
2990
|
+
action,
|
|
2991
|
+
"APPROVE_SPL metadata is missing unsignedApproveTxBase64."
|
|
2992
|
+
);
|
|
2993
|
+
}
|
|
2994
|
+
appendDebug("info", "APPROVE_SPL: entry", {
|
|
2995
|
+
actionId: action.id,
|
|
2996
|
+
ownerPubkey,
|
|
2997
|
+
tokenSymbol: typeof tokenSymbol === "string" ? tokenSymbol : null
|
|
2998
|
+
});
|
|
2999
|
+
if (typeof clientRpcUrl === "string" && clientRpcUrl.trim() !== "") {
|
|
3000
|
+
const sendLabel = "APPROVE_SPL signAndSendApproveSplViaWallet";
|
|
3001
|
+
const sent = await withWatchdog(
|
|
3002
|
+
withTimeout(
|
|
3003
|
+
signAndSendApproveSplViaWallet({
|
|
3004
|
+
selection,
|
|
3005
|
+
unsignedTxBase64: unsignedApproveTxBase64,
|
|
3006
|
+
expectedOwnerPubkey: ownerPubkey,
|
|
3007
|
+
rpcUrl: clientRpcUrl
|
|
3008
|
+
}),
|
|
3009
|
+
WALLET_PROMPT_TIMEOUT_MS,
|
|
3010
|
+
sendLabel
|
|
3011
|
+
),
|
|
3012
|
+
sendLabel
|
|
3013
|
+
);
|
|
3014
|
+
appendDebug("info", "APPROVE_SPL: tx submitted via wallet", {
|
|
3015
|
+
actionId: action.id,
|
|
3016
|
+
signature: sent.signature
|
|
3017
|
+
});
|
|
3018
|
+
try {
|
|
3019
|
+
options?.onConfirming?.(action);
|
|
3020
|
+
} catch (callbackErr) {
|
|
3021
|
+
appendDebug("warn", "APPROVE_SPL: onConfirming callback threw", {
|
|
3022
|
+
actionId: action.id,
|
|
3023
|
+
error: callbackErr instanceof Error ? callbackErr.message : String(callbackErr)
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
const confirmLabel = "APPROVE_SPL confirmApproveSplViaPolling";
|
|
3027
|
+
let slot;
|
|
3028
|
+
try {
|
|
3029
|
+
const confirmation = await withWatchdog(
|
|
3030
|
+
confirmApproveSplViaPolling({
|
|
3031
|
+
rpcUrl: clientRpcUrl,
|
|
3032
|
+
signature: sent.signature
|
|
3033
|
+
}),
|
|
3034
|
+
confirmLabel
|
|
3035
|
+
);
|
|
3036
|
+
slot = confirmation.slot;
|
|
3037
|
+
appendDebug("info", "APPROVE_SPL: settled", {
|
|
3038
|
+
actionId: action.id,
|
|
3039
|
+
signature: sent.signature,
|
|
3040
|
+
slot
|
|
3041
|
+
});
|
|
3042
|
+
} catch (pollErr) {
|
|
3043
|
+
const pollMsg = pollErr instanceof Error ? pollErr.message : String(pollErr);
|
|
3044
|
+
if (pollMsg === APPROVE_SPL_CONFIRMATION_TIMEOUT_MESSAGE) {
|
|
3045
|
+
throw pollErr;
|
|
3046
|
+
}
|
|
3047
|
+
if (pollMsg.startsWith(APPROVE_SPL_ONCHAIN_FAILURE_PREFIX)) {
|
|
3048
|
+
throw pollErr;
|
|
3049
|
+
}
|
|
3050
|
+
appendDebug("warn", "APPROVE_SPL: confirmation poll failed; relying on server verification", {
|
|
3051
|
+
actionId: action.id,
|
|
3052
|
+
signature: sent.signature,
|
|
3053
|
+
error: pollMsg
|
|
3054
|
+
});
|
|
3055
|
+
}
|
|
3056
|
+
return actionSuccess(
|
|
3057
|
+
action,
|
|
3058
|
+
`Confirmed SPL approval for ${typeof tokenSymbol === "string" ? tokenSymbol : "tokens"}.`,
|
|
3059
|
+
{
|
|
3060
|
+
approveSignature: sent.signature
|
|
3061
|
+
}
|
|
3062
|
+
);
|
|
3063
|
+
}
|
|
3064
|
+
const signLabel = "APPROVE_SPL signSolanaTransaction";
|
|
3065
|
+
const signed = await withWatchdog(
|
|
3066
|
+
withTimeout(
|
|
3067
|
+
signSolanaTransaction(
|
|
3068
|
+
selection,
|
|
3069
|
+
unsignedApproveTxBase64,
|
|
3070
|
+
ownerPubkey
|
|
3071
|
+
),
|
|
3072
|
+
WALLET_PROMPT_TIMEOUT_MS,
|
|
3073
|
+
signLabel
|
|
3074
|
+
),
|
|
3075
|
+
signLabel
|
|
3076
|
+
);
|
|
3077
|
+
appendDebug("info", "APPROVE_SPL: signed (legacy path)", {
|
|
3078
|
+
actionId: action.id
|
|
3079
|
+
});
|
|
3080
|
+
return actionSuccess(
|
|
3081
|
+
action,
|
|
3082
|
+
`Signed SPL approval for ${typeof tokenSymbol === "string" ? tokenSymbol : "tokens"}.`,
|
|
3083
|
+
{ signedTxBase64: signed.signedTxBase64 }
|
|
3084
|
+
);
|
|
3085
|
+
} catch (err) {
|
|
3086
|
+
const msg = err instanceof Error ? err.message : "Failed to sign SPL approval";
|
|
3087
|
+
const timedOut = err instanceof PromiseTimeoutError;
|
|
3088
|
+
const confirmTimedOut = msg === APPROVE_SPL_CONFIRMATION_TIMEOUT_MESSAGE;
|
|
3089
|
+
appendDebug(timedOut || confirmTimedOut ? "warn" : "error", "APPROVE_SPL: threw", {
|
|
3090
|
+
actionId: action.id,
|
|
3091
|
+
error: msg,
|
|
3092
|
+
userRejected: isUserRejection(msg),
|
|
3093
|
+
timedOut,
|
|
3094
|
+
confirmTimedOut
|
|
3095
|
+
});
|
|
3096
|
+
if (timedOut) {
|
|
3097
|
+
return actionError(action, WALLET_PROMPT_TIMEOUT_MESSAGE);
|
|
3098
|
+
}
|
|
3099
|
+
if (confirmTimedOut) {
|
|
3100
|
+
return actionError(
|
|
3101
|
+
action,
|
|
3102
|
+
"Your SPL approval was submitted but did not confirm in time. Please wait a moment and retry \u2014 do not re-approve in Phantom yet, as that would create a duplicate transaction."
|
|
3103
|
+
);
|
|
3104
|
+
}
|
|
3105
|
+
return actionError(
|
|
3106
|
+
action,
|
|
3107
|
+
isUserRejection(msg) ? "You rejected the SPL approval transaction. Please approve the transaction in Phantom to continue." : msg
|
|
3108
|
+
);
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
async function executeExecuteBridgeSolana(action) {
|
|
3112
|
+
try {
|
|
3113
|
+
const selection = getSolanaWalletSelection(action);
|
|
3114
|
+
const ownerPubkey = action.metadata?.ownerPubkey ?? action.metadata?.senderAddress;
|
|
3115
|
+
const unsignedTxBase64 = action.metadata?.unsignedTxBase64;
|
|
3116
|
+
if (typeof ownerPubkey !== "string" || ownerPubkey.trim() === "") {
|
|
3117
|
+
return actionError(action, "SVM EXECUTE_BRIDGE metadata is missing ownerPubkey.");
|
|
3118
|
+
}
|
|
3119
|
+
if (typeof unsignedTxBase64 !== "string" || unsignedTxBase64.trim() === "") {
|
|
3120
|
+
return actionError(action, "SVM EXECUTE_BRIDGE metadata is missing unsignedTxBase64.");
|
|
3121
|
+
}
|
|
3122
|
+
appendDebug("info", "EXECUTE_BRIDGE: svm entry", {
|
|
3123
|
+
actionId: action.id,
|
|
3124
|
+
ownerPubkey
|
|
3125
|
+
});
|
|
3126
|
+
const label = "EXECUTE_BRIDGE signSolanaTransaction";
|
|
3127
|
+
const signed = await withWatchdog(
|
|
3128
|
+
withTimeout(
|
|
3129
|
+
signSolanaTransaction(
|
|
3130
|
+
selection,
|
|
3131
|
+
unsignedTxBase64,
|
|
3132
|
+
ownerPubkey
|
|
3133
|
+
),
|
|
3134
|
+
WALLET_PROMPT_TIMEOUT_MS,
|
|
3135
|
+
label
|
|
3136
|
+
),
|
|
3137
|
+
label
|
|
3138
|
+
);
|
|
3139
|
+
appendDebug("info", "EXECUTE_BRIDGE: svm signed", {
|
|
3140
|
+
actionId: action.id
|
|
3141
|
+
});
|
|
3142
|
+
return actionSuccess(
|
|
3143
|
+
action,
|
|
3144
|
+
"Signed Solana bridge transfer in Phantom.",
|
|
3145
|
+
{ signedTxBase64: signed.signedTxBase64, ownerPubkey: signed.ownerPubkey }
|
|
3146
|
+
);
|
|
3147
|
+
} catch (err) {
|
|
3148
|
+
const msg = err instanceof Error ? err.message : "Failed to sign Solana bridge transfer";
|
|
3149
|
+
const timedOut = err instanceof PromiseTimeoutError;
|
|
3150
|
+
appendDebug(timedOut ? "warn" : "error", "EXECUTE_BRIDGE: svm threw", {
|
|
3151
|
+
actionId: action.id,
|
|
3152
|
+
error: msg,
|
|
3153
|
+
userRejected: isUserRejection(msg),
|
|
3154
|
+
timedOut
|
|
3155
|
+
});
|
|
3156
|
+
if (timedOut) {
|
|
3157
|
+
return actionError(action, WALLET_PROMPT_TIMEOUT_MESSAGE);
|
|
3158
|
+
}
|
|
3159
|
+
return actionError(
|
|
3160
|
+
action,
|
|
3161
|
+
isUserRejection(msg) ? "You rejected the Solana transfer transaction. Please approve the transaction in Phantom to continue." : msg
|
|
3162
|
+
);
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
async function executeSvmCombinedFirstTransfer(actions, options) {
|
|
3166
|
+
if (actions.length !== 2) {
|
|
3167
|
+
throw new Error(
|
|
3168
|
+
`executeSvmCombinedFirstTransfer requires exactly 2 actions, received ${actions.length}.`
|
|
3169
|
+
);
|
|
3170
|
+
}
|
|
3171
|
+
const approveAction = actions[0].action;
|
|
3172
|
+
const bridgeAction = actions[1].action;
|
|
3173
|
+
if (approveAction.type !== "APPROVE_SPL") {
|
|
3174
|
+
throw new Error(
|
|
3175
|
+
`executeSvmCombinedFirstTransfer expects APPROVE_SPL first, got ${approveAction.type}.`
|
|
3176
|
+
);
|
|
3177
|
+
}
|
|
3178
|
+
if (bridgeAction.type !== "EXECUTE_BRIDGE" || bridgeAction.metadata?.chainFamily !== "svm") {
|
|
3179
|
+
throw new Error(
|
|
3180
|
+
`executeSvmCombinedFirstTransfer expects SVM EXECUTE_BRIDGE second, got ${bridgeAction.type}/${bridgeAction.metadata?.chainFamily ?? "unknown"}.`
|
|
3181
|
+
);
|
|
3182
|
+
}
|
|
3183
|
+
const ownerPubkey = approveAction.metadata?.ownerPubkey;
|
|
3184
|
+
const unsignedApproveTxBase64 = approveAction.metadata?.unsignedApproveTxBase64;
|
|
3185
|
+
const tokenSymbol = approveAction.metadata?.tokenSymbol;
|
|
3186
|
+
const unsignedBridgeTxBase64 = bridgeAction.metadata?.unsignedTxBase64;
|
|
3187
|
+
if (typeof ownerPubkey !== "string" || ownerPubkey.trim() === "") {
|
|
3188
|
+
return failBothActions(
|
|
3189
|
+
approveAction,
|
|
3190
|
+
bridgeAction,
|
|
3191
|
+
"APPROVE_SPL metadata is missing ownerPubkey."
|
|
3192
|
+
);
|
|
3193
|
+
}
|
|
3194
|
+
if (typeof unsignedApproveTxBase64 !== "string" || unsignedApproveTxBase64.trim() === "") {
|
|
3195
|
+
return failBothActions(
|
|
3196
|
+
approveAction,
|
|
3197
|
+
bridgeAction,
|
|
3198
|
+
"APPROVE_SPL metadata is missing unsignedApproveTxBase64."
|
|
3199
|
+
);
|
|
3200
|
+
}
|
|
3201
|
+
if (typeof unsignedBridgeTxBase64 !== "string" || unsignedBridgeTxBase64.trim() === "") {
|
|
3202
|
+
return failBothActions(
|
|
3203
|
+
approveAction,
|
|
3204
|
+
bridgeAction,
|
|
3205
|
+
"SVM EXECUTE_BRIDGE metadata is missing unsignedTxBase64."
|
|
3206
|
+
);
|
|
3207
|
+
}
|
|
3208
|
+
const selection = getSolanaWalletSelection(approveAction);
|
|
3209
|
+
appendDebug("info", "SVM_COMBINED: entry", {
|
|
3210
|
+
approveActionId: approveAction.id,
|
|
3211
|
+
bridgeActionId: bridgeAction.id,
|
|
3212
|
+
ownerPubkey,
|
|
3213
|
+
tokenSymbol: typeof tokenSymbol === "string" ? tokenSymbol : null
|
|
3214
|
+
});
|
|
3215
|
+
const signLabel = "SVM_COMBINED signAllSolanaTransactions";
|
|
3216
|
+
let signed;
|
|
3217
|
+
try {
|
|
3218
|
+
signed = await withWatchdog(
|
|
3219
|
+
withTimeout(
|
|
3220
|
+
signAllSolanaTransactions(
|
|
3221
|
+
selection,
|
|
3222
|
+
[unsignedApproveTxBase64, unsignedBridgeTxBase64],
|
|
3223
|
+
ownerPubkey
|
|
3224
|
+
),
|
|
3225
|
+
WALLET_PROMPT_TIMEOUT_MS,
|
|
3226
|
+
signLabel
|
|
3227
|
+
),
|
|
3228
|
+
signLabel
|
|
3229
|
+
);
|
|
3230
|
+
} catch (err) {
|
|
3231
|
+
const msg = err instanceof Error ? err.message : "Failed to sign Solana transactions";
|
|
3232
|
+
const timedOut = err instanceof PromiseTimeoutError;
|
|
3233
|
+
appendDebug(timedOut ? "warn" : "error", "SVM_COMBINED: signAllTransactions threw", {
|
|
3234
|
+
approveActionId: approveAction.id,
|
|
3235
|
+
bridgeActionId: bridgeAction.id,
|
|
3236
|
+
error: msg,
|
|
3237
|
+
userRejected: isUserRejection(msg),
|
|
3238
|
+
timedOut
|
|
3239
|
+
});
|
|
3240
|
+
if (timedOut) {
|
|
3241
|
+
return failBothActions(
|
|
3242
|
+
approveAction,
|
|
3243
|
+
bridgeAction,
|
|
3244
|
+
WALLET_PROMPT_TIMEOUT_MESSAGE
|
|
3245
|
+
);
|
|
3246
|
+
}
|
|
3247
|
+
if (isUserRejection(msg)) {
|
|
3248
|
+
return failBothActions(
|
|
3249
|
+
approveAction,
|
|
3250
|
+
bridgeAction,
|
|
3251
|
+
"You rejected the Solana transactions. Please approve the prompt in Phantom to continue."
|
|
3252
|
+
);
|
|
3253
|
+
}
|
|
3254
|
+
return failBothActions(approveAction, bridgeAction, msg);
|
|
3255
|
+
}
|
|
3256
|
+
const [approveSignedB64, bridgeSignedB64] = signed.signedTxsBase64;
|
|
3257
|
+
appendDebug("info", "SVM_COMBINED: signAllTransactions returned", {
|
|
3258
|
+
approveActionId: approveAction.id,
|
|
3259
|
+
bridgeActionId: bridgeAction.id
|
|
3260
|
+
});
|
|
3261
|
+
try {
|
|
3262
|
+
options?.onApproveSplConfirming?.(approveAction);
|
|
3263
|
+
} catch (callbackErr) {
|
|
3264
|
+
appendDebug("warn", "SVM_COMBINED: onApproveSplConfirming callback threw", {
|
|
3265
|
+
approveActionId: approveAction.id,
|
|
3266
|
+
error: callbackErr instanceof Error ? callbackErr.message : String(callbackErr)
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
3269
|
+
return {
|
|
3270
|
+
txHash: void 0,
|
|
3271
|
+
actionResults: [
|
|
3272
|
+
{
|
|
3273
|
+
action: approveAction,
|
|
3274
|
+
result: actionSuccess(
|
|
3275
|
+
approveAction,
|
|
3276
|
+
`Signed SPL approval for ${typeof tokenSymbol === "string" ? tokenSymbol : "tokens"}.`,
|
|
3277
|
+
{ signedTxBase64: approveSignedB64, ownerPubkey: signed.ownerPubkey }
|
|
3278
|
+
)
|
|
3279
|
+
},
|
|
3280
|
+
{
|
|
3281
|
+
action: bridgeAction,
|
|
3282
|
+
result: actionSuccess(
|
|
3283
|
+
bridgeAction,
|
|
3284
|
+
"Signed Solana bridge transfer in Phantom.",
|
|
3285
|
+
{ signedTxBase64: bridgeSignedB64, ownerPubkey: signed.ownerPubkey }
|
|
3286
|
+
)
|
|
3287
|
+
}
|
|
3288
|
+
]
|
|
3289
|
+
};
|
|
3290
|
+
}
|
|
3291
|
+
function failBothActions(approveAction, bridgeAction, message) {
|
|
3292
|
+
return {
|
|
3293
|
+
txHash: void 0,
|
|
3294
|
+
actionResults: [
|
|
3295
|
+
{ action: approveAction, result: actionError(approveAction, message) },
|
|
3296
|
+
{ action: bridgeAction, result: actionError(bridgeAction, message) }
|
|
3297
|
+
]
|
|
3298
|
+
};
|
|
3299
|
+
}
|
|
2586
3300
|
async function executeApprovePermit2(action, wagmiConfig2) {
|
|
2587
3301
|
let walletClient = null;
|
|
2588
3302
|
let sender = null;
|
|
@@ -3228,6 +3942,7 @@ function useAuthorizationExecutor(options) {
|
|
|
3228
3942
|
const [results, setResults] = react.useState([]);
|
|
3229
3943
|
const [error, setError] = react.useState(null);
|
|
3230
3944
|
const [currentAction, setCurrentAction] = react.useState(null);
|
|
3945
|
+
const [approveSplConfirming, setApproveSplConfirming] = react.useState(null);
|
|
3231
3946
|
const executingRef = react.useRef(false);
|
|
3232
3947
|
const walletCapabilitiesRef = react.useRef({});
|
|
3233
3948
|
const walletCapabilitiesRefreshedRef = react.useRef(false);
|
|
@@ -3266,16 +3981,17 @@ function useAuthorizationExecutor(options) {
|
|
|
3266
3981
|
}
|
|
3267
3982
|
setError(null);
|
|
3268
3983
|
setCurrentAction(null);
|
|
3984
|
+
setApproveSplConfirming(null);
|
|
3269
3985
|
setExecuting(false);
|
|
3270
3986
|
executingRef.current = false;
|
|
3271
3987
|
}, []);
|
|
3272
3988
|
const dispatchAction = react.useCallback(
|
|
3273
|
-
async (action) => {
|
|
3989
|
+
async (action, dispatchOptions) => {
|
|
3274
3990
|
setCurrentAction(action);
|
|
3275
3991
|
switch (action.type) {
|
|
3276
3992
|
case "OPEN_PROVIDER": {
|
|
3277
3993
|
const result = await executeOpenProvider(action, wagmiConfig2, connectors, connectAsync);
|
|
3278
|
-
if (result.status === "success") {
|
|
3994
|
+
if (result.status === "success" && action.metadata?.chainFamily !== "svm") {
|
|
3279
3995
|
walletCapabilitiesRef.current = await refreshWalletCapabilities(
|
|
3280
3996
|
wagmiConfig2,
|
|
3281
3997
|
"open-provider"
|
|
@@ -3287,7 +4003,7 @@ function useAuthorizationExecutor(options) {
|
|
|
3287
4003
|
return executeSelectSource(action, waitForSelection);
|
|
3288
4004
|
case "SWITCH_CHAIN": {
|
|
3289
4005
|
const result = await executeSwitchChain(action, wagmiConfig2, switchChainAsync);
|
|
3290
|
-
if (result.status === "success") {
|
|
4006
|
+
if (result.status === "success" && action.metadata?.chainFamily !== "svm") {
|
|
3291
4007
|
walletCapabilitiesRef.current = await refreshWalletCapabilities(
|
|
3292
4008
|
wagmiConfig2,
|
|
3293
4009
|
"switch-chain"
|
|
@@ -3307,7 +4023,33 @@ function useAuthorizationExecutor(options) {
|
|
|
3307
4023
|
}
|
|
3308
4024
|
return executeSignPermit2(action, wagmiConfig2, apiBaseUrl ?? "", sessionIdRef.current);
|
|
3309
4025
|
}
|
|
4026
|
+
case "APPROVE_SPL": {
|
|
4027
|
+
if (action.metadata?.awaitingLimit) {
|
|
4028
|
+
throw new Error(
|
|
4029
|
+
"APPROVE_SPL action has awaitingLimit. The orchestrator must handle one-tap setup before executing this action."
|
|
4030
|
+
);
|
|
4031
|
+
}
|
|
4032
|
+
const externalConfirmingCallback = dispatchOptions?.onApproveSplConfirming;
|
|
4033
|
+
return executeApproveSplSolana(action, {
|
|
4034
|
+
onConfirming: (a) => {
|
|
4035
|
+
setApproveSplConfirming(a);
|
|
4036
|
+
if (externalConfirmingCallback) {
|
|
4037
|
+
try {
|
|
4038
|
+
externalConfirmingCallback(a);
|
|
4039
|
+
} catch (callbackErr) {
|
|
4040
|
+
appendDebug("warn", "APPROVE_SPL: orchestrator onApproveSplConfirming threw", {
|
|
4041
|
+
actionId: a.id,
|
|
4042
|
+
error: callbackErr instanceof Error ? callbackErr.message : String(callbackErr)
|
|
4043
|
+
});
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
});
|
|
4048
|
+
}
|
|
3310
4049
|
case "EXECUTE_BRIDGE":
|
|
4050
|
+
if (action.metadata?.chainFamily === "svm") {
|
|
4051
|
+
return executeExecuteBridgeSolana(action);
|
|
4052
|
+
}
|
|
3311
4053
|
return executeExecuteBridge(action, wagmiConfig2);
|
|
3312
4054
|
default:
|
|
3313
4055
|
return actionError(action, `Unsupported action type: ${action.type}`);
|
|
@@ -3320,7 +4062,11 @@ function useAuthorizationExecutor(options) {
|
|
|
3320
4062
|
if (options2?.sessionId) {
|
|
3321
4063
|
sessionIdRef.current = options2.sessionId;
|
|
3322
4064
|
}
|
|
3323
|
-
|
|
4065
|
+
try {
|
|
4066
|
+
return await dispatchAction(action, options2);
|
|
4067
|
+
} finally {
|
|
4068
|
+
setApproveSplConfirming(null);
|
|
4069
|
+
}
|
|
3324
4070
|
},
|
|
3325
4071
|
[dispatchAction]
|
|
3326
4072
|
);
|
|
@@ -3341,6 +4087,43 @@ function useAuthorizationExecutor(options) {
|
|
|
3341
4087
|
},
|
|
3342
4088
|
[apiBaseUrl, wagmiConfig2]
|
|
3343
4089
|
);
|
|
4090
|
+
const executeSvmCombinedFirstTransferImpl = react.useCallback(
|
|
4091
|
+
async (actions, options2) => {
|
|
4092
|
+
setCurrentAction(actions[0]?.action ?? null);
|
|
4093
|
+
const externalConfirmingCallback = options2?.onApproveSplConfirming;
|
|
4094
|
+
return executeSvmCombinedFirstTransfer(actions, {
|
|
4095
|
+
onApproveSplConfirming: (a) => {
|
|
4096
|
+
setApproveSplConfirming(a);
|
|
4097
|
+
if (externalConfirmingCallback) {
|
|
4098
|
+
try {
|
|
4099
|
+
externalConfirmingCallback(a);
|
|
4100
|
+
} catch (callbackErr) {
|
|
4101
|
+
appendDebug("warn", "SVM_COMBINED: orchestrator onApproveSplConfirming threw", {
|
|
4102
|
+
actionId: a.id,
|
|
4103
|
+
error: callbackErr instanceof Error ? callbackErr.message : String(callbackErr)
|
|
4104
|
+
});
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
});
|
|
4109
|
+
},
|
|
4110
|
+
[]
|
|
4111
|
+
);
|
|
4112
|
+
const canSignAllSolanaTransactions = react.useCallback(
|
|
4113
|
+
async (action) => {
|
|
4114
|
+
try {
|
|
4115
|
+
const selection = getSolanaWalletSelection(action);
|
|
4116
|
+
return await supportsSignAllSolanaTransactions(selection);
|
|
4117
|
+
} catch (err) {
|
|
4118
|
+
appendDebug("warn", "SVM_COMBINED: signAllTransactions feature-detect failed", {
|
|
4119
|
+
actionId: action.id,
|
|
4120
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4121
|
+
});
|
|
4122
|
+
return false;
|
|
4123
|
+
}
|
|
4124
|
+
},
|
|
4125
|
+
[]
|
|
4126
|
+
);
|
|
3344
4127
|
const canBatch = react.useCallback(async () => {
|
|
3345
4128
|
const cacheKey = getBatchCapabilityCacheKey(wagmiConfig2);
|
|
3346
4129
|
const cachedDecision = batchCapabilityDecisionRef.current;
|
|
@@ -3384,11 +4167,13 @@ function useAuthorizationExecutor(options) {
|
|
|
3384
4167
|
setResults([]);
|
|
3385
4168
|
setError(null);
|
|
3386
4169
|
setBatchTxHash(null);
|
|
4170
|
+
setApproveSplConfirming(null);
|
|
3387
4171
|
return true;
|
|
3388
4172
|
}, []);
|
|
3389
4173
|
const endExecution = react.useCallback(() => {
|
|
3390
4174
|
sessionIdRef.current = null;
|
|
3391
4175
|
setCurrentAction(null);
|
|
4176
|
+
setApproveSplConfirming(null);
|
|
3392
4177
|
setExecuting(false);
|
|
3393
4178
|
executingRef.current = false;
|
|
3394
4179
|
}, []);
|
|
@@ -3481,12 +4266,15 @@ function useAuthorizationExecutor(options) {
|
|
|
3481
4266
|
results,
|
|
3482
4267
|
error,
|
|
3483
4268
|
currentAction,
|
|
4269
|
+
approveSplConfirming,
|
|
3484
4270
|
pendingSelectSource,
|
|
3485
4271
|
resolveSelectSource,
|
|
3486
4272
|
cancelPendingExecution,
|
|
3487
4273
|
batchTxHash,
|
|
3488
4274
|
executeAction,
|
|
3489
4275
|
executeBatch,
|
|
4276
|
+
executeSvmCombinedFirstTransfer: executeSvmCombinedFirstTransferImpl,
|
|
4277
|
+
canSignAllSolanaTransactions,
|
|
3490
4278
|
canBatch,
|
|
3491
4279
|
checkPaymasterSupport,
|
|
3492
4280
|
getCapabilitySnapshot,
|
|
@@ -3515,6 +4303,8 @@ function isWebAuthnPasskeyDismissalError(err) {
|
|
|
3515
4303
|
|
|
3516
4304
|
// src/hooks/useTransferSigning.ts
|
|
3517
4305
|
var TRANSFER_SIGN_MAX_POLLS = 60;
|
|
4306
|
+
var FAST_POLL_COUNT2 = 6;
|
|
4307
|
+
var FAST_POLL_INTERVAL_MS2 = 500;
|
|
3518
4308
|
function waitForDocumentFocus2(timeoutMs = 5e3, intervalMs = 100) {
|
|
3519
4309
|
return new Promise((resolve, reject) => {
|
|
3520
4310
|
if (typeof document === "undefined") {
|
|
@@ -3542,9 +4332,23 @@ function hexToBytes(hex) {
|
|
|
3542
4332
|
const bytes = clean.match(/.{1,2}/g).map((b) => parseInt(b, 16));
|
|
3543
4333
|
return new Uint8Array(bytes);
|
|
3544
4334
|
}
|
|
4335
|
+
function base64ToBytes2(base64) {
|
|
4336
|
+
const binary = atob(base64);
|
|
4337
|
+
const bytes = new Uint8Array(binary.length);
|
|
4338
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
4339
|
+
bytes[i] = binary.charCodeAt(i);
|
|
4340
|
+
}
|
|
4341
|
+
return bytes;
|
|
4342
|
+
}
|
|
3545
4343
|
function toBase642(buffer) {
|
|
3546
4344
|
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
3547
4345
|
}
|
|
4346
|
+
function getSigningChallenge(payload) {
|
|
4347
|
+
if (payload.chainFamily === "svm") {
|
|
4348
|
+
return base64ToBytes2(payload.message);
|
|
4349
|
+
}
|
|
4350
|
+
return hexToBytes(payload.userOpHash);
|
|
4351
|
+
}
|
|
3548
4352
|
function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
3549
4353
|
const blinkConfig = useOptionalBlinkConfig();
|
|
3550
4354
|
const apiBaseUrl = options?.apiBaseUrl ?? blinkConfig?.apiBaseUrl;
|
|
@@ -3561,7 +4365,7 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3561
4365
|
const [error, setError] = react.useState(null);
|
|
3562
4366
|
const [passkeyDismissed, setPasskeyDismissed] = react.useState(false);
|
|
3563
4367
|
const signTransfer2 = react.useCallback(
|
|
3564
|
-
async (transferId) => {
|
|
4368
|
+
async (transferId, opts) => {
|
|
3565
4369
|
setSigning(true);
|
|
3566
4370
|
setError(null);
|
|
3567
4371
|
setPasskeyDismissed(false);
|
|
@@ -3578,22 +4382,28 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3578
4382
|
throw new Error("Could not get access token");
|
|
3579
4383
|
}
|
|
3580
4384
|
let payload = null;
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
4385
|
+
if (opts?.knownSignPayload) {
|
|
4386
|
+
payload = opts.knownSignPayload;
|
|
4387
|
+
setSignPayload(payload);
|
|
4388
|
+
} else {
|
|
4389
|
+
for (let i = 0; i < TRANSFER_SIGN_MAX_POLLS; i++) {
|
|
4390
|
+
const transfer = await fetchTransfer(
|
|
4391
|
+
apiBaseUrl,
|
|
4392
|
+
token ?? "",
|
|
4393
|
+
transferId,
|
|
4394
|
+
authorizationSessionToken
|
|
4395
|
+
);
|
|
4396
|
+
if (transfer.signPayload) {
|
|
4397
|
+
payload = transfer.signPayload;
|
|
4398
|
+
setSignPayload(payload);
|
|
4399
|
+
break;
|
|
4400
|
+
}
|
|
4401
|
+
if (transfer.status !== "AUTHORIZED" && transfer.status !== "CREATED") {
|
|
4402
|
+
throw new Error(`Unexpected transfer status: ${transfer.status}`);
|
|
4403
|
+
}
|
|
4404
|
+
const intervalMs = i < FAST_POLL_COUNT2 ? Math.min(FAST_POLL_INTERVAL_MS2, pollIntervalMs) : pollIntervalMs;
|
|
4405
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
3595
4406
|
}
|
|
3596
|
-
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
3597
4407
|
}
|
|
3598
4408
|
if (!payload) {
|
|
3599
4409
|
throw new Error("Timed out waiting for sign payload. Please try again.");
|
|
@@ -3604,8 +4414,7 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3604
4414
|
"Sign payload is missing passkeyCredentialId. Cannot request passkey signing without it."
|
|
3605
4415
|
);
|
|
3606
4416
|
}
|
|
3607
|
-
const
|
|
3608
|
-
let signedUserOp;
|
|
4417
|
+
const challengeBytes = getSigningChallenge(payload);
|
|
3609
4418
|
const allowCredentials = [{
|
|
3610
4419
|
type: "public-key",
|
|
3611
4420
|
id: credentialIdBase64ToBytes(passkeyCredentialId)
|
|
@@ -3624,7 +4433,7 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3624
4433
|
try {
|
|
3625
4434
|
assertion = await navigator.credentials.get({
|
|
3626
4435
|
publicKey: {
|
|
3627
|
-
challenge:
|
|
4436
|
+
challenge: challengeBytes,
|
|
3628
4437
|
rpId: signingRpId,
|
|
3629
4438
|
allowCredentials,
|
|
3630
4439
|
userVerification: "required",
|
|
@@ -3641,18 +4450,24 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3641
4450
|
throw new TransferSigningPasskeyDismissedError();
|
|
3642
4451
|
}
|
|
3643
4452
|
const response = assertion.response;
|
|
3644
|
-
|
|
3645
|
-
...payload.userOp,
|
|
4453
|
+
const webauthnAssertion = {
|
|
3646
4454
|
credentialId: toBase642(assertion.rawId),
|
|
3647
4455
|
signature: toBase642(response.signature),
|
|
3648
4456
|
authenticatorData: toBase642(response.authenticatorData),
|
|
3649
4457
|
clientDataJSON: toBase642(response.clientDataJSON)
|
|
3650
4458
|
};
|
|
4459
|
+
const signedTransfer = payload.chainFamily === "svm" ? {
|
|
4460
|
+
chainFamily: "svm",
|
|
4461
|
+
webauthnAssertion
|
|
4462
|
+
} : {
|
|
4463
|
+
...payload.userOp,
|
|
4464
|
+
...webauthnAssertion
|
|
4465
|
+
};
|
|
3651
4466
|
return await signTransfer(
|
|
3652
4467
|
apiBaseUrl,
|
|
3653
4468
|
token ?? "",
|
|
3654
4469
|
transferId,
|
|
3655
|
-
|
|
4470
|
+
signedTransfer,
|
|
3656
4471
|
authorizationSessionToken
|
|
3657
4472
|
);
|
|
3658
4473
|
} catch (err) {
|
|
@@ -3767,6 +4582,8 @@ function assertLinkedTransferBridgeExecuted(params) {
|
|
|
3767
4582
|
// src/hooks/useAuthorizationOrchestrator.ts
|
|
3768
4583
|
var ACTION_POLL_INTERVAL_MS2 = 500;
|
|
3769
4584
|
var ACTION_POLL_MAX_RETRIES2 = 20;
|
|
4585
|
+
var REPORT_COMPLETION_TIMEOUT_MS = 9e4;
|
|
4586
|
+
var REPORT_COMPLETION_TIMEOUT_MESSAGE = "REPORT_COMPLETION_TIMEOUT";
|
|
3770
4587
|
function useAuthorizationOrchestrator(deps) {
|
|
3771
4588
|
const blinkConfig = useOptionalBlinkConfig();
|
|
3772
4589
|
const resolvedApiBaseUrl = deps.apiBaseUrl ?? blinkConfig?.apiBaseUrl;
|
|
@@ -3894,6 +4711,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
3894
4711
|
waitForLinkedTransferSession: options?.waitForLinkedTransferSession ?? false
|
|
3895
4712
|
});
|
|
3896
4713
|
let cachedBatchSupport = null;
|
|
4714
|
+
let cachedSvmSignAllSupport = null;
|
|
3897
4715
|
while (mergedPending.length > 0) {
|
|
3898
4716
|
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
3899
4717
|
mergedPending = getMergedPending(sessions, completedIds);
|
|
@@ -3981,6 +4799,112 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
3981
4799
|
});
|
|
3982
4800
|
continue;
|
|
3983
4801
|
}
|
|
4802
|
+
if (action.type === "APPROVE_SPL" && (action.metadata?.awaitingLimit || options?.alwaysPauseForOneTap) && !oneTapCompletedActionIds.has(action.id)) {
|
|
4803
|
+
if (options?.onAwaitingOneTap) {
|
|
4804
|
+
console.info("[blink-sdk][orchestrator] Awaiting Solana one-tap setup via callback.", {
|
|
4805
|
+
actionId: action.id,
|
|
4806
|
+
ownerSessionId
|
|
4807
|
+
});
|
|
4808
|
+
await options.onAwaitingOneTap(action);
|
|
4809
|
+
console.info("[blink-sdk][orchestrator] Solana one-tap setup resumed via callback.", {
|
|
4810
|
+
actionId: action.id,
|
|
4811
|
+
ownerSessionId
|
|
4812
|
+
});
|
|
4813
|
+
} else {
|
|
4814
|
+
console.info("[blink-sdk][orchestrator] Awaiting Solana one-tap setup.", {
|
|
4815
|
+
actionId: action.id,
|
|
4816
|
+
ownerSessionId
|
|
4817
|
+
});
|
|
4818
|
+
await waitForOneTapPause(action);
|
|
4819
|
+
console.info("[blink-sdk][orchestrator] Solana one-tap setup resumed.", {
|
|
4820
|
+
actionId: action.id,
|
|
4821
|
+
ownerSessionId
|
|
4822
|
+
});
|
|
4823
|
+
}
|
|
4824
|
+
oneTapCompletedActionIds.add(action.id);
|
|
4825
|
+
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4826
|
+
await refreshAllSessions(apiBaseUrl, sessions, actionSessionMap);
|
|
4827
|
+
queueLinkedTransferSessions({
|
|
4828
|
+
sessions,
|
|
4829
|
+
linkedTransferSessionIds,
|
|
4830
|
+
pendingSessionIdsRef
|
|
4831
|
+
});
|
|
4832
|
+
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4833
|
+
markPendingOneTapActionsCompleted(sessions, oneTapCompletedActionIds);
|
|
4834
|
+
mergedPending = await waitForPendingActions({
|
|
4835
|
+
apiBaseUrl,
|
|
4836
|
+
sessions,
|
|
4837
|
+
completedIds,
|
|
4838
|
+
actionSessionMap,
|
|
4839
|
+
pendingSessionIdsRef,
|
|
4840
|
+
linkedTransferSessionIds,
|
|
4841
|
+
waitForLinkedTransferSession: options?.waitForLinkedTransferSession ?? false
|
|
4842
|
+
});
|
|
4843
|
+
continue;
|
|
4844
|
+
}
|
|
4845
|
+
if (isSvmCombinedFirstTransferPair(mergedPending)) {
|
|
4846
|
+
const pair = mergedPending.slice(0, 2);
|
|
4847
|
+
const [svmApproveAction, svmBridgeAction] = pair;
|
|
4848
|
+
if (cachedSvmSignAllSupport == null) {
|
|
4849
|
+
cachedSvmSignAllSupport = await authExecutor.canSignAllSolanaTransactions(
|
|
4850
|
+
svmApproveAction
|
|
4851
|
+
);
|
|
4852
|
+
}
|
|
4853
|
+
appendDebug("info", "orchestrator:svm-combined evaluation", {
|
|
4854
|
+
approveActionId: svmApproveAction.id,
|
|
4855
|
+
bridgeActionId: svmBridgeAction.id,
|
|
4856
|
+
ownerSessionId,
|
|
4857
|
+
signAllSupported: cachedSvmSignAllSupport
|
|
4858
|
+
});
|
|
4859
|
+
if (cachedSvmSignAllSupport) {
|
|
4860
|
+
const svmInputs = pair.map((candidate) => ({
|
|
4861
|
+
action: candidate,
|
|
4862
|
+
sessionId: actionSessionMap.get(candidate.id) ?? sessionId
|
|
4863
|
+
}));
|
|
4864
|
+
const svmBatchResult = await authExecutor.executeSvmCombinedFirstTransfer(
|
|
4865
|
+
svmInputs,
|
|
4866
|
+
{
|
|
4867
|
+
onApproveSplConfirming: options?.onApproveSplConfirming
|
|
4868
|
+
}
|
|
4869
|
+
);
|
|
4870
|
+
const errorResult = svmBatchResult.actionResults.find(
|
|
4871
|
+
({ result: result2 }) => result2.status === "error"
|
|
4872
|
+
);
|
|
4873
|
+
if (errorResult) {
|
|
4874
|
+
authExecutor.addResult(errorResult.result);
|
|
4875
|
+
throw new Error(errorResult.result.message);
|
|
4876
|
+
}
|
|
4877
|
+
for (const { action: svmAction, result: result2 } of svmBatchResult.actionResults) {
|
|
4878
|
+
completedIds.add(svmAction.id);
|
|
4879
|
+
oneTapCompletedActionIds.delete(svmAction.id);
|
|
4880
|
+
authExecutor.addResult(result2);
|
|
4881
|
+
const svmOwnerSessionId = actionSessionMap.get(svmAction.id) ?? ownerSessionId;
|
|
4882
|
+
const reportedSession2 = await reportActionCompletionWithLogging(
|
|
4883
|
+
apiBaseUrl,
|
|
4884
|
+
svmAction,
|
|
4885
|
+
svmOwnerSessionId,
|
|
4886
|
+
result2
|
|
4887
|
+
);
|
|
4888
|
+
updateTrackedSession(sessions, svmOwnerSessionId, reportedSession2, actionSessionMap);
|
|
4889
|
+
}
|
|
4890
|
+
queueLinkedTransferSessions({
|
|
4891
|
+
sessions,
|
|
4892
|
+
linkedTransferSessionIds,
|
|
4893
|
+
pendingSessionIdsRef
|
|
4894
|
+
});
|
|
4895
|
+
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4896
|
+
mergedPending = await waitForPendingActions({
|
|
4897
|
+
apiBaseUrl,
|
|
4898
|
+
sessions,
|
|
4899
|
+
completedIds,
|
|
4900
|
+
actionSessionMap,
|
|
4901
|
+
pendingSessionIdsRef,
|
|
4902
|
+
linkedTransferSessionIds,
|
|
4903
|
+
waitForLinkedTransferSession: options?.waitForLinkedTransferSession ?? false
|
|
4904
|
+
});
|
|
4905
|
+
continue;
|
|
4906
|
+
}
|
|
4907
|
+
}
|
|
3984
4908
|
if (isBatchableAction(action)) {
|
|
3985
4909
|
const batchable = getLeadingBatchableActions(
|
|
3986
4910
|
mergedPending
|
|
@@ -4007,7 +4931,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
4007
4931
|
pendingSessionIdsRef
|
|
4008
4932
|
});
|
|
4009
4933
|
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4010
|
-
|
|
4934
|
+
markPendingOneTapActionsCompleted(sessions, oneTapCompletedActionIds);
|
|
4011
4935
|
mergedPending = await waitForPendingActions({
|
|
4012
4936
|
apiBaseUrl,
|
|
4013
4937
|
sessions,
|
|
@@ -4038,7 +4962,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
4038
4962
|
pendingSessionIdsRef
|
|
4039
4963
|
});
|
|
4040
4964
|
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4041
|
-
|
|
4965
|
+
markPendingOneTapActionsCompleted(sessions, oneTapCompletedActionIds);
|
|
4042
4966
|
mergedPending = await waitForPendingActions({
|
|
4043
4967
|
apiBaseUrl,
|
|
4044
4968
|
sessions,
|
|
@@ -4110,7 +5034,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
4110
5034
|
continue;
|
|
4111
5035
|
}
|
|
4112
5036
|
}
|
|
4113
|
-
const isPrePromptProbableActionType = action.type === "SIGN_PERMIT2" || action.type === "APPROVE_PERMIT2";
|
|
5037
|
+
const isPrePromptProbableActionType = action.type === "SIGN_PERMIT2" || action.type === "APPROVE_PERMIT2" || action.type === "APPROVE_SPL";
|
|
4114
5038
|
if (isPrePromptProbableActionType && probeBeforePrompt && !preProbedIds.has(action.id)) {
|
|
4115
5039
|
preProbedIds.add(action.id);
|
|
4116
5040
|
appendDebug("info", `${action.type}: pre-prompt probe start`, {
|
|
@@ -4155,7 +5079,10 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
4155
5079
|
actionId: action.id,
|
|
4156
5080
|
ownerSessionId
|
|
4157
5081
|
});
|
|
4158
|
-
const result = await authExecutor.executeAction(action, {
|
|
5082
|
+
const result = await authExecutor.executeAction(action, {
|
|
5083
|
+
sessionId: ownerSessionId,
|
|
5084
|
+
onApproveSplConfirming: options?.onApproveSplConfirming
|
|
5085
|
+
});
|
|
4159
5086
|
if (result.status === "success" && (action.type === "OPEN_PROVIDER" || action.type === "SWITCH_CHAIN")) {
|
|
4160
5087
|
cachedBatchSupport = null;
|
|
4161
5088
|
}
|
|
@@ -4395,7 +5322,24 @@ function getMergedPending(sessions, completedIds) {
|
|
|
4395
5322
|
for (const { session } of sessions) {
|
|
4396
5323
|
allActions.push(...getPendingActions(session, completedIds));
|
|
4397
5324
|
}
|
|
4398
|
-
|
|
5325
|
+
const hasPendingSvmSetupAction = allActions.some((action) => isSvmSetupAction(action));
|
|
5326
|
+
return allActions.sort((a, b) => {
|
|
5327
|
+
const leftIsDeferredSvmBridge = hasPendingSvmSetupAction && isSvmTransferBridgeAction(a);
|
|
5328
|
+
const rightIsDeferredSvmBridge = hasPendingSvmSetupAction && isSvmTransferBridgeAction(b);
|
|
5329
|
+
if (leftIsDeferredSvmBridge !== rightIsDeferredSvmBridge) {
|
|
5330
|
+
return leftIsDeferredSvmBridge ? 1 : -1;
|
|
5331
|
+
}
|
|
5332
|
+
return a.orderIndex - b.orderIndex;
|
|
5333
|
+
});
|
|
5334
|
+
}
|
|
5335
|
+
function isSvmSetupAction(action) {
|
|
5336
|
+
if (action.type === "APPROVE_SPL") {
|
|
5337
|
+
return true;
|
|
5338
|
+
}
|
|
5339
|
+
return action.type === "OPEN_PROVIDER" && action.metadata?.chainFamily === "svm";
|
|
5340
|
+
}
|
|
5341
|
+
function isSvmTransferBridgeAction(action) {
|
|
5342
|
+
return action.type === "EXECUTE_BRIDGE" && action.metadata?.chainFamily === "svm";
|
|
4399
5343
|
}
|
|
4400
5344
|
async function waitForPendingActions(params) {
|
|
4401
5345
|
const {
|
|
@@ -4469,14 +5413,19 @@ async function reportActionCompletionWithLogging(apiBaseUrl, action, ownerSessio
|
|
|
4469
5413
|
actionId: action.id,
|
|
4470
5414
|
ownerSessionId
|
|
4471
5415
|
});
|
|
5416
|
+
const label = `reportActionCompletion ${action.type}`;
|
|
4472
5417
|
try {
|
|
4473
5418
|
const reportedSession = await withWatchdog(
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
5419
|
+
withTimeout(
|
|
5420
|
+
reportActionCompletion(
|
|
5421
|
+
apiBaseUrl,
|
|
5422
|
+
action.id,
|
|
5423
|
+
result.data ?? {}
|
|
5424
|
+
),
|
|
5425
|
+
REPORT_COMPLETION_TIMEOUT_MS,
|
|
5426
|
+
label
|
|
4478
5427
|
),
|
|
4479
|
-
|
|
5428
|
+
label
|
|
4480
5429
|
);
|
|
4481
5430
|
console.info("[blink-sdk][orchestrator] Action completion reported.", {
|
|
4482
5431
|
actionId: action.id,
|
|
@@ -4492,16 +5441,22 @@ async function reportActionCompletionWithLogging(apiBaseUrl, action, ownerSessio
|
|
|
4492
5441
|
});
|
|
4493
5442
|
return reportedSession;
|
|
4494
5443
|
} catch (err) {
|
|
5444
|
+
const timedOut = err instanceof PromiseTimeoutError;
|
|
4495
5445
|
console.warn("[blink-sdk][orchestrator] Failed to report action completion.", {
|
|
4496
5446
|
actionId: action.id,
|
|
4497
5447
|
actionType: action.type,
|
|
4498
5448
|
ownerSessionId,
|
|
5449
|
+
timedOut,
|
|
4499
5450
|
error: err instanceof Error ? err.message : String(err)
|
|
4500
5451
|
});
|
|
4501
|
-
appendDebug("error", `orchestrator:reportActionCompletion failed ${action.type}`, {
|
|
5452
|
+
appendDebug(timedOut ? "warn" : "error", `orchestrator:reportActionCompletion ${timedOut ? "timed out" : "failed"} ${action.type}`, {
|
|
4502
5453
|
actionId: action.id,
|
|
5454
|
+
timedOut,
|
|
4503
5455
|
error: err instanceof Error ? err.message : String(err)
|
|
4504
5456
|
});
|
|
5457
|
+
if (timedOut) {
|
|
5458
|
+
throw new Error(REPORT_COMPLETION_TIMEOUT_MESSAGE);
|
|
5459
|
+
}
|
|
4505
5460
|
throw err;
|
|
4506
5461
|
}
|
|
4507
5462
|
}
|
|
@@ -4534,10 +5489,10 @@ function updateTrackedSession(sessions, ownerSessionId, reportedSession, actionS
|
|
|
4534
5489
|
}
|
|
4535
5490
|
}
|
|
4536
5491
|
}
|
|
4537
|
-
function
|
|
5492
|
+
function markPendingOneTapActionsCompleted(sessions, oneTapCompletedActionIds) {
|
|
4538
5493
|
for (const { session } of sessions) {
|
|
4539
5494
|
for (const action of session.actions) {
|
|
4540
|
-
if (action.type === "SIGN_PERMIT2" && action.status === "PENDING") {
|
|
5495
|
+
if ((action.type === "SIGN_PERMIT2" || action.type === "APPROVE_SPL") && action.status === "PENDING") {
|
|
4541
5496
|
oneTapCompletedActionIds.add(action.id);
|
|
4542
5497
|
}
|
|
4543
5498
|
}
|
|
@@ -4590,6 +5545,8 @@ function screenForPhase(phase) {
|
|
|
4590
5545
|
case "completed":
|
|
4591
5546
|
case "failed":
|
|
4592
5547
|
return "success";
|
|
5548
|
+
case "amount-too-low":
|
|
5549
|
+
return "amount-too-low";
|
|
4593
5550
|
}
|
|
4594
5551
|
}
|
|
4595
5552
|
|
|
@@ -4639,6 +5596,12 @@ function hasActiveWallet(accounts) {
|
|
|
4639
5596
|
return accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
|
|
4640
5597
|
}
|
|
4641
5598
|
function resolveTerminalPhase(state) {
|
|
5599
|
+
if (state.amountTooLow != null) {
|
|
5600
|
+
return {
|
|
5601
|
+
step: "amount-too-low",
|
|
5602
|
+
minAmountUsd: state.amountTooLow.minAmountUsd
|
|
5603
|
+
};
|
|
5604
|
+
}
|
|
4642
5605
|
const transferCompleted = state.transfer?.status === "COMPLETED";
|
|
4643
5606
|
const needsPasskeyBootstrap = state.privyAuthenticated && !state.activeCredentialId;
|
|
4644
5607
|
if (transferCompleted && !needsPasskeyBootstrap && !state.loginRequested) {
|
|
@@ -4792,7 +5755,8 @@ function createInitialState(config) {
|
|
|
4792
5755
|
setupDepositToken: null,
|
|
4793
5756
|
setupDepositConfirmed: false,
|
|
4794
5757
|
guestWalletPrepared: null,
|
|
4795
|
-
guestWalletDeeplinksPreparing: false
|
|
5758
|
+
guestWalletDeeplinksPreparing: false,
|
|
5759
|
+
amountTooLow: null
|
|
4796
5760
|
};
|
|
4797
5761
|
}
|
|
4798
5762
|
function clearAuthenticatedSessionState(state) {
|
|
@@ -4829,7 +5793,8 @@ function clearAuthenticatedSessionState(state) {
|
|
|
4829
5793
|
setupDepositToken: null,
|
|
4830
5794
|
setupDepositConfirmed: false,
|
|
4831
5795
|
guestWalletPrepared: null,
|
|
4832
|
-
guestWalletDeeplinksPreparing: false
|
|
5796
|
+
guestWalletDeeplinksPreparing: false,
|
|
5797
|
+
amountTooLow: null
|
|
4833
5798
|
};
|
|
4834
5799
|
}
|
|
4835
5800
|
function paymentReducer(state, action) {
|
|
@@ -5168,6 +6133,13 @@ function applyAction(state, action) {
|
|
|
5168
6133
|
};
|
|
5169
6134
|
case "SET_ERROR":
|
|
5170
6135
|
return { ...state, error: action.error };
|
|
6136
|
+
case "AMOUNT_TOO_LOW":
|
|
6137
|
+
return {
|
|
6138
|
+
...state,
|
|
6139
|
+
amountTooLow: { minAmountUsd: action.minAmountUsd },
|
|
6140
|
+
creatingTransfer: false,
|
|
6141
|
+
error: null
|
|
6142
|
+
};
|
|
5171
6143
|
case "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP":
|
|
5172
6144
|
return { ...state, oneTapLimitSavedDuringSetup: action.saved };
|
|
5173
6145
|
case "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET":
|
|
@@ -5201,7 +6173,8 @@ function applyAction(state, action) {
|
|
|
5201
6173
|
setupDepositConfirmed: false,
|
|
5202
6174
|
setupFlowScreen: null,
|
|
5203
6175
|
guestWalletPrepared: null,
|
|
5204
|
-
guestWalletDeeplinksPreparing: false
|
|
6176
|
+
guestWalletDeeplinksPreparing: false,
|
|
6177
|
+
amountTooLow: null
|
|
5205
6178
|
};
|
|
5206
6179
|
case "LOGOUT":
|
|
5207
6180
|
return {
|
|
@@ -5548,6 +6521,17 @@ var RABBY_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="70 88 386 334"
|
|
|
5548
6521
|
</linearGradient>
|
|
5549
6522
|
</defs>
|
|
5550
6523
|
</svg>`;
|
|
6524
|
+
var PHANTOM_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" viewBox="0 0 1200 1200" fill="none">
|
|
6525
|
+
<g clip-path="url(#clip0_2596_138580)">
|
|
6526
|
+
<rect y="-0.000976562" width="1200" height="1200" fill="#AB9FF2"/>
|
|
6527
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M522.218 764.813C475.101 837.011 396.147 928.377 291.089 928.377C241.425 928.377 193.671 907.932 193.671 819.121C193.671 592.942 502.479 242.812 789.003 242.812C952.003 242.812 1016.95 355.901 1016.95 484.325C1016.95 649.167 909.979 837.65 803.647 837.65C769.901 837.65 753.346 819.121 753.346 789.731C753.346 782.064 754.62 773.758 757.167 764.813C720.874 826.788 650.835 884.292 585.253 884.292C537.499 884.292 513.304 854.262 513.304 812.093C513.304 796.759 516.487 780.786 522.218 764.813ZM769.035 479.871C769.035 517.293 746.956 536.003 722.258 536.003C697.185 536.003 675.481 517.293 675.481 479.871C675.481 442.449 697.185 423.738 722.258 423.738C746.956 423.738 769.035 442.449 769.035 479.871ZM909.367 479.872C909.367 517.294 887.288 536.005 862.59 536.005C837.517 536.005 815.813 517.294 815.813 479.872C815.813 442.45 837.517 423.74 862.59 423.74C887.288 423.74 909.367 442.45 909.367 479.872Z" fill="#FFFDF8"/>
|
|
6528
|
+
</g>
|
|
6529
|
+
<defs>
|
|
6530
|
+
<clipPath id="clip0_2596_138580">
|
|
6531
|
+
<rect y="-0.000976562" width="1200" height="1200" rx="600" fill="white"/>
|
|
6532
|
+
</clipPath>
|
|
6533
|
+
</defs>
|
|
6534
|
+
</svg>`;
|
|
5551
6535
|
var BLINK_SVG = `<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
5552
6536
|
<g clip-path="url(#clip0_125_480)">
|
|
5553
6537
|
<rect width="100" height="100" rx="18.5874" fill="black"/>
|
|
@@ -5574,6 +6558,37 @@ var TETHER_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800">
|
|
|
5574
6558
|
<circle fill="#009393" cx="400" cy="400" r="400"/>
|
|
5575
6559
|
<path fill="#fff" fill-rule="evenodd" d="M400.49,428.59c68.79,0,126.28-11.63,140.33-27.17-11.93-13.18-55.08-23.56-109.88-26.4v32.83c-9.81.51-20.01.76-30.46.76s-20.65-.25-30.48-.76v-32.83c-54.78,2.84-97.95,13.22-109.88,26.4,14.07,15.54,71.57,27.17,140.36,27.17ZM522.71,274.06v45.21h-91.77v31.35c64.46,3.35,112.83,17.13,113.19,33.62v34.38c-.36,16.49-48.73,30.24-113.19,33.6v76.94h-60.93v-76.94c-64.46-3.35-112.81-17.11-113.17-33.6v-34.38c.36-16.49,48.71-30.27,113.17-33.62v-31.35h-91.77v-45.21h244.48ZM242.15,202.11h322.16c7.7,0,14.79,4.05,18.63,10.63l93.85,161.16c4.86,8.36,3.42,18.91-3.52,25.68l-258.34,252.18c-8.38,8.17-21.84,8.17-30.2,0L126.71,399.92c-7.09-6.94-8.43-17.79-3.2-26.19l100.33-161.49c3.91-6.28,10.85-10.12,18.32-10.12Z"/>
|
|
5576
6560
|
</svg>`;
|
|
6561
|
+
var USDH_SVG = `<svg width="460" height="460" viewBox="0 0 460 460" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
6562
|
+
<rect width="460" height="460" rx="230" fill="url(#paint0_linear_143_25942)"/>
|
|
6563
|
+
<path d="M246 126L214 126L214 61L246 61L246 126Z" fill="url(#paint1_linear_143_25942)"/>
|
|
6564
|
+
<path d="M246 336L214 336L214 401L246 401L246 336Z" fill="url(#paint2_linear_143_25942)"/>
|
|
6565
|
+
<path d="M165 320.5L165 289.5L127 289.5L127 320.5L165 320.5Z" fill="url(#paint3_linear_143_25942)"/>
|
|
6566
|
+
<path d="M290.5 142V173H165V216H325V320.5H165V289.5H294V247H134V142H290.5Z" fill="#4AFFC6"/>
|
|
6567
|
+
<path d="M290 173L290 142L335 142L335 173L290 173Z" fill="url(#paint4_linear_143_25942)"/>
|
|
6568
|
+
<defs>
|
|
6569
|
+
<linearGradient id="paint0_linear_143_25942" x1="230" y1="0" x2="230" y2="460" gradientUnits="userSpaceOnUse">
|
|
6570
|
+
<stop stop-color="#063124"/>
|
|
6571
|
+
<stop offset="1" stop-color="#002B1E"/>
|
|
6572
|
+
</linearGradient>
|
|
6573
|
+
<linearGradient id="paint1_linear_143_25942" x1="230" y1="126" x2="230" y2="61" gradientUnits="userSpaceOnUse">
|
|
6574
|
+
<stop stop-color="#4AFFC6"/>
|
|
6575
|
+
<stop offset="0.971448" stop-color="#4AFFC6" stop-opacity="0"/>
|
|
6576
|
+
</linearGradient>
|
|
6577
|
+
<linearGradient id="paint2_linear_143_25942" x1="230" y1="336" x2="230" y2="401" gradientUnits="userSpaceOnUse">
|
|
6578
|
+
<stop stop-color="#4AFFC6"/>
|
|
6579
|
+
<stop offset="0.971448" stop-color="#4AFFC6" stop-opacity="0"/>
|
|
6580
|
+
</linearGradient>
|
|
6581
|
+
<linearGradient id="paint3_linear_143_25942" x1="165" y1="305" x2="127" y2="305" gradientUnits="userSpaceOnUse">
|
|
6582
|
+
<stop offset="0.112947" stop-color="#4AFFC6"/>
|
|
6583
|
+
<stop offset="1" stop-color="#4AFFC6" stop-opacity="0"/>
|
|
6584
|
+
</linearGradient>
|
|
6585
|
+
<linearGradient id="paint4_linear_143_25942" x1="290" y1="157.5" x2="335" y2="157.5" gradientUnits="userSpaceOnUse">
|
|
6586
|
+
<stop offset="0.112947" stop-color="#4AFFC6"/>
|
|
6587
|
+
<stop offset="1" stop-color="#4AFFC6" stop-opacity="0"/>
|
|
6588
|
+
</linearGradient>
|
|
6589
|
+
</defs>
|
|
6590
|
+
</svg>
|
|
6591
|
+
`;
|
|
5577
6592
|
var BLINK_LOGO = svgToDataUri(BLINK_SVG);
|
|
5578
6593
|
var BLINK_MASCOT = BLINK_LOGO;
|
|
5579
6594
|
var BLINK_SUCCESS_ILLUSTRATION = BLINK_SUCCESS_ILLUSTRATION_DATA_URI;
|
|
@@ -5590,19 +6605,24 @@ var POLYGON_CHAIN_LOGO = svgToDataUri(POLYGON_CHAIN_SVG);
|
|
|
5590
6605
|
var ETHEREUM_CHAIN_LOGO = "https://assets.relay.link/icons/1/light.png";
|
|
5591
6606
|
var MEGAETH_CHAIN_LOGO = svgToDataUri(MEGAETH_CHAIN_SVG);
|
|
5592
6607
|
var MONAD_CHAIN_LOGO = svgToDataUri(MONAD_CHAIN_SVG);
|
|
6608
|
+
var SOLANA_CHAIN_LOGO = "https://assets.relay.link/icons/792703809/light.png";
|
|
6609
|
+
var HYPEREVM_CHAIN_LOGO = "https://assets.relay.link/icons/999/light.png";
|
|
5593
6610
|
var USDM_LOGO = svgToDataUri(USDM_TOKEN_SVG);
|
|
5594
6611
|
var METAMASK_LOGO = svgToDataUri(METAMASK_SVG);
|
|
5595
6612
|
var TRUST_WALLET_LOGO = svgToDataUri(TRUST_WALLET_SVG);
|
|
5596
6613
|
var OKX_WALLET_LOGO = svgToDataUri(OKX_WALLET_SVG);
|
|
5597
6614
|
var RABBY_LOGO = svgToDataUri(RABBY_SVG);
|
|
6615
|
+
var PHANTOM_LOGO = svgToDataUri(PHANTOM_SVG);
|
|
5598
6616
|
var USDC_LOGO = svgToDataUri(USDC_SVG);
|
|
5599
6617
|
var TETHER_LOGO = svgToDataUri(TETHER_SVG);
|
|
6618
|
+
var USDH_LOGO = svgToDataUri(USDH_SVG);
|
|
5600
6619
|
var KNOWN_LOGOS = {
|
|
5601
6620
|
metamask: METAMASK_LOGO,
|
|
5602
6621
|
base: BASE_LOGO,
|
|
5603
6622
|
"base account": BASE_LOGO,
|
|
5604
6623
|
"base app": BASE_LOGO,
|
|
5605
6624
|
"okx wallet": OKX_WALLET_LOGO,
|
|
6625
|
+
phantom: PHANTOM_LOGO,
|
|
5606
6626
|
rabby: RABBY_LOGO,
|
|
5607
6627
|
"trust wallet": TRUST_WALLET_LOGO
|
|
5608
6628
|
};
|
|
@@ -5610,20 +6630,28 @@ var TOKEN_LOGOS = {
|
|
|
5610
6630
|
USDC: USDC_LOGO,
|
|
5611
6631
|
"USDC.e": USDC_LOGO,
|
|
5612
6632
|
USDT: TETHER_LOGO,
|
|
5613
|
-
USDM: USDM_LOGO
|
|
6633
|
+
USDM: USDM_LOGO,
|
|
6634
|
+
USDH: USDH_LOGO
|
|
5614
6635
|
};
|
|
5615
6636
|
var CHAIN_LOGOS = {
|
|
5616
6637
|
base: BASE_CHAIN_LOGO,
|
|
5617
6638
|
ethereum: ETHEREUM_CHAIN_LOGO,
|
|
6639
|
+
"ethereum mainnet": ETHEREUM_CHAIN_LOGO,
|
|
5618
6640
|
polygon: POLYGON_CHAIN_LOGO,
|
|
6641
|
+
"polygon mainnet": POLYGON_CHAIN_LOGO,
|
|
5619
6642
|
arbitrum: ARBITRUM_CHAIN_LOGO,
|
|
5620
6643
|
"arbitrum one": ARBITRUM_CHAIN_LOGO,
|
|
6644
|
+
"arbitrum mainnet": ARBITRUM_CHAIN_LOGO,
|
|
5621
6645
|
bnb: BNB_CHAIN_LOGO,
|
|
5622
6646
|
"bnb chain": BNB_CHAIN_LOGO,
|
|
5623
6647
|
"bnb smart chain": BNB_CHAIN_LOGO,
|
|
5624
6648
|
bsc: BNB_CHAIN_LOGO,
|
|
5625
6649
|
megaeth: MEGAETH_CHAIN_LOGO,
|
|
5626
|
-
monad: MONAD_CHAIN_LOGO
|
|
6650
|
+
monad: MONAD_CHAIN_LOGO,
|
|
6651
|
+
hyperevm: HYPEREVM_CHAIN_LOGO,
|
|
6652
|
+
"hyperevm mainnet": HYPEREVM_CHAIN_LOGO,
|
|
6653
|
+
solana: SOLANA_CHAIN_LOGO,
|
|
6654
|
+
"solana mainnet": SOLANA_CHAIN_LOGO
|
|
5627
6655
|
};
|
|
5628
6656
|
function ScreenLayout({ children, footer }) {
|
|
5629
6657
|
const { tokens, theme } = useBlinkConfig();
|
|
@@ -5871,6 +6899,8 @@ function PrimaryButton({
|
|
|
5871
6899
|
children,
|
|
5872
6900
|
onClick,
|
|
5873
6901
|
href,
|
|
6902
|
+
target,
|
|
6903
|
+
rel,
|
|
5874
6904
|
disabled,
|
|
5875
6905
|
loading,
|
|
5876
6906
|
loadingText = "Please wait...",
|
|
@@ -5923,6 +6953,8 @@ function PrimaryButton({
|
|
|
5923
6953
|
"a",
|
|
5924
6954
|
{
|
|
5925
6955
|
href,
|
|
6956
|
+
target,
|
|
6957
|
+
rel,
|
|
5926
6958
|
onClick,
|
|
5927
6959
|
style: {
|
|
5928
6960
|
...buttonStyle2(tokens, { disabled: false, loading: false }),
|
|
@@ -6679,6 +7711,8 @@ function SourceRow(props) {
|
|
|
6679
7711
|
"a",
|
|
6680
7712
|
{
|
|
6681
7713
|
href: props.href,
|
|
7714
|
+
target: props.target,
|
|
7715
|
+
rel: props.rel,
|
|
6682
7716
|
onClick: props.onClick,
|
|
6683
7717
|
onMouseEnter: () => setHovered(true),
|
|
6684
7718
|
onMouseLeave: () => setHovered(false),
|
|
@@ -7202,6 +8236,22 @@ var closeButtonStyle2 = (tokens) => ({
|
|
|
7202
8236
|
cursor: "pointer",
|
|
7203
8237
|
padding: 0
|
|
7204
8238
|
});
|
|
8239
|
+
function AmountTooLowScreen({
|
|
8240
|
+
minAmountUsd,
|
|
8241
|
+
onRetry,
|
|
8242
|
+
onClose
|
|
8243
|
+
}) {
|
|
8244
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8245
|
+
BlinkErrorScreen,
|
|
8246
|
+
{
|
|
8247
|
+
title: "Amount too low",
|
|
8248
|
+
message: `The minimum payment amount is $${minAmountUsd.toFixed(2)}.`,
|
|
8249
|
+
retryLabel: onRetry ? "Try Again" : void 0,
|
|
8250
|
+
onRetry,
|
|
8251
|
+
onClose
|
|
8252
|
+
}
|
|
8253
|
+
);
|
|
8254
|
+
}
|
|
7205
8255
|
var RESEND_COOLDOWN_SECONDS = 30;
|
|
7206
8256
|
function OtpVerifyScreen({
|
|
7207
8257
|
maskedIdentifier,
|
|
@@ -7477,19 +8527,15 @@ var contentStyle5 = {
|
|
|
7477
8527
|
gap: 16
|
|
7478
8528
|
};
|
|
7479
8529
|
var illustrationFrameStyle = {
|
|
7480
|
-
flex:
|
|
7481
|
-
minHeight: 0,
|
|
8530
|
+
flex: "0 0 auto",
|
|
7482
8531
|
width: "100%",
|
|
7483
|
-
maxWidth: 329,
|
|
7484
8532
|
display: "flex",
|
|
7485
8533
|
alignItems: "center",
|
|
7486
8534
|
justifyContent: "center"
|
|
7487
8535
|
};
|
|
7488
8536
|
var illustrationImgStyle = {
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
width: "auto",
|
|
7492
|
-
height: "auto",
|
|
8537
|
+
width: 200,
|
|
8538
|
+
height: 200,
|
|
7493
8539
|
objectFit: "contain",
|
|
7494
8540
|
display: "block",
|
|
7495
8541
|
pointerEvents: "none",
|
|
@@ -7497,7 +8543,7 @@ var illustrationImgStyle = {
|
|
|
7497
8543
|
};
|
|
7498
8544
|
var footerButtonStyle = {
|
|
7499
8545
|
width: "100%",
|
|
7500
|
-
paddingTop:
|
|
8546
|
+
paddingTop: 48,
|
|
7501
8547
|
paddingBottom: 36
|
|
7502
8548
|
};
|
|
7503
8549
|
var errorBannerStyle3 = (tokens) => ({
|
|
@@ -7571,19 +8617,17 @@ var contentStyle6 = {
|
|
|
7571
8617
|
gap: 16
|
|
7572
8618
|
};
|
|
7573
8619
|
var illustrationFrameStyle2 = {
|
|
7574
|
-
flex: 1,
|
|
7575
|
-
minHeight: 0,
|
|
8620
|
+
flex: "0 1 auto",
|
|
7576
8621
|
width: "100%",
|
|
7577
|
-
maxWidth:
|
|
8622
|
+
maxWidth: 280,
|
|
8623
|
+
height: "clamp(140px, 34vh, 248px)",
|
|
7578
8624
|
display: "flex",
|
|
7579
8625
|
alignItems: "center",
|
|
7580
8626
|
justifyContent: "center"
|
|
7581
8627
|
};
|
|
7582
8628
|
var illustrationImgStyle2 = {
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
width: "auto",
|
|
7586
|
-
height: "auto",
|
|
8629
|
+
width: "100%",
|
|
8630
|
+
height: "100%",
|
|
7587
8631
|
objectFit: "contain",
|
|
7588
8632
|
display: "block",
|
|
7589
8633
|
pointerEvents: "none",
|
|
@@ -7727,6 +8771,36 @@ function triggerDeeplink(uri) {
|
|
|
7727
8771
|
}
|
|
7728
8772
|
}
|
|
7729
8773
|
|
|
8774
|
+
// src/walletDeeplinks.ts
|
|
8775
|
+
function resolveWalletDeeplink(providerId, walletDeeplinks, fallbackUri) {
|
|
8776
|
+
const matchedUri = walletDeeplinks?.find((item) => item.providerId === providerId)?.uri;
|
|
8777
|
+
return matchedUri ?? fallbackUri;
|
|
8778
|
+
}
|
|
8779
|
+
function classifyWalletDeeplinkNavigation(uri) {
|
|
8780
|
+
if (isCustomSchemeUri(uri)) {
|
|
8781
|
+
return { navigationClass: "javascript" };
|
|
8782
|
+
}
|
|
8783
|
+
try {
|
|
8784
|
+
const parsed = new URL(uri);
|
|
8785
|
+
if (isBrowseUniversalLink(parsed)) {
|
|
8786
|
+
return {
|
|
8787
|
+
navigationClass: "iframe-escaping-native-anchor",
|
|
8788
|
+
anchorTarget: "_blank",
|
|
8789
|
+
anchorRel: "noopener noreferrer"
|
|
8790
|
+
};
|
|
8791
|
+
}
|
|
8792
|
+
} catch {
|
|
8793
|
+
return { navigationClass: "javascript" };
|
|
8794
|
+
}
|
|
8795
|
+
return { navigationClass: "javascript" };
|
|
8796
|
+
}
|
|
8797
|
+
function shouldOpenWithJavaScript(navigation) {
|
|
8798
|
+
return navigation.navigationClass === "javascript";
|
|
8799
|
+
}
|
|
8800
|
+
function isBrowseUniversalLink(parsed) {
|
|
8801
|
+
return parsed.protocol === "https:" && parsed.pathname.startsWith("/ul/browse/") && parsed.searchParams.has("ref");
|
|
8802
|
+
}
|
|
8803
|
+
|
|
7730
8804
|
// src/sentry.ts
|
|
7731
8805
|
var _mod;
|
|
7732
8806
|
function captureException(error) {
|
|
@@ -7801,14 +8875,20 @@ function WalletPickerScreen({
|
|
|
7801
8875
|
const rowLoader = isRowPreparing ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 20 }) : void 0;
|
|
7802
8876
|
if (usesDirectLinkCards) {
|
|
7803
8877
|
if (directPreparedSession?.uri) {
|
|
8878
|
+
const navigation = classifyWalletDeeplinkNavigation(directPreparedSession.uri);
|
|
8879
|
+
const openWithJavaScript = shouldOpenWithJavaScript(navigation);
|
|
7804
8880
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7805
8881
|
SourceRow,
|
|
7806
8882
|
{
|
|
7807
8883
|
logo: logoSrc,
|
|
7808
8884
|
name: provider.name,
|
|
7809
8885
|
href: directPreparedSession.uri,
|
|
8886
|
+
target: navigation.anchorTarget,
|
|
8887
|
+
rel: navigation.anchorRel,
|
|
7810
8888
|
onClick: (e) => {
|
|
7811
|
-
|
|
8889
|
+
if (openWithJavaScript) {
|
|
8890
|
+
e.preventDefault();
|
|
8891
|
+
}
|
|
7812
8892
|
setSelectedProviderId(provider.id);
|
|
7813
8893
|
if (directPreparedSession.sessionId) {
|
|
7814
8894
|
void setAuthorizationSessionProvider(
|
|
@@ -7819,7 +8899,9 @@ function WalletPickerScreen({
|
|
|
7819
8899
|
captureException(err);
|
|
7820
8900
|
});
|
|
7821
8901
|
}
|
|
7822
|
-
|
|
8902
|
+
if (openWithJavaScript) {
|
|
8903
|
+
openDeeplink(directPreparedSession.uri);
|
|
8904
|
+
}
|
|
7823
8905
|
void onSelectProvider(provider.id, directPreparedSession);
|
|
7824
8906
|
}
|
|
7825
8907
|
},
|
|
@@ -8677,6 +9759,7 @@ function SelectDepositSourceScreen({
|
|
|
8677
9759
|
const rowAccountId = (opt) => opt.accountId ?? fallbackAccountId;
|
|
8678
9760
|
const isSelected = (opt) => !!selectedTokenSymbol && !!selectedChainName && !!selectedWalletId && opt.symbol === selectedTokenSymbol && opt.chainName === selectedChainName && opt.walletId === selectedWalletId;
|
|
8679
9761
|
const tokenOptionKey = (opt) => `${opt.accountId ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`;
|
|
9762
|
+
const hasPendingMobileSelection = pendingMobileSelection != null;
|
|
8680
9763
|
const handleAuthorizedPick = (opt) => {
|
|
8681
9764
|
onPickToken(opt.symbol, opt.chainName, opt.walletId);
|
|
8682
9765
|
onDone();
|
|
@@ -8706,14 +9789,20 @@ function SelectDepositSourceScreen({
|
|
|
8706
9789
|
handleAuthorizedPick(opt);
|
|
8707
9790
|
};
|
|
8708
9791
|
const footerProviderName = preparedAuthorization?.providerName ?? pendingMobileSelection?.providerName ?? "your wallet";
|
|
9792
|
+
const preparedAuthorizationNavigation = preparedAuthorization ? classifyWalletDeeplinkNavigation(preparedAuthorization.deeplinkUri) : null;
|
|
9793
|
+
const preparedAuthorizationOpensWithJavaScript = preparedAuthorizationNavigation ? shouldOpenWithJavaScript(preparedAuthorizationNavigation) : false;
|
|
8709
9794
|
const footer = pendingMobileSelection ? preparedAuthorization ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8710
9795
|
PrimaryButton,
|
|
8711
9796
|
{
|
|
8712
9797
|
href: preparedAuthorization.deeplinkUri,
|
|
9798
|
+
target: preparedAuthorizationNavigation?.anchorTarget,
|
|
9799
|
+
rel: preparedAuthorizationNavigation?.anchorRel,
|
|
8713
9800
|
onClick: (event) => {
|
|
8714
|
-
event.preventDefault();
|
|
8715
9801
|
onCommitTokenAuthorization?.(preparedAuthorization);
|
|
8716
|
-
|
|
9802
|
+
if (preparedAuthorizationOpensWithJavaScript) {
|
|
9803
|
+
event.preventDefault();
|
|
9804
|
+
openDeeplink(preparedAuthorization.deeplinkUri);
|
|
9805
|
+
}
|
|
8717
9806
|
},
|
|
8718
9807
|
children: `Continue in ${footerProviderName}`
|
|
8719
9808
|
}
|
|
@@ -8763,7 +9852,7 @@ function SelectDepositSourceScreen({
|
|
|
8763
9852
|
symbol: opt.symbol,
|
|
8764
9853
|
chainName: opt.chainName,
|
|
8765
9854
|
balance: opt.balance,
|
|
8766
|
-
selected: isSelected(opt),
|
|
9855
|
+
selected: !hasPendingMobileSelection && isSelected(opt),
|
|
8767
9856
|
onClick: () => handleAuthorizedPick(opt)
|
|
8768
9857
|
},
|
|
8769
9858
|
`auth-${opt.accountId ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`
|
|
@@ -8777,7 +9866,7 @@ function SelectDepositSourceScreen({
|
|
|
8777
9866
|
chainName: opt.chainName,
|
|
8778
9867
|
balance: opt.balance,
|
|
8779
9868
|
requiresAuth: true,
|
|
8780
|
-
selected:
|
|
9869
|
+
selected: hasPendingMobileSelection ? pendingMobileSelection?.key === tokenOptionKey(opt) : isSelected(opt),
|
|
8781
9870
|
onClick: () => handleRequiresAuthPick(account, opt)
|
|
8782
9871
|
},
|
|
8783
9872
|
`req-${opt.accountId ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`
|
|
@@ -9962,11 +11051,13 @@ function OpenWalletScreen({
|
|
|
9962
11051
|
!loading && onRetryStatus != null && error == null,
|
|
9963
11052
|
deeplinkUri
|
|
9964
11053
|
);
|
|
11054
|
+
const deeplinkNavigation = classifyWalletDeeplinkNavigation(deeplinkUri);
|
|
11055
|
+
const openWithJavaScript = shouldOpenWithJavaScript(deeplinkNavigation);
|
|
9965
11056
|
react.useEffect(() => {
|
|
9966
|
-
if (!useDeeplink || loading || !deeplinkUri || autoOpenedRef.current === deeplinkUri) return;
|
|
11057
|
+
if (!useDeeplink || loading || !deeplinkUri || !openWithJavaScript || autoOpenedRef.current === deeplinkUri) return;
|
|
9967
11058
|
autoOpenedRef.current = deeplinkUri;
|
|
9968
11059
|
triggerDeeplink(deeplinkUri);
|
|
9969
|
-
}, [useDeeplink, loading, deeplinkUri]);
|
|
11060
|
+
}, [useDeeplink, loading, deeplinkUri, openWithJavaScript]);
|
|
9970
11061
|
const handleOpen = react.useCallback(() => {
|
|
9971
11062
|
openDeeplink(deeplinkUri);
|
|
9972
11063
|
}, [deeplinkUri]);
|
|
@@ -10006,7 +11097,10 @@ function OpenWalletScreen({
|
|
|
10006
11097
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10007
11098
|
PrimaryButton,
|
|
10008
11099
|
{
|
|
10009
|
-
|
|
11100
|
+
href: !openWithJavaScript ? deeplinkUri : void 0,
|
|
11101
|
+
target: deeplinkNavigation.anchorTarget,
|
|
11102
|
+
rel: deeplinkNavigation.anchorRel,
|
|
11103
|
+
onClick: openWithJavaScript ? handleOpen : void 0,
|
|
10010
11104
|
loading,
|
|
10011
11105
|
loadingText: "Preparing authorization\u2026",
|
|
10012
11106
|
children: [
|
|
@@ -10098,6 +11192,7 @@ var inlineWaitStyle = (color) => ({
|
|
|
10098
11192
|
});
|
|
10099
11193
|
function ConfirmSignScreen({
|
|
10100
11194
|
walletName,
|
|
11195
|
+
chainFamily,
|
|
10101
11196
|
signing,
|
|
10102
11197
|
error,
|
|
10103
11198
|
onSign,
|
|
@@ -10106,26 +11201,28 @@ function ConfirmSignScreen({
|
|
|
10106
11201
|
const { tokens } = useBlinkConfig();
|
|
10107
11202
|
const displayName = walletName ?? "your wallet";
|
|
10108
11203
|
const logoSrc = walletName ? KNOWN_LOGOS[walletName.toLowerCase()] : void 0;
|
|
11204
|
+
const isSvmTransfer = chainFamily === "svm";
|
|
11205
|
+
const heading = isSvmTransfer ? "Ready for passkey approval" : "Wallet authorized";
|
|
11206
|
+
const subtitle = isSvmTransfer ? `${displayName} setup is complete. Your Solana payment signs with your passkey only.` : `${displayName} approved the connection. Tap below to confirm your payment.`;
|
|
11207
|
+
const badge = isSvmTransfer ? "Wallet setup complete" : "Authorization complete";
|
|
11208
|
+
const hint = isSvmTransfer ? "No wallet pop-up is needed for this transfer" : "You may be prompted for biometric verification";
|
|
10109
11209
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10110
11210
|
ScreenLayout,
|
|
10111
11211
|
{
|
|
10112
11212
|
footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10113
11213
|
/* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onSign, disabled: signing, children: "Confirm payment" }),
|
|
10114
11214
|
error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: errorStyle2(tokens.textMuted), children: error }),
|
|
10115
|
-
!error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: hintStyle(tokens.textMuted), children:
|
|
11215
|
+
!error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: hintStyle(tokens.textMuted), children: hint })
|
|
10116
11216
|
] }),
|
|
10117
11217
|
children: [
|
|
10118
11218
|
/* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
|
|
10119
11219
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle15, children: [
|
|
10120
11220
|
logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle3 }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
|
|
10121
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle14(tokens.text), children:
|
|
10122
|
-
/* @__PURE__ */ jsxRuntime.
|
|
10123
|
-
displayName,
|
|
10124
|
-
" approved the connection. Tap below to confirm your payment."
|
|
10125
|
-
] }),
|
|
11221
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle14(tokens.text), children: heading }),
|
|
11222
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle11(tokens.textSecondary), children: subtitle }),
|
|
10126
11223
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: successBadgeStyle(tokens), children: [
|
|
10127
11224
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: checkmarkStyle, children: "\u2713" }),
|
|
10128
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children:
|
|
11225
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: badge })
|
|
10129
11226
|
] })
|
|
10130
11227
|
] })
|
|
10131
11228
|
]
|
|
@@ -10868,6 +11965,7 @@ function buildConfirmSignScreenProps({
|
|
|
10868
11965
|
}) {
|
|
10869
11966
|
return {
|
|
10870
11967
|
walletName: flow.state.providers.find((p) => p.id === flow.state.selectedProviderId)?.name ?? null,
|
|
11968
|
+
chainFamily: remote.pollingTransfer?.signPayload?.chainFamily ?? null,
|
|
10871
11969
|
signing: remote.transferSigningSigning,
|
|
10872
11970
|
error: flow.state.error || remote.transferSigningError,
|
|
10873
11971
|
onSign: handlers.onConfirmSign,
|
|
@@ -11088,6 +12186,19 @@ function buildSuccessScreenProps({
|
|
|
11088
12186
|
onLogout: authenticated ? handlers.onLogout : void 0
|
|
11089
12187
|
};
|
|
11090
12188
|
}
|
|
12189
|
+
function buildAmountTooLowScreenProps({
|
|
12190
|
+
flow,
|
|
12191
|
+
handlers
|
|
12192
|
+
}) {
|
|
12193
|
+
const { state, onDismiss } = flow;
|
|
12194
|
+
const minAmountUsd = state.phase.step === "amount-too-low" ? state.phase.minAmountUsd : state.amountTooLow?.minAmountUsd ?? flow.minTransferAmountUsd;
|
|
12195
|
+
const canRetry = flow.depositAmount == null;
|
|
12196
|
+
return {
|
|
12197
|
+
minAmountUsd,
|
|
12198
|
+
onRetry: canRetry ? handlers.onNewPayment : void 0,
|
|
12199
|
+
onClose: onDismiss
|
|
12200
|
+
};
|
|
12201
|
+
}
|
|
11091
12202
|
function StepRenderer(props) {
|
|
11092
12203
|
const screen = screenForPhase(props.flow.state.phase);
|
|
11093
12204
|
return /* @__PURE__ */ jsxRuntime.jsx(StepRendererContent, { ...props, screen });
|
|
@@ -11155,6 +12266,8 @@ function StepRendererContent({
|
|
|
11155
12266
|
return /* @__PURE__ */ jsxRuntime.jsx(SelectSourceScreen, { ...buildSelectSourceScreenProps(input) });
|
|
11156
12267
|
case "success":
|
|
11157
12268
|
return /* @__PURE__ */ jsxRuntime.jsx(SuccessScreen, { ...buildSuccessScreenProps(input) });
|
|
12269
|
+
case "amount-too-low":
|
|
12270
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AmountTooLowScreen, { ...buildAmountTooLowScreenProps(input) });
|
|
11158
12271
|
default: {
|
|
11159
12272
|
const _exhaustive = screen;
|
|
11160
12273
|
throw new Error(`Unhandled screen: ${_exhaustive}`);
|
|
@@ -11584,9 +12697,8 @@ function useTransferHandlers(deps) {
|
|
|
11584
12697
|
destination
|
|
11585
12698
|
]);
|
|
11586
12699
|
const handlePay = react.useCallback(async (payAmount, sourceOverrides) => {
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
dispatch({ type: "SET_ERROR", error: `Minimum amount is $${minUsd.toFixed(2)}.` });
|
|
12700
|
+
if (isNaN(payAmount) || payAmount < minTransferAmountUsd) {
|
|
12701
|
+
dispatch({ type: "AMOUNT_TOO_LOW", minAmountUsd: minTransferAmountUsd });
|
|
11590
12702
|
return;
|
|
11591
12703
|
}
|
|
11592
12704
|
if (!sourceOverrides?.sourceId && !sourceId) {
|
|
@@ -11637,7 +12749,10 @@ function useTransferHandlers(deps) {
|
|
|
11637
12749
|
);
|
|
11638
12750
|
clearPendingTransferState();
|
|
11639
12751
|
dispatch({ type: "TRANSFER_CREATED", transfer: stagedTransfer });
|
|
11640
|
-
const signedTransfer2 = await transferSigning.signTransfer(
|
|
12752
|
+
const signedTransfer2 = await transferSigning.signTransfer(
|
|
12753
|
+
stagedTransfer.id,
|
|
12754
|
+
{ knownSignPayload: stagedTransfer.signPayload }
|
|
12755
|
+
);
|
|
11641
12756
|
dispatch({ type: "TRANSFER_SIGNED", transfer: signedTransfer2 });
|
|
11642
12757
|
polling.startPolling(stagedTransfer.id);
|
|
11643
12758
|
return;
|
|
@@ -11668,7 +12783,10 @@ function useTransferHandlers(deps) {
|
|
|
11668
12783
|
polling.startPolling(t.id);
|
|
11669
12784
|
return;
|
|
11670
12785
|
}
|
|
11671
|
-
const signedTransfer = await transferSigning.signTransfer(
|
|
12786
|
+
const signedTransfer = await transferSigning.signTransfer(
|
|
12787
|
+
t.id,
|
|
12788
|
+
{ knownSignPayload: t.signPayload }
|
|
12789
|
+
);
|
|
11672
12790
|
dispatch({ type: "TRANSFER_SIGNED", transfer: signedTransfer });
|
|
11673
12791
|
polling.startPolling(t.id);
|
|
11674
12792
|
} catch (err) {
|
|
@@ -11875,12 +12993,6 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
|
|
|
11875
12993
|
};
|
|
11876
12994
|
}
|
|
11877
12995
|
|
|
11878
|
-
// src/walletDeeplinks.ts
|
|
11879
|
-
function resolveWalletDeeplink(providerId, walletDeeplinks, fallbackUri) {
|
|
11880
|
-
const matchedUri = walletDeeplinks?.find((item) => item.providerId === providerId)?.uri;
|
|
11881
|
-
return matchedUri ?? fallbackUri;
|
|
11882
|
-
}
|
|
11883
|
-
|
|
11884
12996
|
// src/hooks/providerSelectionGuards.ts
|
|
11885
12997
|
function resolveSetupFlowDepositAmount({
|
|
11886
12998
|
hasActiveWallet: hasActiveWallet2,
|
|
@@ -11947,9 +13059,10 @@ function buildDesktopDirectDuringSetupRunOptions() {
|
|
|
11947
13059
|
alwaysPauseForOneTap: true
|
|
11948
13060
|
};
|
|
11949
13061
|
}
|
|
11950
|
-
function buildDesktopTokenAuthorizationRunOptions(chainName, tokenSymbol) {
|
|
13062
|
+
function buildDesktopTokenAuthorizationRunOptions(chainName, tokenSymbol, _chainFamily = "evm") {
|
|
13063
|
+
const options = buildDesktopDirectDuringSetupRunOptions();
|
|
11951
13064
|
return {
|
|
11952
|
-
...
|
|
13065
|
+
...options,
|
|
11953
13066
|
// Token authorization should batch like the first-time desktop setup flow:
|
|
11954
13067
|
// auto-resolve SELECT_SOURCE now, then inject the transfer session during one-tap setup.
|
|
11955
13068
|
autoResolveSource: { chainName, tokenSymbol }
|
|
@@ -12618,10 +13731,10 @@ function useProviderHandlers(deps) {
|
|
|
12618
13731
|
dispatch({ type: "SET_ERROR", error: null });
|
|
12619
13732
|
dispatch({ type: "SET_INCREASING_LIMIT", value: true });
|
|
12620
13733
|
dispatch({ type: "SET_SETUP_DEPOSIT_AMOUNT", amount: depositAmount ?? 5 });
|
|
12621
|
-
let
|
|
13734
|
+
let desktopChain = null;
|
|
12622
13735
|
if (!isMobile) {
|
|
12623
|
-
|
|
12624
|
-
if (!
|
|
13736
|
+
desktopChain = chains.find((chain) => chain.commonId === chainId) ?? null;
|
|
13737
|
+
if (!desktopChain) {
|
|
12625
13738
|
dispatch({ type: "SET_INCREASING_LIMIT", value: false });
|
|
12626
13739
|
dispatch({ type: "SET_SETUP_DEPOSIT_AMOUNT", amount: null });
|
|
12627
13740
|
dispatch({ type: "SET_ERROR", error: `No chain found for chainId ${chainId}` });
|
|
@@ -12630,7 +13743,7 @@ function useProviderHandlers(deps) {
|
|
|
12630
13743
|
dispatch({
|
|
12631
13744
|
type: "SET_SETUP_DEPOSIT_TOKEN",
|
|
12632
13745
|
symbol: tokenSymbol,
|
|
12633
|
-
chainName:
|
|
13746
|
+
chainName: desktopChain.name,
|
|
12634
13747
|
walletId: _walletId,
|
|
12635
13748
|
tokenAddress,
|
|
12636
13749
|
chainId
|
|
@@ -12664,7 +13777,11 @@ function useProviderHandlers(deps) {
|
|
|
12664
13777
|
dispatch({ type: "SET_SETUP_FLOW_SCREEN", screen: "one-tap-setup" });
|
|
12665
13778
|
const result = await orchestrator.run(
|
|
12666
13779
|
session.id,
|
|
12667
|
-
buildDesktopTokenAuthorizationRunOptions(
|
|
13780
|
+
buildDesktopTokenAuthorizationRunOptions(
|
|
13781
|
+
desktopChain.name,
|
|
13782
|
+
tokenSymbol,
|
|
13783
|
+
desktopChain.chainFamily
|
|
13784
|
+
)
|
|
12668
13785
|
);
|
|
12669
13786
|
if (result.status === "cancelled") {
|
|
12670
13787
|
dispatch({ type: "SET_SETUP_DEPOSIT_AMOUNT", amount: null });
|
|
@@ -14070,6 +15187,11 @@ function BlinkPaymentInner({
|
|
|
14070
15187
|
dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
|
|
14071
15188
|
}
|
|
14072
15189
|
}, [depositAmount, dispatch]);
|
|
15190
|
+
react.useEffect(() => {
|
|
15191
|
+
if (depositAmount != null && depositAmount < minTransferAmountUsd) {
|
|
15192
|
+
dispatch({ type: "AMOUNT_TOO_LOW", minAmountUsd: minTransferAmountUsd });
|
|
15193
|
+
}
|
|
15194
|
+
}, [depositAmount, minTransferAmountUsd, dispatch]);
|
|
14073
15195
|
react.useEffect(() => {
|
|
14074
15196
|
if (!ready || effectiveAuthenticated) return;
|
|
14075
15197
|
clearLocalSessionArtifacts();
|