@swype-org/react-sdk 0.2.377 → 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 +1645 -785
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1152 -1012
- package/dist/index.d.ts +1152 -1012
- package/dist/index.js +1645 -785
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -55,7 +55,11 @@ var darkTheme = {
|
|
|
55
55
|
shadowLg: "0 18px 44px rgba(0,0,0,0.42)",
|
|
56
56
|
radius: "14px",
|
|
57
57
|
radiusLg: "24px",
|
|
58
|
-
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
|
|
59
63
|
};
|
|
60
64
|
var lightTheme = {
|
|
61
65
|
bg: "#ebf9fb",
|
|
@@ -87,7 +91,11 @@ var lightTheme = {
|
|
|
87
91
|
shadowLg: "0 20px 48px rgba(19, 61, 75, 0.14)",
|
|
88
92
|
radius: "14px",
|
|
89
93
|
radiusLg: "24px",
|
|
90
|
-
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
|
|
91
99
|
};
|
|
92
100
|
var lightTransparentTheme = {
|
|
93
101
|
...lightTheme
|
|
@@ -130,7 +138,13 @@ var lightThemeNew = {
|
|
|
130
138
|
shadowLg: "0 20px 48px rgba(0, 0, 0, 0.14)",
|
|
131
139
|
radius: "14px",
|
|
132
140
|
radiusLg: "24px",
|
|
133
|
-
|
|
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
|
|
134
148
|
};
|
|
135
149
|
var darkThemeNew = { ...lightThemeNew };
|
|
136
150
|
var lightTransparentThemeNew = { ...lightThemeNew };
|
|
@@ -300,7 +314,7 @@ function resolveDepositPriorityContext(accounts, chains, destination) {
|
|
|
300
314
|
for (const account of accounts) {
|
|
301
315
|
for (const wallet of account.wallets) {
|
|
302
316
|
if (wallet.chain.name !== destinationChainName) continue;
|
|
303
|
-
const source = wallet.sources.find(
|
|
317
|
+
const source = (wallet.sources ?? []).find(
|
|
304
318
|
(candidate) => candidate.address.toLowerCase() === destinationTokenAddress
|
|
305
319
|
);
|
|
306
320
|
if (source) {
|
|
@@ -334,6 +348,11 @@ function getWalletAddress(wallet) {
|
|
|
334
348
|
const address = wallet.name.trim();
|
|
335
349
|
return address.length > 0 ? address : null;
|
|
336
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
|
+
}
|
|
337
356
|
function getAddressableWallets(account) {
|
|
338
357
|
return account.wallets.filter((wallet) => getWalletAddress(wallet) != null);
|
|
339
358
|
}
|
|
@@ -341,7 +360,7 @@ function getPreferredDepositWallet(account, transferAmount, priorityContext) {
|
|
|
341
360
|
const wallets = getAddressableWallets(account);
|
|
342
361
|
if (wallets.length === 0) return null;
|
|
343
362
|
const ranked = wallets.map((wallet, walletIndex) => {
|
|
344
|
-
const bestSource = wallet.sources.map((source, sourceIndex) => ({
|
|
363
|
+
const bestSource = (wallet.sources ?? []).map((source, sourceIndex) => ({
|
|
345
364
|
source,
|
|
346
365
|
sourceIndex
|
|
347
366
|
})).sort((a, b) => {
|
|
@@ -366,7 +385,7 @@ function getPreferredDepositWallet(account, transferAmount, priorityContext) {
|
|
|
366
385
|
if (a.wallet.status !== b.wallet.status) {
|
|
367
386
|
return a.wallet.status === "ACTIVE" ? -1 : 1;
|
|
368
387
|
}
|
|
369
|
-
const balanceDiff = b.wallet.balance
|
|
388
|
+
const balanceDiff = (b.wallet.balance?.available.amount ?? 0) - (a.wallet.balance?.available.amount ?? 0);
|
|
370
389
|
if (balanceDiff !== 0) return balanceDiff;
|
|
371
390
|
return a.walletIndex - b.walletIndex;
|
|
372
391
|
});
|
|
@@ -382,7 +401,7 @@ function resolveDepositSelectionAfterRefresh(accounts, transferAmount, prev, pri
|
|
|
382
401
|
const wallet = acct?.wallets.find((w) => w.id === selectedWalletId);
|
|
383
402
|
if (wallet) {
|
|
384
403
|
if (selectedTokenSymbol) {
|
|
385
|
-
const hasToken = wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
|
|
404
|
+
const hasToken = wallet.sources == null || wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
|
|
386
405
|
if (hasToken) {
|
|
387
406
|
return {
|
|
388
407
|
defaults: { accountId: selectedAccountId, walletId: selectedWalletId },
|
|
@@ -420,7 +439,9 @@ function resolveDepositSelection(accounts, transferAmount, selectedAccountId, pr
|
|
|
420
439
|
const eligibleAccounts = getDepositEligibleAccounts(accounts);
|
|
421
440
|
if (eligibleAccounts.length === 0) return null;
|
|
422
441
|
const accountHasSufficientBalanceWallet = (account) => getAddressableWallets(account).some(
|
|
423
|
-
(wallet) => wallet.status === "ACTIVE" && wallet.sources.some(
|
|
442
|
+
(wallet) => wallet.status === "ACTIVE" && (wallet.sources ?? []).some(
|
|
443
|
+
(source) => source.balance.available.amount >= transferAmount
|
|
444
|
+
)
|
|
424
445
|
);
|
|
425
446
|
if (transferAmount <= 0 && selectedAccountId) {
|
|
426
447
|
const selectedAccount = eligibleAccounts.find((account) => account.id === selectedAccountId);
|
|
@@ -442,7 +463,7 @@ function resolveDepositSelection(accounts, transferAmount, selectedAccountId, pr
|
|
|
442
463
|
transferAmount,
|
|
443
464
|
priorityContext
|
|
444
465
|
);
|
|
445
|
-
const preferredSource = preferredWallet ? [...preferredWallet.sources].sort((a, b) => compareDepositSourcePriority(
|
|
466
|
+
const preferredSource = preferredWallet ? [...preferredWallet.sources ?? []].sort((a, b) => compareDepositSourcePriority(
|
|
446
467
|
sourcePriorityInput(preferredWallet.chain.name, a, transferAmount, priorityContext),
|
|
447
468
|
sourcePriorityInput(preferredWallet.chain.name, b, transferAmount, priorityContext)
|
|
448
469
|
))[0] : void 0;
|
|
@@ -508,7 +529,7 @@ function buildNativeUnsupportedEntries(options) {
|
|
|
508
529
|
logoURI: option.logoURI ?? null
|
|
509
530
|
})).filter((entry) => entry.amount > 0);
|
|
510
531
|
}
|
|
511
|
-
function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD) {
|
|
532
|
+
function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD, includeUnfundedTokens = false) {
|
|
512
533
|
const chainChoices = [];
|
|
513
534
|
const chainIndexByName = /* @__PURE__ */ new Map();
|
|
514
535
|
for (const option of options) {
|
|
@@ -549,7 +570,7 @@ function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SE
|
|
|
549
570
|
}
|
|
550
571
|
}
|
|
551
572
|
return chainChoices.map((chain) => {
|
|
552
|
-
const visibleTokens = chain.tokens.filter((t) => isSelectableDepositSourceAmountUsd(t.balance, minTransferAmountUsd));
|
|
573
|
+
const visibleTokens = includeUnfundedTokens ? chain.tokens : chain.tokens.filter((t) => isSelectableDepositSourceAmountUsd(t.balance, minTransferAmountUsd));
|
|
553
574
|
return {
|
|
554
575
|
...chain,
|
|
555
576
|
balance: visibleTokens.reduce((sum, token) => sum + token.balance, 0),
|
|
@@ -1937,464 +1958,197 @@ function screenForPhase(phase) {
|
|
|
1937
1958
|
}
|
|
1938
1959
|
}
|
|
1939
1960
|
|
|
1940
|
-
// src/
|
|
1941
|
-
var
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
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
|
+
}
|
|
1945
2006
|
}
|
|
1946
|
-
}
|
|
1947
|
-
function
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
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}`);
|
|
1955
2025
|
}
|
|
2026
|
+
notify();
|
|
1956
2027
|
}
|
|
1957
|
-
function
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2028
|
+
function subscribeDebug(listener) {
|
|
2029
|
+
listeners.add(listener);
|
|
2030
|
+
return () => {
|
|
2031
|
+
listeners.delete(listener);
|
|
2032
|
+
};
|
|
1961
2033
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
function
|
|
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
|
-
|
|
2001
|
-
}
|
|
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));
|
|
2002
2075
|
}
|
|
2003
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2004
|
-
function cleanup() {
|
|
2005
|
-
clearTimeout(timer);
|
|
2006
|
-
clearInterval(closedPoll);
|
|
2007
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}` }
|
|
2008
2122
|
});
|
|
2123
|
+
if (!res.ok) await throwApiError(res);
|
|
2124
|
+
const data = await res.json();
|
|
2125
|
+
return data.items;
|
|
2009
2126
|
}
|
|
2010
|
-
async function
|
|
2011
|
-
|
|
2012
|
-
const res = await
|
|
2013
|
-
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}` }
|
|
2014
2131
|
});
|
|
2015
|
-
if (!res.ok)
|
|
2016
|
-
const
|
|
2017
|
-
|
|
2018
|
-
const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
|
|
2019
|
-
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;
|
|
2020
2135
|
}
|
|
2021
|
-
function
|
|
2022
|
-
|
|
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();
|
|
2023
2143
|
}
|
|
2024
|
-
|
|
2025
|
-
function signupWithPasskeyViaPopup() {
|
|
2026
|
-
return new Promise((resolve, reject) => {
|
|
2027
|
-
const popupUrl = `${window.location.origin}/passkey-signup`;
|
|
2028
|
-
const popup = window.open(popupUrl, "blink-passkey-signup");
|
|
2029
|
-
if (!popup) {
|
|
2030
|
-
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2031
|
-
return;
|
|
2032
|
-
}
|
|
2033
|
-
let settled = false;
|
|
2034
|
-
const timer = setTimeout(() => {
|
|
2035
|
-
cleanup();
|
|
2036
|
-
resolve(null);
|
|
2037
|
-
}, SIGNUP_POPUP_TIMEOUT_MS);
|
|
2038
|
-
function onMessage(event) {
|
|
2039
|
-
if (event.origin !== window.location.origin) return;
|
|
2040
|
-
if (event.source !== popup) return;
|
|
2041
|
-
const data = event.data;
|
|
2042
|
-
if (!data || data.type !== "blink:passkey-signup-complete") return;
|
|
2043
|
-
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2044
|
-
settled = true;
|
|
2045
|
-
cleanup();
|
|
2046
|
-
resolve({
|
|
2047
|
-
accessToken: data.accessToken,
|
|
2048
|
-
credentialId: data.credentialId,
|
|
2049
|
-
publicKey: data.publicKey
|
|
2050
|
-
});
|
|
2051
|
-
}
|
|
2052
|
-
window.addEventListener("message", onMessage);
|
|
2053
|
-
const closedPoll = setInterval(() => {
|
|
2054
|
-
if (popup.closed && !settled) {
|
|
2055
|
-
settled = true;
|
|
2056
|
-
cleanup();
|
|
2057
|
-
resolve(null);
|
|
2058
|
-
}
|
|
2059
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2060
|
-
function cleanup() {
|
|
2061
|
-
clearTimeout(timer);
|
|
2062
|
-
clearInterval(closedPoll);
|
|
2063
|
-
window.removeEventListener("message", onMessage);
|
|
2064
|
-
}
|
|
2065
|
-
});
|
|
2066
|
-
}
|
|
2067
|
-
var LOGIN_POPUP_TIMEOUT_MS = 12e4;
|
|
2068
|
-
function loginWithPasskeyViaPopup() {
|
|
2069
|
-
return new Promise((resolve, reject) => {
|
|
2070
|
-
const popupUrl = `${window.location.origin}/passkey-login`;
|
|
2071
|
-
const popup = window.open(popupUrl, "blink-passkey-login");
|
|
2072
|
-
if (!popup) {
|
|
2073
|
-
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2074
|
-
return;
|
|
2075
|
-
}
|
|
2076
|
-
let settled = false;
|
|
2077
|
-
const timer = setTimeout(() => {
|
|
2078
|
-
cleanup();
|
|
2079
|
-
resolve(null);
|
|
2080
|
-
}, LOGIN_POPUP_TIMEOUT_MS);
|
|
2081
|
-
function onMessage(event) {
|
|
2082
|
-
if (event.origin !== window.location.origin) return;
|
|
2083
|
-
if (event.source !== popup) return;
|
|
2084
|
-
const data = event.data;
|
|
2085
|
-
if (!data || data.type !== "blink:passkey-login-complete") return;
|
|
2086
|
-
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2087
|
-
settled = true;
|
|
2088
|
-
cleanup();
|
|
2089
|
-
resolve({
|
|
2090
|
-
accessToken: data.accessToken,
|
|
2091
|
-
credentialId: data.credentialId,
|
|
2092
|
-
publicKey: data.publicKey
|
|
2093
|
-
});
|
|
2094
|
-
}
|
|
2095
|
-
window.addEventListener("message", onMessage);
|
|
2096
|
-
const closedPoll = setInterval(() => {
|
|
2097
|
-
if (popup.closed && !settled) {
|
|
2098
|
-
settled = true;
|
|
2099
|
-
cleanup();
|
|
2100
|
-
resolve(null);
|
|
2101
|
-
}
|
|
2102
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2103
|
-
function cleanup() {
|
|
2104
|
-
clearTimeout(timer);
|
|
2105
|
-
clearInterval(closedPoll);
|
|
2106
|
-
window.removeEventListener("message", onMessage);
|
|
2107
|
-
}
|
|
2108
|
-
});
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
// src/credentialIdEncoding.ts
|
|
2112
|
-
function credentialIdBase64ToBytes(value) {
|
|
2113
|
-
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
2114
|
-
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
2115
|
-
const raw = atob(padded);
|
|
2116
|
-
const bytes = new Uint8Array(raw.length);
|
|
2117
|
-
for (let i = 0; i < raw.length; i++) {
|
|
2118
|
-
bytes[i] = raw.charCodeAt(i);
|
|
2119
|
-
}
|
|
2120
|
-
return bytes;
|
|
2121
|
-
}
|
|
2122
|
-
|
|
2123
|
-
// src/passkeyRpId.ts
|
|
2124
|
-
function normalizeConfiguredDomain(value) {
|
|
2125
|
-
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
2126
|
-
}
|
|
2127
|
-
function resolveRootDomainFromHostname(hostname) {
|
|
2128
|
-
const trimmedHostname = hostname.trim().toLowerCase();
|
|
2129
|
-
if (!trimmedHostname) {
|
|
2130
|
-
return "localhost";
|
|
2131
|
-
}
|
|
2132
|
-
if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
|
|
2133
|
-
return trimmedHostname;
|
|
2134
|
-
}
|
|
2135
|
-
const parts = trimmedHostname.split(".").filter(Boolean);
|
|
2136
|
-
if (parts.length < 2) {
|
|
2137
|
-
return trimmedHostname;
|
|
2138
|
-
}
|
|
2139
|
-
return parts.slice(-2).join(".");
|
|
2140
|
-
}
|
|
2141
|
-
|
|
2142
|
-
// src/hooks/passkeyPublic.ts
|
|
2143
|
-
function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
|
|
2144
|
-
return new Promise((resolve) => {
|
|
2145
|
-
if (typeof document === "undefined") {
|
|
2146
|
-
resolve();
|
|
2147
|
-
return;
|
|
2148
|
-
}
|
|
2149
|
-
if (document.hasFocus()) {
|
|
2150
|
-
resolve();
|
|
2151
|
-
return;
|
|
2152
|
-
}
|
|
2153
|
-
const deadline = Date.now() + timeoutMs;
|
|
2154
|
-
const timer = setInterval(() => {
|
|
2155
|
-
if (document.hasFocus()) {
|
|
2156
|
-
clearInterval(timer);
|
|
2157
|
-
resolve();
|
|
2158
|
-
} else if (Date.now() >= deadline) {
|
|
2159
|
-
clearInterval(timer);
|
|
2160
|
-
resolve();
|
|
2161
|
-
}
|
|
2162
|
-
}, intervalMs);
|
|
2163
|
-
});
|
|
2164
|
-
}
|
|
2165
|
-
function toBase64(buffer) {
|
|
2166
|
-
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
2167
|
-
}
|
|
2168
|
-
function readEnvValue(name) {
|
|
2169
|
-
const meta = import.meta;
|
|
2170
|
-
const metaValue = meta.env?.[name];
|
|
2171
|
-
if (typeof metaValue === "string" && metaValue.trim().length > 0) {
|
|
2172
|
-
return metaValue.trim();
|
|
2173
|
-
}
|
|
2174
|
-
const processValue = globalThis.process?.env?.[name];
|
|
2175
|
-
if (typeof processValue === "string" && processValue.trim().length > 0) {
|
|
2176
|
-
return processValue.trim();
|
|
2177
|
-
}
|
|
2178
|
-
return void 0;
|
|
2179
|
-
}
|
|
2180
|
-
function resolvePasskeyRpId() {
|
|
2181
|
-
const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
|
|
2182
|
-
if (configuredDomain) {
|
|
2183
|
-
return normalizeConfiguredDomain(configuredDomain);
|
|
2184
|
-
}
|
|
2185
|
-
if (typeof window !== "undefined") {
|
|
2186
|
-
return resolveRootDomainFromHostname(window.location.hostname);
|
|
2187
|
-
}
|
|
2188
|
-
return "localhost";
|
|
2189
|
-
}
|
|
2190
|
-
async function deviceHasPasskey(credentialId) {
|
|
2191
|
-
const found = await findDevicePasskey([credentialId]);
|
|
2192
|
-
return found != null;
|
|
2193
|
-
}
|
|
2194
|
-
async function findDevicePasskey(credentialIds) {
|
|
2195
|
-
if (credentialIds.length === 0) return null;
|
|
2196
|
-
try {
|
|
2197
|
-
const challenge = new Uint8Array(32);
|
|
2198
|
-
crypto.getRandomValues(challenge);
|
|
2199
|
-
await waitForDocumentFocus();
|
|
2200
|
-
const assertion = await navigator.credentials.get({
|
|
2201
|
-
publicKey: {
|
|
2202
|
-
challenge,
|
|
2203
|
-
rpId: resolvePasskeyRpId(),
|
|
2204
|
-
allowCredentials: credentialIds.map((id) => ({
|
|
2205
|
-
type: "public-key",
|
|
2206
|
-
id: credentialIdBase64ToBytes(id)
|
|
2207
|
-
})),
|
|
2208
|
-
userVerification: "discouraged",
|
|
2209
|
-
timeout: 3e4
|
|
2210
|
-
}
|
|
2211
|
-
});
|
|
2212
|
-
if (!assertion) return null;
|
|
2213
|
-
return toBase64(assertion.rawId);
|
|
2214
|
-
} catch {
|
|
2215
|
-
return null;
|
|
2216
|
-
}
|
|
2217
|
-
}
|
|
2218
|
-
|
|
2219
|
-
// src/api.ts
|
|
2220
|
-
var api_exports = {};
|
|
2221
|
-
__export(api_exports, {
|
|
2222
|
-
createAccount: () => createAccount,
|
|
2223
|
-
createAccountAuthorizationSession: () => createAccountAuthorizationSession,
|
|
2224
|
-
createManualTransfer: () => createManualTransfer,
|
|
2225
|
-
createTransfer: () => createTransfer,
|
|
2226
|
-
fetchAccount: () => fetchAccount,
|
|
2227
|
-
fetchAccounts: () => fetchAccounts,
|
|
2228
|
-
fetchAuthorizationSession: () => fetchAuthorizationSession,
|
|
2229
|
-
fetchAuthorizationSessionByToken: () => fetchAuthorizationSessionByToken,
|
|
2230
|
-
fetchChains: () => fetchChains,
|
|
2231
|
-
fetchManualTransferSession: () => fetchManualTransferSession,
|
|
2232
|
-
fetchManualTransferSources: () => fetchManualTransferSources,
|
|
2233
|
-
fetchMerchantPublicKey: () => fetchMerchantPublicKey,
|
|
2234
|
-
fetchProviders: () => fetchProviders,
|
|
2235
|
-
fetchTransfer: () => fetchTransfer,
|
|
2236
|
-
fetchUserConfig: () => fetchUserConfig,
|
|
2237
|
-
postTransferQuote: () => postTransferQuote,
|
|
2238
|
-
probeActionCompletion: () => probeActionCompletion,
|
|
2239
|
-
refreshManualTransferQuote: () => refreshManualTransferQuote,
|
|
2240
|
-
regenerateTransferSignPayload: () => regenerateTransferSignPayload,
|
|
2241
|
-
registerPasskey: () => registerPasskey,
|
|
2242
|
-
reportActionCompletion: () => reportActionCompletion,
|
|
2243
|
-
reportPasskeyActivity: () => reportPasskeyActivity,
|
|
2244
|
-
setAuthorizationSessionPaymentIntentAmount: () => setAuthorizationSessionPaymentIntentAmount,
|
|
2245
|
-
setAuthorizationSessionProvider: () => setAuthorizationSessionProvider,
|
|
2246
|
-
signTransfer: () => signTransfer,
|
|
2247
|
-
updateManualTransferDepositTargetChain: () => updateManualTransferDepositTargetChain,
|
|
2248
|
-
updateUserConfig: () => updateUserConfig,
|
|
2249
|
-
updateUserConfigBySession: () => updateUserConfigBySession,
|
|
2250
|
-
waitForActionTransactionReceipt: () => waitForActionTransactionReceipt
|
|
2251
|
-
});
|
|
2252
|
-
var DEBUG_BUFFER_CAPACITY = 200;
|
|
2253
|
-
var nextId = 1;
|
|
2254
|
-
var entries = [];
|
|
2255
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
2256
|
-
function notify() {
|
|
2257
|
-
for (const listener of listeners) {
|
|
2258
|
-
try {
|
|
2259
|
-
listener();
|
|
2260
|
-
} catch (err) {
|
|
2261
|
-
console.error("[blink-sdk][debug-log] listener threw:", err);
|
|
2262
|
-
}
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
function appendDebug(level, message, data) {
|
|
2266
|
-
const entry = {
|
|
2267
|
-
id: nextId++,
|
|
2268
|
-
ts: Date.now(),
|
|
2269
|
-
level,
|
|
2270
|
-
message,
|
|
2271
|
-
data
|
|
2272
|
-
};
|
|
2273
|
-
const next = entries.length >= DEBUG_BUFFER_CAPACITY ? entries.slice(entries.length - DEBUG_BUFFER_CAPACITY + 1) : entries.slice();
|
|
2274
|
-
next.push(entry);
|
|
2275
|
-
entries = next;
|
|
2276
|
-
const prefix = "[blink-sdk][debug]";
|
|
2277
|
-
const sink = level === "error" ? console.error : level === "warn" ? console.warn : console.info;
|
|
2278
|
-
if (data !== void 0) {
|
|
2279
|
-
sink(`${prefix} ${message}`, data);
|
|
2280
|
-
} else {
|
|
2281
|
-
sink(`${prefix} ${message}`);
|
|
2282
|
-
}
|
|
2283
|
-
notify();
|
|
2284
|
-
}
|
|
2285
|
-
function subscribeDebug(listener) {
|
|
2286
|
-
listeners.add(listener);
|
|
2287
|
-
return () => {
|
|
2288
|
-
listeners.delete(listener);
|
|
2289
|
-
};
|
|
2290
|
-
}
|
|
2291
|
-
function getDebugEntries() {
|
|
2292
|
-
return entries;
|
|
2293
|
-
}
|
|
2294
|
-
function clearDebugEntries() {
|
|
2295
|
-
entries = [];
|
|
2296
|
-
notify();
|
|
2297
|
-
}
|
|
2298
|
-
function useBlinkDebugLog() {
|
|
2299
|
-
return useSyncExternalStore(subscribeDebug, getDebugEntries, getDebugEntries);
|
|
2300
|
-
}
|
|
2301
|
-
|
|
2302
|
-
// src/fetchWithRetry.ts
|
|
2303
|
-
var DEFAULT_MAX_RETRIES = 3;
|
|
2304
|
-
var DEFAULT_BASE_DELAY_MS = 500;
|
|
2305
|
-
var DEFAULT_MAX_JITTER_MS = 200;
|
|
2306
|
-
function isNetworkTypeError(err) {
|
|
2307
|
-
return err instanceof TypeError && /fetch|network|load failed/i.test(err.message);
|
|
2308
|
-
}
|
|
2309
|
-
async function fetchWithRetry(input, init, options) {
|
|
2310
|
-
const maxRetries = DEFAULT_MAX_RETRIES;
|
|
2311
|
-
const baseDelayMs = DEFAULT_BASE_DELAY_MS;
|
|
2312
|
-
const maxJitterMs = DEFAULT_MAX_JITTER_MS;
|
|
2313
|
-
const label = String(input).replace(/https?:\/\/[^/]+/, "");
|
|
2314
|
-
let lastError;
|
|
2315
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2316
|
-
try {
|
|
2317
|
-
return await fetch(input, init);
|
|
2318
|
-
} catch (err) {
|
|
2319
|
-
lastError = err;
|
|
2320
|
-
if (!isNetworkTypeError(err)) {
|
|
2321
|
-
throw err;
|
|
2322
|
-
}
|
|
2323
|
-
if (attempt < maxRetries) {
|
|
2324
|
-
const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * maxJitterMs;
|
|
2325
|
-
appendDebug("warn", `fetchWithRetry: network error, retrying ${label}`, {
|
|
2326
|
-
attempt: attempt + 1,
|
|
2327
|
-
maxRetries,
|
|
2328
|
-
delayMs: Math.round(delay),
|
|
2329
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2330
|
-
});
|
|
2331
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
throw lastError;
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
// src/apiError.ts
|
|
2339
|
-
var ApiError = class extends Error {
|
|
2340
|
-
status;
|
|
2341
|
-
code;
|
|
2342
|
-
constructor(status, code, message) {
|
|
2343
|
-
super(message);
|
|
2344
|
-
this.name = "ApiError";
|
|
2345
|
-
this.status = status;
|
|
2346
|
-
this.code = code;
|
|
2347
|
-
}
|
|
2348
|
-
};
|
|
2349
|
-
function isApiError(err) {
|
|
2350
|
-
if (err instanceof ApiError) return true;
|
|
2351
|
-
return typeof err === "object" && err !== null && "name" in err && err.name === "ApiError";
|
|
2352
|
-
}
|
|
2353
|
-
var SVM_SIGN_PAYLOAD_EXPIRED_CODE = "SVM_SIGN_PAYLOAD_EXPIRED";
|
|
2354
|
-
function isSvmSignExpiredError(err) {
|
|
2355
|
-
return isApiError(err) && err.code === SVM_SIGN_PAYLOAD_EXPIRED_CODE;
|
|
2356
|
-
}
|
|
2357
|
-
|
|
2358
|
-
// src/api.ts
|
|
2359
|
-
async function throwApiError(res) {
|
|
2360
|
-
const body = await res.json().catch(() => null);
|
|
2361
|
-
const detail = body?.error ?? body;
|
|
2362
|
-
const msg = detail?.message ?? res.statusText;
|
|
2363
|
-
const code = detail?.code ?? String(res.status);
|
|
2364
|
-
throw new ApiError(res.status, code, `${res.status} \u2014 ${code}: ${msg}`);
|
|
2365
|
-
}
|
|
2366
|
-
async function fetchProviders(apiBaseUrl, token) {
|
|
2367
|
-
const headers = {};
|
|
2368
|
-
if (token) {
|
|
2369
|
-
headers.Authorization = `Bearer ${token}`;
|
|
2370
|
-
}
|
|
2371
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/providers`, { headers });
|
|
2372
|
-
if (!res.ok) await throwApiError(res);
|
|
2373
|
-
const data = await res.json();
|
|
2374
|
-
return data.items;
|
|
2375
|
-
}
|
|
2376
|
-
async function fetchChains(apiBaseUrl, token) {
|
|
2377
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/chains`, {
|
|
2378
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2379
|
-
});
|
|
2380
|
-
if (!res.ok) await throwApiError(res);
|
|
2381
|
-
const data = await res.json();
|
|
2382
|
-
return data.items;
|
|
2383
|
-
}
|
|
2384
|
-
async function fetchAccounts(apiBaseUrl, token, credentialId) {
|
|
2385
|
-
const params = new URLSearchParams({ credentialId });
|
|
2386
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts?${params.toString()}`, {
|
|
2387
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2388
|
-
});
|
|
2389
|
-
if (!res.ok) await throwApiError(res);
|
|
2390
|
-
const data = await res.json();
|
|
2391
|
-
return data.items;
|
|
2392
|
-
}
|
|
2393
|
-
async function fetchAccount(apiBaseUrl, token, accountId, credentialId) {
|
|
2144
|
+
async function fetchAccountBalances(apiBaseUrl, token, accountId, credentialId) {
|
|
2394
2145
|
const params = new URLSearchParams({ credentialId });
|
|
2395
|
-
const res = await fetchWithRetry(
|
|
2396
|
-
|
|
2397
|
-
|
|
2146
|
+
const res = await fetchWithRetry(
|
|
2147
|
+
`${apiBaseUrl}/v1/accounts/${accountId}/balances?${params.toString()}`,
|
|
2148
|
+
{
|
|
2149
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2150
|
+
}
|
|
2151
|
+
);
|
|
2398
2152
|
if (!res.ok) await throwApiError(res);
|
|
2399
2153
|
return await res.json();
|
|
2400
2154
|
}
|
|
@@ -2670,89 +2424,368 @@ async function createManualTransfer(apiBaseUrl, params) {
|
|
|
2670
2424
|
headers: { "Content-Type": "application/json" },
|
|
2671
2425
|
body: JSON.stringify(params)
|
|
2672
2426
|
});
|
|
2673
|
-
if (!res.ok) await throwApiError(res);
|
|
2674
|
-
return await res.json();
|
|
2427
|
+
if (!res.ok) await throwApiError(res);
|
|
2428
|
+
return await res.json();
|
|
2429
|
+
}
|
|
2430
|
+
async function fetchManualTransferSession(apiBaseUrl, sessionId) {
|
|
2431
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/manual-transfers/${sessionId}`);
|
|
2432
|
+
if (!res.ok) await throwApiError(res);
|
|
2433
|
+
return await res.json();
|
|
2434
|
+
}
|
|
2435
|
+
async function updateManualTransferDepositTargetChain(apiBaseUrl, sessionId, selectedChainId) {
|
|
2436
|
+
const res = await fetch(`${apiBaseUrl}/v1/manual-transfers/${sessionId}`, {
|
|
2437
|
+
method: "PATCH",
|
|
2438
|
+
headers: { "Content-Type": "application/json" },
|
|
2439
|
+
body: JSON.stringify({ selectedChainId })
|
|
2440
|
+
});
|
|
2441
|
+
if (!res.ok) await throwApiError(res);
|
|
2442
|
+
return await res.json();
|
|
2443
|
+
}
|
|
2444
|
+
async function refreshManualTransferQuote(apiBaseUrl, sessionId) {
|
|
2445
|
+
const res = await fetch(
|
|
2446
|
+
`${apiBaseUrl}/v1/manual-transfers/${sessionId}/refresh-quote`,
|
|
2447
|
+
{ method: "POST" }
|
|
2448
|
+
);
|
|
2449
|
+
if (!res.ok) await throwApiError(res);
|
|
2450
|
+
return await res.json();
|
|
2451
|
+
}
|
|
2452
|
+
async function reportActionCompletion(apiBaseUrl, actionId, result) {
|
|
2453
|
+
const res = await fetchWithRetry(
|
|
2454
|
+
`${apiBaseUrl}/v1/authorization-actions/${actionId}`,
|
|
2455
|
+
{
|
|
2456
|
+
method: "PATCH",
|
|
2457
|
+
headers: { "Content-Type": "application/json" },
|
|
2458
|
+
body: JSON.stringify({ status: "COMPLETED", result })
|
|
2459
|
+
}
|
|
2460
|
+
);
|
|
2461
|
+
if (!res.ok) await throwApiError(res);
|
|
2462
|
+
return await res.json();
|
|
2463
|
+
}
|
|
2464
|
+
async function waitForActionTransactionReceipt(apiBaseUrl, actionId, txHash) {
|
|
2465
|
+
const res = await fetch(
|
|
2466
|
+
`${apiBaseUrl}/v1/authorization-actions/${actionId}/transaction-receipt`,
|
|
2467
|
+
{
|
|
2468
|
+
method: "POST",
|
|
2469
|
+
headers: { "Content-Type": "application/json" },
|
|
2470
|
+
body: JSON.stringify({ txHash })
|
|
2471
|
+
}
|
|
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
|
+
});
|
|
2675
2681
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
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;
|
|
2680
2693
|
}
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
body: JSON.stringify({ selectedChainId })
|
|
2686
|
-
});
|
|
2687
|
-
if (!res.ok) await throwApiError(res);
|
|
2688
|
-
return await res.json();
|
|
2694
|
+
|
|
2695
|
+
// src/passkeyRpId.ts
|
|
2696
|
+
function normalizeConfiguredDomain(value) {
|
|
2697
|
+
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
2689
2698
|
}
|
|
2690
|
-
|
|
2691
|
-
const
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
if (
|
|
2696
|
-
|
|
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(".");
|
|
2697
2712
|
}
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
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;
|
|
2705
2720
|
}
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
}
|
|
2710
|
-
async function waitForActionTransactionReceipt(apiBaseUrl, actionId, txHash) {
|
|
2711
|
-
const res = await fetch(
|
|
2712
|
-
`${apiBaseUrl}/v1/authorization-actions/${actionId}/transaction-receipt`,
|
|
2713
|
-
{
|
|
2714
|
-
method: "POST",
|
|
2715
|
-
headers: { "Content-Type": "application/json" },
|
|
2716
|
-
body: JSON.stringify({ txHash })
|
|
2721
|
+
if (document.hasFocus()) {
|
|
2722
|
+
resolve();
|
|
2723
|
+
return;
|
|
2717
2724
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
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
|
+
});
|
|
2721
2736
|
}
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
);
|
|
2731
|
-
if (res.ok) {
|
|
2732
|
-
const session = await res.json();
|
|
2733
|
-
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();
|
|
2734
2745
|
}
|
|
2735
|
-
const
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
const message = detail?.message ?? res.statusText ?? `HTTP ${res.status}`;
|
|
2739
|
-
if (res.status === 422 && code === "DEPOSIT_TX_NOT_FOUND") {
|
|
2740
|
-
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();
|
|
2741
2749
|
}
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
]);
|
|
2749
|
-
if (res.status === 422 && code && approvalNotDetectedCodes.has(code)) {
|
|
2750
|
-
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);
|
|
2751
2756
|
}
|
|
2752
|
-
if (
|
|
2753
|
-
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;
|
|
2754
2788
|
}
|
|
2755
|
-
return { detected: false, reason: "error", status: res.status, code, message };
|
|
2756
2789
|
}
|
|
2757
2790
|
|
|
2758
2791
|
// src/transferPolling.ts
|
|
@@ -5783,6 +5816,43 @@ function useAuthorizationExecutor(options) {
|
|
|
5783
5816
|
}),
|
|
5784
5817
|
[]
|
|
5785
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
|
+
}, []);
|
|
5786
5856
|
const cancelPendingExecution = useCallback(() => {
|
|
5787
5857
|
if (selectSourceRejectRef.current) {
|
|
5788
5858
|
selectSourceRejectRef.current(new AuthorizationSessionCancelledError());
|
|
@@ -5790,6 +5860,12 @@ function useAuthorizationExecutor(options) {
|
|
|
5790
5860
|
selectSourceResolverRef.current = null;
|
|
5791
5861
|
setPendingSelectSource(null);
|
|
5792
5862
|
}
|
|
5863
|
+
if (approvalRejectRef.current) {
|
|
5864
|
+
approvalRejectRef.current(new AuthorizationSessionCancelledError());
|
|
5865
|
+
approvalRejectRef.current = null;
|
|
5866
|
+
approvalResolverRef.current = null;
|
|
5867
|
+
}
|
|
5868
|
+
setAwaitingApproval(false);
|
|
5793
5869
|
setError(null);
|
|
5794
5870
|
setCurrentAction(null);
|
|
5795
5871
|
setApproveSplConfirming(null);
|
|
@@ -6192,6 +6268,11 @@ function useAuthorizationExecutor(options) {
|
|
|
6192
6268
|
currentAction,
|
|
6193
6269
|
approveSplConfirming,
|
|
6194
6270
|
pendingSelectSource,
|
|
6271
|
+
awaitingApproval,
|
|
6272
|
+
approvalDestinationAddress,
|
|
6273
|
+
waitForApproval,
|
|
6274
|
+
approveAuthorization,
|
|
6275
|
+
resetApprovalGate,
|
|
6195
6276
|
resolveSelectSource,
|
|
6196
6277
|
cancelPendingExecution,
|
|
6197
6278
|
resetWalletConnect,
|
|
@@ -6486,6 +6567,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6486
6567
|
const [pendingSelectSourceAction, setPendingSelectSourceAction] = useState(null);
|
|
6487
6568
|
const [orchestratorCompleted, setOrchestratorCompleted] = useState(false);
|
|
6488
6569
|
const [sourceSelectionResolved, setSourceSelectionResolved] = useState(false);
|
|
6570
|
+
const [approvalSmartAccountAddress, setApprovalSmartAccountAddress] = useState(null);
|
|
6489
6571
|
const selectSourceResolverRef = useRef(null);
|
|
6490
6572
|
const selectSourceRejectRef = useRef(null);
|
|
6491
6573
|
const submittedApprovePermit2ActionIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
@@ -6511,6 +6593,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6511
6593
|
const cancellation = new AuthorizationSessionCancelledError();
|
|
6512
6594
|
setOrchestratorCompleted(false);
|
|
6513
6595
|
setSourceSelectionResolved(false);
|
|
6596
|
+
setApprovalSmartAccountAddress(null);
|
|
6514
6597
|
if (selectSourceRejectRef.current) {
|
|
6515
6598
|
selectSourceRejectRef.current(cancellation);
|
|
6516
6599
|
selectSourceRejectRef.current = null;
|
|
@@ -6533,6 +6616,10 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6533
6616
|
lastRunRef.current = { sessionId, options };
|
|
6534
6617
|
setOrchestratorCompleted(false);
|
|
6535
6618
|
setSourceSelectionResolved(false);
|
|
6619
|
+
setApprovalSmartAccountAddress(null);
|
|
6620
|
+
if (!options?.keepApprovalGate) {
|
|
6621
|
+
authExecutor.resetApprovalGate();
|
|
6622
|
+
}
|
|
6536
6623
|
if (!authExecutor.beginExecution()) {
|
|
6537
6624
|
appendDebug("warn", "orchestrator:run aborted \u2014 already executing");
|
|
6538
6625
|
return { status: "cancelled" };
|
|
@@ -6561,6 +6648,8 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6561
6648
|
let action = mergedPending[0];
|
|
6562
6649
|
if (completedIds.has(action.id)) break;
|
|
6563
6650
|
const ownerSessionId = actionSessionMap.get(action.id) ?? sessionId;
|
|
6651
|
+
const ownerSessionSca = sessions.find((s) => s.id === ownerSessionId)?.session.smartAccountAddress;
|
|
6652
|
+
if (ownerSessionSca) setApprovalSmartAccountAddress(ownerSessionSca);
|
|
6564
6653
|
console.info("[blink-sdk][orchestrator] Next pending action.", {
|
|
6565
6654
|
actionId: action.id,
|
|
6566
6655
|
actionType: action.type,
|
|
@@ -6585,6 +6674,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6585
6674
|
const autoResult = createSelectSourceResult(action, options.autoResolveSource);
|
|
6586
6675
|
completedIds.add(action.id);
|
|
6587
6676
|
authExecutor.addResult(autoResult);
|
|
6677
|
+
setSourceSelectionResolved(true);
|
|
6588
6678
|
const reportedSession3 = await reportActionCompletionWithLogging(
|
|
6589
6679
|
apiBaseUrl,
|
|
6590
6680
|
action,
|
|
@@ -6678,6 +6768,13 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6678
6768
|
throw new Error(SIGN_PERMIT2_CONFIRMING_MESSAGE);
|
|
6679
6769
|
}
|
|
6680
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
|
+
}
|
|
6681
6778
|
appendDebug("info", `orchestrator:executeAction start ${action.type}`, {
|
|
6682
6779
|
actionId: action.id,
|
|
6683
6780
|
ownerSessionId
|
|
@@ -6874,7 +6971,8 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6874
6971
|
authExecutor.setError(null);
|
|
6875
6972
|
return run(lastRun.sessionId, {
|
|
6876
6973
|
...lastRun.options,
|
|
6877
|
-
probeBeforePrompt: true
|
|
6974
|
+
probeBeforePrompt: true,
|
|
6975
|
+
keepApprovalGate: true
|
|
6878
6976
|
});
|
|
6879
6977
|
}, [authExecutor, run, cancelPendingFlow]);
|
|
6880
6978
|
return {
|
|
@@ -6884,9 +6982,17 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6884
6982
|
resolveSelectSource,
|
|
6885
6983
|
sourceSelectionResolved,
|
|
6886
6984
|
orchestratorCompleted,
|
|
6985
|
+
approvalSmartAccountAddress,
|
|
6887
6986
|
cancelPendingFlow
|
|
6888
6987
|
};
|
|
6889
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
|
+
}
|
|
6890
6996
|
function createSelectSourceResult(action, selection) {
|
|
6891
6997
|
return {
|
|
6892
6998
|
actionId: action.id,
|
|
@@ -7857,6 +7963,26 @@ function deriveSourceTypeAndId(state) {
|
|
|
7857
7963
|
}
|
|
7858
7964
|
return { sourceType: "accountId", sourceId: "" };
|
|
7859
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
|
+
}
|
|
7860
7986
|
function clearStaleSelection(state) {
|
|
7861
7987
|
if (state.selectedAccountId == null) return state;
|
|
7862
7988
|
if (state.desktopWait != null || state.standardDesktopInlineOpenWallet) return state;
|
|
@@ -7878,6 +8004,7 @@ function createInitialState(config) {
|
|
|
7878
8004
|
accounts: [],
|
|
7879
8005
|
chains: [],
|
|
7880
8006
|
loadingData: false,
|
|
8007
|
+
balancesLoading: false,
|
|
7881
8008
|
depositSelectionRefreshing: false,
|
|
7882
8009
|
selectedProviderId: null,
|
|
7883
8010
|
selectedAccountId: null,
|
|
@@ -7907,6 +8034,7 @@ function createInitialState(config) {
|
|
|
7907
8034
|
privyAuthenticated: false,
|
|
7908
8035
|
lastResumedAt: 0,
|
|
7909
8036
|
setupDepositToken: null,
|
|
8037
|
+
setupSpendingLimit: null,
|
|
7910
8038
|
guestWalletPrepared: null,
|
|
7911
8039
|
guestWalletDeeplinksPreparing: false,
|
|
7912
8040
|
amountTooLow: null,
|
|
@@ -7926,6 +8054,7 @@ function clearAuthenticatedSessionState(state) {
|
|
|
7926
8054
|
accounts: [],
|
|
7927
8055
|
chains: [],
|
|
7928
8056
|
loadingData: false,
|
|
8057
|
+
balancesLoading: false,
|
|
7929
8058
|
depositSelectionRefreshing: false,
|
|
7930
8059
|
selectedProviderId: null,
|
|
7931
8060
|
selectedAccountId: null,
|
|
@@ -7951,6 +8080,7 @@ function clearAuthenticatedSessionState(state) {
|
|
|
7951
8080
|
mobileTokenAuthorizationPending: false,
|
|
7952
8081
|
linkSettling: false,
|
|
7953
8082
|
setupDepositToken: null,
|
|
8083
|
+
setupSpendingLimit: null,
|
|
7954
8084
|
guestWalletPrepared: null,
|
|
7955
8085
|
guestWalletDeeplinksPreparing: false,
|
|
7956
8086
|
amountTooLow: null,
|
|
@@ -8019,6 +8149,8 @@ function applyAction(state, action) {
|
|
|
8019
8149
|
providers: action.providers,
|
|
8020
8150
|
accounts: action.accounts,
|
|
8021
8151
|
chains: action.chains,
|
|
8152
|
+
// Accounts arrive balance-free; balances follow via BALANCES_LOADED.
|
|
8153
|
+
balancesLoading: true,
|
|
8022
8154
|
depositSelectionRefreshing: false,
|
|
8023
8155
|
initialDataLoaded: true
|
|
8024
8156
|
};
|
|
@@ -8042,10 +8174,12 @@ function applyAction(state, action) {
|
|
|
8042
8174
|
depositSelectionRefreshing: false
|
|
8043
8175
|
};
|
|
8044
8176
|
case "ACCOUNTS_RELOADED": {
|
|
8177
|
+
const hadBalances = hasAnyBalances(state.accounts);
|
|
8045
8178
|
const next = {
|
|
8046
8179
|
...state,
|
|
8047
|
-
accounts: action.accounts,
|
|
8180
|
+
accounts: carryOverBalances(state.accounts, action.accounts),
|
|
8048
8181
|
providers: action.providers,
|
|
8182
|
+
balancesLoading: !hadBalances,
|
|
8049
8183
|
initialDataLoaded: true
|
|
8050
8184
|
};
|
|
8051
8185
|
if (action.defaults) {
|
|
@@ -8057,6 +8191,24 @@ function applyAction(state, action) {
|
|
|
8057
8191
|
}
|
|
8058
8192
|
return clearStaleSelection(next);
|
|
8059
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
|
+
}
|
|
8060
8212
|
case "SET_DEPOSIT_SELECTION_REFRESHING":
|
|
8061
8213
|
return { ...state, depositSelectionRefreshing: action.value };
|
|
8062
8214
|
case "SAVE_SELECTION":
|
|
@@ -8128,6 +8280,7 @@ function applyAction(state, action) {
|
|
|
8128
8280
|
mobileFlow: false,
|
|
8129
8281
|
mobileTokenAuthorizationPending: false,
|
|
8130
8282
|
setupDepositToken: null,
|
|
8283
|
+
setupSpendingLimit: null,
|
|
8131
8284
|
setupFlowScreen: null
|
|
8132
8285
|
};
|
|
8133
8286
|
case "PAY_ENDED":
|
|
@@ -8373,6 +8526,7 @@ function applyAction(state, action) {
|
|
|
8373
8526
|
linkSettling: false,
|
|
8374
8527
|
savedSelection: null,
|
|
8375
8528
|
setupDepositToken: null,
|
|
8529
|
+
setupSpendingLimit: null,
|
|
8376
8530
|
setupFlowScreen: null,
|
|
8377
8531
|
guestWalletPrepared: null,
|
|
8378
8532
|
guestWalletDeeplinksPreparing: false,
|
|
@@ -8438,8 +8592,10 @@ function applyAction(state, action) {
|
|
|
8438
8592
|
...action.chainId != null ? { chainId: action.chainId } : {}
|
|
8439
8593
|
}
|
|
8440
8594
|
};
|
|
8595
|
+
case "SET_SETUP_SPENDING_LIMIT":
|
|
8596
|
+
return { ...state, setupSpendingLimit: action.limit };
|
|
8441
8597
|
case "CLEAR_SETUP_DEPOSIT_TOKEN":
|
|
8442
|
-
return { ...state, setupDepositToken: null };
|
|
8598
|
+
return { ...state, setupDepositToken: null, setupSpendingLimit: null };
|
|
8443
8599
|
default:
|
|
8444
8600
|
return state;
|
|
8445
8601
|
}
|
|
@@ -9225,9 +9381,12 @@ function PrimaryButton({
|
|
|
9225
9381
|
}
|
|
9226
9382
|
);
|
|
9227
9383
|
}
|
|
9384
|
+
var BUTTON_MIN_HEIGHT = 60;
|
|
9228
9385
|
var progressButtonStyle = (tokens, paused) => ({
|
|
9229
9386
|
position: "relative",
|
|
9230
9387
|
width: "100%",
|
|
9388
|
+
minHeight: BUTTON_MIN_HEIGHT,
|
|
9389
|
+
boxSizing: "border-box",
|
|
9231
9390
|
padding: "18px 24px",
|
|
9232
9391
|
background: `linear-gradient(180deg, ${tokens.accent}88, ${tokens.accentHover}88)`,
|
|
9233
9392
|
color: tokens.accentText,
|
|
@@ -9264,6 +9423,8 @@ var buttonStyle2 = (tokens, state) => {
|
|
|
9264
9423
|
const gradient = `linear-gradient(180deg, ${tokens.accent}, ${tokens.accentHover})`;
|
|
9265
9424
|
return {
|
|
9266
9425
|
width: "100%",
|
|
9426
|
+
minHeight: BUTTON_MIN_HEIGHT,
|
|
9427
|
+
boxSizing: "border-box",
|
|
9267
9428
|
padding: "18px 24px",
|
|
9268
9429
|
// Layer a faint white film over the gradient on hover rather than using
|
|
9269
9430
|
// `filter: brightness()` — the accent gradient is near-black in the new
|
|
@@ -10190,6 +10351,8 @@ function SourcePill({
|
|
|
10190
10351
|
logo,
|
|
10191
10352
|
name,
|
|
10192
10353
|
balance,
|
|
10354
|
+
nameSlot,
|
|
10355
|
+
balanceSlot,
|
|
10193
10356
|
expanded,
|
|
10194
10357
|
showChevron = true,
|
|
10195
10358
|
icon
|
|
@@ -10197,8 +10360,8 @@ function SourcePill({
|
|
|
10197
10360
|
const { tokens } = useBlinkConfig();
|
|
10198
10361
|
return /* @__PURE__ */ jsxs("span", { style: pillStyle2(tokens.bgCardTranslucent), children: [
|
|
10199
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),
|
|
10200
|
-
name && /* @__PURE__ */ jsx("span", { style: labelStyle2(tokens.text), children: name }),
|
|
10201
|
-
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 })),
|
|
10202
10365
|
showChevron && /* @__PURE__ */ jsx("svg", { width: "17", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: expanded ? /* @__PURE__ */ jsx(
|
|
10203
10366
|
"path",
|
|
10204
10367
|
{
|
|
@@ -10261,6 +10424,37 @@ var balanceStyle = (color) => ({
|
|
|
10261
10424
|
color,
|
|
10262
10425
|
whiteSpace: "nowrap"
|
|
10263
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
|
+
}
|
|
10264
10458
|
function formatUsdTwoDecimals(value) {
|
|
10265
10459
|
const n = Number(value);
|
|
10266
10460
|
return (Number.isFinite(n) ? n : 0).toLocaleString("en-US", {
|
|
@@ -10961,6 +11155,14 @@ function LockIcon({ size = 24 }) {
|
|
|
10961
11155
|
/* @__PURE__ */ jsx("path", { d: "M8 11V8a4 4 0 1 1 8 0v3", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" })
|
|
10962
11156
|
] });
|
|
10963
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
|
+
}
|
|
10964
11166
|
function FaceIdIcon({ size = 24 }) {
|
|
10965
11167
|
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
10966
11168
|
/* @__PURE__ */ jsx("path", { d: "M4 9V6a2 2 0 0 1 2-2h3", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }),
|
|
@@ -10974,6 +11176,21 @@ function FaceIdIcon({ size = 24 }) {
|
|
|
10974
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" })
|
|
10975
11177
|
] });
|
|
10976
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
|
+
}
|
|
10977
11194
|
function WalletIcon() {
|
|
10978
11195
|
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
10979
11196
|
/* @__PURE__ */ jsx(
|
|
@@ -11345,7 +11562,7 @@ function CopyAddressButton({
|
|
|
11345
11562
|
}) {
|
|
11346
11563
|
const { tokens } = useBlinkConfig();
|
|
11347
11564
|
const [hovered, setHovered] = useState(false);
|
|
11348
|
-
const short =
|
|
11565
|
+
const short = truncateMiddle(address);
|
|
11349
11566
|
return /* @__PURE__ */ jsxs(
|
|
11350
11567
|
"button",
|
|
11351
11568
|
{
|
|
@@ -13503,7 +13720,6 @@ var errorBannerStyle6 = (tokens) => ({
|
|
|
13503
13720
|
textAlign: "left",
|
|
13504
13721
|
boxSizing: "border-box"
|
|
13505
13722
|
});
|
|
13506
|
-
var SHIMMER_ROWS = 3;
|
|
13507
13723
|
function LinkTokensScreen({
|
|
13508
13724
|
entries: entries2,
|
|
13509
13725
|
selectedIndex,
|
|
@@ -13516,44 +13732,75 @@ function LinkTokensScreen({
|
|
|
13516
13732
|
approving = false
|
|
13517
13733
|
}) {
|
|
13518
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);
|
|
13519
13739
|
const showShimmer = loading && entries2.length === 0;
|
|
13520
13740
|
const showEmpty = !loading && entries2.length === 0;
|
|
13521
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 };
|
|
13522
13746
|
const approveDisabled = loading || approving || entries2.length === 0 || selectedIndex < 0 || selectedIndex >= entries2.length || !!selected?.notSupported;
|
|
13523
|
-
|
|
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
|
+
}
|
|
13524
13797
|
return /* @__PURE__ */ jsxs(
|
|
13525
13798
|
ScreenLayout,
|
|
13526
13799
|
{
|
|
13527
13800
|
scrollableBody: false,
|
|
13528
|
-
footer: /* @__PURE__ */
|
|
13529
|
-
/* @__PURE__ */ jsxs("div", { style: lockBannerStyle2, children: [
|
|
13530
|
-
/* @__PURE__ */ jsx("span", { style: lockIconWrapStyle2(t.text), children: /* @__PURE__ */ jsx(LockIcon3, {}) }),
|
|
13531
|
-
/* @__PURE__ */ jsx("p", { style: lockBannerTextStyle2(t.text), children: "Your passkey is required each time you deposit. Funds cannot move without your approval." })
|
|
13532
|
-
] }),
|
|
13533
|
-
/* @__PURE__ */ jsx(PrimaryButton, { onClick: onApprove, disabled: approveDisabled, loading: approving, children: ctaLabel })
|
|
13534
|
-
] }),
|
|
13801
|
+
footer: /* @__PURE__ */ jsx(PrimaryButton, { onClick: () => onApprove(currentSelection()), disabled: approveDisabled, loading: approving, children: "Review" }),
|
|
13535
13802
|
children: [
|
|
13536
|
-
/* @__PURE__ */ jsx(
|
|
13537
|
-
@keyframes blink-link-tokens-shimmer {
|
|
13538
|
-
0% { background-position: 200% 0; }
|
|
13539
|
-
100% { background-position: -200% 0; }
|
|
13540
|
-
}
|
|
13541
|
-
.blink-link-tokens-list {
|
|
13542
|
-
scrollbar-width: thin;
|
|
13543
|
-
scrollbar-color: ${t.textTertiary} transparent;
|
|
13544
|
-
}
|
|
13545
|
-
.blink-link-tokens-list::-webkit-scrollbar {
|
|
13546
|
-
width: 4px;
|
|
13547
|
-
}
|
|
13548
|
-
.blink-link-tokens-list::-webkit-scrollbar-track {
|
|
13549
|
-
background: transparent;
|
|
13550
|
-
margin: 8px 0;
|
|
13551
|
-
}
|
|
13552
|
-
.blink-link-tokens-list::-webkit-scrollbar-thumb {
|
|
13553
|
-
background: ${t.textTertiary};
|
|
13554
|
-
border-radius: 999px;
|
|
13555
|
-
}
|
|
13556
|
-
` }),
|
|
13803
|
+
/* @__PURE__ */ jsx(ListScrollbarStyles, { textTertiary: t.textTertiary }),
|
|
13557
13804
|
/* @__PURE__ */ jsx(
|
|
13558
13805
|
ScreenHeader,
|
|
13559
13806
|
{
|
|
@@ -13563,71 +13810,198 @@ function LinkTokensScreen({
|
|
|
13563
13810
|
}
|
|
13564
13811
|
),
|
|
13565
13812
|
/* @__PURE__ */ jsxs("div", { style: contentStyle10, children: [
|
|
13566
|
-
/* @__PURE__ */ jsx("
|
|
13567
|
-
/* @__PURE__ */ jsxs("div", {
|
|
13568
|
-
|
|
13569
|
-
|
|
13570
|
-
{
|
|
13571
|
-
"
|
|
13572
|
-
"
|
|
13573
|
-
|
|
13574
|
-
children: [
|
|
13575
|
-
/* @__PURE__ */ jsx("div", { style: shimmerCircleStyle(t.bgInput, t.border) }),
|
|
13576
|
-
/* @__PURE__ */ jsxs("div", { style: shimmerInfoStyle, children: [
|
|
13577
|
-
/* @__PURE__ */ jsx("div", { style: shimmerLineStyle(72, t.bgInput, t.border) }),
|
|
13578
|
-
/* @__PURE__ */ jsx("div", { style: shimmerLineStyle(48, t.bgInput, t.border) })
|
|
13579
|
-
] }),
|
|
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) }),
|
|
13580
13821
|
/* @__PURE__ */ jsx("div", { style: shimmerLineStyle(56, t.bgInput, t.border) })
|
|
13581
|
-
]
|
|
13582
|
-
}
|
|
13583
|
-
|
|
13584
|
-
|
|
13585
|
-
|
|
13586
|
-
|
|
13587
|
-
|
|
13588
|
-
|
|
13589
|
-
|
|
13590
|
-
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
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
|
+
] })
|
|
13599
13899
|
] }),
|
|
13600
|
-
|
|
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
|
+
] })
|
|
13601
13924
|
] })
|
|
13602
13925
|
]
|
|
13603
13926
|
}
|
|
13604
13927
|
);
|
|
13605
13928
|
}
|
|
13606
|
-
function
|
|
13607
|
-
|
|
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() }) });
|
|
13608
13936
|
}
|
|
13609
|
-
function
|
|
13610
|
-
|
|
13611
|
-
|
|
13612
|
-
|
|
13613
|
-
|
|
13614
|
-
|
|
13615
|
-
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
|
|
13619
|
-
|
|
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
|
+
` });
|
|
13620
13975
|
}
|
|
13621
13976
|
var contentStyle10 = {
|
|
13622
13977
|
display: "flex",
|
|
13623
13978
|
flexDirection: "column",
|
|
13624
|
-
alignItems: "center",
|
|
13625
|
-
gap: 16,
|
|
13626
|
-
paddingTop: 8,
|
|
13627
13979
|
flex: 1,
|
|
13628
13980
|
minHeight: 0,
|
|
13629
13981
|
width: "100%"
|
|
13630
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
|
+
};
|
|
13631
14005
|
var wordmarkImgStyle3 = {
|
|
13632
14006
|
height: 22,
|
|
13633
14007
|
width: "auto",
|
|
@@ -13635,9 +14009,9 @@ var wordmarkImgStyle3 = {
|
|
|
13635
14009
|
pointerEvents: "none",
|
|
13636
14010
|
userSelect: "none"
|
|
13637
14011
|
};
|
|
13638
|
-
var headingStyle7 = (color) => ({
|
|
14012
|
+
var headingStyle7 = (color, weight) => ({
|
|
13639
14013
|
fontSize: 24,
|
|
13640
|
-
fontWeight:
|
|
14014
|
+
fontWeight: weight,
|
|
13641
14015
|
lineHeight: "normal",
|
|
13642
14016
|
letterSpacing: 0,
|
|
13643
14017
|
color,
|
|
@@ -13657,68 +14031,155 @@ var listCardStyle = (bg) => ({
|
|
|
13657
14031
|
minHeight: 0,
|
|
13658
14032
|
overflowY: "auto"
|
|
13659
14033
|
});
|
|
13660
|
-
var
|
|
14034
|
+
var pillStyle3 = (bg) => ({
|
|
13661
14035
|
display: "flex",
|
|
13662
14036
|
alignItems: "center",
|
|
13663
14037
|
gap: 16,
|
|
13664
|
-
padding: "12px 16px 12px 12px",
|
|
13665
|
-
background: "transparent",
|
|
13666
|
-
border: "none",
|
|
13667
|
-
borderRadius: 16,
|
|
13668
14038
|
width: "100%",
|
|
13669
|
-
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
|
|
13670
14057
|
};
|
|
13671
|
-
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",
|
|
13672
14086
|
width: 40,
|
|
13673
14087
|
height: 40,
|
|
13674
|
-
borderRadius: "50%",
|
|
13675
14088
|
flexShrink: 0,
|
|
13676
|
-
|
|
13677
|
-
|
|
13678
|
-
|
|
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
|
|
13679
14110
|
});
|
|
13680
|
-
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",
|
|
13681
14121
|
display: "flex",
|
|
13682
|
-
|
|
13683
|
-
gap:
|
|
13684
|
-
flex: 1,
|
|
14122
|
+
alignItems: "center",
|
|
14123
|
+
gap: 8,
|
|
13685
14124
|
minWidth: 0
|
|
13686
14125
|
};
|
|
13687
|
-
var
|
|
13688
|
-
|
|
13689
|
-
|
|
13690
|
-
|
|
13691
|
-
|
|
13692
|
-
|
|
13693
|
-
|
|
14126
|
+
var limitAmountStyle = (color, weight) => ({
|
|
14127
|
+
display: "inline-flex",
|
|
14128
|
+
alignItems: "baseline",
|
|
14129
|
+
gap: 1,
|
|
14130
|
+
color,
|
|
14131
|
+
fontSize: "1rem",
|
|
14132
|
+
fontWeight: weight
|
|
13694
14133
|
});
|
|
13695
|
-
var
|
|
13696
|
-
|
|
13697
|
-
|
|
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",
|
|
13698
14142
|
color,
|
|
13699
|
-
|
|
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
|
|
13700
14153
|
});
|
|
13701
|
-
var
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
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) => ({
|
|
13707
14167
|
display: "flex",
|
|
13708
14168
|
flexDirection: "column",
|
|
13709
14169
|
gap: 16,
|
|
13710
|
-
width: "100%"
|
|
13711
|
-
|
|
13712
|
-
|
|
14170
|
+
width: "100%",
|
|
14171
|
+
boxSizing: "border-box",
|
|
14172
|
+
padding: 16,
|
|
14173
|
+
borderRadius: 20,
|
|
14174
|
+
background: bg
|
|
14175
|
+
});
|
|
14176
|
+
var infoRowStyle2 = {
|
|
13713
14177
|
display: "flex",
|
|
13714
14178
|
alignItems: "flex-start",
|
|
13715
|
-
gap:
|
|
13716
|
-
|
|
13717
|
-
borderRadius: 16,
|
|
13718
|
-
width: "100%",
|
|
13719
|
-
boxSizing: "border-box"
|
|
14179
|
+
gap: 14,
|
|
14180
|
+
width: "100%"
|
|
13720
14181
|
};
|
|
13721
|
-
var
|
|
14182
|
+
var infoIconStyle = (color) => ({
|
|
13722
14183
|
flexShrink: 0,
|
|
13723
14184
|
width: 24,
|
|
13724
14185
|
height: 24,
|
|
@@ -13727,14 +14188,60 @@ var lockIconWrapStyle2 = (color) => ({
|
|
|
13727
14188
|
justifyContent: "center",
|
|
13728
14189
|
color
|
|
13729
14190
|
});
|
|
13730
|
-
var
|
|
13731
|
-
|
|
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,
|
|
13732
14223
|
flex: 1,
|
|
13733
|
-
|
|
13734
|
-
|
|
13735
|
-
|
|
13736
|
-
|
|
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"
|
|
13737
14239
|
});
|
|
14240
|
+
var bannerSlotStyle = {
|
|
14241
|
+
display: "flex",
|
|
14242
|
+
justifyContent: "center",
|
|
14243
|
+
width: "100%"
|
|
14244
|
+
};
|
|
13738
14245
|
function DepositCompleteScreen({ amount }) {
|
|
13739
14246
|
const { tokens } = useBlinkConfig();
|
|
13740
14247
|
return /* @__PURE__ */ jsxs(ScreenLayout, { children: [
|
|
@@ -13796,6 +14303,7 @@ function SelectDepositSourceScreen({
|
|
|
13796
14303
|
tokenOptions,
|
|
13797
14304
|
selectedTokenSymbol,
|
|
13798
14305
|
selectedChainName,
|
|
14306
|
+
balancesLoading,
|
|
13799
14307
|
selectedWalletId,
|
|
13800
14308
|
onPickToken,
|
|
13801
14309
|
useDeeplink,
|
|
@@ -13812,8 +14320,9 @@ function SelectDepositSourceScreen({
|
|
|
13812
14320
|
const [pendingMobileSelection, setPendingMobileSelection] = useState(null);
|
|
13813
14321
|
const [preparedAuthorization, setPreparedAuthorization] = useState(null);
|
|
13814
14322
|
const [preparingAuthorization, setPreparingAuthorization] = useState(false);
|
|
14323
|
+
const isAuthorizedOption = (opt) => !opt.status || opt.status === "AUTHORIZED";
|
|
13815
14324
|
const selectableOptions = tokenOptions.filter(
|
|
13816
|
-
(opt) => opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minTransferAmountUsd)
|
|
14325
|
+
(opt) => isAuthorizedOption(opt) || opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minTransferAmountUsd)
|
|
13817
14326
|
);
|
|
13818
14327
|
const authorized = selectableOptions.filter(
|
|
13819
14328
|
(opt) => !opt.status || opt.status === "AUTHORIZED"
|
|
@@ -13967,6 +14476,30 @@ function SelectDepositSourceScreen({
|
|
|
13967
14476
|
"data-testid": "select-deposit-source-scroll-content",
|
|
13968
14477
|
style: contentStyle12,
|
|
13969
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
|
+
)) }) }),
|
|
13970
14503
|
orderedAccounts.map((account) => {
|
|
13971
14504
|
const authRows = authorized.filter((opt) => rowMatchesSection(opt, account));
|
|
13972
14505
|
const reqRows = requiresAuth.filter((opt) => rowMatchesSection(opt, account)).sort((a, b) => Number(!!a.notSupported) - Number(!!b.notSupported));
|
|
@@ -14181,6 +14714,7 @@ function ShimmerBlock({
|
|
|
14181
14714
|
);
|
|
14182
14715
|
}
|
|
14183
14716
|
function DepositScreen({
|
|
14717
|
+
balancesLoading,
|
|
14184
14718
|
availableBalance,
|
|
14185
14719
|
remainingLimit,
|
|
14186
14720
|
initialAmount,
|
|
@@ -14234,6 +14768,10 @@ function DepositScreen({
|
|
|
14234
14768
|
const showMobileKeypad = isEntryMode && !isDesktop;
|
|
14235
14769
|
const [keypadOpen, setKeypadOpen] = useState(showMobileKeypad);
|
|
14236
14770
|
const [tokenPickerOpen, setTokenPickerOpen] = useState(false);
|
|
14771
|
+
const hasLoadedQuoteRef = useRef(false);
|
|
14772
|
+
if (!quoteLoading) {
|
|
14773
|
+
hasLoadedQuoteRef.current = true;
|
|
14774
|
+
}
|
|
14237
14775
|
const selectableTokenOptions = tokenOptions?.filter(
|
|
14238
14776
|
(opt) => opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minDepositFloor)
|
|
14239
14777
|
) ?? [];
|
|
@@ -14251,11 +14789,11 @@ function DepositScreen({
|
|
|
14251
14789
|
}, []);
|
|
14252
14790
|
const selectedAccount = accounts?.find((a) => a.id === selectedAccountId);
|
|
14253
14791
|
const selectedProviderName = selectedAccount?.name ?? "Wallet";
|
|
14254
|
-
const isLowBalance = availableBalance < minDepositFloor;
|
|
14255
|
-
const insufficientFunds = availableBalance < validationAmount;
|
|
14792
|
+
const isLowBalance = !balancesLoading && availableBalance < minDepositFloor;
|
|
14793
|
+
const insufficientFunds = !balancesLoading && availableBalance < validationAmount;
|
|
14256
14794
|
const needsAuthorization = selectedTokenStatus != null && selectedTokenStatus !== "AUTHORIZED" && !insufficientFunds && !isLowBalance;
|
|
14257
14795
|
const exceedsLimit = remainingLimit != null && validationAmount > remainingLimit && !isLowBalance && !needsAuthorization;
|
|
14258
|
-
const canDeposit = validationAmount >= minDepositFloor && !exceedsLimit && !isLowBalance && !insufficientFunds && !needsAuthorization && !processing;
|
|
14796
|
+
const canDeposit = validationAmount >= minDepositFloor && !balancesLoading && !exceedsLimit && !isLowBalance && !insufficientFunds && !needsAuthorization && !processing;
|
|
14259
14797
|
const hasAccountPill = !!accounts && accounts.length > 0;
|
|
14260
14798
|
const pillClickable = canOpenInlineSheet || !!onSelectToken;
|
|
14261
14799
|
const formattedBalance = selectedTokenSymbol != null ? `$${formatUsdTwoDecimals2(availableBalance)}` : void 0;
|
|
@@ -14278,6 +14816,7 @@ function DepositScreen({
|
|
|
14278
14816
|
accounts: depositSourceAccounts,
|
|
14279
14817
|
selectedAccountId: selectedAccountId ?? null,
|
|
14280
14818
|
tokenOptions: selectableTokenOptions,
|
|
14819
|
+
balancesLoading,
|
|
14281
14820
|
selectedTokenSymbol,
|
|
14282
14821
|
selectedChainName,
|
|
14283
14822
|
selectedWalletId: selectedWalletId ?? null,
|
|
@@ -14299,7 +14838,7 @@ function DepositScreen({
|
|
|
14299
14838
|
const mobileEntryWithKeypad = isEntryMode && !isDesktop && keypadOpen;
|
|
14300
14839
|
const showLiveMobileHero = isEntryMode && !isDesktop && keypadOpen;
|
|
14301
14840
|
const showDesktopInputHero = isEntryMode && isDesktop;
|
|
14302
|
-
const
|
|
14841
|
+
const showFeeRow = validationAmount > 0;
|
|
14303
14842
|
let primaryButton;
|
|
14304
14843
|
if (mobileEntryWithKeypad) {
|
|
14305
14844
|
const canConfirmEntry = canContinue(amountEntryValue ?? "");
|
|
@@ -14429,7 +14968,32 @@ function DepositScreen({
|
|
|
14429
14968
|
"aria-label": tokenAriaLabel,
|
|
14430
14969
|
"aria-expanded": canOpenInlineSheet ? tokenPickerOpen : void 0,
|
|
14431
14970
|
"aria-haspopup": canOpenInlineSheet ? "listbox" : void 0,
|
|
14432
|
-
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(
|
|
14433
14997
|
SourcePill,
|
|
14434
14998
|
{
|
|
14435
14999
|
icon: /* @__PURE__ */ jsx(
|
|
@@ -14449,7 +15013,7 @@ function DepositScreen({
|
|
|
14449
15013
|
)
|
|
14450
15014
|
}
|
|
14451
15015
|
),
|
|
14452
|
-
|
|
15016
|
+
/* @__PURE__ */ jsx("div", { "data-testid": "deposit-fee-row", style: redesignFeeRowStyle, children: showFeeRow && (quoteLoading && !hasLoadedQuoteRef.current ? /* @__PURE__ */ jsx(
|
|
14453
15017
|
"div",
|
|
14454
15018
|
{
|
|
14455
15019
|
"data-testid": "deposit-fee-shimmer",
|
|
@@ -14467,7 +15031,7 @@ function DepositScreen({
|
|
|
14467
15031
|
}
|
|
14468
15032
|
)
|
|
14469
15033
|
}
|
|
14470
|
-
) : /* @__PURE__ */ jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" }) })
|
|
15034
|
+
) : /* @__PURE__ */ jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" })) })
|
|
14471
15035
|
] }),
|
|
14472
15036
|
/* @__PURE__ */ jsx("div", { style: bannerSlotStyle2, children: mobileEntryWithKeypad ? null : showLowFunds ? /* @__PURE__ */ jsx(
|
|
14473
15037
|
NotificationBanner,
|
|
@@ -15844,7 +16408,7 @@ function OpenWalletScreen({
|
|
|
15844
16408
|
return /* @__PURE__ */ jsxs(
|
|
15845
16409
|
ScreenLayout,
|
|
15846
16410
|
{
|
|
15847
|
-
footer: /* @__PURE__ */ jsxs("div", { style:
|
|
16411
|
+
footer: /* @__PURE__ */ jsxs("div", { style: footerStackStyle4, children: [
|
|
15848
16412
|
error && /* @__PURE__ */ jsx(InfoBanner, { children: error }),
|
|
15849
16413
|
/* @__PURE__ */ jsxs(
|
|
15850
16414
|
SecondaryButton,
|
|
@@ -15891,7 +16455,7 @@ function OpenWalletScreen({
|
|
|
15891
16455
|
return /* @__PURE__ */ jsxs(
|
|
15892
16456
|
ScreenLayout,
|
|
15893
16457
|
{
|
|
15894
|
-
footer: /* @__PURE__ */ jsxs("div", { style:
|
|
16458
|
+
footer: /* @__PURE__ */ jsxs("div", { style: footerStackStyle4, children: [
|
|
15895
16459
|
error && /* @__PURE__ */ jsx(InfoBanner, { children: error }),
|
|
15896
16460
|
/* @__PURE__ */ jsxs(
|
|
15897
16461
|
SecondaryButton,
|
|
@@ -15975,7 +16539,7 @@ var primaryClusterStyle = {
|
|
|
15975
16539
|
textAlign: "center",
|
|
15976
16540
|
gap: 12
|
|
15977
16541
|
};
|
|
15978
|
-
var
|
|
16542
|
+
var footerStackStyle4 = {
|
|
15979
16543
|
display: "flex",
|
|
15980
16544
|
flexDirection: "column",
|
|
15981
16545
|
gap: 8
|
|
@@ -16103,6 +16667,12 @@ function ApprovingInWalletScreen({
|
|
|
16103
16667
|
chainName,
|
|
16104
16668
|
step,
|
|
16105
16669
|
complete,
|
|
16670
|
+
signing,
|
|
16671
|
+
spendingLimitLabel,
|
|
16672
|
+
destinationAddress,
|
|
16673
|
+
tokenLogoUri,
|
|
16674
|
+
awaitingApproval,
|
|
16675
|
+
onApprove,
|
|
16106
16676
|
error,
|
|
16107
16677
|
onRetry,
|
|
16108
16678
|
onBack,
|
|
@@ -16112,10 +16682,11 @@ function ApprovingInWalletScreen({
|
|
|
16112
16682
|
onOpenWallet
|
|
16113
16683
|
}) {
|
|
16114
16684
|
const { tokens: t } = useBlinkConfig();
|
|
16115
|
-
const showDelayedRetry = useDelayedRetry(error == null && onRetry != null);
|
|
16685
|
+
const showDelayedRetry = useDelayedRetry(!awaitingApproval && error == null && onRetry != null);
|
|
16116
16686
|
const retryVisible = onRetry != null && (error != null || showDelayedRetry);
|
|
16117
16687
|
const token = tokenSymbol ?? "token";
|
|
16118
|
-
const
|
|
16688
|
+
const chain = chainName ?? "chain";
|
|
16689
|
+
const steps = buildSteps(step, tokenSymbol, complete, signing);
|
|
16119
16690
|
const foregroundNavigation = foregroundDeeplink ? classifyWalletDeeplinkNavigation(foregroundDeeplink) : null;
|
|
16120
16691
|
const foregroundWithJs = foregroundNavigation ? shouldOpenWithJavaScript(foregroundNavigation) : false;
|
|
16121
16692
|
const showOpenWalletButton = !!foregroundDeeplink && onOpenWallet != null;
|
|
@@ -16123,11 +16694,7 @@ function ApprovingInWalletScreen({
|
|
|
16123
16694
|
ScreenLayout,
|
|
16124
16695
|
{
|
|
16125
16696
|
scrollableBody: false,
|
|
16126
|
-
footer: /* @__PURE__ */ jsxs("div", { style:
|
|
16127
|
-
/* @__PURE__ */ jsxs("div", { style: lockBannerStyle3, children: [
|
|
16128
|
-
/* @__PURE__ */ jsx("span", { style: lockIconWrapStyle3(t.text), children: /* @__PURE__ */ jsx(LockIcon4, {}) }),
|
|
16129
|
-
/* @__PURE__ */ jsx("p", { style: lockBannerTextStyle3(t.text), children: "Your passkey is required each time you deposit. Funds cannot move without your approval." })
|
|
16130
|
-
] }),
|
|
16697
|
+
footer: /* @__PURE__ */ jsxs("div", { style: footerStackStyle5, children: [
|
|
16131
16698
|
showOpenWalletButton && /* @__PURE__ */ jsxs(
|
|
16132
16699
|
SecondaryButton,
|
|
16133
16700
|
{
|
|
@@ -16144,7 +16711,7 @@ function ApprovingInWalletScreen({
|
|
|
16144
16711
|
retryVisible ? /* @__PURE__ */ jsxs("div", { style: retryStackStyle, children: [
|
|
16145
16712
|
error && /* @__PURE__ */ jsx(InfoBanner, { children: error }),
|
|
16146
16713
|
/* @__PURE__ */ jsx(OutlineButton, { onClick: onRetry, children: "Retry" })
|
|
16147
|
-
] }) : /* @__PURE__ */ jsx(PrimaryButton, { spinnerOnly: true })
|
|
16714
|
+
] }) : awaitingApproval ? /* @__PURE__ */ jsx(PrimaryButton, { onClick: onApprove, children: "Approve" }) : /* @__PURE__ */ jsx(PrimaryButton, { spinnerOnly: true })
|
|
16148
16715
|
] }),
|
|
16149
16716
|
children: [
|
|
16150
16717
|
/* @__PURE__ */ jsx(
|
|
@@ -16156,7 +16723,39 @@ function ApprovingInWalletScreen({
|
|
|
16156
16723
|
}
|
|
16157
16724
|
),
|
|
16158
16725
|
/* @__PURE__ */ jsxs("div", { style: contentStyle16, children: [
|
|
16159
|
-
/* @__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
|
+
] }),
|
|
16160
16759
|
/* @__PURE__ */ jsxs("div", { style: cardStyle2(t.bgRecessed, t.radiusLg), children: [
|
|
16161
16760
|
/* @__PURE__ */ jsx("style", { children: SPIN_KEYFRAMES_CSS2 }),
|
|
16162
16761
|
steps.map((s, i) => /* @__PURE__ */ jsx(ChecklistRow, { step: s }, i))
|
|
@@ -16166,6 +16765,32 @@ function ApprovingInWalletScreen({
|
|
|
16166
16765
|
}
|
|
16167
16766
|
);
|
|
16168
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
|
+
}
|
|
16169
16794
|
function ChecklistRow({ step }) {
|
|
16170
16795
|
const { tokens: t } = useBlinkConfig();
|
|
16171
16796
|
const isComplete = step.status === "complete";
|
|
@@ -16178,7 +16803,7 @@ function ChecklistRow({ step }) {
|
|
|
16178
16803
|
fill: t.accentText
|
|
16179
16804
|
}
|
|
16180
16805
|
) }) }) : isActive ? /* @__PURE__ */ jsx(RowSpinner, { color: t.accent }) : /* @__PURE__ */ jsx("span", { style: pendingBadgeStyle(t.bgRecessed) }),
|
|
16181
|
-
/* @__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 })
|
|
16182
16807
|
] });
|
|
16183
16808
|
}
|
|
16184
16809
|
function RowSpinner({ color }) {
|
|
@@ -16213,22 +16838,28 @@ function RowSpinner({ color }) {
|
|
|
16213
16838
|
}
|
|
16214
16839
|
);
|
|
16215
16840
|
}
|
|
16216
|
-
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) {
|
|
16217
16848
|
const token = tokenSymbol ?? "token";
|
|
16218
|
-
const
|
|
16219
|
-
const
|
|
16849
|
+
const passkeyLabel = "Assign spending right to your passkey";
|
|
16850
|
+
const activeStatus = signing ? "active" : "pending";
|
|
16220
16851
|
if (step === "spl") {
|
|
16221
|
-
return [{ label: passkeyLabel, status: complete ? "complete" :
|
|
16852
|
+
return [{ label: passkeyLabel, status: complete ? "complete" : activeStatus }];
|
|
16222
16853
|
}
|
|
16223
|
-
const approveLabel = `Approve ${token}
|
|
16854
|
+
const approveLabel = `Approve ${token} for spending via Permit2`;
|
|
16224
16855
|
return [
|
|
16225
16856
|
{
|
|
16226
16857
|
label: approveLabel,
|
|
16227
|
-
status: complete || step !== "approve" ? "complete" :
|
|
16858
|
+
status: complete || step !== "approve" ? "complete" : activeStatus
|
|
16228
16859
|
},
|
|
16229
16860
|
{
|
|
16230
16861
|
label: passkeyLabel,
|
|
16231
|
-
status: complete ? "complete" : step === "approve" ? "pending" :
|
|
16862
|
+
status: complete ? "complete" : step === "approve" ? "pending" : activeStatus
|
|
16232
16863
|
}
|
|
16233
16864
|
];
|
|
16234
16865
|
}
|
|
@@ -16244,18 +16875,6 @@ function useDelayedRetry(enabled) {
|
|
|
16244
16875
|
}, [enabled]);
|
|
16245
16876
|
return visible;
|
|
16246
16877
|
}
|
|
16247
|
-
function LockIcon4() {
|
|
16248
|
-
return /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
16249
|
-
"path",
|
|
16250
|
-
{
|
|
16251
|
-
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",
|
|
16252
|
-
stroke: "currentColor",
|
|
16253
|
-
strokeWidth: "2",
|
|
16254
|
-
strokeLinecap: "round",
|
|
16255
|
-
strokeLinejoin: "round"
|
|
16256
|
-
}
|
|
16257
|
-
) });
|
|
16258
|
-
}
|
|
16259
16878
|
var wordmarkImgStyle5 = {
|
|
16260
16879
|
height: 22,
|
|
16261
16880
|
width: "auto",
|
|
@@ -16273,15 +16892,84 @@ var contentStyle16 = {
|
|
|
16273
16892
|
minHeight: 0,
|
|
16274
16893
|
width: "100%"
|
|
16275
16894
|
};
|
|
16276
|
-
var headingStyle13 = (color) => ({
|
|
16895
|
+
var headingStyle13 = (color, fontWeight) => ({
|
|
16277
16896
|
fontSize: 24,
|
|
16278
|
-
fontWeight
|
|
16897
|
+
fontWeight,
|
|
16279
16898
|
lineHeight: "normal",
|
|
16280
16899
|
letterSpacing: 0,
|
|
16281
16900
|
color,
|
|
16282
|
-
margin: 0,
|
|
16901
|
+
margin: "8px 0 8px",
|
|
16283
16902
|
textAlign: "center"
|
|
16284
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
|
+
};
|
|
16285
16973
|
var cardStyle2 = (bg, radius) => ({
|
|
16286
16974
|
background: bg,
|
|
16287
16975
|
borderRadius: radius,
|
|
@@ -16289,7 +16977,9 @@ var cardStyle2 = (bg, radius) => ({
|
|
|
16289
16977
|
width: "100%",
|
|
16290
16978
|
boxSizing: "border-box",
|
|
16291
16979
|
display: "flex",
|
|
16980
|
+
flex: 1,
|
|
16292
16981
|
flexDirection: "column",
|
|
16982
|
+
justifyContent: "center",
|
|
16293
16983
|
gap: 4
|
|
16294
16984
|
});
|
|
16295
16985
|
var rowStyle6 = {
|
|
@@ -16297,10 +16987,25 @@ var rowStyle6 = {
|
|
|
16297
16987
|
alignItems: "center",
|
|
16298
16988
|
gap: 16,
|
|
16299
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,
|
|
16300
16994
|
borderRadius: 16,
|
|
16301
16995
|
width: "100%",
|
|
16302
16996
|
boxSizing: "border-box"
|
|
16303
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
|
+
});
|
|
16304
17009
|
var completeBadgeStyle = (color) => ({
|
|
16305
17010
|
width: 24,
|
|
16306
17011
|
height: 24,
|
|
@@ -16318,13 +17023,13 @@ var pendingBadgeStyle = (color) => ({
|
|
|
16318
17023
|
background: color,
|
|
16319
17024
|
flexShrink: 0
|
|
16320
17025
|
});
|
|
16321
|
-
var labelStyle7 = (color) => ({
|
|
17026
|
+
var labelStyle7 = (color, fontWeight) => ({
|
|
16322
17027
|
fontSize: "1rem",
|
|
16323
|
-
fontWeight
|
|
17028
|
+
fontWeight,
|
|
16324
17029
|
lineHeight: 1.25,
|
|
16325
17030
|
color
|
|
16326
17031
|
});
|
|
16327
|
-
var
|
|
17032
|
+
var footerStackStyle5 = {
|
|
16328
17033
|
display: "flex",
|
|
16329
17034
|
flexDirection: "column",
|
|
16330
17035
|
gap: 16,
|
|
@@ -16336,32 +17041,6 @@ var retryStackStyle = {
|
|
|
16336
17041
|
gap: 8,
|
|
16337
17042
|
width: "100%"
|
|
16338
17043
|
};
|
|
16339
|
-
var lockBannerStyle3 = {
|
|
16340
|
-
display: "flex",
|
|
16341
|
-
alignItems: "flex-start",
|
|
16342
|
-
gap: 16,
|
|
16343
|
-
padding: "12px 16px 12px 12px",
|
|
16344
|
-
borderRadius: 16,
|
|
16345
|
-
width: "100%",
|
|
16346
|
-
boxSizing: "border-box"
|
|
16347
|
-
};
|
|
16348
|
-
var lockIconWrapStyle3 = (color) => ({
|
|
16349
|
-
flexShrink: 0,
|
|
16350
|
-
width: 24,
|
|
16351
|
-
height: 24,
|
|
16352
|
-
display: "inline-flex",
|
|
16353
|
-
alignItems: "center",
|
|
16354
|
-
justifyContent: "center",
|
|
16355
|
-
color
|
|
16356
|
-
});
|
|
16357
|
-
var lockBannerTextStyle3 = (color) => ({
|
|
16358
|
-
margin: 0,
|
|
16359
|
-
flex: 1,
|
|
16360
|
-
fontSize: 12,
|
|
16361
|
-
fontWeight: 500,
|
|
16362
|
-
lineHeight: "normal",
|
|
16363
|
-
color
|
|
16364
|
-
});
|
|
16365
17044
|
function ConfirmSignScreen({
|
|
16366
17045
|
walletName,
|
|
16367
17046
|
chainFamily,
|
|
@@ -16487,7 +17166,7 @@ function TokenPickerScreen({
|
|
|
16487
17166
|
}
|
|
16488
17167
|
const entries2 = [];
|
|
16489
17168
|
for (const wallet of account.wallets) {
|
|
16490
|
-
for (const source of wallet.sources) {
|
|
17169
|
+
for (const source of wallet.sources ?? []) {
|
|
16491
17170
|
const visibleBalance = source.balance.available.amount;
|
|
16492
17171
|
if (!isSelectableDepositSourceAmountUsd(visibleBalance, minDepositFloor)) continue;
|
|
16493
17172
|
entries2.push({
|
|
@@ -17328,22 +18007,45 @@ function buildOpenWalletScreenProps({
|
|
|
17328
18007
|
function isApprovingInWalletAction(actionType) {
|
|
17329
18008
|
return actionType === "APPROVE_PERMIT2" || actionType === "SIGN_PERMIT2" || actionType === "APPROVE_SPL";
|
|
17330
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
|
+
}
|
|
17331
18020
|
function buildApprovingInWalletScreenProps({
|
|
17332
18021
|
flow,
|
|
17333
18022
|
remote,
|
|
18023
|
+
derived,
|
|
17334
18024
|
handlers
|
|
17335
18025
|
}) {
|
|
17336
18026
|
const { state } = flow;
|
|
17337
18027
|
const setupToken = state.setupDepositToken;
|
|
17338
|
-
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");
|
|
17339
18033
|
const isSolana = setupToken != null && state.chains.find((c) => c.name === setupToken.chainName)?.chainFamily === "svm";
|
|
17340
18034
|
const step = actionType === "SIGN_PERMIT2" ? "sign" : actionType === "APPROVE_SPL" ? "spl" : actionType == null && isSolana ? "spl" : "approve";
|
|
17341
18035
|
const complete = remote.authExecutorCompleted && actionType == null && !remote.authExecutorExecuting;
|
|
18036
|
+
const awaitingApproval = remote.authExecutorAwaitingApproval ?? false;
|
|
18037
|
+
const signing = isApprovingInWalletAction(actionType) && !awaitingApproval && !complete;
|
|
17342
18038
|
return {
|
|
17343
18039
|
tokenSymbol: setupToken?.symbol ?? null,
|
|
17344
18040
|
chainName: setupToken?.chainName ?? null,
|
|
17345
18041
|
step,
|
|
17346
18042
|
complete,
|
|
18043
|
+
signing,
|
|
18044
|
+
spendingLimitLabel,
|
|
18045
|
+
destinationAddress,
|
|
18046
|
+
tokenLogoUri: derived.selectedSource?.token.logoURI ?? null,
|
|
18047
|
+
awaitingApproval,
|
|
18048
|
+
onApprove: handlers.onApprove,
|
|
17347
18049
|
error: flow.state.error || remote.authExecutorError,
|
|
17348
18050
|
onRetry: handlers.onRetryAuthorization,
|
|
17349
18051
|
onLogout: flow.isDesktop ? handlers.onLogout : void 0,
|
|
@@ -17362,7 +18064,7 @@ function buildLinkTokensScreenProps({
|
|
|
17362
18064
|
handlers
|
|
17363
18065
|
}) {
|
|
17364
18066
|
const sourceOptions = remote.pendingSelectSource?.metadata?.options ?? [];
|
|
17365
|
-
const
|
|
18067
|
+
const supportedRows = derived.selectSourceChoices.flatMap(
|
|
17366
18068
|
(chain) => chain.tokens.map((t) => {
|
|
17367
18069
|
const rawOption = sourceOptions.find(
|
|
17368
18070
|
(option) => option.chainName === chain.chainName && option.tokenSymbol === t.tokenSymbol
|
|
@@ -17382,6 +18084,10 @@ function buildLinkTokensScreenProps({
|
|
|
17382
18084
|
};
|
|
17383
18085
|
})
|
|
17384
18086
|
);
|
|
18087
|
+
const rowContexts = [
|
|
18088
|
+
...supportedRows.filter((row) => isVisibleUsdAmountAtTwoDecimals(row.balanceUsd)),
|
|
18089
|
+
...supportedRows.filter((row) => !isVisibleUsdAmountAtTwoDecimals(row.balanceUsd))
|
|
18090
|
+
];
|
|
17385
18091
|
rowContexts.push(
|
|
17386
18092
|
...derived.selectSourceUnsupportedChoices.flatMap(
|
|
17387
18093
|
(chain) => chain.tokens.map((t) => ({
|
|
@@ -17403,6 +18109,17 @@ function buildLinkTokensScreenProps({
|
|
|
17403
18109
|
balanceLabel: `${formatNativeAmount(native.amount)} ${native.tokenSymbol}`
|
|
17404
18110
|
}))
|
|
17405
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
|
+
}
|
|
17406
18123
|
const entries2 = rowContexts.map(
|
|
17407
18124
|
({ symbol, chainName, balanceUsd, notSupported, tokenLogoUri, balanceLabel }) => ({
|
|
17408
18125
|
tokenSymbol: symbol,
|
|
@@ -17415,8 +18132,6 @@ function buildLinkTokensScreenProps({
|
|
|
17415
18132
|
})
|
|
17416
18133
|
);
|
|
17417
18134
|
const firstSupportedIndex = rowContexts.findIndex((row) => !row.notSupported);
|
|
17418
|
-
const selectedTokenSymbol = flow.state.setupDepositToken?.symbol ?? derived.selectSourceRecommended?.tokenSymbol;
|
|
17419
|
-
const selectedChainName = flow.state.setupDepositToken?.chainName ?? derived.selectSourceRecommended?.chainName;
|
|
17420
18135
|
const selectedIndex = (() => {
|
|
17421
18136
|
if (!selectedTokenSymbol || !selectedChainName) {
|
|
17422
18137
|
return firstSupportedIndex;
|
|
@@ -17508,9 +18223,10 @@ function buildDepositScreenProps({
|
|
|
17508
18223
|
const registryChainIds = derived.walletConnectChainIdsByAccount[account.id] ?? null;
|
|
17509
18224
|
const approvedChainIds = account.walletConnect?.approvedChainIds ?? null;
|
|
17510
18225
|
for (const wallet of account.wallets) {
|
|
17511
|
-
for (const source of wallet.sources) {
|
|
18226
|
+
for (const source of wallet.sources ?? []) {
|
|
17512
18227
|
const balance = source.balance.available.amount;
|
|
17513
|
-
|
|
18228
|
+
const isAuthorized = source.token.status === "AUTHORIZED";
|
|
18229
|
+
if (!isAuthorized && !isSelectableDepositSourceAmountUsd(balance, minDepositFloor)) continue;
|
|
17514
18230
|
const chain = flow.state.chains.find((c) => c.name === wallet.chain.name);
|
|
17515
18231
|
const requiresAuth = source.token.status != null && source.token.status !== "AUTHORIZED";
|
|
17516
18232
|
const notSupported = requiresAuth && chain?.commonId != null && registryChainIds != null && !registryChainIds.includes(chain.commonId) && !(approvedChainIds?.includes(chain.commonId) ?? false);
|
|
@@ -17532,7 +18248,10 @@ function buildDepositScreenProps({
|
|
|
17532
18248
|
}
|
|
17533
18249
|
return {
|
|
17534
18250
|
merchantName,
|
|
17535
|
-
|
|
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,
|
|
17536
18255
|
remainingLimit: selectedSource != null ? selectedSource.remainingAllowance ?? null : selectedAccount?.remainingAllowance ?? null,
|
|
17537
18256
|
tokenCount,
|
|
17538
18257
|
initialAmount: parsedAmt,
|
|
@@ -17723,6 +18442,28 @@ function StepRendererContent({
|
|
|
17723
18442
|
screen
|
|
17724
18443
|
}) {
|
|
17725
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]);
|
|
17726
18467
|
switch (screen) {
|
|
17727
18468
|
case "loading":
|
|
17728
18469
|
return /* @__PURE__ */ jsx(BlinkLoadingScreen, {});
|
|
@@ -17751,11 +18492,9 @@ function StepRendererContent({
|
|
|
17751
18492
|
case "wallet-picker":
|
|
17752
18493
|
return /* @__PURE__ */ jsx(WalletPickerScreen, { ...buildWalletPickerScreenProps(input) });
|
|
17753
18494
|
case "open-wallet": {
|
|
17754
|
-
const
|
|
17755
|
-
const inSigningAction = isApprovingInWalletAction(currentActionType);
|
|
17756
|
-
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;
|
|
17757
18496
|
const settlingPostSign = input.flow.state.linkSettling;
|
|
17758
|
-
if (
|
|
18497
|
+
if (pastSourceSelection || settlingPostSign) {
|
|
17759
18498
|
return /* @__PURE__ */ jsx(ApprovingInWalletScreen, { ...buildApprovingInWalletScreenProps(input) });
|
|
17760
18499
|
}
|
|
17761
18500
|
return /* @__PURE__ */ jsx(OpenWalletScreen, { ...buildOpenWalletScreenProps(input) });
|
|
@@ -17765,12 +18504,9 @@ function StepRendererContent({
|
|
|
17765
18504
|
if (phase.step === "wallet-setup" && phase.pendingSourceWait) {
|
|
17766
18505
|
return /* @__PURE__ */ jsx(LinkTokensScreen, { ...buildLinkTokensScreenProps(input) });
|
|
17767
18506
|
}
|
|
17768
|
-
const
|
|
17769
|
-
input.remote.authExecutorCurrentAction?.type
|
|
17770
|
-
);
|
|
17771
|
-
const justResolvedSelectSource = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null;
|
|
18507
|
+
const pastSourceSelection = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null && input.remote.sourceSelectionResolved;
|
|
17772
18508
|
const settlingPostSign = input.flow.state.linkSettling;
|
|
17773
|
-
if (
|
|
18509
|
+
if (pastSourceSelection || settlingPostSign) {
|
|
17774
18510
|
return /* @__PURE__ */ jsx(ApprovingInWalletScreen, { ...buildApprovingInWalletScreenProps(input) });
|
|
17775
18511
|
}
|
|
17776
18512
|
return /* @__PURE__ */ jsx(LinkTokensScreen, { ...buildLinkTokensScreenProps(input) });
|
|
@@ -17854,11 +18590,12 @@ var PaymentErrorBoundary = class extends Component {
|
|
|
17854
18590
|
};
|
|
17855
18591
|
function selectedSourceForWallet(selectedWallet, selectedTokenSymbol, depositAmount, priorityContext) {
|
|
17856
18592
|
if (!selectedWallet) return null;
|
|
18593
|
+
const walletSources = selectedWallet.sources ?? [];
|
|
17857
18594
|
if (selectedTokenSymbol) {
|
|
17858
|
-
return
|
|
18595
|
+
return walletSources.find((s) => s.token.symbol === selectedTokenSymbol) ?? null;
|
|
17859
18596
|
}
|
|
17860
18597
|
const walletChainName = selectedWallet.chain.name;
|
|
17861
|
-
const ranked =
|
|
18598
|
+
const ranked = walletSources.map((source, index) => {
|
|
17862
18599
|
return { source, index };
|
|
17863
18600
|
}).sort((a, b) => {
|
|
17864
18601
|
const priority = compareDepositSourcePriority(
|
|
@@ -17906,7 +18643,7 @@ function computeDerivedState(state, depositAmount = 0, priorityContext) {
|
|
|
17906
18643
|
let maxSourceBalance = 0;
|
|
17907
18644
|
for (const acct of state.accounts) {
|
|
17908
18645
|
for (const wallet of acct.wallets) {
|
|
17909
|
-
for (const source of wallet.sources) {
|
|
18646
|
+
for (const source of wallet.sources ?? []) {
|
|
17910
18647
|
if (source.balance.available.amount > maxSourceBalance) {
|
|
17911
18648
|
maxSourceBalance = source.balance.available.amount;
|
|
17912
18649
|
}
|
|
@@ -17916,7 +18653,7 @@ function computeDerivedState(state, depositAmount = 0, priorityContext) {
|
|
|
17916
18653
|
let tokenCount = 0;
|
|
17917
18654
|
for (const acct of state.accounts) {
|
|
17918
18655
|
for (const wallet of acct.wallets) {
|
|
17919
|
-
tokenCount += wallet.sources.filter((s) => s.balance.available.amount > 0).length;
|
|
18656
|
+
tokenCount += (wallet.sources ?? []).filter((s) => s.balance.available.amount > 0).length;
|
|
17920
18657
|
}
|
|
17921
18658
|
}
|
|
17922
18659
|
return {
|
|
@@ -18136,6 +18873,31 @@ function usePasskeyHandlers(dispatch, apiBaseUrl) {
|
|
|
18136
18873
|
};
|
|
18137
18874
|
}
|
|
18138
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
|
+
|
|
18139
18901
|
// src/transferSourceResolution.ts
|
|
18140
18902
|
var STANDARD_TRANSFER_SOURCE_UNAVAILABLE_ERROR = "Selected payment source is no longer available. Choose or link an active wallet before depositing.";
|
|
18141
18903
|
function resolveStandardTransferSource({
|
|
@@ -18187,14 +18949,14 @@ function useTransferHandlers(deps) {
|
|
|
18187
18949
|
const processingStartedAtRef = useRef(null);
|
|
18188
18950
|
const pollingTransferIdRef = useRef(null);
|
|
18189
18951
|
const depositSelectionReloadCountRef = useRef(0);
|
|
18190
|
-
const reloadAccounts = useCallback(async () => {
|
|
18952
|
+
const reloadAccounts = useCallback(async (opts) => {
|
|
18191
18953
|
depositSelectionReloadCountRef.current += 1;
|
|
18192
18954
|
if (depositSelectionReloadCountRef.current === 1) {
|
|
18193
18955
|
dispatch({ type: "SET_DEPOSIT_SELECTION_REFRESHING", value: true });
|
|
18194
18956
|
}
|
|
18195
18957
|
try {
|
|
18196
18958
|
const token = await getAccessToken();
|
|
18197
|
-
if (!token || !activeCredentialId) return;
|
|
18959
|
+
if (!token || !activeCredentialId) return null;
|
|
18198
18960
|
const [accts, prov] = await Promise.all([
|
|
18199
18961
|
fetchAccounts(apiBaseUrl, token, activeCredentialId),
|
|
18200
18962
|
fetchProviders(apiBaseUrl, token)
|
|
@@ -18218,6 +18980,20 @@ function useTransferHandlers(deps) {
|
|
|
18218
18980
|
defaults,
|
|
18219
18981
|
resetSelectedTokenSymbol
|
|
18220
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;
|
|
18221
18997
|
} finally {
|
|
18222
18998
|
depositSelectionReloadCountRef.current = Math.max(
|
|
18223
18999
|
0,
|
|
@@ -18413,7 +19189,7 @@ function useSourceSelectionHandlers(_dispatch, orchestrator, options) {
|
|
|
18413
19189
|
const selectSourceChoices = useMemo(() => {
|
|
18414
19190
|
if (!pendingSelectSourceAction) return [];
|
|
18415
19191
|
const sourceOptions = pendingSelectSourceAction.metadata?.options ?? [];
|
|
18416
|
-
return buildSelectSourceChoices(sourceOptions, minTransferAmountUsd);
|
|
19192
|
+
return buildSelectSourceChoices(sourceOptions, minTransferAmountUsd, true);
|
|
18417
19193
|
}, [pendingSelectSourceAction, minTransferAmountUsd]);
|
|
18418
19194
|
const selectSourceUnsupportedChoices = useMemo(() => {
|
|
18419
19195
|
if (!pendingSelectSourceAction) return [];
|
|
@@ -18599,13 +19375,6 @@ function buildReauthorizationSessionOptions(params) {
|
|
|
18599
19375
|
return options;
|
|
18600
19376
|
}
|
|
18601
19377
|
|
|
18602
|
-
// src/tokenAuthorizationRunOptions.ts
|
|
18603
|
-
function buildDesktopTokenAuthorizationOnlyRunOptions(chainName, tokenSymbol) {
|
|
18604
|
-
return {
|
|
18605
|
-
autoResolveSource: { chainName, tokenSymbol }
|
|
18606
|
-
};
|
|
18607
|
-
}
|
|
18608
|
-
|
|
18609
19378
|
// src/oneTapDefaults.ts
|
|
18610
19379
|
var DEFAULT_ONE_TAP_ALLOWANCE_USD = 1e4;
|
|
18611
19380
|
async function ensureDefaultOneTapAllowance(apiBaseUrl, token) {
|
|
@@ -18626,7 +19395,7 @@ function resolveReauthorizationTarget(input) {
|
|
|
18626
19395
|
};
|
|
18627
19396
|
}
|
|
18628
19397
|
const wallet = input.account?.wallets.find((candidate) => candidate.id === input.selectedWalletId);
|
|
18629
|
-
const source = wallet?.sources
|
|
19398
|
+
const source = wallet?.sources?.find(
|
|
18630
19399
|
(candidate) => input.selectedTokenSymbol ? candidate.token.symbol === input.selectedTokenSymbol : candidate.token.status === "AUTHORIZED"
|
|
18631
19400
|
);
|
|
18632
19401
|
return {
|
|
@@ -18637,6 +19406,28 @@ function resolveReauthorizationTarget(input) {
|
|
|
18637
19406
|
};
|
|
18638
19407
|
}
|
|
18639
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
|
+
|
|
18640
19431
|
// src/walletConnectLinks.ts
|
|
18641
19432
|
function buildWalletConnectDeeplink(links, walletConnectUri) {
|
|
18642
19433
|
const baseLink = links?.native ?? links?.universal;
|
|
@@ -18768,6 +19559,8 @@ function useProviderHandlers(deps) {
|
|
|
18768
19559
|
setupDepositToken,
|
|
18769
19560
|
tryStartExtensionConnectForReownWallet
|
|
18770
19561
|
} = deps;
|
|
19562
|
+
const setupDepositTokenRef = useRef(setupDepositToken);
|
|
19563
|
+
setupDepositTokenRef.current = setupDepositToken;
|
|
18771
19564
|
const checkWalletConnectChainSupport = useMemo(
|
|
18772
19565
|
() => deps.checkWalletConnectChainSupport ?? ((reownWalletId, chainId) => checkReownWalletChainSupport(BLINK_WC_PROJECT_ID, reownWalletId, chainId)),
|
|
18773
19566
|
[deps.checkWalletConnectChainSupport]
|
|
@@ -18983,7 +19776,7 @@ function useProviderHandlers(deps) {
|
|
|
18983
19776
|
}
|
|
18984
19777
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
18985
19778
|
try {
|
|
18986
|
-
await reloadAccounts();
|
|
19779
|
+
await reloadAccounts({ awaitBalances: true });
|
|
18987
19780
|
} finally {
|
|
18988
19781
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
18989
19782
|
}
|
|
@@ -19110,7 +19903,7 @@ function useProviderHandlers(deps) {
|
|
|
19110
19903
|
}
|
|
19111
19904
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19112
19905
|
try {
|
|
19113
|
-
await reloadAccounts();
|
|
19906
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19114
19907
|
} finally {
|
|
19115
19908
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19116
19909
|
}
|
|
@@ -19334,7 +20127,7 @@ function useProviderHandlers(deps) {
|
|
|
19334
20127
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
19335
20128
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19336
20129
|
try {
|
|
19337
|
-
await reloadAccounts();
|
|
20130
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19338
20131
|
} finally {
|
|
19339
20132
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19340
20133
|
}
|
|
@@ -19436,7 +20229,9 @@ function useProviderHandlers(deps) {
|
|
|
19436
20229
|
const result = await orchestrator.run(
|
|
19437
20230
|
sessionId,
|
|
19438
20231
|
{
|
|
19439
|
-
|
|
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.
|
|
19440
20235
|
// Pin the existing account id so pairing and signing share one
|
|
19441
20236
|
// runtime (see handleSelectWalletConnectWallet).
|
|
19442
20237
|
walletConnectRuntimeKey: runtimeAccountId,
|
|
@@ -19444,10 +20239,11 @@ function useProviderHandlers(deps) {
|
|
|
19444
20239
|
// Require the target token's chain at pairing. A reused session
|
|
19445
20240
|
// that doesn't approve it re-pairs with the chain required; a
|
|
19446
20241
|
// wallet that approves the connection without it fails
|
|
19447
|
-
// OPEN_PROVIDER with a clear error instead of the
|
|
19448
|
-
//
|
|
19449
|
-
//
|
|
19450
|
-
//
|
|
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.
|
|
19451
20247
|
walletConnectRequiredChainId: chainId,
|
|
19452
20248
|
walletConnectRequiredChainName: inlineChain.name,
|
|
19453
20249
|
onWalletConnectDisplayUri: (uri) => {
|
|
@@ -19483,11 +20279,17 @@ function useProviderHandlers(deps) {
|
|
|
19483
20279
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
19484
20280
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19485
20281
|
try {
|
|
19486
|
-
await reloadAccounts();
|
|
20282
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19487
20283
|
} finally {
|
|
19488
20284
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19489
20285
|
}
|
|
19490
|
-
|
|
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
|
+
});
|
|
19491
20293
|
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19492
20294
|
} catch (err) {
|
|
19493
20295
|
captureException(err);
|
|
@@ -19802,10 +20604,7 @@ function useProviderHandlers(deps) {
|
|
|
19802
20604
|
walletDeeplinks: walletDeeplinks ?? null,
|
|
19803
20605
|
providerId: matchedProvider?.id ?? null
|
|
19804
20606
|
});
|
|
19805
|
-
const result = await orchestrator.run(
|
|
19806
|
-
session.id,
|
|
19807
|
-
buildDesktopTokenAuthorizationOnlyRunOptions(inlineChain.name, tokenSymbol)
|
|
19808
|
-
);
|
|
20607
|
+
const result = await orchestrator.run(session.id);
|
|
19809
20608
|
if (result.status === "cancelled") {
|
|
19810
20609
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
19811
20610
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
@@ -19817,14 +20616,25 @@ function useProviderHandlers(deps) {
|
|
|
19817
20616
|
}
|
|
19818
20617
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
19819
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
|
+
};
|
|
19820
20625
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19821
20626
|
try {
|
|
19822
|
-
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" });
|
|
19823
20635
|
} finally {
|
|
19824
20636
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19825
20637
|
}
|
|
19826
|
-
dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol, accountId: _accountId });
|
|
19827
|
-
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19828
20638
|
} catch (err) {
|
|
19829
20639
|
captureException(err);
|
|
19830
20640
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
@@ -20284,6 +21094,15 @@ function useDataLoadEffect(deps) {
|
|
|
20284
21094
|
hasActiveCredential: !!state.activeCredentialId,
|
|
20285
21095
|
loading: loadingDataRef.current
|
|
20286
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
|
+
});
|
|
20287
21106
|
if (loadAction === "reset") {
|
|
20288
21107
|
loadingDataRef.current = false;
|
|
20289
21108
|
dispatch({ type: "DATA_LOAD_END" });
|
|
@@ -20301,13 +21120,21 @@ function useDataLoadEffect(deps) {
|
|
|
20301
21120
|
const load = async () => {
|
|
20302
21121
|
dispatch({ type: "DATA_LOAD_START" });
|
|
20303
21122
|
try {
|
|
21123
|
+
appendDebug("warn", "dataLoad: requesting access token");
|
|
20304
21124
|
const token = await getAccessTokenRef.current();
|
|
21125
|
+
appendDebug("warn", "dataLoad: token result", { hasToken: !!token, cancelled });
|
|
20305
21126
|
if (!token) throw new Error("Not authenticated");
|
|
20306
21127
|
const [prov, accts, chn] = await Promise.all([
|
|
20307
21128
|
fetchProviders(apiBaseUrl, token),
|
|
20308
21129
|
fetchAccounts(apiBaseUrl, token, credentialId),
|
|
20309
21130
|
fetchChains(apiBaseUrl, token)
|
|
20310
21131
|
]);
|
|
21132
|
+
appendDebug("warn", "dataLoad: fetch resolved", {
|
|
21133
|
+
cancelled,
|
|
21134
|
+
providers: prov.length,
|
|
21135
|
+
accounts: accts.length,
|
|
21136
|
+
chains: chn.length
|
|
21137
|
+
});
|
|
20311
21138
|
if (cancelled) return;
|
|
20312
21139
|
const parsedAmt = depositAmountRef.current != null ? depositAmountRef.current : 0;
|
|
20313
21140
|
const priorityContext = resolveDepositPriorityContext(accts, chn, destination);
|
|
@@ -20333,7 +21160,16 @@ function useDataLoadEffect(deps) {
|
|
|
20333
21160
|
resetSelectedTokenSymbol
|
|
20334
21161
|
});
|
|
20335
21162
|
if (clearMobile) clearMobileFlowState();
|
|
21163
|
+
void fetchBalancesByAccountId(apiBaseUrl, token, credentialId, accts).then(
|
|
21164
|
+
(balancesByAccountId) => {
|
|
21165
|
+
dispatch({ type: "BALANCES_LOADED", balancesByAccountId });
|
|
21166
|
+
}
|
|
21167
|
+
);
|
|
20336
21168
|
} catch (err) {
|
|
21169
|
+
appendDebug("error", "dataLoad: threw", {
|
|
21170
|
+
cancelled,
|
|
21171
|
+
message: err instanceof Error ? err.message : String(err)
|
|
21172
|
+
});
|
|
20337
21173
|
if (!cancelled) {
|
|
20338
21174
|
captureException(err);
|
|
20339
21175
|
dispatch({
|
|
@@ -20350,6 +21186,7 @@ function useDataLoadEffect(deps) {
|
|
|
20350
21186
|
};
|
|
20351
21187
|
load();
|
|
20352
21188
|
return () => {
|
|
21189
|
+
appendDebug("warn", "dataLoadEffect cleanup (cancelling in-flight load)");
|
|
20353
21190
|
cancelled = true;
|
|
20354
21191
|
loadingDataRef.current = false;
|
|
20355
21192
|
dispatch({ type: "DATA_LOAD_END" });
|
|
@@ -21485,7 +22322,7 @@ function BlinkPaymentInner({
|
|
|
21485
22322
|
const handleSetDepositToken = useCallback((symbol, chainName, walletId, tokenAddress, chainId) => {
|
|
21486
22323
|
dispatch({ type: "SET_SETUP_DEPOSIT_TOKEN", symbol, chainName, walletId, tokenAddress, chainId });
|
|
21487
22324
|
}, []);
|
|
21488
|
-
const handleConfirmSetupDeposit = useCallback(async () => {
|
|
22325
|
+
const handleConfirmSetupDeposit = useCallback(async (limit) => {
|
|
21489
22326
|
const plan = planConfirmSetupDeposit({
|
|
21490
22327
|
setupDepositToken: state.setupDepositToken,
|
|
21491
22328
|
setupSelectedSourceOption,
|
|
@@ -21495,6 +22332,19 @@ function BlinkPaymentInner({
|
|
|
21495
22332
|
dispatch({ type: "SET_ERROR", error: plan.error });
|
|
21496
22333
|
return;
|
|
21497
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 });
|
|
21498
22348
|
for (const action of plan.actions) {
|
|
21499
22349
|
dispatch(action);
|
|
21500
22350
|
}
|
|
@@ -21503,6 +22353,8 @@ function BlinkPaymentInner({
|
|
|
21503
22353
|
state.selectedAccountId,
|
|
21504
22354
|
state.setupDepositToken,
|
|
21505
22355
|
setupSelectedSourceOption,
|
|
22356
|
+
effectiveGetAccessToken,
|
|
22357
|
+
apiBaseUrl,
|
|
21506
22358
|
dispatch,
|
|
21507
22359
|
orchestrator
|
|
21508
22360
|
]);
|
|
@@ -21563,6 +22415,9 @@ function BlinkPaymentInner({
|
|
|
21563
22415
|
}
|
|
21564
22416
|
})();
|
|
21565
22417
|
}, [orchestrator]);
|
|
22418
|
+
const handleApprove = useCallback(() => {
|
|
22419
|
+
authExecutor.approveAuthorization();
|
|
22420
|
+
}, [authExecutor]);
|
|
21566
22421
|
const handleSetPhase = useCallback((phase) => {
|
|
21567
22422
|
clearScreenErrors();
|
|
21568
22423
|
if (phase.step === "deposit") {
|
|
@@ -21584,6 +22439,7 @@ function BlinkPaymentInner({
|
|
|
21584
22439
|
onConfirmSign: transfer.handleConfirmSign,
|
|
21585
22440
|
onRetryMobileStatus: mobileFlow.handleRetryMobileStatus,
|
|
21586
22441
|
onRetryAuthorization: handleAuthorizationRetry,
|
|
22442
|
+
onApprove: handleApprove,
|
|
21587
22443
|
onRetryTransferSigning: transfer.handleRetryTransferSigning,
|
|
21588
22444
|
onBackFromOpenWallet: handleBackFromOpenWallet,
|
|
21589
22445
|
onLogout: handleLogout,
|
|
@@ -21654,6 +22510,7 @@ function BlinkPaymentInner({
|
|
|
21654
22510
|
handleConfirmSetupDeposit,
|
|
21655
22511
|
handleBackFromSetupDeposit,
|
|
21656
22512
|
handleAuthorizationRetry,
|
|
22513
|
+
handleApprove,
|
|
21657
22514
|
disconnectWallets,
|
|
21658
22515
|
state.desktopWait?.walletForegroundLink
|
|
21659
22516
|
]);
|
|
@@ -21693,6 +22550,9 @@ function BlinkPaymentInner({
|
|
|
21693
22550
|
authExecutorError: authExecutor.error,
|
|
21694
22551
|
authExecutorExecuting: authExecutor.executing,
|
|
21695
22552
|
authExecutorCurrentAction: authExecutor.currentAction,
|
|
22553
|
+
authExecutorAwaitingApproval: authExecutor.awaitingApproval,
|
|
22554
|
+
authExecutorApprovalDestinationAddress: authExecutor.approvalDestinationAddress,
|
|
22555
|
+
authApprovalSmartAccountAddress: orchestrator.approvalSmartAccountAddress,
|
|
21696
22556
|
authExecutorCompleted: orchestrator.orchestratorCompleted,
|
|
21697
22557
|
transferSigningSigning: transferSigning.signing,
|
|
21698
22558
|
transferSigningError: transferSigning.error,
|