@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.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
1
2
|
import { createContext, forwardRef, useContext, useRef, useState, useCallback, useMemo, useEffect, useSyncExternalStore, useReducer, Component } from 'react';
|
|
2
3
|
import { PrivyProvider, usePrivy, useLoginWithPasskey, useSignupWithPasskey } from '@privy-io/react-auth';
|
|
3
4
|
import { createConfig, http, WagmiProvider, useConfig, useConnect, useSwitchChain } from 'wagmi';
|
|
4
|
-
import { mainnet, arbitrum, base, polygon, bsc, megaeth, monad, baseSepolia } from 'wagmi/chains';
|
|
5
|
+
import { mainnet, arbitrum, base, polygon, bsc, megaeth, monad, hyperliquid, baseSepolia } from 'wagmi/chains';
|
|
5
6
|
import { injected, coinbaseWallet } from 'wagmi/connectors';
|
|
6
7
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
7
8
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -14,6 +15,10 @@ var __export = (target, all) => {
|
|
|
14
15
|
for (var name in all)
|
|
15
16
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
17
|
};
|
|
18
|
+
var g = globalThis;
|
|
19
|
+
if (typeof g.Buffer === "undefined") {
|
|
20
|
+
g.Buffer = Buffer;
|
|
21
|
+
}
|
|
17
22
|
|
|
18
23
|
// src/theme.ts
|
|
19
24
|
var darkTheme = {
|
|
@@ -606,7 +611,7 @@ function resolveSelectSourceOption(choices, options, chainName, tokenSymbol, rec
|
|
|
606
611
|
}
|
|
607
612
|
var BLINK_PRIVY_APP_ID = "cmlil87uv004n0ck0blwumwek";
|
|
608
613
|
var wagmiConfig = createConfig({
|
|
609
|
-
chains: [mainnet, arbitrum, base, polygon, bsc, megaeth, monad, baseSepolia],
|
|
614
|
+
chains: [mainnet, arbitrum, base, polygon, bsc, megaeth, monad, hyperliquid, baseSepolia],
|
|
610
615
|
connectors: [
|
|
611
616
|
injected({ unstable_shimAsyncInject: 2e3 }),
|
|
612
617
|
// `preference: 'all'` (default) lets the Coinbase Wallet SDK prefer an
|
|
@@ -626,6 +631,7 @@ var wagmiConfig = createConfig({
|
|
|
626
631
|
[bsc.id]: http(),
|
|
627
632
|
[megaeth.id]: http(),
|
|
628
633
|
[monad.id]: http(),
|
|
634
|
+
[hyperliquid.id]: http(),
|
|
629
635
|
[baseSepolia.id]: http()
|
|
630
636
|
}
|
|
631
637
|
});
|
|
@@ -1198,7 +1204,7 @@ async function fetchTransfer(apiBaseUrl, token, transferId, authorizationSession
|
|
|
1198
1204
|
if (!res.ok) await throwApiError(res);
|
|
1199
1205
|
return await res.json();
|
|
1200
1206
|
}
|
|
1201
|
-
async function signTransfer(apiBaseUrl, token, transferId,
|
|
1207
|
+
async function signTransfer(apiBaseUrl, token, transferId, signedTransfer, authorizationSessionToken) {
|
|
1202
1208
|
if (!token && !authorizationSessionToken) {
|
|
1203
1209
|
throw new Error("Missing auth credentials for transfer signing.");
|
|
1204
1210
|
}
|
|
@@ -1209,7 +1215,9 @@ async function signTransfer(apiBaseUrl, token, transferId, signedUserOp, authori
|
|
|
1209
1215
|
...token ? { Authorization: `Bearer ${token}` } : {},
|
|
1210
1216
|
...authorizationSessionToken ? { "x-authorization-session-token": authorizationSessionToken } : {}
|
|
1211
1217
|
},
|
|
1212
|
-
body: JSON.stringify(
|
|
1218
|
+
body: JSON.stringify(
|
|
1219
|
+
signedTransfer.chainFamily === "svm" ? { signedTransfer } : { signedUserOp: signedTransfer }
|
|
1220
|
+
)
|
|
1213
1221
|
});
|
|
1214
1222
|
if (!res.ok) await throwApiError(res);
|
|
1215
1223
|
return await res.json();
|
|
@@ -1323,7 +1331,14 @@ async function probeActionCompletion(apiBaseUrl, actionId) {
|
|
|
1323
1331
|
if (res.status === 422 && code === "DEPOSIT_TX_NOT_FOUND") {
|
|
1324
1332
|
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
1325
1333
|
}
|
|
1326
|
-
|
|
1334
|
+
const approvalNotDetectedCodes = /* @__PURE__ */ new Set([
|
|
1335
|
+
"APPROVE_NOT_DETECTED",
|
|
1336
|
+
"APPROVE_SPL_NOT_DETECTED",
|
|
1337
|
+
"SPL_DELEGATE_MISSING",
|
|
1338
|
+
"SPL_DELEGATE_INSUFFICIENT",
|
|
1339
|
+
"SPL_DELEGATE_WRONG_OWNER"
|
|
1340
|
+
]);
|
|
1341
|
+
if (res.status === 422 && code && approvalNotDetectedCodes.has(code)) {
|
|
1327
1342
|
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
1328
1343
|
}
|
|
1329
1344
|
if (res.status === 422 && code === "INVALID_TRANSFER_STATE") {
|
|
@@ -1351,6 +1366,8 @@ async function pollTransferTick(params) {
|
|
|
1351
1366
|
}
|
|
1352
1367
|
|
|
1353
1368
|
// src/hooks/useTransferPolling.ts
|
|
1369
|
+
var FAST_POLL_COUNT = 8;
|
|
1370
|
+
var FAST_POLL_INTERVAL_MS = 1e3;
|
|
1354
1371
|
function useTransferPolling(intervalMs = 3e3, overrideGetAccessToken) {
|
|
1355
1372
|
const { apiBaseUrl } = useBlinkConfig();
|
|
1356
1373
|
const { getAccessToken: privyGetAccessToken } = usePrivy();
|
|
@@ -1358,12 +1375,15 @@ function useTransferPolling(intervalMs = 3e3, overrideGetAccessToken) {
|
|
|
1358
1375
|
const [transfer, setTransfer] = useState(null);
|
|
1359
1376
|
const [error, setError] = useState(null);
|
|
1360
1377
|
const [isPolling, setIsPolling] = useState(false);
|
|
1361
|
-
const
|
|
1378
|
+
const timeoutRef = useRef(null);
|
|
1362
1379
|
const transferIdRef = useRef(null);
|
|
1380
|
+
const tickCountRef = useRef(0);
|
|
1381
|
+
const runningRef = useRef(false);
|
|
1363
1382
|
const stopPolling = useCallback(() => {
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1383
|
+
runningRef.current = false;
|
|
1384
|
+
if (timeoutRef.current) {
|
|
1385
|
+
clearTimeout(timeoutRef.current);
|
|
1386
|
+
timeoutRef.current = null;
|
|
1367
1387
|
}
|
|
1368
1388
|
setIsPolling(false);
|
|
1369
1389
|
}, []);
|
|
@@ -1388,16 +1408,27 @@ function useTransferPolling(intervalMs = 3e3, overrideGetAccessToken) {
|
|
|
1388
1408
|
stopPolling();
|
|
1389
1409
|
}
|
|
1390
1410
|
}, [apiBaseUrl, getAccessToken, stopPolling]);
|
|
1411
|
+
const scheduleNext = useCallback(() => {
|
|
1412
|
+
if (!runningRef.current) return;
|
|
1413
|
+
const next = tickCountRef.current < FAST_POLL_COUNT ? Math.min(FAST_POLL_INTERVAL_MS, intervalMs) : intervalMs;
|
|
1414
|
+
timeoutRef.current = setTimeout(async () => {
|
|
1415
|
+
tickCountRef.current += 1;
|
|
1416
|
+
await poll();
|
|
1417
|
+
scheduleNext();
|
|
1418
|
+
}, next);
|
|
1419
|
+
}, [intervalMs, poll]);
|
|
1391
1420
|
const startPolling = useCallback(
|
|
1392
1421
|
(transferId) => {
|
|
1393
1422
|
stopPolling();
|
|
1394
1423
|
transferIdRef.current = transferId;
|
|
1424
|
+
tickCountRef.current = 0;
|
|
1425
|
+
runningRef.current = true;
|
|
1395
1426
|
setIsPolling(true);
|
|
1396
1427
|
setError(null);
|
|
1397
1428
|
poll();
|
|
1398
|
-
|
|
1429
|
+
scheduleNext();
|
|
1399
1430
|
},
|
|
1400
|
-
[poll,
|
|
1431
|
+
[poll, scheduleNext, stopPolling]
|
|
1401
1432
|
);
|
|
1402
1433
|
useEffect(() => () => stopPolling(), [stopPolling]);
|
|
1403
1434
|
return { transfer, error, isPolling, startPolling, stopPolling };
|
|
@@ -1650,7 +1681,7 @@ async function resolvePermit2BatchMetadata(options) {
|
|
|
1650
1681
|
approveAction = null,
|
|
1651
1682
|
signAction,
|
|
1652
1683
|
fetchAuthorizationSession: fetchAuthorizationSession2 = fetchAuthorizationSession,
|
|
1653
|
-
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
1684
|
+
sleep: sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
1654
1685
|
logPrefix = "[blink-sdk][permit2-batch]",
|
|
1655
1686
|
waitForAwaitingLimit = false
|
|
1656
1687
|
} = options;
|
|
@@ -1690,7 +1721,7 @@ async function resolvePermit2BatchMetadata(options) {
|
|
|
1690
1721
|
`SIGN_PERMIT2 metadata is incomplete for batch execution. Missing: ${missingFields.join(", ")}.`
|
|
1691
1722
|
);
|
|
1692
1723
|
}
|
|
1693
|
-
await
|
|
1724
|
+
await sleep2(BATCH_SIGN_PERMIT2_POLL_MS);
|
|
1694
1725
|
const refreshedSession = await fetchAuthorizationSession2(apiBaseUrl, sessionId);
|
|
1695
1726
|
const refreshedSignAction = refreshedSession.actions.find((action) => action.id === signAction.id);
|
|
1696
1727
|
if (!refreshedSignAction) {
|
|
@@ -1744,7 +1775,7 @@ function isTxSettlementTimeoutError(err) {
|
|
|
1744
1775
|
async function waitForTransactionReceipt(walletClient, txHash, logContext, options = {}) {
|
|
1745
1776
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_TX_RECEIPT_POLL_INTERVAL_MS;
|
|
1746
1777
|
const maxAttempts = options.maxAttempts ?? DEFAULT_TX_RECEIPT_MAX_ATTEMPTS;
|
|
1747
|
-
const
|
|
1778
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1748
1779
|
const info = options.logger?.info ?? ((message) => console.info(message));
|
|
1749
1780
|
const warn = options.logger?.warn ?? ((message) => console.warn(message));
|
|
1750
1781
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
@@ -1764,7 +1795,7 @@ async function waitForTransactionReceipt(walletClient, txHash, logContext, optio
|
|
|
1764
1795
|
`[blink-sdk][tx-receipt] eth_getTransactionReceipt errored; will retry. context=${logContext}, txHash=${txHash}, error=${err instanceof Error ? err.message : String(err)}`
|
|
1765
1796
|
);
|
|
1766
1797
|
}
|
|
1767
|
-
await
|
|
1798
|
+
await sleep2(pollIntervalMs);
|
|
1768
1799
|
}
|
|
1769
1800
|
const waitedMs = pollIntervalMs * maxAttempts;
|
|
1770
1801
|
if (options.strict) {
|
|
@@ -1807,7 +1838,7 @@ function parseNonce(raw) {
|
|
|
1807
1838
|
async function waitForNonceAdvance(walletClient, sender, expectedMinNonce, logContext, options = {}) {
|
|
1808
1839
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_TX_RECEIPT_POLL_INTERVAL_MS;
|
|
1809
1840
|
const maxAttempts = options.maxAttempts ?? DEFAULT_TX_RECEIPT_MAX_ATTEMPTS;
|
|
1810
|
-
const
|
|
1841
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1811
1842
|
const info = options.logger?.info ?? ((message) => console.info(message));
|
|
1812
1843
|
const warn = options.logger?.warn ?? ((message) => console.warn(message));
|
|
1813
1844
|
let lastObservedNonce = null;
|
|
@@ -1826,7 +1857,7 @@ async function waitForNonceAdvance(walletClient, sender, expectedMinNonce, logCo
|
|
|
1826
1857
|
`[blink-sdk][tx-nonce] eth_getTransactionCount errored; will retry. context=${logContext}, sender=${sender}, error=${err instanceof Error ? err.message : String(err)}`
|
|
1827
1858
|
);
|
|
1828
1859
|
}
|
|
1829
|
-
await
|
|
1860
|
+
await sleep2(pollIntervalMs);
|
|
1830
1861
|
}
|
|
1831
1862
|
const waitedMs = pollIntervalMs * maxAttempts;
|
|
1832
1863
|
throw new TxSettlementTimeoutError({
|
|
@@ -1849,7 +1880,7 @@ async function waitForTransactionSettled(params) {
|
|
|
1849
1880
|
logContext,
|
|
1850
1881
|
pollIntervalMs,
|
|
1851
1882
|
maxAttempts,
|
|
1852
|
-
sleep,
|
|
1883
|
+
sleep: sleep2,
|
|
1853
1884
|
logger
|
|
1854
1885
|
} = params;
|
|
1855
1886
|
const expectedMinNonce = preSendNonce + 1;
|
|
@@ -1864,7 +1895,7 @@ async function waitForTransactionSettled(params) {
|
|
|
1864
1895
|
await waitForTransactionReceipt(walletClient, txHash, logContext, {
|
|
1865
1896
|
pollIntervalMs: resolvedPollIntervalMs,
|
|
1866
1897
|
maxAttempts: resolvedMaxAttempts,
|
|
1867
|
-
sleep,
|
|
1898
|
+
sleep: sleep2,
|
|
1868
1899
|
logger: sharedLogger,
|
|
1869
1900
|
strict: true
|
|
1870
1901
|
});
|
|
@@ -1886,7 +1917,7 @@ async function waitForTransactionSettled(params) {
|
|
|
1886
1917
|
{
|
|
1887
1918
|
pollIntervalMs: resolvedPollIntervalMs,
|
|
1888
1919
|
maxAttempts: resolvedMaxAttempts,
|
|
1889
|
-
sleep,
|
|
1920
|
+
sleep: sleep2,
|
|
1890
1921
|
logger: sharedLogger
|
|
1891
1922
|
}
|
|
1892
1923
|
);
|
|
@@ -1924,13 +1955,13 @@ async function waitForWalletNonceAgreement(params) {
|
|
|
1924
1955
|
pollIntervalMs,
|
|
1925
1956
|
maxAttempts,
|
|
1926
1957
|
minWaitMs,
|
|
1927
|
-
sleep,
|
|
1958
|
+
sleep: sleep2,
|
|
1928
1959
|
logger
|
|
1929
1960
|
} = params;
|
|
1930
1961
|
const resolvedPollIntervalMs = pollIntervalMs ?? DEFAULT_WALLET_NONCE_AGREEMENT_POLL_INTERVAL_MS;
|
|
1931
1962
|
const resolvedMaxAttempts = maxAttempts ?? DEFAULT_WALLET_NONCE_AGREEMENT_MAX_ATTEMPTS;
|
|
1932
1963
|
const resolvedMinWaitMs = minWaitMs ?? DEFAULT_WALLET_NONCE_AGREEMENT_MIN_WAIT_MS;
|
|
1933
|
-
const resolvedSleep =
|
|
1964
|
+
const resolvedSleep = sleep2 ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1934
1965
|
const info = logger?.info ?? ((message) => console.info(message));
|
|
1935
1966
|
const warn = logger?.warn ?? ((message) => console.warn(message));
|
|
1936
1967
|
const startedAt = Date.now();
|
|
@@ -2016,7 +2047,7 @@ async function pollWalletCallsStatus(walletClient, callsId, options = {}) {
|
|
|
2016
2047
|
const maxConsecutiveErrors = options.maxConsecutiveErrors ?? DEFAULT_MAX_CONSECUTIVE_ERRORS;
|
|
2017
2048
|
const errorGracePeriodMs = options.errorGracePeriodMs ?? DEFAULT_ERROR_GRACE_PERIOD_MS;
|
|
2018
2049
|
const logEveryN = Math.max(1, options.logEveryNAttempts ?? DEFAULT_LOG_EVERY_N_ATTEMPTS);
|
|
2019
|
-
const
|
|
2050
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
2020
2051
|
const now = options.now ?? (() => Date.now());
|
|
2021
2052
|
const info = options.logger?.info ?? ((m) => console.info(m));
|
|
2022
2053
|
const warn = options.logger?.warn ?? ((m) => console.warn(m));
|
|
@@ -2025,7 +2056,7 @@ async function pollWalletCallsStatus(walletClient, callsId, options = {}) {
|
|
|
2025
2056
|
let consecutiveErrors = 0;
|
|
2026
2057
|
let lastStatus;
|
|
2027
2058
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
2028
|
-
await
|
|
2059
|
+
await sleep2(pollIntervalMs);
|
|
2029
2060
|
let status;
|
|
2030
2061
|
try {
|
|
2031
2062
|
status = await withTimeout(
|
|
@@ -2207,6 +2238,10 @@ function buildTargetMatchers(target) {
|
|
|
2207
2238
|
aliases.add("rabby");
|
|
2208
2239
|
aliases.add("io.rabby");
|
|
2209
2240
|
}
|
|
2241
|
+
if (value.includes("phantom")) {
|
|
2242
|
+
aliases.add("phantom");
|
|
2243
|
+
aliases.add("app.phantom");
|
|
2244
|
+
}
|
|
2210
2245
|
if (value.includes("injected")) {
|
|
2211
2246
|
aliases.add("injected");
|
|
2212
2247
|
}
|
|
@@ -2244,6 +2279,252 @@ function resolveWalletConnector(connectors, target) {
|
|
|
2244
2279
|
return metaMaskConnector ?? connectors[0];
|
|
2245
2280
|
}
|
|
2246
2281
|
|
|
2282
|
+
// src/solanaWalletRuntime.ts
|
|
2283
|
+
function asSigner(adapter) {
|
|
2284
|
+
return adapter;
|
|
2285
|
+
}
|
|
2286
|
+
var PHANTOM_SOLANA_CONNECT_TIMEOUT_MESSAGE = "PHANTOM_SOLANA_CONNECT_TIMEOUT";
|
|
2287
|
+
var APPROVE_SPL_CONFIRMATION_TIMEOUT_MESSAGE = "APPROVE_SPL_CONFIRMATION_TIMEOUT";
|
|
2288
|
+
var APPROVE_SPL_ONCHAIN_FAILURE_PREFIX = "Solana transaction failed:";
|
|
2289
|
+
var DEFAULT_CONFIRM_TIMEOUT_MS = 45e3;
|
|
2290
|
+
var DEFAULT_POLL_INTERVAL_MS2 = 1e3;
|
|
2291
|
+
var DEFAULT_COMMITMENT = "confirmed";
|
|
2292
|
+
var cachedAdapter = null;
|
|
2293
|
+
var cachedProviderKey = null;
|
|
2294
|
+
function providerKey(selection) {
|
|
2295
|
+
const providerName = selection.providerName?.trim();
|
|
2296
|
+
if (!providerName) {
|
|
2297
|
+
throw new Error("Solana wallet metadata is missing providerName.");
|
|
2298
|
+
}
|
|
2299
|
+
return `${selection.providerId ?? ""}:${providerName.toLowerCase()}`;
|
|
2300
|
+
}
|
|
2301
|
+
function assertSupportedProvider(selection) {
|
|
2302
|
+
const name = selection.providerName?.trim().toLowerCase();
|
|
2303
|
+
if (name !== "phantom") {
|
|
2304
|
+
throw new Error(`Unsupported Solana wallet provider: ${selection.providerName ?? "unknown"}.`);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
async function createAdapter(selection) {
|
|
2308
|
+
assertSupportedProvider(selection);
|
|
2309
|
+
const { PhantomWalletAdapter } = await import('@solana/wallet-adapter-phantom');
|
|
2310
|
+
return new PhantomWalletAdapter();
|
|
2311
|
+
}
|
|
2312
|
+
function readPublicKey(adapter) {
|
|
2313
|
+
const publicKey = adapter.publicKey?.toBase58();
|
|
2314
|
+
if (!publicKey) {
|
|
2315
|
+
throw new Error("Solana wallet did not return a public key.");
|
|
2316
|
+
}
|
|
2317
|
+
return publicKey;
|
|
2318
|
+
}
|
|
2319
|
+
async function connectSolanaWallet(selection, options) {
|
|
2320
|
+
const key = providerKey(selection);
|
|
2321
|
+
if (!cachedAdapter || cachedProviderKey !== key) {
|
|
2322
|
+
cachedAdapter = await createAdapter(selection);
|
|
2323
|
+
cachedProviderKey = key;
|
|
2324
|
+
}
|
|
2325
|
+
if (!cachedAdapter.connected) {
|
|
2326
|
+
const adapter = cachedAdapter;
|
|
2327
|
+
const connectPromise = adapter.connect();
|
|
2328
|
+
const timeoutMs = options?.timeoutMs;
|
|
2329
|
+
if (typeof timeoutMs === "number" && timeoutMs > 0) {
|
|
2330
|
+
let timeoutHandle = null;
|
|
2331
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
2332
|
+
timeoutHandle = setTimeout(() => {
|
|
2333
|
+
reject(new Error(PHANTOM_SOLANA_CONNECT_TIMEOUT_MESSAGE));
|
|
2334
|
+
}, timeoutMs);
|
|
2335
|
+
});
|
|
2336
|
+
try {
|
|
2337
|
+
await Promise.race([connectPromise, timeoutPromise]);
|
|
2338
|
+
} finally {
|
|
2339
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
2340
|
+
}
|
|
2341
|
+
} else {
|
|
2342
|
+
await connectPromise;
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
return {
|
|
2346
|
+
adapter: cachedAdapter,
|
|
2347
|
+
publicKey: readPublicKey(cachedAdapter)
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
async function signSolanaTransaction(selection, unsignedTxBase64, expectedOwnerPubkey) {
|
|
2351
|
+
const { adapter, publicKey } = await connectSolanaWallet(selection);
|
|
2352
|
+
if (publicKey !== expectedOwnerPubkey) {
|
|
2353
|
+
throw new Error(
|
|
2354
|
+
`Connected Solana wallet ${publicKey} does not match the required source wallet ${expectedOwnerPubkey}. Please switch accounts in Phantom and retry.`
|
|
2355
|
+
);
|
|
2356
|
+
}
|
|
2357
|
+
const signer = asSigner(adapter);
|
|
2358
|
+
if (typeof signer.signTransaction !== "function") {
|
|
2359
|
+
throw new Error("Selected Solana wallet does not support transaction signing.");
|
|
2360
|
+
}
|
|
2361
|
+
const transaction = await deserializeTransaction(unsignedTxBase64);
|
|
2362
|
+
const signedTransaction = await signer.signTransaction(transaction);
|
|
2363
|
+
return {
|
|
2364
|
+
ownerPubkey: publicKey,
|
|
2365
|
+
signedTxBase64: bytesToBase64(serializeTransaction(signedTransaction))
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
async function supportsSignAllSolanaTransactions(selection) {
|
|
2369
|
+
const { adapter } = await connectSolanaWallet(selection);
|
|
2370
|
+
return typeof asSigner(adapter).signAllTransactions === "function";
|
|
2371
|
+
}
|
|
2372
|
+
async function signAllSolanaTransactions(selection, unsignedTxsBase64, expectedOwnerPubkey) {
|
|
2373
|
+
if (!Array.isArray(unsignedTxsBase64) || unsignedTxsBase64.length === 0) {
|
|
2374
|
+
throw new Error("signAllSolanaTransactions requires at least one unsigned transaction.");
|
|
2375
|
+
}
|
|
2376
|
+
const { adapter, publicKey } = await connectSolanaWallet(selection);
|
|
2377
|
+
if (publicKey !== expectedOwnerPubkey) {
|
|
2378
|
+
throw new Error(
|
|
2379
|
+
`Connected Solana wallet ${publicKey} does not match the required source wallet ${expectedOwnerPubkey}. Please switch accounts in Phantom and retry.`
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
const signer = asSigner(adapter);
|
|
2383
|
+
if (typeof signer.signAllTransactions !== "function") {
|
|
2384
|
+
throw new Error("Selected Solana wallet does not support signAllTransactions.");
|
|
2385
|
+
}
|
|
2386
|
+
const transactions = [];
|
|
2387
|
+
for (const txBase64 of unsignedTxsBase64) {
|
|
2388
|
+
transactions.push(await deserializeTransaction(txBase64));
|
|
2389
|
+
}
|
|
2390
|
+
const signed = await signer.signAllTransactions(transactions);
|
|
2391
|
+
if (!Array.isArray(signed) || signed.length !== transactions.length) {
|
|
2392
|
+
throw new Error(
|
|
2393
|
+
`signAllTransactions returned ${Array.isArray(signed) ? signed.length : "non-array"} transactions; expected ${transactions.length}.`
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
2396
|
+
return {
|
|
2397
|
+
ownerPubkey: publicKey,
|
|
2398
|
+
signedTxsBase64: signed.map((tx) => bytesToBase64(serializeTransaction(tx)))
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
async function deserializeTransaction(unsignedTxBase64) {
|
|
2402
|
+
const { Transaction, VersionedTransaction } = await import('@solana/web3.js');
|
|
2403
|
+
const bytes = base64ToBytes(unsignedTxBase64);
|
|
2404
|
+
try {
|
|
2405
|
+
return Transaction.from(bytes);
|
|
2406
|
+
} catch {
|
|
2407
|
+
return VersionedTransaction.deserialize(bytes);
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
function serializeTransaction(transaction) {
|
|
2411
|
+
if ("version" in transaction) {
|
|
2412
|
+
return transaction.serialize();
|
|
2413
|
+
}
|
|
2414
|
+
return transaction.serialize({
|
|
2415
|
+
requireAllSignatures: false,
|
|
2416
|
+
verifySignatures: false
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
function base64ToBytes(value) {
|
|
2420
|
+
if (typeof globalThis.atob === "function") {
|
|
2421
|
+
const binary = globalThis.atob(value);
|
|
2422
|
+
const bytes = new Uint8Array(binary.length);
|
|
2423
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2424
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2425
|
+
}
|
|
2426
|
+
return bytes;
|
|
2427
|
+
}
|
|
2428
|
+
const bufferCtor = globalThis.Buffer;
|
|
2429
|
+
if (bufferCtor) {
|
|
2430
|
+
return bufferCtor.from(value, "base64");
|
|
2431
|
+
}
|
|
2432
|
+
throw new Error("Base64 decoding is not available in this environment.");
|
|
2433
|
+
}
|
|
2434
|
+
function bytesToBase64(bytes) {
|
|
2435
|
+
if (typeof globalThis.btoa === "function") {
|
|
2436
|
+
let binary = "";
|
|
2437
|
+
for (const byte of bytes) {
|
|
2438
|
+
binary += String.fromCharCode(byte);
|
|
2439
|
+
}
|
|
2440
|
+
return globalThis.btoa(binary);
|
|
2441
|
+
}
|
|
2442
|
+
const bufferCtor = globalThis.Buffer;
|
|
2443
|
+
if (bufferCtor) {
|
|
2444
|
+
return bufferCtor.from(bytes).toString("base64");
|
|
2445
|
+
}
|
|
2446
|
+
throw new Error("Base64 encoding is not available in this environment.");
|
|
2447
|
+
}
|
|
2448
|
+
async function signAndSendApproveSplViaWallet(params) {
|
|
2449
|
+
const rpcUrl = params.rpcUrl?.trim();
|
|
2450
|
+
if (!rpcUrl) {
|
|
2451
|
+
throw new Error("signAndSendApproveSplViaWallet requires an rpcUrl.");
|
|
2452
|
+
}
|
|
2453
|
+
const unsignedTxBase64 = params.unsignedTxBase64?.trim();
|
|
2454
|
+
if (!unsignedTxBase64) {
|
|
2455
|
+
throw new Error("signAndSendApproveSplViaWallet requires unsignedTxBase64.");
|
|
2456
|
+
}
|
|
2457
|
+
const commitment = params.commitment ?? DEFAULT_COMMITMENT;
|
|
2458
|
+
const { adapter, publicKey } = await connectSolanaWallet(params.selection);
|
|
2459
|
+
if (publicKey !== params.expectedOwnerPubkey) {
|
|
2460
|
+
throw new Error(
|
|
2461
|
+
`Connected Solana wallet ${publicKey} does not match the required source wallet ${params.expectedOwnerPubkey}. Please switch accounts in Phantom and retry.`
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
if (!adapter.sendTransaction) {
|
|
2465
|
+
throw new Error("Selected Solana wallet does not support sendTransaction.");
|
|
2466
|
+
}
|
|
2467
|
+
const transaction = await deserializeTransaction(unsignedTxBase64);
|
|
2468
|
+
const connection = await defaultConnectionFactory(rpcUrl, commitment);
|
|
2469
|
+
const signature = await adapter.sendTransaction(
|
|
2470
|
+
transaction,
|
|
2471
|
+
connection,
|
|
2472
|
+
{
|
|
2473
|
+
preflightCommitment: commitment,
|
|
2474
|
+
maxRetries: 3
|
|
2475
|
+
}
|
|
2476
|
+
);
|
|
2477
|
+
return { ownerPubkey: publicKey, signature };
|
|
2478
|
+
}
|
|
2479
|
+
async function confirmApproveSplViaPolling(params) {
|
|
2480
|
+
const rpcUrl = params.rpcUrl?.trim();
|
|
2481
|
+
if (!rpcUrl) {
|
|
2482
|
+
throw new Error("confirmApproveSplViaPolling requires an rpcUrl.");
|
|
2483
|
+
}
|
|
2484
|
+
const signature = params.signature?.trim();
|
|
2485
|
+
if (!signature) {
|
|
2486
|
+
throw new Error("confirmApproveSplViaPolling requires a signature.");
|
|
2487
|
+
}
|
|
2488
|
+
const confirmTimeoutMs = params.confirmTimeoutMs ?? DEFAULT_CONFIRM_TIMEOUT_MS;
|
|
2489
|
+
const pollIntervalMs = params.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
2490
|
+
const commitment = params.commitment ?? DEFAULT_COMMITMENT;
|
|
2491
|
+
const connection = await defaultConnectionFactory(rpcUrl, commitment);
|
|
2492
|
+
const slot = await pollForConfirmation({
|
|
2493
|
+
connection,
|
|
2494
|
+
signature,
|
|
2495
|
+
confirmTimeoutMs,
|
|
2496
|
+
pollIntervalMs
|
|
2497
|
+
});
|
|
2498
|
+
return { signature, slot };
|
|
2499
|
+
}
|
|
2500
|
+
async function defaultConnectionFactory(rpcUrl, commitment) {
|
|
2501
|
+
const { Connection } = await import('@solana/web3.js');
|
|
2502
|
+
return new Connection(rpcUrl, commitment);
|
|
2503
|
+
}
|
|
2504
|
+
async function pollForConfirmation(args) {
|
|
2505
|
+
const { connection, signature, confirmTimeoutMs, pollIntervalMs } = args;
|
|
2506
|
+
const expiresAt = Date.now() + confirmTimeoutMs;
|
|
2507
|
+
while (Date.now() <= expiresAt) {
|
|
2508
|
+
const result = await connection.getSignatureStatuses(
|
|
2509
|
+
[signature],
|
|
2510
|
+
{ searchTransactionHistory: true }
|
|
2511
|
+
);
|
|
2512
|
+
const status = result.value[0];
|
|
2513
|
+
if (status?.err) {
|
|
2514
|
+
throw new Error(`${APPROVE_SPL_ONCHAIN_FAILURE_PREFIX} ${JSON.stringify(status.err)}`);
|
|
2515
|
+
}
|
|
2516
|
+
const confirmationStatus = status?.confirmationStatus;
|
|
2517
|
+
if (confirmationStatus === "confirmed" || confirmationStatus === "finalized") {
|
|
2518
|
+
return status?.slot;
|
|
2519
|
+
}
|
|
2520
|
+
await sleep(pollIntervalMs);
|
|
2521
|
+
}
|
|
2522
|
+
throw new Error(APPROVE_SPL_CONFIRMATION_TIMEOUT_MESSAGE);
|
|
2523
|
+
}
|
|
2524
|
+
function sleep(ms) {
|
|
2525
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2247
2528
|
// src/hooks/authorizationExecutor.ts
|
|
2248
2529
|
var WALLET_CLIENT_MAX_ATTEMPTS = 25;
|
|
2249
2530
|
var WALLET_CLIENT_POLL_MS = 400;
|
|
@@ -2255,6 +2536,7 @@ var EXECUTE_BRIDGE_TX_TIMEOUT_MS = 12e4;
|
|
|
2255
2536
|
var EXECUTE_BRIDGE_TX_TIMEOUT_MESSAGE = "EXECUTE_BRIDGE_TX_TIMEOUT";
|
|
2256
2537
|
var WALLET_PROMPT_TIMEOUT_MS = 9e4;
|
|
2257
2538
|
var WALLET_PROMPT_TIMEOUT_MESSAGE = "WALLET_PROMPT_TIMEOUT";
|
|
2539
|
+
var PHANTOM_SOLANA_CONNECT_TIMEOUT_MS = 8e3;
|
|
2258
2540
|
var BATCH_RECEIPT_POLL_INTERVAL_MS = 1500;
|
|
2259
2541
|
var BATCH_RECEIPT_MAX_ATTEMPTS = 20;
|
|
2260
2542
|
var BATCHABLE_ACTION_TYPES = /* @__PURE__ */ new Set(["APPROVE_PERMIT2", "SIGN_PERMIT2", "EXECUTE_BRIDGE"]);
|
|
@@ -2368,13 +2650,110 @@ function parseSignTypedDataPayload(typedData) {
|
|
|
2368
2650
|
};
|
|
2369
2651
|
}
|
|
2370
2652
|
function isBatchableAction(action) {
|
|
2653
|
+
if (action.type === "EXECUTE_BRIDGE" && action.metadata?.chainFamily === "svm") {
|
|
2654
|
+
return false;
|
|
2655
|
+
}
|
|
2371
2656
|
return BATCHABLE_ACTION_TYPES.has(action.type);
|
|
2372
2657
|
}
|
|
2658
|
+
function isSvmCombinedFirstTransferPair(pending) {
|
|
2659
|
+
if (pending.length < 2) return false;
|
|
2660
|
+
const [first, second] = pending;
|
|
2661
|
+
if (first.type !== "APPROVE_SPL") return false;
|
|
2662
|
+
if (second.type !== "EXECUTE_BRIDGE") return false;
|
|
2663
|
+
if (second.metadata?.chainFamily !== "svm") return false;
|
|
2664
|
+
if (first.metadata?.awaitingLimit) return false;
|
|
2665
|
+
const approveTx = first.metadata?.unsignedApproveTxBase64;
|
|
2666
|
+
const bridgeTx = second.metadata?.unsignedTxBase64;
|
|
2667
|
+
if (typeof approveTx !== "string" || approveTx.trim() === "") return false;
|
|
2668
|
+
if (typeof bridgeTx !== "string" || bridgeTx.trim() === "") return false;
|
|
2669
|
+
const approveOwner = first.metadata?.ownerPubkey;
|
|
2670
|
+
const bridgeOwner = second.metadata?.ownerPubkey ?? second.metadata?.senderAddress;
|
|
2671
|
+
if (typeof approveOwner !== "string" || approveOwner.trim() === "") return false;
|
|
2672
|
+
if (typeof bridgeOwner !== "string" || bridgeOwner.trim() === "") return false;
|
|
2673
|
+
if (approveOwner !== bridgeOwner) return false;
|
|
2674
|
+
return true;
|
|
2675
|
+
}
|
|
2676
|
+
function getSolanaWalletSelection(action) {
|
|
2677
|
+
const providerName = action.metadata?.providerName;
|
|
2678
|
+
if (typeof providerName !== "string" || providerName.trim() === "") {
|
|
2679
|
+
throw new Error(`${action.type} metadata is missing providerName.`);
|
|
2680
|
+
}
|
|
2681
|
+
const providerId = action.metadata?.providerId;
|
|
2682
|
+
return {
|
|
2683
|
+
...typeof providerId === "string" && providerId.trim() !== "" ? { providerId } : {},
|
|
2684
|
+
providerName
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
function isPhantomEvmProviderAvailable() {
|
|
2688
|
+
if (typeof window === "undefined") return false;
|
|
2689
|
+
const phantom = window.phantom;
|
|
2690
|
+
return Boolean(phantom?.ethereum?.isPhantom);
|
|
2691
|
+
}
|
|
2692
|
+
async function phantomEvmHasAuthorizedAccount() {
|
|
2693
|
+
if (typeof window === "undefined") return false;
|
|
2694
|
+
const provider = window.phantom?.ethereum;
|
|
2695
|
+
if (!provider?.isPhantom) return false;
|
|
2696
|
+
try {
|
|
2697
|
+
const accounts = await provider.request({ method: "eth_accounts" });
|
|
2698
|
+
return Array.isArray(accounts) && accounts.length > 0;
|
|
2699
|
+
} catch {
|
|
2700
|
+
return false;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
async function attemptPhantomEvmConnect(connectors, connectAsync) {
|
|
2704
|
+
let connector = resolveWalletConnector(connectors, { providerName: "Phantom" });
|
|
2705
|
+
if (!connector) {
|
|
2706
|
+
const ethereum = typeof window !== "undefined" ? window.ethereum : void 0;
|
|
2707
|
+
if (ethereum?.isPhantom) {
|
|
2708
|
+
connector = resolveWalletConnector(connectors, { wagmiConnectorId: "injected" });
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
if (!connector) return null;
|
|
2712
|
+
const result = await connectAsync({ connector });
|
|
2713
|
+
const address = result.accounts[0];
|
|
2714
|
+
if (!address) return null;
|
|
2715
|
+
return { address, chainId: result.chainId };
|
|
2716
|
+
}
|
|
2373
2717
|
function getPendingActions(session, completedIds) {
|
|
2374
2718
|
return session.actions.filter((a) => a.status === "PENDING" && !completedIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
|
|
2375
2719
|
}
|
|
2376
2720
|
async function executeOpenProvider(action, wagmiConfig2, connectors, connectAsync) {
|
|
2377
2721
|
try {
|
|
2722
|
+
if (action.metadata?.chainFamily === "svm") {
|
|
2723
|
+
const selection = getSolanaWalletSelection(action);
|
|
2724
|
+
const evmProbed = isPhantomEvmProviderAvailable() && await phantomEvmHasAuthorizedAccount();
|
|
2725
|
+
const svmPromise = connectSolanaWallet(selection, {
|
|
2726
|
+
timeoutMs: evmProbed ? PHANTOM_SOLANA_CONNECT_TIMEOUT_MS : void 0
|
|
2727
|
+
});
|
|
2728
|
+
const evmPromise = evmProbed ? attemptPhantomEvmConnect(connectors, connectAsync) : Promise.resolve(null);
|
|
2729
|
+
const [svmSettled, evmSettled] = await Promise.allSettled([svmPromise, evmPromise]);
|
|
2730
|
+
const svmPublicKey = svmSettled.status === "fulfilled" ? svmSettled.value.publicKey : null;
|
|
2731
|
+
const evmAddress = evmSettled.status === "fulfilled" && evmSettled.value ? evmSettled.value.address : null;
|
|
2732
|
+
if (!svmPublicKey && !evmAddress) {
|
|
2733
|
+
const svmReason = svmSettled.status === "rejected" ? svmSettled.reason instanceof Error ? svmSettled.reason.message : String(svmSettled.reason) : "Failed to connect Phantom.";
|
|
2734
|
+
const friendlyMsg = svmReason === PHANTOM_SOLANA_CONNECT_TIMEOUT_MESSAGE ? "Phantom did not respond. Please try again." : svmReason;
|
|
2735
|
+
return actionError(
|
|
2736
|
+
action,
|
|
2737
|
+
isUserRejection(friendlyMsg) ? "You rejected the Solana wallet connection request. Please connect Phantom to continue." : friendlyMsg
|
|
2738
|
+
);
|
|
2739
|
+
}
|
|
2740
|
+
if (evmAddress) {
|
|
2741
|
+
await refreshWalletCapabilities(wagmiConfig2, "open-provider:phantom-dual");
|
|
2742
|
+
}
|
|
2743
|
+
const messageParts = [];
|
|
2744
|
+
if (svmPublicKey) messageParts.push(`SVM: ${svmPublicKey}`);
|
|
2745
|
+
if (evmAddress) messageParts.push(`EVM: ${evmAddress}`);
|
|
2746
|
+
return actionSuccess(
|
|
2747
|
+
action,
|
|
2748
|
+
`Connected to ${selection.providerName}. ${messageParts.join(", ")}`,
|
|
2749
|
+
{
|
|
2750
|
+
accounts: svmPublicKey ? [svmPublicKey, ...evmAddress ? [evmAddress] : []] : evmAddress ? [evmAddress] : [],
|
|
2751
|
+
chainFamily: "svm",
|
|
2752
|
+
...svmPublicKey ? { userSolanaWalletPubkey: svmPublicKey } : {},
|
|
2753
|
+
...evmAddress ? { evmAddress } : {}
|
|
2754
|
+
}
|
|
2755
|
+
);
|
|
2756
|
+
}
|
|
2378
2757
|
const account = getAccount(wagmiConfig2);
|
|
2379
2758
|
const targetId = action.metadata?.wagmiConnectorId;
|
|
2380
2759
|
const connector = resolveWalletConnector(connectors, { wagmiConnectorId: targetId });
|
|
@@ -2500,9 +2879,16 @@ async function executeOpenProvider(action, wagmiConfig2, connectors, connectAsyn
|
|
|
2500
2879
|
{ accounts: [...result.accounts], chainId: hexChainId }
|
|
2501
2880
|
);
|
|
2502
2881
|
} catch (err) {
|
|
2882
|
+
const msg = err instanceof Error ? err.message : "Failed to connect wallet";
|
|
2883
|
+
if (action.metadata?.chainFamily === "svm") {
|
|
2884
|
+
return actionError(
|
|
2885
|
+
action,
|
|
2886
|
+
isUserRejection(msg) ? "You rejected the Solana wallet connection request. Please connect Phantom to continue." : msg
|
|
2887
|
+
);
|
|
2888
|
+
}
|
|
2503
2889
|
return actionError(
|
|
2504
2890
|
action,
|
|
2505
|
-
|
|
2891
|
+
msg
|
|
2506
2892
|
);
|
|
2507
2893
|
}
|
|
2508
2894
|
}
|
|
@@ -2580,6 +2966,334 @@ async function executeSwitchChain(action, wagmiConfig2, switchChainAsync) {
|
|
|
2580
2966
|
);
|
|
2581
2967
|
}
|
|
2582
2968
|
}
|
|
2969
|
+
async function executeApproveSplSolana(action, options) {
|
|
2970
|
+
try {
|
|
2971
|
+
const selection = getSolanaWalletSelection(action);
|
|
2972
|
+
const ownerPubkey = action.metadata?.ownerPubkey;
|
|
2973
|
+
const unsignedApproveTxBase64 = action.metadata?.unsignedApproveTxBase64;
|
|
2974
|
+
const tokenSymbol = action.metadata?.tokenSymbol;
|
|
2975
|
+
const clientRpcUrl = action.metadata?.clientRpcUrl;
|
|
2976
|
+
if (action.metadata?.awaitingLimit) {
|
|
2977
|
+
return actionError(
|
|
2978
|
+
action,
|
|
2979
|
+
"APPROVE_SPL action is still waiting for a One-Tap allowance amount."
|
|
2980
|
+
);
|
|
2981
|
+
}
|
|
2982
|
+
if (typeof ownerPubkey !== "string" || ownerPubkey.trim() === "") {
|
|
2983
|
+
return actionError(action, "APPROVE_SPL metadata is missing ownerPubkey.");
|
|
2984
|
+
}
|
|
2985
|
+
if (typeof unsignedApproveTxBase64 !== "string" || unsignedApproveTxBase64.trim() === "") {
|
|
2986
|
+
return actionError(
|
|
2987
|
+
action,
|
|
2988
|
+
"APPROVE_SPL metadata is missing unsignedApproveTxBase64."
|
|
2989
|
+
);
|
|
2990
|
+
}
|
|
2991
|
+
appendDebug("info", "APPROVE_SPL: entry", {
|
|
2992
|
+
actionId: action.id,
|
|
2993
|
+
ownerPubkey,
|
|
2994
|
+
tokenSymbol: typeof tokenSymbol === "string" ? tokenSymbol : null
|
|
2995
|
+
});
|
|
2996
|
+
if (typeof clientRpcUrl === "string" && clientRpcUrl.trim() !== "") {
|
|
2997
|
+
const sendLabel = "APPROVE_SPL signAndSendApproveSplViaWallet";
|
|
2998
|
+
const sent = await withWatchdog(
|
|
2999
|
+
withTimeout(
|
|
3000
|
+
signAndSendApproveSplViaWallet({
|
|
3001
|
+
selection,
|
|
3002
|
+
unsignedTxBase64: unsignedApproveTxBase64,
|
|
3003
|
+
expectedOwnerPubkey: ownerPubkey,
|
|
3004
|
+
rpcUrl: clientRpcUrl
|
|
3005
|
+
}),
|
|
3006
|
+
WALLET_PROMPT_TIMEOUT_MS,
|
|
3007
|
+
sendLabel
|
|
3008
|
+
),
|
|
3009
|
+
sendLabel
|
|
3010
|
+
);
|
|
3011
|
+
appendDebug("info", "APPROVE_SPL: tx submitted via wallet", {
|
|
3012
|
+
actionId: action.id,
|
|
3013
|
+
signature: sent.signature
|
|
3014
|
+
});
|
|
3015
|
+
try {
|
|
3016
|
+
options?.onConfirming?.(action);
|
|
3017
|
+
} catch (callbackErr) {
|
|
3018
|
+
appendDebug("warn", "APPROVE_SPL: onConfirming callback threw", {
|
|
3019
|
+
actionId: action.id,
|
|
3020
|
+
error: callbackErr instanceof Error ? callbackErr.message : String(callbackErr)
|
|
3021
|
+
});
|
|
3022
|
+
}
|
|
3023
|
+
const confirmLabel = "APPROVE_SPL confirmApproveSplViaPolling";
|
|
3024
|
+
let slot;
|
|
3025
|
+
try {
|
|
3026
|
+
const confirmation = await withWatchdog(
|
|
3027
|
+
confirmApproveSplViaPolling({
|
|
3028
|
+
rpcUrl: clientRpcUrl,
|
|
3029
|
+
signature: sent.signature
|
|
3030
|
+
}),
|
|
3031
|
+
confirmLabel
|
|
3032
|
+
);
|
|
3033
|
+
slot = confirmation.slot;
|
|
3034
|
+
appendDebug("info", "APPROVE_SPL: settled", {
|
|
3035
|
+
actionId: action.id,
|
|
3036
|
+
signature: sent.signature,
|
|
3037
|
+
slot
|
|
3038
|
+
});
|
|
3039
|
+
} catch (pollErr) {
|
|
3040
|
+
const pollMsg = pollErr instanceof Error ? pollErr.message : String(pollErr);
|
|
3041
|
+
if (pollMsg === APPROVE_SPL_CONFIRMATION_TIMEOUT_MESSAGE) {
|
|
3042
|
+
throw pollErr;
|
|
3043
|
+
}
|
|
3044
|
+
if (pollMsg.startsWith(APPROVE_SPL_ONCHAIN_FAILURE_PREFIX)) {
|
|
3045
|
+
throw pollErr;
|
|
3046
|
+
}
|
|
3047
|
+
appendDebug("warn", "APPROVE_SPL: confirmation poll failed; relying on server verification", {
|
|
3048
|
+
actionId: action.id,
|
|
3049
|
+
signature: sent.signature,
|
|
3050
|
+
error: pollMsg
|
|
3051
|
+
});
|
|
3052
|
+
}
|
|
3053
|
+
return actionSuccess(
|
|
3054
|
+
action,
|
|
3055
|
+
`Confirmed SPL approval for ${typeof tokenSymbol === "string" ? tokenSymbol : "tokens"}.`,
|
|
3056
|
+
{
|
|
3057
|
+
approveSignature: sent.signature
|
|
3058
|
+
}
|
|
3059
|
+
);
|
|
3060
|
+
}
|
|
3061
|
+
const signLabel = "APPROVE_SPL signSolanaTransaction";
|
|
3062
|
+
const signed = await withWatchdog(
|
|
3063
|
+
withTimeout(
|
|
3064
|
+
signSolanaTransaction(
|
|
3065
|
+
selection,
|
|
3066
|
+
unsignedApproveTxBase64,
|
|
3067
|
+
ownerPubkey
|
|
3068
|
+
),
|
|
3069
|
+
WALLET_PROMPT_TIMEOUT_MS,
|
|
3070
|
+
signLabel
|
|
3071
|
+
),
|
|
3072
|
+
signLabel
|
|
3073
|
+
);
|
|
3074
|
+
appendDebug("info", "APPROVE_SPL: signed (legacy path)", {
|
|
3075
|
+
actionId: action.id
|
|
3076
|
+
});
|
|
3077
|
+
return actionSuccess(
|
|
3078
|
+
action,
|
|
3079
|
+
`Signed SPL approval for ${typeof tokenSymbol === "string" ? tokenSymbol : "tokens"}.`,
|
|
3080
|
+
{ signedTxBase64: signed.signedTxBase64 }
|
|
3081
|
+
);
|
|
3082
|
+
} catch (err) {
|
|
3083
|
+
const msg = err instanceof Error ? err.message : "Failed to sign SPL approval";
|
|
3084
|
+
const timedOut = err instanceof PromiseTimeoutError;
|
|
3085
|
+
const confirmTimedOut = msg === APPROVE_SPL_CONFIRMATION_TIMEOUT_MESSAGE;
|
|
3086
|
+
appendDebug(timedOut || confirmTimedOut ? "warn" : "error", "APPROVE_SPL: threw", {
|
|
3087
|
+
actionId: action.id,
|
|
3088
|
+
error: msg,
|
|
3089
|
+
userRejected: isUserRejection(msg),
|
|
3090
|
+
timedOut,
|
|
3091
|
+
confirmTimedOut
|
|
3092
|
+
});
|
|
3093
|
+
if (timedOut) {
|
|
3094
|
+
return actionError(action, WALLET_PROMPT_TIMEOUT_MESSAGE);
|
|
3095
|
+
}
|
|
3096
|
+
if (confirmTimedOut) {
|
|
3097
|
+
return actionError(
|
|
3098
|
+
action,
|
|
3099
|
+
"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."
|
|
3100
|
+
);
|
|
3101
|
+
}
|
|
3102
|
+
return actionError(
|
|
3103
|
+
action,
|
|
3104
|
+
isUserRejection(msg) ? "You rejected the SPL approval transaction. Please approve the transaction in Phantom to continue." : msg
|
|
3105
|
+
);
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
async function executeExecuteBridgeSolana(action) {
|
|
3109
|
+
try {
|
|
3110
|
+
const selection = getSolanaWalletSelection(action);
|
|
3111
|
+
const ownerPubkey = action.metadata?.ownerPubkey ?? action.metadata?.senderAddress;
|
|
3112
|
+
const unsignedTxBase64 = action.metadata?.unsignedTxBase64;
|
|
3113
|
+
if (typeof ownerPubkey !== "string" || ownerPubkey.trim() === "") {
|
|
3114
|
+
return actionError(action, "SVM EXECUTE_BRIDGE metadata is missing ownerPubkey.");
|
|
3115
|
+
}
|
|
3116
|
+
if (typeof unsignedTxBase64 !== "string" || unsignedTxBase64.trim() === "") {
|
|
3117
|
+
return actionError(action, "SVM EXECUTE_BRIDGE metadata is missing unsignedTxBase64.");
|
|
3118
|
+
}
|
|
3119
|
+
appendDebug("info", "EXECUTE_BRIDGE: svm entry", {
|
|
3120
|
+
actionId: action.id,
|
|
3121
|
+
ownerPubkey
|
|
3122
|
+
});
|
|
3123
|
+
const label = "EXECUTE_BRIDGE signSolanaTransaction";
|
|
3124
|
+
const signed = await withWatchdog(
|
|
3125
|
+
withTimeout(
|
|
3126
|
+
signSolanaTransaction(
|
|
3127
|
+
selection,
|
|
3128
|
+
unsignedTxBase64,
|
|
3129
|
+
ownerPubkey
|
|
3130
|
+
),
|
|
3131
|
+
WALLET_PROMPT_TIMEOUT_MS,
|
|
3132
|
+
label
|
|
3133
|
+
),
|
|
3134
|
+
label
|
|
3135
|
+
);
|
|
3136
|
+
appendDebug("info", "EXECUTE_BRIDGE: svm signed", {
|
|
3137
|
+
actionId: action.id
|
|
3138
|
+
});
|
|
3139
|
+
return actionSuccess(
|
|
3140
|
+
action,
|
|
3141
|
+
"Signed Solana bridge transfer in Phantom.",
|
|
3142
|
+
{ signedTxBase64: signed.signedTxBase64, ownerPubkey: signed.ownerPubkey }
|
|
3143
|
+
);
|
|
3144
|
+
} catch (err) {
|
|
3145
|
+
const msg = err instanceof Error ? err.message : "Failed to sign Solana bridge transfer";
|
|
3146
|
+
const timedOut = err instanceof PromiseTimeoutError;
|
|
3147
|
+
appendDebug(timedOut ? "warn" : "error", "EXECUTE_BRIDGE: svm threw", {
|
|
3148
|
+
actionId: action.id,
|
|
3149
|
+
error: msg,
|
|
3150
|
+
userRejected: isUserRejection(msg),
|
|
3151
|
+
timedOut
|
|
3152
|
+
});
|
|
3153
|
+
if (timedOut) {
|
|
3154
|
+
return actionError(action, WALLET_PROMPT_TIMEOUT_MESSAGE);
|
|
3155
|
+
}
|
|
3156
|
+
return actionError(
|
|
3157
|
+
action,
|
|
3158
|
+
isUserRejection(msg) ? "You rejected the Solana transfer transaction. Please approve the transaction in Phantom to continue." : msg
|
|
3159
|
+
);
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
async function executeSvmCombinedFirstTransfer(actions, options) {
|
|
3163
|
+
if (actions.length !== 2) {
|
|
3164
|
+
throw new Error(
|
|
3165
|
+
`executeSvmCombinedFirstTransfer requires exactly 2 actions, received ${actions.length}.`
|
|
3166
|
+
);
|
|
3167
|
+
}
|
|
3168
|
+
const approveAction = actions[0].action;
|
|
3169
|
+
const bridgeAction = actions[1].action;
|
|
3170
|
+
if (approveAction.type !== "APPROVE_SPL") {
|
|
3171
|
+
throw new Error(
|
|
3172
|
+
`executeSvmCombinedFirstTransfer expects APPROVE_SPL first, got ${approveAction.type}.`
|
|
3173
|
+
);
|
|
3174
|
+
}
|
|
3175
|
+
if (bridgeAction.type !== "EXECUTE_BRIDGE" || bridgeAction.metadata?.chainFamily !== "svm") {
|
|
3176
|
+
throw new Error(
|
|
3177
|
+
`executeSvmCombinedFirstTransfer expects SVM EXECUTE_BRIDGE second, got ${bridgeAction.type}/${bridgeAction.metadata?.chainFamily ?? "unknown"}.`
|
|
3178
|
+
);
|
|
3179
|
+
}
|
|
3180
|
+
const ownerPubkey = approveAction.metadata?.ownerPubkey;
|
|
3181
|
+
const unsignedApproveTxBase64 = approveAction.metadata?.unsignedApproveTxBase64;
|
|
3182
|
+
const tokenSymbol = approveAction.metadata?.tokenSymbol;
|
|
3183
|
+
const unsignedBridgeTxBase64 = bridgeAction.metadata?.unsignedTxBase64;
|
|
3184
|
+
if (typeof ownerPubkey !== "string" || ownerPubkey.trim() === "") {
|
|
3185
|
+
return failBothActions(
|
|
3186
|
+
approveAction,
|
|
3187
|
+
bridgeAction,
|
|
3188
|
+
"APPROVE_SPL metadata is missing ownerPubkey."
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
if (typeof unsignedApproveTxBase64 !== "string" || unsignedApproveTxBase64.trim() === "") {
|
|
3192
|
+
return failBothActions(
|
|
3193
|
+
approveAction,
|
|
3194
|
+
bridgeAction,
|
|
3195
|
+
"APPROVE_SPL metadata is missing unsignedApproveTxBase64."
|
|
3196
|
+
);
|
|
3197
|
+
}
|
|
3198
|
+
if (typeof unsignedBridgeTxBase64 !== "string" || unsignedBridgeTxBase64.trim() === "") {
|
|
3199
|
+
return failBothActions(
|
|
3200
|
+
approveAction,
|
|
3201
|
+
bridgeAction,
|
|
3202
|
+
"SVM EXECUTE_BRIDGE metadata is missing unsignedTxBase64."
|
|
3203
|
+
);
|
|
3204
|
+
}
|
|
3205
|
+
const selection = getSolanaWalletSelection(approveAction);
|
|
3206
|
+
appendDebug("info", "SVM_COMBINED: entry", {
|
|
3207
|
+
approveActionId: approveAction.id,
|
|
3208
|
+
bridgeActionId: bridgeAction.id,
|
|
3209
|
+
ownerPubkey,
|
|
3210
|
+
tokenSymbol: typeof tokenSymbol === "string" ? tokenSymbol : null
|
|
3211
|
+
});
|
|
3212
|
+
const signLabel = "SVM_COMBINED signAllSolanaTransactions";
|
|
3213
|
+
let signed;
|
|
3214
|
+
try {
|
|
3215
|
+
signed = await withWatchdog(
|
|
3216
|
+
withTimeout(
|
|
3217
|
+
signAllSolanaTransactions(
|
|
3218
|
+
selection,
|
|
3219
|
+
[unsignedApproveTxBase64, unsignedBridgeTxBase64],
|
|
3220
|
+
ownerPubkey
|
|
3221
|
+
),
|
|
3222
|
+
WALLET_PROMPT_TIMEOUT_MS,
|
|
3223
|
+
signLabel
|
|
3224
|
+
),
|
|
3225
|
+
signLabel
|
|
3226
|
+
);
|
|
3227
|
+
} catch (err) {
|
|
3228
|
+
const msg = err instanceof Error ? err.message : "Failed to sign Solana transactions";
|
|
3229
|
+
const timedOut = err instanceof PromiseTimeoutError;
|
|
3230
|
+
appendDebug(timedOut ? "warn" : "error", "SVM_COMBINED: signAllTransactions threw", {
|
|
3231
|
+
approveActionId: approveAction.id,
|
|
3232
|
+
bridgeActionId: bridgeAction.id,
|
|
3233
|
+
error: msg,
|
|
3234
|
+
userRejected: isUserRejection(msg),
|
|
3235
|
+
timedOut
|
|
3236
|
+
});
|
|
3237
|
+
if (timedOut) {
|
|
3238
|
+
return failBothActions(
|
|
3239
|
+
approveAction,
|
|
3240
|
+
bridgeAction,
|
|
3241
|
+
WALLET_PROMPT_TIMEOUT_MESSAGE
|
|
3242
|
+
);
|
|
3243
|
+
}
|
|
3244
|
+
if (isUserRejection(msg)) {
|
|
3245
|
+
return failBothActions(
|
|
3246
|
+
approveAction,
|
|
3247
|
+
bridgeAction,
|
|
3248
|
+
"You rejected the Solana transactions. Please approve the prompt in Phantom to continue."
|
|
3249
|
+
);
|
|
3250
|
+
}
|
|
3251
|
+
return failBothActions(approveAction, bridgeAction, msg);
|
|
3252
|
+
}
|
|
3253
|
+
const [approveSignedB64, bridgeSignedB64] = signed.signedTxsBase64;
|
|
3254
|
+
appendDebug("info", "SVM_COMBINED: signAllTransactions returned", {
|
|
3255
|
+
approveActionId: approveAction.id,
|
|
3256
|
+
bridgeActionId: bridgeAction.id
|
|
3257
|
+
});
|
|
3258
|
+
try {
|
|
3259
|
+
options?.onApproveSplConfirming?.(approveAction);
|
|
3260
|
+
} catch (callbackErr) {
|
|
3261
|
+
appendDebug("warn", "SVM_COMBINED: onApproveSplConfirming callback threw", {
|
|
3262
|
+
approveActionId: approveAction.id,
|
|
3263
|
+
error: callbackErr instanceof Error ? callbackErr.message : String(callbackErr)
|
|
3264
|
+
});
|
|
3265
|
+
}
|
|
3266
|
+
return {
|
|
3267
|
+
txHash: void 0,
|
|
3268
|
+
actionResults: [
|
|
3269
|
+
{
|
|
3270
|
+
action: approveAction,
|
|
3271
|
+
result: actionSuccess(
|
|
3272
|
+
approveAction,
|
|
3273
|
+
`Signed SPL approval for ${typeof tokenSymbol === "string" ? tokenSymbol : "tokens"}.`,
|
|
3274
|
+
{ signedTxBase64: approveSignedB64, ownerPubkey: signed.ownerPubkey }
|
|
3275
|
+
)
|
|
3276
|
+
},
|
|
3277
|
+
{
|
|
3278
|
+
action: bridgeAction,
|
|
3279
|
+
result: actionSuccess(
|
|
3280
|
+
bridgeAction,
|
|
3281
|
+
"Signed Solana bridge transfer in Phantom.",
|
|
3282
|
+
{ signedTxBase64: bridgeSignedB64, ownerPubkey: signed.ownerPubkey }
|
|
3283
|
+
)
|
|
3284
|
+
}
|
|
3285
|
+
]
|
|
3286
|
+
};
|
|
3287
|
+
}
|
|
3288
|
+
function failBothActions(approveAction, bridgeAction, message) {
|
|
3289
|
+
return {
|
|
3290
|
+
txHash: void 0,
|
|
3291
|
+
actionResults: [
|
|
3292
|
+
{ action: approveAction, result: actionError(approveAction, message) },
|
|
3293
|
+
{ action: bridgeAction, result: actionError(bridgeAction, message) }
|
|
3294
|
+
]
|
|
3295
|
+
};
|
|
3296
|
+
}
|
|
2583
3297
|
async function executeApprovePermit2(action, wagmiConfig2) {
|
|
2584
3298
|
let walletClient = null;
|
|
2585
3299
|
let sender = null;
|
|
@@ -3225,6 +3939,7 @@ function useAuthorizationExecutor(options) {
|
|
|
3225
3939
|
const [results, setResults] = useState([]);
|
|
3226
3940
|
const [error, setError] = useState(null);
|
|
3227
3941
|
const [currentAction, setCurrentAction] = useState(null);
|
|
3942
|
+
const [approveSplConfirming, setApproveSplConfirming] = useState(null);
|
|
3228
3943
|
const executingRef = useRef(false);
|
|
3229
3944
|
const walletCapabilitiesRef = useRef({});
|
|
3230
3945
|
const walletCapabilitiesRefreshedRef = useRef(false);
|
|
@@ -3263,16 +3978,17 @@ function useAuthorizationExecutor(options) {
|
|
|
3263
3978
|
}
|
|
3264
3979
|
setError(null);
|
|
3265
3980
|
setCurrentAction(null);
|
|
3981
|
+
setApproveSplConfirming(null);
|
|
3266
3982
|
setExecuting(false);
|
|
3267
3983
|
executingRef.current = false;
|
|
3268
3984
|
}, []);
|
|
3269
3985
|
const dispatchAction = useCallback(
|
|
3270
|
-
async (action) => {
|
|
3986
|
+
async (action, dispatchOptions) => {
|
|
3271
3987
|
setCurrentAction(action);
|
|
3272
3988
|
switch (action.type) {
|
|
3273
3989
|
case "OPEN_PROVIDER": {
|
|
3274
3990
|
const result = await executeOpenProvider(action, wagmiConfig2, connectors, connectAsync);
|
|
3275
|
-
if (result.status === "success") {
|
|
3991
|
+
if (result.status === "success" && action.metadata?.chainFamily !== "svm") {
|
|
3276
3992
|
walletCapabilitiesRef.current = await refreshWalletCapabilities(
|
|
3277
3993
|
wagmiConfig2,
|
|
3278
3994
|
"open-provider"
|
|
@@ -3284,7 +4000,7 @@ function useAuthorizationExecutor(options) {
|
|
|
3284
4000
|
return executeSelectSource(action, waitForSelection);
|
|
3285
4001
|
case "SWITCH_CHAIN": {
|
|
3286
4002
|
const result = await executeSwitchChain(action, wagmiConfig2, switchChainAsync);
|
|
3287
|
-
if (result.status === "success") {
|
|
4003
|
+
if (result.status === "success" && action.metadata?.chainFamily !== "svm") {
|
|
3288
4004
|
walletCapabilitiesRef.current = await refreshWalletCapabilities(
|
|
3289
4005
|
wagmiConfig2,
|
|
3290
4006
|
"switch-chain"
|
|
@@ -3304,7 +4020,33 @@ function useAuthorizationExecutor(options) {
|
|
|
3304
4020
|
}
|
|
3305
4021
|
return executeSignPermit2(action, wagmiConfig2, apiBaseUrl ?? "", sessionIdRef.current);
|
|
3306
4022
|
}
|
|
4023
|
+
case "APPROVE_SPL": {
|
|
4024
|
+
if (action.metadata?.awaitingLimit) {
|
|
4025
|
+
throw new Error(
|
|
4026
|
+
"APPROVE_SPL action has awaitingLimit. The orchestrator must handle one-tap setup before executing this action."
|
|
4027
|
+
);
|
|
4028
|
+
}
|
|
4029
|
+
const externalConfirmingCallback = dispatchOptions?.onApproveSplConfirming;
|
|
4030
|
+
return executeApproveSplSolana(action, {
|
|
4031
|
+
onConfirming: (a) => {
|
|
4032
|
+
setApproveSplConfirming(a);
|
|
4033
|
+
if (externalConfirmingCallback) {
|
|
4034
|
+
try {
|
|
4035
|
+
externalConfirmingCallback(a);
|
|
4036
|
+
} catch (callbackErr) {
|
|
4037
|
+
appendDebug("warn", "APPROVE_SPL: orchestrator onApproveSplConfirming threw", {
|
|
4038
|
+
actionId: a.id,
|
|
4039
|
+
error: callbackErr instanceof Error ? callbackErr.message : String(callbackErr)
|
|
4040
|
+
});
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
});
|
|
4045
|
+
}
|
|
3307
4046
|
case "EXECUTE_BRIDGE":
|
|
4047
|
+
if (action.metadata?.chainFamily === "svm") {
|
|
4048
|
+
return executeExecuteBridgeSolana(action);
|
|
4049
|
+
}
|
|
3308
4050
|
return executeExecuteBridge(action, wagmiConfig2);
|
|
3309
4051
|
default:
|
|
3310
4052
|
return actionError(action, `Unsupported action type: ${action.type}`);
|
|
@@ -3317,7 +4059,11 @@ function useAuthorizationExecutor(options) {
|
|
|
3317
4059
|
if (options2?.sessionId) {
|
|
3318
4060
|
sessionIdRef.current = options2.sessionId;
|
|
3319
4061
|
}
|
|
3320
|
-
|
|
4062
|
+
try {
|
|
4063
|
+
return await dispatchAction(action, options2);
|
|
4064
|
+
} finally {
|
|
4065
|
+
setApproveSplConfirming(null);
|
|
4066
|
+
}
|
|
3321
4067
|
},
|
|
3322
4068
|
[dispatchAction]
|
|
3323
4069
|
);
|
|
@@ -3338,6 +4084,43 @@ function useAuthorizationExecutor(options) {
|
|
|
3338
4084
|
},
|
|
3339
4085
|
[apiBaseUrl, wagmiConfig2]
|
|
3340
4086
|
);
|
|
4087
|
+
const executeSvmCombinedFirstTransferImpl = useCallback(
|
|
4088
|
+
async (actions, options2) => {
|
|
4089
|
+
setCurrentAction(actions[0]?.action ?? null);
|
|
4090
|
+
const externalConfirmingCallback = options2?.onApproveSplConfirming;
|
|
4091
|
+
return executeSvmCombinedFirstTransfer(actions, {
|
|
4092
|
+
onApproveSplConfirming: (a) => {
|
|
4093
|
+
setApproveSplConfirming(a);
|
|
4094
|
+
if (externalConfirmingCallback) {
|
|
4095
|
+
try {
|
|
4096
|
+
externalConfirmingCallback(a);
|
|
4097
|
+
} catch (callbackErr) {
|
|
4098
|
+
appendDebug("warn", "SVM_COMBINED: orchestrator onApproveSplConfirming threw", {
|
|
4099
|
+
actionId: a.id,
|
|
4100
|
+
error: callbackErr instanceof Error ? callbackErr.message : String(callbackErr)
|
|
4101
|
+
});
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
});
|
|
4106
|
+
},
|
|
4107
|
+
[]
|
|
4108
|
+
);
|
|
4109
|
+
const canSignAllSolanaTransactions = useCallback(
|
|
4110
|
+
async (action) => {
|
|
4111
|
+
try {
|
|
4112
|
+
const selection = getSolanaWalletSelection(action);
|
|
4113
|
+
return await supportsSignAllSolanaTransactions(selection);
|
|
4114
|
+
} catch (err) {
|
|
4115
|
+
appendDebug("warn", "SVM_COMBINED: signAllTransactions feature-detect failed", {
|
|
4116
|
+
actionId: action.id,
|
|
4117
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4118
|
+
});
|
|
4119
|
+
return false;
|
|
4120
|
+
}
|
|
4121
|
+
},
|
|
4122
|
+
[]
|
|
4123
|
+
);
|
|
3341
4124
|
const canBatch = useCallback(async () => {
|
|
3342
4125
|
const cacheKey = getBatchCapabilityCacheKey(wagmiConfig2);
|
|
3343
4126
|
const cachedDecision = batchCapabilityDecisionRef.current;
|
|
@@ -3381,11 +4164,13 @@ function useAuthorizationExecutor(options) {
|
|
|
3381
4164
|
setResults([]);
|
|
3382
4165
|
setError(null);
|
|
3383
4166
|
setBatchTxHash(null);
|
|
4167
|
+
setApproveSplConfirming(null);
|
|
3384
4168
|
return true;
|
|
3385
4169
|
}, []);
|
|
3386
4170
|
const endExecution = useCallback(() => {
|
|
3387
4171
|
sessionIdRef.current = null;
|
|
3388
4172
|
setCurrentAction(null);
|
|
4173
|
+
setApproveSplConfirming(null);
|
|
3389
4174
|
setExecuting(false);
|
|
3390
4175
|
executingRef.current = false;
|
|
3391
4176
|
}, []);
|
|
@@ -3478,12 +4263,15 @@ function useAuthorizationExecutor(options) {
|
|
|
3478
4263
|
results,
|
|
3479
4264
|
error,
|
|
3480
4265
|
currentAction,
|
|
4266
|
+
approveSplConfirming,
|
|
3481
4267
|
pendingSelectSource,
|
|
3482
4268
|
resolveSelectSource,
|
|
3483
4269
|
cancelPendingExecution,
|
|
3484
4270
|
batchTxHash,
|
|
3485
4271
|
executeAction,
|
|
3486
4272
|
executeBatch,
|
|
4273
|
+
executeSvmCombinedFirstTransfer: executeSvmCombinedFirstTransferImpl,
|
|
4274
|
+
canSignAllSolanaTransactions,
|
|
3487
4275
|
canBatch,
|
|
3488
4276
|
checkPaymasterSupport,
|
|
3489
4277
|
getCapabilitySnapshot,
|
|
@@ -3512,6 +4300,8 @@ function isWebAuthnPasskeyDismissalError(err) {
|
|
|
3512
4300
|
|
|
3513
4301
|
// src/hooks/useTransferSigning.ts
|
|
3514
4302
|
var TRANSFER_SIGN_MAX_POLLS = 60;
|
|
4303
|
+
var FAST_POLL_COUNT2 = 6;
|
|
4304
|
+
var FAST_POLL_INTERVAL_MS2 = 500;
|
|
3515
4305
|
function waitForDocumentFocus2(timeoutMs = 5e3, intervalMs = 100) {
|
|
3516
4306
|
return new Promise((resolve, reject) => {
|
|
3517
4307
|
if (typeof document === "undefined") {
|
|
@@ -3539,9 +4329,23 @@ function hexToBytes(hex) {
|
|
|
3539
4329
|
const bytes = clean.match(/.{1,2}/g).map((b) => parseInt(b, 16));
|
|
3540
4330
|
return new Uint8Array(bytes);
|
|
3541
4331
|
}
|
|
4332
|
+
function base64ToBytes2(base64) {
|
|
4333
|
+
const binary = atob(base64);
|
|
4334
|
+
const bytes = new Uint8Array(binary.length);
|
|
4335
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
4336
|
+
bytes[i] = binary.charCodeAt(i);
|
|
4337
|
+
}
|
|
4338
|
+
return bytes;
|
|
4339
|
+
}
|
|
3542
4340
|
function toBase642(buffer) {
|
|
3543
4341
|
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
3544
4342
|
}
|
|
4343
|
+
function getSigningChallenge(payload) {
|
|
4344
|
+
if (payload.chainFamily === "svm") {
|
|
4345
|
+
return base64ToBytes2(payload.message);
|
|
4346
|
+
}
|
|
4347
|
+
return hexToBytes(payload.userOpHash);
|
|
4348
|
+
}
|
|
3545
4349
|
function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
3546
4350
|
const blinkConfig = useOptionalBlinkConfig();
|
|
3547
4351
|
const apiBaseUrl = options?.apiBaseUrl ?? blinkConfig?.apiBaseUrl;
|
|
@@ -3558,7 +4362,7 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3558
4362
|
const [error, setError] = useState(null);
|
|
3559
4363
|
const [passkeyDismissed, setPasskeyDismissed] = useState(false);
|
|
3560
4364
|
const signTransfer2 = useCallback(
|
|
3561
|
-
async (transferId) => {
|
|
4365
|
+
async (transferId, opts) => {
|
|
3562
4366
|
setSigning(true);
|
|
3563
4367
|
setError(null);
|
|
3564
4368
|
setPasskeyDismissed(false);
|
|
@@ -3575,22 +4379,28 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3575
4379
|
throw new Error("Could not get access token");
|
|
3576
4380
|
}
|
|
3577
4381
|
let payload = null;
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
4382
|
+
if (opts?.knownSignPayload) {
|
|
4383
|
+
payload = opts.knownSignPayload;
|
|
4384
|
+
setSignPayload(payload);
|
|
4385
|
+
} else {
|
|
4386
|
+
for (let i = 0; i < TRANSFER_SIGN_MAX_POLLS; i++) {
|
|
4387
|
+
const transfer = await fetchTransfer(
|
|
4388
|
+
apiBaseUrl,
|
|
4389
|
+
token ?? "",
|
|
4390
|
+
transferId,
|
|
4391
|
+
authorizationSessionToken
|
|
4392
|
+
);
|
|
4393
|
+
if (transfer.signPayload) {
|
|
4394
|
+
payload = transfer.signPayload;
|
|
4395
|
+
setSignPayload(payload);
|
|
4396
|
+
break;
|
|
4397
|
+
}
|
|
4398
|
+
if (transfer.status !== "AUTHORIZED" && transfer.status !== "CREATED") {
|
|
4399
|
+
throw new Error(`Unexpected transfer status: ${transfer.status}`);
|
|
4400
|
+
}
|
|
4401
|
+
const intervalMs = i < FAST_POLL_COUNT2 ? Math.min(FAST_POLL_INTERVAL_MS2, pollIntervalMs) : pollIntervalMs;
|
|
4402
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
3592
4403
|
}
|
|
3593
|
-
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
3594
4404
|
}
|
|
3595
4405
|
if (!payload) {
|
|
3596
4406
|
throw new Error("Timed out waiting for sign payload. Please try again.");
|
|
@@ -3601,8 +4411,7 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3601
4411
|
"Sign payload is missing passkeyCredentialId. Cannot request passkey signing without it."
|
|
3602
4412
|
);
|
|
3603
4413
|
}
|
|
3604
|
-
const
|
|
3605
|
-
let signedUserOp;
|
|
4414
|
+
const challengeBytes = getSigningChallenge(payload);
|
|
3606
4415
|
const allowCredentials = [{
|
|
3607
4416
|
type: "public-key",
|
|
3608
4417
|
id: credentialIdBase64ToBytes(passkeyCredentialId)
|
|
@@ -3621,7 +4430,7 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3621
4430
|
try {
|
|
3622
4431
|
assertion = await navigator.credentials.get({
|
|
3623
4432
|
publicKey: {
|
|
3624
|
-
challenge:
|
|
4433
|
+
challenge: challengeBytes,
|
|
3625
4434
|
rpId: signingRpId,
|
|
3626
4435
|
allowCredentials,
|
|
3627
4436
|
userVerification: "required",
|
|
@@ -3638,18 +4447,24 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
3638
4447
|
throw new TransferSigningPasskeyDismissedError();
|
|
3639
4448
|
}
|
|
3640
4449
|
const response = assertion.response;
|
|
3641
|
-
|
|
3642
|
-
...payload.userOp,
|
|
4450
|
+
const webauthnAssertion = {
|
|
3643
4451
|
credentialId: toBase642(assertion.rawId),
|
|
3644
4452
|
signature: toBase642(response.signature),
|
|
3645
4453
|
authenticatorData: toBase642(response.authenticatorData),
|
|
3646
4454
|
clientDataJSON: toBase642(response.clientDataJSON)
|
|
3647
4455
|
};
|
|
4456
|
+
const signedTransfer = payload.chainFamily === "svm" ? {
|
|
4457
|
+
chainFamily: "svm",
|
|
4458
|
+
webauthnAssertion
|
|
4459
|
+
} : {
|
|
4460
|
+
...payload.userOp,
|
|
4461
|
+
...webauthnAssertion
|
|
4462
|
+
};
|
|
3648
4463
|
return await signTransfer(
|
|
3649
4464
|
apiBaseUrl,
|
|
3650
4465
|
token ?? "",
|
|
3651
4466
|
transferId,
|
|
3652
|
-
|
|
4467
|
+
signedTransfer,
|
|
3653
4468
|
authorizationSessionToken
|
|
3654
4469
|
);
|
|
3655
4470
|
} catch (err) {
|
|
@@ -3764,6 +4579,8 @@ function assertLinkedTransferBridgeExecuted(params) {
|
|
|
3764
4579
|
// src/hooks/useAuthorizationOrchestrator.ts
|
|
3765
4580
|
var ACTION_POLL_INTERVAL_MS2 = 500;
|
|
3766
4581
|
var ACTION_POLL_MAX_RETRIES2 = 20;
|
|
4582
|
+
var REPORT_COMPLETION_TIMEOUT_MS = 9e4;
|
|
4583
|
+
var REPORT_COMPLETION_TIMEOUT_MESSAGE = "REPORT_COMPLETION_TIMEOUT";
|
|
3767
4584
|
function useAuthorizationOrchestrator(deps) {
|
|
3768
4585
|
const blinkConfig = useOptionalBlinkConfig();
|
|
3769
4586
|
const resolvedApiBaseUrl = deps.apiBaseUrl ?? blinkConfig?.apiBaseUrl;
|
|
@@ -3891,6 +4708,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
3891
4708
|
waitForLinkedTransferSession: options?.waitForLinkedTransferSession ?? false
|
|
3892
4709
|
});
|
|
3893
4710
|
let cachedBatchSupport = null;
|
|
4711
|
+
let cachedSvmSignAllSupport = null;
|
|
3894
4712
|
while (mergedPending.length > 0) {
|
|
3895
4713
|
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
3896
4714
|
mergedPending = getMergedPending(sessions, completedIds);
|
|
@@ -3978,6 +4796,112 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
3978
4796
|
});
|
|
3979
4797
|
continue;
|
|
3980
4798
|
}
|
|
4799
|
+
if (action.type === "APPROVE_SPL" && (action.metadata?.awaitingLimit || options?.alwaysPauseForOneTap) && !oneTapCompletedActionIds.has(action.id)) {
|
|
4800
|
+
if (options?.onAwaitingOneTap) {
|
|
4801
|
+
console.info("[blink-sdk][orchestrator] Awaiting Solana one-tap setup via callback.", {
|
|
4802
|
+
actionId: action.id,
|
|
4803
|
+
ownerSessionId
|
|
4804
|
+
});
|
|
4805
|
+
await options.onAwaitingOneTap(action);
|
|
4806
|
+
console.info("[blink-sdk][orchestrator] Solana one-tap setup resumed via callback.", {
|
|
4807
|
+
actionId: action.id,
|
|
4808
|
+
ownerSessionId
|
|
4809
|
+
});
|
|
4810
|
+
} else {
|
|
4811
|
+
console.info("[blink-sdk][orchestrator] Awaiting Solana one-tap setup.", {
|
|
4812
|
+
actionId: action.id,
|
|
4813
|
+
ownerSessionId
|
|
4814
|
+
});
|
|
4815
|
+
await waitForOneTapPause(action);
|
|
4816
|
+
console.info("[blink-sdk][orchestrator] Solana one-tap setup resumed.", {
|
|
4817
|
+
actionId: action.id,
|
|
4818
|
+
ownerSessionId
|
|
4819
|
+
});
|
|
4820
|
+
}
|
|
4821
|
+
oneTapCompletedActionIds.add(action.id);
|
|
4822
|
+
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4823
|
+
await refreshAllSessions(apiBaseUrl, sessions, actionSessionMap);
|
|
4824
|
+
queueLinkedTransferSessions({
|
|
4825
|
+
sessions,
|
|
4826
|
+
linkedTransferSessionIds,
|
|
4827
|
+
pendingSessionIdsRef
|
|
4828
|
+
});
|
|
4829
|
+
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4830
|
+
markPendingOneTapActionsCompleted(sessions, oneTapCompletedActionIds);
|
|
4831
|
+
mergedPending = await waitForPendingActions({
|
|
4832
|
+
apiBaseUrl,
|
|
4833
|
+
sessions,
|
|
4834
|
+
completedIds,
|
|
4835
|
+
actionSessionMap,
|
|
4836
|
+
pendingSessionIdsRef,
|
|
4837
|
+
linkedTransferSessionIds,
|
|
4838
|
+
waitForLinkedTransferSession: options?.waitForLinkedTransferSession ?? false
|
|
4839
|
+
});
|
|
4840
|
+
continue;
|
|
4841
|
+
}
|
|
4842
|
+
if (isSvmCombinedFirstTransferPair(mergedPending)) {
|
|
4843
|
+
const pair = mergedPending.slice(0, 2);
|
|
4844
|
+
const [svmApproveAction, svmBridgeAction] = pair;
|
|
4845
|
+
if (cachedSvmSignAllSupport == null) {
|
|
4846
|
+
cachedSvmSignAllSupport = await authExecutor.canSignAllSolanaTransactions(
|
|
4847
|
+
svmApproveAction
|
|
4848
|
+
);
|
|
4849
|
+
}
|
|
4850
|
+
appendDebug("info", "orchestrator:svm-combined evaluation", {
|
|
4851
|
+
approveActionId: svmApproveAction.id,
|
|
4852
|
+
bridgeActionId: svmBridgeAction.id,
|
|
4853
|
+
ownerSessionId,
|
|
4854
|
+
signAllSupported: cachedSvmSignAllSupport
|
|
4855
|
+
});
|
|
4856
|
+
if (cachedSvmSignAllSupport) {
|
|
4857
|
+
const svmInputs = pair.map((candidate) => ({
|
|
4858
|
+
action: candidate,
|
|
4859
|
+
sessionId: actionSessionMap.get(candidate.id) ?? sessionId
|
|
4860
|
+
}));
|
|
4861
|
+
const svmBatchResult = await authExecutor.executeSvmCombinedFirstTransfer(
|
|
4862
|
+
svmInputs,
|
|
4863
|
+
{
|
|
4864
|
+
onApproveSplConfirming: options?.onApproveSplConfirming
|
|
4865
|
+
}
|
|
4866
|
+
);
|
|
4867
|
+
const errorResult = svmBatchResult.actionResults.find(
|
|
4868
|
+
({ result: result2 }) => result2.status === "error"
|
|
4869
|
+
);
|
|
4870
|
+
if (errorResult) {
|
|
4871
|
+
authExecutor.addResult(errorResult.result);
|
|
4872
|
+
throw new Error(errorResult.result.message);
|
|
4873
|
+
}
|
|
4874
|
+
for (const { action: svmAction, result: result2 } of svmBatchResult.actionResults) {
|
|
4875
|
+
completedIds.add(svmAction.id);
|
|
4876
|
+
oneTapCompletedActionIds.delete(svmAction.id);
|
|
4877
|
+
authExecutor.addResult(result2);
|
|
4878
|
+
const svmOwnerSessionId = actionSessionMap.get(svmAction.id) ?? ownerSessionId;
|
|
4879
|
+
const reportedSession2 = await reportActionCompletionWithLogging(
|
|
4880
|
+
apiBaseUrl,
|
|
4881
|
+
svmAction,
|
|
4882
|
+
svmOwnerSessionId,
|
|
4883
|
+
result2
|
|
4884
|
+
);
|
|
4885
|
+
updateTrackedSession(sessions, svmOwnerSessionId, reportedSession2, actionSessionMap);
|
|
4886
|
+
}
|
|
4887
|
+
queueLinkedTransferSessions({
|
|
4888
|
+
sessions,
|
|
4889
|
+
linkedTransferSessionIds,
|
|
4890
|
+
pendingSessionIdsRef
|
|
4891
|
+
});
|
|
4892
|
+
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4893
|
+
mergedPending = await waitForPendingActions({
|
|
4894
|
+
apiBaseUrl,
|
|
4895
|
+
sessions,
|
|
4896
|
+
completedIds,
|
|
4897
|
+
actionSessionMap,
|
|
4898
|
+
pendingSessionIdsRef,
|
|
4899
|
+
linkedTransferSessionIds,
|
|
4900
|
+
waitForLinkedTransferSession: options?.waitForLinkedTransferSession ?? false
|
|
4901
|
+
});
|
|
4902
|
+
continue;
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
3981
4905
|
if (isBatchableAction(action)) {
|
|
3982
4906
|
const batchable = getLeadingBatchableActions(
|
|
3983
4907
|
mergedPending
|
|
@@ -4004,7 +4928,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
4004
4928
|
pendingSessionIdsRef
|
|
4005
4929
|
});
|
|
4006
4930
|
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4007
|
-
|
|
4931
|
+
markPendingOneTapActionsCompleted(sessions, oneTapCompletedActionIds);
|
|
4008
4932
|
mergedPending = await waitForPendingActions({
|
|
4009
4933
|
apiBaseUrl,
|
|
4010
4934
|
sessions,
|
|
@@ -4035,7 +4959,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
4035
4959
|
pendingSessionIdsRef
|
|
4036
4960
|
});
|
|
4037
4961
|
await ingestPendingSessions(apiBaseUrl, sessions, actionSessionMap, pendingSessionIdsRef);
|
|
4038
|
-
|
|
4962
|
+
markPendingOneTapActionsCompleted(sessions, oneTapCompletedActionIds);
|
|
4039
4963
|
mergedPending = await waitForPendingActions({
|
|
4040
4964
|
apiBaseUrl,
|
|
4041
4965
|
sessions,
|
|
@@ -4107,7 +5031,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
4107
5031
|
continue;
|
|
4108
5032
|
}
|
|
4109
5033
|
}
|
|
4110
|
-
const isPrePromptProbableActionType = action.type === "SIGN_PERMIT2" || action.type === "APPROVE_PERMIT2";
|
|
5034
|
+
const isPrePromptProbableActionType = action.type === "SIGN_PERMIT2" || action.type === "APPROVE_PERMIT2" || action.type === "APPROVE_SPL";
|
|
4111
5035
|
if (isPrePromptProbableActionType && probeBeforePrompt && !preProbedIds.has(action.id)) {
|
|
4112
5036
|
preProbedIds.add(action.id);
|
|
4113
5037
|
appendDebug("info", `${action.type}: pre-prompt probe start`, {
|
|
@@ -4152,7 +5076,10 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
4152
5076
|
actionId: action.id,
|
|
4153
5077
|
ownerSessionId
|
|
4154
5078
|
});
|
|
4155
|
-
const result = await authExecutor.executeAction(action, {
|
|
5079
|
+
const result = await authExecutor.executeAction(action, {
|
|
5080
|
+
sessionId: ownerSessionId,
|
|
5081
|
+
onApproveSplConfirming: options?.onApproveSplConfirming
|
|
5082
|
+
});
|
|
4156
5083
|
if (result.status === "success" && (action.type === "OPEN_PROVIDER" || action.type === "SWITCH_CHAIN")) {
|
|
4157
5084
|
cachedBatchSupport = null;
|
|
4158
5085
|
}
|
|
@@ -4392,7 +5319,24 @@ function getMergedPending(sessions, completedIds) {
|
|
|
4392
5319
|
for (const { session } of sessions) {
|
|
4393
5320
|
allActions.push(...getPendingActions(session, completedIds));
|
|
4394
5321
|
}
|
|
4395
|
-
|
|
5322
|
+
const hasPendingSvmSetupAction = allActions.some((action) => isSvmSetupAction(action));
|
|
5323
|
+
return allActions.sort((a, b) => {
|
|
5324
|
+
const leftIsDeferredSvmBridge = hasPendingSvmSetupAction && isSvmTransferBridgeAction(a);
|
|
5325
|
+
const rightIsDeferredSvmBridge = hasPendingSvmSetupAction && isSvmTransferBridgeAction(b);
|
|
5326
|
+
if (leftIsDeferredSvmBridge !== rightIsDeferredSvmBridge) {
|
|
5327
|
+
return leftIsDeferredSvmBridge ? 1 : -1;
|
|
5328
|
+
}
|
|
5329
|
+
return a.orderIndex - b.orderIndex;
|
|
5330
|
+
});
|
|
5331
|
+
}
|
|
5332
|
+
function isSvmSetupAction(action) {
|
|
5333
|
+
if (action.type === "APPROVE_SPL") {
|
|
5334
|
+
return true;
|
|
5335
|
+
}
|
|
5336
|
+
return action.type === "OPEN_PROVIDER" && action.metadata?.chainFamily === "svm";
|
|
5337
|
+
}
|
|
5338
|
+
function isSvmTransferBridgeAction(action) {
|
|
5339
|
+
return action.type === "EXECUTE_BRIDGE" && action.metadata?.chainFamily === "svm";
|
|
4396
5340
|
}
|
|
4397
5341
|
async function waitForPendingActions(params) {
|
|
4398
5342
|
const {
|
|
@@ -4466,14 +5410,19 @@ async function reportActionCompletionWithLogging(apiBaseUrl, action, ownerSessio
|
|
|
4466
5410
|
actionId: action.id,
|
|
4467
5411
|
ownerSessionId
|
|
4468
5412
|
});
|
|
5413
|
+
const label = `reportActionCompletion ${action.type}`;
|
|
4469
5414
|
try {
|
|
4470
5415
|
const reportedSession = await withWatchdog(
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
5416
|
+
withTimeout(
|
|
5417
|
+
reportActionCompletion(
|
|
5418
|
+
apiBaseUrl,
|
|
5419
|
+
action.id,
|
|
5420
|
+
result.data ?? {}
|
|
5421
|
+
),
|
|
5422
|
+
REPORT_COMPLETION_TIMEOUT_MS,
|
|
5423
|
+
label
|
|
4475
5424
|
),
|
|
4476
|
-
|
|
5425
|
+
label
|
|
4477
5426
|
);
|
|
4478
5427
|
console.info("[blink-sdk][orchestrator] Action completion reported.", {
|
|
4479
5428
|
actionId: action.id,
|
|
@@ -4489,16 +5438,22 @@ async function reportActionCompletionWithLogging(apiBaseUrl, action, ownerSessio
|
|
|
4489
5438
|
});
|
|
4490
5439
|
return reportedSession;
|
|
4491
5440
|
} catch (err) {
|
|
5441
|
+
const timedOut = err instanceof PromiseTimeoutError;
|
|
4492
5442
|
console.warn("[blink-sdk][orchestrator] Failed to report action completion.", {
|
|
4493
5443
|
actionId: action.id,
|
|
4494
5444
|
actionType: action.type,
|
|
4495
5445
|
ownerSessionId,
|
|
5446
|
+
timedOut,
|
|
4496
5447
|
error: err instanceof Error ? err.message : String(err)
|
|
4497
5448
|
});
|
|
4498
|
-
appendDebug("error", `orchestrator:reportActionCompletion failed ${action.type}`, {
|
|
5449
|
+
appendDebug(timedOut ? "warn" : "error", `orchestrator:reportActionCompletion ${timedOut ? "timed out" : "failed"} ${action.type}`, {
|
|
4499
5450
|
actionId: action.id,
|
|
5451
|
+
timedOut,
|
|
4500
5452
|
error: err instanceof Error ? err.message : String(err)
|
|
4501
5453
|
});
|
|
5454
|
+
if (timedOut) {
|
|
5455
|
+
throw new Error(REPORT_COMPLETION_TIMEOUT_MESSAGE);
|
|
5456
|
+
}
|
|
4502
5457
|
throw err;
|
|
4503
5458
|
}
|
|
4504
5459
|
}
|
|
@@ -4531,10 +5486,10 @@ function updateTrackedSession(sessions, ownerSessionId, reportedSession, actionS
|
|
|
4531
5486
|
}
|
|
4532
5487
|
}
|
|
4533
5488
|
}
|
|
4534
|
-
function
|
|
5489
|
+
function markPendingOneTapActionsCompleted(sessions, oneTapCompletedActionIds) {
|
|
4535
5490
|
for (const { session } of sessions) {
|
|
4536
5491
|
for (const action of session.actions) {
|
|
4537
|
-
if (action.type === "SIGN_PERMIT2" && action.status === "PENDING") {
|
|
5492
|
+
if ((action.type === "SIGN_PERMIT2" || action.type === "APPROVE_SPL") && action.status === "PENDING") {
|
|
4538
5493
|
oneTapCompletedActionIds.add(action.id);
|
|
4539
5494
|
}
|
|
4540
5495
|
}
|
|
@@ -4587,6 +5542,8 @@ function screenForPhase(phase) {
|
|
|
4587
5542
|
case "completed":
|
|
4588
5543
|
case "failed":
|
|
4589
5544
|
return "success";
|
|
5545
|
+
case "amount-too-low":
|
|
5546
|
+
return "amount-too-low";
|
|
4590
5547
|
}
|
|
4591
5548
|
}
|
|
4592
5549
|
|
|
@@ -4636,6 +5593,12 @@ function hasActiveWallet(accounts) {
|
|
|
4636
5593
|
return accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
|
|
4637
5594
|
}
|
|
4638
5595
|
function resolveTerminalPhase(state) {
|
|
5596
|
+
if (state.amountTooLow != null) {
|
|
5597
|
+
return {
|
|
5598
|
+
step: "amount-too-low",
|
|
5599
|
+
minAmountUsd: state.amountTooLow.minAmountUsd
|
|
5600
|
+
};
|
|
5601
|
+
}
|
|
4639
5602
|
const transferCompleted = state.transfer?.status === "COMPLETED";
|
|
4640
5603
|
const needsPasskeyBootstrap = state.privyAuthenticated && !state.activeCredentialId;
|
|
4641
5604
|
if (transferCompleted && !needsPasskeyBootstrap && !state.loginRequested) {
|
|
@@ -4789,7 +5752,8 @@ function createInitialState(config) {
|
|
|
4789
5752
|
setupDepositToken: null,
|
|
4790
5753
|
setupDepositConfirmed: false,
|
|
4791
5754
|
guestWalletPrepared: null,
|
|
4792
|
-
guestWalletDeeplinksPreparing: false
|
|
5755
|
+
guestWalletDeeplinksPreparing: false,
|
|
5756
|
+
amountTooLow: null
|
|
4793
5757
|
};
|
|
4794
5758
|
}
|
|
4795
5759
|
function clearAuthenticatedSessionState(state) {
|
|
@@ -4826,7 +5790,8 @@ function clearAuthenticatedSessionState(state) {
|
|
|
4826
5790
|
setupDepositToken: null,
|
|
4827
5791
|
setupDepositConfirmed: false,
|
|
4828
5792
|
guestWalletPrepared: null,
|
|
4829
|
-
guestWalletDeeplinksPreparing: false
|
|
5793
|
+
guestWalletDeeplinksPreparing: false,
|
|
5794
|
+
amountTooLow: null
|
|
4830
5795
|
};
|
|
4831
5796
|
}
|
|
4832
5797
|
function paymentReducer(state, action) {
|
|
@@ -5165,6 +6130,13 @@ function applyAction(state, action) {
|
|
|
5165
6130
|
};
|
|
5166
6131
|
case "SET_ERROR":
|
|
5167
6132
|
return { ...state, error: action.error };
|
|
6133
|
+
case "AMOUNT_TOO_LOW":
|
|
6134
|
+
return {
|
|
6135
|
+
...state,
|
|
6136
|
+
amountTooLow: { minAmountUsd: action.minAmountUsd },
|
|
6137
|
+
creatingTransfer: false,
|
|
6138
|
+
error: null
|
|
6139
|
+
};
|
|
5168
6140
|
case "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP":
|
|
5169
6141
|
return { ...state, oneTapLimitSavedDuringSetup: action.saved };
|
|
5170
6142
|
case "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET":
|
|
@@ -5198,7 +6170,8 @@ function applyAction(state, action) {
|
|
|
5198
6170
|
setupDepositConfirmed: false,
|
|
5199
6171
|
setupFlowScreen: null,
|
|
5200
6172
|
guestWalletPrepared: null,
|
|
5201
|
-
guestWalletDeeplinksPreparing: false
|
|
6173
|
+
guestWalletDeeplinksPreparing: false,
|
|
6174
|
+
amountTooLow: null
|
|
5202
6175
|
};
|
|
5203
6176
|
case "LOGOUT":
|
|
5204
6177
|
return {
|
|
@@ -5545,6 +6518,17 @@ var RABBY_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="70 88 386 334"
|
|
|
5545
6518
|
</linearGradient>
|
|
5546
6519
|
</defs>
|
|
5547
6520
|
</svg>`;
|
|
6521
|
+
var PHANTOM_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" viewBox="0 0 1200 1200" fill="none">
|
|
6522
|
+
<g clip-path="url(#clip0_2596_138580)">
|
|
6523
|
+
<rect y="-0.000976562" width="1200" height="1200" fill="#AB9FF2"/>
|
|
6524
|
+
<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"/>
|
|
6525
|
+
</g>
|
|
6526
|
+
<defs>
|
|
6527
|
+
<clipPath id="clip0_2596_138580">
|
|
6528
|
+
<rect y="-0.000976562" width="1200" height="1200" rx="600" fill="white"/>
|
|
6529
|
+
</clipPath>
|
|
6530
|
+
</defs>
|
|
6531
|
+
</svg>`;
|
|
5548
6532
|
var BLINK_SVG = `<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
5549
6533
|
<g clip-path="url(#clip0_125_480)">
|
|
5550
6534
|
<rect width="100" height="100" rx="18.5874" fill="black"/>
|
|
@@ -5571,6 +6555,37 @@ var TETHER_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800">
|
|
|
5571
6555
|
<circle fill="#009393" cx="400" cy="400" r="400"/>
|
|
5572
6556
|
<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"/>
|
|
5573
6557
|
</svg>`;
|
|
6558
|
+
var USDH_SVG = `<svg width="460" height="460" viewBox="0 0 460 460" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
6559
|
+
<rect width="460" height="460" rx="230" fill="url(#paint0_linear_143_25942)"/>
|
|
6560
|
+
<path d="M246 126L214 126L214 61L246 61L246 126Z" fill="url(#paint1_linear_143_25942)"/>
|
|
6561
|
+
<path d="M246 336L214 336L214 401L246 401L246 336Z" fill="url(#paint2_linear_143_25942)"/>
|
|
6562
|
+
<path d="M165 320.5L165 289.5L127 289.5L127 320.5L165 320.5Z" fill="url(#paint3_linear_143_25942)"/>
|
|
6563
|
+
<path d="M290.5 142V173H165V216H325V320.5H165V289.5H294V247H134V142H290.5Z" fill="#4AFFC6"/>
|
|
6564
|
+
<path d="M290 173L290 142L335 142L335 173L290 173Z" fill="url(#paint4_linear_143_25942)"/>
|
|
6565
|
+
<defs>
|
|
6566
|
+
<linearGradient id="paint0_linear_143_25942" x1="230" y1="0" x2="230" y2="460" gradientUnits="userSpaceOnUse">
|
|
6567
|
+
<stop stop-color="#063124"/>
|
|
6568
|
+
<stop offset="1" stop-color="#002B1E"/>
|
|
6569
|
+
</linearGradient>
|
|
6570
|
+
<linearGradient id="paint1_linear_143_25942" x1="230" y1="126" x2="230" y2="61" gradientUnits="userSpaceOnUse">
|
|
6571
|
+
<stop stop-color="#4AFFC6"/>
|
|
6572
|
+
<stop offset="0.971448" stop-color="#4AFFC6" stop-opacity="0"/>
|
|
6573
|
+
</linearGradient>
|
|
6574
|
+
<linearGradient id="paint2_linear_143_25942" x1="230" y1="336" x2="230" y2="401" gradientUnits="userSpaceOnUse">
|
|
6575
|
+
<stop stop-color="#4AFFC6"/>
|
|
6576
|
+
<stop offset="0.971448" stop-color="#4AFFC6" stop-opacity="0"/>
|
|
6577
|
+
</linearGradient>
|
|
6578
|
+
<linearGradient id="paint3_linear_143_25942" x1="165" y1="305" x2="127" y2="305" gradientUnits="userSpaceOnUse">
|
|
6579
|
+
<stop offset="0.112947" stop-color="#4AFFC6"/>
|
|
6580
|
+
<stop offset="1" stop-color="#4AFFC6" stop-opacity="0"/>
|
|
6581
|
+
</linearGradient>
|
|
6582
|
+
<linearGradient id="paint4_linear_143_25942" x1="290" y1="157.5" x2="335" y2="157.5" gradientUnits="userSpaceOnUse">
|
|
6583
|
+
<stop offset="0.112947" stop-color="#4AFFC6"/>
|
|
6584
|
+
<stop offset="1" stop-color="#4AFFC6" stop-opacity="0"/>
|
|
6585
|
+
</linearGradient>
|
|
6586
|
+
</defs>
|
|
6587
|
+
</svg>
|
|
6588
|
+
`;
|
|
5574
6589
|
var BLINK_LOGO = svgToDataUri(BLINK_SVG);
|
|
5575
6590
|
var BLINK_MASCOT = BLINK_LOGO;
|
|
5576
6591
|
var BLINK_SUCCESS_ILLUSTRATION = BLINK_SUCCESS_ILLUSTRATION_DATA_URI;
|
|
@@ -5587,19 +6602,24 @@ var POLYGON_CHAIN_LOGO = svgToDataUri(POLYGON_CHAIN_SVG);
|
|
|
5587
6602
|
var ETHEREUM_CHAIN_LOGO = "https://assets.relay.link/icons/1/light.png";
|
|
5588
6603
|
var MEGAETH_CHAIN_LOGO = svgToDataUri(MEGAETH_CHAIN_SVG);
|
|
5589
6604
|
var MONAD_CHAIN_LOGO = svgToDataUri(MONAD_CHAIN_SVG);
|
|
6605
|
+
var SOLANA_CHAIN_LOGO = "https://assets.relay.link/icons/792703809/light.png";
|
|
6606
|
+
var HYPEREVM_CHAIN_LOGO = "https://assets.relay.link/icons/999/light.png";
|
|
5590
6607
|
var USDM_LOGO = svgToDataUri(USDM_TOKEN_SVG);
|
|
5591
6608
|
var METAMASK_LOGO = svgToDataUri(METAMASK_SVG);
|
|
5592
6609
|
var TRUST_WALLET_LOGO = svgToDataUri(TRUST_WALLET_SVG);
|
|
5593
6610
|
var OKX_WALLET_LOGO = svgToDataUri(OKX_WALLET_SVG);
|
|
5594
6611
|
var RABBY_LOGO = svgToDataUri(RABBY_SVG);
|
|
6612
|
+
var PHANTOM_LOGO = svgToDataUri(PHANTOM_SVG);
|
|
5595
6613
|
var USDC_LOGO = svgToDataUri(USDC_SVG);
|
|
5596
6614
|
var TETHER_LOGO = svgToDataUri(TETHER_SVG);
|
|
6615
|
+
var USDH_LOGO = svgToDataUri(USDH_SVG);
|
|
5597
6616
|
var KNOWN_LOGOS = {
|
|
5598
6617
|
metamask: METAMASK_LOGO,
|
|
5599
6618
|
base: BASE_LOGO,
|
|
5600
6619
|
"base account": BASE_LOGO,
|
|
5601
6620
|
"base app": BASE_LOGO,
|
|
5602
6621
|
"okx wallet": OKX_WALLET_LOGO,
|
|
6622
|
+
phantom: PHANTOM_LOGO,
|
|
5603
6623
|
rabby: RABBY_LOGO,
|
|
5604
6624
|
"trust wallet": TRUST_WALLET_LOGO
|
|
5605
6625
|
};
|
|
@@ -5607,20 +6627,28 @@ var TOKEN_LOGOS = {
|
|
|
5607
6627
|
USDC: USDC_LOGO,
|
|
5608
6628
|
"USDC.e": USDC_LOGO,
|
|
5609
6629
|
USDT: TETHER_LOGO,
|
|
5610
|
-
USDM: USDM_LOGO
|
|
6630
|
+
USDM: USDM_LOGO,
|
|
6631
|
+
USDH: USDH_LOGO
|
|
5611
6632
|
};
|
|
5612
6633
|
var CHAIN_LOGOS = {
|
|
5613
6634
|
base: BASE_CHAIN_LOGO,
|
|
5614
6635
|
ethereum: ETHEREUM_CHAIN_LOGO,
|
|
6636
|
+
"ethereum mainnet": ETHEREUM_CHAIN_LOGO,
|
|
5615
6637
|
polygon: POLYGON_CHAIN_LOGO,
|
|
6638
|
+
"polygon mainnet": POLYGON_CHAIN_LOGO,
|
|
5616
6639
|
arbitrum: ARBITRUM_CHAIN_LOGO,
|
|
5617
6640
|
"arbitrum one": ARBITRUM_CHAIN_LOGO,
|
|
6641
|
+
"arbitrum mainnet": ARBITRUM_CHAIN_LOGO,
|
|
5618
6642
|
bnb: BNB_CHAIN_LOGO,
|
|
5619
6643
|
"bnb chain": BNB_CHAIN_LOGO,
|
|
5620
6644
|
"bnb smart chain": BNB_CHAIN_LOGO,
|
|
5621
6645
|
bsc: BNB_CHAIN_LOGO,
|
|
5622
6646
|
megaeth: MEGAETH_CHAIN_LOGO,
|
|
5623
|
-
monad: MONAD_CHAIN_LOGO
|
|
6647
|
+
monad: MONAD_CHAIN_LOGO,
|
|
6648
|
+
hyperevm: HYPEREVM_CHAIN_LOGO,
|
|
6649
|
+
"hyperevm mainnet": HYPEREVM_CHAIN_LOGO,
|
|
6650
|
+
solana: SOLANA_CHAIN_LOGO,
|
|
6651
|
+
"solana mainnet": SOLANA_CHAIN_LOGO
|
|
5624
6652
|
};
|
|
5625
6653
|
function ScreenLayout({ children, footer }) {
|
|
5626
6654
|
const { tokens, theme } = useBlinkConfig();
|
|
@@ -5868,6 +6896,8 @@ function PrimaryButton({
|
|
|
5868
6896
|
children,
|
|
5869
6897
|
onClick,
|
|
5870
6898
|
href,
|
|
6899
|
+
target,
|
|
6900
|
+
rel,
|
|
5871
6901
|
disabled,
|
|
5872
6902
|
loading,
|
|
5873
6903
|
loadingText = "Please wait...",
|
|
@@ -5920,6 +6950,8 @@ function PrimaryButton({
|
|
|
5920
6950
|
"a",
|
|
5921
6951
|
{
|
|
5922
6952
|
href,
|
|
6953
|
+
target,
|
|
6954
|
+
rel,
|
|
5923
6955
|
onClick,
|
|
5924
6956
|
style: {
|
|
5925
6957
|
...buttonStyle2(tokens, { disabled: false, loading: false }),
|
|
@@ -6676,6 +7708,8 @@ function SourceRow(props) {
|
|
|
6676
7708
|
"a",
|
|
6677
7709
|
{
|
|
6678
7710
|
href: props.href,
|
|
7711
|
+
target: props.target,
|
|
7712
|
+
rel: props.rel,
|
|
6679
7713
|
onClick: props.onClick,
|
|
6680
7714
|
onMouseEnter: () => setHovered(true),
|
|
6681
7715
|
onMouseLeave: () => setHovered(false),
|
|
@@ -7199,6 +8233,22 @@ var closeButtonStyle2 = (tokens) => ({
|
|
|
7199
8233
|
cursor: "pointer",
|
|
7200
8234
|
padding: 0
|
|
7201
8235
|
});
|
|
8236
|
+
function AmountTooLowScreen({
|
|
8237
|
+
minAmountUsd,
|
|
8238
|
+
onRetry,
|
|
8239
|
+
onClose
|
|
8240
|
+
}) {
|
|
8241
|
+
return /* @__PURE__ */ jsx(
|
|
8242
|
+
BlinkErrorScreen,
|
|
8243
|
+
{
|
|
8244
|
+
title: "Amount too low",
|
|
8245
|
+
message: `The minimum payment amount is $${minAmountUsd.toFixed(2)}.`,
|
|
8246
|
+
retryLabel: onRetry ? "Try Again" : void 0,
|
|
8247
|
+
onRetry,
|
|
8248
|
+
onClose
|
|
8249
|
+
}
|
|
8250
|
+
);
|
|
8251
|
+
}
|
|
7202
8252
|
var RESEND_COOLDOWN_SECONDS = 30;
|
|
7203
8253
|
function OtpVerifyScreen({
|
|
7204
8254
|
maskedIdentifier,
|
|
@@ -7474,19 +8524,15 @@ var contentStyle5 = {
|
|
|
7474
8524
|
gap: 16
|
|
7475
8525
|
};
|
|
7476
8526
|
var illustrationFrameStyle = {
|
|
7477
|
-
flex:
|
|
7478
|
-
minHeight: 0,
|
|
8527
|
+
flex: "0 0 auto",
|
|
7479
8528
|
width: "100%",
|
|
7480
|
-
maxWidth: 329,
|
|
7481
8529
|
display: "flex",
|
|
7482
8530
|
alignItems: "center",
|
|
7483
8531
|
justifyContent: "center"
|
|
7484
8532
|
};
|
|
7485
8533
|
var illustrationImgStyle = {
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
width: "auto",
|
|
7489
|
-
height: "auto",
|
|
8534
|
+
width: 200,
|
|
8535
|
+
height: 200,
|
|
7490
8536
|
objectFit: "contain",
|
|
7491
8537
|
display: "block",
|
|
7492
8538
|
pointerEvents: "none",
|
|
@@ -7494,7 +8540,7 @@ var illustrationImgStyle = {
|
|
|
7494
8540
|
};
|
|
7495
8541
|
var footerButtonStyle = {
|
|
7496
8542
|
width: "100%",
|
|
7497
|
-
paddingTop:
|
|
8543
|
+
paddingTop: 48,
|
|
7498
8544
|
paddingBottom: 36
|
|
7499
8545
|
};
|
|
7500
8546
|
var errorBannerStyle3 = (tokens) => ({
|
|
@@ -7568,19 +8614,17 @@ var contentStyle6 = {
|
|
|
7568
8614
|
gap: 16
|
|
7569
8615
|
};
|
|
7570
8616
|
var illustrationFrameStyle2 = {
|
|
7571
|
-
flex: 1,
|
|
7572
|
-
minHeight: 0,
|
|
8617
|
+
flex: "0 1 auto",
|
|
7573
8618
|
width: "100%",
|
|
7574
|
-
maxWidth:
|
|
8619
|
+
maxWidth: 280,
|
|
8620
|
+
height: "clamp(140px, 34vh, 248px)",
|
|
7575
8621
|
display: "flex",
|
|
7576
8622
|
alignItems: "center",
|
|
7577
8623
|
justifyContent: "center"
|
|
7578
8624
|
};
|
|
7579
8625
|
var illustrationImgStyle2 = {
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
width: "auto",
|
|
7583
|
-
height: "auto",
|
|
8626
|
+
width: "100%",
|
|
8627
|
+
height: "100%",
|
|
7584
8628
|
objectFit: "contain",
|
|
7585
8629
|
display: "block",
|
|
7586
8630
|
pointerEvents: "none",
|
|
@@ -7724,6 +8768,36 @@ function triggerDeeplink(uri) {
|
|
|
7724
8768
|
}
|
|
7725
8769
|
}
|
|
7726
8770
|
|
|
8771
|
+
// src/walletDeeplinks.ts
|
|
8772
|
+
function resolveWalletDeeplink(providerId, walletDeeplinks, fallbackUri) {
|
|
8773
|
+
const matchedUri = walletDeeplinks?.find((item) => item.providerId === providerId)?.uri;
|
|
8774
|
+
return matchedUri ?? fallbackUri;
|
|
8775
|
+
}
|
|
8776
|
+
function classifyWalletDeeplinkNavigation(uri) {
|
|
8777
|
+
if (isCustomSchemeUri(uri)) {
|
|
8778
|
+
return { navigationClass: "javascript" };
|
|
8779
|
+
}
|
|
8780
|
+
try {
|
|
8781
|
+
const parsed = new URL(uri);
|
|
8782
|
+
if (isBrowseUniversalLink(parsed)) {
|
|
8783
|
+
return {
|
|
8784
|
+
navigationClass: "iframe-escaping-native-anchor",
|
|
8785
|
+
anchorTarget: "_blank",
|
|
8786
|
+
anchorRel: "noopener noreferrer"
|
|
8787
|
+
};
|
|
8788
|
+
}
|
|
8789
|
+
} catch {
|
|
8790
|
+
return { navigationClass: "javascript" };
|
|
8791
|
+
}
|
|
8792
|
+
return { navigationClass: "javascript" };
|
|
8793
|
+
}
|
|
8794
|
+
function shouldOpenWithJavaScript(navigation) {
|
|
8795
|
+
return navigation.navigationClass === "javascript";
|
|
8796
|
+
}
|
|
8797
|
+
function isBrowseUniversalLink(parsed) {
|
|
8798
|
+
return parsed.protocol === "https:" && parsed.pathname.startsWith("/ul/browse/") && parsed.searchParams.has("ref");
|
|
8799
|
+
}
|
|
8800
|
+
|
|
7727
8801
|
// src/sentry.ts
|
|
7728
8802
|
var _mod;
|
|
7729
8803
|
function captureException(error) {
|
|
@@ -7798,14 +8872,20 @@ function WalletPickerScreen({
|
|
|
7798
8872
|
const rowLoader = isRowPreparing ? /* @__PURE__ */ jsx(Spinner, { size: 20 }) : void 0;
|
|
7799
8873
|
if (usesDirectLinkCards) {
|
|
7800
8874
|
if (directPreparedSession?.uri) {
|
|
8875
|
+
const navigation = classifyWalletDeeplinkNavigation(directPreparedSession.uri);
|
|
8876
|
+
const openWithJavaScript = shouldOpenWithJavaScript(navigation);
|
|
7801
8877
|
return /* @__PURE__ */ jsx(
|
|
7802
8878
|
SourceRow,
|
|
7803
8879
|
{
|
|
7804
8880
|
logo: logoSrc,
|
|
7805
8881
|
name: provider.name,
|
|
7806
8882
|
href: directPreparedSession.uri,
|
|
8883
|
+
target: navigation.anchorTarget,
|
|
8884
|
+
rel: navigation.anchorRel,
|
|
7807
8885
|
onClick: (e) => {
|
|
7808
|
-
|
|
8886
|
+
if (openWithJavaScript) {
|
|
8887
|
+
e.preventDefault();
|
|
8888
|
+
}
|
|
7809
8889
|
setSelectedProviderId(provider.id);
|
|
7810
8890
|
if (directPreparedSession.sessionId) {
|
|
7811
8891
|
void setAuthorizationSessionProvider(
|
|
@@ -7816,7 +8896,9 @@ function WalletPickerScreen({
|
|
|
7816
8896
|
captureException(err);
|
|
7817
8897
|
});
|
|
7818
8898
|
}
|
|
7819
|
-
|
|
8899
|
+
if (openWithJavaScript) {
|
|
8900
|
+
openDeeplink(directPreparedSession.uri);
|
|
8901
|
+
}
|
|
7820
8902
|
void onSelectProvider(provider.id, directPreparedSession);
|
|
7821
8903
|
}
|
|
7822
8904
|
},
|
|
@@ -8674,6 +9756,7 @@ function SelectDepositSourceScreen({
|
|
|
8674
9756
|
const rowAccountId = (opt) => opt.accountId ?? fallbackAccountId;
|
|
8675
9757
|
const isSelected = (opt) => !!selectedTokenSymbol && !!selectedChainName && !!selectedWalletId && opt.symbol === selectedTokenSymbol && opt.chainName === selectedChainName && opt.walletId === selectedWalletId;
|
|
8676
9758
|
const tokenOptionKey = (opt) => `${opt.accountId ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`;
|
|
9759
|
+
const hasPendingMobileSelection = pendingMobileSelection != null;
|
|
8677
9760
|
const handleAuthorizedPick = (opt) => {
|
|
8678
9761
|
onPickToken(opt.symbol, opt.chainName, opt.walletId);
|
|
8679
9762
|
onDone();
|
|
@@ -8703,14 +9786,20 @@ function SelectDepositSourceScreen({
|
|
|
8703
9786
|
handleAuthorizedPick(opt);
|
|
8704
9787
|
};
|
|
8705
9788
|
const footerProviderName = preparedAuthorization?.providerName ?? pendingMobileSelection?.providerName ?? "your wallet";
|
|
9789
|
+
const preparedAuthorizationNavigation = preparedAuthorization ? classifyWalletDeeplinkNavigation(preparedAuthorization.deeplinkUri) : null;
|
|
9790
|
+
const preparedAuthorizationOpensWithJavaScript = preparedAuthorizationNavigation ? shouldOpenWithJavaScript(preparedAuthorizationNavigation) : false;
|
|
8706
9791
|
const footer = pendingMobileSelection ? preparedAuthorization ? /* @__PURE__ */ jsx(
|
|
8707
9792
|
PrimaryButton,
|
|
8708
9793
|
{
|
|
8709
9794
|
href: preparedAuthorization.deeplinkUri,
|
|
9795
|
+
target: preparedAuthorizationNavigation?.anchorTarget,
|
|
9796
|
+
rel: preparedAuthorizationNavigation?.anchorRel,
|
|
8710
9797
|
onClick: (event) => {
|
|
8711
|
-
event.preventDefault();
|
|
8712
9798
|
onCommitTokenAuthorization?.(preparedAuthorization);
|
|
8713
|
-
|
|
9799
|
+
if (preparedAuthorizationOpensWithJavaScript) {
|
|
9800
|
+
event.preventDefault();
|
|
9801
|
+
openDeeplink(preparedAuthorization.deeplinkUri);
|
|
9802
|
+
}
|
|
8714
9803
|
},
|
|
8715
9804
|
children: `Continue in ${footerProviderName}`
|
|
8716
9805
|
}
|
|
@@ -8760,7 +9849,7 @@ function SelectDepositSourceScreen({
|
|
|
8760
9849
|
symbol: opt.symbol,
|
|
8761
9850
|
chainName: opt.chainName,
|
|
8762
9851
|
balance: opt.balance,
|
|
8763
|
-
selected: isSelected(opt),
|
|
9852
|
+
selected: !hasPendingMobileSelection && isSelected(opt),
|
|
8764
9853
|
onClick: () => handleAuthorizedPick(opt)
|
|
8765
9854
|
},
|
|
8766
9855
|
`auth-${opt.accountId ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`
|
|
@@ -8774,7 +9863,7 @@ function SelectDepositSourceScreen({
|
|
|
8774
9863
|
chainName: opt.chainName,
|
|
8775
9864
|
balance: opt.balance,
|
|
8776
9865
|
requiresAuth: true,
|
|
8777
|
-
selected:
|
|
9866
|
+
selected: hasPendingMobileSelection ? pendingMobileSelection?.key === tokenOptionKey(opt) : isSelected(opt),
|
|
8778
9867
|
onClick: () => handleRequiresAuthPick(account, opt)
|
|
8779
9868
|
},
|
|
8780
9869
|
`req-${opt.accountId ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`
|
|
@@ -9959,11 +11048,13 @@ function OpenWalletScreen({
|
|
|
9959
11048
|
!loading && onRetryStatus != null && error == null,
|
|
9960
11049
|
deeplinkUri
|
|
9961
11050
|
);
|
|
11051
|
+
const deeplinkNavigation = classifyWalletDeeplinkNavigation(deeplinkUri);
|
|
11052
|
+
const openWithJavaScript = shouldOpenWithJavaScript(deeplinkNavigation);
|
|
9962
11053
|
useEffect(() => {
|
|
9963
|
-
if (!useDeeplink || loading || !deeplinkUri || autoOpenedRef.current === deeplinkUri) return;
|
|
11054
|
+
if (!useDeeplink || loading || !deeplinkUri || !openWithJavaScript || autoOpenedRef.current === deeplinkUri) return;
|
|
9964
11055
|
autoOpenedRef.current = deeplinkUri;
|
|
9965
11056
|
triggerDeeplink(deeplinkUri);
|
|
9966
|
-
}, [useDeeplink, loading, deeplinkUri]);
|
|
11057
|
+
}, [useDeeplink, loading, deeplinkUri, openWithJavaScript]);
|
|
9967
11058
|
const handleOpen = useCallback(() => {
|
|
9968
11059
|
openDeeplink(deeplinkUri);
|
|
9969
11060
|
}, [deeplinkUri]);
|
|
@@ -10003,7 +11094,10 @@ function OpenWalletScreen({
|
|
|
10003
11094
|
/* @__PURE__ */ jsxs(
|
|
10004
11095
|
PrimaryButton,
|
|
10005
11096
|
{
|
|
10006
|
-
|
|
11097
|
+
href: !openWithJavaScript ? deeplinkUri : void 0,
|
|
11098
|
+
target: deeplinkNavigation.anchorTarget,
|
|
11099
|
+
rel: deeplinkNavigation.anchorRel,
|
|
11100
|
+
onClick: openWithJavaScript ? handleOpen : void 0,
|
|
10007
11101
|
loading,
|
|
10008
11102
|
loadingText: "Preparing authorization\u2026",
|
|
10009
11103
|
children: [
|
|
@@ -10095,6 +11189,7 @@ var inlineWaitStyle = (color) => ({
|
|
|
10095
11189
|
});
|
|
10096
11190
|
function ConfirmSignScreen({
|
|
10097
11191
|
walletName,
|
|
11192
|
+
chainFamily,
|
|
10098
11193
|
signing,
|
|
10099
11194
|
error,
|
|
10100
11195
|
onSign,
|
|
@@ -10103,26 +11198,28 @@ function ConfirmSignScreen({
|
|
|
10103
11198
|
const { tokens } = useBlinkConfig();
|
|
10104
11199
|
const displayName = walletName ?? "your wallet";
|
|
10105
11200
|
const logoSrc = walletName ? KNOWN_LOGOS[walletName.toLowerCase()] : void 0;
|
|
11201
|
+
const isSvmTransfer = chainFamily === "svm";
|
|
11202
|
+
const heading = isSvmTransfer ? "Ready for passkey approval" : "Wallet authorized";
|
|
11203
|
+
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.`;
|
|
11204
|
+
const badge = isSvmTransfer ? "Wallet setup complete" : "Authorization complete";
|
|
11205
|
+
const hint = isSvmTransfer ? "No wallet pop-up is needed for this transfer" : "You may be prompted for biometric verification";
|
|
10106
11206
|
return /* @__PURE__ */ jsxs(
|
|
10107
11207
|
ScreenLayout,
|
|
10108
11208
|
{
|
|
10109
11209
|
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10110
11210
|
/* @__PURE__ */ jsx(PrimaryButton, { onClick: onSign, disabled: signing, children: "Confirm payment" }),
|
|
10111
11211
|
error && /* @__PURE__ */ jsx("p", { style: errorStyle2(tokens.textMuted), children: error }),
|
|
10112
|
-
!error && /* @__PURE__ */ jsx("p", { style: hintStyle(tokens.textMuted), children:
|
|
11212
|
+
!error && /* @__PURE__ */ jsx("p", { style: hintStyle(tokens.textMuted), children: hint })
|
|
10113
11213
|
] }),
|
|
10114
11214
|
children: [
|
|
10115
11215
|
/* @__PURE__ */ jsx(ScreenHeader, { onLogout }),
|
|
10116
11216
|
/* @__PURE__ */ jsxs("div", { style: contentStyle15, children: [
|
|
10117
11217
|
logoSrc ? /* @__PURE__ */ jsx("img", { src: logoSrc, alt: displayName, style: logoStyle3 }) : /* @__PURE__ */ jsx(Spinner, { size: 48 }),
|
|
10118
|
-
/* @__PURE__ */ jsx("h2", { style: headingStyle14(tokens.text), children:
|
|
10119
|
-
/* @__PURE__ */
|
|
10120
|
-
displayName,
|
|
10121
|
-
" approved the connection. Tap below to confirm your payment."
|
|
10122
|
-
] }),
|
|
11218
|
+
/* @__PURE__ */ jsx("h2", { style: headingStyle14(tokens.text), children: heading }),
|
|
11219
|
+
/* @__PURE__ */ jsx("p", { style: subtitleStyle11(tokens.textSecondary), children: subtitle }),
|
|
10123
11220
|
/* @__PURE__ */ jsxs("div", { style: successBadgeStyle(tokens), children: [
|
|
10124
11221
|
/* @__PURE__ */ jsx("span", { style: checkmarkStyle, children: "\u2713" }),
|
|
10125
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
11222
|
+
/* @__PURE__ */ jsx("span", { children: badge })
|
|
10126
11223
|
] })
|
|
10127
11224
|
] })
|
|
10128
11225
|
]
|
|
@@ -10865,6 +11962,7 @@ function buildConfirmSignScreenProps({
|
|
|
10865
11962
|
}) {
|
|
10866
11963
|
return {
|
|
10867
11964
|
walletName: flow.state.providers.find((p) => p.id === flow.state.selectedProviderId)?.name ?? null,
|
|
11965
|
+
chainFamily: remote.pollingTransfer?.signPayload?.chainFamily ?? null,
|
|
10868
11966
|
signing: remote.transferSigningSigning,
|
|
10869
11967
|
error: flow.state.error || remote.transferSigningError,
|
|
10870
11968
|
onSign: handlers.onConfirmSign,
|
|
@@ -11085,6 +12183,19 @@ function buildSuccessScreenProps({
|
|
|
11085
12183
|
onLogout: authenticated ? handlers.onLogout : void 0
|
|
11086
12184
|
};
|
|
11087
12185
|
}
|
|
12186
|
+
function buildAmountTooLowScreenProps({
|
|
12187
|
+
flow,
|
|
12188
|
+
handlers
|
|
12189
|
+
}) {
|
|
12190
|
+
const { state, onDismiss } = flow;
|
|
12191
|
+
const minAmountUsd = state.phase.step === "amount-too-low" ? state.phase.minAmountUsd : state.amountTooLow?.minAmountUsd ?? flow.minTransferAmountUsd;
|
|
12192
|
+
const canRetry = flow.depositAmount == null;
|
|
12193
|
+
return {
|
|
12194
|
+
minAmountUsd,
|
|
12195
|
+
onRetry: canRetry ? handlers.onNewPayment : void 0,
|
|
12196
|
+
onClose: onDismiss
|
|
12197
|
+
};
|
|
12198
|
+
}
|
|
11088
12199
|
function StepRenderer(props) {
|
|
11089
12200
|
const screen = screenForPhase(props.flow.state.phase);
|
|
11090
12201
|
return /* @__PURE__ */ jsx(StepRendererContent, { ...props, screen });
|
|
@@ -11152,6 +12263,8 @@ function StepRendererContent({
|
|
|
11152
12263
|
return /* @__PURE__ */ jsx(SelectSourceScreen, { ...buildSelectSourceScreenProps(input) });
|
|
11153
12264
|
case "success":
|
|
11154
12265
|
return /* @__PURE__ */ jsx(SuccessScreen, { ...buildSuccessScreenProps(input) });
|
|
12266
|
+
case "amount-too-low":
|
|
12267
|
+
return /* @__PURE__ */ jsx(AmountTooLowScreen, { ...buildAmountTooLowScreenProps(input) });
|
|
11155
12268
|
default: {
|
|
11156
12269
|
const _exhaustive = screen;
|
|
11157
12270
|
throw new Error(`Unhandled screen: ${_exhaustive}`);
|
|
@@ -11581,9 +12694,8 @@ function useTransferHandlers(deps) {
|
|
|
11581
12694
|
destination
|
|
11582
12695
|
]);
|
|
11583
12696
|
const handlePay = useCallback(async (payAmount, sourceOverrides) => {
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
dispatch({ type: "SET_ERROR", error: `Minimum amount is $${minUsd.toFixed(2)}.` });
|
|
12697
|
+
if (isNaN(payAmount) || payAmount < minTransferAmountUsd) {
|
|
12698
|
+
dispatch({ type: "AMOUNT_TOO_LOW", minAmountUsd: minTransferAmountUsd });
|
|
11587
12699
|
return;
|
|
11588
12700
|
}
|
|
11589
12701
|
if (!sourceOverrides?.sourceId && !sourceId) {
|
|
@@ -11634,7 +12746,10 @@ function useTransferHandlers(deps) {
|
|
|
11634
12746
|
);
|
|
11635
12747
|
clearPendingTransferState();
|
|
11636
12748
|
dispatch({ type: "TRANSFER_CREATED", transfer: stagedTransfer });
|
|
11637
|
-
const signedTransfer2 = await transferSigning.signTransfer(
|
|
12749
|
+
const signedTransfer2 = await transferSigning.signTransfer(
|
|
12750
|
+
stagedTransfer.id,
|
|
12751
|
+
{ knownSignPayload: stagedTransfer.signPayload }
|
|
12752
|
+
);
|
|
11638
12753
|
dispatch({ type: "TRANSFER_SIGNED", transfer: signedTransfer2 });
|
|
11639
12754
|
polling.startPolling(stagedTransfer.id);
|
|
11640
12755
|
return;
|
|
@@ -11665,7 +12780,10 @@ function useTransferHandlers(deps) {
|
|
|
11665
12780
|
polling.startPolling(t.id);
|
|
11666
12781
|
return;
|
|
11667
12782
|
}
|
|
11668
|
-
const signedTransfer = await transferSigning.signTransfer(
|
|
12783
|
+
const signedTransfer = await transferSigning.signTransfer(
|
|
12784
|
+
t.id,
|
|
12785
|
+
{ knownSignPayload: t.signPayload }
|
|
12786
|
+
);
|
|
11669
12787
|
dispatch({ type: "TRANSFER_SIGNED", transfer: signedTransfer });
|
|
11670
12788
|
polling.startPolling(t.id);
|
|
11671
12789
|
} catch (err) {
|
|
@@ -11872,12 +12990,6 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
|
|
|
11872
12990
|
};
|
|
11873
12991
|
}
|
|
11874
12992
|
|
|
11875
|
-
// src/walletDeeplinks.ts
|
|
11876
|
-
function resolveWalletDeeplink(providerId, walletDeeplinks, fallbackUri) {
|
|
11877
|
-
const matchedUri = walletDeeplinks?.find((item) => item.providerId === providerId)?.uri;
|
|
11878
|
-
return matchedUri ?? fallbackUri;
|
|
11879
|
-
}
|
|
11880
|
-
|
|
11881
12993
|
// src/hooks/providerSelectionGuards.ts
|
|
11882
12994
|
function resolveSetupFlowDepositAmount({
|
|
11883
12995
|
hasActiveWallet: hasActiveWallet2,
|
|
@@ -11944,9 +13056,10 @@ function buildDesktopDirectDuringSetupRunOptions() {
|
|
|
11944
13056
|
alwaysPauseForOneTap: true
|
|
11945
13057
|
};
|
|
11946
13058
|
}
|
|
11947
|
-
function buildDesktopTokenAuthorizationRunOptions(chainName, tokenSymbol) {
|
|
13059
|
+
function buildDesktopTokenAuthorizationRunOptions(chainName, tokenSymbol, _chainFamily = "evm") {
|
|
13060
|
+
const options = buildDesktopDirectDuringSetupRunOptions();
|
|
11948
13061
|
return {
|
|
11949
|
-
...
|
|
13062
|
+
...options,
|
|
11950
13063
|
// Token authorization should batch like the first-time desktop setup flow:
|
|
11951
13064
|
// auto-resolve SELECT_SOURCE now, then inject the transfer session during one-tap setup.
|
|
11952
13065
|
autoResolveSource: { chainName, tokenSymbol }
|
|
@@ -12615,10 +13728,10 @@ function useProviderHandlers(deps) {
|
|
|
12615
13728
|
dispatch({ type: "SET_ERROR", error: null });
|
|
12616
13729
|
dispatch({ type: "SET_INCREASING_LIMIT", value: true });
|
|
12617
13730
|
dispatch({ type: "SET_SETUP_DEPOSIT_AMOUNT", amount: depositAmount ?? 5 });
|
|
12618
|
-
let
|
|
13731
|
+
let desktopChain = null;
|
|
12619
13732
|
if (!isMobile) {
|
|
12620
|
-
|
|
12621
|
-
if (!
|
|
13733
|
+
desktopChain = chains.find((chain) => chain.commonId === chainId) ?? null;
|
|
13734
|
+
if (!desktopChain) {
|
|
12622
13735
|
dispatch({ type: "SET_INCREASING_LIMIT", value: false });
|
|
12623
13736
|
dispatch({ type: "SET_SETUP_DEPOSIT_AMOUNT", amount: null });
|
|
12624
13737
|
dispatch({ type: "SET_ERROR", error: `No chain found for chainId ${chainId}` });
|
|
@@ -12627,7 +13740,7 @@ function useProviderHandlers(deps) {
|
|
|
12627
13740
|
dispatch({
|
|
12628
13741
|
type: "SET_SETUP_DEPOSIT_TOKEN",
|
|
12629
13742
|
symbol: tokenSymbol,
|
|
12630
|
-
chainName:
|
|
13743
|
+
chainName: desktopChain.name,
|
|
12631
13744
|
walletId: _walletId,
|
|
12632
13745
|
tokenAddress,
|
|
12633
13746
|
chainId
|
|
@@ -12661,7 +13774,11 @@ function useProviderHandlers(deps) {
|
|
|
12661
13774
|
dispatch({ type: "SET_SETUP_FLOW_SCREEN", screen: "one-tap-setup" });
|
|
12662
13775
|
const result = await orchestrator.run(
|
|
12663
13776
|
session.id,
|
|
12664
|
-
buildDesktopTokenAuthorizationRunOptions(
|
|
13777
|
+
buildDesktopTokenAuthorizationRunOptions(
|
|
13778
|
+
desktopChain.name,
|
|
13779
|
+
tokenSymbol,
|
|
13780
|
+
desktopChain.chainFamily
|
|
13781
|
+
)
|
|
12665
13782
|
);
|
|
12666
13783
|
if (result.status === "cancelled") {
|
|
12667
13784
|
dispatch({ type: "SET_SETUP_DEPOSIT_AMOUNT", amount: null });
|
|
@@ -14067,6 +15184,11 @@ function BlinkPaymentInner({
|
|
|
14067
15184
|
dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
|
|
14068
15185
|
}
|
|
14069
15186
|
}, [depositAmount, dispatch]);
|
|
15187
|
+
useEffect(() => {
|
|
15188
|
+
if (depositAmount != null && depositAmount < minTransferAmountUsd) {
|
|
15189
|
+
dispatch({ type: "AMOUNT_TOO_LOW", minAmountUsd: minTransferAmountUsd });
|
|
15190
|
+
}
|
|
15191
|
+
}, [depositAmount, minTransferAmountUsd, dispatch]);
|
|
14070
15192
|
useEffect(() => {
|
|
14071
15193
|
if (!ready || effectiveAuthenticated) return;
|
|
14072
15194
|
clearLocalSessionArtifacts();
|