@swype-org/react-sdk 0.2.375 → 0.2.399
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1868 -766
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1176 -1009
- package/dist/index.d.ts +1176 -1009
- package/dist/index.js +1861 -767
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { getAccount, reconnect, disconnect, getWalletClient, getConnectors } fro
|
|
|
11
11
|
import { encodeFunctionData, recoverTypedDataAddress, decodeAbiParameters } from 'viem';
|
|
12
12
|
import { parseErc6492Signature } from 'viem/utils';
|
|
13
13
|
import * as QRCode from 'qrcode';
|
|
14
|
+
import posthog from 'posthog-js';
|
|
14
15
|
import * as Select from '@radix-ui/react-select';
|
|
15
16
|
|
|
16
17
|
var __defProp = Object.defineProperty;
|
|
@@ -54,7 +55,11 @@ var darkTheme = {
|
|
|
54
55
|
shadowLg: "0 18px 44px rgba(0,0,0,0.42)",
|
|
55
56
|
radius: "14px",
|
|
56
57
|
radiusLg: "24px",
|
|
57
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
58
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
59
|
+
fontWeightRegular: 400,
|
|
60
|
+
fontWeightMedium: 500,
|
|
61
|
+
fontWeightSemibold: 600,
|
|
62
|
+
fontWeightBold: 700
|
|
58
63
|
};
|
|
59
64
|
var lightTheme = {
|
|
60
65
|
bg: "#ebf9fb",
|
|
@@ -86,7 +91,11 @@ var lightTheme = {
|
|
|
86
91
|
shadowLg: "0 20px 48px rgba(19, 61, 75, 0.14)",
|
|
87
92
|
radius: "14px",
|
|
88
93
|
radiusLg: "24px",
|
|
89
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
94
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
95
|
+
fontWeightRegular: 400,
|
|
96
|
+
fontWeightMedium: 500,
|
|
97
|
+
fontWeightSemibold: 600,
|
|
98
|
+
fontWeightBold: 700
|
|
90
99
|
};
|
|
91
100
|
var lightTransparentTheme = {
|
|
92
101
|
...lightTheme
|
|
@@ -129,7 +138,13 @@ var lightThemeNew = {
|
|
|
129
138
|
shadowLg: "0 20px 48px rgba(0, 0, 0, 0.14)",
|
|
130
139
|
radius: "14px",
|
|
131
140
|
radiusLg: "24px",
|
|
132
|
-
|
|
141
|
+
// Figma redesign uses Inter; fall back to the system stack when the host
|
|
142
|
+
// page hasn't loaded the Inter webfont.
|
|
143
|
+
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
144
|
+
fontWeightRegular: 400,
|
|
145
|
+
fontWeightMedium: 500,
|
|
146
|
+
fontWeightSemibold: 600,
|
|
147
|
+
fontWeightBold: 700
|
|
133
148
|
};
|
|
134
149
|
var darkThemeNew = { ...lightThemeNew };
|
|
135
150
|
var lightTransparentThemeNew = { ...lightThemeNew };
|
|
@@ -299,7 +314,7 @@ function resolveDepositPriorityContext(accounts, chains, destination) {
|
|
|
299
314
|
for (const account of accounts) {
|
|
300
315
|
for (const wallet of account.wallets) {
|
|
301
316
|
if (wallet.chain.name !== destinationChainName) continue;
|
|
302
|
-
const source = wallet.sources.find(
|
|
317
|
+
const source = (wallet.sources ?? []).find(
|
|
303
318
|
(candidate) => candidate.address.toLowerCase() === destinationTokenAddress
|
|
304
319
|
);
|
|
305
320
|
if (source) {
|
|
@@ -333,6 +348,11 @@ function getWalletAddress(wallet) {
|
|
|
333
348
|
const address = wallet.name.trim();
|
|
334
349
|
return address.length > 0 ? address : null;
|
|
335
350
|
}
|
|
351
|
+
function truncateMiddle(address, head = 8, tail = 6) {
|
|
352
|
+
const trimmed = address.trim();
|
|
353
|
+
if (trimmed.length <= head + tail + 4) return trimmed;
|
|
354
|
+
return `${trimmed.slice(0, head)}...${trimmed.slice(-tail)}`;
|
|
355
|
+
}
|
|
336
356
|
function getAddressableWallets(account) {
|
|
337
357
|
return account.wallets.filter((wallet) => getWalletAddress(wallet) != null);
|
|
338
358
|
}
|
|
@@ -340,7 +360,7 @@ function getPreferredDepositWallet(account, transferAmount, priorityContext) {
|
|
|
340
360
|
const wallets = getAddressableWallets(account);
|
|
341
361
|
if (wallets.length === 0) return null;
|
|
342
362
|
const ranked = wallets.map((wallet, walletIndex) => {
|
|
343
|
-
const bestSource = wallet.sources.map((source, sourceIndex) => ({
|
|
363
|
+
const bestSource = (wallet.sources ?? []).map((source, sourceIndex) => ({
|
|
344
364
|
source,
|
|
345
365
|
sourceIndex
|
|
346
366
|
})).sort((a, b) => {
|
|
@@ -365,7 +385,7 @@ function getPreferredDepositWallet(account, transferAmount, priorityContext) {
|
|
|
365
385
|
if (a.wallet.status !== b.wallet.status) {
|
|
366
386
|
return a.wallet.status === "ACTIVE" ? -1 : 1;
|
|
367
387
|
}
|
|
368
|
-
const balanceDiff = b.wallet.balance
|
|
388
|
+
const balanceDiff = (b.wallet.balance?.available.amount ?? 0) - (a.wallet.balance?.available.amount ?? 0);
|
|
369
389
|
if (balanceDiff !== 0) return balanceDiff;
|
|
370
390
|
return a.walletIndex - b.walletIndex;
|
|
371
391
|
});
|
|
@@ -381,7 +401,7 @@ function resolveDepositSelectionAfterRefresh(accounts, transferAmount, prev, pri
|
|
|
381
401
|
const wallet = acct?.wallets.find((w) => w.id === selectedWalletId);
|
|
382
402
|
if (wallet) {
|
|
383
403
|
if (selectedTokenSymbol) {
|
|
384
|
-
const hasToken = wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
|
|
404
|
+
const hasToken = wallet.sources == null || wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
|
|
385
405
|
if (hasToken) {
|
|
386
406
|
return {
|
|
387
407
|
defaults: { accountId: selectedAccountId, walletId: selectedWalletId },
|
|
@@ -419,7 +439,9 @@ function resolveDepositSelection(accounts, transferAmount, selectedAccountId, pr
|
|
|
419
439
|
const eligibleAccounts = getDepositEligibleAccounts(accounts);
|
|
420
440
|
if (eligibleAccounts.length === 0) return null;
|
|
421
441
|
const accountHasSufficientBalanceWallet = (account) => getAddressableWallets(account).some(
|
|
422
|
-
(wallet) => wallet.status === "ACTIVE" && wallet.sources.some(
|
|
442
|
+
(wallet) => wallet.status === "ACTIVE" && (wallet.sources ?? []).some(
|
|
443
|
+
(source) => source.balance.available.amount >= transferAmount
|
|
444
|
+
)
|
|
423
445
|
);
|
|
424
446
|
if (transferAmount <= 0 && selectedAccountId) {
|
|
425
447
|
const selectedAccount = eligibleAccounts.find((account) => account.id === selectedAccountId);
|
|
@@ -441,7 +463,7 @@ function resolveDepositSelection(accounts, transferAmount, selectedAccountId, pr
|
|
|
441
463
|
transferAmount,
|
|
442
464
|
priorityContext
|
|
443
465
|
);
|
|
444
|
-
const preferredSource = preferredWallet ? [...preferredWallet.sources].sort((a, b) => compareDepositSourcePriority(
|
|
466
|
+
const preferredSource = preferredWallet ? [...preferredWallet.sources ?? []].sort((a, b) => compareDepositSourcePriority(
|
|
445
467
|
sourcePriorityInput(preferredWallet.chain.name, a, transferAmount, priorityContext),
|
|
446
468
|
sourcePriorityInput(preferredWallet.chain.name, b, transferAmount, priorityContext)
|
|
447
469
|
))[0] : void 0;
|
|
@@ -507,7 +529,7 @@ function buildNativeUnsupportedEntries(options) {
|
|
|
507
529
|
logoURI: option.logoURI ?? null
|
|
508
530
|
})).filter((entry) => entry.amount > 0);
|
|
509
531
|
}
|
|
510
|
-
function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD) {
|
|
532
|
+
function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD, includeUnfundedTokens = false) {
|
|
511
533
|
const chainChoices = [];
|
|
512
534
|
const chainIndexByName = /* @__PURE__ */ new Map();
|
|
513
535
|
for (const option of options) {
|
|
@@ -548,7 +570,7 @@ function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SE
|
|
|
548
570
|
}
|
|
549
571
|
}
|
|
550
572
|
return chainChoices.map((chain) => {
|
|
551
|
-
const visibleTokens = chain.tokens.filter((t) => isSelectableDepositSourceAmountUsd(t.balance, minTransferAmountUsd));
|
|
573
|
+
const visibleTokens = includeUnfundedTokens ? chain.tokens : chain.tokens.filter((t) => isSelectableDepositSourceAmountUsd(t.balance, minTransferAmountUsd));
|
|
552
574
|
return {
|
|
553
575
|
...chain,
|
|
554
576
|
balance: visibleTokens.reduce((sum, token) => sum + token.balance, 0),
|
|
@@ -1553,11 +1575,11 @@ function detachAccountChangeListeners() {
|
|
|
1553
1575
|
accountChangeBinding = null;
|
|
1554
1576
|
}
|
|
1555
1577
|
function providerKey(selection) {
|
|
1556
|
-
const
|
|
1557
|
-
if (!
|
|
1578
|
+
const providerName2 = selection.providerName?.trim();
|
|
1579
|
+
if (!providerName2) {
|
|
1558
1580
|
throw new Error("Solana wallet metadata is missing providerName.");
|
|
1559
1581
|
}
|
|
1560
|
-
return `${selection.providerId ?? ""}:${
|
|
1582
|
+
return `${selection.providerId ?? ""}:${providerName2.toLowerCase()}`;
|
|
1561
1583
|
}
|
|
1562
1584
|
function assertSupportedProvider(selection) {
|
|
1563
1585
|
const name = selection.providerName?.trim().toLowerCase();
|
|
@@ -1936,464 +1958,197 @@ function screenForPhase(phase) {
|
|
|
1936
1958
|
}
|
|
1937
1959
|
}
|
|
1938
1960
|
|
|
1939
|
-
// src/
|
|
1940
|
-
var
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1961
|
+
// src/api.ts
|
|
1962
|
+
var api_exports = {};
|
|
1963
|
+
__export(api_exports, {
|
|
1964
|
+
createAccount: () => createAccount,
|
|
1965
|
+
createAccountAuthorizationSession: () => createAccountAuthorizationSession,
|
|
1966
|
+
createManualTransfer: () => createManualTransfer,
|
|
1967
|
+
createTransfer: () => createTransfer,
|
|
1968
|
+
fetchAccount: () => fetchAccount,
|
|
1969
|
+
fetchAccountBalances: () => fetchAccountBalances,
|
|
1970
|
+
fetchAccounts: () => fetchAccounts,
|
|
1971
|
+
fetchAuthorizationSession: () => fetchAuthorizationSession,
|
|
1972
|
+
fetchAuthorizationSessionByToken: () => fetchAuthorizationSessionByToken,
|
|
1973
|
+
fetchChains: () => fetchChains,
|
|
1974
|
+
fetchManualTransferSession: () => fetchManualTransferSession,
|
|
1975
|
+
fetchManualTransferSources: () => fetchManualTransferSources,
|
|
1976
|
+
fetchMerchantPublicKey: () => fetchMerchantPublicKey,
|
|
1977
|
+
fetchProviders: () => fetchProviders,
|
|
1978
|
+
fetchTransfer: () => fetchTransfer,
|
|
1979
|
+
fetchUserConfig: () => fetchUserConfig,
|
|
1980
|
+
postTransferQuote: () => postTransferQuote,
|
|
1981
|
+
probeActionCompletion: () => probeActionCompletion,
|
|
1982
|
+
refreshManualTransferQuote: () => refreshManualTransferQuote,
|
|
1983
|
+
regenerateTransferSignPayload: () => regenerateTransferSignPayload,
|
|
1984
|
+
registerPasskey: () => registerPasskey,
|
|
1985
|
+
reportActionCompletion: () => reportActionCompletion,
|
|
1986
|
+
reportPasskeyActivity: () => reportPasskeyActivity,
|
|
1987
|
+
setAuthorizationSessionPaymentIntentAmount: () => setAuthorizationSessionPaymentIntentAmount,
|
|
1988
|
+
setAuthorizationSessionProvider: () => setAuthorizationSessionProvider,
|
|
1989
|
+
signTransfer: () => signTransfer,
|
|
1990
|
+
updateManualTransferDepositTargetChain: () => updateManualTransferDepositTargetChain,
|
|
1991
|
+
updateUserConfig: () => updateUserConfig,
|
|
1992
|
+
updateUserConfigBySession: () => updateUserConfigBySession,
|
|
1993
|
+
waitForActionTransactionReceipt: () => waitForActionTransactionReceipt
|
|
1994
|
+
});
|
|
1995
|
+
var DEBUG_BUFFER_CAPACITY = 200;
|
|
1996
|
+
var nextId = 1;
|
|
1997
|
+
var entries = [];
|
|
1998
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1999
|
+
function notify() {
|
|
2000
|
+
for (const listener of listeners) {
|
|
2001
|
+
try {
|
|
2002
|
+
listener();
|
|
2003
|
+
} catch (err) {
|
|
2004
|
+
console.error("[blink-sdk][debug-log] listener threw:", err);
|
|
2005
|
+
}
|
|
1944
2006
|
}
|
|
1945
|
-
}
|
|
1946
|
-
function
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
2007
|
+
}
|
|
2008
|
+
function appendDebug(level, message, data) {
|
|
2009
|
+
const entry = {
|
|
2010
|
+
id: nextId++,
|
|
2011
|
+
ts: Date.now(),
|
|
2012
|
+
level,
|
|
2013
|
+
message,
|
|
2014
|
+
data
|
|
2015
|
+
};
|
|
2016
|
+
const next = entries.length >= DEBUG_BUFFER_CAPACITY ? entries.slice(entries.length - DEBUG_BUFFER_CAPACITY + 1) : entries.slice();
|
|
2017
|
+
next.push(entry);
|
|
2018
|
+
entries = next;
|
|
2019
|
+
const prefix = "[blink-sdk][debug]";
|
|
2020
|
+
const sink = level === "error" ? console.error : level === "warn" ? console.warn : console.info;
|
|
2021
|
+
if (data !== void 0) {
|
|
2022
|
+
sink(`${prefix} ${message}`, data);
|
|
2023
|
+
} else {
|
|
2024
|
+
sink(`${prefix} ${message}`);
|
|
1954
2025
|
}
|
|
2026
|
+
notify();
|
|
1955
2027
|
}
|
|
1956
|
-
function
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
2028
|
+
function subscribeDebug(listener) {
|
|
2029
|
+
listeners.add(listener);
|
|
2030
|
+
return () => {
|
|
2031
|
+
listeners.delete(listener);
|
|
2032
|
+
};
|
|
1960
2033
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
function
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
}
|
|
2034
|
+
function getDebugEntries() {
|
|
2035
|
+
return entries;
|
|
2036
|
+
}
|
|
2037
|
+
function clearDebugEntries() {
|
|
2038
|
+
entries = [];
|
|
2039
|
+
notify();
|
|
2040
|
+
}
|
|
2041
|
+
function useBlinkDebugLog() {
|
|
2042
|
+
return useSyncExternalStore(subscribeDebug, getDebugEntries, getDebugEntries);
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
// src/fetchWithRetry.ts
|
|
2046
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
2047
|
+
var DEFAULT_BASE_DELAY_MS = 500;
|
|
2048
|
+
var DEFAULT_MAX_JITTER_MS = 200;
|
|
2049
|
+
function isNetworkTypeError(err) {
|
|
2050
|
+
return err instanceof TypeError && /fetch|network|load failed/i.test(err.message);
|
|
2051
|
+
}
|
|
2052
|
+
async function fetchWithRetry(input, init, options) {
|
|
2053
|
+
const maxRetries = DEFAULT_MAX_RETRIES;
|
|
2054
|
+
const baseDelayMs = DEFAULT_BASE_DELAY_MS;
|
|
2055
|
+
const maxJitterMs = DEFAULT_MAX_JITTER_MS;
|
|
2056
|
+
const label = String(input).replace(/https?:\/\/[^/]+/, "");
|
|
2057
|
+
let lastError;
|
|
2058
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2059
|
+
try {
|
|
2060
|
+
return await fetch(input, init);
|
|
2061
|
+
} catch (err) {
|
|
2062
|
+
lastError = err;
|
|
2063
|
+
if (!isNetworkTypeError(err)) {
|
|
2064
|
+
throw err;
|
|
2065
|
+
}
|
|
2066
|
+
if (attempt < maxRetries) {
|
|
2067
|
+
const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * maxJitterMs;
|
|
2068
|
+
appendDebug("warn", `fetchWithRetry: network error, retrying ${label}`, {
|
|
2069
|
+
attempt: attempt + 1,
|
|
2070
|
+
maxRetries,
|
|
2071
|
+
delayMs: Math.round(delay),
|
|
2072
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2073
|
+
});
|
|
2074
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2001
2075
|
}
|
|
2002
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2003
|
-
function cleanup() {
|
|
2004
|
-
clearTimeout(timer);
|
|
2005
|
-
clearInterval(closedPoll);
|
|
2006
2076
|
}
|
|
2077
|
+
}
|
|
2078
|
+
throw lastError;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// src/apiError.ts
|
|
2082
|
+
var ApiError = class extends Error {
|
|
2083
|
+
status;
|
|
2084
|
+
code;
|
|
2085
|
+
constructor(status, code, message) {
|
|
2086
|
+
super(message);
|
|
2087
|
+
this.name = "ApiError";
|
|
2088
|
+
this.status = status;
|
|
2089
|
+
this.code = code;
|
|
2090
|
+
}
|
|
2091
|
+
};
|
|
2092
|
+
function isApiError(err) {
|
|
2093
|
+
if (err instanceof ApiError) return true;
|
|
2094
|
+
return typeof err === "object" && err !== null && "name" in err && err.name === "ApiError";
|
|
2095
|
+
}
|
|
2096
|
+
var SVM_SIGN_PAYLOAD_EXPIRED_CODE = "SVM_SIGN_PAYLOAD_EXPIRED";
|
|
2097
|
+
function isSvmSignExpiredError(err) {
|
|
2098
|
+
return isApiError(err) && err.code === SVM_SIGN_PAYLOAD_EXPIRED_CODE;
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
// src/api.ts
|
|
2102
|
+
async function throwApiError(res) {
|
|
2103
|
+
const body = await res.json().catch(() => null);
|
|
2104
|
+
const detail = body?.error ?? body;
|
|
2105
|
+
const msg = detail?.message ?? res.statusText;
|
|
2106
|
+
const code = detail?.code ?? String(res.status);
|
|
2107
|
+
throw new ApiError(res.status, code, `${res.status} \u2014 ${code}: ${msg}`);
|
|
2108
|
+
}
|
|
2109
|
+
async function fetchProviders(apiBaseUrl, token) {
|
|
2110
|
+
const headers = {};
|
|
2111
|
+
if (token) {
|
|
2112
|
+
headers.Authorization = `Bearer ${token}`;
|
|
2113
|
+
}
|
|
2114
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/providers`, { headers });
|
|
2115
|
+
if (!res.ok) await throwApiError(res);
|
|
2116
|
+
const data = await res.json();
|
|
2117
|
+
return data.items;
|
|
2118
|
+
}
|
|
2119
|
+
async function fetchChains(apiBaseUrl, token) {
|
|
2120
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/chains`, {
|
|
2121
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2007
2122
|
});
|
|
2123
|
+
if (!res.ok) await throwApiError(res);
|
|
2124
|
+
const data = await res.json();
|
|
2125
|
+
return data.items;
|
|
2008
2126
|
}
|
|
2009
|
-
async function
|
|
2010
|
-
|
|
2011
|
-
const res = await
|
|
2012
|
-
headers: { Authorization: `Bearer ${
|
|
2127
|
+
async function fetchAccounts(apiBaseUrl, token, credentialId) {
|
|
2128
|
+
const params = new URLSearchParams({ credentialId });
|
|
2129
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts?${params.toString()}`, {
|
|
2130
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2013
2131
|
});
|
|
2014
|
-
if (!res.ok)
|
|
2015
|
-
const
|
|
2016
|
-
|
|
2017
|
-
const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
|
|
2018
|
-
return matched ? { credentialId: matched.credentialId, publicKey: matched.publicKey } : null;
|
|
2132
|
+
if (!res.ok) await throwApiError(res);
|
|
2133
|
+
const data = await res.json();
|
|
2134
|
+
return data.items;
|
|
2019
2135
|
}
|
|
2020
|
-
function
|
|
2021
|
-
|
|
2136
|
+
async function fetchAccount(apiBaseUrl, token, accountId, credentialId) {
|
|
2137
|
+
const params = new URLSearchParams({ credentialId });
|
|
2138
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts/${accountId}?${params.toString()}`, {
|
|
2139
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2140
|
+
});
|
|
2141
|
+
if (!res.ok) await throwApiError(res);
|
|
2142
|
+
return await res.json();
|
|
2022
2143
|
}
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
}
|
|
2032
|
-
let settled = false;
|
|
2033
|
-
const timer = setTimeout(() => {
|
|
2034
|
-
cleanup();
|
|
2035
|
-
resolve(null);
|
|
2036
|
-
}, SIGNUP_POPUP_TIMEOUT_MS);
|
|
2037
|
-
function onMessage(event) {
|
|
2038
|
-
if (event.origin !== window.location.origin) return;
|
|
2039
|
-
if (event.source !== popup) return;
|
|
2040
|
-
const data = event.data;
|
|
2041
|
-
if (!data || data.type !== "blink:passkey-signup-complete") return;
|
|
2042
|
-
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2043
|
-
settled = true;
|
|
2044
|
-
cleanup();
|
|
2045
|
-
resolve({
|
|
2046
|
-
accessToken: data.accessToken,
|
|
2047
|
-
credentialId: data.credentialId,
|
|
2048
|
-
publicKey: data.publicKey
|
|
2049
|
-
});
|
|
2050
|
-
}
|
|
2051
|
-
window.addEventListener("message", onMessage);
|
|
2052
|
-
const closedPoll = setInterval(() => {
|
|
2053
|
-
if (popup.closed && !settled) {
|
|
2054
|
-
settled = true;
|
|
2055
|
-
cleanup();
|
|
2056
|
-
resolve(null);
|
|
2057
|
-
}
|
|
2058
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2059
|
-
function cleanup() {
|
|
2060
|
-
clearTimeout(timer);
|
|
2061
|
-
clearInterval(closedPoll);
|
|
2062
|
-
window.removeEventListener("message", onMessage);
|
|
2063
|
-
}
|
|
2064
|
-
});
|
|
2065
|
-
}
|
|
2066
|
-
var LOGIN_POPUP_TIMEOUT_MS = 12e4;
|
|
2067
|
-
function loginWithPasskeyViaPopup() {
|
|
2068
|
-
return new Promise((resolve, reject) => {
|
|
2069
|
-
const popupUrl = `${window.location.origin}/passkey-login`;
|
|
2070
|
-
const popup = window.open(popupUrl, "blink-passkey-login");
|
|
2071
|
-
if (!popup) {
|
|
2072
|
-
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2073
|
-
return;
|
|
2074
|
-
}
|
|
2075
|
-
let settled = false;
|
|
2076
|
-
const timer = setTimeout(() => {
|
|
2077
|
-
cleanup();
|
|
2078
|
-
resolve(null);
|
|
2079
|
-
}, LOGIN_POPUP_TIMEOUT_MS);
|
|
2080
|
-
function onMessage(event) {
|
|
2081
|
-
if (event.origin !== window.location.origin) return;
|
|
2082
|
-
if (event.source !== popup) return;
|
|
2083
|
-
const data = event.data;
|
|
2084
|
-
if (!data || data.type !== "blink:passkey-login-complete") return;
|
|
2085
|
-
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2086
|
-
settled = true;
|
|
2087
|
-
cleanup();
|
|
2088
|
-
resolve({
|
|
2089
|
-
accessToken: data.accessToken,
|
|
2090
|
-
credentialId: data.credentialId,
|
|
2091
|
-
publicKey: data.publicKey
|
|
2092
|
-
});
|
|
2093
|
-
}
|
|
2094
|
-
window.addEventListener("message", onMessage);
|
|
2095
|
-
const closedPoll = setInterval(() => {
|
|
2096
|
-
if (popup.closed && !settled) {
|
|
2097
|
-
settled = true;
|
|
2098
|
-
cleanup();
|
|
2099
|
-
resolve(null);
|
|
2100
|
-
}
|
|
2101
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2102
|
-
function cleanup() {
|
|
2103
|
-
clearTimeout(timer);
|
|
2104
|
-
clearInterval(closedPoll);
|
|
2105
|
-
window.removeEventListener("message", onMessage);
|
|
2106
|
-
}
|
|
2107
|
-
});
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
// src/credentialIdEncoding.ts
|
|
2111
|
-
function credentialIdBase64ToBytes(value) {
|
|
2112
|
-
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
2113
|
-
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
2114
|
-
const raw = atob(padded);
|
|
2115
|
-
const bytes = new Uint8Array(raw.length);
|
|
2116
|
-
for (let i = 0; i < raw.length; i++) {
|
|
2117
|
-
bytes[i] = raw.charCodeAt(i);
|
|
2118
|
-
}
|
|
2119
|
-
return bytes;
|
|
2120
|
-
}
|
|
2121
|
-
|
|
2122
|
-
// src/passkeyRpId.ts
|
|
2123
|
-
function normalizeConfiguredDomain(value) {
|
|
2124
|
-
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
2125
|
-
}
|
|
2126
|
-
function resolveRootDomainFromHostname(hostname) {
|
|
2127
|
-
const trimmedHostname = hostname.trim().toLowerCase();
|
|
2128
|
-
if (!trimmedHostname) {
|
|
2129
|
-
return "localhost";
|
|
2130
|
-
}
|
|
2131
|
-
if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
|
|
2132
|
-
return trimmedHostname;
|
|
2133
|
-
}
|
|
2134
|
-
const parts = trimmedHostname.split(".").filter(Boolean);
|
|
2135
|
-
if (parts.length < 2) {
|
|
2136
|
-
return trimmedHostname;
|
|
2137
|
-
}
|
|
2138
|
-
return parts.slice(-2).join(".");
|
|
2139
|
-
}
|
|
2140
|
-
|
|
2141
|
-
// src/hooks/passkeyPublic.ts
|
|
2142
|
-
function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
|
|
2143
|
-
return new Promise((resolve) => {
|
|
2144
|
-
if (typeof document === "undefined") {
|
|
2145
|
-
resolve();
|
|
2146
|
-
return;
|
|
2147
|
-
}
|
|
2148
|
-
if (document.hasFocus()) {
|
|
2149
|
-
resolve();
|
|
2150
|
-
return;
|
|
2151
|
-
}
|
|
2152
|
-
const deadline = Date.now() + timeoutMs;
|
|
2153
|
-
const timer = setInterval(() => {
|
|
2154
|
-
if (document.hasFocus()) {
|
|
2155
|
-
clearInterval(timer);
|
|
2156
|
-
resolve();
|
|
2157
|
-
} else if (Date.now() >= deadline) {
|
|
2158
|
-
clearInterval(timer);
|
|
2159
|
-
resolve();
|
|
2160
|
-
}
|
|
2161
|
-
}, intervalMs);
|
|
2162
|
-
});
|
|
2163
|
-
}
|
|
2164
|
-
function toBase64(buffer) {
|
|
2165
|
-
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
2166
|
-
}
|
|
2167
|
-
function readEnvValue(name) {
|
|
2168
|
-
const meta = import.meta;
|
|
2169
|
-
const metaValue = meta.env?.[name];
|
|
2170
|
-
if (typeof metaValue === "string" && metaValue.trim().length > 0) {
|
|
2171
|
-
return metaValue.trim();
|
|
2172
|
-
}
|
|
2173
|
-
const processValue = globalThis.process?.env?.[name];
|
|
2174
|
-
if (typeof processValue === "string" && processValue.trim().length > 0) {
|
|
2175
|
-
return processValue.trim();
|
|
2176
|
-
}
|
|
2177
|
-
return void 0;
|
|
2178
|
-
}
|
|
2179
|
-
function resolvePasskeyRpId() {
|
|
2180
|
-
const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
|
|
2181
|
-
if (configuredDomain) {
|
|
2182
|
-
return normalizeConfiguredDomain(configuredDomain);
|
|
2183
|
-
}
|
|
2184
|
-
if (typeof window !== "undefined") {
|
|
2185
|
-
return resolveRootDomainFromHostname(window.location.hostname);
|
|
2186
|
-
}
|
|
2187
|
-
return "localhost";
|
|
2188
|
-
}
|
|
2189
|
-
async function deviceHasPasskey(credentialId) {
|
|
2190
|
-
const found = await findDevicePasskey([credentialId]);
|
|
2191
|
-
return found != null;
|
|
2192
|
-
}
|
|
2193
|
-
async function findDevicePasskey(credentialIds) {
|
|
2194
|
-
if (credentialIds.length === 0) return null;
|
|
2195
|
-
try {
|
|
2196
|
-
const challenge = new Uint8Array(32);
|
|
2197
|
-
crypto.getRandomValues(challenge);
|
|
2198
|
-
await waitForDocumentFocus();
|
|
2199
|
-
const assertion = await navigator.credentials.get({
|
|
2200
|
-
publicKey: {
|
|
2201
|
-
challenge,
|
|
2202
|
-
rpId: resolvePasskeyRpId(),
|
|
2203
|
-
allowCredentials: credentialIds.map((id) => ({
|
|
2204
|
-
type: "public-key",
|
|
2205
|
-
id: credentialIdBase64ToBytes(id)
|
|
2206
|
-
})),
|
|
2207
|
-
userVerification: "discouraged",
|
|
2208
|
-
timeout: 3e4
|
|
2209
|
-
}
|
|
2210
|
-
});
|
|
2211
|
-
if (!assertion) return null;
|
|
2212
|
-
return toBase64(assertion.rawId);
|
|
2213
|
-
} catch {
|
|
2214
|
-
return null;
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
// src/api.ts
|
|
2219
|
-
var api_exports = {};
|
|
2220
|
-
__export(api_exports, {
|
|
2221
|
-
createAccount: () => createAccount,
|
|
2222
|
-
createAccountAuthorizationSession: () => createAccountAuthorizationSession,
|
|
2223
|
-
createManualTransfer: () => createManualTransfer,
|
|
2224
|
-
createTransfer: () => createTransfer,
|
|
2225
|
-
fetchAccount: () => fetchAccount,
|
|
2226
|
-
fetchAccounts: () => fetchAccounts,
|
|
2227
|
-
fetchAuthorizationSession: () => fetchAuthorizationSession,
|
|
2228
|
-
fetchAuthorizationSessionByToken: () => fetchAuthorizationSessionByToken,
|
|
2229
|
-
fetchChains: () => fetchChains,
|
|
2230
|
-
fetchManualTransferSession: () => fetchManualTransferSession,
|
|
2231
|
-
fetchManualTransferSources: () => fetchManualTransferSources,
|
|
2232
|
-
fetchMerchantPublicKey: () => fetchMerchantPublicKey,
|
|
2233
|
-
fetchProviders: () => fetchProviders,
|
|
2234
|
-
fetchTransfer: () => fetchTransfer,
|
|
2235
|
-
fetchUserConfig: () => fetchUserConfig,
|
|
2236
|
-
postTransferQuote: () => postTransferQuote,
|
|
2237
|
-
probeActionCompletion: () => probeActionCompletion,
|
|
2238
|
-
refreshManualTransferQuote: () => refreshManualTransferQuote,
|
|
2239
|
-
regenerateTransferSignPayload: () => regenerateTransferSignPayload,
|
|
2240
|
-
registerPasskey: () => registerPasskey,
|
|
2241
|
-
reportActionCompletion: () => reportActionCompletion,
|
|
2242
|
-
reportPasskeyActivity: () => reportPasskeyActivity,
|
|
2243
|
-
setAuthorizationSessionPaymentIntentAmount: () => setAuthorizationSessionPaymentIntentAmount,
|
|
2244
|
-
setAuthorizationSessionProvider: () => setAuthorizationSessionProvider,
|
|
2245
|
-
signTransfer: () => signTransfer,
|
|
2246
|
-
updateManualTransferDepositTargetChain: () => updateManualTransferDepositTargetChain,
|
|
2247
|
-
updateUserConfig: () => updateUserConfig,
|
|
2248
|
-
updateUserConfigBySession: () => updateUserConfigBySession,
|
|
2249
|
-
waitForActionTransactionReceipt: () => waitForActionTransactionReceipt
|
|
2250
|
-
});
|
|
2251
|
-
var DEBUG_BUFFER_CAPACITY = 200;
|
|
2252
|
-
var nextId = 1;
|
|
2253
|
-
var entries = [];
|
|
2254
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
2255
|
-
function notify() {
|
|
2256
|
-
for (const listener of listeners) {
|
|
2257
|
-
try {
|
|
2258
|
-
listener();
|
|
2259
|
-
} catch (err) {
|
|
2260
|
-
console.error("[blink-sdk][debug-log] listener threw:", err);
|
|
2261
|
-
}
|
|
2262
|
-
}
|
|
2263
|
-
}
|
|
2264
|
-
function appendDebug(level, message, data) {
|
|
2265
|
-
const entry = {
|
|
2266
|
-
id: nextId++,
|
|
2267
|
-
ts: Date.now(),
|
|
2268
|
-
level,
|
|
2269
|
-
message,
|
|
2270
|
-
data
|
|
2271
|
-
};
|
|
2272
|
-
const next = entries.length >= DEBUG_BUFFER_CAPACITY ? entries.slice(entries.length - DEBUG_BUFFER_CAPACITY + 1) : entries.slice();
|
|
2273
|
-
next.push(entry);
|
|
2274
|
-
entries = next;
|
|
2275
|
-
const prefix = "[blink-sdk][debug]";
|
|
2276
|
-
const sink = level === "error" ? console.error : level === "warn" ? console.warn : console.info;
|
|
2277
|
-
if (data !== void 0) {
|
|
2278
|
-
sink(`${prefix} ${message}`, data);
|
|
2279
|
-
} else {
|
|
2280
|
-
sink(`${prefix} ${message}`);
|
|
2281
|
-
}
|
|
2282
|
-
notify();
|
|
2283
|
-
}
|
|
2284
|
-
function subscribeDebug(listener) {
|
|
2285
|
-
listeners.add(listener);
|
|
2286
|
-
return () => {
|
|
2287
|
-
listeners.delete(listener);
|
|
2288
|
-
};
|
|
2289
|
-
}
|
|
2290
|
-
function getDebugEntries() {
|
|
2291
|
-
return entries;
|
|
2292
|
-
}
|
|
2293
|
-
function clearDebugEntries() {
|
|
2294
|
-
entries = [];
|
|
2295
|
-
notify();
|
|
2296
|
-
}
|
|
2297
|
-
function useBlinkDebugLog() {
|
|
2298
|
-
return useSyncExternalStore(subscribeDebug, getDebugEntries, getDebugEntries);
|
|
2299
|
-
}
|
|
2300
|
-
|
|
2301
|
-
// src/fetchWithRetry.ts
|
|
2302
|
-
var DEFAULT_MAX_RETRIES = 3;
|
|
2303
|
-
var DEFAULT_BASE_DELAY_MS = 500;
|
|
2304
|
-
var DEFAULT_MAX_JITTER_MS = 200;
|
|
2305
|
-
function isNetworkTypeError(err) {
|
|
2306
|
-
return err instanceof TypeError && /fetch|network|load failed/i.test(err.message);
|
|
2307
|
-
}
|
|
2308
|
-
async function fetchWithRetry(input, init, options) {
|
|
2309
|
-
const maxRetries = DEFAULT_MAX_RETRIES;
|
|
2310
|
-
const baseDelayMs = DEFAULT_BASE_DELAY_MS;
|
|
2311
|
-
const maxJitterMs = DEFAULT_MAX_JITTER_MS;
|
|
2312
|
-
const label = String(input).replace(/https?:\/\/[^/]+/, "");
|
|
2313
|
-
let lastError;
|
|
2314
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2315
|
-
try {
|
|
2316
|
-
return await fetch(input, init);
|
|
2317
|
-
} catch (err) {
|
|
2318
|
-
lastError = err;
|
|
2319
|
-
if (!isNetworkTypeError(err)) {
|
|
2320
|
-
throw err;
|
|
2321
|
-
}
|
|
2322
|
-
if (attempt < maxRetries) {
|
|
2323
|
-
const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * maxJitterMs;
|
|
2324
|
-
appendDebug("warn", `fetchWithRetry: network error, retrying ${label}`, {
|
|
2325
|
-
attempt: attempt + 1,
|
|
2326
|
-
maxRetries,
|
|
2327
|
-
delayMs: Math.round(delay),
|
|
2328
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2329
|
-
});
|
|
2330
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2331
|
-
}
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
throw lastError;
|
|
2335
|
-
}
|
|
2336
|
-
|
|
2337
|
-
// src/apiError.ts
|
|
2338
|
-
var ApiError = class extends Error {
|
|
2339
|
-
status;
|
|
2340
|
-
code;
|
|
2341
|
-
constructor(status, code, message) {
|
|
2342
|
-
super(message);
|
|
2343
|
-
this.name = "ApiError";
|
|
2344
|
-
this.status = status;
|
|
2345
|
-
this.code = code;
|
|
2346
|
-
}
|
|
2347
|
-
};
|
|
2348
|
-
function isApiError(err) {
|
|
2349
|
-
if (err instanceof ApiError) return true;
|
|
2350
|
-
return typeof err === "object" && err !== null && "name" in err && err.name === "ApiError";
|
|
2351
|
-
}
|
|
2352
|
-
var SVM_SIGN_PAYLOAD_EXPIRED_CODE = "SVM_SIGN_PAYLOAD_EXPIRED";
|
|
2353
|
-
function isSvmSignExpiredError(err) {
|
|
2354
|
-
return isApiError(err) && err.code === SVM_SIGN_PAYLOAD_EXPIRED_CODE;
|
|
2355
|
-
}
|
|
2356
|
-
|
|
2357
|
-
// src/api.ts
|
|
2358
|
-
async function throwApiError(res) {
|
|
2359
|
-
const body = await res.json().catch(() => null);
|
|
2360
|
-
const detail = body?.error ?? body;
|
|
2361
|
-
const msg = detail?.message ?? res.statusText;
|
|
2362
|
-
const code = detail?.code ?? String(res.status);
|
|
2363
|
-
throw new ApiError(res.status, code, `${res.status} \u2014 ${code}: ${msg}`);
|
|
2364
|
-
}
|
|
2365
|
-
async function fetchProviders(apiBaseUrl, token) {
|
|
2366
|
-
const headers = {};
|
|
2367
|
-
if (token) {
|
|
2368
|
-
headers.Authorization = `Bearer ${token}`;
|
|
2369
|
-
}
|
|
2370
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/providers`, { headers });
|
|
2371
|
-
if (!res.ok) await throwApiError(res);
|
|
2372
|
-
const data = await res.json();
|
|
2373
|
-
return data.items;
|
|
2374
|
-
}
|
|
2375
|
-
async function fetchChains(apiBaseUrl, token) {
|
|
2376
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/chains`, {
|
|
2377
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2378
|
-
});
|
|
2379
|
-
if (!res.ok) await throwApiError(res);
|
|
2380
|
-
const data = await res.json();
|
|
2381
|
-
return data.items;
|
|
2382
|
-
}
|
|
2383
|
-
async function fetchAccounts(apiBaseUrl, token, credentialId) {
|
|
2384
|
-
const params = new URLSearchParams({ credentialId });
|
|
2385
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts?${params.toString()}`, {
|
|
2386
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2387
|
-
});
|
|
2388
|
-
if (!res.ok) await throwApiError(res);
|
|
2389
|
-
const data = await res.json();
|
|
2390
|
-
return data.items;
|
|
2391
|
-
}
|
|
2392
|
-
async function fetchAccount(apiBaseUrl, token, accountId, credentialId) {
|
|
2393
|
-
const params = new URLSearchParams({ credentialId });
|
|
2394
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts/${accountId}?${params.toString()}`, {
|
|
2395
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2396
|
-
});
|
|
2144
|
+
async function fetchAccountBalances(apiBaseUrl, token, accountId, credentialId) {
|
|
2145
|
+
const params = new URLSearchParams({ credentialId });
|
|
2146
|
+
const res = await fetchWithRetry(
|
|
2147
|
+
`${apiBaseUrl}/v1/accounts/${accountId}/balances?${params.toString()}`,
|
|
2148
|
+
{
|
|
2149
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2150
|
+
}
|
|
2151
|
+
);
|
|
2397
2152
|
if (!res.ok) await throwApiError(res);
|
|
2398
2153
|
return await res.json();
|
|
2399
2154
|
}
|
|
@@ -2714,44 +2469,323 @@ async function waitForActionTransactionReceipt(apiBaseUrl, actionId, txHash) {
|
|
|
2714
2469
|
headers: { "Content-Type": "application/json" },
|
|
2715
2470
|
body: JSON.stringify({ txHash })
|
|
2716
2471
|
}
|
|
2717
|
-
);
|
|
2718
|
-
if (!res.ok) await throwApiError(res);
|
|
2719
|
-
return await res.json();
|
|
2472
|
+
);
|
|
2473
|
+
if (!res.ok) await throwApiError(res);
|
|
2474
|
+
return await res.json();
|
|
2475
|
+
}
|
|
2476
|
+
async function probeActionCompletion(apiBaseUrl, actionId) {
|
|
2477
|
+
const res = await fetchWithRetry(
|
|
2478
|
+
`${apiBaseUrl}/v1/authorization-actions/${actionId}`,
|
|
2479
|
+
{
|
|
2480
|
+
method: "PATCH",
|
|
2481
|
+
headers: { "Content-Type": "application/json" },
|
|
2482
|
+
body: JSON.stringify({ status: "COMPLETED", result: {} })
|
|
2483
|
+
}
|
|
2484
|
+
);
|
|
2485
|
+
if (res.ok) {
|
|
2486
|
+
const session = await res.json();
|
|
2487
|
+
return { detected: true, session };
|
|
2488
|
+
}
|
|
2489
|
+
const body = await res.json().catch(() => null);
|
|
2490
|
+
const detail = body?.error ?? body;
|
|
2491
|
+
const code = detail?.code;
|
|
2492
|
+
const message = detail?.message ?? res.statusText ?? `HTTP ${res.status}`;
|
|
2493
|
+
if (res.status === 422 && code === "DEPOSIT_TX_NOT_FOUND") {
|
|
2494
|
+
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
2495
|
+
}
|
|
2496
|
+
const approvalNotDetectedCodes = /* @__PURE__ */ new Set([
|
|
2497
|
+
"APPROVE_NOT_DETECTED",
|
|
2498
|
+
"APPROVE_SPL_NOT_DETECTED",
|
|
2499
|
+
"SPL_DELEGATE_MISSING",
|
|
2500
|
+
"SPL_DELEGATE_INSUFFICIENT",
|
|
2501
|
+
"SPL_DELEGATE_WRONG_OWNER"
|
|
2502
|
+
]);
|
|
2503
|
+
if (res.status === 422 && code && approvalNotDetectedCodes.has(code)) {
|
|
2504
|
+
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
2505
|
+
}
|
|
2506
|
+
if (res.status === 422 && code === "INVALID_TRANSFER_STATE") {
|
|
2507
|
+
return { detected: false, reason: "invalid-state", status: res.status, code, message };
|
|
2508
|
+
}
|
|
2509
|
+
return { detected: false, reason: "error", status: res.status, code, message };
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
// src/passkey-delegation.ts
|
|
2513
|
+
var PasskeyIframeBlockedError = class extends Error {
|
|
2514
|
+
constructor(message = "Passkey creation is not supported in this browser context.") {
|
|
2515
|
+
super(message);
|
|
2516
|
+
this.name = "PasskeyIframeBlockedError";
|
|
2517
|
+
}
|
|
2518
|
+
};
|
|
2519
|
+
function isInCrossOriginIframe() {
|
|
2520
|
+
if (typeof window === "undefined") return false;
|
|
2521
|
+
if (window.parent === window) return false;
|
|
2522
|
+
try {
|
|
2523
|
+
void window.parent.location.origin;
|
|
2524
|
+
return false;
|
|
2525
|
+
} catch {
|
|
2526
|
+
return true;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
function isSafari() {
|
|
2530
|
+
if (typeof navigator === "undefined") return false;
|
|
2531
|
+
const ua = navigator.userAgent;
|
|
2532
|
+
return /Safari/i.test(ua) && !/Chrome|CriOS|Chromium|Edg|OPR|Firefox/i.test(ua);
|
|
2533
|
+
}
|
|
2534
|
+
var VERIFY_POPUP_TIMEOUT_MS = 6e4;
|
|
2535
|
+
var POPUP_CLOSED_POLL_MS = 500;
|
|
2536
|
+
var POPUP_CLOSED_GRACE_MS = 1e3;
|
|
2537
|
+
function findDevicePasskeyViaPopup(options) {
|
|
2538
|
+
return new Promise((resolve, reject) => {
|
|
2539
|
+
const verificationToken = crypto.randomUUID();
|
|
2540
|
+
const payload = {
|
|
2541
|
+
...options,
|
|
2542
|
+
verificationToken
|
|
2543
|
+
};
|
|
2544
|
+
const encoded = btoa(JSON.stringify(payload));
|
|
2545
|
+
const popupUrl = `${window.location.origin}/passkey-verify#${encoded}`;
|
|
2546
|
+
const popup = window.open(popupUrl, "blink-passkey-verify");
|
|
2547
|
+
if (!popup) {
|
|
2548
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
let settled = false;
|
|
2552
|
+
const timer = setTimeout(() => {
|
|
2553
|
+
cleanup();
|
|
2554
|
+
resolve(null);
|
|
2555
|
+
}, VERIFY_POPUP_TIMEOUT_MS);
|
|
2556
|
+
const closedPoll = setInterval(() => {
|
|
2557
|
+
if (popup.closed && !settled) {
|
|
2558
|
+
clearInterval(closedPoll);
|
|
2559
|
+
setTimeout(() => {
|
|
2560
|
+
if (!settled) {
|
|
2561
|
+
settled = true;
|
|
2562
|
+
cleanup();
|
|
2563
|
+
checkServerForPasskeyByToken(
|
|
2564
|
+
options.authToken,
|
|
2565
|
+
options.apiBaseUrl,
|
|
2566
|
+
verificationToken
|
|
2567
|
+
).then((result) => {
|
|
2568
|
+
resolve(result?.credentialId ?? null);
|
|
2569
|
+
}).catch(() => {
|
|
2570
|
+
resolve(null);
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
}, POPUP_CLOSED_GRACE_MS);
|
|
2574
|
+
}
|
|
2575
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
2576
|
+
function cleanup() {
|
|
2577
|
+
clearTimeout(timer);
|
|
2578
|
+
clearInterval(closedPoll);
|
|
2579
|
+
}
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
async function checkServerForPasskeyByToken(authToken, apiBaseUrl, verificationToken) {
|
|
2583
|
+
if (!authToken || !apiBaseUrl) return null;
|
|
2584
|
+
const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
|
|
2585
|
+
headers: { Authorization: `Bearer ${authToken}` }
|
|
2586
|
+
});
|
|
2587
|
+
if (!res.ok) return null;
|
|
2588
|
+
const body = await res.json();
|
|
2589
|
+
const passkeys = body.config.passkeys ?? [];
|
|
2590
|
+
const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
|
|
2591
|
+
return matched ? { credentialId: matched.credentialId, publicKey: matched.publicKey } : null;
|
|
2592
|
+
}
|
|
2593
|
+
function shouldUsePasskeySignupPopup() {
|
|
2594
|
+
return isSafari() && isInCrossOriginIframe();
|
|
2595
|
+
}
|
|
2596
|
+
var SIGNUP_POPUP_TIMEOUT_MS = 12e4;
|
|
2597
|
+
function signupWithPasskeyViaPopup() {
|
|
2598
|
+
return new Promise((resolve, reject) => {
|
|
2599
|
+
const popupUrl = `${window.location.origin}/passkey-signup`;
|
|
2600
|
+
const popup = window.open(popupUrl, "blink-passkey-signup");
|
|
2601
|
+
if (!popup) {
|
|
2602
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2603
|
+
return;
|
|
2604
|
+
}
|
|
2605
|
+
let settled = false;
|
|
2606
|
+
const timer = setTimeout(() => {
|
|
2607
|
+
cleanup();
|
|
2608
|
+
resolve(null);
|
|
2609
|
+
}, SIGNUP_POPUP_TIMEOUT_MS);
|
|
2610
|
+
function onMessage(event) {
|
|
2611
|
+
if (event.origin !== window.location.origin) return;
|
|
2612
|
+
if (event.source !== popup) return;
|
|
2613
|
+
const data = event.data;
|
|
2614
|
+
if (!data || data.type !== "blink:passkey-signup-complete") return;
|
|
2615
|
+
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2616
|
+
settled = true;
|
|
2617
|
+
cleanup();
|
|
2618
|
+
resolve({
|
|
2619
|
+
accessToken: data.accessToken,
|
|
2620
|
+
credentialId: data.credentialId,
|
|
2621
|
+
publicKey: data.publicKey
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
window.addEventListener("message", onMessage);
|
|
2625
|
+
const closedPoll = setInterval(() => {
|
|
2626
|
+
if (popup.closed && !settled) {
|
|
2627
|
+
settled = true;
|
|
2628
|
+
cleanup();
|
|
2629
|
+
resolve(null);
|
|
2630
|
+
}
|
|
2631
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
2632
|
+
function cleanup() {
|
|
2633
|
+
clearTimeout(timer);
|
|
2634
|
+
clearInterval(closedPoll);
|
|
2635
|
+
window.removeEventListener("message", onMessage);
|
|
2636
|
+
}
|
|
2637
|
+
});
|
|
2638
|
+
}
|
|
2639
|
+
var LOGIN_POPUP_TIMEOUT_MS = 12e4;
|
|
2640
|
+
function loginWithPasskeyViaPopup() {
|
|
2641
|
+
return new Promise((resolve, reject) => {
|
|
2642
|
+
const popupUrl = `${window.location.origin}/passkey-login`;
|
|
2643
|
+
const popup = window.open(popupUrl, "blink-passkey-login");
|
|
2644
|
+
if (!popup) {
|
|
2645
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2646
|
+
return;
|
|
2647
|
+
}
|
|
2648
|
+
let settled = false;
|
|
2649
|
+
const timer = setTimeout(() => {
|
|
2650
|
+
cleanup();
|
|
2651
|
+
resolve(null);
|
|
2652
|
+
}, LOGIN_POPUP_TIMEOUT_MS);
|
|
2653
|
+
function onMessage(event) {
|
|
2654
|
+
if (event.origin !== window.location.origin) return;
|
|
2655
|
+
if (event.source !== popup) return;
|
|
2656
|
+
const data = event.data;
|
|
2657
|
+
if (!data || data.type !== "blink:passkey-login-complete") return;
|
|
2658
|
+
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2659
|
+
settled = true;
|
|
2660
|
+
cleanup();
|
|
2661
|
+
resolve({
|
|
2662
|
+
accessToken: data.accessToken,
|
|
2663
|
+
credentialId: data.credentialId,
|
|
2664
|
+
publicKey: data.publicKey
|
|
2665
|
+
});
|
|
2666
|
+
}
|
|
2667
|
+
window.addEventListener("message", onMessage);
|
|
2668
|
+
const closedPoll = setInterval(() => {
|
|
2669
|
+
if (popup.closed && !settled) {
|
|
2670
|
+
settled = true;
|
|
2671
|
+
cleanup();
|
|
2672
|
+
resolve(null);
|
|
2673
|
+
}
|
|
2674
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
2675
|
+
function cleanup() {
|
|
2676
|
+
clearTimeout(timer);
|
|
2677
|
+
clearInterval(closedPoll);
|
|
2678
|
+
window.removeEventListener("message", onMessage);
|
|
2679
|
+
}
|
|
2680
|
+
});
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
// src/credentialIdEncoding.ts
|
|
2684
|
+
function credentialIdBase64ToBytes(value) {
|
|
2685
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
2686
|
+
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
2687
|
+
const raw = atob(padded);
|
|
2688
|
+
const bytes = new Uint8Array(raw.length);
|
|
2689
|
+
for (let i = 0; i < raw.length; i++) {
|
|
2690
|
+
bytes[i] = raw.charCodeAt(i);
|
|
2691
|
+
}
|
|
2692
|
+
return bytes;
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
// src/passkeyRpId.ts
|
|
2696
|
+
function normalizeConfiguredDomain(value) {
|
|
2697
|
+
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
2698
|
+
}
|
|
2699
|
+
function resolveRootDomainFromHostname(hostname) {
|
|
2700
|
+
const trimmedHostname = hostname.trim().toLowerCase();
|
|
2701
|
+
if (!trimmedHostname) {
|
|
2702
|
+
return "localhost";
|
|
2703
|
+
}
|
|
2704
|
+
if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
|
|
2705
|
+
return trimmedHostname;
|
|
2706
|
+
}
|
|
2707
|
+
const parts = trimmedHostname.split(".").filter(Boolean);
|
|
2708
|
+
if (parts.length < 2) {
|
|
2709
|
+
return trimmedHostname;
|
|
2710
|
+
}
|
|
2711
|
+
return parts.slice(-2).join(".");
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
// src/hooks/passkeyPublic.ts
|
|
2715
|
+
function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
|
|
2716
|
+
return new Promise((resolve) => {
|
|
2717
|
+
if (typeof document === "undefined") {
|
|
2718
|
+
resolve();
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
2721
|
+
if (document.hasFocus()) {
|
|
2722
|
+
resolve();
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
const deadline = Date.now() + timeoutMs;
|
|
2726
|
+
const timer = setInterval(() => {
|
|
2727
|
+
if (document.hasFocus()) {
|
|
2728
|
+
clearInterval(timer);
|
|
2729
|
+
resolve();
|
|
2730
|
+
} else if (Date.now() >= deadline) {
|
|
2731
|
+
clearInterval(timer);
|
|
2732
|
+
resolve();
|
|
2733
|
+
}
|
|
2734
|
+
}, intervalMs);
|
|
2735
|
+
});
|
|
2720
2736
|
}
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
);
|
|
2730
|
-
if (res.ok) {
|
|
2731
|
-
const session = await res.json();
|
|
2732
|
-
return { detected: true, session };
|
|
2737
|
+
function toBase64(buffer) {
|
|
2738
|
+
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
2739
|
+
}
|
|
2740
|
+
function readEnvValue(name) {
|
|
2741
|
+
const meta = import.meta;
|
|
2742
|
+
const metaValue = meta.env?.[name];
|
|
2743
|
+
if (typeof metaValue === "string" && metaValue.trim().length > 0) {
|
|
2744
|
+
return metaValue.trim();
|
|
2733
2745
|
}
|
|
2734
|
-
const
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
const message = detail?.message ?? res.statusText ?? `HTTP ${res.status}`;
|
|
2738
|
-
if (res.status === 422 && code === "DEPOSIT_TX_NOT_FOUND") {
|
|
2739
|
-
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
2746
|
+
const processValue = globalThis.process?.env?.[name];
|
|
2747
|
+
if (typeof processValue === "string" && processValue.trim().length > 0) {
|
|
2748
|
+
return processValue.trim();
|
|
2740
2749
|
}
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
]);
|
|
2748
|
-
if (res.status === 422 && code && approvalNotDetectedCodes.has(code)) {
|
|
2749
|
-
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
2750
|
+
return void 0;
|
|
2751
|
+
}
|
|
2752
|
+
function resolvePasskeyRpId() {
|
|
2753
|
+
const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
|
|
2754
|
+
if (configuredDomain) {
|
|
2755
|
+
return normalizeConfiguredDomain(configuredDomain);
|
|
2750
2756
|
}
|
|
2751
|
-
if (
|
|
2752
|
-
return
|
|
2757
|
+
if (typeof window !== "undefined") {
|
|
2758
|
+
return resolveRootDomainFromHostname(window.location.hostname);
|
|
2759
|
+
}
|
|
2760
|
+
return "localhost";
|
|
2761
|
+
}
|
|
2762
|
+
async function deviceHasPasskey(credentialId) {
|
|
2763
|
+
const found = await findDevicePasskey([credentialId]);
|
|
2764
|
+
return found != null;
|
|
2765
|
+
}
|
|
2766
|
+
async function findDevicePasskey(credentialIds) {
|
|
2767
|
+
if (credentialIds.length === 0) return null;
|
|
2768
|
+
try {
|
|
2769
|
+
const challenge = new Uint8Array(32);
|
|
2770
|
+
crypto.getRandomValues(challenge);
|
|
2771
|
+
await waitForDocumentFocus();
|
|
2772
|
+
const assertion = await navigator.credentials.get({
|
|
2773
|
+
publicKey: {
|
|
2774
|
+
challenge,
|
|
2775
|
+
rpId: resolvePasskeyRpId(),
|
|
2776
|
+
allowCredentials: credentialIds.map((id) => ({
|
|
2777
|
+
type: "public-key",
|
|
2778
|
+
id: credentialIdBase64ToBytes(id)
|
|
2779
|
+
})),
|
|
2780
|
+
userVerification: "discouraged",
|
|
2781
|
+
timeout: 3e4
|
|
2782
|
+
}
|
|
2783
|
+
});
|
|
2784
|
+
if (!assertion) return null;
|
|
2785
|
+
return toBase64(assertion.rawId);
|
|
2786
|
+
} catch {
|
|
2787
|
+
return null;
|
|
2753
2788
|
}
|
|
2754
|
-
return { detected: false, reason: "error", status: res.status, code, message };
|
|
2755
2789
|
}
|
|
2756
2790
|
|
|
2757
2791
|
// src/transferPolling.ts
|
|
@@ -4125,14 +4159,14 @@ function isBatchableAction(action) {
|
|
|
4125
4159
|
return BATCHABLE_ACTION_TYPES.has(action.type);
|
|
4126
4160
|
}
|
|
4127
4161
|
function getSolanaWalletSelection(action) {
|
|
4128
|
-
const
|
|
4129
|
-
if (typeof
|
|
4162
|
+
const providerName2 = action.metadata?.providerName;
|
|
4163
|
+
if (typeof providerName2 !== "string" || providerName2.trim() === "") {
|
|
4130
4164
|
throw new Error(`${action.type} metadata is missing providerName.`);
|
|
4131
4165
|
}
|
|
4132
4166
|
const providerId = action.metadata?.providerId;
|
|
4133
4167
|
return {
|
|
4134
4168
|
...typeof providerId === "string" && providerId.trim() !== "" ? { providerId } : {},
|
|
4135
|
-
providerName
|
|
4169
|
+
providerName: providerName2
|
|
4136
4170
|
};
|
|
4137
4171
|
}
|
|
4138
4172
|
function isPhantomEvmProviderAvailable() {
|
|
@@ -5782,6 +5816,43 @@ function useAuthorizationExecutor(options) {
|
|
|
5782
5816
|
}),
|
|
5783
5817
|
[]
|
|
5784
5818
|
);
|
|
5819
|
+
const [awaitingApproval, setAwaitingApproval] = useState(false);
|
|
5820
|
+
const [approvalDestinationAddress, setApprovalDestinationAddress] = useState(null);
|
|
5821
|
+
const approvalResolverRef = useRef(null);
|
|
5822
|
+
const approvalRejectRef = useRef(null);
|
|
5823
|
+
const approvalInitiatedRef = useRef(false);
|
|
5824
|
+
const waitForApproval = useCallback(
|
|
5825
|
+
(action, destinationAddress) => {
|
|
5826
|
+
if (approvalInitiatedRef.current) return Promise.resolve();
|
|
5827
|
+
return new Promise((resolve, reject) => {
|
|
5828
|
+
approvalResolverRef.current = resolve;
|
|
5829
|
+
approvalRejectRef.current = reject;
|
|
5830
|
+
setCurrentAction(action);
|
|
5831
|
+
setApprovalDestinationAddress(destinationAddress);
|
|
5832
|
+
setAwaitingApproval(true);
|
|
5833
|
+
});
|
|
5834
|
+
},
|
|
5835
|
+
[]
|
|
5836
|
+
);
|
|
5837
|
+
const approveAuthorization = useCallback(() => {
|
|
5838
|
+
approvalInitiatedRef.current = true;
|
|
5839
|
+
setAwaitingApproval(false);
|
|
5840
|
+
if (approvalResolverRef.current) {
|
|
5841
|
+
approvalResolverRef.current();
|
|
5842
|
+
approvalResolverRef.current = null;
|
|
5843
|
+
approvalRejectRef.current = null;
|
|
5844
|
+
}
|
|
5845
|
+
}, []);
|
|
5846
|
+
const resetApprovalGate = useCallback(() => {
|
|
5847
|
+
approvalInitiatedRef.current = false;
|
|
5848
|
+
setAwaitingApproval(false);
|
|
5849
|
+
setApprovalDestinationAddress(null);
|
|
5850
|
+
if (approvalRejectRef.current) {
|
|
5851
|
+
approvalRejectRef.current(new AuthorizationSessionCancelledError());
|
|
5852
|
+
approvalRejectRef.current = null;
|
|
5853
|
+
approvalResolverRef.current = null;
|
|
5854
|
+
}
|
|
5855
|
+
}, []);
|
|
5785
5856
|
const cancelPendingExecution = useCallback(() => {
|
|
5786
5857
|
if (selectSourceRejectRef.current) {
|
|
5787
5858
|
selectSourceRejectRef.current(new AuthorizationSessionCancelledError());
|
|
@@ -5789,6 +5860,12 @@ function useAuthorizationExecutor(options) {
|
|
|
5789
5860
|
selectSourceResolverRef.current = null;
|
|
5790
5861
|
setPendingSelectSource(null);
|
|
5791
5862
|
}
|
|
5863
|
+
if (approvalRejectRef.current) {
|
|
5864
|
+
approvalRejectRef.current(new AuthorizationSessionCancelledError());
|
|
5865
|
+
approvalRejectRef.current = null;
|
|
5866
|
+
approvalResolverRef.current = null;
|
|
5867
|
+
}
|
|
5868
|
+
setAwaitingApproval(false);
|
|
5792
5869
|
setError(null);
|
|
5793
5870
|
setCurrentAction(null);
|
|
5794
5871
|
setApproveSplConfirming(null);
|
|
@@ -6191,6 +6268,11 @@ function useAuthorizationExecutor(options) {
|
|
|
6191
6268
|
currentAction,
|
|
6192
6269
|
approveSplConfirming,
|
|
6193
6270
|
pendingSelectSource,
|
|
6271
|
+
awaitingApproval,
|
|
6272
|
+
approvalDestinationAddress,
|
|
6273
|
+
waitForApproval,
|
|
6274
|
+
approveAuthorization,
|
|
6275
|
+
resetApprovalGate,
|
|
6194
6276
|
resolveSelectSource,
|
|
6195
6277
|
cancelPendingExecution,
|
|
6196
6278
|
resetWalletConnect,
|
|
@@ -6485,6 +6567,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6485
6567
|
const [pendingSelectSourceAction, setPendingSelectSourceAction] = useState(null);
|
|
6486
6568
|
const [orchestratorCompleted, setOrchestratorCompleted] = useState(false);
|
|
6487
6569
|
const [sourceSelectionResolved, setSourceSelectionResolved] = useState(false);
|
|
6570
|
+
const [approvalSmartAccountAddress, setApprovalSmartAccountAddress] = useState(null);
|
|
6488
6571
|
const selectSourceResolverRef = useRef(null);
|
|
6489
6572
|
const selectSourceRejectRef = useRef(null);
|
|
6490
6573
|
const submittedApprovePermit2ActionIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
@@ -6510,6 +6593,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6510
6593
|
const cancellation = new AuthorizationSessionCancelledError();
|
|
6511
6594
|
setOrchestratorCompleted(false);
|
|
6512
6595
|
setSourceSelectionResolved(false);
|
|
6596
|
+
setApprovalSmartAccountAddress(null);
|
|
6513
6597
|
if (selectSourceRejectRef.current) {
|
|
6514
6598
|
selectSourceRejectRef.current(cancellation);
|
|
6515
6599
|
selectSourceRejectRef.current = null;
|
|
@@ -6532,6 +6616,10 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6532
6616
|
lastRunRef.current = { sessionId, options };
|
|
6533
6617
|
setOrchestratorCompleted(false);
|
|
6534
6618
|
setSourceSelectionResolved(false);
|
|
6619
|
+
setApprovalSmartAccountAddress(null);
|
|
6620
|
+
if (!options?.keepApprovalGate) {
|
|
6621
|
+
authExecutor.resetApprovalGate();
|
|
6622
|
+
}
|
|
6535
6623
|
if (!authExecutor.beginExecution()) {
|
|
6536
6624
|
appendDebug("warn", "orchestrator:run aborted \u2014 already executing");
|
|
6537
6625
|
return { status: "cancelled" };
|
|
@@ -6560,6 +6648,8 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6560
6648
|
let action = mergedPending[0];
|
|
6561
6649
|
if (completedIds.has(action.id)) break;
|
|
6562
6650
|
const ownerSessionId = actionSessionMap.get(action.id) ?? sessionId;
|
|
6651
|
+
const ownerSessionSca = sessions.find((s) => s.id === ownerSessionId)?.session.smartAccountAddress;
|
|
6652
|
+
if (ownerSessionSca) setApprovalSmartAccountAddress(ownerSessionSca);
|
|
6563
6653
|
console.info("[blink-sdk][orchestrator] Next pending action.", {
|
|
6564
6654
|
actionId: action.id,
|
|
6565
6655
|
actionType: action.type,
|
|
@@ -6584,6 +6674,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6584
6674
|
const autoResult = createSelectSourceResult(action, options.autoResolveSource);
|
|
6585
6675
|
completedIds.add(action.id);
|
|
6586
6676
|
authExecutor.addResult(autoResult);
|
|
6677
|
+
setSourceSelectionResolved(true);
|
|
6587
6678
|
const reportedSession3 = await reportActionCompletionWithLogging(
|
|
6588
6679
|
apiBaseUrl,
|
|
6589
6680
|
action,
|
|
@@ -6677,6 +6768,13 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6677
6768
|
throw new Error(SIGN_PERMIT2_CONFIRMING_MESSAGE);
|
|
6678
6769
|
}
|
|
6679
6770
|
}
|
|
6771
|
+
if (isApprovalGatedAction(action)) {
|
|
6772
|
+
const sessionSca = sessions.find((s) => s.id === ownerSessionId)?.session.smartAccountAddress;
|
|
6773
|
+
await authExecutor.waitForApproval(
|
|
6774
|
+
action,
|
|
6775
|
+
sessionSca ?? readActionMetadataString(action.metadata, "smartAccountAddress") ?? readActionMetadataString(action.metadata, "ownerPubkey")
|
|
6776
|
+
);
|
|
6777
|
+
}
|
|
6680
6778
|
appendDebug("info", `orchestrator:executeAction start ${action.type}`, {
|
|
6681
6779
|
actionId: action.id,
|
|
6682
6780
|
ownerSessionId
|
|
@@ -6873,7 +6971,8 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6873
6971
|
authExecutor.setError(null);
|
|
6874
6972
|
return run(lastRun.sessionId, {
|
|
6875
6973
|
...lastRun.options,
|
|
6876
|
-
probeBeforePrompt: true
|
|
6974
|
+
probeBeforePrompt: true,
|
|
6975
|
+
keepApprovalGate: true
|
|
6877
6976
|
});
|
|
6878
6977
|
}, [authExecutor, run, cancelPendingFlow]);
|
|
6879
6978
|
return {
|
|
@@ -6883,9 +6982,17 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6883
6982
|
resolveSelectSource,
|
|
6884
6983
|
sourceSelectionResolved,
|
|
6885
6984
|
orchestratorCompleted,
|
|
6985
|
+
approvalSmartAccountAddress,
|
|
6886
6986
|
cancelPendingFlow
|
|
6887
6987
|
};
|
|
6888
6988
|
}
|
|
6989
|
+
function readActionMetadataString(metadata, key) {
|
|
6990
|
+
const value = metadata?.[key];
|
|
6991
|
+
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
6992
|
+
}
|
|
6993
|
+
function isApprovalGatedAction(action) {
|
|
6994
|
+
return action.type === "APPROVE_PERMIT2" || action.type === "SIGN_PERMIT2" || action.type === "APPROVE_SPL";
|
|
6995
|
+
}
|
|
6889
6996
|
function createSelectSourceResult(action, selection) {
|
|
6890
6997
|
return {
|
|
6891
6998
|
actionId: action.id,
|
|
@@ -7856,6 +7963,26 @@ function deriveSourceTypeAndId(state) {
|
|
|
7856
7963
|
}
|
|
7857
7964
|
return { sourceType: "accountId", sourceId: "" };
|
|
7858
7965
|
}
|
|
7966
|
+
function hasAnyBalances(accounts) {
|
|
7967
|
+
return accounts.some(
|
|
7968
|
+
(account) => account.wallets.some(
|
|
7969
|
+
(wallet) => wallet.balance !== void 0 || wallet.sources !== void 0
|
|
7970
|
+
)
|
|
7971
|
+
);
|
|
7972
|
+
}
|
|
7973
|
+
function carryOverBalances(prev, next) {
|
|
7974
|
+
const prevWallets = new Map(
|
|
7975
|
+
prev.flatMap((account) => account.wallets.map((wallet) => [wallet.id, wallet]))
|
|
7976
|
+
);
|
|
7977
|
+
return next.map((account) => ({
|
|
7978
|
+
...account,
|
|
7979
|
+
wallets: account.wallets.map((wallet) => {
|
|
7980
|
+
if (wallet.balance !== void 0 || wallet.sources !== void 0) return wallet;
|
|
7981
|
+
const prior = prevWallets.get(wallet.id);
|
|
7982
|
+
return prior && (prior.balance !== void 0 || prior.sources !== void 0) ? { ...wallet, balance: prior.balance, sources: prior.sources } : wallet;
|
|
7983
|
+
})
|
|
7984
|
+
}));
|
|
7985
|
+
}
|
|
7859
7986
|
function clearStaleSelection(state) {
|
|
7860
7987
|
if (state.selectedAccountId == null) return state;
|
|
7861
7988
|
if (state.desktopWait != null || state.standardDesktopInlineOpenWallet) return state;
|
|
@@ -7877,6 +8004,7 @@ function createInitialState(config) {
|
|
|
7877
8004
|
accounts: [],
|
|
7878
8005
|
chains: [],
|
|
7879
8006
|
loadingData: false,
|
|
8007
|
+
balancesLoading: false,
|
|
7880
8008
|
depositSelectionRefreshing: false,
|
|
7881
8009
|
selectedProviderId: null,
|
|
7882
8010
|
selectedAccountId: null,
|
|
@@ -7906,6 +8034,7 @@ function createInitialState(config) {
|
|
|
7906
8034
|
privyAuthenticated: false,
|
|
7907
8035
|
lastResumedAt: 0,
|
|
7908
8036
|
setupDepositToken: null,
|
|
8037
|
+
setupSpendingLimit: null,
|
|
7909
8038
|
guestWalletPrepared: null,
|
|
7910
8039
|
guestWalletDeeplinksPreparing: false,
|
|
7911
8040
|
amountTooLow: null,
|
|
@@ -7925,6 +8054,7 @@ function clearAuthenticatedSessionState(state) {
|
|
|
7925
8054
|
accounts: [],
|
|
7926
8055
|
chains: [],
|
|
7927
8056
|
loadingData: false,
|
|
8057
|
+
balancesLoading: false,
|
|
7928
8058
|
depositSelectionRefreshing: false,
|
|
7929
8059
|
selectedProviderId: null,
|
|
7930
8060
|
selectedAccountId: null,
|
|
@@ -7950,6 +8080,7 @@ function clearAuthenticatedSessionState(state) {
|
|
|
7950
8080
|
mobileTokenAuthorizationPending: false,
|
|
7951
8081
|
linkSettling: false,
|
|
7952
8082
|
setupDepositToken: null,
|
|
8083
|
+
setupSpendingLimit: null,
|
|
7953
8084
|
guestWalletPrepared: null,
|
|
7954
8085
|
guestWalletDeeplinksPreparing: false,
|
|
7955
8086
|
amountTooLow: null,
|
|
@@ -8018,6 +8149,8 @@ function applyAction(state, action) {
|
|
|
8018
8149
|
providers: action.providers,
|
|
8019
8150
|
accounts: action.accounts,
|
|
8020
8151
|
chains: action.chains,
|
|
8152
|
+
// Accounts arrive balance-free; balances follow via BALANCES_LOADED.
|
|
8153
|
+
balancesLoading: true,
|
|
8021
8154
|
depositSelectionRefreshing: false,
|
|
8022
8155
|
initialDataLoaded: true
|
|
8023
8156
|
};
|
|
@@ -8041,10 +8174,12 @@ function applyAction(state, action) {
|
|
|
8041
8174
|
depositSelectionRefreshing: false
|
|
8042
8175
|
};
|
|
8043
8176
|
case "ACCOUNTS_RELOADED": {
|
|
8177
|
+
const hadBalances = hasAnyBalances(state.accounts);
|
|
8044
8178
|
const next = {
|
|
8045
8179
|
...state,
|
|
8046
|
-
accounts: action.accounts,
|
|
8180
|
+
accounts: carryOverBalances(state.accounts, action.accounts),
|
|
8047
8181
|
providers: action.providers,
|
|
8182
|
+
balancesLoading: !hadBalances,
|
|
8048
8183
|
initialDataLoaded: true
|
|
8049
8184
|
};
|
|
8050
8185
|
if (action.defaults) {
|
|
@@ -8056,6 +8191,24 @@ function applyAction(state, action) {
|
|
|
8056
8191
|
}
|
|
8057
8192
|
return clearStaleSelection(next);
|
|
8058
8193
|
}
|
|
8194
|
+
case "BALANCES_LOADED": {
|
|
8195
|
+
const { balancesByAccountId } = action;
|
|
8196
|
+
const accounts = state.accounts.map((account) => {
|
|
8197
|
+
const accountBalances = balancesByAccountId[account.id];
|
|
8198
|
+
if (!accountBalances) return account;
|
|
8199
|
+
const balancesByWalletId = new Map(
|
|
8200
|
+
accountBalances.wallets.map((w) => [w.id, w])
|
|
8201
|
+
);
|
|
8202
|
+
return {
|
|
8203
|
+
...account,
|
|
8204
|
+
wallets: account.wallets.map((wallet) => {
|
|
8205
|
+
const merged = balancesByWalletId.get(wallet.id);
|
|
8206
|
+
return merged ? { ...wallet, balance: merged.balance, sources: merged.sources } : wallet;
|
|
8207
|
+
})
|
|
8208
|
+
};
|
|
8209
|
+
});
|
|
8210
|
+
return { ...state, accounts, balancesLoading: false };
|
|
8211
|
+
}
|
|
8059
8212
|
case "SET_DEPOSIT_SELECTION_REFRESHING":
|
|
8060
8213
|
return { ...state, depositSelectionRefreshing: action.value };
|
|
8061
8214
|
case "SAVE_SELECTION":
|
|
@@ -8127,6 +8280,7 @@ function applyAction(state, action) {
|
|
|
8127
8280
|
mobileFlow: false,
|
|
8128
8281
|
mobileTokenAuthorizationPending: false,
|
|
8129
8282
|
setupDepositToken: null,
|
|
8283
|
+
setupSpendingLimit: null,
|
|
8130
8284
|
setupFlowScreen: null
|
|
8131
8285
|
};
|
|
8132
8286
|
case "PAY_ENDED":
|
|
@@ -8372,6 +8526,7 @@ function applyAction(state, action) {
|
|
|
8372
8526
|
linkSettling: false,
|
|
8373
8527
|
savedSelection: null,
|
|
8374
8528
|
setupDepositToken: null,
|
|
8529
|
+
setupSpendingLimit: null,
|
|
8375
8530
|
setupFlowScreen: null,
|
|
8376
8531
|
guestWalletPrepared: null,
|
|
8377
8532
|
guestWalletDeeplinksPreparing: false,
|
|
@@ -8437,12 +8592,217 @@ function applyAction(state, action) {
|
|
|
8437
8592
|
...action.chainId != null ? { chainId: action.chainId } : {}
|
|
8438
8593
|
}
|
|
8439
8594
|
};
|
|
8595
|
+
case "SET_SETUP_SPENDING_LIMIT":
|
|
8596
|
+
return { ...state, setupSpendingLimit: action.limit };
|
|
8440
8597
|
case "CLEAR_SETUP_DEPOSIT_TOKEN":
|
|
8441
|
-
return { ...state, setupDepositToken: null };
|
|
8598
|
+
return { ...state, setupDepositToken: null, setupSpendingLimit: null };
|
|
8442
8599
|
default:
|
|
8443
8600
|
return state;
|
|
8444
8601
|
}
|
|
8445
8602
|
}
|
|
8603
|
+
var BLOCKED_SUBSTRINGS = [
|
|
8604
|
+
"bearer",
|
|
8605
|
+
"signature",
|
|
8606
|
+
"signedpayload",
|
|
8607
|
+
"userop",
|
|
8608
|
+
"jwt",
|
|
8609
|
+
"secret",
|
|
8610
|
+
"password",
|
|
8611
|
+
"privatekey",
|
|
8612
|
+
"mnemonic",
|
|
8613
|
+
"seedphrase"
|
|
8614
|
+
];
|
|
8615
|
+
function isBlockedKey(key) {
|
|
8616
|
+
const lower = key.toLowerCase();
|
|
8617
|
+
if (lower === "token" || lower.endsWith("token")) return true;
|
|
8618
|
+
return BLOCKED_SUBSTRINGS.some((blocked) => lower.includes(blocked));
|
|
8619
|
+
}
|
|
8620
|
+
function isReady() {
|
|
8621
|
+
return typeof posthog !== "undefined" && posthog.__loaded === true;
|
|
8622
|
+
}
|
|
8623
|
+
function sanitizeProps(props) {
|
|
8624
|
+
if (!props) return props;
|
|
8625
|
+
const clean = {};
|
|
8626
|
+
for (const [key, value] of Object.entries(props)) {
|
|
8627
|
+
if (isBlockedKey(key)) continue;
|
|
8628
|
+
if (value === void 0) continue;
|
|
8629
|
+
clean[key] = value;
|
|
8630
|
+
}
|
|
8631
|
+
return clean;
|
|
8632
|
+
}
|
|
8633
|
+
function registerSuperProps(props) {
|
|
8634
|
+
if (!isReady()) return;
|
|
8635
|
+
try {
|
|
8636
|
+
posthog.register(sanitizeProps(props) ?? {});
|
|
8637
|
+
} catch {
|
|
8638
|
+
}
|
|
8639
|
+
}
|
|
8640
|
+
function track(event, props) {
|
|
8641
|
+
if (!isReady()) return;
|
|
8642
|
+
try {
|
|
8643
|
+
posthog.capture(event, sanitizeProps(props));
|
|
8644
|
+
} catch {
|
|
8645
|
+
}
|
|
8646
|
+
}
|
|
8647
|
+
function identifyUser(privyUserId, props) {
|
|
8648
|
+
if (!isReady() || !privyUserId) return;
|
|
8649
|
+
try {
|
|
8650
|
+
posthog.identify(privyUserId, sanitizeProps(props));
|
|
8651
|
+
} catch {
|
|
8652
|
+
}
|
|
8653
|
+
}
|
|
8654
|
+
function resolveAnalyticsEnvironment(opts) {
|
|
8655
|
+
const explicit = opts.explicit?.trim();
|
|
8656
|
+
if (explicit) return explicit;
|
|
8657
|
+
const raw = opts.apiBaseUrl ?? "";
|
|
8658
|
+
let host = raw;
|
|
8659
|
+
try {
|
|
8660
|
+
host = new URL(raw).hostname;
|
|
8661
|
+
} catch {
|
|
8662
|
+
}
|
|
8663
|
+
const h = host.toLowerCase();
|
|
8664
|
+
if (h === "" || h.includes("localhost") || h.startsWith("127.")) return "development";
|
|
8665
|
+
if (h.includes("smokebox")) return "smokebox";
|
|
8666
|
+
if (h.includes("sandbox")) return "sandbox";
|
|
8667
|
+
if (h.includes("staging")) return "staging";
|
|
8668
|
+
return "production";
|
|
8669
|
+
}
|
|
8670
|
+
function resetUser() {
|
|
8671
|
+
if (!isReady()) return;
|
|
8672
|
+
try {
|
|
8673
|
+
posthog.reset();
|
|
8674
|
+
} catch {
|
|
8675
|
+
}
|
|
8676
|
+
}
|
|
8677
|
+
|
|
8678
|
+
// src/analyticsEvents.ts
|
|
8679
|
+
function providerName(state, providerId) {
|
|
8680
|
+
return state.providers.find((p) => p.id === providerId)?.name;
|
|
8681
|
+
}
|
|
8682
|
+
function amountUsd(transfer) {
|
|
8683
|
+
return transfer.amount?.currency === "USD" ? transfer.amount.amount : transfer.amount?.amount;
|
|
8684
|
+
}
|
|
8685
|
+
function sourceChain(transfer) {
|
|
8686
|
+
return transfer.sources?.[0]?.provider?.chainFamily;
|
|
8687
|
+
}
|
|
8688
|
+
function trackAction(action, state, ctx) {
|
|
8689
|
+
const device = ctx.device;
|
|
8690
|
+
switch (action.type) {
|
|
8691
|
+
case "PASSKEY_ACTIVATED":
|
|
8692
|
+
track("auth_succeeded", { method: "passkey", device });
|
|
8693
|
+
break;
|
|
8694
|
+
case "SELECT_PROVIDER":
|
|
8695
|
+
track("wallet_provider_selected", {
|
|
8696
|
+
device,
|
|
8697
|
+
providerId: action.providerId,
|
|
8698
|
+
providerName: providerName(state, action.providerId),
|
|
8699
|
+
transport: device === "desktop" ? "inline" : "deeplink"
|
|
8700
|
+
});
|
|
8701
|
+
break;
|
|
8702
|
+
case "MOBILE_DEEPLINK_READY":
|
|
8703
|
+
track("wallet_deeplink_opened", { device });
|
|
8704
|
+
break;
|
|
8705
|
+
case "SET_SETUP_DEPOSIT_TOKEN":
|
|
8706
|
+
track("token_selected", {
|
|
8707
|
+
device,
|
|
8708
|
+
tokenSymbol: action.symbol,
|
|
8709
|
+
chainName: action.chainName
|
|
8710
|
+
});
|
|
8711
|
+
break;
|
|
8712
|
+
case "FINALIZE_AMOUNT": {
|
|
8713
|
+
const parsed = Number(state.amount);
|
|
8714
|
+
track("amount_entered", {
|
|
8715
|
+
device,
|
|
8716
|
+
amountUsd: Number.isFinite(parsed) ? parsed : void 0
|
|
8717
|
+
});
|
|
8718
|
+
break;
|
|
8719
|
+
}
|
|
8720
|
+
case "PAY_STARTED":
|
|
8721
|
+
track("deposit_clicked", { device });
|
|
8722
|
+
break;
|
|
8723
|
+
case "TRANSFER_CREATED":
|
|
8724
|
+
track("transfer_created", {
|
|
8725
|
+
device,
|
|
8726
|
+
chainFamily: sourceChain(action.transfer)
|
|
8727
|
+
});
|
|
8728
|
+
break;
|
|
8729
|
+
case "TRANSFER_SIGNED":
|
|
8730
|
+
track("transfer_signed", { device });
|
|
8731
|
+
break;
|
|
8732
|
+
case "TRANSFER_COMPLETED":
|
|
8733
|
+
track("transfer_completed", {
|
|
8734
|
+
device,
|
|
8735
|
+
amountUsd: amountUsd(action.transfer),
|
|
8736
|
+
chainFamily: sourceChain(action.transfer)
|
|
8737
|
+
});
|
|
8738
|
+
break;
|
|
8739
|
+
case "CONFIRM_SIGN_SUCCESS":
|
|
8740
|
+
track("confirm_sign_success", { device });
|
|
8741
|
+
break;
|
|
8742
|
+
case "MOBILE_SETUP_COMPLETE":
|
|
8743
|
+
track("wallet_setup_complete", { device });
|
|
8744
|
+
break;
|
|
8745
|
+
case "TRANSFER_FAILED":
|
|
8746
|
+
track("transfer_error", {
|
|
8747
|
+
device,
|
|
8748
|
+
type: "transfer_failed",
|
|
8749
|
+
stage: "processing",
|
|
8750
|
+
message: action.error
|
|
8751
|
+
});
|
|
8752
|
+
break;
|
|
8753
|
+
case "PROCESSING_TIMEOUT":
|
|
8754
|
+
track("transfer_error", {
|
|
8755
|
+
device,
|
|
8756
|
+
type: "processing_timeout",
|
|
8757
|
+
stage: "processing",
|
|
8758
|
+
message: action.error
|
|
8759
|
+
});
|
|
8760
|
+
break;
|
|
8761
|
+
case "SIGN_PAYLOAD_EXPIRED":
|
|
8762
|
+
track("transfer_error", {
|
|
8763
|
+
device,
|
|
8764
|
+
type: "sign_payload_expired",
|
|
8765
|
+
stage: "signing",
|
|
8766
|
+
message: action.error
|
|
8767
|
+
});
|
|
8768
|
+
break;
|
|
8769
|
+
case "PAY_ERROR":
|
|
8770
|
+
track("transfer_error", {
|
|
8771
|
+
device,
|
|
8772
|
+
type: "pay_error",
|
|
8773
|
+
stage: "creation",
|
|
8774
|
+
message: action.error
|
|
8775
|
+
});
|
|
8776
|
+
break;
|
|
8777
|
+
}
|
|
8778
|
+
}
|
|
8779
|
+
function useAnalyticsScreenView(phase, device) {
|
|
8780
|
+
const lastScreenRef = useRef(null);
|
|
8781
|
+
const screen = screenForPhase(phase);
|
|
8782
|
+
useEffect(() => {
|
|
8783
|
+
if (lastScreenRef.current === screen) return;
|
|
8784
|
+
lastScreenRef.current = screen;
|
|
8785
|
+
track("screen_viewed", { screen, phase: phase.step, device });
|
|
8786
|
+
}, [screen, phase.step, device]);
|
|
8787
|
+
}
|
|
8788
|
+
function usePostHogIdentify() {
|
|
8789
|
+
const { ready, authenticated, user } = usePrivy();
|
|
8790
|
+
const identifiedIdRef = useRef(null);
|
|
8791
|
+
useEffect(() => {
|
|
8792
|
+
if (!ready) return;
|
|
8793
|
+
if (authenticated && user?.id) {
|
|
8794
|
+
if (identifiedIdRef.current !== user.id) {
|
|
8795
|
+
identifyUser(user.id);
|
|
8796
|
+
identifiedIdRef.current = user.id;
|
|
8797
|
+
}
|
|
8798
|
+
return;
|
|
8799
|
+
}
|
|
8800
|
+
if (identifiedIdRef.current !== null) {
|
|
8801
|
+
resetUser();
|
|
8802
|
+
identifiedIdRef.current = null;
|
|
8803
|
+
}
|
|
8804
|
+
}, [ready, authenticated, user?.id]);
|
|
8805
|
+
}
|
|
8446
8806
|
|
|
8447
8807
|
// src/setupDepositConfirmation.ts
|
|
8448
8808
|
function deriveEffectiveSetupSource(setupDepositToken, setupSelectedSourceOption) {
|
|
@@ -9021,9 +9381,12 @@ function PrimaryButton({
|
|
|
9021
9381
|
}
|
|
9022
9382
|
);
|
|
9023
9383
|
}
|
|
9384
|
+
var BUTTON_MIN_HEIGHT = 60;
|
|
9024
9385
|
var progressButtonStyle = (tokens, paused) => ({
|
|
9025
9386
|
position: "relative",
|
|
9026
9387
|
width: "100%",
|
|
9388
|
+
minHeight: BUTTON_MIN_HEIGHT,
|
|
9389
|
+
boxSizing: "border-box",
|
|
9027
9390
|
padding: "18px 24px",
|
|
9028
9391
|
background: `linear-gradient(180deg, ${tokens.accent}88, ${tokens.accentHover}88)`,
|
|
9029
9392
|
color: tokens.accentText,
|
|
@@ -9060,6 +9423,8 @@ var buttonStyle2 = (tokens, state) => {
|
|
|
9060
9423
|
const gradient = `linear-gradient(180deg, ${tokens.accent}, ${tokens.accentHover})`;
|
|
9061
9424
|
return {
|
|
9062
9425
|
width: "100%",
|
|
9426
|
+
minHeight: BUTTON_MIN_HEIGHT,
|
|
9427
|
+
boxSizing: "border-box",
|
|
9063
9428
|
padding: "18px 24px",
|
|
9064
9429
|
// Layer a faint white film over the gradient on hover rather than using
|
|
9065
9430
|
// `filter: brightness()` — the accent gradient is near-black in the new
|
|
@@ -9307,6 +9672,7 @@ var USDH_LOGO = svgToDataUri(USDH_SVG);
|
|
|
9307
9672
|
var ETH_LOGO = "https://assets.relay.link/icons/currencies/eth.png";
|
|
9308
9673
|
var SOL_LOGO = "https://assets.relay.link/icons/currencies/sol.png";
|
|
9309
9674
|
var MON_LOGO = "https://assets.relay.link/icons/currencies/mon.png";
|
|
9675
|
+
var BNB_LOGO = "https://assets.relay.link/icons/currencies/bnb.png";
|
|
9310
9676
|
var KNOWN_LOGOS = {
|
|
9311
9677
|
metamask: METAMASK_LOGO,
|
|
9312
9678
|
base: BASE_LOGO,
|
|
@@ -9325,7 +9691,8 @@ var TOKEN_LOGOS = {
|
|
|
9325
9691
|
USDH: USDH_LOGO,
|
|
9326
9692
|
ETH: ETH_LOGO,
|
|
9327
9693
|
SOL: SOL_LOGO,
|
|
9328
|
-
MON: MON_LOGO
|
|
9694
|
+
MON: MON_LOGO,
|
|
9695
|
+
BNB: BNB_LOGO
|
|
9329
9696
|
};
|
|
9330
9697
|
var CHAIN_LOGOS = {
|
|
9331
9698
|
base: BASE_CHAIN_LOGO,
|
|
@@ -9984,6 +10351,8 @@ function SourcePill({
|
|
|
9984
10351
|
logo,
|
|
9985
10352
|
name,
|
|
9986
10353
|
balance,
|
|
10354
|
+
nameSlot,
|
|
10355
|
+
balanceSlot,
|
|
9987
10356
|
expanded,
|
|
9988
10357
|
showChevron = true,
|
|
9989
10358
|
icon
|
|
@@ -9991,8 +10360,8 @@ function SourcePill({
|
|
|
9991
10360
|
const { tokens } = useBlinkConfig();
|
|
9992
10361
|
return /* @__PURE__ */ jsxs("span", { style: pillStyle2(tokens.bgCardTranslucent), children: [
|
|
9993
10362
|
icon ?? (logo ? /* @__PURE__ */ jsx("img", { src: logo, alt: "", "aria-hidden": "true", style: logoStyle }) : name ? /* @__PURE__ */ jsx("span", { style: logoFallbackStyle(tokens.bgInput, tokens.textMuted), children: name.charAt(0).toUpperCase() }) : null),
|
|
9994
|
-
name && /* @__PURE__ */ jsx("span", { style: labelStyle2(tokens.text), children: name }),
|
|
9995
|
-
balance && /* @__PURE__ */ jsx("span", { style: balanceStyle(tokens.text), children: balance }),
|
|
10363
|
+
nameSlot ?? (name && /* @__PURE__ */ jsx("span", { style: labelStyle2(tokens.text), children: name })),
|
|
10364
|
+
balanceSlot ?? (balance && /* @__PURE__ */ jsx("span", { style: balanceStyle(tokens.text), children: balance })),
|
|
9996
10365
|
showChevron && /* @__PURE__ */ jsx("svg", { width: "17", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: expanded ? /* @__PURE__ */ jsx(
|
|
9997
10366
|
"path",
|
|
9998
10367
|
{
|
|
@@ -10055,6 +10424,37 @@ var balanceStyle = (color) => ({
|
|
|
10055
10424
|
color,
|
|
10056
10425
|
whiteSpace: "nowrap"
|
|
10057
10426
|
});
|
|
10427
|
+
var SHIMMER_KEYFRAMES = `
|
|
10428
|
+
@keyframes blink-shimmer-sweep {
|
|
10429
|
+
0% { background-position: 200% 0; }
|
|
10430
|
+
100% { background-position: -200% 0; }
|
|
10431
|
+
}`;
|
|
10432
|
+
function Shimmer({
|
|
10433
|
+
width,
|
|
10434
|
+
height,
|
|
10435
|
+
borderRadius = 999,
|
|
10436
|
+
baseColor,
|
|
10437
|
+
highlightColor,
|
|
10438
|
+
style
|
|
10439
|
+
}) {
|
|
10440
|
+
return /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", style: { display: "inline-block", ...style }, children: [
|
|
10441
|
+
/* @__PURE__ */ jsx("style", { children: SHIMMER_KEYFRAMES }),
|
|
10442
|
+
/* @__PURE__ */ jsx(
|
|
10443
|
+
"span",
|
|
10444
|
+
{
|
|
10445
|
+
style: {
|
|
10446
|
+
display: "block",
|
|
10447
|
+
width,
|
|
10448
|
+
height,
|
|
10449
|
+
borderRadius,
|
|
10450
|
+
background: `linear-gradient(90deg, ${baseColor} 0%, ${highlightColor} 50%, ${baseColor} 100%)`,
|
|
10451
|
+
backgroundSize: "200% 100%",
|
|
10452
|
+
animation: "blink-shimmer-sweep 1.4s ease-in-out infinite"
|
|
10453
|
+
}
|
|
10454
|
+
}
|
|
10455
|
+
)
|
|
10456
|
+
] });
|
|
10457
|
+
}
|
|
10058
10458
|
function formatUsdTwoDecimals(value) {
|
|
10059
10459
|
const n = Number(value);
|
|
10060
10460
|
return (Number.isFinite(n) ? n : 0).toLocaleString("en-US", {
|
|
@@ -10755,6 +11155,14 @@ function LockIcon({ size = 24 }) {
|
|
|
10755
11155
|
/* @__PURE__ */ jsx("path", { d: "M8 11V8a4 4 0 1 1 8 0v3", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" })
|
|
10756
11156
|
] });
|
|
10757
11157
|
}
|
|
11158
|
+
function KeyIcon({ size = 24 }) {
|
|
11159
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
11160
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "12", r: "3.6", stroke: "currentColor", strokeWidth: "2" }),
|
|
11161
|
+
/* @__PURE__ */ jsx("path", { d: "M10.6 12H20", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
11162
|
+
/* @__PURE__ */ jsx("path", { d: "M17 12v3.2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
11163
|
+
/* @__PURE__ */ jsx("path", { d: "M20 12v3.2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
|
|
11164
|
+
] });
|
|
11165
|
+
}
|
|
10758
11166
|
function FaceIdIcon({ size = 24 }) {
|
|
10759
11167
|
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
10760
11168
|
/* @__PURE__ */ jsx("path", { d: "M4 9V6a2 2 0 0 1 2-2h3", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }),
|
|
@@ -10768,6 +11176,21 @@ function FaceIdIcon({ size = 24 }) {
|
|
|
10768
11176
|
/* @__PURE__ */ jsx("path", { d: "M9.5 16c.7.6 1.6 1 2.5 1s1.8-.4 2.5-1", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" })
|
|
10769
11177
|
] });
|
|
10770
11178
|
}
|
|
11179
|
+
function PencilIcon({ size = 18 } = {}) {
|
|
11180
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
11181
|
+
/* @__PURE__ */ jsx(
|
|
11182
|
+
"path",
|
|
11183
|
+
{
|
|
11184
|
+
d: "M4 20h4l10.5-10.5a2 2 0 0 0 0-2.83l-1.17-1.17a2 2 0 0 0-2.83 0L4 16v4Z",
|
|
11185
|
+
stroke: "currentColor",
|
|
11186
|
+
strokeWidth: "1.7",
|
|
11187
|
+
strokeLinecap: "round",
|
|
11188
|
+
strokeLinejoin: "round"
|
|
11189
|
+
}
|
|
11190
|
+
),
|
|
11191
|
+
/* @__PURE__ */ jsx("path", { d: "M13.5 6.5l4 4", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
11192
|
+
] });
|
|
11193
|
+
}
|
|
10771
11194
|
function WalletIcon() {
|
|
10772
11195
|
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
10773
11196
|
/* @__PURE__ */ jsx(
|
|
@@ -11139,7 +11562,7 @@ function CopyAddressButton({
|
|
|
11139
11562
|
}) {
|
|
11140
11563
|
const { tokens } = useBlinkConfig();
|
|
11141
11564
|
const [hovered, setHovered] = useState(false);
|
|
11142
|
-
const short =
|
|
11565
|
+
const short = truncateMiddle(address);
|
|
11143
11566
|
return /* @__PURE__ */ jsxs(
|
|
11144
11567
|
"button",
|
|
11145
11568
|
{
|
|
@@ -13099,7 +13522,7 @@ var lockBannerTextStyle = (color) => ({
|
|
|
13099
13522
|
function ManualTransferPasskeyScreen({
|
|
13100
13523
|
onCreatePasskey,
|
|
13101
13524
|
onNoThanks,
|
|
13102
|
-
amountUsd,
|
|
13525
|
+
amountUsd: amountUsd2,
|
|
13103
13526
|
loading = false,
|
|
13104
13527
|
error,
|
|
13105
13528
|
onBack,
|
|
@@ -13297,7 +13720,6 @@ var errorBannerStyle6 = (tokens) => ({
|
|
|
13297
13720
|
textAlign: "left",
|
|
13298
13721
|
boxSizing: "border-box"
|
|
13299
13722
|
});
|
|
13300
|
-
var SHIMMER_ROWS = 3;
|
|
13301
13723
|
function LinkTokensScreen({
|
|
13302
13724
|
entries: entries2,
|
|
13303
13725
|
selectedIndex,
|
|
@@ -13310,44 +13732,75 @@ function LinkTokensScreen({
|
|
|
13310
13732
|
approving = false
|
|
13311
13733
|
}) {
|
|
13312
13734
|
const { tokens: t } = useBlinkConfig();
|
|
13735
|
+
const [view, setView] = useState("summary");
|
|
13736
|
+
const [pendingIndex, setPendingIndex] = useState(selectedIndex);
|
|
13737
|
+
const [limit, setLimit] = useState("unlimited");
|
|
13738
|
+
const [editing, setEditing] = useState(false);
|
|
13313
13739
|
const showShimmer = loading && entries2.length === 0;
|
|
13314
13740
|
const showEmpty = !loading && entries2.length === 0;
|
|
13315
13741
|
const selected = entries2[selectedIndex];
|
|
13742
|
+
const limitUsd = limit === "unlimited" ? null : parseAmount(limit);
|
|
13743
|
+
const isUnlimited = limitUsd == null;
|
|
13744
|
+
const limitLabel = isUnlimited ? "Unlimited" : `$${limit}`;
|
|
13745
|
+
const currentSelection = () => limitUsd == null ? { unlimited: true } : { usd: limitUsd };
|
|
13316
13746
|
const approveDisabled = loading || approving || entries2.length === 0 || selectedIndex < 0 || selectedIndex >= entries2.length || !!selected?.notSupported;
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
|
|
13325
|
-
|
|
13326
|
-
|
|
13327
|
-
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
|
|
13347
|
-
|
|
13348
|
-
|
|
13349
|
-
|
|
13350
|
-
|
|
13747
|
+
function openSelectToken() {
|
|
13748
|
+
setPendingIndex(selectedIndex);
|
|
13749
|
+
setView("selectToken");
|
|
13750
|
+
}
|
|
13751
|
+
function commitSelectToken() {
|
|
13752
|
+
const row = entries2[pendingIndex];
|
|
13753
|
+
if (row && !row.notSupported) onSelect(pendingIndex);
|
|
13754
|
+
setView("summary");
|
|
13755
|
+
}
|
|
13756
|
+
if (view === "selectToken") {
|
|
13757
|
+
const pending = entries2[pendingIndex];
|
|
13758
|
+
const continueDisabled = !pending || !!pending.notSupported || entries2.length === 0;
|
|
13759
|
+
return /* @__PURE__ */ jsxs(
|
|
13760
|
+
ScreenLayout,
|
|
13761
|
+
{
|
|
13762
|
+
scrollableBody: false,
|
|
13763
|
+
footer: /* @__PURE__ */ jsx(PrimaryButton, { onClick: commitSelectToken, disabled: continueDisabled, children: "Continue" }),
|
|
13764
|
+
children: [
|
|
13765
|
+
/* @__PURE__ */ jsx(ListScrollbarStyles, { textTertiary: t.textTertiary }),
|
|
13766
|
+
/* @__PURE__ */ jsx(
|
|
13767
|
+
ScreenHeader,
|
|
13768
|
+
{
|
|
13769
|
+
onBack: () => setView("summary"),
|
|
13770
|
+
center: /* @__PURE__ */ jsx("img", { src: BLINK_WORDMARK, alt: "Blink", style: wordmarkImgStyle3 })
|
|
13771
|
+
}
|
|
13772
|
+
),
|
|
13773
|
+
/* @__PURE__ */ jsxs("div", { style: contentStyle10, children: [
|
|
13774
|
+
/* @__PURE__ */ jsx("h2", { style: headingStyle7(t.text, t.fontWeightBold), children: "Select token" }),
|
|
13775
|
+
/* @__PURE__ */ jsxs("div", { className: "blink-link-tokens-list", style: listCardStyle(t.bgRecessed), children: [
|
|
13776
|
+
entries2.map((entry, i) => /* @__PURE__ */ jsx(
|
|
13777
|
+
TokenSourceRow,
|
|
13778
|
+
{
|
|
13779
|
+
symbol: entry.tokenSymbol,
|
|
13780
|
+
chainName: entry.chainName,
|
|
13781
|
+
tokenLogoUri: entry.tokenLogoUri,
|
|
13782
|
+
balance: entry.balanceLabel == null ? entry.balanceUsd : void 0,
|
|
13783
|
+
balanceLabel: entry.balanceLabel,
|
|
13784
|
+
selected: i === pendingIndex,
|
|
13785
|
+
notSupported: entry.notSupported,
|
|
13786
|
+
onClick: () => setPendingIndex(i)
|
|
13787
|
+
},
|
|
13788
|
+
`${entry.tokenSymbol}-${entry.chainName}-${i}`
|
|
13789
|
+
)),
|
|
13790
|
+
showEmpty && /* @__PURE__ */ jsx("div", { style: emptyStyle(t.textMuted), children: "No tokens available to link. Switch wallets or fund this account to continue." })
|
|
13791
|
+
] })
|
|
13792
|
+
] })
|
|
13793
|
+
]
|
|
13794
|
+
}
|
|
13795
|
+
);
|
|
13796
|
+
}
|
|
13797
|
+
return /* @__PURE__ */ jsxs(
|
|
13798
|
+
ScreenLayout,
|
|
13799
|
+
{
|
|
13800
|
+
scrollableBody: false,
|
|
13801
|
+
footer: /* @__PURE__ */ jsx(PrimaryButton, { onClick: () => onApprove(currentSelection()), disabled: approveDisabled, loading: approving, children: "Review" }),
|
|
13802
|
+
children: [
|
|
13803
|
+
/* @__PURE__ */ jsx(ListScrollbarStyles, { textTertiary: t.textTertiary }),
|
|
13351
13804
|
/* @__PURE__ */ jsx(
|
|
13352
13805
|
ScreenHeader,
|
|
13353
13806
|
{
|
|
@@ -13357,71 +13810,198 @@ function LinkTokensScreen({
|
|
|
13357
13810
|
}
|
|
13358
13811
|
),
|
|
13359
13812
|
/* @__PURE__ */ jsxs("div", { style: contentStyle10, children: [
|
|
13360
|
-
/* @__PURE__ */ jsx("
|
|
13361
|
-
/* @__PURE__ */ jsxs("div", {
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
{
|
|
13365
|
-
"
|
|
13366
|
-
"
|
|
13367
|
-
|
|
13368
|
-
children: [
|
|
13369
|
-
/* @__PURE__ */ jsx("div", { style: shimmerCircleStyle(t.bgInput, t.border) }),
|
|
13370
|
-
/* @__PURE__ */ jsxs("div", { style: shimmerInfoStyle, children: [
|
|
13371
|
-
/* @__PURE__ */ jsx("div", { style: shimmerLineStyle(72, t.bgInput, t.border) }),
|
|
13372
|
-
/* @__PURE__ */ jsx("div", { style: shimmerLineStyle(48, t.bgInput, t.border) })
|
|
13373
|
-
] }),
|
|
13813
|
+
/* @__PURE__ */ jsx("div", { style: flexSpacerStyle }),
|
|
13814
|
+
/* @__PURE__ */ jsxs("div", { style: topGroupStyle, children: [
|
|
13815
|
+
/* @__PURE__ */ jsx("h2", { style: headingStyle7(t.text, t.fontWeightBold), children: "Let your passkey spend" }),
|
|
13816
|
+
/* @__PURE__ */ jsxs("div", { style: pillsGroupStyle, children: [
|
|
13817
|
+
showShimmer ? /* @__PURE__ */ jsxs("div", { "data-testid": "link-tokens-summary-shimmer", "aria-hidden": "true", style: pillStyle3(t.bgRecessed), children: [
|
|
13818
|
+
/* @__PURE__ */ jsx("div", { style: shimmerCircleStyle(t.bgInput, t.border) }),
|
|
13819
|
+
/* @__PURE__ */ jsxs("div", { style: shimmerInfoStyle, children: [
|
|
13820
|
+
/* @__PURE__ */ jsx("div", { style: shimmerLineStyle(96, t.bgInput, t.border) }),
|
|
13374
13821
|
/* @__PURE__ */ jsx("div", { style: shimmerLineStyle(56, t.bgInput, t.border) })
|
|
13375
|
-
]
|
|
13376
|
-
}
|
|
13377
|
-
|
|
13378
|
-
|
|
13379
|
-
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
|
|
13822
|
+
] })
|
|
13823
|
+
] }) : selected ? /* @__PURE__ */ jsxs(
|
|
13824
|
+
"button",
|
|
13825
|
+
{
|
|
13826
|
+
type: "button",
|
|
13827
|
+
onClick: openSelectToken,
|
|
13828
|
+
style: pillButtonStyle(t.bgRecessed),
|
|
13829
|
+
"aria-label": `Selected token ${selected.tokenSymbol} on ${selected.chainName}. Change token.`,
|
|
13830
|
+
"data-testid": "link-tokens-selected-pill",
|
|
13831
|
+
children: [
|
|
13832
|
+
/* @__PURE__ */ jsx(TokenPillLogo, { entry: selected, fallbackBg: t.bgRecessed, fallbackColor: t.textMuted }),
|
|
13833
|
+
/* @__PURE__ */ jsxs("span", { style: pillTextColStyle, children: [
|
|
13834
|
+
/* @__PURE__ */ jsxs("span", { style: pillTitleStyle(t.text, t.fontWeightRegular), children: [
|
|
13835
|
+
selected.tokenSymbol,
|
|
13836
|
+
" on ",
|
|
13837
|
+
selected.chainName
|
|
13838
|
+
] }),
|
|
13839
|
+
/* @__PURE__ */ jsx("span", { style: pillSubStyle(t.textMuted, t.fontWeightRegular), children: selected.balanceLabel ?? `$${formatUsdTwoDecimals2(selected.balanceUsd)}` })
|
|
13840
|
+
] }),
|
|
13841
|
+
/* @__PURE__ */ jsx("span", { style: pillChevronStyle(t.textMuted), children: /* @__PURE__ */ jsx(ChevronDownIcon, { color: t.textMuted }) })
|
|
13842
|
+
]
|
|
13843
|
+
}
|
|
13844
|
+
) : null,
|
|
13845
|
+
editing ? /* @__PURE__ */ jsxs("div", { style: pillStyle3(t.bgRecessed), children: [
|
|
13846
|
+
/* @__PURE__ */ jsx("span", { style: limitEditLabelStyle(t.text, t.fontWeightRegular), children: "Spending limit" }),
|
|
13847
|
+
/* @__PURE__ */ jsxs("div", { style: limitEditRightStyle, children: [
|
|
13848
|
+
/* @__PURE__ */ jsxs("span", { style: limitAmountStyle(t.text, t.fontWeightRegular), children: [
|
|
13849
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: limitDollarStyle(isUnlimited, t.text), children: "$" }),
|
|
13850
|
+
/* @__PURE__ */ jsx(
|
|
13851
|
+
"input",
|
|
13852
|
+
{
|
|
13853
|
+
type: "text",
|
|
13854
|
+
inputMode: "decimal",
|
|
13855
|
+
autoFocus: true,
|
|
13856
|
+
placeholder: "0",
|
|
13857
|
+
"aria-label": "Lifetime spending limit",
|
|
13858
|
+
value: limit === "unlimited" ? "" : limit,
|
|
13859
|
+
onChange: (e) => setLimit(sanitizeRawInput(e.target.value)),
|
|
13860
|
+
onBlur: () => setEditing(false),
|
|
13861
|
+
"data-testid": "link-tokens-limit-input",
|
|
13862
|
+
style: limitInputStyle(t.text, t.textMuted)
|
|
13863
|
+
}
|
|
13864
|
+
)
|
|
13865
|
+
] }),
|
|
13866
|
+
/* @__PURE__ */ jsx(
|
|
13867
|
+
"button",
|
|
13868
|
+
{
|
|
13869
|
+
type: "button",
|
|
13870
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
13871
|
+
onClick: () => {
|
|
13872
|
+
setLimit("unlimited");
|
|
13873
|
+
setEditing(false);
|
|
13874
|
+
},
|
|
13875
|
+
style: limitResetStyle(t.border, t.textMuted, t.fontWeightMedium),
|
|
13876
|
+
"data-testid": "link-tokens-limit-reset",
|
|
13877
|
+
children: "Unlimited"
|
|
13878
|
+
}
|
|
13879
|
+
)
|
|
13880
|
+
] })
|
|
13881
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
13882
|
+
"button",
|
|
13883
|
+
{
|
|
13884
|
+
type: "button",
|
|
13885
|
+
onClick: () => setEditing(true),
|
|
13886
|
+
style: pillButtonStyle(t.bgRecessed),
|
|
13887
|
+
"aria-label": `Lifetime spending limit: ${limitLabel}. Edit.`,
|
|
13888
|
+
"data-testid": "link-tokens-limit-pill",
|
|
13889
|
+
children: [
|
|
13890
|
+
/* @__PURE__ */ jsx("span", { style: pillTitleStyle(t.text, t.fontWeightRegular), children: "Lifetime spending limit" }),
|
|
13891
|
+
/* @__PURE__ */ jsxs("span", { style: limitValueStyle, children: [
|
|
13892
|
+
/* @__PURE__ */ jsx("span", { style: pillSubStyle(isUnlimited ? t.textMuted : t.text, t.fontWeightRegular), children: limitLabel }),
|
|
13893
|
+
/* @__PURE__ */ jsx("span", { style: pillChevronStyle(t.textMuted), children: /* @__PURE__ */ jsx(PencilIcon, { size: 16 }) })
|
|
13894
|
+
] })
|
|
13895
|
+
]
|
|
13896
|
+
}
|
|
13897
|
+
)
|
|
13898
|
+
] })
|
|
13393
13899
|
] }),
|
|
13394
|
-
|
|
13900
|
+
/* @__PURE__ */ jsx("div", { style: flexSpacerStyle }),
|
|
13901
|
+
/* @__PURE__ */ jsxs("div", { style: bottomGroupStyle, children: [
|
|
13902
|
+
/* @__PURE__ */ jsxs("div", { style: infoCardStyle3(t.bgRecessed), children: [
|
|
13903
|
+
/* @__PURE__ */ jsx(
|
|
13904
|
+
InfoRow,
|
|
13905
|
+
{
|
|
13906
|
+
icon: /* @__PURE__ */ jsx(LockIcon, { size: 22 }),
|
|
13907
|
+
title: "Blink is non-custodial",
|
|
13908
|
+
body: "Your passkey moves money, not Blink.",
|
|
13909
|
+
t
|
|
13910
|
+
}
|
|
13911
|
+
),
|
|
13912
|
+
/* @__PURE__ */ jsx(
|
|
13913
|
+
InfoRow,
|
|
13914
|
+
{
|
|
13915
|
+
icon: /* @__PURE__ */ jsx(FaceIdIcon, { size: 22 }),
|
|
13916
|
+
title: "Every transaction is signed",
|
|
13917
|
+
body: "Nothing can move without your direct approval.",
|
|
13918
|
+
t
|
|
13919
|
+
}
|
|
13920
|
+
)
|
|
13921
|
+
] }),
|
|
13922
|
+
error && /* @__PURE__ */ jsx("div", { style: bannerSlotStyle, children: /* @__PURE__ */ jsx(NotificationBanner, { title: "Something went wrong", description: error }) })
|
|
13923
|
+
] })
|
|
13395
13924
|
] })
|
|
13396
13925
|
]
|
|
13397
13926
|
}
|
|
13398
13927
|
);
|
|
13399
13928
|
}
|
|
13400
|
-
function
|
|
13401
|
-
|
|
13929
|
+
function TokenPillLogo({
|
|
13930
|
+
entry,
|
|
13931
|
+
fallbackBg,
|
|
13932
|
+
fallbackColor
|
|
13933
|
+
}) {
|
|
13934
|
+
const logo = entry.tokenLogoUri ?? TOKEN_LOGOS[entry.tokenSymbol];
|
|
13935
|
+
return /* @__PURE__ */ jsx("span", { style: pillLogoSlotStyle, children: logo ? /* @__PURE__ */ jsx("img", { src: logo, alt: "", "aria-hidden": "true", style: pillLogoImgStyle }) : /* @__PURE__ */ jsx("span", { style: pillLogoFallbackStyle(fallbackBg, fallbackColor), children: entry.tokenSymbol.charAt(0).toUpperCase() }) });
|
|
13402
13936
|
}
|
|
13403
|
-
function
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
|
|
13937
|
+
function InfoRow({
|
|
13938
|
+
icon,
|
|
13939
|
+
title,
|
|
13940
|
+
body,
|
|
13941
|
+
t
|
|
13942
|
+
}) {
|
|
13943
|
+
return /* @__PURE__ */ jsxs("div", { style: infoRowStyle2, children: [
|
|
13944
|
+
/* @__PURE__ */ jsx("span", { style: infoIconStyle(t.text), children: icon }),
|
|
13945
|
+
/* @__PURE__ */ jsxs("span", { style: infoTextColStyle, children: [
|
|
13946
|
+
/* @__PURE__ */ jsx("span", { style: infoTitleStyle2(t.text, t.fontWeightSemibold), children: title }),
|
|
13947
|
+
/* @__PURE__ */ jsx("span", { style: infoBodyStyle(t.textMuted, t.fontWeightMedium), children: body })
|
|
13948
|
+
] })
|
|
13949
|
+
] });
|
|
13950
|
+
}
|
|
13951
|
+
function ListScrollbarStyles({ textTertiary }) {
|
|
13952
|
+
return /* @__PURE__ */ jsx("style", { children: `
|
|
13953
|
+
@keyframes blink-link-tokens-shimmer {
|
|
13954
|
+
0% { background-position: 200% 0; }
|
|
13955
|
+
100% { background-position: -200% 0; }
|
|
13956
|
+
}
|
|
13957
|
+
.blink-link-tokens-list {
|
|
13958
|
+
scrollbar-width: thin;
|
|
13959
|
+
scrollbar-color: ${textTertiary} transparent;
|
|
13960
|
+
}
|
|
13961
|
+
.blink-link-tokens-list::-webkit-scrollbar { width: 4px; }
|
|
13962
|
+
.blink-link-tokens-list::-webkit-scrollbar-track {
|
|
13963
|
+
background: transparent;
|
|
13964
|
+
margin: 8px 0;
|
|
13965
|
+
}
|
|
13966
|
+
.blink-link-tokens-list::-webkit-scrollbar-thumb {
|
|
13967
|
+
background: ${textTertiary};
|
|
13968
|
+
border-radius: 999px;
|
|
13969
|
+
}
|
|
13970
|
+
input[data-testid="link-tokens-limit-input"]::placeholder {
|
|
13971
|
+
color: var(--link-limit-placeholder);
|
|
13972
|
+
opacity: 1;
|
|
13973
|
+
}
|
|
13974
|
+
` });
|
|
13414
13975
|
}
|
|
13415
13976
|
var contentStyle10 = {
|
|
13416
13977
|
display: "flex",
|
|
13417
13978
|
flexDirection: "column",
|
|
13418
|
-
alignItems: "center",
|
|
13419
|
-
gap: 16,
|
|
13420
|
-
paddingTop: 8,
|
|
13421
13979
|
flex: 1,
|
|
13422
13980
|
minHeight: 0,
|
|
13423
13981
|
width: "100%"
|
|
13424
13982
|
};
|
|
13983
|
+
var flexSpacerStyle = {
|
|
13984
|
+
flex: 1,
|
|
13985
|
+
minHeight: 16
|
|
13986
|
+
};
|
|
13987
|
+
var topGroupStyle = {
|
|
13988
|
+
display: "flex",
|
|
13989
|
+
flexDirection: "column",
|
|
13990
|
+
gap: 24,
|
|
13991
|
+
width: "100%"
|
|
13992
|
+
};
|
|
13993
|
+
var bottomGroupStyle = {
|
|
13994
|
+
display: "flex",
|
|
13995
|
+
flexDirection: "column",
|
|
13996
|
+
gap: 12,
|
|
13997
|
+
width: "100%"
|
|
13998
|
+
};
|
|
13999
|
+
var pillsGroupStyle = {
|
|
14000
|
+
display: "flex",
|
|
14001
|
+
flexDirection: "column",
|
|
14002
|
+
gap: 8,
|
|
14003
|
+
width: "100%"
|
|
14004
|
+
};
|
|
13425
14005
|
var wordmarkImgStyle3 = {
|
|
13426
14006
|
height: 22,
|
|
13427
14007
|
width: "auto",
|
|
@@ -13429,9 +14009,9 @@ var wordmarkImgStyle3 = {
|
|
|
13429
14009
|
pointerEvents: "none",
|
|
13430
14010
|
userSelect: "none"
|
|
13431
14011
|
};
|
|
13432
|
-
var headingStyle7 = (color) => ({
|
|
14012
|
+
var headingStyle7 = (color, weight) => ({
|
|
13433
14013
|
fontSize: 24,
|
|
13434
|
-
fontWeight:
|
|
14014
|
+
fontWeight: weight,
|
|
13435
14015
|
lineHeight: "normal",
|
|
13436
14016
|
letterSpacing: 0,
|
|
13437
14017
|
color,
|
|
@@ -13451,68 +14031,155 @@ var listCardStyle = (bg) => ({
|
|
|
13451
14031
|
minHeight: 0,
|
|
13452
14032
|
overflowY: "auto"
|
|
13453
14033
|
});
|
|
13454
|
-
var
|
|
14034
|
+
var pillStyle3 = (bg) => ({
|
|
13455
14035
|
display: "flex",
|
|
13456
14036
|
alignItems: "center",
|
|
13457
14037
|
gap: 16,
|
|
13458
|
-
padding: "12px 16px 12px 12px",
|
|
13459
|
-
background: "transparent",
|
|
13460
|
-
border: "none",
|
|
13461
|
-
borderRadius: 16,
|
|
13462
14038
|
width: "100%",
|
|
13463
|
-
boxSizing: "border-box"
|
|
14039
|
+
boxSizing: "border-box",
|
|
14040
|
+
padding: "14px 16px",
|
|
14041
|
+
borderRadius: 20,
|
|
14042
|
+
background: bg
|
|
14043
|
+
});
|
|
14044
|
+
var pillButtonStyle = (bg) => ({
|
|
14045
|
+
...pillStyle3(bg),
|
|
14046
|
+
border: "none",
|
|
14047
|
+
cursor: "pointer",
|
|
14048
|
+
fontFamily: "inherit",
|
|
14049
|
+
textAlign: "left"
|
|
14050
|
+
});
|
|
14051
|
+
var pillTextColStyle = {
|
|
14052
|
+
display: "flex",
|
|
14053
|
+
flexDirection: "column",
|
|
14054
|
+
flex: 1,
|
|
14055
|
+
minWidth: 0,
|
|
14056
|
+
gap: 2
|
|
13464
14057
|
};
|
|
13465
|
-
var
|
|
14058
|
+
var pillTitleStyle = (color, weight) => ({
|
|
14059
|
+
fontSize: "1rem",
|
|
14060
|
+
fontWeight: weight,
|
|
14061
|
+
lineHeight: "normal",
|
|
14062
|
+
color,
|
|
14063
|
+
whiteSpace: "nowrap",
|
|
14064
|
+
overflow: "hidden",
|
|
14065
|
+
textOverflow: "ellipsis"
|
|
14066
|
+
});
|
|
14067
|
+
var pillSubStyle = (color, weight) => ({
|
|
14068
|
+
fontSize: "0.95rem",
|
|
14069
|
+
fontWeight: weight,
|
|
14070
|
+
color
|
|
14071
|
+
});
|
|
14072
|
+
var pillChevronStyle = (color) => ({
|
|
14073
|
+
flexShrink: 0,
|
|
14074
|
+
display: "inline-flex",
|
|
14075
|
+
alignItems: "center",
|
|
14076
|
+
color
|
|
14077
|
+
});
|
|
14078
|
+
var limitValueStyle = {
|
|
14079
|
+
marginLeft: "auto",
|
|
14080
|
+
display: "inline-flex",
|
|
14081
|
+
alignItems: "center",
|
|
14082
|
+
gap: 8
|
|
14083
|
+
};
|
|
14084
|
+
var pillLogoSlotStyle = {
|
|
14085
|
+
position: "relative",
|
|
13466
14086
|
width: 40,
|
|
13467
14087
|
height: 40,
|
|
13468
|
-
borderRadius: "50%",
|
|
13469
14088
|
flexShrink: 0,
|
|
13470
|
-
|
|
13471
|
-
|
|
13472
|
-
|
|
14089
|
+
display: "inline-flex",
|
|
14090
|
+
alignItems: "center",
|
|
14091
|
+
justifyContent: "center"
|
|
14092
|
+
};
|
|
14093
|
+
var pillLogoImgStyle = {
|
|
14094
|
+
width: "100%",
|
|
14095
|
+
height: "100%",
|
|
14096
|
+
borderRadius: "50%",
|
|
14097
|
+
objectFit: "cover"
|
|
14098
|
+
};
|
|
14099
|
+
var pillLogoFallbackStyle = (bg, color) => ({
|
|
14100
|
+
width: "100%",
|
|
14101
|
+
height: "100%",
|
|
14102
|
+
borderRadius: "50%",
|
|
14103
|
+
background: bg,
|
|
14104
|
+
color,
|
|
14105
|
+
display: "inline-flex",
|
|
14106
|
+
alignItems: "center",
|
|
14107
|
+
justifyContent: "center",
|
|
14108
|
+
fontSize: "0.95rem",
|
|
14109
|
+
fontWeight: 700
|
|
13473
14110
|
});
|
|
13474
|
-
var
|
|
14111
|
+
var limitEditLabelStyle = (color, weight) => ({
|
|
14112
|
+
fontSize: "1rem",
|
|
14113
|
+
fontWeight: weight,
|
|
14114
|
+
lineHeight: "normal",
|
|
14115
|
+
color,
|
|
14116
|
+
whiteSpace: "nowrap",
|
|
14117
|
+
flexShrink: 0
|
|
14118
|
+
});
|
|
14119
|
+
var limitEditRightStyle = {
|
|
14120
|
+
marginLeft: "auto",
|
|
13475
14121
|
display: "flex",
|
|
13476
|
-
|
|
13477
|
-
gap:
|
|
13478
|
-
flex: 1,
|
|
14122
|
+
alignItems: "center",
|
|
14123
|
+
gap: 8,
|
|
13479
14124
|
minWidth: 0
|
|
13480
14125
|
};
|
|
13481
|
-
var
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
14126
|
+
var limitAmountStyle = (color, weight) => ({
|
|
14127
|
+
display: "inline-flex",
|
|
14128
|
+
alignItems: "baseline",
|
|
14129
|
+
gap: 1,
|
|
14130
|
+
color,
|
|
14131
|
+
fontSize: "1rem",
|
|
14132
|
+
fontWeight: weight
|
|
13488
14133
|
});
|
|
13489
|
-
var
|
|
13490
|
-
|
|
13491
|
-
|
|
14134
|
+
var limitDollarStyle = (isZero, color) => ({
|
|
14135
|
+
opacity: isZero ? 0.5 : 1,
|
|
14136
|
+
color
|
|
14137
|
+
});
|
|
14138
|
+
var limitInputStyle = (color, mutedColor) => ({
|
|
14139
|
+
background: "transparent",
|
|
14140
|
+
border: "none",
|
|
14141
|
+
outline: "none",
|
|
13492
14142
|
color,
|
|
13493
|
-
|
|
14143
|
+
font: "inherit",
|
|
14144
|
+
textAlign: "right",
|
|
14145
|
+
padding: 0,
|
|
14146
|
+
margin: 0,
|
|
14147
|
+
width: "auto",
|
|
14148
|
+
minWidth: "1ch",
|
|
14149
|
+
...{ fieldSizing: "content" },
|
|
14150
|
+
fontVariantNumeric: "tabular-nums",
|
|
14151
|
+
caretColor: color,
|
|
14152
|
+
["--link-limit-placeholder"]: mutedColor
|
|
13494
14153
|
});
|
|
13495
|
-
var
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
14154
|
+
var limitResetStyle = (borderColor, color, weight) => ({
|
|
14155
|
+
flexShrink: 0,
|
|
14156
|
+
padding: "5px 12px",
|
|
14157
|
+
borderRadius: 999,
|
|
14158
|
+
fontSize: "0.8rem",
|
|
14159
|
+
fontWeight: weight,
|
|
14160
|
+
fontFamily: "inherit",
|
|
14161
|
+
cursor: "pointer",
|
|
14162
|
+
border: `1.5px solid ${borderColor}`,
|
|
14163
|
+
background: "transparent",
|
|
14164
|
+
color
|
|
14165
|
+
});
|
|
14166
|
+
var infoCardStyle3 = (bg) => ({
|
|
13501
14167
|
display: "flex",
|
|
13502
14168
|
flexDirection: "column",
|
|
13503
14169
|
gap: 16,
|
|
13504
|
-
width: "100%"
|
|
13505
|
-
|
|
13506
|
-
|
|
14170
|
+
width: "100%",
|
|
14171
|
+
boxSizing: "border-box",
|
|
14172
|
+
padding: 16,
|
|
14173
|
+
borderRadius: 20,
|
|
14174
|
+
background: bg
|
|
14175
|
+
});
|
|
14176
|
+
var infoRowStyle2 = {
|
|
13507
14177
|
display: "flex",
|
|
13508
14178
|
alignItems: "flex-start",
|
|
13509
|
-
gap:
|
|
13510
|
-
|
|
13511
|
-
borderRadius: 16,
|
|
13512
|
-
width: "100%",
|
|
13513
|
-
boxSizing: "border-box"
|
|
14179
|
+
gap: 14,
|
|
14180
|
+
width: "100%"
|
|
13514
14181
|
};
|
|
13515
|
-
var
|
|
14182
|
+
var infoIconStyle = (color) => ({
|
|
13516
14183
|
flexShrink: 0,
|
|
13517
14184
|
width: 24,
|
|
13518
14185
|
height: 24,
|
|
@@ -13521,14 +14188,60 @@ var lockIconWrapStyle2 = (color) => ({
|
|
|
13521
14188
|
justifyContent: "center",
|
|
13522
14189
|
color
|
|
13523
14190
|
});
|
|
13524
|
-
var
|
|
13525
|
-
|
|
13526
|
-
|
|
13527
|
-
|
|
13528
|
-
|
|
13529
|
-
|
|
13530
|
-
|
|
14191
|
+
var infoTextColStyle = {
|
|
14192
|
+
display: "flex",
|
|
14193
|
+
flexDirection: "column",
|
|
14194
|
+
gap: 2,
|
|
14195
|
+
flex: 1,
|
|
14196
|
+
minWidth: 0
|
|
14197
|
+
};
|
|
14198
|
+
var infoTitleStyle2 = (color, weight) => ({
|
|
14199
|
+
fontSize: "1rem",
|
|
14200
|
+
fontWeight: weight,
|
|
14201
|
+
lineHeight: "normal",
|
|
14202
|
+
color
|
|
14203
|
+
});
|
|
14204
|
+
var infoBodyStyle = (color, weight) => ({
|
|
14205
|
+
fontSize: "0.85rem",
|
|
14206
|
+
fontWeight: weight,
|
|
14207
|
+
lineHeight: 1.4,
|
|
14208
|
+
color
|
|
14209
|
+
});
|
|
14210
|
+
var shimmerCircleStyle = (baseColor, highlightColor) => ({
|
|
14211
|
+
width: 40,
|
|
14212
|
+
height: 40,
|
|
14213
|
+
borderRadius: "50%",
|
|
14214
|
+
flexShrink: 0,
|
|
14215
|
+
background: `linear-gradient(90deg, ${baseColor} 0%, ${highlightColor} 50%, ${baseColor} 100%)`,
|
|
14216
|
+
backgroundSize: "200% 100%",
|
|
14217
|
+
animation: "blink-link-tokens-shimmer 1.4s ease-in-out infinite"
|
|
14218
|
+
});
|
|
14219
|
+
var shimmerInfoStyle = {
|
|
14220
|
+
display: "flex",
|
|
14221
|
+
flexDirection: "column",
|
|
14222
|
+
gap: 6,
|
|
14223
|
+
flex: 1,
|
|
14224
|
+
minWidth: 0
|
|
14225
|
+
};
|
|
14226
|
+
var shimmerLineStyle = (width, baseColor, highlightColor) => ({
|
|
14227
|
+
width,
|
|
14228
|
+
height: 12,
|
|
14229
|
+
borderRadius: 6,
|
|
14230
|
+
background: `linear-gradient(90deg, ${baseColor} 0%, ${highlightColor} 50%, ${baseColor} 100%)`,
|
|
14231
|
+
backgroundSize: "200% 100%",
|
|
14232
|
+
animation: "blink-link-tokens-shimmer 1.4s ease-in-out infinite"
|
|
14233
|
+
});
|
|
14234
|
+
var emptyStyle = (color) => ({
|
|
14235
|
+
padding: "32px 16px",
|
|
14236
|
+
textAlign: "center",
|
|
14237
|
+
color,
|
|
14238
|
+
fontSize: "0.92rem"
|
|
13531
14239
|
});
|
|
14240
|
+
var bannerSlotStyle = {
|
|
14241
|
+
display: "flex",
|
|
14242
|
+
justifyContent: "center",
|
|
14243
|
+
width: "100%"
|
|
14244
|
+
};
|
|
13532
14245
|
function DepositCompleteScreen({ amount }) {
|
|
13533
14246
|
const { tokens } = useBlinkConfig();
|
|
13534
14247
|
return /* @__PURE__ */ jsxs(ScreenLayout, { children: [
|
|
@@ -13590,6 +14303,7 @@ function SelectDepositSourceScreen({
|
|
|
13590
14303
|
tokenOptions,
|
|
13591
14304
|
selectedTokenSymbol,
|
|
13592
14305
|
selectedChainName,
|
|
14306
|
+
balancesLoading,
|
|
13593
14307
|
selectedWalletId,
|
|
13594
14308
|
onPickToken,
|
|
13595
14309
|
useDeeplink,
|
|
@@ -13606,8 +14320,9 @@ function SelectDepositSourceScreen({
|
|
|
13606
14320
|
const [pendingMobileSelection, setPendingMobileSelection] = useState(null);
|
|
13607
14321
|
const [preparedAuthorization, setPreparedAuthorization] = useState(null);
|
|
13608
14322
|
const [preparingAuthorization, setPreparingAuthorization] = useState(false);
|
|
14323
|
+
const isAuthorizedOption = (opt) => !opt.status || opt.status === "AUTHORIZED";
|
|
13609
14324
|
const selectableOptions = tokenOptions.filter(
|
|
13610
|
-
(opt) => opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minTransferAmountUsd)
|
|
14325
|
+
(opt) => isAuthorizedOption(opt) || opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minTransferAmountUsd)
|
|
13611
14326
|
);
|
|
13612
14327
|
const authorized = selectableOptions.filter(
|
|
13613
14328
|
(opt) => !opt.status || opt.status === "AUTHORIZED"
|
|
@@ -13761,6 +14476,30 @@ function SelectDepositSourceScreen({
|
|
|
13761
14476
|
"data-testid": "select-deposit-source-scroll-content",
|
|
13762
14477
|
style: contentStyle12,
|
|
13763
14478
|
children: [
|
|
14479
|
+
balancesLoading && tokenOptions.length === 0 && /* @__PURE__ */ jsx("div", { style: accountSectionStyle, "data-testid": "select-source-shimmer", children: /* @__PURE__ */ jsx("div", { style: groupCardStyle(tokens.bgRecessed), children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxs(
|
|
14480
|
+
"div",
|
|
14481
|
+
{
|
|
14482
|
+
style: { display: "flex", alignItems: "center", gap: 12, padding: 12 },
|
|
14483
|
+
children: [
|
|
14484
|
+
/* @__PURE__ */ jsx(
|
|
14485
|
+
Shimmer,
|
|
14486
|
+
{
|
|
14487
|
+
width: 36,
|
|
14488
|
+
height: 36,
|
|
14489
|
+
borderRadius: "50%",
|
|
14490
|
+
baseColor: tokens.bgInput,
|
|
14491
|
+
highlightColor: tokens.border
|
|
14492
|
+
}
|
|
14493
|
+
),
|
|
14494
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 6, flex: 1 }, children: [
|
|
14495
|
+
/* @__PURE__ */ jsx(Shimmer, { width: 96, height: 12, baseColor: tokens.bgInput, highlightColor: tokens.border }),
|
|
14496
|
+
/* @__PURE__ */ jsx(Shimmer, { width: 56, height: 10, baseColor: tokens.bgInput, highlightColor: tokens.border })
|
|
14497
|
+
] }),
|
|
14498
|
+
/* @__PURE__ */ jsx(Shimmer, { width: 48, height: 12, baseColor: tokens.bgInput, highlightColor: tokens.border })
|
|
14499
|
+
]
|
|
14500
|
+
},
|
|
14501
|
+
`shimmer-row-${i}`
|
|
14502
|
+
)) }) }),
|
|
13764
14503
|
orderedAccounts.map((account) => {
|
|
13765
14504
|
const authRows = authorized.filter((opt) => rowMatchesSection(opt, account));
|
|
13766
14505
|
const reqRows = requiresAuth.filter((opt) => rowMatchesSection(opt, account)).sort((a, b) => Number(!!a.notSupported) - Number(!!b.notSupported));
|
|
@@ -13975,6 +14714,7 @@ function ShimmerBlock({
|
|
|
13975
14714
|
);
|
|
13976
14715
|
}
|
|
13977
14716
|
function DepositScreen({
|
|
14717
|
+
balancesLoading,
|
|
13978
14718
|
availableBalance,
|
|
13979
14719
|
remainingLimit,
|
|
13980
14720
|
initialAmount,
|
|
@@ -14028,6 +14768,10 @@ function DepositScreen({
|
|
|
14028
14768
|
const showMobileKeypad = isEntryMode && !isDesktop;
|
|
14029
14769
|
const [keypadOpen, setKeypadOpen] = useState(showMobileKeypad);
|
|
14030
14770
|
const [tokenPickerOpen, setTokenPickerOpen] = useState(false);
|
|
14771
|
+
const hasLoadedQuoteRef = useRef(false);
|
|
14772
|
+
if (!quoteLoading) {
|
|
14773
|
+
hasLoadedQuoteRef.current = true;
|
|
14774
|
+
}
|
|
14031
14775
|
const selectableTokenOptions = tokenOptions?.filter(
|
|
14032
14776
|
(opt) => opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minDepositFloor)
|
|
14033
14777
|
) ?? [];
|
|
@@ -14045,11 +14789,11 @@ function DepositScreen({
|
|
|
14045
14789
|
}, []);
|
|
14046
14790
|
const selectedAccount = accounts?.find((a) => a.id === selectedAccountId);
|
|
14047
14791
|
const selectedProviderName = selectedAccount?.name ?? "Wallet";
|
|
14048
|
-
const isLowBalance = availableBalance < minDepositFloor;
|
|
14049
|
-
const insufficientFunds = availableBalance < validationAmount;
|
|
14792
|
+
const isLowBalance = !balancesLoading && availableBalance < minDepositFloor;
|
|
14793
|
+
const insufficientFunds = !balancesLoading && availableBalance < validationAmount;
|
|
14050
14794
|
const needsAuthorization = selectedTokenStatus != null && selectedTokenStatus !== "AUTHORIZED" && !insufficientFunds && !isLowBalance;
|
|
14051
14795
|
const exceedsLimit = remainingLimit != null && validationAmount > remainingLimit && !isLowBalance && !needsAuthorization;
|
|
14052
|
-
const canDeposit = validationAmount >= minDepositFloor && !exceedsLimit && !isLowBalance && !insufficientFunds && !needsAuthorization && !processing;
|
|
14796
|
+
const canDeposit = validationAmount >= minDepositFloor && !balancesLoading && !exceedsLimit && !isLowBalance && !insufficientFunds && !needsAuthorization && !processing;
|
|
14053
14797
|
const hasAccountPill = !!accounts && accounts.length > 0;
|
|
14054
14798
|
const pillClickable = canOpenInlineSheet || !!onSelectToken;
|
|
14055
14799
|
const formattedBalance = selectedTokenSymbol != null ? `$${formatUsdTwoDecimals2(availableBalance)}` : void 0;
|
|
@@ -14072,6 +14816,7 @@ function DepositScreen({
|
|
|
14072
14816
|
accounts: depositSourceAccounts,
|
|
14073
14817
|
selectedAccountId: selectedAccountId ?? null,
|
|
14074
14818
|
tokenOptions: selectableTokenOptions,
|
|
14819
|
+
balancesLoading,
|
|
14075
14820
|
selectedTokenSymbol,
|
|
14076
14821
|
selectedChainName,
|
|
14077
14822
|
selectedWalletId: selectedWalletId ?? null,
|
|
@@ -14093,7 +14838,7 @@ function DepositScreen({
|
|
|
14093
14838
|
const mobileEntryWithKeypad = isEntryMode && !isDesktop && keypadOpen;
|
|
14094
14839
|
const showLiveMobileHero = isEntryMode && !isDesktop && keypadOpen;
|
|
14095
14840
|
const showDesktopInputHero = isEntryMode && isDesktop;
|
|
14096
|
-
const
|
|
14841
|
+
const showFeeRow = validationAmount > 0;
|
|
14097
14842
|
let primaryButton;
|
|
14098
14843
|
if (mobileEntryWithKeypad) {
|
|
14099
14844
|
const canConfirmEntry = canContinue(amountEntryValue ?? "");
|
|
@@ -14223,7 +14968,32 @@ function DepositScreen({
|
|
|
14223
14968
|
"aria-label": tokenAriaLabel,
|
|
14224
14969
|
"aria-expanded": canOpenInlineSheet ? tokenPickerOpen : void 0,
|
|
14225
14970
|
"aria-haspopup": canOpenInlineSheet ? "listbox" : void 0,
|
|
14226
|
-
children:
|
|
14971
|
+
children: balancesLoading ? (
|
|
14972
|
+
// Until balances merge in we don't know the selected token or its
|
|
14973
|
+
// balance, so the whole pill (icon + name + balance) is a shimmer
|
|
14974
|
+
// placeholder rather than showing the account name as a default.
|
|
14975
|
+
// Render through SourcePill so the placeholder inherits the exact
|
|
14976
|
+
// same container (fill, radius, padding, chevron) as the loaded
|
|
14977
|
+
// pill and does not change size when balances arrive.
|
|
14978
|
+
/* @__PURE__ */ jsx(
|
|
14979
|
+
"span",
|
|
14980
|
+
{
|
|
14981
|
+
"data-testid": "deposit-pill-shimmer",
|
|
14982
|
+
"aria-label": "Loading balance",
|
|
14983
|
+
"aria-busy": "true",
|
|
14984
|
+
style: { display: "inline-flex" },
|
|
14985
|
+
children: /* @__PURE__ */ jsx(
|
|
14986
|
+
SourcePill,
|
|
14987
|
+
{
|
|
14988
|
+
icon: /* @__PURE__ */ jsx(Shimmer, { width: 28, height: 28, borderRadius: "50%", baseColor: tokens.bgInput, highlightColor: tokens.border }),
|
|
14989
|
+
nameSlot: /* @__PURE__ */ jsx(Shimmer, { width: 72, height: 12, baseColor: tokens.bgInput, highlightColor: tokens.border }),
|
|
14990
|
+
balanceSlot: /* @__PURE__ */ jsx(Shimmer, { width: 48, height: 12, baseColor: tokens.bgInput, highlightColor: tokens.border }),
|
|
14991
|
+
showChevron: pillClickable
|
|
14992
|
+
}
|
|
14993
|
+
)
|
|
14994
|
+
}
|
|
14995
|
+
)
|
|
14996
|
+
) : /* @__PURE__ */ jsx(
|
|
14227
14997
|
SourcePill,
|
|
14228
14998
|
{
|
|
14229
14999
|
icon: /* @__PURE__ */ jsx(
|
|
@@ -14243,7 +15013,7 @@ function DepositScreen({
|
|
|
14243
15013
|
)
|
|
14244
15014
|
}
|
|
14245
15015
|
),
|
|
14246
|
-
|
|
15016
|
+
/* @__PURE__ */ jsx("div", { "data-testid": "deposit-fee-row", style: redesignFeeRowStyle, children: showFeeRow && (quoteLoading && !hasLoadedQuoteRef.current ? /* @__PURE__ */ jsx(
|
|
14247
15017
|
"div",
|
|
14248
15018
|
{
|
|
14249
15019
|
"data-testid": "deposit-fee-shimmer",
|
|
@@ -14261,7 +15031,7 @@ function DepositScreen({
|
|
|
14261
15031
|
}
|
|
14262
15032
|
)
|
|
14263
15033
|
}
|
|
14264
|
-
) : /* @__PURE__ */ jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" }) })
|
|
15034
|
+
) : /* @__PURE__ */ jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" })) })
|
|
14265
15035
|
] }),
|
|
14266
15036
|
/* @__PURE__ */ jsx("div", { style: bannerSlotStyle2, children: mobileEntryWithKeypad ? null : showLowFunds ? /* @__PURE__ */ jsx(
|
|
14267
15037
|
NotificationBanner,
|
|
@@ -15638,7 +16408,7 @@ function OpenWalletScreen({
|
|
|
15638
16408
|
return /* @__PURE__ */ jsxs(
|
|
15639
16409
|
ScreenLayout,
|
|
15640
16410
|
{
|
|
15641
|
-
footer: /* @__PURE__ */ jsxs("div", { style:
|
|
16411
|
+
footer: /* @__PURE__ */ jsxs("div", { style: footerStackStyle4, children: [
|
|
15642
16412
|
error && /* @__PURE__ */ jsx(InfoBanner, { children: error }),
|
|
15643
16413
|
/* @__PURE__ */ jsxs(
|
|
15644
16414
|
SecondaryButton,
|
|
@@ -15685,7 +16455,7 @@ function OpenWalletScreen({
|
|
|
15685
16455
|
return /* @__PURE__ */ jsxs(
|
|
15686
16456
|
ScreenLayout,
|
|
15687
16457
|
{
|
|
15688
|
-
footer: /* @__PURE__ */ jsxs("div", { style:
|
|
16458
|
+
footer: /* @__PURE__ */ jsxs("div", { style: footerStackStyle4, children: [
|
|
15689
16459
|
error && /* @__PURE__ */ jsx(InfoBanner, { children: error }),
|
|
15690
16460
|
/* @__PURE__ */ jsxs(
|
|
15691
16461
|
SecondaryButton,
|
|
@@ -15769,7 +16539,7 @@ var primaryClusterStyle = {
|
|
|
15769
16539
|
textAlign: "center",
|
|
15770
16540
|
gap: 12
|
|
15771
16541
|
};
|
|
15772
|
-
var
|
|
16542
|
+
var footerStackStyle4 = {
|
|
15773
16543
|
display: "flex",
|
|
15774
16544
|
flexDirection: "column",
|
|
15775
16545
|
gap: 8
|
|
@@ -15897,6 +16667,12 @@ function ApprovingInWalletScreen({
|
|
|
15897
16667
|
chainName,
|
|
15898
16668
|
step,
|
|
15899
16669
|
complete,
|
|
16670
|
+
signing,
|
|
16671
|
+
spendingLimitLabel,
|
|
16672
|
+
destinationAddress,
|
|
16673
|
+
tokenLogoUri,
|
|
16674
|
+
awaitingApproval,
|
|
16675
|
+
onApprove,
|
|
15900
16676
|
error,
|
|
15901
16677
|
onRetry,
|
|
15902
16678
|
onBack,
|
|
@@ -15906,10 +16682,11 @@ function ApprovingInWalletScreen({
|
|
|
15906
16682
|
onOpenWallet
|
|
15907
16683
|
}) {
|
|
15908
16684
|
const { tokens: t } = useBlinkConfig();
|
|
15909
|
-
const showDelayedRetry = useDelayedRetry(error == null && onRetry != null);
|
|
16685
|
+
const showDelayedRetry = useDelayedRetry(!awaitingApproval && error == null && onRetry != null);
|
|
15910
16686
|
const retryVisible = onRetry != null && (error != null || showDelayedRetry);
|
|
15911
16687
|
const token = tokenSymbol ?? "token";
|
|
15912
|
-
const
|
|
16688
|
+
const chain = chainName ?? "chain";
|
|
16689
|
+
const steps = buildSteps(step, tokenSymbol, complete, signing);
|
|
15913
16690
|
const foregroundNavigation = foregroundDeeplink ? classifyWalletDeeplinkNavigation(foregroundDeeplink) : null;
|
|
15914
16691
|
const foregroundWithJs = foregroundNavigation ? shouldOpenWithJavaScript(foregroundNavigation) : false;
|
|
15915
16692
|
const showOpenWalletButton = !!foregroundDeeplink && onOpenWallet != null;
|
|
@@ -15917,11 +16694,7 @@ function ApprovingInWalletScreen({
|
|
|
15917
16694
|
ScreenLayout,
|
|
15918
16695
|
{
|
|
15919
16696
|
scrollableBody: false,
|
|
15920
|
-
footer: /* @__PURE__ */ jsxs("div", { style:
|
|
15921
|
-
/* @__PURE__ */ jsxs("div", { style: lockBannerStyle3, children: [
|
|
15922
|
-
/* @__PURE__ */ jsx("span", { style: lockIconWrapStyle3(t.text), children: /* @__PURE__ */ jsx(LockIcon4, {}) }),
|
|
15923
|
-
/* @__PURE__ */ jsx("p", { style: lockBannerTextStyle3(t.text), children: "Your passkey is required each time you deposit. Funds cannot move without your approval." })
|
|
15924
|
-
] }),
|
|
16697
|
+
footer: /* @__PURE__ */ jsxs("div", { style: footerStackStyle5, children: [
|
|
15925
16698
|
showOpenWalletButton && /* @__PURE__ */ jsxs(
|
|
15926
16699
|
SecondaryButton,
|
|
15927
16700
|
{
|
|
@@ -15938,7 +16711,7 @@ function ApprovingInWalletScreen({
|
|
|
15938
16711
|
retryVisible ? /* @__PURE__ */ jsxs("div", { style: retryStackStyle, children: [
|
|
15939
16712
|
error && /* @__PURE__ */ jsx(InfoBanner, { children: error }),
|
|
15940
16713
|
/* @__PURE__ */ jsx(OutlineButton, { onClick: onRetry, children: "Retry" })
|
|
15941
|
-
] }) : /* @__PURE__ */ jsx(PrimaryButton, { spinnerOnly: true })
|
|
16714
|
+
] }) : awaitingApproval ? /* @__PURE__ */ jsx(PrimaryButton, { onClick: onApprove, children: "Approve" }) : /* @__PURE__ */ jsx(PrimaryButton, { spinnerOnly: true })
|
|
15942
16715
|
] }),
|
|
15943
16716
|
children: [
|
|
15944
16717
|
/* @__PURE__ */ jsx(
|
|
@@ -15950,7 +16723,39 @@ function ApprovingInWalletScreen({
|
|
|
15950
16723
|
}
|
|
15951
16724
|
),
|
|
15952
16725
|
/* @__PURE__ */ jsxs("div", { style: contentStyle16, children: [
|
|
15953
|
-
/* @__PURE__ */ jsx("h2", { style: headingStyle13(t.text), children:
|
|
16726
|
+
/* @__PURE__ */ jsx("h2", { style: headingStyle13(t.text, t.fontWeightBold), children: "Approve in wallet" }),
|
|
16727
|
+
/* @__PURE__ */ jsxs("div", { style: transferCardStyle(t.bgCardTranslucent, t.radiusLg), children: [
|
|
16728
|
+
/* @__PURE__ */ jsx(
|
|
16729
|
+
AddressRow,
|
|
16730
|
+
{
|
|
16731
|
+
circle: /* @__PURE__ */ jsx(
|
|
16732
|
+
LogoCircle,
|
|
16733
|
+
{
|
|
16734
|
+
src: tokenLogoUri ?? TOKEN_LOGOS[token] ?? void 0,
|
|
16735
|
+
fallback: token.slice(0, 1).toUpperCase(),
|
|
16736
|
+
size: 40
|
|
16737
|
+
}
|
|
16738
|
+
),
|
|
16739
|
+
title: `${token} on ${chain}`,
|
|
16740
|
+
subtitle: spendingLimitLabel
|
|
16741
|
+
}
|
|
16742
|
+
),
|
|
16743
|
+
/* @__PURE__ */ jsx("div", { style: arrowRowStyle, children: /* @__PURE__ */ jsx(ArrowDownIcon, { color: t.textMuted }) }),
|
|
16744
|
+
/* @__PURE__ */ jsx(
|
|
16745
|
+
AddressRow,
|
|
16746
|
+
{
|
|
16747
|
+
circle: /* @__PURE__ */ jsx("span", { style: passkeyCircleStyle(t.accent, t.highlight), children: /* @__PURE__ */ jsx(KeyIcon, { size: 22 }) }),
|
|
16748
|
+
title: "Your Blink Passkey",
|
|
16749
|
+
address: destinationAddress,
|
|
16750
|
+
addressLoading: destinationAddress == null
|
|
16751
|
+
}
|
|
16752
|
+
)
|
|
16753
|
+
] }),
|
|
16754
|
+
/* @__PURE__ */ jsxs("div", { style: dividerRowStyle3, children: [
|
|
16755
|
+
/* @__PURE__ */ jsx("span", { style: dividerLineStyle(t.textMuted) }),
|
|
16756
|
+
/* @__PURE__ */ jsx("span", { style: dividerLabelStyle3(t.textMuted, t.fontWeightSemibold), children: "Continue in wallet" }),
|
|
16757
|
+
/* @__PURE__ */ jsx("span", { style: dividerLineStyle(t.textMuted) })
|
|
16758
|
+
] }),
|
|
15954
16759
|
/* @__PURE__ */ jsxs("div", { style: cardStyle2(t.bgRecessed, t.radiusLg), children: [
|
|
15955
16760
|
/* @__PURE__ */ jsx("style", { children: SPIN_KEYFRAMES_CSS2 }),
|
|
15956
16761
|
steps.map((s, i) => /* @__PURE__ */ jsx(ChecklistRow, { step: s }, i))
|
|
@@ -15960,6 +16765,32 @@ function ApprovingInWalletScreen({
|
|
|
15960
16765
|
}
|
|
15961
16766
|
);
|
|
15962
16767
|
}
|
|
16768
|
+
function AddressRow({
|
|
16769
|
+
circle,
|
|
16770
|
+
title,
|
|
16771
|
+
address,
|
|
16772
|
+
subtitle,
|
|
16773
|
+
addressLoading
|
|
16774
|
+
}) {
|
|
16775
|
+
const { tokens: t } = useBlinkConfig();
|
|
16776
|
+
const sub = subtitle ?? (address ? truncateMiddle(address) : null);
|
|
16777
|
+
return /* @__PURE__ */ jsxs("div", { style: addressRowStyle, children: [
|
|
16778
|
+
circle,
|
|
16779
|
+
/* @__PURE__ */ jsxs("div", { style: addressTextColStyle, children: [
|
|
16780
|
+
/* @__PURE__ */ jsx("span", { style: addressTitleStyle(t.text, t.fontWeightSemibold), children: title }),
|
|
16781
|
+
(sub || addressLoading) && /* @__PURE__ */ jsx("span", { style: addressSubSlotStyle, children: sub ? /* @__PURE__ */ jsx("span", { style: addressSubStyle(t.textMuted, t.fontWeightRegular), children: sub }) : /* @__PURE__ */ jsx(
|
|
16782
|
+
Shimmer,
|
|
16783
|
+
{
|
|
16784
|
+
width: 132,
|
|
16785
|
+
height: 12,
|
|
16786
|
+
borderRadius: 4,
|
|
16787
|
+
baseColor: t.bgInput,
|
|
16788
|
+
highlightColor: t.border
|
|
16789
|
+
}
|
|
16790
|
+
) })
|
|
16791
|
+
] })
|
|
16792
|
+
] });
|
|
16793
|
+
}
|
|
15963
16794
|
function ChecklistRow({ step }) {
|
|
15964
16795
|
const { tokens: t } = useBlinkConfig();
|
|
15965
16796
|
const isComplete = step.status === "complete";
|
|
@@ -15972,7 +16803,7 @@ function ChecklistRow({ step }) {
|
|
|
15972
16803
|
fill: t.accentText
|
|
15973
16804
|
}
|
|
15974
16805
|
) }) }) : isActive ? /* @__PURE__ */ jsx(RowSpinner, { color: t.accent }) : /* @__PURE__ */ jsx("span", { style: pendingBadgeStyle(t.bgRecessed) }),
|
|
15975
|
-
/* @__PURE__ */ jsx("span", { style: labelStyle7(isComplete || isActive ? t.text : t.textMuted), children: step.label })
|
|
16806
|
+
/* @__PURE__ */ jsx("span", { style: labelStyle7(isComplete || isActive ? t.text : t.textMuted, t.fontWeightRegular), children: step.label })
|
|
15976
16807
|
] });
|
|
15977
16808
|
}
|
|
15978
16809
|
function RowSpinner({ color }) {
|
|
@@ -16007,22 +16838,28 @@ function RowSpinner({ color }) {
|
|
|
16007
16838
|
}
|
|
16008
16839
|
);
|
|
16009
16840
|
}
|
|
16010
|
-
function
|
|
16841
|
+
function ArrowDownIcon({ color }) {
|
|
16842
|
+
return /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", style: { color }, children: [
|
|
16843
|
+
/* @__PURE__ */ jsx("path", { d: "M12 5v14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
16844
|
+
/* @__PURE__ */ jsx("path", { d: "M6 13l6 6 6-6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
16845
|
+
] });
|
|
16846
|
+
}
|
|
16847
|
+
function buildSteps(step, tokenSymbol, complete, signing) {
|
|
16011
16848
|
const token = tokenSymbol ?? "token";
|
|
16012
|
-
const
|
|
16013
|
-
const
|
|
16849
|
+
const passkeyLabel = "Assign spending right to your passkey";
|
|
16850
|
+
const activeStatus = signing ? "active" : "pending";
|
|
16014
16851
|
if (step === "spl") {
|
|
16015
|
-
return [{ label: passkeyLabel, status: complete ? "complete" :
|
|
16852
|
+
return [{ label: passkeyLabel, status: complete ? "complete" : activeStatus }];
|
|
16016
16853
|
}
|
|
16017
|
-
const approveLabel = `Approve ${token}
|
|
16854
|
+
const approveLabel = `Approve ${token} for spending via Permit2`;
|
|
16018
16855
|
return [
|
|
16019
16856
|
{
|
|
16020
16857
|
label: approveLabel,
|
|
16021
|
-
status: complete || step !== "approve" ? "complete" :
|
|
16858
|
+
status: complete || step !== "approve" ? "complete" : activeStatus
|
|
16022
16859
|
},
|
|
16023
16860
|
{
|
|
16024
16861
|
label: passkeyLabel,
|
|
16025
|
-
status: complete ? "complete" : step === "approve" ? "pending" :
|
|
16862
|
+
status: complete ? "complete" : step === "approve" ? "pending" : activeStatus
|
|
16026
16863
|
}
|
|
16027
16864
|
];
|
|
16028
16865
|
}
|
|
@@ -16038,18 +16875,6 @@ function useDelayedRetry(enabled) {
|
|
|
16038
16875
|
}, [enabled]);
|
|
16039
16876
|
return visible;
|
|
16040
16877
|
}
|
|
16041
|
-
function LockIcon4() {
|
|
16042
|
-
return /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
16043
|
-
"path",
|
|
16044
|
-
{
|
|
16045
|
-
d: "M7 10V8a5 5 0 0 1 10 0v2m-12 0h14a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1z",
|
|
16046
|
-
stroke: "currentColor",
|
|
16047
|
-
strokeWidth: "2",
|
|
16048
|
-
strokeLinecap: "round",
|
|
16049
|
-
strokeLinejoin: "round"
|
|
16050
|
-
}
|
|
16051
|
-
) });
|
|
16052
|
-
}
|
|
16053
16878
|
var wordmarkImgStyle5 = {
|
|
16054
16879
|
height: 22,
|
|
16055
16880
|
width: "auto",
|
|
@@ -16067,15 +16892,84 @@ var contentStyle16 = {
|
|
|
16067
16892
|
minHeight: 0,
|
|
16068
16893
|
width: "100%"
|
|
16069
16894
|
};
|
|
16070
|
-
var headingStyle13 = (color) => ({
|
|
16895
|
+
var headingStyle13 = (color, fontWeight) => ({
|
|
16071
16896
|
fontSize: 24,
|
|
16072
|
-
fontWeight
|
|
16897
|
+
fontWeight,
|
|
16073
16898
|
lineHeight: "normal",
|
|
16074
16899
|
letterSpacing: 0,
|
|
16075
16900
|
color,
|
|
16076
|
-
margin: 0,
|
|
16901
|
+
margin: "8px 0 8px",
|
|
16077
16902
|
textAlign: "center"
|
|
16078
16903
|
});
|
|
16904
|
+
var transferCardStyle = (bg, radius) => ({
|
|
16905
|
+
background: bg,
|
|
16906
|
+
borderRadius: radius,
|
|
16907
|
+
padding: "20px 16px",
|
|
16908
|
+
width: "100%",
|
|
16909
|
+
boxSizing: "border-box",
|
|
16910
|
+
display: "flex",
|
|
16911
|
+
flex: 1,
|
|
16912
|
+
flexDirection: "column",
|
|
16913
|
+
justifyContent: "space-between",
|
|
16914
|
+
gap: 12
|
|
16915
|
+
});
|
|
16916
|
+
var dividerRowStyle3 = {
|
|
16917
|
+
display: "flex",
|
|
16918
|
+
alignItems: "center",
|
|
16919
|
+
gap: 12,
|
|
16920
|
+
width: "100%"
|
|
16921
|
+
};
|
|
16922
|
+
var dividerLineStyle = (color) => ({
|
|
16923
|
+
flex: 1,
|
|
16924
|
+
height: 1,
|
|
16925
|
+
background: color
|
|
16926
|
+
});
|
|
16927
|
+
var dividerLabelStyle3 = (color, fontWeight) => ({
|
|
16928
|
+
fontSize: 11,
|
|
16929
|
+
fontWeight,
|
|
16930
|
+
letterSpacing: "0.08em",
|
|
16931
|
+
textTransform: "uppercase",
|
|
16932
|
+
color,
|
|
16933
|
+
whiteSpace: "nowrap"
|
|
16934
|
+
});
|
|
16935
|
+
var addressRowStyle = {
|
|
16936
|
+
display: "flex",
|
|
16937
|
+
alignItems: "center",
|
|
16938
|
+
gap: 12,
|
|
16939
|
+
width: "100%"
|
|
16940
|
+
};
|
|
16941
|
+
var addressTextColStyle = {
|
|
16942
|
+
display: "flex",
|
|
16943
|
+
flexDirection: "column",
|
|
16944
|
+
gap: 2,
|
|
16945
|
+
minWidth: 0
|
|
16946
|
+
};
|
|
16947
|
+
var addressTitleStyle = (color, fontWeight) => ({
|
|
16948
|
+
fontSize: 16,
|
|
16949
|
+
fontWeight,
|
|
16950
|
+
lineHeight: 1.25,
|
|
16951
|
+
color
|
|
16952
|
+
});
|
|
16953
|
+
var addressSubSlotStyle = {
|
|
16954
|
+
display: "flex",
|
|
16955
|
+
alignItems: "center",
|
|
16956
|
+
minHeight: 18
|
|
16957
|
+
};
|
|
16958
|
+
var addressSubStyle = (color, fontWeight) => ({
|
|
16959
|
+
fontSize: 14,
|
|
16960
|
+
fontWeight,
|
|
16961
|
+
lineHeight: 1.25,
|
|
16962
|
+
color,
|
|
16963
|
+
overflow: "hidden",
|
|
16964
|
+
textOverflow: "ellipsis",
|
|
16965
|
+
whiteSpace: "nowrap"
|
|
16966
|
+
});
|
|
16967
|
+
var arrowRowStyle = {
|
|
16968
|
+
display: "flex",
|
|
16969
|
+
alignItems: "center",
|
|
16970
|
+
height: 20,
|
|
16971
|
+
paddingLeft: 10
|
|
16972
|
+
};
|
|
16079
16973
|
var cardStyle2 = (bg, radius) => ({
|
|
16080
16974
|
background: bg,
|
|
16081
16975
|
borderRadius: radius,
|
|
@@ -16083,7 +16977,9 @@ var cardStyle2 = (bg, radius) => ({
|
|
|
16083
16977
|
width: "100%",
|
|
16084
16978
|
boxSizing: "border-box",
|
|
16085
16979
|
display: "flex",
|
|
16980
|
+
flex: 1,
|
|
16086
16981
|
flexDirection: "column",
|
|
16982
|
+
justifyContent: "center",
|
|
16087
16983
|
gap: 4
|
|
16088
16984
|
});
|
|
16089
16985
|
var rowStyle6 = {
|
|
@@ -16091,10 +16987,25 @@ var rowStyle6 = {
|
|
|
16091
16987
|
alignItems: "center",
|
|
16092
16988
|
gap: 16,
|
|
16093
16989
|
padding: "12px 16px 12px 12px",
|
|
16990
|
+
// Reserve a stable height (two label lines + vertical padding) so swapping the
|
|
16991
|
+
// leading badge between the pending circle, the spinner, and the completion
|
|
16992
|
+
// checkmark — and the muted→active label restyle — never reflows the card.
|
|
16993
|
+
minHeight: 64,
|
|
16094
16994
|
borderRadius: 16,
|
|
16095
16995
|
width: "100%",
|
|
16096
16996
|
boxSizing: "border-box"
|
|
16097
16997
|
};
|
|
16998
|
+
var passkeyCircleStyle = (bg, iconColor) => ({
|
|
16999
|
+
width: 40,
|
|
17000
|
+
height: 40,
|
|
17001
|
+
borderRadius: "50%",
|
|
17002
|
+
background: bg,
|
|
17003
|
+
color: iconColor,
|
|
17004
|
+
display: "inline-flex",
|
|
17005
|
+
alignItems: "center",
|
|
17006
|
+
justifyContent: "center",
|
|
17007
|
+
flexShrink: 0
|
|
17008
|
+
});
|
|
16098
17009
|
var completeBadgeStyle = (color) => ({
|
|
16099
17010
|
width: 24,
|
|
16100
17011
|
height: 24,
|
|
@@ -16112,13 +17023,13 @@ var pendingBadgeStyle = (color) => ({
|
|
|
16112
17023
|
background: color,
|
|
16113
17024
|
flexShrink: 0
|
|
16114
17025
|
});
|
|
16115
|
-
var labelStyle7 = (color) => ({
|
|
17026
|
+
var labelStyle7 = (color, fontWeight) => ({
|
|
16116
17027
|
fontSize: "1rem",
|
|
16117
|
-
fontWeight
|
|
17028
|
+
fontWeight,
|
|
16118
17029
|
lineHeight: 1.25,
|
|
16119
17030
|
color
|
|
16120
17031
|
});
|
|
16121
|
-
var
|
|
17032
|
+
var footerStackStyle5 = {
|
|
16122
17033
|
display: "flex",
|
|
16123
17034
|
flexDirection: "column",
|
|
16124
17035
|
gap: 16,
|
|
@@ -16130,32 +17041,6 @@ var retryStackStyle = {
|
|
|
16130
17041
|
gap: 8,
|
|
16131
17042
|
width: "100%"
|
|
16132
17043
|
};
|
|
16133
|
-
var lockBannerStyle3 = {
|
|
16134
|
-
display: "flex",
|
|
16135
|
-
alignItems: "flex-start",
|
|
16136
|
-
gap: 16,
|
|
16137
|
-
padding: "12px 16px 12px 12px",
|
|
16138
|
-
borderRadius: 16,
|
|
16139
|
-
width: "100%",
|
|
16140
|
-
boxSizing: "border-box"
|
|
16141
|
-
};
|
|
16142
|
-
var lockIconWrapStyle3 = (color) => ({
|
|
16143
|
-
flexShrink: 0,
|
|
16144
|
-
width: 24,
|
|
16145
|
-
height: 24,
|
|
16146
|
-
display: "inline-flex",
|
|
16147
|
-
alignItems: "center",
|
|
16148
|
-
justifyContent: "center",
|
|
16149
|
-
color
|
|
16150
|
-
});
|
|
16151
|
-
var lockBannerTextStyle3 = (color) => ({
|
|
16152
|
-
margin: 0,
|
|
16153
|
-
flex: 1,
|
|
16154
|
-
fontSize: 12,
|
|
16155
|
-
fontWeight: 500,
|
|
16156
|
-
lineHeight: "normal",
|
|
16157
|
-
color
|
|
16158
|
-
});
|
|
16159
17044
|
function ConfirmSignScreen({
|
|
16160
17045
|
walletName,
|
|
16161
17046
|
chainFamily,
|
|
@@ -16281,7 +17166,7 @@ function TokenPickerScreen({
|
|
|
16281
17166
|
}
|
|
16282
17167
|
const entries2 = [];
|
|
16283
17168
|
for (const wallet of account.wallets) {
|
|
16284
|
-
for (const source of wallet.sources) {
|
|
17169
|
+
for (const source of wallet.sources ?? []) {
|
|
16285
17170
|
const visibleBalance = source.balance.available.amount;
|
|
16286
17171
|
if (!isSelectableDepositSourceAmountUsd(visibleBalance, minDepositFloor)) continue;
|
|
16287
17172
|
entries2.push({
|
|
@@ -17122,22 +18007,45 @@ function buildOpenWalletScreenProps({
|
|
|
17122
18007
|
function isApprovingInWalletAction(actionType) {
|
|
17123
18008
|
return actionType === "APPROVE_PERMIT2" || actionType === "SIGN_PERMIT2" || actionType === "APPROVE_SPL";
|
|
17124
18009
|
}
|
|
18010
|
+
function formatUsdTwoDecimals3(value) {
|
|
18011
|
+
return (Number.isFinite(value) ? value : 0).toLocaleString("en-US", {
|
|
18012
|
+
minimumFractionDigits: 2,
|
|
18013
|
+
maximumFractionDigits: 2
|
|
18014
|
+
});
|
|
18015
|
+
}
|
|
18016
|
+
function readMetadataString(metadata, key) {
|
|
18017
|
+
const value = metadata?.[key];
|
|
18018
|
+
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
18019
|
+
}
|
|
17125
18020
|
function buildApprovingInWalletScreenProps({
|
|
17126
18021
|
flow,
|
|
17127
18022
|
remote,
|
|
18023
|
+
derived,
|
|
17128
18024
|
handlers
|
|
17129
18025
|
}) {
|
|
17130
18026
|
const { state } = flow;
|
|
17131
18027
|
const setupToken = state.setupDepositToken;
|
|
17132
|
-
const
|
|
18028
|
+
const currentAction = remote.authExecutorCurrentAction;
|
|
18029
|
+
const actionType = currentAction?.type ?? null;
|
|
18030
|
+
const limit = state.setupSpendingLimit;
|
|
18031
|
+
const spendingLimitLabel = limit == null ? null : "unlimited" in limit ? "Unlimited lifetime spending limit" : `$${formatUsdTwoDecimals3(limit.usd)} lifetime spending limit`;
|
|
18032
|
+
const destinationAddress = remote.authApprovalSmartAccountAddress ?? remote.authExecutorApprovalDestinationAddress ?? readMetadataString(currentAction?.metadata, "smartAccountAddress") ?? readMetadataString(currentAction?.metadata, "ownerPubkey");
|
|
17133
18033
|
const isSolana = setupToken != null && state.chains.find((c) => c.name === setupToken.chainName)?.chainFamily === "svm";
|
|
17134
18034
|
const step = actionType === "SIGN_PERMIT2" ? "sign" : actionType === "APPROVE_SPL" ? "spl" : actionType == null && isSolana ? "spl" : "approve";
|
|
17135
18035
|
const complete = remote.authExecutorCompleted && actionType == null && !remote.authExecutorExecuting;
|
|
18036
|
+
const awaitingApproval = remote.authExecutorAwaitingApproval ?? false;
|
|
18037
|
+
const signing = isApprovingInWalletAction(actionType) && !awaitingApproval && !complete;
|
|
17136
18038
|
return {
|
|
17137
18039
|
tokenSymbol: setupToken?.symbol ?? null,
|
|
17138
18040
|
chainName: setupToken?.chainName ?? null,
|
|
17139
18041
|
step,
|
|
17140
18042
|
complete,
|
|
18043
|
+
signing,
|
|
18044
|
+
spendingLimitLabel,
|
|
18045
|
+
destinationAddress,
|
|
18046
|
+
tokenLogoUri: derived.selectedSource?.token.logoURI ?? null,
|
|
18047
|
+
awaitingApproval,
|
|
18048
|
+
onApprove: handlers.onApprove,
|
|
17141
18049
|
error: flow.state.error || remote.authExecutorError,
|
|
17142
18050
|
onRetry: handlers.onRetryAuthorization,
|
|
17143
18051
|
onLogout: flow.isDesktop ? handlers.onLogout : void 0,
|
|
@@ -17156,7 +18064,7 @@ function buildLinkTokensScreenProps({
|
|
|
17156
18064
|
handlers
|
|
17157
18065
|
}) {
|
|
17158
18066
|
const sourceOptions = remote.pendingSelectSource?.metadata?.options ?? [];
|
|
17159
|
-
const
|
|
18067
|
+
const supportedRows = derived.selectSourceChoices.flatMap(
|
|
17160
18068
|
(chain) => chain.tokens.map((t) => {
|
|
17161
18069
|
const rawOption = sourceOptions.find(
|
|
17162
18070
|
(option) => option.chainName === chain.chainName && option.tokenSymbol === t.tokenSymbol
|
|
@@ -17176,6 +18084,10 @@ function buildLinkTokensScreenProps({
|
|
|
17176
18084
|
};
|
|
17177
18085
|
})
|
|
17178
18086
|
);
|
|
18087
|
+
const rowContexts = [
|
|
18088
|
+
...supportedRows.filter((row) => isVisibleUsdAmountAtTwoDecimals(row.balanceUsd)),
|
|
18089
|
+
...supportedRows.filter((row) => !isVisibleUsdAmountAtTwoDecimals(row.balanceUsd))
|
|
18090
|
+
];
|
|
17179
18091
|
rowContexts.push(
|
|
17180
18092
|
...derived.selectSourceUnsupportedChoices.flatMap(
|
|
17181
18093
|
(chain) => chain.tokens.map((t) => ({
|
|
@@ -17197,6 +18109,17 @@ function buildLinkTokensScreenProps({
|
|
|
17197
18109
|
balanceLabel: `${formatNativeAmount(native.amount)} ${native.tokenSymbol}`
|
|
17198
18110
|
}))
|
|
17199
18111
|
);
|
|
18112
|
+
const selectedTokenSymbol = flow.state.setupDepositToken?.symbol ?? derived.selectSourceRecommended?.tokenSymbol;
|
|
18113
|
+
const selectedChainName = flow.state.setupDepositToken?.chainName ?? derived.selectSourceRecommended?.chainName;
|
|
18114
|
+
if (selectedTokenSymbol && selectedChainName) {
|
|
18115
|
+
const match = rowContexts.findIndex(
|
|
18116
|
+
(row) => row.symbol === selectedTokenSymbol && row.chainName === selectedChainName
|
|
18117
|
+
);
|
|
18118
|
+
if (match >= 0 && !rowContexts[match].notSupported) {
|
|
18119
|
+
const [pinned] = rowContexts.splice(match, 1);
|
|
18120
|
+
rowContexts.unshift(pinned);
|
|
18121
|
+
}
|
|
18122
|
+
}
|
|
17200
18123
|
const entries2 = rowContexts.map(
|
|
17201
18124
|
({ symbol, chainName, balanceUsd, notSupported, tokenLogoUri, balanceLabel }) => ({
|
|
17202
18125
|
tokenSymbol: symbol,
|
|
@@ -17209,8 +18132,6 @@ function buildLinkTokensScreenProps({
|
|
|
17209
18132
|
})
|
|
17210
18133
|
);
|
|
17211
18134
|
const firstSupportedIndex = rowContexts.findIndex((row) => !row.notSupported);
|
|
17212
|
-
const selectedTokenSymbol = flow.state.setupDepositToken?.symbol ?? derived.selectSourceRecommended?.tokenSymbol;
|
|
17213
|
-
const selectedChainName = flow.state.setupDepositToken?.chainName ?? derived.selectSourceRecommended?.chainName;
|
|
17214
18135
|
const selectedIndex = (() => {
|
|
17215
18136
|
if (!selectedTokenSymbol || !selectedChainName) {
|
|
17216
18137
|
return firstSupportedIndex;
|
|
@@ -17302,9 +18223,10 @@ function buildDepositScreenProps({
|
|
|
17302
18223
|
const registryChainIds = derived.walletConnectChainIdsByAccount[account.id] ?? null;
|
|
17303
18224
|
const approvedChainIds = account.walletConnect?.approvedChainIds ?? null;
|
|
17304
18225
|
for (const wallet of account.wallets) {
|
|
17305
|
-
for (const source of wallet.sources) {
|
|
18226
|
+
for (const source of wallet.sources ?? []) {
|
|
17306
18227
|
const balance = source.balance.available.amount;
|
|
17307
|
-
|
|
18228
|
+
const isAuthorized = source.token.status === "AUTHORIZED";
|
|
18229
|
+
if (!isAuthorized && !isSelectableDepositSourceAmountUsd(balance, minDepositFloor)) continue;
|
|
17308
18230
|
const chain = flow.state.chains.find((c) => c.name === wallet.chain.name);
|
|
17309
18231
|
const requiresAuth = source.token.status != null && source.token.status !== "AUTHORIZED";
|
|
17310
18232
|
const notSupported = requiresAuth && chain?.commonId != null && registryChainIds != null && !registryChainIds.includes(chain.commonId) && !(approvedChainIds?.includes(chain.commonId) ?? false);
|
|
@@ -17326,7 +18248,10 @@ function buildDepositScreenProps({
|
|
|
17326
18248
|
}
|
|
17327
18249
|
return {
|
|
17328
18250
|
merchantName,
|
|
17329
|
-
|
|
18251
|
+
// Wallet balances arrive after the (balance-free) accounts load; while they
|
|
18252
|
+
// do, the source pill / token rows shimmer instead of showing $0.00.
|
|
18253
|
+
balancesLoading: state.balancesLoading,
|
|
18254
|
+
availableBalance: selectedSource ? selectedSource.balance.available.amount : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + (w.balance?.available.amount ?? 0), 0) : maxSourceBalance,
|
|
17330
18255
|
remainingLimit: selectedSource != null ? selectedSource.remainingAllowance ?? null : selectedAccount?.remainingAllowance ?? null,
|
|
17331
18256
|
tokenCount,
|
|
17332
18257
|
initialAmount: parsedAmt,
|
|
@@ -17517,6 +18442,28 @@ function StepRendererContent({
|
|
|
17517
18442
|
screen
|
|
17518
18443
|
}) {
|
|
17519
18444
|
const input = { flow, remote, derived, forms, handlers };
|
|
18445
|
+
const s = flow.state;
|
|
18446
|
+
useEffect(() => {
|
|
18447
|
+
if (screen !== "loading") return;
|
|
18448
|
+
appendDebug("warn", "stuck on loading shimmer", {
|
|
18449
|
+
phaseStep: s.phase.step,
|
|
18450
|
+
privyReady: s.privyReady,
|
|
18451
|
+
privyAuthenticated: s.privyAuthenticated,
|
|
18452
|
+
activeCredentialId: s.activeCredentialId,
|
|
18453
|
+
passkeyConfigLoaded: s.passkeyConfigLoaded,
|
|
18454
|
+
loadingData: s.loadingData,
|
|
18455
|
+
initialDataLoaded: s.initialDataLoaded,
|
|
18456
|
+
loginRequested: s.loginRequested,
|
|
18457
|
+
enableFullWidget: s.enableFullWidget,
|
|
18458
|
+
authenticatedOnOpen: s.authenticatedOnOpen,
|
|
18459
|
+
welcomeBackAcknowledged: s.welcomeBackAcknowledged,
|
|
18460
|
+
mobileFlow: s.mobileFlow,
|
|
18461
|
+
error: s.error,
|
|
18462
|
+
accountsCount: s.accounts.length,
|
|
18463
|
+
hasActiveWallet: s.accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE")),
|
|
18464
|
+
providersCount: s.providers.length
|
|
18465
|
+
});
|
|
18466
|
+
}, [screen, s]);
|
|
17520
18467
|
switch (screen) {
|
|
17521
18468
|
case "loading":
|
|
17522
18469
|
return /* @__PURE__ */ jsx(BlinkLoadingScreen, {});
|
|
@@ -17545,11 +18492,9 @@ function StepRendererContent({
|
|
|
17545
18492
|
case "wallet-picker":
|
|
17546
18493
|
return /* @__PURE__ */ jsx(WalletPickerScreen, { ...buildWalletPickerScreenProps(input) });
|
|
17547
18494
|
case "open-wallet": {
|
|
17548
|
-
const
|
|
17549
|
-
const inSigningAction = isApprovingInWalletAction(currentActionType);
|
|
17550
|
-
const pastPairingTransient = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null && currentActionType != null && (currentActionType !== "OPEN_PROVIDER" || input.remote.sourceSelectionResolved);
|
|
18495
|
+
const pastSourceSelection = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null && input.remote.sourceSelectionResolved;
|
|
17551
18496
|
const settlingPostSign = input.flow.state.linkSettling;
|
|
17552
|
-
if (
|
|
18497
|
+
if (pastSourceSelection || settlingPostSign) {
|
|
17553
18498
|
return /* @__PURE__ */ jsx(ApprovingInWalletScreen, { ...buildApprovingInWalletScreenProps(input) });
|
|
17554
18499
|
}
|
|
17555
18500
|
return /* @__PURE__ */ jsx(OpenWalletScreen, { ...buildOpenWalletScreenProps(input) });
|
|
@@ -17559,12 +18504,9 @@ function StepRendererContent({
|
|
|
17559
18504
|
if (phase.step === "wallet-setup" && phase.pendingSourceWait) {
|
|
17560
18505
|
return /* @__PURE__ */ jsx(LinkTokensScreen, { ...buildLinkTokensScreenProps(input) });
|
|
17561
18506
|
}
|
|
17562
|
-
const
|
|
17563
|
-
input.remote.authExecutorCurrentAction?.type
|
|
17564
|
-
);
|
|
17565
|
-
const justResolvedSelectSource = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null;
|
|
18507
|
+
const pastSourceSelection = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null && input.remote.sourceSelectionResolved;
|
|
17566
18508
|
const settlingPostSign = input.flow.state.linkSettling;
|
|
17567
|
-
if (
|
|
18509
|
+
if (pastSourceSelection || settlingPostSign) {
|
|
17568
18510
|
return /* @__PURE__ */ jsx(ApprovingInWalletScreen, { ...buildApprovingInWalletScreenProps(input) });
|
|
17569
18511
|
}
|
|
17570
18512
|
return /* @__PURE__ */ jsx(LinkTokensScreen, { ...buildLinkTokensScreenProps(input) });
|
|
@@ -17648,11 +18590,12 @@ var PaymentErrorBoundary = class extends Component {
|
|
|
17648
18590
|
};
|
|
17649
18591
|
function selectedSourceForWallet(selectedWallet, selectedTokenSymbol, depositAmount, priorityContext) {
|
|
17650
18592
|
if (!selectedWallet) return null;
|
|
18593
|
+
const walletSources = selectedWallet.sources ?? [];
|
|
17651
18594
|
if (selectedTokenSymbol) {
|
|
17652
|
-
return
|
|
18595
|
+
return walletSources.find((s) => s.token.symbol === selectedTokenSymbol) ?? null;
|
|
17653
18596
|
}
|
|
17654
18597
|
const walletChainName = selectedWallet.chain.name;
|
|
17655
|
-
const ranked =
|
|
18598
|
+
const ranked = walletSources.map((source, index) => {
|
|
17656
18599
|
return { source, index };
|
|
17657
18600
|
}).sort((a, b) => {
|
|
17658
18601
|
const priority = compareDepositSourcePriority(
|
|
@@ -17700,7 +18643,7 @@ function computeDerivedState(state, depositAmount = 0, priorityContext) {
|
|
|
17700
18643
|
let maxSourceBalance = 0;
|
|
17701
18644
|
for (const acct of state.accounts) {
|
|
17702
18645
|
for (const wallet of acct.wallets) {
|
|
17703
|
-
for (const source of wallet.sources) {
|
|
18646
|
+
for (const source of wallet.sources ?? []) {
|
|
17704
18647
|
if (source.balance.available.amount > maxSourceBalance) {
|
|
17705
18648
|
maxSourceBalance = source.balance.available.amount;
|
|
17706
18649
|
}
|
|
@@ -17710,7 +18653,7 @@ function computeDerivedState(state, depositAmount = 0, priorityContext) {
|
|
|
17710
18653
|
let tokenCount = 0;
|
|
17711
18654
|
for (const acct of state.accounts) {
|
|
17712
18655
|
for (const wallet of acct.wallets) {
|
|
17713
|
-
tokenCount += wallet.sources.filter((s) => s.balance.available.amount > 0).length;
|
|
18656
|
+
tokenCount += (wallet.sources ?? []).filter((s) => s.balance.available.amount > 0).length;
|
|
17714
18657
|
}
|
|
17715
18658
|
}
|
|
17716
18659
|
return {
|
|
@@ -17803,6 +18746,7 @@ function useAuthHandlers(dispatch, apiBaseUrl, popupAuthRef) {
|
|
|
17803
18746
|
dispatch({ type: "PASSKEY_ACTIVATED", credentialId: result.credentialId, publicKey: result.publicKey });
|
|
17804
18747
|
dispatch({ type: "SYNC_PRIVY_SESSION", ready: true, authenticated: true });
|
|
17805
18748
|
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, result.credentialId);
|
|
18749
|
+
track("login_succeeded", { method: "passkey", popup: true });
|
|
17806
18750
|
} catch (err) {
|
|
17807
18751
|
captureException(err);
|
|
17808
18752
|
dispatch({
|
|
@@ -17815,6 +18759,7 @@ function useAuthHandlers(dispatch, apiBaseUrl, popupAuthRef) {
|
|
|
17815
18759
|
}, [dispatch, popupAuthRef]);
|
|
17816
18760
|
const { loginWithPasskey, state: loginState } = useLoginWithPasskey({
|
|
17817
18761
|
onComplete: (params) => {
|
|
18762
|
+
track("login_succeeded", { method: "passkey", popup: false });
|
|
17818
18763
|
handleAuthComplete(params.user);
|
|
17819
18764
|
},
|
|
17820
18765
|
onError: (error) => {
|
|
@@ -17843,6 +18788,7 @@ function useAuthHandlers(dispatch, apiBaseUrl, popupAuthRef) {
|
|
|
17843
18788
|
dispatch({ type: "PASSKEY_ACTIVATED", credentialId: result.credentialId, publicKey: result.publicKey });
|
|
17844
18789
|
dispatch({ type: "SYNC_PRIVY_SESSION", ready: true, authenticated: true });
|
|
17845
18790
|
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, result.credentialId);
|
|
18791
|
+
track("signup_succeeded", { method: "passkey", popup: true });
|
|
17846
18792
|
} catch (err) {
|
|
17847
18793
|
captureException(err);
|
|
17848
18794
|
dispatch({
|
|
@@ -17855,6 +18801,7 @@ function useAuthHandlers(dispatch, apiBaseUrl, popupAuthRef) {
|
|
|
17855
18801
|
}, [dispatch, popupAuthRef]);
|
|
17856
18802
|
const { signupWithPasskey, state: signupState } = useSignupWithPasskey({
|
|
17857
18803
|
onComplete: (params) => {
|
|
18804
|
+
track("signup_succeeded", { method: "passkey", popup: false });
|
|
17858
18805
|
handleAuthComplete(params.user);
|
|
17859
18806
|
},
|
|
17860
18807
|
onError: (error) => {
|
|
@@ -17926,6 +18873,31 @@ function usePasskeyHandlers(dispatch, apiBaseUrl) {
|
|
|
17926
18873
|
};
|
|
17927
18874
|
}
|
|
17928
18875
|
|
|
18876
|
+
// src/balancesLoad.ts
|
|
18877
|
+
async function fetchBalancesByAccountId(apiBaseUrl, token, credentialId, accounts) {
|
|
18878
|
+
const entries2 = await Promise.all(
|
|
18879
|
+
accounts.map(async (account) => {
|
|
18880
|
+
try {
|
|
18881
|
+
const balances = await fetchAccountBalances(
|
|
18882
|
+
apiBaseUrl,
|
|
18883
|
+
token,
|
|
18884
|
+
account.id,
|
|
18885
|
+
credentialId
|
|
18886
|
+
);
|
|
18887
|
+
return [account.id, balances];
|
|
18888
|
+
} catch (err) {
|
|
18889
|
+
captureException(err);
|
|
18890
|
+
return null;
|
|
18891
|
+
}
|
|
18892
|
+
})
|
|
18893
|
+
);
|
|
18894
|
+
const result = {};
|
|
18895
|
+
for (const entry of entries2) {
|
|
18896
|
+
if (entry) result[entry[0]] = entry[1];
|
|
18897
|
+
}
|
|
18898
|
+
return result;
|
|
18899
|
+
}
|
|
18900
|
+
|
|
17929
18901
|
// src/transferSourceResolution.ts
|
|
17930
18902
|
var STANDARD_TRANSFER_SOURCE_UNAVAILABLE_ERROR = "Selected payment source is no longer available. Choose or link an active wallet before depositing.";
|
|
17931
18903
|
function resolveStandardTransferSource({
|
|
@@ -17977,14 +18949,14 @@ function useTransferHandlers(deps) {
|
|
|
17977
18949
|
const processingStartedAtRef = useRef(null);
|
|
17978
18950
|
const pollingTransferIdRef = useRef(null);
|
|
17979
18951
|
const depositSelectionReloadCountRef = useRef(0);
|
|
17980
|
-
const reloadAccounts = useCallback(async () => {
|
|
18952
|
+
const reloadAccounts = useCallback(async (opts) => {
|
|
17981
18953
|
depositSelectionReloadCountRef.current += 1;
|
|
17982
18954
|
if (depositSelectionReloadCountRef.current === 1) {
|
|
17983
18955
|
dispatch({ type: "SET_DEPOSIT_SELECTION_REFRESHING", value: true });
|
|
17984
18956
|
}
|
|
17985
18957
|
try {
|
|
17986
18958
|
const token = await getAccessToken();
|
|
17987
|
-
if (!token || !activeCredentialId) return;
|
|
18959
|
+
if (!token || !activeCredentialId) return null;
|
|
17988
18960
|
const [accts, prov] = await Promise.all([
|
|
17989
18961
|
fetchAccounts(apiBaseUrl, token, activeCredentialId),
|
|
17990
18962
|
fetchProviders(apiBaseUrl, token)
|
|
@@ -18008,6 +18980,20 @@ function useTransferHandlers(deps) {
|
|
|
18008
18980
|
defaults,
|
|
18009
18981
|
resetSelectedTokenSymbol
|
|
18010
18982
|
});
|
|
18983
|
+
const balancesPromise = fetchBalancesByAccountId(
|
|
18984
|
+
apiBaseUrl,
|
|
18985
|
+
token,
|
|
18986
|
+
activeCredentialId,
|
|
18987
|
+
accts
|
|
18988
|
+
).then((balancesByAccountId) => {
|
|
18989
|
+
dispatch({ type: "BALANCES_LOADED", balancesByAccountId });
|
|
18990
|
+
return balancesByAccountId;
|
|
18991
|
+
}).catch(() => null);
|
|
18992
|
+
if (opts?.awaitBalances) {
|
|
18993
|
+
return await balancesPromise;
|
|
18994
|
+
}
|
|
18995
|
+
void balancesPromise;
|
|
18996
|
+
return null;
|
|
18011
18997
|
} finally {
|
|
18012
18998
|
depositSelectionReloadCountRef.current = Math.max(
|
|
18013
18999
|
0,
|
|
@@ -18203,7 +19189,7 @@ function useSourceSelectionHandlers(_dispatch, orchestrator, options) {
|
|
|
18203
19189
|
const selectSourceChoices = useMemo(() => {
|
|
18204
19190
|
if (!pendingSelectSourceAction) return [];
|
|
18205
19191
|
const sourceOptions = pendingSelectSourceAction.metadata?.options ?? [];
|
|
18206
|
-
return buildSelectSourceChoices(sourceOptions, minTransferAmountUsd);
|
|
19192
|
+
return buildSelectSourceChoices(sourceOptions, minTransferAmountUsd, true);
|
|
18207
19193
|
}, [pendingSelectSourceAction, minTransferAmountUsd]);
|
|
18208
19194
|
const selectSourceUnsupportedChoices = useMemo(() => {
|
|
18209
19195
|
if (!pendingSelectSourceAction) return [];
|
|
@@ -18389,13 +19375,6 @@ function buildReauthorizationSessionOptions(params) {
|
|
|
18389
19375
|
return options;
|
|
18390
19376
|
}
|
|
18391
19377
|
|
|
18392
|
-
// src/tokenAuthorizationRunOptions.ts
|
|
18393
|
-
function buildDesktopTokenAuthorizationOnlyRunOptions(chainName, tokenSymbol) {
|
|
18394
|
-
return {
|
|
18395
|
-
autoResolveSource: { chainName, tokenSymbol }
|
|
18396
|
-
};
|
|
18397
|
-
}
|
|
18398
|
-
|
|
18399
19378
|
// src/oneTapDefaults.ts
|
|
18400
19379
|
var DEFAULT_ONE_TAP_ALLOWANCE_USD = 1e4;
|
|
18401
19380
|
async function ensureDefaultOneTapAllowance(apiBaseUrl, token) {
|
|
@@ -18416,7 +19395,7 @@ function resolveReauthorizationTarget(input) {
|
|
|
18416
19395
|
};
|
|
18417
19396
|
}
|
|
18418
19397
|
const wallet = input.account?.wallets.find((candidate) => candidate.id === input.selectedWalletId);
|
|
18419
|
-
const source = wallet?.sources
|
|
19398
|
+
const source = wallet?.sources?.find(
|
|
18420
19399
|
(candidate) => input.selectedTokenSymbol ? candidate.token.symbol === input.selectedTokenSymbol : candidate.token.status === "AUTHORIZED"
|
|
18421
19400
|
);
|
|
18422
19401
|
return {
|
|
@@ -18427,6 +19406,28 @@ function resolveReauthorizationTarget(input) {
|
|
|
18427
19406
|
};
|
|
18428
19407
|
}
|
|
18429
19408
|
|
|
19409
|
+
// src/depositTokenSelection.ts
|
|
19410
|
+
function isSourceAuthorized(balancesByAccountId, target) {
|
|
19411
|
+
const account = target.accountId != null ? balancesByAccountId?.[target.accountId] : void 0;
|
|
19412
|
+
if (!account) return false;
|
|
19413
|
+
const wallet = account.wallets.find((w) => w.id === target.walletId);
|
|
19414
|
+
if (!wallet) return false;
|
|
19415
|
+
const source = wallet.sources.find((s) => s.token.symbol === target.tokenSymbol);
|
|
19416
|
+
return source?.token.status === "AUTHORIZED";
|
|
19417
|
+
}
|
|
19418
|
+
var defaultSettleDelay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
19419
|
+
async function settleUntilTokenAuthorized(reloadAccounts, target, opts = {}) {
|
|
19420
|
+
const maxAttempts = opts.maxAttempts ?? 4;
|
|
19421
|
+
const backoffMs = opts.backoffMs ?? 400;
|
|
19422
|
+
const delay = opts.delay ?? defaultSettleDelay;
|
|
19423
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
19424
|
+
const balances = await reloadAccounts({ awaitBalances: true });
|
|
19425
|
+
if (isSourceAuthorized(balances, target)) return true;
|
|
19426
|
+
if (attempt < maxAttempts - 1) await delay(backoffMs);
|
|
19427
|
+
}
|
|
19428
|
+
return false;
|
|
19429
|
+
}
|
|
19430
|
+
|
|
18430
19431
|
// src/walletConnectLinks.ts
|
|
18431
19432
|
function buildWalletConnectDeeplink(links, walletConnectUri) {
|
|
18432
19433
|
const baseLink = links?.native ?? links?.universal;
|
|
@@ -18558,6 +19559,8 @@ function useProviderHandlers(deps) {
|
|
|
18558
19559
|
setupDepositToken,
|
|
18559
19560
|
tryStartExtensionConnectForReownWallet
|
|
18560
19561
|
} = deps;
|
|
19562
|
+
const setupDepositTokenRef = useRef(setupDepositToken);
|
|
19563
|
+
setupDepositTokenRef.current = setupDepositToken;
|
|
18561
19564
|
const checkWalletConnectChainSupport = useMemo(
|
|
18562
19565
|
() => deps.checkWalletConnectChainSupport ?? ((reownWalletId, chainId) => checkReownWalletChainSupport(BLINK_WC_PROJECT_ID, reownWalletId, chainId)),
|
|
18563
19566
|
[deps.checkWalletConnectChainSupport]
|
|
@@ -18625,7 +19628,7 @@ function useProviderHandlers(deps) {
|
|
|
18625
19628
|
return null;
|
|
18626
19629
|
}
|
|
18627
19630
|
const provider = providers.find((p) => p.id === providerId);
|
|
18628
|
-
const
|
|
19631
|
+
const providerName2 = provider?.name ?? "Wallet";
|
|
18629
19632
|
try {
|
|
18630
19633
|
const token = await getAccessToken();
|
|
18631
19634
|
if (!token) throw new Error("Not authenticated");
|
|
@@ -18633,7 +19636,7 @@ function useProviderHandlers(deps) {
|
|
|
18633
19636
|
const accountId = crypto.randomUUID();
|
|
18634
19637
|
const account = await createAccount(apiBaseUrl, token, {
|
|
18635
19638
|
id: accountId,
|
|
18636
|
-
name:
|
|
19639
|
+
name: providerName2,
|
|
18637
19640
|
credentialId: activeCredentialId,
|
|
18638
19641
|
providerId,
|
|
18639
19642
|
...merchantAuthorization ? {
|
|
@@ -18682,7 +19685,7 @@ function useProviderHandlers(deps) {
|
|
|
18682
19685
|
return;
|
|
18683
19686
|
}
|
|
18684
19687
|
const provider = providers.find((p) => p.id === providerId);
|
|
18685
|
-
const
|
|
19688
|
+
const providerName2 = provider?.name ?? "Wallet";
|
|
18686
19689
|
let shouldRestoreDesktopSelectionOnError = shouldSnapshotDesktopSelection;
|
|
18687
19690
|
try {
|
|
18688
19691
|
await resetWalletConnect();
|
|
@@ -18705,7 +19708,7 @@ function useProviderHandlers(deps) {
|
|
|
18705
19708
|
const newAccountId = crypto.randomUUID();
|
|
18706
19709
|
const account = await createAccount(apiBaseUrl, token, {
|
|
18707
19710
|
id: newAccountId,
|
|
18708
|
-
name:
|
|
19711
|
+
name: providerName2,
|
|
18709
19712
|
credentialId: activeCredentialId,
|
|
18710
19713
|
providerId,
|
|
18711
19714
|
...merchantAuthorization ? {
|
|
@@ -18773,7 +19776,7 @@ function useProviderHandlers(deps) {
|
|
|
18773
19776
|
}
|
|
18774
19777
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
18775
19778
|
try {
|
|
18776
|
-
await reloadAccounts();
|
|
19779
|
+
await reloadAccounts({ awaitBalances: true });
|
|
18777
19780
|
} finally {
|
|
18778
19781
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
18779
19782
|
}
|
|
@@ -18900,7 +19903,7 @@ function useProviderHandlers(deps) {
|
|
|
18900
19903
|
}
|
|
18901
19904
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
18902
19905
|
try {
|
|
18903
|
-
await reloadAccounts();
|
|
19906
|
+
await reloadAccounts({ awaitBalances: true });
|
|
18904
19907
|
} finally {
|
|
18905
19908
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
18906
19909
|
}
|
|
@@ -19124,7 +20127,7 @@ function useProviderHandlers(deps) {
|
|
|
19124
20127
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
19125
20128
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19126
20129
|
try {
|
|
19127
|
-
await reloadAccounts();
|
|
20130
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19128
20131
|
} finally {
|
|
19129
20132
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19130
20133
|
}
|
|
@@ -19226,7 +20229,9 @@ function useProviderHandlers(deps) {
|
|
|
19226
20229
|
const result = await orchestrator.run(
|
|
19227
20230
|
sessionId,
|
|
19228
20231
|
{
|
|
19229
|
-
|
|
20232
|
+
// No autoResolveSource: pause at SELECT_SOURCE so LinkTokensScreen
|
|
20233
|
+
// renders with the token preselected (SET_SETUP_DEPOSIT_TOKEN pin
|
|
20234
|
+
// above) and the user can set a spending limit before approving.
|
|
19230
20235
|
// Pin the existing account id so pairing and signing share one
|
|
19231
20236
|
// runtime (see handleSelectWalletConnectWallet).
|
|
19232
20237
|
walletConnectRuntimeKey: runtimeAccountId,
|
|
@@ -19234,10 +20239,11 @@ function useProviderHandlers(deps) {
|
|
|
19234
20239
|
// Require the target token's chain at pairing. A reused session
|
|
19235
20240
|
// that doesn't approve it re-pairs with the chain required; a
|
|
19236
20241
|
// wallet that approves the connection without it fails
|
|
19237
|
-
// OPEN_PROVIDER with a clear error instead of the
|
|
19238
|
-
//
|
|
19239
|
-
//
|
|
19240
|
-
//
|
|
20242
|
+
// OPEN_PROVIDER with a clear error up front instead of stranding the
|
|
20243
|
+
// user on LinkTokensScreen whose only option (SELECT_SOURCE) would
|
|
20244
|
+
// 422 (INVALID_SELECT_SOURCE_SELECTION) on Approve — e.g. authorizing
|
|
20245
|
+
// USDC on HyperEVM (999, a WC-optional chain) with a wallet that
|
|
20246
|
+
// dropped or doesn't support that chain.
|
|
19241
20247
|
walletConnectRequiredChainId: chainId,
|
|
19242
20248
|
walletConnectRequiredChainName: inlineChain.name,
|
|
19243
20249
|
onWalletConnectDisplayUri: (uri) => {
|
|
@@ -19273,11 +20279,17 @@ function useProviderHandlers(deps) {
|
|
|
19273
20279
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
19274
20280
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19275
20281
|
try {
|
|
19276
|
-
await reloadAccounts();
|
|
20282
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19277
20283
|
} finally {
|
|
19278
20284
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19279
20285
|
}
|
|
19280
|
-
|
|
20286
|
+
const confirmed = setupDepositTokenRef.current;
|
|
20287
|
+
dispatch({
|
|
20288
|
+
type: "SELECT_TOKEN",
|
|
20289
|
+
walletId: confirmed?.walletId ?? walletId,
|
|
20290
|
+
tokenSymbol: confirmed?.symbol ?? tokenSymbol,
|
|
20291
|
+
accountId: selectTokenAccountId
|
|
20292
|
+
});
|
|
19281
20293
|
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19282
20294
|
} catch (err) {
|
|
19283
20295
|
captureException(err);
|
|
@@ -19592,10 +20604,7 @@ function useProviderHandlers(deps) {
|
|
|
19592
20604
|
walletDeeplinks: walletDeeplinks ?? null,
|
|
19593
20605
|
providerId: matchedProvider?.id ?? null
|
|
19594
20606
|
});
|
|
19595
|
-
const result = await orchestrator.run(
|
|
19596
|
-
session.id,
|
|
19597
|
-
buildDesktopTokenAuthorizationOnlyRunOptions(inlineChain.name, tokenSymbol)
|
|
19598
|
-
);
|
|
20607
|
+
const result = await orchestrator.run(session.id);
|
|
19599
20608
|
if (result.status === "cancelled") {
|
|
19600
20609
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
19601
20610
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
@@ -19607,14 +20616,25 @@ function useProviderHandlers(deps) {
|
|
|
19607
20616
|
}
|
|
19608
20617
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
19609
20618
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
20619
|
+
const confirmed = setupDepositTokenRef.current;
|
|
20620
|
+
const settleTarget = {
|
|
20621
|
+
accountId: _accountId,
|
|
20622
|
+
walletId: confirmed?.walletId ?? _walletId,
|
|
20623
|
+
tokenSymbol: confirmed?.symbol ?? tokenSymbol
|
|
20624
|
+
};
|
|
19610
20625
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19611
20626
|
try {
|
|
19612
|
-
await reloadAccounts
|
|
20627
|
+
await settleUntilTokenAuthorized(reloadAccounts, settleTarget);
|
|
20628
|
+
dispatch({
|
|
20629
|
+
type: "SELECT_TOKEN",
|
|
20630
|
+
walletId: settleTarget.walletId,
|
|
20631
|
+
tokenSymbol: settleTarget.tokenSymbol,
|
|
20632
|
+
accountId: _accountId
|
|
20633
|
+
});
|
|
20634
|
+
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19613
20635
|
} finally {
|
|
19614
20636
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19615
20637
|
}
|
|
19616
|
-
dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol, accountId: _accountId });
|
|
19617
|
-
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19618
20638
|
} catch (err) {
|
|
19619
20639
|
captureException(err);
|
|
19620
20640
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
@@ -20074,6 +21094,15 @@ function useDataLoadEffect(deps) {
|
|
|
20074
21094
|
hasActiveCredential: !!state.activeCredentialId,
|
|
20075
21095
|
loading: loadingDataRef.current
|
|
20076
21096
|
});
|
|
21097
|
+
appendDebug("warn", "dataLoadEffect run", {
|
|
21098
|
+
loadAction,
|
|
21099
|
+
authenticated,
|
|
21100
|
+
accountsCount: state.accounts.length,
|
|
21101
|
+
hasActiveCredential: !!state.activeCredentialId,
|
|
21102
|
+
loadingRef: loadingDataRef.current,
|
|
21103
|
+
privyAuthenticated: state.privyAuthenticated,
|
|
21104
|
+
activeCredentialId: state.activeCredentialId ? `${state.activeCredentialId.slice(0, 8)}\u2026` : null
|
|
21105
|
+
});
|
|
20077
21106
|
if (loadAction === "reset") {
|
|
20078
21107
|
loadingDataRef.current = false;
|
|
20079
21108
|
dispatch({ type: "DATA_LOAD_END" });
|
|
@@ -20091,13 +21120,21 @@ function useDataLoadEffect(deps) {
|
|
|
20091
21120
|
const load = async () => {
|
|
20092
21121
|
dispatch({ type: "DATA_LOAD_START" });
|
|
20093
21122
|
try {
|
|
21123
|
+
appendDebug("warn", "dataLoad: requesting access token");
|
|
20094
21124
|
const token = await getAccessTokenRef.current();
|
|
21125
|
+
appendDebug("warn", "dataLoad: token result", { hasToken: !!token, cancelled });
|
|
20095
21126
|
if (!token) throw new Error("Not authenticated");
|
|
20096
21127
|
const [prov, accts, chn] = await Promise.all([
|
|
20097
21128
|
fetchProviders(apiBaseUrl, token),
|
|
20098
21129
|
fetchAccounts(apiBaseUrl, token, credentialId),
|
|
20099
21130
|
fetchChains(apiBaseUrl, token)
|
|
20100
21131
|
]);
|
|
21132
|
+
appendDebug("warn", "dataLoad: fetch resolved", {
|
|
21133
|
+
cancelled,
|
|
21134
|
+
providers: prov.length,
|
|
21135
|
+
accounts: accts.length,
|
|
21136
|
+
chains: chn.length
|
|
21137
|
+
});
|
|
20101
21138
|
if (cancelled) return;
|
|
20102
21139
|
const parsedAmt = depositAmountRef.current != null ? depositAmountRef.current : 0;
|
|
20103
21140
|
const priorityContext = resolveDepositPriorityContext(accts, chn, destination);
|
|
@@ -20123,7 +21160,16 @@ function useDataLoadEffect(deps) {
|
|
|
20123
21160
|
resetSelectedTokenSymbol
|
|
20124
21161
|
});
|
|
20125
21162
|
if (clearMobile) clearMobileFlowState();
|
|
21163
|
+
void fetchBalancesByAccountId(apiBaseUrl, token, credentialId, accts).then(
|
|
21164
|
+
(balancesByAccountId) => {
|
|
21165
|
+
dispatch({ type: "BALANCES_LOADED", balancesByAccountId });
|
|
21166
|
+
}
|
|
21167
|
+
);
|
|
20126
21168
|
} catch (err) {
|
|
21169
|
+
appendDebug("error", "dataLoad: threw", {
|
|
21170
|
+
cancelled,
|
|
21171
|
+
message: err instanceof Error ? err.message : String(err)
|
|
21172
|
+
});
|
|
20127
21173
|
if (!cancelled) {
|
|
20128
21174
|
captureException(err);
|
|
20129
21175
|
dispatch({
|
|
@@ -20140,6 +21186,7 @@ function useDataLoadEffect(deps) {
|
|
|
20140
21186
|
};
|
|
20141
21187
|
load();
|
|
20142
21188
|
return () => {
|
|
21189
|
+
appendDebug("warn", "dataLoadEffect cleanup (cancelling in-flight load)");
|
|
20143
21190
|
cancelled = true;
|
|
20144
21191
|
loadingDataRef.current = false;
|
|
20145
21192
|
dispatch({ type: "DATA_LOAD_END" });
|
|
@@ -20883,7 +21930,7 @@ function BlinkPaymentInner({
|
|
|
20883
21930
|
useWalletConnector: useWalletConnectorProp,
|
|
20884
21931
|
userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
|
|
20885
21932
|
});
|
|
20886
|
-
const [state,
|
|
21933
|
+
const [state, rawDispatch] = useReducer(
|
|
20887
21934
|
paymentReducer,
|
|
20888
21935
|
{
|
|
20889
21936
|
depositAmount,
|
|
@@ -20893,6 +21940,30 @@ function BlinkPaymentInner({
|
|
|
20893
21940
|
},
|
|
20894
21941
|
createInitialState
|
|
20895
21942
|
);
|
|
21943
|
+
const device = isDesktop ? "desktop" : "mobile";
|
|
21944
|
+
const stateRef = useRef(state);
|
|
21945
|
+
stateRef.current = state;
|
|
21946
|
+
const dispatch = useCallback(
|
|
21947
|
+
(action) => {
|
|
21948
|
+
trackAction(action, stateRef.current, { device });
|
|
21949
|
+
rawDispatch(action);
|
|
21950
|
+
},
|
|
21951
|
+
[device]
|
|
21952
|
+
);
|
|
21953
|
+
usePostHogIdentify();
|
|
21954
|
+
useAnalyticsScreenView(state.phase, device);
|
|
21955
|
+
const flowOpenedRef = useRef(false);
|
|
21956
|
+
useEffect(() => {
|
|
21957
|
+
if (flowOpenedRef.current) return;
|
|
21958
|
+
flowOpenedRef.current = true;
|
|
21959
|
+
registerSuperProps({ device });
|
|
21960
|
+
track("flow_opened", {
|
|
21961
|
+
device,
|
|
21962
|
+
isMobileApp: isMobileApp ?? false,
|
|
21963
|
+
fullWidget: enableFullWidget ?? false,
|
|
21964
|
+
hasAmount: depositAmount != null
|
|
21965
|
+
});
|
|
21966
|
+
}, []);
|
|
20896
21967
|
const polling = useTransferPolling(3e3, effectiveGetAccessToken);
|
|
20897
21968
|
const transferSigning = useTransferSigning(2e3, { getAccessToken: effectiveGetAccessToken });
|
|
20898
21969
|
const mobileFlowRefs = {
|
|
@@ -21251,7 +22322,7 @@ function BlinkPaymentInner({
|
|
|
21251
22322
|
const handleSetDepositToken = useCallback((symbol, chainName, walletId, tokenAddress, chainId) => {
|
|
21252
22323
|
dispatch({ type: "SET_SETUP_DEPOSIT_TOKEN", symbol, chainName, walletId, tokenAddress, chainId });
|
|
21253
22324
|
}, []);
|
|
21254
|
-
const handleConfirmSetupDeposit = useCallback(async () => {
|
|
22325
|
+
const handleConfirmSetupDeposit = useCallback(async (limit) => {
|
|
21255
22326
|
const plan = planConfirmSetupDeposit({
|
|
21256
22327
|
setupDepositToken: state.setupDepositToken,
|
|
21257
22328
|
setupSelectedSourceOption,
|
|
@@ -21261,6 +22332,19 @@ function BlinkPaymentInner({
|
|
|
21261
22332
|
dispatch({ type: "SET_ERROR", error: plan.error });
|
|
21262
22333
|
return;
|
|
21263
22334
|
}
|
|
22335
|
+
const token = await effectiveGetAccessToken();
|
|
22336
|
+
if (!token) {
|
|
22337
|
+
dispatch({ type: "SET_ERROR", error: "Your session expired. Please try again." });
|
|
22338
|
+
return;
|
|
22339
|
+
}
|
|
22340
|
+
const config = "unlimited" in limit ? { unlimitedAllowance: true } : { defaultAllowance: limit.usd, unlimitedAllowance: false };
|
|
22341
|
+
try {
|
|
22342
|
+
await updateUserConfig(apiBaseUrl, token, config);
|
|
22343
|
+
} catch {
|
|
22344
|
+
dispatch({ type: "SET_ERROR", error: "Could not save your spending limit. Please try again." });
|
|
22345
|
+
return;
|
|
22346
|
+
}
|
|
22347
|
+
dispatch({ type: "SET_SETUP_SPENDING_LIMIT", limit });
|
|
21264
22348
|
for (const action of plan.actions) {
|
|
21265
22349
|
dispatch(action);
|
|
21266
22350
|
}
|
|
@@ -21269,6 +22353,8 @@ function BlinkPaymentInner({
|
|
|
21269
22353
|
state.selectedAccountId,
|
|
21270
22354
|
state.setupDepositToken,
|
|
21271
22355
|
setupSelectedSourceOption,
|
|
22356
|
+
effectiveGetAccessToken,
|
|
22357
|
+
apiBaseUrl,
|
|
21272
22358
|
dispatch,
|
|
21273
22359
|
orchestrator
|
|
21274
22360
|
]);
|
|
@@ -21329,6 +22415,9 @@ function BlinkPaymentInner({
|
|
|
21329
22415
|
}
|
|
21330
22416
|
})();
|
|
21331
22417
|
}, [orchestrator]);
|
|
22418
|
+
const handleApprove = useCallback(() => {
|
|
22419
|
+
authExecutor.approveAuthorization();
|
|
22420
|
+
}, [authExecutor]);
|
|
21332
22421
|
const handleSetPhase = useCallback((phase) => {
|
|
21333
22422
|
clearScreenErrors();
|
|
21334
22423
|
if (phase.step === "deposit") {
|
|
@@ -21350,6 +22439,7 @@ function BlinkPaymentInner({
|
|
|
21350
22439
|
onConfirmSign: transfer.handleConfirmSign,
|
|
21351
22440
|
onRetryMobileStatus: mobileFlow.handleRetryMobileStatus,
|
|
21352
22441
|
onRetryAuthorization: handleAuthorizationRetry,
|
|
22442
|
+
onApprove: handleApprove,
|
|
21353
22443
|
onRetryTransferSigning: transfer.handleRetryTransferSigning,
|
|
21354
22444
|
onBackFromOpenWallet: handleBackFromOpenWallet,
|
|
21355
22445
|
onLogout: handleLogout,
|
|
@@ -21420,6 +22510,7 @@ function BlinkPaymentInner({
|
|
|
21420
22510
|
handleConfirmSetupDeposit,
|
|
21421
22511
|
handleBackFromSetupDeposit,
|
|
21422
22512
|
handleAuthorizationRetry,
|
|
22513
|
+
handleApprove,
|
|
21423
22514
|
disconnectWallets,
|
|
21424
22515
|
state.desktopWait?.walletForegroundLink
|
|
21425
22516
|
]);
|
|
@@ -21459,6 +22550,9 @@ function BlinkPaymentInner({
|
|
|
21459
22550
|
authExecutorError: authExecutor.error,
|
|
21460
22551
|
authExecutorExecuting: authExecutor.executing,
|
|
21461
22552
|
authExecutorCurrentAction: authExecutor.currentAction,
|
|
22553
|
+
authExecutorAwaitingApproval: authExecutor.awaitingApproval,
|
|
22554
|
+
authExecutorApprovalDestinationAddress: authExecutor.approvalDestinationAddress,
|
|
22555
|
+
authApprovalSmartAccountAddress: orchestrator.approvalSmartAccountAddress,
|
|
21462
22556
|
authExecutorCompleted: orchestrator.orchestratorCompleted,
|
|
21463
22557
|
transferSigningSigning: transferSigning.signing,
|
|
21464
22558
|
transferSigningError: transferSigning.error,
|
|
@@ -21496,6 +22590,6 @@ function BlinkPaymentInner({
|
|
|
21496
22590
|
) });
|
|
21497
22591
|
}
|
|
21498
22592
|
|
|
21499
|
-
export { ACCOUNT_SWITCH_CONFLICT_MESSAGE, AdvancedSourceScreen, ApprovingInWalletScreen, AuthorizationSessionCancelledError, BLINK_ERROR_ILLUSTRATION, BLINK_LOGO, BLINK_MASCOT, BLINK_PASSKEY_ILLUSTRATION, BLINK_SUCCESS_ILLUSTRATION, BlinkDepositButton, BlinkErrorScreen, BlinkInitialLoadingScreen, BlinkLoadingScreen, BlinkPayment, BlinkProvider, ConfirmSignScreen, DepositCompleteScreen, DepositOptionsScreen, DepositScreen, DepositTransferStatusScreen, GuestTokenPickerScreen, IconCircle, InfoBanner, LOGIN_KEY_ILLUSTRATION, LinkTokensScreen, LoginScreen, ManualTransferPasskeyScreen, OpenWalletScreen, OtpVerifyScreen, OutlineButton, PasskeyIframeBlockedError, PasskeyPopupWelcomeScreen, PasskeyScreen, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SecondaryButton, SelectDepositSourceScreen, SelectSourceScreen, SettingsMenu, Spinner, StepList, StepRenderer, SuccessScreen, TokenPickerScreen, VerifyPasskeyScreen, WalletPickerScreen, WelcomeBackScreen, appendDebug, api_exports as blinkApi, buildNativeUnsupportedEntries, clearDebugEntries, createInitialState, credentialIdBase64ToBytes, darkTheme, darkThemeNew, darkTransparentTheme, darkTransparentThemeNew, deviceHasPasskey, encodePermit2ApproveCalldata, findDevicePasskey, findDevicePasskeyViaPopup, formatNativeAmount, getAtomicBatchSupportDebugInfo, getDebugEntries, getDeviceBiometricUnlockText, getTheme, getThemeBase, getWalletCapabilities, isAuthorizationSessionCancelled, isExpectedAuthorizationCancellation, isTerminalTransferStatus, isTransferAwaitingCompletion, isTransparentTheme, isUserDismissedAuthorizationError, isVisibleUsdAmountAtTwoDecimals, lightTheme, lightThemeNew, lightTransparentTheme, lightTransparentThemeNew, mapGuestPickerEntries, replaceOpenProviderForAccountSwitch, resolvePasskeyRpId, screenForPhase, subscribeDebug, supportsAtomicBatch, supportsPaymasterService, useAuthorizationExecutor, useAuthorizationOrchestrator, useBlinkConfig, useBlinkDebugLog, useBlinkDepositAmount, useSolanaAccountSwitchEffect, useTransferPolling, useTransferSigning };
|
|
22593
|
+
export { ACCOUNT_SWITCH_CONFLICT_MESSAGE, AdvancedSourceScreen, ApprovingInWalletScreen, AuthorizationSessionCancelledError, BLINK_ERROR_ILLUSTRATION, BLINK_LOGO, BLINK_MASCOT, BLINK_PASSKEY_ILLUSTRATION, BLINK_SUCCESS_ILLUSTRATION, BlinkDepositButton, BlinkErrorScreen, BlinkInitialLoadingScreen, BlinkLoadingScreen, BlinkPayment, BlinkProvider, ConfirmSignScreen, DepositCompleteScreen, DepositOptionsScreen, DepositScreen, DepositTransferStatusScreen, GuestTokenPickerScreen, IconCircle, InfoBanner, LOGIN_KEY_ILLUSTRATION, LinkTokensScreen, LoginScreen, ManualTransferPasskeyScreen, OpenWalletScreen, OtpVerifyScreen, OutlineButton, PasskeyIframeBlockedError, PasskeyPopupWelcomeScreen, PasskeyScreen, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SecondaryButton, SelectDepositSourceScreen, SelectSourceScreen, SettingsMenu, Spinner, StepList, StepRenderer, SuccessScreen, TokenPickerScreen, VerifyPasskeyScreen, WalletPickerScreen, WelcomeBackScreen, appendDebug, api_exports as blinkApi, buildNativeUnsupportedEntries, clearDebugEntries, createInitialState, credentialIdBase64ToBytes, darkTheme, darkThemeNew, darkTransparentTheme, darkTransparentThemeNew, deviceHasPasskey, encodePermit2ApproveCalldata, findDevicePasskey, findDevicePasskeyViaPopup, formatNativeAmount, getAtomicBatchSupportDebugInfo, getDebugEntries, getDeviceBiometricUnlockText, getTheme, getThemeBase, getWalletCapabilities, identifyUser, isAuthorizationSessionCancelled, isExpectedAuthorizationCancellation, isTerminalTransferStatus, isTransferAwaitingCompletion, isTransparentTheme, isUserDismissedAuthorizationError, isVisibleUsdAmountAtTwoDecimals, lightTheme, lightThemeNew, lightTransparentTheme, lightTransparentThemeNew, mapGuestPickerEntries, replaceOpenProviderForAccountSwitch, resetUser, resolveAnalyticsEnvironment, resolvePasskeyRpId, sanitizeProps, screenForPhase, subscribeDebug, supportsAtomicBatch, supportsPaymasterService, track, useAuthorizationExecutor, useAuthorizationOrchestrator, useBlinkConfig, useBlinkDebugLog, useBlinkDepositAmount, useSolanaAccountSwitchEffect, useTransferPolling, useTransferSigning };
|
|
21500
22594
|
//# sourceMappingURL=index.js.map
|
|
21501
22595
|
//# sourceMappingURL=index.js.map
|