@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.cjs
CHANGED
|
@@ -82,7 +82,11 @@ var darkTheme = {
|
|
|
82
82
|
shadowLg: "0 18px 44px rgba(0,0,0,0.42)",
|
|
83
83
|
radius: "14px",
|
|
84
84
|
radiusLg: "24px",
|
|
85
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
85
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
86
|
+
fontWeightRegular: 400,
|
|
87
|
+
fontWeightMedium: 500,
|
|
88
|
+
fontWeightSemibold: 600,
|
|
89
|
+
fontWeightBold: 700
|
|
86
90
|
};
|
|
87
91
|
var lightTheme = {
|
|
88
92
|
bg: "#ebf9fb",
|
|
@@ -114,7 +118,11 @@ var lightTheme = {
|
|
|
114
118
|
shadowLg: "0 20px 48px rgba(19, 61, 75, 0.14)",
|
|
115
119
|
radius: "14px",
|
|
116
120
|
radiusLg: "24px",
|
|
117
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
121
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
122
|
+
fontWeightRegular: 400,
|
|
123
|
+
fontWeightMedium: 500,
|
|
124
|
+
fontWeightSemibold: 600,
|
|
125
|
+
fontWeightBold: 700
|
|
118
126
|
};
|
|
119
127
|
var lightTransparentTheme = {
|
|
120
128
|
...lightTheme
|
|
@@ -157,7 +165,13 @@ var lightThemeNew = {
|
|
|
157
165
|
shadowLg: "0 20px 48px rgba(0, 0, 0, 0.14)",
|
|
158
166
|
radius: "14px",
|
|
159
167
|
radiusLg: "24px",
|
|
160
|
-
|
|
168
|
+
// Figma redesign uses Inter; fall back to the system stack when the host
|
|
169
|
+
// page hasn't loaded the Inter webfont.
|
|
170
|
+
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
171
|
+
fontWeightRegular: 400,
|
|
172
|
+
fontWeightMedium: 500,
|
|
173
|
+
fontWeightSemibold: 600,
|
|
174
|
+
fontWeightBold: 700
|
|
161
175
|
};
|
|
162
176
|
var darkThemeNew = { ...lightThemeNew };
|
|
163
177
|
var lightTransparentThemeNew = { ...lightThemeNew };
|
|
@@ -327,7 +341,7 @@ function resolveDepositPriorityContext(accounts, chains, destination) {
|
|
|
327
341
|
for (const account of accounts) {
|
|
328
342
|
for (const wallet of account.wallets) {
|
|
329
343
|
if (wallet.chain.name !== destinationChainName) continue;
|
|
330
|
-
const source = wallet.sources.find(
|
|
344
|
+
const source = (wallet.sources ?? []).find(
|
|
331
345
|
(candidate) => candidate.address.toLowerCase() === destinationTokenAddress
|
|
332
346
|
);
|
|
333
347
|
if (source) {
|
|
@@ -361,6 +375,11 @@ function getWalletAddress(wallet) {
|
|
|
361
375
|
const address = wallet.name.trim();
|
|
362
376
|
return address.length > 0 ? address : null;
|
|
363
377
|
}
|
|
378
|
+
function truncateMiddle(address, head = 8, tail = 6) {
|
|
379
|
+
const trimmed = address.trim();
|
|
380
|
+
if (trimmed.length <= head + tail + 4) return trimmed;
|
|
381
|
+
return `${trimmed.slice(0, head)}...${trimmed.slice(-tail)}`;
|
|
382
|
+
}
|
|
364
383
|
function getAddressableWallets(account) {
|
|
365
384
|
return account.wallets.filter((wallet) => getWalletAddress(wallet) != null);
|
|
366
385
|
}
|
|
@@ -368,7 +387,7 @@ function getPreferredDepositWallet(account, transferAmount, priorityContext) {
|
|
|
368
387
|
const wallets = getAddressableWallets(account);
|
|
369
388
|
if (wallets.length === 0) return null;
|
|
370
389
|
const ranked = wallets.map((wallet, walletIndex) => {
|
|
371
|
-
const bestSource = wallet.sources.map((source, sourceIndex) => ({
|
|
390
|
+
const bestSource = (wallet.sources ?? []).map((source, sourceIndex) => ({
|
|
372
391
|
source,
|
|
373
392
|
sourceIndex
|
|
374
393
|
})).sort((a, b) => {
|
|
@@ -393,7 +412,7 @@ function getPreferredDepositWallet(account, transferAmount, priorityContext) {
|
|
|
393
412
|
if (a.wallet.status !== b.wallet.status) {
|
|
394
413
|
return a.wallet.status === "ACTIVE" ? -1 : 1;
|
|
395
414
|
}
|
|
396
|
-
const balanceDiff = b.wallet.balance
|
|
415
|
+
const balanceDiff = (b.wallet.balance?.available.amount ?? 0) - (a.wallet.balance?.available.amount ?? 0);
|
|
397
416
|
if (balanceDiff !== 0) return balanceDiff;
|
|
398
417
|
return a.walletIndex - b.walletIndex;
|
|
399
418
|
});
|
|
@@ -409,7 +428,7 @@ function resolveDepositSelectionAfterRefresh(accounts, transferAmount, prev, pri
|
|
|
409
428
|
const wallet = acct?.wallets.find((w) => w.id === selectedWalletId);
|
|
410
429
|
if (wallet) {
|
|
411
430
|
if (selectedTokenSymbol) {
|
|
412
|
-
const hasToken = wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
|
|
431
|
+
const hasToken = wallet.sources == null || wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
|
|
413
432
|
if (hasToken) {
|
|
414
433
|
return {
|
|
415
434
|
defaults: { accountId: selectedAccountId, walletId: selectedWalletId },
|
|
@@ -447,7 +466,9 @@ function resolveDepositSelection(accounts, transferAmount, selectedAccountId, pr
|
|
|
447
466
|
const eligibleAccounts = getDepositEligibleAccounts(accounts);
|
|
448
467
|
if (eligibleAccounts.length === 0) return null;
|
|
449
468
|
const accountHasSufficientBalanceWallet = (account) => getAddressableWallets(account).some(
|
|
450
|
-
(wallet) => wallet.status === "ACTIVE" && wallet.sources.some(
|
|
469
|
+
(wallet) => wallet.status === "ACTIVE" && (wallet.sources ?? []).some(
|
|
470
|
+
(source) => source.balance.available.amount >= transferAmount
|
|
471
|
+
)
|
|
451
472
|
);
|
|
452
473
|
if (transferAmount <= 0 && selectedAccountId) {
|
|
453
474
|
const selectedAccount = eligibleAccounts.find((account) => account.id === selectedAccountId);
|
|
@@ -469,7 +490,7 @@ function resolveDepositSelection(accounts, transferAmount, selectedAccountId, pr
|
|
|
469
490
|
transferAmount,
|
|
470
491
|
priorityContext
|
|
471
492
|
);
|
|
472
|
-
const preferredSource = preferredWallet ? [...preferredWallet.sources].sort((a, b) => compareDepositSourcePriority(
|
|
493
|
+
const preferredSource = preferredWallet ? [...preferredWallet.sources ?? []].sort((a, b) => compareDepositSourcePriority(
|
|
473
494
|
sourcePriorityInput(preferredWallet.chain.name, a, transferAmount, priorityContext),
|
|
474
495
|
sourcePriorityInput(preferredWallet.chain.name, b, transferAmount, priorityContext)
|
|
475
496
|
))[0] : void 0;
|
|
@@ -535,7 +556,7 @@ function buildNativeUnsupportedEntries(options) {
|
|
|
535
556
|
logoURI: option.logoURI ?? null
|
|
536
557
|
})).filter((entry) => entry.amount > 0);
|
|
537
558
|
}
|
|
538
|
-
function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD) {
|
|
559
|
+
function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD, includeUnfundedTokens = false) {
|
|
539
560
|
const chainChoices = [];
|
|
540
561
|
const chainIndexByName = /* @__PURE__ */ new Map();
|
|
541
562
|
for (const option of options) {
|
|
@@ -576,7 +597,7 @@ function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SE
|
|
|
576
597
|
}
|
|
577
598
|
}
|
|
578
599
|
return chainChoices.map((chain) => {
|
|
579
|
-
const visibleTokens = chain.tokens.filter((t) => isSelectableDepositSourceAmountUsd(t.balance, minTransferAmountUsd));
|
|
600
|
+
const visibleTokens = includeUnfundedTokens ? chain.tokens : chain.tokens.filter((t) => isSelectableDepositSourceAmountUsd(t.balance, minTransferAmountUsd));
|
|
580
601
|
return {
|
|
581
602
|
...chain,
|
|
582
603
|
balance: visibleTokens.reduce((sum, token) => sum + token.balance, 0),
|
|
@@ -1964,464 +1985,197 @@ function screenForPhase(phase) {
|
|
|
1964
1985
|
}
|
|
1965
1986
|
}
|
|
1966
1987
|
|
|
1967
|
-
// src/
|
|
1968
|
-
var
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1988
|
+
// src/api.ts
|
|
1989
|
+
var api_exports = {};
|
|
1990
|
+
__export(api_exports, {
|
|
1991
|
+
createAccount: () => createAccount,
|
|
1992
|
+
createAccountAuthorizationSession: () => createAccountAuthorizationSession,
|
|
1993
|
+
createManualTransfer: () => createManualTransfer,
|
|
1994
|
+
createTransfer: () => createTransfer,
|
|
1995
|
+
fetchAccount: () => fetchAccount,
|
|
1996
|
+
fetchAccountBalances: () => fetchAccountBalances,
|
|
1997
|
+
fetchAccounts: () => fetchAccounts,
|
|
1998
|
+
fetchAuthorizationSession: () => fetchAuthorizationSession,
|
|
1999
|
+
fetchAuthorizationSessionByToken: () => fetchAuthorizationSessionByToken,
|
|
2000
|
+
fetchChains: () => fetchChains,
|
|
2001
|
+
fetchManualTransferSession: () => fetchManualTransferSession,
|
|
2002
|
+
fetchManualTransferSources: () => fetchManualTransferSources,
|
|
2003
|
+
fetchMerchantPublicKey: () => fetchMerchantPublicKey,
|
|
2004
|
+
fetchProviders: () => fetchProviders,
|
|
2005
|
+
fetchTransfer: () => fetchTransfer,
|
|
2006
|
+
fetchUserConfig: () => fetchUserConfig,
|
|
2007
|
+
postTransferQuote: () => postTransferQuote,
|
|
2008
|
+
probeActionCompletion: () => probeActionCompletion,
|
|
2009
|
+
refreshManualTransferQuote: () => refreshManualTransferQuote,
|
|
2010
|
+
regenerateTransferSignPayload: () => regenerateTransferSignPayload,
|
|
2011
|
+
registerPasskey: () => registerPasskey,
|
|
2012
|
+
reportActionCompletion: () => reportActionCompletion,
|
|
2013
|
+
reportPasskeyActivity: () => reportPasskeyActivity,
|
|
2014
|
+
setAuthorizationSessionPaymentIntentAmount: () => setAuthorizationSessionPaymentIntentAmount,
|
|
2015
|
+
setAuthorizationSessionProvider: () => setAuthorizationSessionProvider,
|
|
2016
|
+
signTransfer: () => signTransfer,
|
|
2017
|
+
updateManualTransferDepositTargetChain: () => updateManualTransferDepositTargetChain,
|
|
2018
|
+
updateUserConfig: () => updateUserConfig,
|
|
2019
|
+
updateUserConfigBySession: () => updateUserConfigBySession,
|
|
2020
|
+
waitForActionTransactionReceipt: () => waitForActionTransactionReceipt
|
|
2021
|
+
});
|
|
2022
|
+
var DEBUG_BUFFER_CAPACITY = 200;
|
|
2023
|
+
var nextId = 1;
|
|
2024
|
+
var entries = [];
|
|
2025
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
2026
|
+
function notify() {
|
|
2027
|
+
for (const listener of listeners) {
|
|
2028
|
+
try {
|
|
2029
|
+
listener();
|
|
2030
|
+
} catch (err) {
|
|
2031
|
+
console.error("[blink-sdk][debug-log] listener threw:", err);
|
|
2032
|
+
}
|
|
1972
2033
|
}
|
|
1973
|
-
}
|
|
1974
|
-
function
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
2034
|
+
}
|
|
2035
|
+
function appendDebug(level, message, data) {
|
|
2036
|
+
const entry = {
|
|
2037
|
+
id: nextId++,
|
|
2038
|
+
ts: Date.now(),
|
|
2039
|
+
level,
|
|
2040
|
+
message,
|
|
2041
|
+
data
|
|
2042
|
+
};
|
|
2043
|
+
const next = entries.length >= DEBUG_BUFFER_CAPACITY ? entries.slice(entries.length - DEBUG_BUFFER_CAPACITY + 1) : entries.slice();
|
|
2044
|
+
next.push(entry);
|
|
2045
|
+
entries = next;
|
|
2046
|
+
const prefix = "[blink-sdk][debug]";
|
|
2047
|
+
const sink = level === "error" ? console.error : level === "warn" ? console.warn : console.info;
|
|
2048
|
+
if (data !== void 0) {
|
|
2049
|
+
sink(`${prefix} ${message}`, data);
|
|
2050
|
+
} else {
|
|
2051
|
+
sink(`${prefix} ${message}`);
|
|
1982
2052
|
}
|
|
2053
|
+
notify();
|
|
1983
2054
|
}
|
|
1984
|
-
function
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2055
|
+
function subscribeDebug(listener) {
|
|
2056
|
+
listeners.add(listener);
|
|
2057
|
+
return () => {
|
|
2058
|
+
listeners.delete(listener);
|
|
2059
|
+
};
|
|
1988
2060
|
}
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
function
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
}
|
|
2061
|
+
function getDebugEntries() {
|
|
2062
|
+
return entries;
|
|
2063
|
+
}
|
|
2064
|
+
function clearDebugEntries() {
|
|
2065
|
+
entries = [];
|
|
2066
|
+
notify();
|
|
2067
|
+
}
|
|
2068
|
+
function useBlinkDebugLog() {
|
|
2069
|
+
return react.useSyncExternalStore(subscribeDebug, getDebugEntries, getDebugEntries);
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
// src/fetchWithRetry.ts
|
|
2073
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
2074
|
+
var DEFAULT_BASE_DELAY_MS = 500;
|
|
2075
|
+
var DEFAULT_MAX_JITTER_MS = 200;
|
|
2076
|
+
function isNetworkTypeError(err) {
|
|
2077
|
+
return err instanceof TypeError && /fetch|network|load failed/i.test(err.message);
|
|
2078
|
+
}
|
|
2079
|
+
async function fetchWithRetry(input, init, options) {
|
|
2080
|
+
const maxRetries = DEFAULT_MAX_RETRIES;
|
|
2081
|
+
const baseDelayMs = DEFAULT_BASE_DELAY_MS;
|
|
2082
|
+
const maxJitterMs = DEFAULT_MAX_JITTER_MS;
|
|
2083
|
+
const label = String(input).replace(/https?:\/\/[^/]+/, "");
|
|
2084
|
+
let lastError;
|
|
2085
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2086
|
+
try {
|
|
2087
|
+
return await fetch(input, init);
|
|
2088
|
+
} catch (err) {
|
|
2089
|
+
lastError = err;
|
|
2090
|
+
if (!isNetworkTypeError(err)) {
|
|
2091
|
+
throw err;
|
|
2092
|
+
}
|
|
2093
|
+
if (attempt < maxRetries) {
|
|
2094
|
+
const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * maxJitterMs;
|
|
2095
|
+
appendDebug("warn", `fetchWithRetry: network error, retrying ${label}`, {
|
|
2096
|
+
attempt: attempt + 1,
|
|
2097
|
+
maxRetries,
|
|
2098
|
+
delayMs: Math.round(delay),
|
|
2099
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2100
|
+
});
|
|
2101
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2029
2102
|
}
|
|
2030
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2031
|
-
function cleanup() {
|
|
2032
|
-
clearTimeout(timer);
|
|
2033
|
-
clearInterval(closedPoll);
|
|
2034
2103
|
}
|
|
2104
|
+
}
|
|
2105
|
+
throw lastError;
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// src/apiError.ts
|
|
2109
|
+
var ApiError = class extends Error {
|
|
2110
|
+
status;
|
|
2111
|
+
code;
|
|
2112
|
+
constructor(status, code, message) {
|
|
2113
|
+
super(message);
|
|
2114
|
+
this.name = "ApiError";
|
|
2115
|
+
this.status = status;
|
|
2116
|
+
this.code = code;
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
function isApiError(err) {
|
|
2120
|
+
if (err instanceof ApiError) return true;
|
|
2121
|
+
return typeof err === "object" && err !== null && "name" in err && err.name === "ApiError";
|
|
2122
|
+
}
|
|
2123
|
+
var SVM_SIGN_PAYLOAD_EXPIRED_CODE = "SVM_SIGN_PAYLOAD_EXPIRED";
|
|
2124
|
+
function isSvmSignExpiredError(err) {
|
|
2125
|
+
return isApiError(err) && err.code === SVM_SIGN_PAYLOAD_EXPIRED_CODE;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
// src/api.ts
|
|
2129
|
+
async function throwApiError(res) {
|
|
2130
|
+
const body = await res.json().catch(() => null);
|
|
2131
|
+
const detail = body?.error ?? body;
|
|
2132
|
+
const msg = detail?.message ?? res.statusText;
|
|
2133
|
+
const code = detail?.code ?? String(res.status);
|
|
2134
|
+
throw new ApiError(res.status, code, `${res.status} \u2014 ${code}: ${msg}`);
|
|
2135
|
+
}
|
|
2136
|
+
async function fetchProviders(apiBaseUrl, token) {
|
|
2137
|
+
const headers = {};
|
|
2138
|
+
if (token) {
|
|
2139
|
+
headers.Authorization = `Bearer ${token}`;
|
|
2140
|
+
}
|
|
2141
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/providers`, { headers });
|
|
2142
|
+
if (!res.ok) await throwApiError(res);
|
|
2143
|
+
const data = await res.json();
|
|
2144
|
+
return data.items;
|
|
2145
|
+
}
|
|
2146
|
+
async function fetchChains(apiBaseUrl, token) {
|
|
2147
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/chains`, {
|
|
2148
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2035
2149
|
});
|
|
2150
|
+
if (!res.ok) await throwApiError(res);
|
|
2151
|
+
const data = await res.json();
|
|
2152
|
+
return data.items;
|
|
2036
2153
|
}
|
|
2037
|
-
async function
|
|
2038
|
-
|
|
2039
|
-
const res = await
|
|
2040
|
-
headers: { Authorization: `Bearer ${
|
|
2154
|
+
async function fetchAccounts(apiBaseUrl, token, credentialId) {
|
|
2155
|
+
const params = new URLSearchParams({ credentialId });
|
|
2156
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts?${params.toString()}`, {
|
|
2157
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2041
2158
|
});
|
|
2042
|
-
if (!res.ok)
|
|
2043
|
-
const
|
|
2044
|
-
|
|
2045
|
-
const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
|
|
2046
|
-
return matched ? { credentialId: matched.credentialId, publicKey: matched.publicKey } : null;
|
|
2159
|
+
if (!res.ok) await throwApiError(res);
|
|
2160
|
+
const data = await res.json();
|
|
2161
|
+
return data.items;
|
|
2047
2162
|
}
|
|
2048
|
-
function
|
|
2049
|
-
|
|
2163
|
+
async function fetchAccount(apiBaseUrl, token, accountId, credentialId) {
|
|
2164
|
+
const params = new URLSearchParams({ credentialId });
|
|
2165
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts/${accountId}?${params.toString()}`, {
|
|
2166
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2167
|
+
});
|
|
2168
|
+
if (!res.ok) await throwApiError(res);
|
|
2169
|
+
return await res.json();
|
|
2050
2170
|
}
|
|
2051
|
-
|
|
2052
|
-
function signupWithPasskeyViaPopup() {
|
|
2053
|
-
return new Promise((resolve, reject) => {
|
|
2054
|
-
const popupUrl = `${window.location.origin}/passkey-signup`;
|
|
2055
|
-
const popup = window.open(popupUrl, "blink-passkey-signup");
|
|
2056
|
-
if (!popup) {
|
|
2057
|
-
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2058
|
-
return;
|
|
2059
|
-
}
|
|
2060
|
-
let settled = false;
|
|
2061
|
-
const timer = setTimeout(() => {
|
|
2062
|
-
cleanup();
|
|
2063
|
-
resolve(null);
|
|
2064
|
-
}, SIGNUP_POPUP_TIMEOUT_MS);
|
|
2065
|
-
function onMessage(event) {
|
|
2066
|
-
if (event.origin !== window.location.origin) return;
|
|
2067
|
-
if (event.source !== popup) return;
|
|
2068
|
-
const data = event.data;
|
|
2069
|
-
if (!data || data.type !== "blink:passkey-signup-complete") return;
|
|
2070
|
-
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2071
|
-
settled = true;
|
|
2072
|
-
cleanup();
|
|
2073
|
-
resolve({
|
|
2074
|
-
accessToken: data.accessToken,
|
|
2075
|
-
credentialId: data.credentialId,
|
|
2076
|
-
publicKey: data.publicKey
|
|
2077
|
-
});
|
|
2078
|
-
}
|
|
2079
|
-
window.addEventListener("message", onMessage);
|
|
2080
|
-
const closedPoll = setInterval(() => {
|
|
2081
|
-
if (popup.closed && !settled) {
|
|
2082
|
-
settled = true;
|
|
2083
|
-
cleanup();
|
|
2084
|
-
resolve(null);
|
|
2085
|
-
}
|
|
2086
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2087
|
-
function cleanup() {
|
|
2088
|
-
clearTimeout(timer);
|
|
2089
|
-
clearInterval(closedPoll);
|
|
2090
|
-
window.removeEventListener("message", onMessage);
|
|
2091
|
-
}
|
|
2092
|
-
});
|
|
2093
|
-
}
|
|
2094
|
-
var LOGIN_POPUP_TIMEOUT_MS = 12e4;
|
|
2095
|
-
function loginWithPasskeyViaPopup() {
|
|
2096
|
-
return new Promise((resolve, reject) => {
|
|
2097
|
-
const popupUrl = `${window.location.origin}/passkey-login`;
|
|
2098
|
-
const popup = window.open(popupUrl, "blink-passkey-login");
|
|
2099
|
-
if (!popup) {
|
|
2100
|
-
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2101
|
-
return;
|
|
2102
|
-
}
|
|
2103
|
-
let settled = false;
|
|
2104
|
-
const timer = setTimeout(() => {
|
|
2105
|
-
cleanup();
|
|
2106
|
-
resolve(null);
|
|
2107
|
-
}, LOGIN_POPUP_TIMEOUT_MS);
|
|
2108
|
-
function onMessage(event) {
|
|
2109
|
-
if (event.origin !== window.location.origin) return;
|
|
2110
|
-
if (event.source !== popup) return;
|
|
2111
|
-
const data = event.data;
|
|
2112
|
-
if (!data || data.type !== "blink:passkey-login-complete") return;
|
|
2113
|
-
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2114
|
-
settled = true;
|
|
2115
|
-
cleanup();
|
|
2116
|
-
resolve({
|
|
2117
|
-
accessToken: data.accessToken,
|
|
2118
|
-
credentialId: data.credentialId,
|
|
2119
|
-
publicKey: data.publicKey
|
|
2120
|
-
});
|
|
2121
|
-
}
|
|
2122
|
-
window.addEventListener("message", onMessage);
|
|
2123
|
-
const closedPoll = setInterval(() => {
|
|
2124
|
-
if (popup.closed && !settled) {
|
|
2125
|
-
settled = true;
|
|
2126
|
-
cleanup();
|
|
2127
|
-
resolve(null);
|
|
2128
|
-
}
|
|
2129
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2130
|
-
function cleanup() {
|
|
2131
|
-
clearTimeout(timer);
|
|
2132
|
-
clearInterval(closedPoll);
|
|
2133
|
-
window.removeEventListener("message", onMessage);
|
|
2134
|
-
}
|
|
2135
|
-
});
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
|
-
// src/credentialIdEncoding.ts
|
|
2139
|
-
function credentialIdBase64ToBytes(value) {
|
|
2140
|
-
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
2141
|
-
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
2142
|
-
const raw = atob(padded);
|
|
2143
|
-
const bytes = new Uint8Array(raw.length);
|
|
2144
|
-
for (let i = 0; i < raw.length; i++) {
|
|
2145
|
-
bytes[i] = raw.charCodeAt(i);
|
|
2146
|
-
}
|
|
2147
|
-
return bytes;
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
// src/passkeyRpId.ts
|
|
2151
|
-
function normalizeConfiguredDomain(value) {
|
|
2152
|
-
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
2153
|
-
}
|
|
2154
|
-
function resolveRootDomainFromHostname(hostname) {
|
|
2155
|
-
const trimmedHostname = hostname.trim().toLowerCase();
|
|
2156
|
-
if (!trimmedHostname) {
|
|
2157
|
-
return "localhost";
|
|
2158
|
-
}
|
|
2159
|
-
if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
|
|
2160
|
-
return trimmedHostname;
|
|
2161
|
-
}
|
|
2162
|
-
const parts = trimmedHostname.split(".").filter(Boolean);
|
|
2163
|
-
if (parts.length < 2) {
|
|
2164
|
-
return trimmedHostname;
|
|
2165
|
-
}
|
|
2166
|
-
return parts.slice(-2).join(".");
|
|
2167
|
-
}
|
|
2168
|
-
|
|
2169
|
-
// src/hooks/passkeyPublic.ts
|
|
2170
|
-
function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
|
|
2171
|
-
return new Promise((resolve) => {
|
|
2172
|
-
if (typeof document === "undefined") {
|
|
2173
|
-
resolve();
|
|
2174
|
-
return;
|
|
2175
|
-
}
|
|
2176
|
-
if (document.hasFocus()) {
|
|
2177
|
-
resolve();
|
|
2178
|
-
return;
|
|
2179
|
-
}
|
|
2180
|
-
const deadline = Date.now() + timeoutMs;
|
|
2181
|
-
const timer = setInterval(() => {
|
|
2182
|
-
if (document.hasFocus()) {
|
|
2183
|
-
clearInterval(timer);
|
|
2184
|
-
resolve();
|
|
2185
|
-
} else if (Date.now() >= deadline) {
|
|
2186
|
-
clearInterval(timer);
|
|
2187
|
-
resolve();
|
|
2188
|
-
}
|
|
2189
|
-
}, intervalMs);
|
|
2190
|
-
});
|
|
2191
|
-
}
|
|
2192
|
-
function toBase64(buffer) {
|
|
2193
|
-
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
2194
|
-
}
|
|
2195
|
-
function readEnvValue(name) {
|
|
2196
|
-
const meta = ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) });
|
|
2197
|
-
const metaValue = meta.env?.[name];
|
|
2198
|
-
if (typeof metaValue === "string" && metaValue.trim().length > 0) {
|
|
2199
|
-
return metaValue.trim();
|
|
2200
|
-
}
|
|
2201
|
-
const processValue = globalThis.process?.env?.[name];
|
|
2202
|
-
if (typeof processValue === "string" && processValue.trim().length > 0) {
|
|
2203
|
-
return processValue.trim();
|
|
2204
|
-
}
|
|
2205
|
-
return void 0;
|
|
2206
|
-
}
|
|
2207
|
-
function resolvePasskeyRpId() {
|
|
2208
|
-
const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
|
|
2209
|
-
if (configuredDomain) {
|
|
2210
|
-
return normalizeConfiguredDomain(configuredDomain);
|
|
2211
|
-
}
|
|
2212
|
-
if (typeof window !== "undefined") {
|
|
2213
|
-
return resolveRootDomainFromHostname(window.location.hostname);
|
|
2214
|
-
}
|
|
2215
|
-
return "localhost";
|
|
2216
|
-
}
|
|
2217
|
-
async function deviceHasPasskey(credentialId) {
|
|
2218
|
-
const found = await findDevicePasskey([credentialId]);
|
|
2219
|
-
return found != null;
|
|
2220
|
-
}
|
|
2221
|
-
async function findDevicePasskey(credentialIds) {
|
|
2222
|
-
if (credentialIds.length === 0) return null;
|
|
2223
|
-
try {
|
|
2224
|
-
const challenge = new Uint8Array(32);
|
|
2225
|
-
crypto.getRandomValues(challenge);
|
|
2226
|
-
await waitForDocumentFocus();
|
|
2227
|
-
const assertion = await navigator.credentials.get({
|
|
2228
|
-
publicKey: {
|
|
2229
|
-
challenge,
|
|
2230
|
-
rpId: resolvePasskeyRpId(),
|
|
2231
|
-
allowCredentials: credentialIds.map((id) => ({
|
|
2232
|
-
type: "public-key",
|
|
2233
|
-
id: credentialIdBase64ToBytes(id)
|
|
2234
|
-
})),
|
|
2235
|
-
userVerification: "discouraged",
|
|
2236
|
-
timeout: 3e4
|
|
2237
|
-
}
|
|
2238
|
-
});
|
|
2239
|
-
if (!assertion) return null;
|
|
2240
|
-
return toBase64(assertion.rawId);
|
|
2241
|
-
} catch {
|
|
2242
|
-
return null;
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
|
|
2246
|
-
// src/api.ts
|
|
2247
|
-
var api_exports = {};
|
|
2248
|
-
__export(api_exports, {
|
|
2249
|
-
createAccount: () => createAccount,
|
|
2250
|
-
createAccountAuthorizationSession: () => createAccountAuthorizationSession,
|
|
2251
|
-
createManualTransfer: () => createManualTransfer,
|
|
2252
|
-
createTransfer: () => createTransfer,
|
|
2253
|
-
fetchAccount: () => fetchAccount,
|
|
2254
|
-
fetchAccounts: () => fetchAccounts,
|
|
2255
|
-
fetchAuthorizationSession: () => fetchAuthorizationSession,
|
|
2256
|
-
fetchAuthorizationSessionByToken: () => fetchAuthorizationSessionByToken,
|
|
2257
|
-
fetchChains: () => fetchChains,
|
|
2258
|
-
fetchManualTransferSession: () => fetchManualTransferSession,
|
|
2259
|
-
fetchManualTransferSources: () => fetchManualTransferSources,
|
|
2260
|
-
fetchMerchantPublicKey: () => fetchMerchantPublicKey,
|
|
2261
|
-
fetchProviders: () => fetchProviders,
|
|
2262
|
-
fetchTransfer: () => fetchTransfer,
|
|
2263
|
-
fetchUserConfig: () => fetchUserConfig,
|
|
2264
|
-
postTransferQuote: () => postTransferQuote,
|
|
2265
|
-
probeActionCompletion: () => probeActionCompletion,
|
|
2266
|
-
refreshManualTransferQuote: () => refreshManualTransferQuote,
|
|
2267
|
-
regenerateTransferSignPayload: () => regenerateTransferSignPayload,
|
|
2268
|
-
registerPasskey: () => registerPasskey,
|
|
2269
|
-
reportActionCompletion: () => reportActionCompletion,
|
|
2270
|
-
reportPasskeyActivity: () => reportPasskeyActivity,
|
|
2271
|
-
setAuthorizationSessionPaymentIntentAmount: () => setAuthorizationSessionPaymentIntentAmount,
|
|
2272
|
-
setAuthorizationSessionProvider: () => setAuthorizationSessionProvider,
|
|
2273
|
-
signTransfer: () => signTransfer,
|
|
2274
|
-
updateManualTransferDepositTargetChain: () => updateManualTransferDepositTargetChain,
|
|
2275
|
-
updateUserConfig: () => updateUserConfig,
|
|
2276
|
-
updateUserConfigBySession: () => updateUserConfigBySession,
|
|
2277
|
-
waitForActionTransactionReceipt: () => waitForActionTransactionReceipt
|
|
2278
|
-
});
|
|
2279
|
-
var DEBUG_BUFFER_CAPACITY = 200;
|
|
2280
|
-
var nextId = 1;
|
|
2281
|
-
var entries = [];
|
|
2282
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
2283
|
-
function notify() {
|
|
2284
|
-
for (const listener of listeners) {
|
|
2285
|
-
try {
|
|
2286
|
-
listener();
|
|
2287
|
-
} catch (err) {
|
|
2288
|
-
console.error("[blink-sdk][debug-log] listener threw:", err);
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
function appendDebug(level, message, data) {
|
|
2293
|
-
const entry = {
|
|
2294
|
-
id: nextId++,
|
|
2295
|
-
ts: Date.now(),
|
|
2296
|
-
level,
|
|
2297
|
-
message,
|
|
2298
|
-
data
|
|
2299
|
-
};
|
|
2300
|
-
const next = entries.length >= DEBUG_BUFFER_CAPACITY ? entries.slice(entries.length - DEBUG_BUFFER_CAPACITY + 1) : entries.slice();
|
|
2301
|
-
next.push(entry);
|
|
2302
|
-
entries = next;
|
|
2303
|
-
const prefix = "[blink-sdk][debug]";
|
|
2304
|
-
const sink = level === "error" ? console.error : level === "warn" ? console.warn : console.info;
|
|
2305
|
-
if (data !== void 0) {
|
|
2306
|
-
sink(`${prefix} ${message}`, data);
|
|
2307
|
-
} else {
|
|
2308
|
-
sink(`${prefix} ${message}`);
|
|
2309
|
-
}
|
|
2310
|
-
notify();
|
|
2311
|
-
}
|
|
2312
|
-
function subscribeDebug(listener) {
|
|
2313
|
-
listeners.add(listener);
|
|
2314
|
-
return () => {
|
|
2315
|
-
listeners.delete(listener);
|
|
2316
|
-
};
|
|
2317
|
-
}
|
|
2318
|
-
function getDebugEntries() {
|
|
2319
|
-
return entries;
|
|
2320
|
-
}
|
|
2321
|
-
function clearDebugEntries() {
|
|
2322
|
-
entries = [];
|
|
2323
|
-
notify();
|
|
2324
|
-
}
|
|
2325
|
-
function useBlinkDebugLog() {
|
|
2326
|
-
return react.useSyncExternalStore(subscribeDebug, getDebugEntries, getDebugEntries);
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
|
-
// src/fetchWithRetry.ts
|
|
2330
|
-
var DEFAULT_MAX_RETRIES = 3;
|
|
2331
|
-
var DEFAULT_BASE_DELAY_MS = 500;
|
|
2332
|
-
var DEFAULT_MAX_JITTER_MS = 200;
|
|
2333
|
-
function isNetworkTypeError(err) {
|
|
2334
|
-
return err instanceof TypeError && /fetch|network|load failed/i.test(err.message);
|
|
2335
|
-
}
|
|
2336
|
-
async function fetchWithRetry(input, init, options) {
|
|
2337
|
-
const maxRetries = DEFAULT_MAX_RETRIES;
|
|
2338
|
-
const baseDelayMs = DEFAULT_BASE_DELAY_MS;
|
|
2339
|
-
const maxJitterMs = DEFAULT_MAX_JITTER_MS;
|
|
2340
|
-
const label = String(input).replace(/https?:\/\/[^/]+/, "");
|
|
2341
|
-
let lastError;
|
|
2342
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2343
|
-
try {
|
|
2344
|
-
return await fetch(input, init);
|
|
2345
|
-
} catch (err) {
|
|
2346
|
-
lastError = err;
|
|
2347
|
-
if (!isNetworkTypeError(err)) {
|
|
2348
|
-
throw err;
|
|
2349
|
-
}
|
|
2350
|
-
if (attempt < maxRetries) {
|
|
2351
|
-
const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * maxJitterMs;
|
|
2352
|
-
appendDebug("warn", `fetchWithRetry: network error, retrying ${label}`, {
|
|
2353
|
-
attempt: attempt + 1,
|
|
2354
|
-
maxRetries,
|
|
2355
|
-
delayMs: Math.round(delay),
|
|
2356
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2357
|
-
});
|
|
2358
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2359
|
-
}
|
|
2360
|
-
}
|
|
2361
|
-
}
|
|
2362
|
-
throw lastError;
|
|
2363
|
-
}
|
|
2364
|
-
|
|
2365
|
-
// src/apiError.ts
|
|
2366
|
-
var ApiError = class extends Error {
|
|
2367
|
-
status;
|
|
2368
|
-
code;
|
|
2369
|
-
constructor(status, code, message) {
|
|
2370
|
-
super(message);
|
|
2371
|
-
this.name = "ApiError";
|
|
2372
|
-
this.status = status;
|
|
2373
|
-
this.code = code;
|
|
2374
|
-
}
|
|
2375
|
-
};
|
|
2376
|
-
function isApiError(err) {
|
|
2377
|
-
if (err instanceof ApiError) return true;
|
|
2378
|
-
return typeof err === "object" && err !== null && "name" in err && err.name === "ApiError";
|
|
2379
|
-
}
|
|
2380
|
-
var SVM_SIGN_PAYLOAD_EXPIRED_CODE = "SVM_SIGN_PAYLOAD_EXPIRED";
|
|
2381
|
-
function isSvmSignExpiredError(err) {
|
|
2382
|
-
return isApiError(err) && err.code === SVM_SIGN_PAYLOAD_EXPIRED_CODE;
|
|
2383
|
-
}
|
|
2384
|
-
|
|
2385
|
-
// src/api.ts
|
|
2386
|
-
async function throwApiError(res) {
|
|
2387
|
-
const body = await res.json().catch(() => null);
|
|
2388
|
-
const detail = body?.error ?? body;
|
|
2389
|
-
const msg = detail?.message ?? res.statusText;
|
|
2390
|
-
const code = detail?.code ?? String(res.status);
|
|
2391
|
-
throw new ApiError(res.status, code, `${res.status} \u2014 ${code}: ${msg}`);
|
|
2392
|
-
}
|
|
2393
|
-
async function fetchProviders(apiBaseUrl, token) {
|
|
2394
|
-
const headers = {};
|
|
2395
|
-
if (token) {
|
|
2396
|
-
headers.Authorization = `Bearer ${token}`;
|
|
2397
|
-
}
|
|
2398
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/providers`, { headers });
|
|
2399
|
-
if (!res.ok) await throwApiError(res);
|
|
2400
|
-
const data = await res.json();
|
|
2401
|
-
return data.items;
|
|
2402
|
-
}
|
|
2403
|
-
async function fetchChains(apiBaseUrl, token) {
|
|
2404
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/chains`, {
|
|
2405
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2406
|
-
});
|
|
2407
|
-
if (!res.ok) await throwApiError(res);
|
|
2408
|
-
const data = await res.json();
|
|
2409
|
-
return data.items;
|
|
2410
|
-
}
|
|
2411
|
-
async function fetchAccounts(apiBaseUrl, token, credentialId) {
|
|
2412
|
-
const params = new URLSearchParams({ credentialId });
|
|
2413
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts?${params.toString()}`, {
|
|
2414
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2415
|
-
});
|
|
2416
|
-
if (!res.ok) await throwApiError(res);
|
|
2417
|
-
const data = await res.json();
|
|
2418
|
-
return data.items;
|
|
2419
|
-
}
|
|
2420
|
-
async function fetchAccount(apiBaseUrl, token, accountId, credentialId) {
|
|
2171
|
+
async function fetchAccountBalances(apiBaseUrl, token, accountId, credentialId) {
|
|
2421
2172
|
const params = new URLSearchParams({ credentialId });
|
|
2422
|
-
const res = await fetchWithRetry(
|
|
2423
|
-
|
|
2424
|
-
|
|
2173
|
+
const res = await fetchWithRetry(
|
|
2174
|
+
`${apiBaseUrl}/v1/accounts/${accountId}/balances?${params.toString()}`,
|
|
2175
|
+
{
|
|
2176
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2177
|
+
}
|
|
2178
|
+
);
|
|
2425
2179
|
if (!res.ok) await throwApiError(res);
|
|
2426
2180
|
return await res.json();
|
|
2427
2181
|
}
|
|
@@ -2697,89 +2451,368 @@ async function createManualTransfer(apiBaseUrl, params) {
|
|
|
2697
2451
|
headers: { "Content-Type": "application/json" },
|
|
2698
2452
|
body: JSON.stringify(params)
|
|
2699
2453
|
});
|
|
2700
|
-
if (!res.ok) await throwApiError(res);
|
|
2701
|
-
return await res.json();
|
|
2454
|
+
if (!res.ok) await throwApiError(res);
|
|
2455
|
+
return await res.json();
|
|
2456
|
+
}
|
|
2457
|
+
async function fetchManualTransferSession(apiBaseUrl, sessionId) {
|
|
2458
|
+
const res = await fetchWithRetry(`${apiBaseUrl}/v1/manual-transfers/${sessionId}`);
|
|
2459
|
+
if (!res.ok) await throwApiError(res);
|
|
2460
|
+
return await res.json();
|
|
2461
|
+
}
|
|
2462
|
+
async function updateManualTransferDepositTargetChain(apiBaseUrl, sessionId, selectedChainId) {
|
|
2463
|
+
const res = await fetch(`${apiBaseUrl}/v1/manual-transfers/${sessionId}`, {
|
|
2464
|
+
method: "PATCH",
|
|
2465
|
+
headers: { "Content-Type": "application/json" },
|
|
2466
|
+
body: JSON.stringify({ selectedChainId })
|
|
2467
|
+
});
|
|
2468
|
+
if (!res.ok) await throwApiError(res);
|
|
2469
|
+
return await res.json();
|
|
2470
|
+
}
|
|
2471
|
+
async function refreshManualTransferQuote(apiBaseUrl, sessionId) {
|
|
2472
|
+
const res = await fetch(
|
|
2473
|
+
`${apiBaseUrl}/v1/manual-transfers/${sessionId}/refresh-quote`,
|
|
2474
|
+
{ method: "POST" }
|
|
2475
|
+
);
|
|
2476
|
+
if (!res.ok) await throwApiError(res);
|
|
2477
|
+
return await res.json();
|
|
2478
|
+
}
|
|
2479
|
+
async function reportActionCompletion(apiBaseUrl, actionId, result) {
|
|
2480
|
+
const res = await fetchWithRetry(
|
|
2481
|
+
`${apiBaseUrl}/v1/authorization-actions/${actionId}`,
|
|
2482
|
+
{
|
|
2483
|
+
method: "PATCH",
|
|
2484
|
+
headers: { "Content-Type": "application/json" },
|
|
2485
|
+
body: JSON.stringify({ status: "COMPLETED", result })
|
|
2486
|
+
}
|
|
2487
|
+
);
|
|
2488
|
+
if (!res.ok) await throwApiError(res);
|
|
2489
|
+
return await res.json();
|
|
2490
|
+
}
|
|
2491
|
+
async function waitForActionTransactionReceipt(apiBaseUrl, actionId, txHash) {
|
|
2492
|
+
const res = await fetch(
|
|
2493
|
+
`${apiBaseUrl}/v1/authorization-actions/${actionId}/transaction-receipt`,
|
|
2494
|
+
{
|
|
2495
|
+
method: "POST",
|
|
2496
|
+
headers: { "Content-Type": "application/json" },
|
|
2497
|
+
body: JSON.stringify({ txHash })
|
|
2498
|
+
}
|
|
2499
|
+
);
|
|
2500
|
+
if (!res.ok) await throwApiError(res);
|
|
2501
|
+
return await res.json();
|
|
2502
|
+
}
|
|
2503
|
+
async function probeActionCompletion(apiBaseUrl, actionId) {
|
|
2504
|
+
const res = await fetchWithRetry(
|
|
2505
|
+
`${apiBaseUrl}/v1/authorization-actions/${actionId}`,
|
|
2506
|
+
{
|
|
2507
|
+
method: "PATCH",
|
|
2508
|
+
headers: { "Content-Type": "application/json" },
|
|
2509
|
+
body: JSON.stringify({ status: "COMPLETED", result: {} })
|
|
2510
|
+
}
|
|
2511
|
+
);
|
|
2512
|
+
if (res.ok) {
|
|
2513
|
+
const session = await res.json();
|
|
2514
|
+
return { detected: true, session };
|
|
2515
|
+
}
|
|
2516
|
+
const body = await res.json().catch(() => null);
|
|
2517
|
+
const detail = body?.error ?? body;
|
|
2518
|
+
const code = detail?.code;
|
|
2519
|
+
const message = detail?.message ?? res.statusText ?? `HTTP ${res.status}`;
|
|
2520
|
+
if (res.status === 422 && code === "DEPOSIT_TX_NOT_FOUND") {
|
|
2521
|
+
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
2522
|
+
}
|
|
2523
|
+
const approvalNotDetectedCodes = /* @__PURE__ */ new Set([
|
|
2524
|
+
"APPROVE_NOT_DETECTED",
|
|
2525
|
+
"APPROVE_SPL_NOT_DETECTED",
|
|
2526
|
+
"SPL_DELEGATE_MISSING",
|
|
2527
|
+
"SPL_DELEGATE_INSUFFICIENT",
|
|
2528
|
+
"SPL_DELEGATE_WRONG_OWNER"
|
|
2529
|
+
]);
|
|
2530
|
+
if (res.status === 422 && code && approvalNotDetectedCodes.has(code)) {
|
|
2531
|
+
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
2532
|
+
}
|
|
2533
|
+
if (res.status === 422 && code === "INVALID_TRANSFER_STATE") {
|
|
2534
|
+
return { detected: false, reason: "invalid-state", status: res.status, code, message };
|
|
2535
|
+
}
|
|
2536
|
+
return { detected: false, reason: "error", status: res.status, code, message };
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
// src/passkey-delegation.ts
|
|
2540
|
+
var PasskeyIframeBlockedError = class extends Error {
|
|
2541
|
+
constructor(message = "Passkey creation is not supported in this browser context.") {
|
|
2542
|
+
super(message);
|
|
2543
|
+
this.name = "PasskeyIframeBlockedError";
|
|
2544
|
+
}
|
|
2545
|
+
};
|
|
2546
|
+
function isInCrossOriginIframe() {
|
|
2547
|
+
if (typeof window === "undefined") return false;
|
|
2548
|
+
if (window.parent === window) return false;
|
|
2549
|
+
try {
|
|
2550
|
+
void window.parent.location.origin;
|
|
2551
|
+
return false;
|
|
2552
|
+
} catch {
|
|
2553
|
+
return true;
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
function isSafari() {
|
|
2557
|
+
if (typeof navigator === "undefined") return false;
|
|
2558
|
+
const ua = navigator.userAgent;
|
|
2559
|
+
return /Safari/i.test(ua) && !/Chrome|CriOS|Chromium|Edg|OPR|Firefox/i.test(ua);
|
|
2560
|
+
}
|
|
2561
|
+
var VERIFY_POPUP_TIMEOUT_MS = 6e4;
|
|
2562
|
+
var POPUP_CLOSED_POLL_MS = 500;
|
|
2563
|
+
var POPUP_CLOSED_GRACE_MS = 1e3;
|
|
2564
|
+
function findDevicePasskeyViaPopup(options) {
|
|
2565
|
+
return new Promise((resolve, reject) => {
|
|
2566
|
+
const verificationToken = crypto.randomUUID();
|
|
2567
|
+
const payload = {
|
|
2568
|
+
...options,
|
|
2569
|
+
verificationToken
|
|
2570
|
+
};
|
|
2571
|
+
const encoded = btoa(JSON.stringify(payload));
|
|
2572
|
+
const popupUrl = `${window.location.origin}/passkey-verify#${encoded}`;
|
|
2573
|
+
const popup = window.open(popupUrl, "blink-passkey-verify");
|
|
2574
|
+
if (!popup) {
|
|
2575
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
let settled = false;
|
|
2579
|
+
const timer = setTimeout(() => {
|
|
2580
|
+
cleanup();
|
|
2581
|
+
resolve(null);
|
|
2582
|
+
}, VERIFY_POPUP_TIMEOUT_MS);
|
|
2583
|
+
const closedPoll = setInterval(() => {
|
|
2584
|
+
if (popup.closed && !settled) {
|
|
2585
|
+
clearInterval(closedPoll);
|
|
2586
|
+
setTimeout(() => {
|
|
2587
|
+
if (!settled) {
|
|
2588
|
+
settled = true;
|
|
2589
|
+
cleanup();
|
|
2590
|
+
checkServerForPasskeyByToken(
|
|
2591
|
+
options.authToken,
|
|
2592
|
+
options.apiBaseUrl,
|
|
2593
|
+
verificationToken
|
|
2594
|
+
).then((result) => {
|
|
2595
|
+
resolve(result?.credentialId ?? null);
|
|
2596
|
+
}).catch(() => {
|
|
2597
|
+
resolve(null);
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2600
|
+
}, POPUP_CLOSED_GRACE_MS);
|
|
2601
|
+
}
|
|
2602
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
2603
|
+
function cleanup() {
|
|
2604
|
+
clearTimeout(timer);
|
|
2605
|
+
clearInterval(closedPoll);
|
|
2606
|
+
}
|
|
2607
|
+
});
|
|
2608
|
+
}
|
|
2609
|
+
async function checkServerForPasskeyByToken(authToken, apiBaseUrl, verificationToken) {
|
|
2610
|
+
if (!authToken || !apiBaseUrl) return null;
|
|
2611
|
+
const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
|
|
2612
|
+
headers: { Authorization: `Bearer ${authToken}` }
|
|
2613
|
+
});
|
|
2614
|
+
if (!res.ok) return null;
|
|
2615
|
+
const body = await res.json();
|
|
2616
|
+
const passkeys = body.config.passkeys ?? [];
|
|
2617
|
+
const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
|
|
2618
|
+
return matched ? { credentialId: matched.credentialId, publicKey: matched.publicKey } : null;
|
|
2619
|
+
}
|
|
2620
|
+
function shouldUsePasskeySignupPopup() {
|
|
2621
|
+
return isSafari() && isInCrossOriginIframe();
|
|
2622
|
+
}
|
|
2623
|
+
var SIGNUP_POPUP_TIMEOUT_MS = 12e4;
|
|
2624
|
+
function signupWithPasskeyViaPopup() {
|
|
2625
|
+
return new Promise((resolve, reject) => {
|
|
2626
|
+
const popupUrl = `${window.location.origin}/passkey-signup`;
|
|
2627
|
+
const popup = window.open(popupUrl, "blink-passkey-signup");
|
|
2628
|
+
if (!popup) {
|
|
2629
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
let settled = false;
|
|
2633
|
+
const timer = setTimeout(() => {
|
|
2634
|
+
cleanup();
|
|
2635
|
+
resolve(null);
|
|
2636
|
+
}, SIGNUP_POPUP_TIMEOUT_MS);
|
|
2637
|
+
function onMessage(event) {
|
|
2638
|
+
if (event.origin !== window.location.origin) return;
|
|
2639
|
+
if (event.source !== popup) return;
|
|
2640
|
+
const data = event.data;
|
|
2641
|
+
if (!data || data.type !== "blink:passkey-signup-complete") return;
|
|
2642
|
+
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2643
|
+
settled = true;
|
|
2644
|
+
cleanup();
|
|
2645
|
+
resolve({
|
|
2646
|
+
accessToken: data.accessToken,
|
|
2647
|
+
credentialId: data.credentialId,
|
|
2648
|
+
publicKey: data.publicKey
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
window.addEventListener("message", onMessage);
|
|
2652
|
+
const closedPoll = setInterval(() => {
|
|
2653
|
+
if (popup.closed && !settled) {
|
|
2654
|
+
settled = true;
|
|
2655
|
+
cleanup();
|
|
2656
|
+
resolve(null);
|
|
2657
|
+
}
|
|
2658
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
2659
|
+
function cleanup() {
|
|
2660
|
+
clearTimeout(timer);
|
|
2661
|
+
clearInterval(closedPoll);
|
|
2662
|
+
window.removeEventListener("message", onMessage);
|
|
2663
|
+
}
|
|
2664
|
+
});
|
|
2665
|
+
}
|
|
2666
|
+
var LOGIN_POPUP_TIMEOUT_MS = 12e4;
|
|
2667
|
+
function loginWithPasskeyViaPopup() {
|
|
2668
|
+
return new Promise((resolve, reject) => {
|
|
2669
|
+
const popupUrl = `${window.location.origin}/passkey-login`;
|
|
2670
|
+
const popup = window.open(popupUrl, "blink-passkey-login");
|
|
2671
|
+
if (!popup) {
|
|
2672
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2673
|
+
return;
|
|
2674
|
+
}
|
|
2675
|
+
let settled = false;
|
|
2676
|
+
const timer = setTimeout(() => {
|
|
2677
|
+
cleanup();
|
|
2678
|
+
resolve(null);
|
|
2679
|
+
}, LOGIN_POPUP_TIMEOUT_MS);
|
|
2680
|
+
function onMessage(event) {
|
|
2681
|
+
if (event.origin !== window.location.origin) return;
|
|
2682
|
+
if (event.source !== popup) return;
|
|
2683
|
+
const data = event.data;
|
|
2684
|
+
if (!data || data.type !== "blink:passkey-login-complete") return;
|
|
2685
|
+
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2686
|
+
settled = true;
|
|
2687
|
+
cleanup();
|
|
2688
|
+
resolve({
|
|
2689
|
+
accessToken: data.accessToken,
|
|
2690
|
+
credentialId: data.credentialId,
|
|
2691
|
+
publicKey: data.publicKey
|
|
2692
|
+
});
|
|
2693
|
+
}
|
|
2694
|
+
window.addEventListener("message", onMessage);
|
|
2695
|
+
const closedPoll = setInterval(() => {
|
|
2696
|
+
if (popup.closed && !settled) {
|
|
2697
|
+
settled = true;
|
|
2698
|
+
cleanup();
|
|
2699
|
+
resolve(null);
|
|
2700
|
+
}
|
|
2701
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
2702
|
+
function cleanup() {
|
|
2703
|
+
clearTimeout(timer);
|
|
2704
|
+
clearInterval(closedPoll);
|
|
2705
|
+
window.removeEventListener("message", onMessage);
|
|
2706
|
+
}
|
|
2707
|
+
});
|
|
2702
2708
|
}
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2709
|
+
|
|
2710
|
+
// src/credentialIdEncoding.ts
|
|
2711
|
+
function credentialIdBase64ToBytes(value) {
|
|
2712
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
2713
|
+
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
2714
|
+
const raw = atob(padded);
|
|
2715
|
+
const bytes = new Uint8Array(raw.length);
|
|
2716
|
+
for (let i = 0; i < raw.length; i++) {
|
|
2717
|
+
bytes[i] = raw.charCodeAt(i);
|
|
2718
|
+
}
|
|
2719
|
+
return bytes;
|
|
2707
2720
|
}
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
body: JSON.stringify({ selectedChainId })
|
|
2713
|
-
});
|
|
2714
|
-
if (!res.ok) await throwApiError(res);
|
|
2715
|
-
return await res.json();
|
|
2721
|
+
|
|
2722
|
+
// src/passkeyRpId.ts
|
|
2723
|
+
function normalizeConfiguredDomain(value) {
|
|
2724
|
+
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
2716
2725
|
}
|
|
2717
|
-
|
|
2718
|
-
const
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
if (
|
|
2723
|
-
|
|
2726
|
+
function resolveRootDomainFromHostname(hostname) {
|
|
2727
|
+
const trimmedHostname = hostname.trim().toLowerCase();
|
|
2728
|
+
if (!trimmedHostname) {
|
|
2729
|
+
return "localhost";
|
|
2730
|
+
}
|
|
2731
|
+
if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
|
|
2732
|
+
return trimmedHostname;
|
|
2733
|
+
}
|
|
2734
|
+
const parts = trimmedHostname.split(".").filter(Boolean);
|
|
2735
|
+
if (parts.length < 2) {
|
|
2736
|
+
return trimmedHostname;
|
|
2737
|
+
}
|
|
2738
|
+
return parts.slice(-2).join(".");
|
|
2724
2739
|
}
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2740
|
+
|
|
2741
|
+
// src/hooks/passkeyPublic.ts
|
|
2742
|
+
function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
|
|
2743
|
+
return new Promise((resolve) => {
|
|
2744
|
+
if (typeof document === "undefined") {
|
|
2745
|
+
resolve();
|
|
2746
|
+
return;
|
|
2732
2747
|
}
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
}
|
|
2737
|
-
async function waitForActionTransactionReceipt(apiBaseUrl, actionId, txHash) {
|
|
2738
|
-
const res = await fetch(
|
|
2739
|
-
`${apiBaseUrl}/v1/authorization-actions/${actionId}/transaction-receipt`,
|
|
2740
|
-
{
|
|
2741
|
-
method: "POST",
|
|
2742
|
-
headers: { "Content-Type": "application/json" },
|
|
2743
|
-
body: JSON.stringify({ txHash })
|
|
2748
|
+
if (document.hasFocus()) {
|
|
2749
|
+
resolve();
|
|
2750
|
+
return;
|
|
2744
2751
|
}
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2752
|
+
const deadline = Date.now() + timeoutMs;
|
|
2753
|
+
const timer = setInterval(() => {
|
|
2754
|
+
if (document.hasFocus()) {
|
|
2755
|
+
clearInterval(timer);
|
|
2756
|
+
resolve();
|
|
2757
|
+
} else if (Date.now() >= deadline) {
|
|
2758
|
+
clearInterval(timer);
|
|
2759
|
+
resolve();
|
|
2760
|
+
}
|
|
2761
|
+
}, intervalMs);
|
|
2762
|
+
});
|
|
2748
2763
|
}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
);
|
|
2758
|
-
if (res.ok) {
|
|
2759
|
-
const session = await res.json();
|
|
2760
|
-
return { detected: true, session };
|
|
2764
|
+
function toBase64(buffer) {
|
|
2765
|
+
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
2766
|
+
}
|
|
2767
|
+
function readEnvValue(name) {
|
|
2768
|
+
const meta = ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) });
|
|
2769
|
+
const metaValue = meta.env?.[name];
|
|
2770
|
+
if (typeof metaValue === "string" && metaValue.trim().length > 0) {
|
|
2771
|
+
return metaValue.trim();
|
|
2761
2772
|
}
|
|
2762
|
-
const
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
const message = detail?.message ?? res.statusText ?? `HTTP ${res.status}`;
|
|
2766
|
-
if (res.status === 422 && code === "DEPOSIT_TX_NOT_FOUND") {
|
|
2767
|
-
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
2773
|
+
const processValue = globalThis.process?.env?.[name];
|
|
2774
|
+
if (typeof processValue === "string" && processValue.trim().length > 0) {
|
|
2775
|
+
return processValue.trim();
|
|
2768
2776
|
}
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
]);
|
|
2776
|
-
if (res.status === 422 && code && approvalNotDetectedCodes.has(code)) {
|
|
2777
|
-
return { detected: false, reason: "not-found", status: res.status, code, message };
|
|
2777
|
+
return void 0;
|
|
2778
|
+
}
|
|
2779
|
+
function resolvePasskeyRpId() {
|
|
2780
|
+
const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
|
|
2781
|
+
if (configuredDomain) {
|
|
2782
|
+
return normalizeConfiguredDomain(configuredDomain);
|
|
2778
2783
|
}
|
|
2779
|
-
if (
|
|
2780
|
-
return
|
|
2784
|
+
if (typeof window !== "undefined") {
|
|
2785
|
+
return resolveRootDomainFromHostname(window.location.hostname);
|
|
2786
|
+
}
|
|
2787
|
+
return "localhost";
|
|
2788
|
+
}
|
|
2789
|
+
async function deviceHasPasskey(credentialId) {
|
|
2790
|
+
const found = await findDevicePasskey([credentialId]);
|
|
2791
|
+
return found != null;
|
|
2792
|
+
}
|
|
2793
|
+
async function findDevicePasskey(credentialIds) {
|
|
2794
|
+
if (credentialIds.length === 0) return null;
|
|
2795
|
+
try {
|
|
2796
|
+
const challenge = new Uint8Array(32);
|
|
2797
|
+
crypto.getRandomValues(challenge);
|
|
2798
|
+
await waitForDocumentFocus();
|
|
2799
|
+
const assertion = await navigator.credentials.get({
|
|
2800
|
+
publicKey: {
|
|
2801
|
+
challenge,
|
|
2802
|
+
rpId: resolvePasskeyRpId(),
|
|
2803
|
+
allowCredentials: credentialIds.map((id) => ({
|
|
2804
|
+
type: "public-key",
|
|
2805
|
+
id: credentialIdBase64ToBytes(id)
|
|
2806
|
+
})),
|
|
2807
|
+
userVerification: "discouraged",
|
|
2808
|
+
timeout: 3e4
|
|
2809
|
+
}
|
|
2810
|
+
});
|
|
2811
|
+
if (!assertion) return null;
|
|
2812
|
+
return toBase64(assertion.rawId);
|
|
2813
|
+
} catch {
|
|
2814
|
+
return null;
|
|
2781
2815
|
}
|
|
2782
|
-
return { detected: false, reason: "error", status: res.status, code, message };
|
|
2783
2816
|
}
|
|
2784
2817
|
|
|
2785
2818
|
// src/transferPolling.ts
|
|
@@ -5810,6 +5843,43 @@ function useAuthorizationExecutor(options) {
|
|
|
5810
5843
|
}),
|
|
5811
5844
|
[]
|
|
5812
5845
|
);
|
|
5846
|
+
const [awaitingApproval, setAwaitingApproval] = react.useState(false);
|
|
5847
|
+
const [approvalDestinationAddress, setApprovalDestinationAddress] = react.useState(null);
|
|
5848
|
+
const approvalResolverRef = react.useRef(null);
|
|
5849
|
+
const approvalRejectRef = react.useRef(null);
|
|
5850
|
+
const approvalInitiatedRef = react.useRef(false);
|
|
5851
|
+
const waitForApproval = react.useCallback(
|
|
5852
|
+
(action, destinationAddress) => {
|
|
5853
|
+
if (approvalInitiatedRef.current) return Promise.resolve();
|
|
5854
|
+
return new Promise((resolve, reject) => {
|
|
5855
|
+
approvalResolverRef.current = resolve;
|
|
5856
|
+
approvalRejectRef.current = reject;
|
|
5857
|
+
setCurrentAction(action);
|
|
5858
|
+
setApprovalDestinationAddress(destinationAddress);
|
|
5859
|
+
setAwaitingApproval(true);
|
|
5860
|
+
});
|
|
5861
|
+
},
|
|
5862
|
+
[]
|
|
5863
|
+
);
|
|
5864
|
+
const approveAuthorization = react.useCallback(() => {
|
|
5865
|
+
approvalInitiatedRef.current = true;
|
|
5866
|
+
setAwaitingApproval(false);
|
|
5867
|
+
if (approvalResolverRef.current) {
|
|
5868
|
+
approvalResolverRef.current();
|
|
5869
|
+
approvalResolverRef.current = null;
|
|
5870
|
+
approvalRejectRef.current = null;
|
|
5871
|
+
}
|
|
5872
|
+
}, []);
|
|
5873
|
+
const resetApprovalGate = react.useCallback(() => {
|
|
5874
|
+
approvalInitiatedRef.current = false;
|
|
5875
|
+
setAwaitingApproval(false);
|
|
5876
|
+
setApprovalDestinationAddress(null);
|
|
5877
|
+
if (approvalRejectRef.current) {
|
|
5878
|
+
approvalRejectRef.current(new AuthorizationSessionCancelledError());
|
|
5879
|
+
approvalRejectRef.current = null;
|
|
5880
|
+
approvalResolverRef.current = null;
|
|
5881
|
+
}
|
|
5882
|
+
}, []);
|
|
5813
5883
|
const cancelPendingExecution = react.useCallback(() => {
|
|
5814
5884
|
if (selectSourceRejectRef.current) {
|
|
5815
5885
|
selectSourceRejectRef.current(new AuthorizationSessionCancelledError());
|
|
@@ -5817,6 +5887,12 @@ function useAuthorizationExecutor(options) {
|
|
|
5817
5887
|
selectSourceResolverRef.current = null;
|
|
5818
5888
|
setPendingSelectSource(null);
|
|
5819
5889
|
}
|
|
5890
|
+
if (approvalRejectRef.current) {
|
|
5891
|
+
approvalRejectRef.current(new AuthorizationSessionCancelledError());
|
|
5892
|
+
approvalRejectRef.current = null;
|
|
5893
|
+
approvalResolverRef.current = null;
|
|
5894
|
+
}
|
|
5895
|
+
setAwaitingApproval(false);
|
|
5820
5896
|
setError(null);
|
|
5821
5897
|
setCurrentAction(null);
|
|
5822
5898
|
setApproveSplConfirming(null);
|
|
@@ -6219,6 +6295,11 @@ function useAuthorizationExecutor(options) {
|
|
|
6219
6295
|
currentAction,
|
|
6220
6296
|
approveSplConfirming,
|
|
6221
6297
|
pendingSelectSource,
|
|
6298
|
+
awaitingApproval,
|
|
6299
|
+
approvalDestinationAddress,
|
|
6300
|
+
waitForApproval,
|
|
6301
|
+
approveAuthorization,
|
|
6302
|
+
resetApprovalGate,
|
|
6222
6303
|
resolveSelectSource,
|
|
6223
6304
|
cancelPendingExecution,
|
|
6224
6305
|
resetWalletConnect,
|
|
@@ -6513,6 +6594,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6513
6594
|
const [pendingSelectSourceAction, setPendingSelectSourceAction] = react.useState(null);
|
|
6514
6595
|
const [orchestratorCompleted, setOrchestratorCompleted] = react.useState(false);
|
|
6515
6596
|
const [sourceSelectionResolved, setSourceSelectionResolved] = react.useState(false);
|
|
6597
|
+
const [approvalSmartAccountAddress, setApprovalSmartAccountAddress] = react.useState(null);
|
|
6516
6598
|
const selectSourceResolverRef = react.useRef(null);
|
|
6517
6599
|
const selectSourceRejectRef = react.useRef(null);
|
|
6518
6600
|
const submittedApprovePermit2ActionIdsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
@@ -6538,6 +6620,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6538
6620
|
const cancellation = new AuthorizationSessionCancelledError();
|
|
6539
6621
|
setOrchestratorCompleted(false);
|
|
6540
6622
|
setSourceSelectionResolved(false);
|
|
6623
|
+
setApprovalSmartAccountAddress(null);
|
|
6541
6624
|
if (selectSourceRejectRef.current) {
|
|
6542
6625
|
selectSourceRejectRef.current(cancellation);
|
|
6543
6626
|
selectSourceRejectRef.current = null;
|
|
@@ -6560,6 +6643,10 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6560
6643
|
lastRunRef.current = { sessionId, options };
|
|
6561
6644
|
setOrchestratorCompleted(false);
|
|
6562
6645
|
setSourceSelectionResolved(false);
|
|
6646
|
+
setApprovalSmartAccountAddress(null);
|
|
6647
|
+
if (!options?.keepApprovalGate) {
|
|
6648
|
+
authExecutor.resetApprovalGate();
|
|
6649
|
+
}
|
|
6563
6650
|
if (!authExecutor.beginExecution()) {
|
|
6564
6651
|
appendDebug("warn", "orchestrator:run aborted \u2014 already executing");
|
|
6565
6652
|
return { status: "cancelled" };
|
|
@@ -6588,6 +6675,8 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6588
6675
|
let action = mergedPending[0];
|
|
6589
6676
|
if (completedIds.has(action.id)) break;
|
|
6590
6677
|
const ownerSessionId = actionSessionMap.get(action.id) ?? sessionId;
|
|
6678
|
+
const ownerSessionSca = sessions.find((s) => s.id === ownerSessionId)?.session.smartAccountAddress;
|
|
6679
|
+
if (ownerSessionSca) setApprovalSmartAccountAddress(ownerSessionSca);
|
|
6591
6680
|
console.info("[blink-sdk][orchestrator] Next pending action.", {
|
|
6592
6681
|
actionId: action.id,
|
|
6593
6682
|
actionType: action.type,
|
|
@@ -6612,6 +6701,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6612
6701
|
const autoResult = createSelectSourceResult(action, options.autoResolveSource);
|
|
6613
6702
|
completedIds.add(action.id);
|
|
6614
6703
|
authExecutor.addResult(autoResult);
|
|
6704
|
+
setSourceSelectionResolved(true);
|
|
6615
6705
|
const reportedSession3 = await reportActionCompletionWithLogging(
|
|
6616
6706
|
apiBaseUrl,
|
|
6617
6707
|
action,
|
|
@@ -6705,6 +6795,13 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6705
6795
|
throw new Error(SIGN_PERMIT2_CONFIRMING_MESSAGE);
|
|
6706
6796
|
}
|
|
6707
6797
|
}
|
|
6798
|
+
if (isApprovalGatedAction(action)) {
|
|
6799
|
+
const sessionSca = sessions.find((s) => s.id === ownerSessionId)?.session.smartAccountAddress;
|
|
6800
|
+
await authExecutor.waitForApproval(
|
|
6801
|
+
action,
|
|
6802
|
+
sessionSca ?? readActionMetadataString(action.metadata, "smartAccountAddress") ?? readActionMetadataString(action.metadata, "ownerPubkey")
|
|
6803
|
+
);
|
|
6804
|
+
}
|
|
6708
6805
|
appendDebug("info", `orchestrator:executeAction start ${action.type}`, {
|
|
6709
6806
|
actionId: action.id,
|
|
6710
6807
|
ownerSessionId
|
|
@@ -6901,7 +6998,8 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6901
6998
|
authExecutor.setError(null);
|
|
6902
6999
|
return run(lastRun.sessionId, {
|
|
6903
7000
|
...lastRun.options,
|
|
6904
|
-
probeBeforePrompt: true
|
|
7001
|
+
probeBeforePrompt: true,
|
|
7002
|
+
keepApprovalGate: true
|
|
6905
7003
|
});
|
|
6906
7004
|
}, [authExecutor, run, cancelPendingFlow]);
|
|
6907
7005
|
return {
|
|
@@ -6911,9 +7009,17 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6911
7009
|
resolveSelectSource,
|
|
6912
7010
|
sourceSelectionResolved,
|
|
6913
7011
|
orchestratorCompleted,
|
|
7012
|
+
approvalSmartAccountAddress,
|
|
6914
7013
|
cancelPendingFlow
|
|
6915
7014
|
};
|
|
6916
7015
|
}
|
|
7016
|
+
function readActionMetadataString(metadata, key) {
|
|
7017
|
+
const value = metadata?.[key];
|
|
7018
|
+
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
7019
|
+
}
|
|
7020
|
+
function isApprovalGatedAction(action) {
|
|
7021
|
+
return action.type === "APPROVE_PERMIT2" || action.type === "SIGN_PERMIT2" || action.type === "APPROVE_SPL";
|
|
7022
|
+
}
|
|
6917
7023
|
function createSelectSourceResult(action, selection) {
|
|
6918
7024
|
return {
|
|
6919
7025
|
actionId: action.id,
|
|
@@ -7884,6 +7990,26 @@ function deriveSourceTypeAndId(state) {
|
|
|
7884
7990
|
}
|
|
7885
7991
|
return { sourceType: "accountId", sourceId: "" };
|
|
7886
7992
|
}
|
|
7993
|
+
function hasAnyBalances(accounts) {
|
|
7994
|
+
return accounts.some(
|
|
7995
|
+
(account) => account.wallets.some(
|
|
7996
|
+
(wallet) => wallet.balance !== void 0 || wallet.sources !== void 0
|
|
7997
|
+
)
|
|
7998
|
+
);
|
|
7999
|
+
}
|
|
8000
|
+
function carryOverBalances(prev, next) {
|
|
8001
|
+
const prevWallets = new Map(
|
|
8002
|
+
prev.flatMap((account) => account.wallets.map((wallet) => [wallet.id, wallet]))
|
|
8003
|
+
);
|
|
8004
|
+
return next.map((account) => ({
|
|
8005
|
+
...account,
|
|
8006
|
+
wallets: account.wallets.map((wallet) => {
|
|
8007
|
+
if (wallet.balance !== void 0 || wallet.sources !== void 0) return wallet;
|
|
8008
|
+
const prior = prevWallets.get(wallet.id);
|
|
8009
|
+
return prior && (prior.balance !== void 0 || prior.sources !== void 0) ? { ...wallet, balance: prior.balance, sources: prior.sources } : wallet;
|
|
8010
|
+
})
|
|
8011
|
+
}));
|
|
8012
|
+
}
|
|
7887
8013
|
function clearStaleSelection(state) {
|
|
7888
8014
|
if (state.selectedAccountId == null) return state;
|
|
7889
8015
|
if (state.desktopWait != null || state.standardDesktopInlineOpenWallet) return state;
|
|
@@ -7905,6 +8031,7 @@ function createInitialState(config) {
|
|
|
7905
8031
|
accounts: [],
|
|
7906
8032
|
chains: [],
|
|
7907
8033
|
loadingData: false,
|
|
8034
|
+
balancesLoading: false,
|
|
7908
8035
|
depositSelectionRefreshing: false,
|
|
7909
8036
|
selectedProviderId: null,
|
|
7910
8037
|
selectedAccountId: null,
|
|
@@ -7934,6 +8061,7 @@ function createInitialState(config) {
|
|
|
7934
8061
|
privyAuthenticated: false,
|
|
7935
8062
|
lastResumedAt: 0,
|
|
7936
8063
|
setupDepositToken: null,
|
|
8064
|
+
setupSpendingLimit: null,
|
|
7937
8065
|
guestWalletPrepared: null,
|
|
7938
8066
|
guestWalletDeeplinksPreparing: false,
|
|
7939
8067
|
amountTooLow: null,
|
|
@@ -7953,6 +8081,7 @@ function clearAuthenticatedSessionState(state) {
|
|
|
7953
8081
|
accounts: [],
|
|
7954
8082
|
chains: [],
|
|
7955
8083
|
loadingData: false,
|
|
8084
|
+
balancesLoading: false,
|
|
7956
8085
|
depositSelectionRefreshing: false,
|
|
7957
8086
|
selectedProviderId: null,
|
|
7958
8087
|
selectedAccountId: null,
|
|
@@ -7978,6 +8107,7 @@ function clearAuthenticatedSessionState(state) {
|
|
|
7978
8107
|
mobileTokenAuthorizationPending: false,
|
|
7979
8108
|
linkSettling: false,
|
|
7980
8109
|
setupDepositToken: null,
|
|
8110
|
+
setupSpendingLimit: null,
|
|
7981
8111
|
guestWalletPrepared: null,
|
|
7982
8112
|
guestWalletDeeplinksPreparing: false,
|
|
7983
8113
|
amountTooLow: null,
|
|
@@ -8046,6 +8176,8 @@ function applyAction(state, action) {
|
|
|
8046
8176
|
providers: action.providers,
|
|
8047
8177
|
accounts: action.accounts,
|
|
8048
8178
|
chains: action.chains,
|
|
8179
|
+
// Accounts arrive balance-free; balances follow via BALANCES_LOADED.
|
|
8180
|
+
balancesLoading: true,
|
|
8049
8181
|
depositSelectionRefreshing: false,
|
|
8050
8182
|
initialDataLoaded: true
|
|
8051
8183
|
};
|
|
@@ -8069,10 +8201,12 @@ function applyAction(state, action) {
|
|
|
8069
8201
|
depositSelectionRefreshing: false
|
|
8070
8202
|
};
|
|
8071
8203
|
case "ACCOUNTS_RELOADED": {
|
|
8204
|
+
const hadBalances = hasAnyBalances(state.accounts);
|
|
8072
8205
|
const next = {
|
|
8073
8206
|
...state,
|
|
8074
|
-
accounts: action.accounts,
|
|
8207
|
+
accounts: carryOverBalances(state.accounts, action.accounts),
|
|
8075
8208
|
providers: action.providers,
|
|
8209
|
+
balancesLoading: !hadBalances,
|
|
8076
8210
|
initialDataLoaded: true
|
|
8077
8211
|
};
|
|
8078
8212
|
if (action.defaults) {
|
|
@@ -8084,6 +8218,24 @@ function applyAction(state, action) {
|
|
|
8084
8218
|
}
|
|
8085
8219
|
return clearStaleSelection(next);
|
|
8086
8220
|
}
|
|
8221
|
+
case "BALANCES_LOADED": {
|
|
8222
|
+
const { balancesByAccountId } = action;
|
|
8223
|
+
const accounts = state.accounts.map((account) => {
|
|
8224
|
+
const accountBalances = balancesByAccountId[account.id];
|
|
8225
|
+
if (!accountBalances) return account;
|
|
8226
|
+
const balancesByWalletId = new Map(
|
|
8227
|
+
accountBalances.wallets.map((w) => [w.id, w])
|
|
8228
|
+
);
|
|
8229
|
+
return {
|
|
8230
|
+
...account,
|
|
8231
|
+
wallets: account.wallets.map((wallet) => {
|
|
8232
|
+
const merged = balancesByWalletId.get(wallet.id);
|
|
8233
|
+
return merged ? { ...wallet, balance: merged.balance, sources: merged.sources } : wallet;
|
|
8234
|
+
})
|
|
8235
|
+
};
|
|
8236
|
+
});
|
|
8237
|
+
return { ...state, accounts, balancesLoading: false };
|
|
8238
|
+
}
|
|
8087
8239
|
case "SET_DEPOSIT_SELECTION_REFRESHING":
|
|
8088
8240
|
return { ...state, depositSelectionRefreshing: action.value };
|
|
8089
8241
|
case "SAVE_SELECTION":
|
|
@@ -8155,6 +8307,7 @@ function applyAction(state, action) {
|
|
|
8155
8307
|
mobileFlow: false,
|
|
8156
8308
|
mobileTokenAuthorizationPending: false,
|
|
8157
8309
|
setupDepositToken: null,
|
|
8310
|
+
setupSpendingLimit: null,
|
|
8158
8311
|
setupFlowScreen: null
|
|
8159
8312
|
};
|
|
8160
8313
|
case "PAY_ENDED":
|
|
@@ -8400,6 +8553,7 @@ function applyAction(state, action) {
|
|
|
8400
8553
|
linkSettling: false,
|
|
8401
8554
|
savedSelection: null,
|
|
8402
8555
|
setupDepositToken: null,
|
|
8556
|
+
setupSpendingLimit: null,
|
|
8403
8557
|
setupFlowScreen: null,
|
|
8404
8558
|
guestWalletPrepared: null,
|
|
8405
8559
|
guestWalletDeeplinksPreparing: false,
|
|
@@ -8465,8 +8619,10 @@ function applyAction(state, action) {
|
|
|
8465
8619
|
...action.chainId != null ? { chainId: action.chainId } : {}
|
|
8466
8620
|
}
|
|
8467
8621
|
};
|
|
8622
|
+
case "SET_SETUP_SPENDING_LIMIT":
|
|
8623
|
+
return { ...state, setupSpendingLimit: action.limit };
|
|
8468
8624
|
case "CLEAR_SETUP_DEPOSIT_TOKEN":
|
|
8469
|
-
return { ...state, setupDepositToken: null };
|
|
8625
|
+
return { ...state, setupDepositToken: null, setupSpendingLimit: null };
|
|
8470
8626
|
default:
|
|
8471
8627
|
return state;
|
|
8472
8628
|
}
|
|
@@ -9252,9 +9408,12 @@ function PrimaryButton({
|
|
|
9252
9408
|
}
|
|
9253
9409
|
);
|
|
9254
9410
|
}
|
|
9411
|
+
var BUTTON_MIN_HEIGHT = 60;
|
|
9255
9412
|
var progressButtonStyle = (tokens, paused) => ({
|
|
9256
9413
|
position: "relative",
|
|
9257
9414
|
width: "100%",
|
|
9415
|
+
minHeight: BUTTON_MIN_HEIGHT,
|
|
9416
|
+
boxSizing: "border-box",
|
|
9258
9417
|
padding: "18px 24px",
|
|
9259
9418
|
background: `linear-gradient(180deg, ${tokens.accent}88, ${tokens.accentHover}88)`,
|
|
9260
9419
|
color: tokens.accentText,
|
|
@@ -9291,6 +9450,8 @@ var buttonStyle2 = (tokens, state) => {
|
|
|
9291
9450
|
const gradient = `linear-gradient(180deg, ${tokens.accent}, ${tokens.accentHover})`;
|
|
9292
9451
|
return {
|
|
9293
9452
|
width: "100%",
|
|
9453
|
+
minHeight: BUTTON_MIN_HEIGHT,
|
|
9454
|
+
boxSizing: "border-box",
|
|
9294
9455
|
padding: "18px 24px",
|
|
9295
9456
|
// Layer a faint white film over the gradient on hover rather than using
|
|
9296
9457
|
// `filter: brightness()` — the accent gradient is near-black in the new
|
|
@@ -10217,6 +10378,8 @@ function SourcePill({
|
|
|
10217
10378
|
logo,
|
|
10218
10379
|
name,
|
|
10219
10380
|
balance,
|
|
10381
|
+
nameSlot,
|
|
10382
|
+
balanceSlot,
|
|
10220
10383
|
expanded,
|
|
10221
10384
|
showChevron = true,
|
|
10222
10385
|
icon
|
|
@@ -10224,8 +10387,8 @@ function SourcePill({
|
|
|
10224
10387
|
const { tokens } = useBlinkConfig();
|
|
10225
10388
|
return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: pillStyle2(tokens.bgCardTranslucent), children: [
|
|
10226
10389
|
icon ?? (logo ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logo, alt: "", "aria-hidden": "true", style: logoStyle }) : name ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: logoFallbackStyle(tokens.bgInput, tokens.textMuted), children: name.charAt(0).toUpperCase() }) : null),
|
|
10227
|
-
name && /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelStyle2(tokens.text), children: name }),
|
|
10228
|
-
balance && /* @__PURE__ */ jsxRuntime.jsx("span", { style: balanceStyle(tokens.text), children: balance }),
|
|
10390
|
+
nameSlot ?? (name && /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelStyle2(tokens.text), children: name })),
|
|
10391
|
+
balanceSlot ?? (balance && /* @__PURE__ */ jsxRuntime.jsx("span", { style: balanceStyle(tokens.text), children: balance })),
|
|
10229
10392
|
showChevron && /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "17", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: expanded ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10230
10393
|
"path",
|
|
10231
10394
|
{
|
|
@@ -10288,6 +10451,37 @@ var balanceStyle = (color) => ({
|
|
|
10288
10451
|
color,
|
|
10289
10452
|
whiteSpace: "nowrap"
|
|
10290
10453
|
});
|
|
10454
|
+
var SHIMMER_KEYFRAMES = `
|
|
10455
|
+
@keyframes blink-shimmer-sweep {
|
|
10456
|
+
0% { background-position: 200% 0; }
|
|
10457
|
+
100% { background-position: -200% 0; }
|
|
10458
|
+
}`;
|
|
10459
|
+
function Shimmer({
|
|
10460
|
+
width,
|
|
10461
|
+
height,
|
|
10462
|
+
borderRadius = 999,
|
|
10463
|
+
baseColor,
|
|
10464
|
+
highlightColor,
|
|
10465
|
+
style
|
|
10466
|
+
}) {
|
|
10467
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", style: { display: "inline-block", ...style }, children: [
|
|
10468
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: SHIMMER_KEYFRAMES }),
|
|
10469
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10470
|
+
"span",
|
|
10471
|
+
{
|
|
10472
|
+
style: {
|
|
10473
|
+
display: "block",
|
|
10474
|
+
width,
|
|
10475
|
+
height,
|
|
10476
|
+
borderRadius,
|
|
10477
|
+
background: `linear-gradient(90deg, ${baseColor} 0%, ${highlightColor} 50%, ${baseColor} 100%)`,
|
|
10478
|
+
backgroundSize: "200% 100%",
|
|
10479
|
+
animation: "blink-shimmer-sweep 1.4s ease-in-out infinite"
|
|
10480
|
+
}
|
|
10481
|
+
}
|
|
10482
|
+
)
|
|
10483
|
+
] });
|
|
10484
|
+
}
|
|
10291
10485
|
function formatUsdTwoDecimals(value) {
|
|
10292
10486
|
const n = Number(value);
|
|
10293
10487
|
return (Number.isFinite(n) ? n : 0).toLocaleString("en-US", {
|
|
@@ -10988,6 +11182,14 @@ function LockIcon({ size = 24 }) {
|
|
|
10988
11182
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 11V8a4 4 0 1 1 8 0v3", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" })
|
|
10989
11183
|
] });
|
|
10990
11184
|
}
|
|
11185
|
+
function KeyIcon({ size = 24 }) {
|
|
11186
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
11187
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "7", cy: "12", r: "3.6", stroke: "currentColor", strokeWidth: "2" }),
|
|
11188
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10.6 12H20", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
11189
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17 12v3.2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
11190
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 12v3.2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
|
|
11191
|
+
] });
|
|
11192
|
+
}
|
|
10991
11193
|
function FaceIdIcon({ size = 24 }) {
|
|
10992
11194
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
10993
11195
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 9V6a2 2 0 0 1 2-2h3", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }),
|
|
@@ -11001,6 +11203,21 @@ function FaceIdIcon({ size = 24 }) {
|
|
|
11001
11203
|
/* @__PURE__ */ jsxRuntime.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" })
|
|
11002
11204
|
] });
|
|
11003
11205
|
}
|
|
11206
|
+
function PencilIcon({ size = 18 } = {}) {
|
|
11207
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
11208
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11209
|
+
"path",
|
|
11210
|
+
{
|
|
11211
|
+
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",
|
|
11212
|
+
stroke: "currentColor",
|
|
11213
|
+
strokeWidth: "1.7",
|
|
11214
|
+
strokeLinecap: "round",
|
|
11215
|
+
strokeLinejoin: "round"
|
|
11216
|
+
}
|
|
11217
|
+
),
|
|
11218
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.5 6.5l4 4", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
11219
|
+
] });
|
|
11220
|
+
}
|
|
11004
11221
|
function WalletIcon() {
|
|
11005
11222
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
11006
11223
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -11372,7 +11589,7 @@ function CopyAddressButton({
|
|
|
11372
11589
|
}) {
|
|
11373
11590
|
const { tokens } = useBlinkConfig();
|
|
11374
11591
|
const [hovered, setHovered] = react.useState(false);
|
|
11375
|
-
const short =
|
|
11592
|
+
const short = truncateMiddle(address);
|
|
11376
11593
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11377
11594
|
"button",
|
|
11378
11595
|
{
|
|
@@ -13530,7 +13747,6 @@ var errorBannerStyle6 = (tokens) => ({
|
|
|
13530
13747
|
textAlign: "left",
|
|
13531
13748
|
boxSizing: "border-box"
|
|
13532
13749
|
});
|
|
13533
|
-
var SHIMMER_ROWS = 3;
|
|
13534
13750
|
function LinkTokensScreen({
|
|
13535
13751
|
entries: entries2,
|
|
13536
13752
|
selectedIndex,
|
|
@@ -13543,44 +13759,75 @@ function LinkTokensScreen({
|
|
|
13543
13759
|
approving = false
|
|
13544
13760
|
}) {
|
|
13545
13761
|
const { tokens: t } = useBlinkConfig();
|
|
13762
|
+
const [view, setView] = react.useState("summary");
|
|
13763
|
+
const [pendingIndex, setPendingIndex] = react.useState(selectedIndex);
|
|
13764
|
+
const [limit, setLimit] = react.useState("unlimited");
|
|
13765
|
+
const [editing, setEditing] = react.useState(false);
|
|
13546
13766
|
const showShimmer = loading && entries2.length === 0;
|
|
13547
13767
|
const showEmpty = !loading && entries2.length === 0;
|
|
13548
13768
|
const selected = entries2[selectedIndex];
|
|
13769
|
+
const limitUsd = limit === "unlimited" ? null : parseAmount(limit);
|
|
13770
|
+
const isUnlimited = limitUsd == null;
|
|
13771
|
+
const limitLabel = isUnlimited ? "Unlimited" : `$${limit}`;
|
|
13772
|
+
const currentSelection = () => limitUsd == null ? { unlimited: true } : { usd: limitUsd };
|
|
13549
13773
|
const approveDisabled = loading || approving || entries2.length === 0 || selectedIndex < 0 || selectedIndex >= entries2.length || !!selected?.notSupported;
|
|
13550
|
-
|
|
13774
|
+
function openSelectToken() {
|
|
13775
|
+
setPendingIndex(selectedIndex);
|
|
13776
|
+
setView("selectToken");
|
|
13777
|
+
}
|
|
13778
|
+
function commitSelectToken() {
|
|
13779
|
+
const row = entries2[pendingIndex];
|
|
13780
|
+
if (row && !row.notSupported) onSelect(pendingIndex);
|
|
13781
|
+
setView("summary");
|
|
13782
|
+
}
|
|
13783
|
+
if (view === "selectToken") {
|
|
13784
|
+
const pending = entries2[pendingIndex];
|
|
13785
|
+
const continueDisabled = !pending || !!pending.notSupported || entries2.length === 0;
|
|
13786
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13787
|
+
ScreenLayout,
|
|
13788
|
+
{
|
|
13789
|
+
scrollableBody: false,
|
|
13790
|
+
footer: /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: commitSelectToken, disabled: continueDisabled, children: "Continue" }),
|
|
13791
|
+
children: [
|
|
13792
|
+
/* @__PURE__ */ jsxRuntime.jsx(ListScrollbarStyles, { textTertiary: t.textTertiary }),
|
|
13793
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13794
|
+
ScreenHeader,
|
|
13795
|
+
{
|
|
13796
|
+
onBack: () => setView("summary"),
|
|
13797
|
+
center: /* @__PURE__ */ jsxRuntime.jsx("img", { src: BLINK_WORDMARK, alt: "Blink", style: wordmarkImgStyle3 })
|
|
13798
|
+
}
|
|
13799
|
+
),
|
|
13800
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle10, children: [
|
|
13801
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(t.text, t.fontWeightBold), children: "Select token" }),
|
|
13802
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "blink-link-tokens-list", style: listCardStyle(t.bgRecessed), children: [
|
|
13803
|
+
entries2.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
13804
|
+
TokenSourceRow,
|
|
13805
|
+
{
|
|
13806
|
+
symbol: entry.tokenSymbol,
|
|
13807
|
+
chainName: entry.chainName,
|
|
13808
|
+
tokenLogoUri: entry.tokenLogoUri,
|
|
13809
|
+
balance: entry.balanceLabel == null ? entry.balanceUsd : void 0,
|
|
13810
|
+
balanceLabel: entry.balanceLabel,
|
|
13811
|
+
selected: i === pendingIndex,
|
|
13812
|
+
notSupported: entry.notSupported,
|
|
13813
|
+
onClick: () => setPendingIndex(i)
|
|
13814
|
+
},
|
|
13815
|
+
`${entry.tokenSymbol}-${entry.chainName}-${i}`
|
|
13816
|
+
)),
|
|
13817
|
+
showEmpty && /* @__PURE__ */ jsxRuntime.jsx("div", { style: emptyStyle(t.textMuted), children: "No tokens available to link. Switch wallets or fund this account to continue." })
|
|
13818
|
+
] })
|
|
13819
|
+
] })
|
|
13820
|
+
]
|
|
13821
|
+
}
|
|
13822
|
+
);
|
|
13823
|
+
}
|
|
13551
13824
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13552
13825
|
ScreenLayout,
|
|
13553
13826
|
{
|
|
13554
13827
|
scrollableBody: false,
|
|
13555
|
-
footer: /* @__PURE__ */ jsxRuntime.
|
|
13556
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: lockBannerStyle2, children: [
|
|
13557
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: lockIconWrapStyle2(t.text), children: /* @__PURE__ */ jsxRuntime.jsx(LockIcon3, {}) }),
|
|
13558
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: lockBannerTextStyle2(t.text), children: "Your passkey is required each time you deposit. Funds cannot move without your approval." })
|
|
13559
|
-
] }),
|
|
13560
|
-
/* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onApprove, disabled: approveDisabled, loading: approving, children: ctaLabel })
|
|
13561
|
-
] }),
|
|
13828
|
+
footer: /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: () => onApprove(currentSelection()), disabled: approveDisabled, loading: approving, children: "Review" }),
|
|
13562
13829
|
children: [
|
|
13563
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13564
|
-
@keyframes blink-link-tokens-shimmer {
|
|
13565
|
-
0% { background-position: 200% 0; }
|
|
13566
|
-
100% { background-position: -200% 0; }
|
|
13567
|
-
}
|
|
13568
|
-
.blink-link-tokens-list {
|
|
13569
|
-
scrollbar-width: thin;
|
|
13570
|
-
scrollbar-color: ${t.textTertiary} transparent;
|
|
13571
|
-
}
|
|
13572
|
-
.blink-link-tokens-list::-webkit-scrollbar {
|
|
13573
|
-
width: 4px;
|
|
13574
|
-
}
|
|
13575
|
-
.blink-link-tokens-list::-webkit-scrollbar-track {
|
|
13576
|
-
background: transparent;
|
|
13577
|
-
margin: 8px 0;
|
|
13578
|
-
}
|
|
13579
|
-
.blink-link-tokens-list::-webkit-scrollbar-thumb {
|
|
13580
|
-
background: ${t.textTertiary};
|
|
13581
|
-
border-radius: 999px;
|
|
13582
|
-
}
|
|
13583
|
-
` }),
|
|
13830
|
+
/* @__PURE__ */ jsxRuntime.jsx(ListScrollbarStyles, { textTertiary: t.textTertiary }),
|
|
13584
13831
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13585
13832
|
ScreenHeader,
|
|
13586
13833
|
{
|
|
@@ -13590,71 +13837,198 @@ function LinkTokensScreen({
|
|
|
13590
13837
|
}
|
|
13591
13838
|
),
|
|
13592
13839
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle10, children: [
|
|
13593
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
13594
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
{
|
|
13598
|
-
"
|
|
13599
|
-
"
|
|
13600
|
-
|
|
13601
|
-
children: [
|
|
13602
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerCircleStyle(t.bgInput, t.border) }),
|
|
13603
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: shimmerInfoStyle, children: [
|
|
13604
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerLineStyle(72, t.bgInput, t.border) }),
|
|
13605
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerLineStyle(48, t.bgInput, t.border) })
|
|
13606
|
-
] }),
|
|
13840
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: flexSpacerStyle }),
|
|
13841
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: topGroupStyle, children: [
|
|
13842
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(t.text, t.fontWeightBold), children: "Let your passkey spend" }),
|
|
13843
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: pillsGroupStyle, children: [
|
|
13844
|
+
showShimmer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": "link-tokens-summary-shimmer", "aria-hidden": "true", style: pillStyle3(t.bgRecessed), children: [
|
|
13845
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerCircleStyle(t.bgInput, t.border) }),
|
|
13846
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: shimmerInfoStyle, children: [
|
|
13847
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerLineStyle(96, t.bgInput, t.border) }),
|
|
13607
13848
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerLineStyle(56, t.bgInput, t.border) })
|
|
13608
|
-
]
|
|
13609
|
-
}
|
|
13610
|
-
|
|
13611
|
-
|
|
13612
|
-
|
|
13613
|
-
|
|
13614
|
-
|
|
13615
|
-
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
|
|
13619
|
-
|
|
13620
|
-
|
|
13621
|
-
|
|
13622
|
-
|
|
13623
|
-
|
|
13624
|
-
|
|
13625
|
-
|
|
13849
|
+
] })
|
|
13850
|
+
] }) : selected ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13851
|
+
"button",
|
|
13852
|
+
{
|
|
13853
|
+
type: "button",
|
|
13854
|
+
onClick: openSelectToken,
|
|
13855
|
+
style: pillButtonStyle(t.bgRecessed),
|
|
13856
|
+
"aria-label": `Selected token ${selected.tokenSymbol} on ${selected.chainName}. Change token.`,
|
|
13857
|
+
"data-testid": "link-tokens-selected-pill",
|
|
13858
|
+
children: [
|
|
13859
|
+
/* @__PURE__ */ jsxRuntime.jsx(TokenPillLogo, { entry: selected, fallbackBg: t.bgRecessed, fallbackColor: t.textMuted }),
|
|
13860
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: pillTextColStyle, children: [
|
|
13861
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: pillTitleStyle(t.text, t.fontWeightRegular), children: [
|
|
13862
|
+
selected.tokenSymbol,
|
|
13863
|
+
" on ",
|
|
13864
|
+
selected.chainName
|
|
13865
|
+
] }),
|
|
13866
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: pillSubStyle(t.textMuted, t.fontWeightRegular), children: selected.balanceLabel ?? `$${formatUsdTwoDecimals2(selected.balanceUsd)}` })
|
|
13867
|
+
] }),
|
|
13868
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: pillChevronStyle(t.textMuted), children: /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { color: t.textMuted }) })
|
|
13869
|
+
]
|
|
13870
|
+
}
|
|
13871
|
+
) : null,
|
|
13872
|
+
editing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: pillStyle3(t.bgRecessed), children: [
|
|
13873
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: limitEditLabelStyle(t.text, t.fontWeightRegular), children: "Spending limit" }),
|
|
13874
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: limitEditRightStyle, children: [
|
|
13875
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: limitAmountStyle(t.text, t.fontWeightRegular), children: [
|
|
13876
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: limitDollarStyle(isUnlimited, t.text), children: "$" }),
|
|
13877
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13878
|
+
"input",
|
|
13879
|
+
{
|
|
13880
|
+
type: "text",
|
|
13881
|
+
inputMode: "decimal",
|
|
13882
|
+
autoFocus: true,
|
|
13883
|
+
placeholder: "0",
|
|
13884
|
+
"aria-label": "Lifetime spending limit",
|
|
13885
|
+
value: limit === "unlimited" ? "" : limit,
|
|
13886
|
+
onChange: (e) => setLimit(sanitizeRawInput(e.target.value)),
|
|
13887
|
+
onBlur: () => setEditing(false),
|
|
13888
|
+
"data-testid": "link-tokens-limit-input",
|
|
13889
|
+
style: limitInputStyle(t.text, t.textMuted)
|
|
13890
|
+
}
|
|
13891
|
+
)
|
|
13892
|
+
] }),
|
|
13893
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13894
|
+
"button",
|
|
13895
|
+
{
|
|
13896
|
+
type: "button",
|
|
13897
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
13898
|
+
onClick: () => {
|
|
13899
|
+
setLimit("unlimited");
|
|
13900
|
+
setEditing(false);
|
|
13901
|
+
},
|
|
13902
|
+
style: limitResetStyle(t.border, t.textMuted, t.fontWeightMedium),
|
|
13903
|
+
"data-testid": "link-tokens-limit-reset",
|
|
13904
|
+
children: "Unlimited"
|
|
13905
|
+
}
|
|
13906
|
+
)
|
|
13907
|
+
] })
|
|
13908
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13909
|
+
"button",
|
|
13910
|
+
{
|
|
13911
|
+
type: "button",
|
|
13912
|
+
onClick: () => setEditing(true),
|
|
13913
|
+
style: pillButtonStyle(t.bgRecessed),
|
|
13914
|
+
"aria-label": `Lifetime spending limit: ${limitLabel}. Edit.`,
|
|
13915
|
+
"data-testid": "link-tokens-limit-pill",
|
|
13916
|
+
children: [
|
|
13917
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: pillTitleStyle(t.text, t.fontWeightRegular), children: "Lifetime spending limit" }),
|
|
13918
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: limitValueStyle, children: [
|
|
13919
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: pillSubStyle(isUnlimited ? t.textMuted : t.text, t.fontWeightRegular), children: limitLabel }),
|
|
13920
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: pillChevronStyle(t.textMuted), children: /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, { size: 16 }) })
|
|
13921
|
+
] })
|
|
13922
|
+
]
|
|
13923
|
+
}
|
|
13924
|
+
)
|
|
13925
|
+
] })
|
|
13626
13926
|
] }),
|
|
13627
|
-
|
|
13927
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: flexSpacerStyle }),
|
|
13928
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: bottomGroupStyle, children: [
|
|
13929
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: infoCardStyle3(t.bgRecessed), children: [
|
|
13930
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13931
|
+
InfoRow,
|
|
13932
|
+
{
|
|
13933
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(LockIcon, { size: 22 }),
|
|
13934
|
+
title: "Blink is non-custodial",
|
|
13935
|
+
body: "Your passkey moves money, not Blink.",
|
|
13936
|
+
t
|
|
13937
|
+
}
|
|
13938
|
+
),
|
|
13939
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13940
|
+
InfoRow,
|
|
13941
|
+
{
|
|
13942
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(FaceIdIcon, { size: 22 }),
|
|
13943
|
+
title: "Every transaction is signed",
|
|
13944
|
+
body: "Nothing can move without your direct approval.",
|
|
13945
|
+
t
|
|
13946
|
+
}
|
|
13947
|
+
)
|
|
13948
|
+
] }),
|
|
13949
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: bannerSlotStyle, children: /* @__PURE__ */ jsxRuntime.jsx(NotificationBanner, { title: "Something went wrong", description: error }) })
|
|
13950
|
+
] })
|
|
13628
13951
|
] })
|
|
13629
13952
|
]
|
|
13630
13953
|
}
|
|
13631
13954
|
);
|
|
13632
13955
|
}
|
|
13633
|
-
function
|
|
13634
|
-
|
|
13956
|
+
function TokenPillLogo({
|
|
13957
|
+
entry,
|
|
13958
|
+
fallbackBg,
|
|
13959
|
+
fallbackColor
|
|
13960
|
+
}) {
|
|
13961
|
+
const logo = entry.tokenLogoUri ?? TOKEN_LOGOS[entry.tokenSymbol];
|
|
13962
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { style: pillLogoSlotStyle, children: logo ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logo, alt: "", "aria-hidden": "true", style: pillLogoImgStyle }) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: pillLogoFallbackStyle(fallbackBg, fallbackColor), children: entry.tokenSymbol.charAt(0).toUpperCase() }) });
|
|
13635
13963
|
}
|
|
13636
|
-
function
|
|
13637
|
-
|
|
13638
|
-
|
|
13639
|
-
|
|
13640
|
-
|
|
13641
|
-
|
|
13642
|
-
|
|
13643
|
-
|
|
13644
|
-
|
|
13645
|
-
|
|
13646
|
-
|
|
13964
|
+
function InfoRow({
|
|
13965
|
+
icon,
|
|
13966
|
+
title,
|
|
13967
|
+
body,
|
|
13968
|
+
t
|
|
13969
|
+
}) {
|
|
13970
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: infoRowStyle2, children: [
|
|
13971
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: infoIconStyle(t.text), children: icon }),
|
|
13972
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: infoTextColStyle, children: [
|
|
13973
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: infoTitleStyle2(t.text, t.fontWeightSemibold), children: title }),
|
|
13974
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: infoBodyStyle(t.textMuted, t.fontWeightMedium), children: body })
|
|
13975
|
+
] })
|
|
13976
|
+
] });
|
|
13977
|
+
}
|
|
13978
|
+
function ListScrollbarStyles({ textTertiary }) {
|
|
13979
|
+
return /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
13980
|
+
@keyframes blink-link-tokens-shimmer {
|
|
13981
|
+
0% { background-position: 200% 0; }
|
|
13982
|
+
100% { background-position: -200% 0; }
|
|
13983
|
+
}
|
|
13984
|
+
.blink-link-tokens-list {
|
|
13985
|
+
scrollbar-width: thin;
|
|
13986
|
+
scrollbar-color: ${textTertiary} transparent;
|
|
13987
|
+
}
|
|
13988
|
+
.blink-link-tokens-list::-webkit-scrollbar { width: 4px; }
|
|
13989
|
+
.blink-link-tokens-list::-webkit-scrollbar-track {
|
|
13990
|
+
background: transparent;
|
|
13991
|
+
margin: 8px 0;
|
|
13992
|
+
}
|
|
13993
|
+
.blink-link-tokens-list::-webkit-scrollbar-thumb {
|
|
13994
|
+
background: ${textTertiary};
|
|
13995
|
+
border-radius: 999px;
|
|
13996
|
+
}
|
|
13997
|
+
input[data-testid="link-tokens-limit-input"]::placeholder {
|
|
13998
|
+
color: var(--link-limit-placeholder);
|
|
13999
|
+
opacity: 1;
|
|
14000
|
+
}
|
|
14001
|
+
` });
|
|
13647
14002
|
}
|
|
13648
14003
|
var contentStyle10 = {
|
|
13649
14004
|
display: "flex",
|
|
13650
14005
|
flexDirection: "column",
|
|
13651
|
-
alignItems: "center",
|
|
13652
|
-
gap: 16,
|
|
13653
|
-
paddingTop: 8,
|
|
13654
14006
|
flex: 1,
|
|
13655
14007
|
minHeight: 0,
|
|
13656
14008
|
width: "100%"
|
|
13657
14009
|
};
|
|
14010
|
+
var flexSpacerStyle = {
|
|
14011
|
+
flex: 1,
|
|
14012
|
+
minHeight: 16
|
|
14013
|
+
};
|
|
14014
|
+
var topGroupStyle = {
|
|
14015
|
+
display: "flex",
|
|
14016
|
+
flexDirection: "column",
|
|
14017
|
+
gap: 24,
|
|
14018
|
+
width: "100%"
|
|
14019
|
+
};
|
|
14020
|
+
var bottomGroupStyle = {
|
|
14021
|
+
display: "flex",
|
|
14022
|
+
flexDirection: "column",
|
|
14023
|
+
gap: 12,
|
|
14024
|
+
width: "100%"
|
|
14025
|
+
};
|
|
14026
|
+
var pillsGroupStyle = {
|
|
14027
|
+
display: "flex",
|
|
14028
|
+
flexDirection: "column",
|
|
14029
|
+
gap: 8,
|
|
14030
|
+
width: "100%"
|
|
14031
|
+
};
|
|
13658
14032
|
var wordmarkImgStyle3 = {
|
|
13659
14033
|
height: 22,
|
|
13660
14034
|
width: "auto",
|
|
@@ -13662,9 +14036,9 @@ var wordmarkImgStyle3 = {
|
|
|
13662
14036
|
pointerEvents: "none",
|
|
13663
14037
|
userSelect: "none"
|
|
13664
14038
|
};
|
|
13665
|
-
var headingStyle7 = (color) => ({
|
|
14039
|
+
var headingStyle7 = (color, weight) => ({
|
|
13666
14040
|
fontSize: 24,
|
|
13667
|
-
fontWeight:
|
|
14041
|
+
fontWeight: weight,
|
|
13668
14042
|
lineHeight: "normal",
|
|
13669
14043
|
letterSpacing: 0,
|
|
13670
14044
|
color,
|
|
@@ -13684,68 +14058,155 @@ var listCardStyle = (bg) => ({
|
|
|
13684
14058
|
minHeight: 0,
|
|
13685
14059
|
overflowY: "auto"
|
|
13686
14060
|
});
|
|
13687
|
-
var
|
|
14061
|
+
var pillStyle3 = (bg) => ({
|
|
13688
14062
|
display: "flex",
|
|
13689
14063
|
alignItems: "center",
|
|
13690
14064
|
gap: 16,
|
|
13691
|
-
padding: "12px 16px 12px 12px",
|
|
13692
|
-
background: "transparent",
|
|
13693
|
-
border: "none",
|
|
13694
|
-
borderRadius: 16,
|
|
13695
14065
|
width: "100%",
|
|
13696
|
-
boxSizing: "border-box"
|
|
14066
|
+
boxSizing: "border-box",
|
|
14067
|
+
padding: "14px 16px",
|
|
14068
|
+
borderRadius: 20,
|
|
14069
|
+
background: bg
|
|
14070
|
+
});
|
|
14071
|
+
var pillButtonStyle = (bg) => ({
|
|
14072
|
+
...pillStyle3(bg),
|
|
14073
|
+
border: "none",
|
|
14074
|
+
cursor: "pointer",
|
|
14075
|
+
fontFamily: "inherit",
|
|
14076
|
+
textAlign: "left"
|
|
14077
|
+
});
|
|
14078
|
+
var pillTextColStyle = {
|
|
14079
|
+
display: "flex",
|
|
14080
|
+
flexDirection: "column",
|
|
14081
|
+
flex: 1,
|
|
14082
|
+
minWidth: 0,
|
|
14083
|
+
gap: 2
|
|
13697
14084
|
};
|
|
13698
|
-
var
|
|
14085
|
+
var pillTitleStyle = (color, weight) => ({
|
|
14086
|
+
fontSize: "1rem",
|
|
14087
|
+
fontWeight: weight,
|
|
14088
|
+
lineHeight: "normal",
|
|
14089
|
+
color,
|
|
14090
|
+
whiteSpace: "nowrap",
|
|
14091
|
+
overflow: "hidden",
|
|
14092
|
+
textOverflow: "ellipsis"
|
|
14093
|
+
});
|
|
14094
|
+
var pillSubStyle = (color, weight) => ({
|
|
14095
|
+
fontSize: "0.95rem",
|
|
14096
|
+
fontWeight: weight,
|
|
14097
|
+
color
|
|
14098
|
+
});
|
|
14099
|
+
var pillChevronStyle = (color) => ({
|
|
14100
|
+
flexShrink: 0,
|
|
14101
|
+
display: "inline-flex",
|
|
14102
|
+
alignItems: "center",
|
|
14103
|
+
color
|
|
14104
|
+
});
|
|
14105
|
+
var limitValueStyle = {
|
|
14106
|
+
marginLeft: "auto",
|
|
14107
|
+
display: "inline-flex",
|
|
14108
|
+
alignItems: "center",
|
|
14109
|
+
gap: 8
|
|
14110
|
+
};
|
|
14111
|
+
var pillLogoSlotStyle = {
|
|
14112
|
+
position: "relative",
|
|
13699
14113
|
width: 40,
|
|
13700
14114
|
height: 40,
|
|
13701
|
-
borderRadius: "50%",
|
|
13702
14115
|
flexShrink: 0,
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
14116
|
+
display: "inline-flex",
|
|
14117
|
+
alignItems: "center",
|
|
14118
|
+
justifyContent: "center"
|
|
14119
|
+
};
|
|
14120
|
+
var pillLogoImgStyle = {
|
|
14121
|
+
width: "100%",
|
|
14122
|
+
height: "100%",
|
|
14123
|
+
borderRadius: "50%",
|
|
14124
|
+
objectFit: "cover"
|
|
14125
|
+
};
|
|
14126
|
+
var pillLogoFallbackStyle = (bg, color) => ({
|
|
14127
|
+
width: "100%",
|
|
14128
|
+
height: "100%",
|
|
14129
|
+
borderRadius: "50%",
|
|
14130
|
+
background: bg,
|
|
14131
|
+
color,
|
|
14132
|
+
display: "inline-flex",
|
|
14133
|
+
alignItems: "center",
|
|
14134
|
+
justifyContent: "center",
|
|
14135
|
+
fontSize: "0.95rem",
|
|
14136
|
+
fontWeight: 700
|
|
13706
14137
|
});
|
|
13707
|
-
var
|
|
14138
|
+
var limitEditLabelStyle = (color, weight) => ({
|
|
14139
|
+
fontSize: "1rem",
|
|
14140
|
+
fontWeight: weight,
|
|
14141
|
+
lineHeight: "normal",
|
|
14142
|
+
color,
|
|
14143
|
+
whiteSpace: "nowrap",
|
|
14144
|
+
flexShrink: 0
|
|
14145
|
+
});
|
|
14146
|
+
var limitEditRightStyle = {
|
|
14147
|
+
marginLeft: "auto",
|
|
13708
14148
|
display: "flex",
|
|
13709
|
-
|
|
13710
|
-
gap:
|
|
13711
|
-
flex: 1,
|
|
14149
|
+
alignItems: "center",
|
|
14150
|
+
gap: 8,
|
|
13712
14151
|
minWidth: 0
|
|
13713
14152
|
};
|
|
13714
|
-
var
|
|
13715
|
-
|
|
13716
|
-
|
|
13717
|
-
|
|
13718
|
-
|
|
13719
|
-
|
|
13720
|
-
|
|
14153
|
+
var limitAmountStyle = (color, weight) => ({
|
|
14154
|
+
display: "inline-flex",
|
|
14155
|
+
alignItems: "baseline",
|
|
14156
|
+
gap: 1,
|
|
14157
|
+
color,
|
|
14158
|
+
fontSize: "1rem",
|
|
14159
|
+
fontWeight: weight
|
|
13721
14160
|
});
|
|
13722
|
-
var
|
|
13723
|
-
|
|
13724
|
-
|
|
14161
|
+
var limitDollarStyle = (isZero, color) => ({
|
|
14162
|
+
opacity: isZero ? 0.5 : 1,
|
|
14163
|
+
color
|
|
14164
|
+
});
|
|
14165
|
+
var limitInputStyle = (color, mutedColor) => ({
|
|
14166
|
+
background: "transparent",
|
|
14167
|
+
border: "none",
|
|
14168
|
+
outline: "none",
|
|
13725
14169
|
color,
|
|
13726
|
-
|
|
14170
|
+
font: "inherit",
|
|
14171
|
+
textAlign: "right",
|
|
14172
|
+
padding: 0,
|
|
14173
|
+
margin: 0,
|
|
14174
|
+
width: "auto",
|
|
14175
|
+
minWidth: "1ch",
|
|
14176
|
+
...{ fieldSizing: "content" },
|
|
14177
|
+
fontVariantNumeric: "tabular-nums",
|
|
14178
|
+
caretColor: color,
|
|
14179
|
+
["--link-limit-placeholder"]: mutedColor
|
|
13727
14180
|
});
|
|
13728
|
-
var
|
|
13729
|
-
|
|
13730
|
-
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
|
|
14181
|
+
var limitResetStyle = (borderColor, color, weight) => ({
|
|
14182
|
+
flexShrink: 0,
|
|
14183
|
+
padding: "5px 12px",
|
|
14184
|
+
borderRadius: 999,
|
|
14185
|
+
fontSize: "0.8rem",
|
|
14186
|
+
fontWeight: weight,
|
|
14187
|
+
fontFamily: "inherit",
|
|
14188
|
+
cursor: "pointer",
|
|
14189
|
+
border: `1.5px solid ${borderColor}`,
|
|
14190
|
+
background: "transparent",
|
|
14191
|
+
color
|
|
14192
|
+
});
|
|
14193
|
+
var infoCardStyle3 = (bg) => ({
|
|
13734
14194
|
display: "flex",
|
|
13735
14195
|
flexDirection: "column",
|
|
13736
14196
|
gap: 16,
|
|
13737
|
-
width: "100%"
|
|
13738
|
-
|
|
13739
|
-
|
|
14197
|
+
width: "100%",
|
|
14198
|
+
boxSizing: "border-box",
|
|
14199
|
+
padding: 16,
|
|
14200
|
+
borderRadius: 20,
|
|
14201
|
+
background: bg
|
|
14202
|
+
});
|
|
14203
|
+
var infoRowStyle2 = {
|
|
13740
14204
|
display: "flex",
|
|
13741
14205
|
alignItems: "flex-start",
|
|
13742
|
-
gap:
|
|
13743
|
-
|
|
13744
|
-
borderRadius: 16,
|
|
13745
|
-
width: "100%",
|
|
13746
|
-
boxSizing: "border-box"
|
|
14206
|
+
gap: 14,
|
|
14207
|
+
width: "100%"
|
|
13747
14208
|
};
|
|
13748
|
-
var
|
|
14209
|
+
var infoIconStyle = (color) => ({
|
|
13749
14210
|
flexShrink: 0,
|
|
13750
14211
|
width: 24,
|
|
13751
14212
|
height: 24,
|
|
@@ -13754,14 +14215,60 @@ var lockIconWrapStyle2 = (color) => ({
|
|
|
13754
14215
|
justifyContent: "center",
|
|
13755
14216
|
color
|
|
13756
14217
|
});
|
|
13757
|
-
var
|
|
13758
|
-
|
|
14218
|
+
var infoTextColStyle = {
|
|
14219
|
+
display: "flex",
|
|
14220
|
+
flexDirection: "column",
|
|
14221
|
+
gap: 2,
|
|
14222
|
+
flex: 1,
|
|
14223
|
+
minWidth: 0
|
|
14224
|
+
};
|
|
14225
|
+
var infoTitleStyle2 = (color, weight) => ({
|
|
14226
|
+
fontSize: "1rem",
|
|
14227
|
+
fontWeight: weight,
|
|
14228
|
+
lineHeight: "normal",
|
|
14229
|
+
color
|
|
14230
|
+
});
|
|
14231
|
+
var infoBodyStyle = (color, weight) => ({
|
|
14232
|
+
fontSize: "0.85rem",
|
|
14233
|
+
fontWeight: weight,
|
|
14234
|
+
lineHeight: 1.4,
|
|
14235
|
+
color
|
|
14236
|
+
});
|
|
14237
|
+
var shimmerCircleStyle = (baseColor, highlightColor) => ({
|
|
14238
|
+
width: 40,
|
|
14239
|
+
height: 40,
|
|
14240
|
+
borderRadius: "50%",
|
|
14241
|
+
flexShrink: 0,
|
|
14242
|
+
background: `linear-gradient(90deg, ${baseColor} 0%, ${highlightColor} 50%, ${baseColor} 100%)`,
|
|
14243
|
+
backgroundSize: "200% 100%",
|
|
14244
|
+
animation: "blink-link-tokens-shimmer 1.4s ease-in-out infinite"
|
|
14245
|
+
});
|
|
14246
|
+
var shimmerInfoStyle = {
|
|
14247
|
+
display: "flex",
|
|
14248
|
+
flexDirection: "column",
|
|
14249
|
+
gap: 6,
|
|
13759
14250
|
flex: 1,
|
|
13760
|
-
|
|
13761
|
-
|
|
13762
|
-
|
|
13763
|
-
|
|
14251
|
+
minWidth: 0
|
|
14252
|
+
};
|
|
14253
|
+
var shimmerLineStyle = (width, baseColor, highlightColor) => ({
|
|
14254
|
+
width,
|
|
14255
|
+
height: 12,
|
|
14256
|
+
borderRadius: 6,
|
|
14257
|
+
background: `linear-gradient(90deg, ${baseColor} 0%, ${highlightColor} 50%, ${baseColor} 100%)`,
|
|
14258
|
+
backgroundSize: "200% 100%",
|
|
14259
|
+
animation: "blink-link-tokens-shimmer 1.4s ease-in-out infinite"
|
|
14260
|
+
});
|
|
14261
|
+
var emptyStyle = (color) => ({
|
|
14262
|
+
padding: "32px 16px",
|
|
14263
|
+
textAlign: "center",
|
|
14264
|
+
color,
|
|
14265
|
+
fontSize: "0.92rem"
|
|
13764
14266
|
});
|
|
14267
|
+
var bannerSlotStyle = {
|
|
14268
|
+
display: "flex",
|
|
14269
|
+
justifyContent: "center",
|
|
14270
|
+
width: "100%"
|
|
14271
|
+
};
|
|
13765
14272
|
function DepositCompleteScreen({ amount }) {
|
|
13766
14273
|
const { tokens } = useBlinkConfig();
|
|
13767
14274
|
return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
|
|
@@ -13823,6 +14330,7 @@ function SelectDepositSourceScreen({
|
|
|
13823
14330
|
tokenOptions,
|
|
13824
14331
|
selectedTokenSymbol,
|
|
13825
14332
|
selectedChainName,
|
|
14333
|
+
balancesLoading,
|
|
13826
14334
|
selectedWalletId,
|
|
13827
14335
|
onPickToken,
|
|
13828
14336
|
useDeeplink,
|
|
@@ -13839,8 +14347,9 @@ function SelectDepositSourceScreen({
|
|
|
13839
14347
|
const [pendingMobileSelection, setPendingMobileSelection] = react.useState(null);
|
|
13840
14348
|
const [preparedAuthorization, setPreparedAuthorization] = react.useState(null);
|
|
13841
14349
|
const [preparingAuthorization, setPreparingAuthorization] = react.useState(false);
|
|
14350
|
+
const isAuthorizedOption = (opt) => !opt.status || opt.status === "AUTHORIZED";
|
|
13842
14351
|
const selectableOptions = tokenOptions.filter(
|
|
13843
|
-
(opt) => opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minTransferAmountUsd)
|
|
14352
|
+
(opt) => isAuthorizedOption(opt) || opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minTransferAmountUsd)
|
|
13844
14353
|
);
|
|
13845
14354
|
const authorized = selectableOptions.filter(
|
|
13846
14355
|
(opt) => !opt.status || opt.status === "AUTHORIZED"
|
|
@@ -13994,6 +14503,30 @@ function SelectDepositSourceScreen({
|
|
|
13994
14503
|
"data-testid": "select-deposit-source-scroll-content",
|
|
13995
14504
|
style: contentStyle12,
|
|
13996
14505
|
children: [
|
|
14506
|
+
balancesLoading && tokenOptions.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: accountSectionStyle, "data-testid": "select-source-shimmer", children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: groupCardStyle(tokens.bgRecessed), children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14507
|
+
"div",
|
|
14508
|
+
{
|
|
14509
|
+
style: { display: "flex", alignItems: "center", gap: 12, padding: 12 },
|
|
14510
|
+
children: [
|
|
14511
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14512
|
+
Shimmer,
|
|
14513
|
+
{
|
|
14514
|
+
width: 36,
|
|
14515
|
+
height: 36,
|
|
14516
|
+
borderRadius: "50%",
|
|
14517
|
+
baseColor: tokens.bgInput,
|
|
14518
|
+
highlightColor: tokens.border
|
|
14519
|
+
}
|
|
14520
|
+
),
|
|
14521
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 6, flex: 1 }, children: [
|
|
14522
|
+
/* @__PURE__ */ jsxRuntime.jsx(Shimmer, { width: 96, height: 12, baseColor: tokens.bgInput, highlightColor: tokens.border }),
|
|
14523
|
+
/* @__PURE__ */ jsxRuntime.jsx(Shimmer, { width: 56, height: 10, baseColor: tokens.bgInput, highlightColor: tokens.border })
|
|
14524
|
+
] }),
|
|
14525
|
+
/* @__PURE__ */ jsxRuntime.jsx(Shimmer, { width: 48, height: 12, baseColor: tokens.bgInput, highlightColor: tokens.border })
|
|
14526
|
+
]
|
|
14527
|
+
},
|
|
14528
|
+
`shimmer-row-${i}`
|
|
14529
|
+
)) }) }),
|
|
13997
14530
|
orderedAccounts.map((account) => {
|
|
13998
14531
|
const authRows = authorized.filter((opt) => rowMatchesSection(opt, account));
|
|
13999
14532
|
const reqRows = requiresAuth.filter((opt) => rowMatchesSection(opt, account)).sort((a, b) => Number(!!a.notSupported) - Number(!!b.notSupported));
|
|
@@ -14208,6 +14741,7 @@ function ShimmerBlock({
|
|
|
14208
14741
|
);
|
|
14209
14742
|
}
|
|
14210
14743
|
function DepositScreen({
|
|
14744
|
+
balancesLoading,
|
|
14211
14745
|
availableBalance,
|
|
14212
14746
|
remainingLimit,
|
|
14213
14747
|
initialAmount,
|
|
@@ -14261,6 +14795,10 @@ function DepositScreen({
|
|
|
14261
14795
|
const showMobileKeypad = isEntryMode && !isDesktop;
|
|
14262
14796
|
const [keypadOpen, setKeypadOpen] = react.useState(showMobileKeypad);
|
|
14263
14797
|
const [tokenPickerOpen, setTokenPickerOpen] = react.useState(false);
|
|
14798
|
+
const hasLoadedQuoteRef = react.useRef(false);
|
|
14799
|
+
if (!quoteLoading) {
|
|
14800
|
+
hasLoadedQuoteRef.current = true;
|
|
14801
|
+
}
|
|
14264
14802
|
const selectableTokenOptions = tokenOptions?.filter(
|
|
14265
14803
|
(opt) => opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minDepositFloor)
|
|
14266
14804
|
) ?? [];
|
|
@@ -14278,11 +14816,11 @@ function DepositScreen({
|
|
|
14278
14816
|
}, []);
|
|
14279
14817
|
const selectedAccount = accounts?.find((a) => a.id === selectedAccountId);
|
|
14280
14818
|
const selectedProviderName = selectedAccount?.name ?? "Wallet";
|
|
14281
|
-
const isLowBalance = availableBalance < minDepositFloor;
|
|
14282
|
-
const insufficientFunds = availableBalance < validationAmount;
|
|
14819
|
+
const isLowBalance = !balancesLoading && availableBalance < minDepositFloor;
|
|
14820
|
+
const insufficientFunds = !balancesLoading && availableBalance < validationAmount;
|
|
14283
14821
|
const needsAuthorization = selectedTokenStatus != null && selectedTokenStatus !== "AUTHORIZED" && !insufficientFunds && !isLowBalance;
|
|
14284
14822
|
const exceedsLimit = remainingLimit != null && validationAmount > remainingLimit && !isLowBalance && !needsAuthorization;
|
|
14285
|
-
const canDeposit = validationAmount >= minDepositFloor && !exceedsLimit && !isLowBalance && !insufficientFunds && !needsAuthorization && !processing;
|
|
14823
|
+
const canDeposit = validationAmount >= minDepositFloor && !balancesLoading && !exceedsLimit && !isLowBalance && !insufficientFunds && !needsAuthorization && !processing;
|
|
14286
14824
|
const hasAccountPill = !!accounts && accounts.length > 0;
|
|
14287
14825
|
const pillClickable = canOpenInlineSheet || !!onSelectToken;
|
|
14288
14826
|
const formattedBalance = selectedTokenSymbol != null ? `$${formatUsdTwoDecimals2(availableBalance)}` : void 0;
|
|
@@ -14305,6 +14843,7 @@ function DepositScreen({
|
|
|
14305
14843
|
accounts: depositSourceAccounts,
|
|
14306
14844
|
selectedAccountId: selectedAccountId ?? null,
|
|
14307
14845
|
tokenOptions: selectableTokenOptions,
|
|
14846
|
+
balancesLoading,
|
|
14308
14847
|
selectedTokenSymbol,
|
|
14309
14848
|
selectedChainName,
|
|
14310
14849
|
selectedWalletId: selectedWalletId ?? null,
|
|
@@ -14326,7 +14865,7 @@ function DepositScreen({
|
|
|
14326
14865
|
const mobileEntryWithKeypad = isEntryMode && !isDesktop && keypadOpen;
|
|
14327
14866
|
const showLiveMobileHero = isEntryMode && !isDesktop && keypadOpen;
|
|
14328
14867
|
const showDesktopInputHero = isEntryMode && isDesktop;
|
|
14329
|
-
const
|
|
14868
|
+
const showFeeRow = validationAmount > 0;
|
|
14330
14869
|
let primaryButton;
|
|
14331
14870
|
if (mobileEntryWithKeypad) {
|
|
14332
14871
|
const canConfirmEntry = canContinue(amountEntryValue ?? "");
|
|
@@ -14456,7 +14995,32 @@ function DepositScreen({
|
|
|
14456
14995
|
"aria-label": tokenAriaLabel,
|
|
14457
14996
|
"aria-expanded": canOpenInlineSheet ? tokenPickerOpen : void 0,
|
|
14458
14997
|
"aria-haspopup": canOpenInlineSheet ? "listbox" : void 0,
|
|
14459
|
-
children:
|
|
14998
|
+
children: balancesLoading ? (
|
|
14999
|
+
// Until balances merge in we don't know the selected token or its
|
|
15000
|
+
// balance, so the whole pill (icon + name + balance) is a shimmer
|
|
15001
|
+
// placeholder rather than showing the account name as a default.
|
|
15002
|
+
// Render through SourcePill so the placeholder inherits the exact
|
|
15003
|
+
// same container (fill, radius, padding, chevron) as the loaded
|
|
15004
|
+
// pill and does not change size when balances arrive.
|
|
15005
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
15006
|
+
"span",
|
|
15007
|
+
{
|
|
15008
|
+
"data-testid": "deposit-pill-shimmer",
|
|
15009
|
+
"aria-label": "Loading balance",
|
|
15010
|
+
"aria-busy": "true",
|
|
15011
|
+
style: { display: "inline-flex" },
|
|
15012
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15013
|
+
SourcePill,
|
|
15014
|
+
{
|
|
15015
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { width: 28, height: 28, borderRadius: "50%", baseColor: tokens.bgInput, highlightColor: tokens.border }),
|
|
15016
|
+
nameSlot: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { width: 72, height: 12, baseColor: tokens.bgInput, highlightColor: tokens.border }),
|
|
15017
|
+
balanceSlot: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { width: 48, height: 12, baseColor: tokens.bgInput, highlightColor: tokens.border }),
|
|
15018
|
+
showChevron: pillClickable
|
|
15019
|
+
}
|
|
15020
|
+
)
|
|
15021
|
+
}
|
|
15022
|
+
)
|
|
15023
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
14460
15024
|
SourcePill,
|
|
14461
15025
|
{
|
|
14462
15026
|
icon: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -14476,7 +15040,7 @@ function DepositScreen({
|
|
|
14476
15040
|
)
|
|
14477
15041
|
}
|
|
14478
15042
|
),
|
|
14479
|
-
|
|
15043
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": "deposit-fee-row", style: redesignFeeRowStyle, children: showFeeRow && (quoteLoading && !hasLoadedQuoteRef.current ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
14480
15044
|
"div",
|
|
14481
15045
|
{
|
|
14482
15046
|
"data-testid": "deposit-fee-shimmer",
|
|
@@ -14494,7 +15058,7 @@ function DepositScreen({
|
|
|
14494
15058
|
}
|
|
14495
15059
|
)
|
|
14496
15060
|
}
|
|
14497
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" }) })
|
|
15061
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" })) })
|
|
14498
15062
|
] }),
|
|
14499
15063
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: bannerSlotStyle2, children: mobileEntryWithKeypad ? null : showLowFunds ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
14500
15064
|
NotificationBanner,
|
|
@@ -15871,7 +16435,7 @@ function OpenWalletScreen({
|
|
|
15871
16435
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15872
16436
|
ScreenLayout,
|
|
15873
16437
|
{
|
|
15874
|
-
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style:
|
|
16438
|
+
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: footerStackStyle4, children: [
|
|
15875
16439
|
error && /* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: error }),
|
|
15876
16440
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
15877
16441
|
SecondaryButton,
|
|
@@ -15918,7 +16482,7 @@ function OpenWalletScreen({
|
|
|
15918
16482
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15919
16483
|
ScreenLayout,
|
|
15920
16484
|
{
|
|
15921
|
-
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style:
|
|
16485
|
+
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: footerStackStyle4, children: [
|
|
15922
16486
|
error && /* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: error }),
|
|
15923
16487
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
15924
16488
|
SecondaryButton,
|
|
@@ -16002,7 +16566,7 @@ var primaryClusterStyle = {
|
|
|
16002
16566
|
textAlign: "center",
|
|
16003
16567
|
gap: 12
|
|
16004
16568
|
};
|
|
16005
|
-
var
|
|
16569
|
+
var footerStackStyle4 = {
|
|
16006
16570
|
display: "flex",
|
|
16007
16571
|
flexDirection: "column",
|
|
16008
16572
|
gap: 8
|
|
@@ -16130,6 +16694,12 @@ function ApprovingInWalletScreen({
|
|
|
16130
16694
|
chainName,
|
|
16131
16695
|
step,
|
|
16132
16696
|
complete,
|
|
16697
|
+
signing,
|
|
16698
|
+
spendingLimitLabel,
|
|
16699
|
+
destinationAddress,
|
|
16700
|
+
tokenLogoUri,
|
|
16701
|
+
awaitingApproval,
|
|
16702
|
+
onApprove,
|
|
16133
16703
|
error,
|
|
16134
16704
|
onRetry,
|
|
16135
16705
|
onBack,
|
|
@@ -16139,10 +16709,11 @@ function ApprovingInWalletScreen({
|
|
|
16139
16709
|
onOpenWallet
|
|
16140
16710
|
}) {
|
|
16141
16711
|
const { tokens: t } = useBlinkConfig();
|
|
16142
|
-
const showDelayedRetry = useDelayedRetry(error == null && onRetry != null);
|
|
16712
|
+
const showDelayedRetry = useDelayedRetry(!awaitingApproval && error == null && onRetry != null);
|
|
16143
16713
|
const retryVisible = onRetry != null && (error != null || showDelayedRetry);
|
|
16144
16714
|
const token = tokenSymbol ?? "token";
|
|
16145
|
-
const
|
|
16715
|
+
const chain = chainName ?? "chain";
|
|
16716
|
+
const steps = buildSteps(step, tokenSymbol, complete, signing);
|
|
16146
16717
|
const foregroundNavigation = foregroundDeeplink ? classifyWalletDeeplinkNavigation(foregroundDeeplink) : null;
|
|
16147
16718
|
const foregroundWithJs = foregroundNavigation ? shouldOpenWithJavaScript(foregroundNavigation) : false;
|
|
16148
16719
|
const showOpenWalletButton = !!foregroundDeeplink && onOpenWallet != null;
|
|
@@ -16150,11 +16721,7 @@ function ApprovingInWalletScreen({
|
|
|
16150
16721
|
ScreenLayout,
|
|
16151
16722
|
{
|
|
16152
16723
|
scrollableBody: false,
|
|
16153
|
-
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style:
|
|
16154
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: lockBannerStyle3, children: [
|
|
16155
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: lockIconWrapStyle3(t.text), children: /* @__PURE__ */ jsxRuntime.jsx(LockIcon4, {}) }),
|
|
16156
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: lockBannerTextStyle3(t.text), children: "Your passkey is required each time you deposit. Funds cannot move without your approval." })
|
|
16157
|
-
] }),
|
|
16724
|
+
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: footerStackStyle5, children: [
|
|
16158
16725
|
showOpenWalletButton && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
16159
16726
|
SecondaryButton,
|
|
16160
16727
|
{
|
|
@@ -16171,7 +16738,7 @@ function ApprovingInWalletScreen({
|
|
|
16171
16738
|
retryVisible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: retryStackStyle, children: [
|
|
16172
16739
|
error && /* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: error }),
|
|
16173
16740
|
/* @__PURE__ */ jsxRuntime.jsx(OutlineButton, { onClick: onRetry, children: "Retry" })
|
|
16174
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { spinnerOnly: true })
|
|
16741
|
+
] }) : awaitingApproval ? /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onApprove, children: "Approve" }) : /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { spinnerOnly: true })
|
|
16175
16742
|
] }),
|
|
16176
16743
|
children: [
|
|
16177
16744
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -16183,7 +16750,39 @@ function ApprovingInWalletScreen({
|
|
|
16183
16750
|
}
|
|
16184
16751
|
),
|
|
16185
16752
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle16, children: [
|
|
16186
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle13(t.text), children:
|
|
16753
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle13(t.text, t.fontWeightBold), children: "Approve in wallet" }),
|
|
16754
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: transferCardStyle(t.bgCardTranslucent, t.radiusLg), children: [
|
|
16755
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16756
|
+
AddressRow,
|
|
16757
|
+
{
|
|
16758
|
+
circle: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16759
|
+
LogoCircle,
|
|
16760
|
+
{
|
|
16761
|
+
src: tokenLogoUri ?? TOKEN_LOGOS[token] ?? void 0,
|
|
16762
|
+
fallback: token.slice(0, 1).toUpperCase(),
|
|
16763
|
+
size: 40
|
|
16764
|
+
}
|
|
16765
|
+
),
|
|
16766
|
+
title: `${token} on ${chain}`,
|
|
16767
|
+
subtitle: spendingLimitLabel
|
|
16768
|
+
}
|
|
16769
|
+
),
|
|
16770
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: arrowRowStyle, children: /* @__PURE__ */ jsxRuntime.jsx(ArrowDownIcon, { color: t.textMuted }) }),
|
|
16771
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16772
|
+
AddressRow,
|
|
16773
|
+
{
|
|
16774
|
+
circle: /* @__PURE__ */ jsxRuntime.jsx("span", { style: passkeyCircleStyle(t.accent, t.highlight), children: /* @__PURE__ */ jsxRuntime.jsx(KeyIcon, { size: 22 }) }),
|
|
16775
|
+
title: "Your Blink Passkey",
|
|
16776
|
+
address: destinationAddress,
|
|
16777
|
+
addressLoading: destinationAddress == null
|
|
16778
|
+
}
|
|
16779
|
+
)
|
|
16780
|
+
] }),
|
|
16781
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: dividerRowStyle3, children: [
|
|
16782
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: dividerLineStyle(t.textMuted) }),
|
|
16783
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: dividerLabelStyle3(t.textMuted, t.fontWeightSemibold), children: "Continue in wallet" }),
|
|
16784
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: dividerLineStyle(t.textMuted) })
|
|
16785
|
+
] }),
|
|
16187
16786
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: cardStyle2(t.bgRecessed, t.radiusLg), children: [
|
|
16188
16787
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: SPIN_KEYFRAMES_CSS2 }),
|
|
16189
16788
|
steps.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(ChecklistRow, { step: s }, i))
|
|
@@ -16193,6 +16792,32 @@ function ApprovingInWalletScreen({
|
|
|
16193
16792
|
}
|
|
16194
16793
|
);
|
|
16195
16794
|
}
|
|
16795
|
+
function AddressRow({
|
|
16796
|
+
circle,
|
|
16797
|
+
title,
|
|
16798
|
+
address,
|
|
16799
|
+
subtitle,
|
|
16800
|
+
addressLoading
|
|
16801
|
+
}) {
|
|
16802
|
+
const { tokens: t } = useBlinkConfig();
|
|
16803
|
+
const sub = subtitle ?? (address ? truncateMiddle(address) : null);
|
|
16804
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: addressRowStyle, children: [
|
|
16805
|
+
circle,
|
|
16806
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: addressTextColStyle, children: [
|
|
16807
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: addressTitleStyle(t.text, t.fontWeightSemibold), children: title }),
|
|
16808
|
+
(sub || addressLoading) && /* @__PURE__ */ jsxRuntime.jsx("span", { style: addressSubSlotStyle, children: sub ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: addressSubStyle(t.textMuted, t.fontWeightRegular), children: sub }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
16809
|
+
Shimmer,
|
|
16810
|
+
{
|
|
16811
|
+
width: 132,
|
|
16812
|
+
height: 12,
|
|
16813
|
+
borderRadius: 4,
|
|
16814
|
+
baseColor: t.bgInput,
|
|
16815
|
+
highlightColor: t.border
|
|
16816
|
+
}
|
|
16817
|
+
) })
|
|
16818
|
+
] })
|
|
16819
|
+
] });
|
|
16820
|
+
}
|
|
16196
16821
|
function ChecklistRow({ step }) {
|
|
16197
16822
|
const { tokens: t } = useBlinkConfig();
|
|
16198
16823
|
const isComplete = step.status === "complete";
|
|
@@ -16205,7 +16830,7 @@ function ChecklistRow({ step }) {
|
|
|
16205
16830
|
fill: t.accentText
|
|
16206
16831
|
}
|
|
16207
16832
|
) }) }) : isActive ? /* @__PURE__ */ jsxRuntime.jsx(RowSpinner, { color: t.accent }) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: pendingBadgeStyle(t.bgRecessed) }),
|
|
16208
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: labelStyle7(isComplete || isActive ? t.text : t.textMuted), children: step.label })
|
|
16833
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: labelStyle7(isComplete || isActive ? t.text : t.textMuted, t.fontWeightRegular), children: step.label })
|
|
16209
16834
|
] });
|
|
16210
16835
|
}
|
|
16211
16836
|
function RowSpinner({ color }) {
|
|
@@ -16240,22 +16865,28 @@ function RowSpinner({ color }) {
|
|
|
16240
16865
|
}
|
|
16241
16866
|
);
|
|
16242
16867
|
}
|
|
16243
|
-
function
|
|
16868
|
+
function ArrowDownIcon({ color }) {
|
|
16869
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", style: { color }, children: [
|
|
16870
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5v14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
16871
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 13l6 6 6-6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
16872
|
+
] });
|
|
16873
|
+
}
|
|
16874
|
+
function buildSteps(step, tokenSymbol, complete, signing) {
|
|
16244
16875
|
const token = tokenSymbol ?? "token";
|
|
16245
|
-
const
|
|
16246
|
-
const
|
|
16876
|
+
const passkeyLabel = "Assign spending right to your passkey";
|
|
16877
|
+
const activeStatus = signing ? "active" : "pending";
|
|
16247
16878
|
if (step === "spl") {
|
|
16248
|
-
return [{ label: passkeyLabel, status: complete ? "complete" :
|
|
16879
|
+
return [{ label: passkeyLabel, status: complete ? "complete" : activeStatus }];
|
|
16249
16880
|
}
|
|
16250
|
-
const approveLabel = `Approve ${token}
|
|
16881
|
+
const approveLabel = `Approve ${token} for spending via Permit2`;
|
|
16251
16882
|
return [
|
|
16252
16883
|
{
|
|
16253
16884
|
label: approveLabel,
|
|
16254
|
-
status: complete || step !== "approve" ? "complete" :
|
|
16885
|
+
status: complete || step !== "approve" ? "complete" : activeStatus
|
|
16255
16886
|
},
|
|
16256
16887
|
{
|
|
16257
16888
|
label: passkeyLabel,
|
|
16258
|
-
status: complete ? "complete" : step === "approve" ? "pending" :
|
|
16889
|
+
status: complete ? "complete" : step === "approve" ? "pending" : activeStatus
|
|
16259
16890
|
}
|
|
16260
16891
|
];
|
|
16261
16892
|
}
|
|
@@ -16271,18 +16902,6 @@ function useDelayedRetry(enabled) {
|
|
|
16271
16902
|
}, [enabled]);
|
|
16272
16903
|
return visible;
|
|
16273
16904
|
}
|
|
16274
|
-
function LockIcon4() {
|
|
16275
|
-
return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16276
|
-
"path",
|
|
16277
|
-
{
|
|
16278
|
-
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",
|
|
16279
|
-
stroke: "currentColor",
|
|
16280
|
-
strokeWidth: "2",
|
|
16281
|
-
strokeLinecap: "round",
|
|
16282
|
-
strokeLinejoin: "round"
|
|
16283
|
-
}
|
|
16284
|
-
) });
|
|
16285
|
-
}
|
|
16286
16905
|
var wordmarkImgStyle5 = {
|
|
16287
16906
|
height: 22,
|
|
16288
16907
|
width: "auto",
|
|
@@ -16300,15 +16919,84 @@ var contentStyle16 = {
|
|
|
16300
16919
|
minHeight: 0,
|
|
16301
16920
|
width: "100%"
|
|
16302
16921
|
};
|
|
16303
|
-
var headingStyle13 = (color) => ({
|
|
16922
|
+
var headingStyle13 = (color, fontWeight) => ({
|
|
16304
16923
|
fontSize: 24,
|
|
16305
|
-
fontWeight
|
|
16924
|
+
fontWeight,
|
|
16306
16925
|
lineHeight: "normal",
|
|
16307
16926
|
letterSpacing: 0,
|
|
16308
16927
|
color,
|
|
16309
|
-
margin: 0,
|
|
16928
|
+
margin: "8px 0 8px",
|
|
16310
16929
|
textAlign: "center"
|
|
16311
16930
|
});
|
|
16931
|
+
var transferCardStyle = (bg, radius) => ({
|
|
16932
|
+
background: bg,
|
|
16933
|
+
borderRadius: radius,
|
|
16934
|
+
padding: "20px 16px",
|
|
16935
|
+
width: "100%",
|
|
16936
|
+
boxSizing: "border-box",
|
|
16937
|
+
display: "flex",
|
|
16938
|
+
flex: 1,
|
|
16939
|
+
flexDirection: "column",
|
|
16940
|
+
justifyContent: "space-between",
|
|
16941
|
+
gap: 12
|
|
16942
|
+
});
|
|
16943
|
+
var dividerRowStyle3 = {
|
|
16944
|
+
display: "flex",
|
|
16945
|
+
alignItems: "center",
|
|
16946
|
+
gap: 12,
|
|
16947
|
+
width: "100%"
|
|
16948
|
+
};
|
|
16949
|
+
var dividerLineStyle = (color) => ({
|
|
16950
|
+
flex: 1,
|
|
16951
|
+
height: 1,
|
|
16952
|
+
background: color
|
|
16953
|
+
});
|
|
16954
|
+
var dividerLabelStyle3 = (color, fontWeight) => ({
|
|
16955
|
+
fontSize: 11,
|
|
16956
|
+
fontWeight,
|
|
16957
|
+
letterSpacing: "0.08em",
|
|
16958
|
+
textTransform: "uppercase",
|
|
16959
|
+
color,
|
|
16960
|
+
whiteSpace: "nowrap"
|
|
16961
|
+
});
|
|
16962
|
+
var addressRowStyle = {
|
|
16963
|
+
display: "flex",
|
|
16964
|
+
alignItems: "center",
|
|
16965
|
+
gap: 12,
|
|
16966
|
+
width: "100%"
|
|
16967
|
+
};
|
|
16968
|
+
var addressTextColStyle = {
|
|
16969
|
+
display: "flex",
|
|
16970
|
+
flexDirection: "column",
|
|
16971
|
+
gap: 2,
|
|
16972
|
+
minWidth: 0
|
|
16973
|
+
};
|
|
16974
|
+
var addressTitleStyle = (color, fontWeight) => ({
|
|
16975
|
+
fontSize: 16,
|
|
16976
|
+
fontWeight,
|
|
16977
|
+
lineHeight: 1.25,
|
|
16978
|
+
color
|
|
16979
|
+
});
|
|
16980
|
+
var addressSubSlotStyle = {
|
|
16981
|
+
display: "flex",
|
|
16982
|
+
alignItems: "center",
|
|
16983
|
+
minHeight: 18
|
|
16984
|
+
};
|
|
16985
|
+
var addressSubStyle = (color, fontWeight) => ({
|
|
16986
|
+
fontSize: 14,
|
|
16987
|
+
fontWeight,
|
|
16988
|
+
lineHeight: 1.25,
|
|
16989
|
+
color,
|
|
16990
|
+
overflow: "hidden",
|
|
16991
|
+
textOverflow: "ellipsis",
|
|
16992
|
+
whiteSpace: "nowrap"
|
|
16993
|
+
});
|
|
16994
|
+
var arrowRowStyle = {
|
|
16995
|
+
display: "flex",
|
|
16996
|
+
alignItems: "center",
|
|
16997
|
+
height: 20,
|
|
16998
|
+
paddingLeft: 10
|
|
16999
|
+
};
|
|
16312
17000
|
var cardStyle2 = (bg, radius) => ({
|
|
16313
17001
|
background: bg,
|
|
16314
17002
|
borderRadius: radius,
|
|
@@ -16316,7 +17004,9 @@ var cardStyle2 = (bg, radius) => ({
|
|
|
16316
17004
|
width: "100%",
|
|
16317
17005
|
boxSizing: "border-box",
|
|
16318
17006
|
display: "flex",
|
|
17007
|
+
flex: 1,
|
|
16319
17008
|
flexDirection: "column",
|
|
17009
|
+
justifyContent: "center",
|
|
16320
17010
|
gap: 4
|
|
16321
17011
|
});
|
|
16322
17012
|
var rowStyle6 = {
|
|
@@ -16324,10 +17014,25 @@ var rowStyle6 = {
|
|
|
16324
17014
|
alignItems: "center",
|
|
16325
17015
|
gap: 16,
|
|
16326
17016
|
padding: "12px 16px 12px 12px",
|
|
17017
|
+
// Reserve a stable height (two label lines + vertical padding) so swapping the
|
|
17018
|
+
// leading badge between the pending circle, the spinner, and the completion
|
|
17019
|
+
// checkmark — and the muted→active label restyle — never reflows the card.
|
|
17020
|
+
minHeight: 64,
|
|
16327
17021
|
borderRadius: 16,
|
|
16328
17022
|
width: "100%",
|
|
16329
17023
|
boxSizing: "border-box"
|
|
16330
17024
|
};
|
|
17025
|
+
var passkeyCircleStyle = (bg, iconColor) => ({
|
|
17026
|
+
width: 40,
|
|
17027
|
+
height: 40,
|
|
17028
|
+
borderRadius: "50%",
|
|
17029
|
+
background: bg,
|
|
17030
|
+
color: iconColor,
|
|
17031
|
+
display: "inline-flex",
|
|
17032
|
+
alignItems: "center",
|
|
17033
|
+
justifyContent: "center",
|
|
17034
|
+
flexShrink: 0
|
|
17035
|
+
});
|
|
16331
17036
|
var completeBadgeStyle = (color) => ({
|
|
16332
17037
|
width: 24,
|
|
16333
17038
|
height: 24,
|
|
@@ -16345,13 +17050,13 @@ var pendingBadgeStyle = (color) => ({
|
|
|
16345
17050
|
background: color,
|
|
16346
17051
|
flexShrink: 0
|
|
16347
17052
|
});
|
|
16348
|
-
var labelStyle7 = (color) => ({
|
|
17053
|
+
var labelStyle7 = (color, fontWeight) => ({
|
|
16349
17054
|
fontSize: "1rem",
|
|
16350
|
-
fontWeight
|
|
17055
|
+
fontWeight,
|
|
16351
17056
|
lineHeight: 1.25,
|
|
16352
17057
|
color
|
|
16353
17058
|
});
|
|
16354
|
-
var
|
|
17059
|
+
var footerStackStyle5 = {
|
|
16355
17060
|
display: "flex",
|
|
16356
17061
|
flexDirection: "column",
|
|
16357
17062
|
gap: 16,
|
|
@@ -16363,32 +17068,6 @@ var retryStackStyle = {
|
|
|
16363
17068
|
gap: 8,
|
|
16364
17069
|
width: "100%"
|
|
16365
17070
|
};
|
|
16366
|
-
var lockBannerStyle3 = {
|
|
16367
|
-
display: "flex",
|
|
16368
|
-
alignItems: "flex-start",
|
|
16369
|
-
gap: 16,
|
|
16370
|
-
padding: "12px 16px 12px 12px",
|
|
16371
|
-
borderRadius: 16,
|
|
16372
|
-
width: "100%",
|
|
16373
|
-
boxSizing: "border-box"
|
|
16374
|
-
};
|
|
16375
|
-
var lockIconWrapStyle3 = (color) => ({
|
|
16376
|
-
flexShrink: 0,
|
|
16377
|
-
width: 24,
|
|
16378
|
-
height: 24,
|
|
16379
|
-
display: "inline-flex",
|
|
16380
|
-
alignItems: "center",
|
|
16381
|
-
justifyContent: "center",
|
|
16382
|
-
color
|
|
16383
|
-
});
|
|
16384
|
-
var lockBannerTextStyle3 = (color) => ({
|
|
16385
|
-
margin: 0,
|
|
16386
|
-
flex: 1,
|
|
16387
|
-
fontSize: 12,
|
|
16388
|
-
fontWeight: 500,
|
|
16389
|
-
lineHeight: "normal",
|
|
16390
|
-
color
|
|
16391
|
-
});
|
|
16392
17071
|
function ConfirmSignScreen({
|
|
16393
17072
|
walletName,
|
|
16394
17073
|
chainFamily,
|
|
@@ -16514,7 +17193,7 @@ function TokenPickerScreen({
|
|
|
16514
17193
|
}
|
|
16515
17194
|
const entries2 = [];
|
|
16516
17195
|
for (const wallet of account.wallets) {
|
|
16517
|
-
for (const source of wallet.sources) {
|
|
17196
|
+
for (const source of wallet.sources ?? []) {
|
|
16518
17197
|
const visibleBalance = source.balance.available.amount;
|
|
16519
17198
|
if (!isSelectableDepositSourceAmountUsd(visibleBalance, minDepositFloor)) continue;
|
|
16520
17199
|
entries2.push({
|
|
@@ -17355,22 +18034,45 @@ function buildOpenWalletScreenProps({
|
|
|
17355
18034
|
function isApprovingInWalletAction(actionType) {
|
|
17356
18035
|
return actionType === "APPROVE_PERMIT2" || actionType === "SIGN_PERMIT2" || actionType === "APPROVE_SPL";
|
|
17357
18036
|
}
|
|
18037
|
+
function formatUsdTwoDecimals3(value) {
|
|
18038
|
+
return (Number.isFinite(value) ? value : 0).toLocaleString("en-US", {
|
|
18039
|
+
minimumFractionDigits: 2,
|
|
18040
|
+
maximumFractionDigits: 2
|
|
18041
|
+
});
|
|
18042
|
+
}
|
|
18043
|
+
function readMetadataString(metadata, key) {
|
|
18044
|
+
const value = metadata?.[key];
|
|
18045
|
+
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
18046
|
+
}
|
|
17358
18047
|
function buildApprovingInWalletScreenProps({
|
|
17359
18048
|
flow,
|
|
17360
18049
|
remote,
|
|
18050
|
+
derived,
|
|
17361
18051
|
handlers
|
|
17362
18052
|
}) {
|
|
17363
18053
|
const { state } = flow;
|
|
17364
18054
|
const setupToken = state.setupDepositToken;
|
|
17365
|
-
const
|
|
18055
|
+
const currentAction = remote.authExecutorCurrentAction;
|
|
18056
|
+
const actionType = currentAction?.type ?? null;
|
|
18057
|
+
const limit = state.setupSpendingLimit;
|
|
18058
|
+
const spendingLimitLabel = limit == null ? null : "unlimited" in limit ? "Unlimited lifetime spending limit" : `$${formatUsdTwoDecimals3(limit.usd)} lifetime spending limit`;
|
|
18059
|
+
const destinationAddress = remote.authApprovalSmartAccountAddress ?? remote.authExecutorApprovalDestinationAddress ?? readMetadataString(currentAction?.metadata, "smartAccountAddress") ?? readMetadataString(currentAction?.metadata, "ownerPubkey");
|
|
17366
18060
|
const isSolana = setupToken != null && state.chains.find((c) => c.name === setupToken.chainName)?.chainFamily === "svm";
|
|
17367
18061
|
const step = actionType === "SIGN_PERMIT2" ? "sign" : actionType === "APPROVE_SPL" ? "spl" : actionType == null && isSolana ? "spl" : "approve";
|
|
17368
18062
|
const complete = remote.authExecutorCompleted && actionType == null && !remote.authExecutorExecuting;
|
|
18063
|
+
const awaitingApproval = remote.authExecutorAwaitingApproval ?? false;
|
|
18064
|
+
const signing = isApprovingInWalletAction(actionType) && !awaitingApproval && !complete;
|
|
17369
18065
|
return {
|
|
17370
18066
|
tokenSymbol: setupToken?.symbol ?? null,
|
|
17371
18067
|
chainName: setupToken?.chainName ?? null,
|
|
17372
18068
|
step,
|
|
17373
18069
|
complete,
|
|
18070
|
+
signing,
|
|
18071
|
+
spendingLimitLabel,
|
|
18072
|
+
destinationAddress,
|
|
18073
|
+
tokenLogoUri: derived.selectedSource?.token.logoURI ?? null,
|
|
18074
|
+
awaitingApproval,
|
|
18075
|
+
onApprove: handlers.onApprove,
|
|
17374
18076
|
error: flow.state.error || remote.authExecutorError,
|
|
17375
18077
|
onRetry: handlers.onRetryAuthorization,
|
|
17376
18078
|
onLogout: flow.isDesktop ? handlers.onLogout : void 0,
|
|
@@ -17389,7 +18091,7 @@ function buildLinkTokensScreenProps({
|
|
|
17389
18091
|
handlers
|
|
17390
18092
|
}) {
|
|
17391
18093
|
const sourceOptions = remote.pendingSelectSource?.metadata?.options ?? [];
|
|
17392
|
-
const
|
|
18094
|
+
const supportedRows = derived.selectSourceChoices.flatMap(
|
|
17393
18095
|
(chain) => chain.tokens.map((t) => {
|
|
17394
18096
|
const rawOption = sourceOptions.find(
|
|
17395
18097
|
(option) => option.chainName === chain.chainName && option.tokenSymbol === t.tokenSymbol
|
|
@@ -17409,6 +18111,10 @@ function buildLinkTokensScreenProps({
|
|
|
17409
18111
|
};
|
|
17410
18112
|
})
|
|
17411
18113
|
);
|
|
18114
|
+
const rowContexts = [
|
|
18115
|
+
...supportedRows.filter((row) => isVisibleUsdAmountAtTwoDecimals(row.balanceUsd)),
|
|
18116
|
+
...supportedRows.filter((row) => !isVisibleUsdAmountAtTwoDecimals(row.balanceUsd))
|
|
18117
|
+
];
|
|
17412
18118
|
rowContexts.push(
|
|
17413
18119
|
...derived.selectSourceUnsupportedChoices.flatMap(
|
|
17414
18120
|
(chain) => chain.tokens.map((t) => ({
|
|
@@ -17430,6 +18136,17 @@ function buildLinkTokensScreenProps({
|
|
|
17430
18136
|
balanceLabel: `${formatNativeAmount(native.amount)} ${native.tokenSymbol}`
|
|
17431
18137
|
}))
|
|
17432
18138
|
);
|
|
18139
|
+
const selectedTokenSymbol = flow.state.setupDepositToken?.symbol ?? derived.selectSourceRecommended?.tokenSymbol;
|
|
18140
|
+
const selectedChainName = flow.state.setupDepositToken?.chainName ?? derived.selectSourceRecommended?.chainName;
|
|
18141
|
+
if (selectedTokenSymbol && selectedChainName) {
|
|
18142
|
+
const match = rowContexts.findIndex(
|
|
18143
|
+
(row) => row.symbol === selectedTokenSymbol && row.chainName === selectedChainName
|
|
18144
|
+
);
|
|
18145
|
+
if (match >= 0 && !rowContexts[match].notSupported) {
|
|
18146
|
+
const [pinned] = rowContexts.splice(match, 1);
|
|
18147
|
+
rowContexts.unshift(pinned);
|
|
18148
|
+
}
|
|
18149
|
+
}
|
|
17433
18150
|
const entries2 = rowContexts.map(
|
|
17434
18151
|
({ symbol, chainName, balanceUsd, notSupported, tokenLogoUri, balanceLabel }) => ({
|
|
17435
18152
|
tokenSymbol: symbol,
|
|
@@ -17442,8 +18159,6 @@ function buildLinkTokensScreenProps({
|
|
|
17442
18159
|
})
|
|
17443
18160
|
);
|
|
17444
18161
|
const firstSupportedIndex = rowContexts.findIndex((row) => !row.notSupported);
|
|
17445
|
-
const selectedTokenSymbol = flow.state.setupDepositToken?.symbol ?? derived.selectSourceRecommended?.tokenSymbol;
|
|
17446
|
-
const selectedChainName = flow.state.setupDepositToken?.chainName ?? derived.selectSourceRecommended?.chainName;
|
|
17447
18162
|
const selectedIndex = (() => {
|
|
17448
18163
|
if (!selectedTokenSymbol || !selectedChainName) {
|
|
17449
18164
|
return firstSupportedIndex;
|
|
@@ -17535,9 +18250,10 @@ function buildDepositScreenProps({
|
|
|
17535
18250
|
const registryChainIds = derived.walletConnectChainIdsByAccount[account.id] ?? null;
|
|
17536
18251
|
const approvedChainIds = account.walletConnect?.approvedChainIds ?? null;
|
|
17537
18252
|
for (const wallet of account.wallets) {
|
|
17538
|
-
for (const source of wallet.sources) {
|
|
18253
|
+
for (const source of wallet.sources ?? []) {
|
|
17539
18254
|
const balance = source.balance.available.amount;
|
|
17540
|
-
|
|
18255
|
+
const isAuthorized = source.token.status === "AUTHORIZED";
|
|
18256
|
+
if (!isAuthorized && !isSelectableDepositSourceAmountUsd(balance, minDepositFloor)) continue;
|
|
17541
18257
|
const chain = flow.state.chains.find((c) => c.name === wallet.chain.name);
|
|
17542
18258
|
const requiresAuth = source.token.status != null && source.token.status !== "AUTHORIZED";
|
|
17543
18259
|
const notSupported = requiresAuth && chain?.commonId != null && registryChainIds != null && !registryChainIds.includes(chain.commonId) && !(approvedChainIds?.includes(chain.commonId) ?? false);
|
|
@@ -17559,7 +18275,10 @@ function buildDepositScreenProps({
|
|
|
17559
18275
|
}
|
|
17560
18276
|
return {
|
|
17561
18277
|
merchantName,
|
|
17562
|
-
|
|
18278
|
+
// Wallet balances arrive after the (balance-free) accounts load; while they
|
|
18279
|
+
// do, the source pill / token rows shimmer instead of showing $0.00.
|
|
18280
|
+
balancesLoading: state.balancesLoading,
|
|
18281
|
+
availableBalance: selectedSource ? selectedSource.balance.available.amount : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + (w.balance?.available.amount ?? 0), 0) : maxSourceBalance,
|
|
17563
18282
|
remainingLimit: selectedSource != null ? selectedSource.remainingAllowance ?? null : selectedAccount?.remainingAllowance ?? null,
|
|
17564
18283
|
tokenCount,
|
|
17565
18284
|
initialAmount: parsedAmt,
|
|
@@ -17750,6 +18469,28 @@ function StepRendererContent({
|
|
|
17750
18469
|
screen
|
|
17751
18470
|
}) {
|
|
17752
18471
|
const input = { flow, remote, derived, forms, handlers };
|
|
18472
|
+
const s = flow.state;
|
|
18473
|
+
react.useEffect(() => {
|
|
18474
|
+
if (screen !== "loading") return;
|
|
18475
|
+
appendDebug("warn", "stuck on loading shimmer", {
|
|
18476
|
+
phaseStep: s.phase.step,
|
|
18477
|
+
privyReady: s.privyReady,
|
|
18478
|
+
privyAuthenticated: s.privyAuthenticated,
|
|
18479
|
+
activeCredentialId: s.activeCredentialId,
|
|
18480
|
+
passkeyConfigLoaded: s.passkeyConfigLoaded,
|
|
18481
|
+
loadingData: s.loadingData,
|
|
18482
|
+
initialDataLoaded: s.initialDataLoaded,
|
|
18483
|
+
loginRequested: s.loginRequested,
|
|
18484
|
+
enableFullWidget: s.enableFullWidget,
|
|
18485
|
+
authenticatedOnOpen: s.authenticatedOnOpen,
|
|
18486
|
+
welcomeBackAcknowledged: s.welcomeBackAcknowledged,
|
|
18487
|
+
mobileFlow: s.mobileFlow,
|
|
18488
|
+
error: s.error,
|
|
18489
|
+
accountsCount: s.accounts.length,
|
|
18490
|
+
hasActiveWallet: s.accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE")),
|
|
18491
|
+
providersCount: s.providers.length
|
|
18492
|
+
});
|
|
18493
|
+
}, [screen, s]);
|
|
17753
18494
|
switch (screen) {
|
|
17754
18495
|
case "loading":
|
|
17755
18496
|
return /* @__PURE__ */ jsxRuntime.jsx(BlinkLoadingScreen, {});
|
|
@@ -17778,11 +18519,9 @@ function StepRendererContent({
|
|
|
17778
18519
|
case "wallet-picker":
|
|
17779
18520
|
return /* @__PURE__ */ jsxRuntime.jsx(WalletPickerScreen, { ...buildWalletPickerScreenProps(input) });
|
|
17780
18521
|
case "open-wallet": {
|
|
17781
|
-
const
|
|
17782
|
-
const inSigningAction = isApprovingInWalletAction(currentActionType);
|
|
17783
|
-
const pastPairingTransient = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null && currentActionType != null && (currentActionType !== "OPEN_PROVIDER" || input.remote.sourceSelectionResolved);
|
|
18522
|
+
const pastSourceSelection = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null && input.remote.sourceSelectionResolved;
|
|
17784
18523
|
const settlingPostSign = input.flow.state.linkSettling;
|
|
17785
|
-
if (
|
|
18524
|
+
if (pastSourceSelection || settlingPostSign) {
|
|
17786
18525
|
return /* @__PURE__ */ jsxRuntime.jsx(ApprovingInWalletScreen, { ...buildApprovingInWalletScreenProps(input) });
|
|
17787
18526
|
}
|
|
17788
18527
|
return /* @__PURE__ */ jsxRuntime.jsx(OpenWalletScreen, { ...buildOpenWalletScreenProps(input) });
|
|
@@ -17792,12 +18531,9 @@ function StepRendererContent({
|
|
|
17792
18531
|
if (phase.step === "wallet-setup" && phase.pendingSourceWait) {
|
|
17793
18532
|
return /* @__PURE__ */ jsxRuntime.jsx(LinkTokensScreen, { ...buildLinkTokensScreenProps(input) });
|
|
17794
18533
|
}
|
|
17795
|
-
const
|
|
17796
|
-
input.remote.authExecutorCurrentAction?.type
|
|
17797
|
-
);
|
|
17798
|
-
const justResolvedSelectSource = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null;
|
|
18534
|
+
const pastSourceSelection = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null && input.remote.sourceSelectionResolved;
|
|
17799
18535
|
const settlingPostSign = input.flow.state.linkSettling;
|
|
17800
|
-
if (
|
|
18536
|
+
if (pastSourceSelection || settlingPostSign) {
|
|
17801
18537
|
return /* @__PURE__ */ jsxRuntime.jsx(ApprovingInWalletScreen, { ...buildApprovingInWalletScreenProps(input) });
|
|
17802
18538
|
}
|
|
17803
18539
|
return /* @__PURE__ */ jsxRuntime.jsx(LinkTokensScreen, { ...buildLinkTokensScreenProps(input) });
|
|
@@ -17881,11 +18617,12 @@ var PaymentErrorBoundary = class extends react.Component {
|
|
|
17881
18617
|
};
|
|
17882
18618
|
function selectedSourceForWallet(selectedWallet, selectedTokenSymbol, depositAmount, priorityContext) {
|
|
17883
18619
|
if (!selectedWallet) return null;
|
|
18620
|
+
const walletSources = selectedWallet.sources ?? [];
|
|
17884
18621
|
if (selectedTokenSymbol) {
|
|
17885
|
-
return
|
|
18622
|
+
return walletSources.find((s) => s.token.symbol === selectedTokenSymbol) ?? null;
|
|
17886
18623
|
}
|
|
17887
18624
|
const walletChainName = selectedWallet.chain.name;
|
|
17888
|
-
const ranked =
|
|
18625
|
+
const ranked = walletSources.map((source, index) => {
|
|
17889
18626
|
return { source, index };
|
|
17890
18627
|
}).sort((a, b) => {
|
|
17891
18628
|
const priority = compareDepositSourcePriority(
|
|
@@ -17933,7 +18670,7 @@ function computeDerivedState(state, depositAmount = 0, priorityContext) {
|
|
|
17933
18670
|
let maxSourceBalance = 0;
|
|
17934
18671
|
for (const acct of state.accounts) {
|
|
17935
18672
|
for (const wallet of acct.wallets) {
|
|
17936
|
-
for (const source of wallet.sources) {
|
|
18673
|
+
for (const source of wallet.sources ?? []) {
|
|
17937
18674
|
if (source.balance.available.amount > maxSourceBalance) {
|
|
17938
18675
|
maxSourceBalance = source.balance.available.amount;
|
|
17939
18676
|
}
|
|
@@ -17943,7 +18680,7 @@ function computeDerivedState(state, depositAmount = 0, priorityContext) {
|
|
|
17943
18680
|
let tokenCount = 0;
|
|
17944
18681
|
for (const acct of state.accounts) {
|
|
17945
18682
|
for (const wallet of acct.wallets) {
|
|
17946
|
-
tokenCount += wallet.sources.filter((s) => s.balance.available.amount > 0).length;
|
|
18683
|
+
tokenCount += (wallet.sources ?? []).filter((s) => s.balance.available.amount > 0).length;
|
|
17947
18684
|
}
|
|
17948
18685
|
}
|
|
17949
18686
|
return {
|
|
@@ -18163,6 +18900,31 @@ function usePasskeyHandlers(dispatch, apiBaseUrl) {
|
|
|
18163
18900
|
};
|
|
18164
18901
|
}
|
|
18165
18902
|
|
|
18903
|
+
// src/balancesLoad.ts
|
|
18904
|
+
async function fetchBalancesByAccountId(apiBaseUrl, token, credentialId, accounts) {
|
|
18905
|
+
const entries2 = await Promise.all(
|
|
18906
|
+
accounts.map(async (account) => {
|
|
18907
|
+
try {
|
|
18908
|
+
const balances = await fetchAccountBalances(
|
|
18909
|
+
apiBaseUrl,
|
|
18910
|
+
token,
|
|
18911
|
+
account.id,
|
|
18912
|
+
credentialId
|
|
18913
|
+
);
|
|
18914
|
+
return [account.id, balances];
|
|
18915
|
+
} catch (err) {
|
|
18916
|
+
captureException(err);
|
|
18917
|
+
return null;
|
|
18918
|
+
}
|
|
18919
|
+
})
|
|
18920
|
+
);
|
|
18921
|
+
const result = {};
|
|
18922
|
+
for (const entry of entries2) {
|
|
18923
|
+
if (entry) result[entry[0]] = entry[1];
|
|
18924
|
+
}
|
|
18925
|
+
return result;
|
|
18926
|
+
}
|
|
18927
|
+
|
|
18166
18928
|
// src/transferSourceResolution.ts
|
|
18167
18929
|
var STANDARD_TRANSFER_SOURCE_UNAVAILABLE_ERROR = "Selected payment source is no longer available. Choose or link an active wallet before depositing.";
|
|
18168
18930
|
function resolveStandardTransferSource({
|
|
@@ -18214,14 +18976,14 @@ function useTransferHandlers(deps) {
|
|
|
18214
18976
|
const processingStartedAtRef = react.useRef(null);
|
|
18215
18977
|
const pollingTransferIdRef = react.useRef(null);
|
|
18216
18978
|
const depositSelectionReloadCountRef = react.useRef(0);
|
|
18217
|
-
const reloadAccounts = react.useCallback(async () => {
|
|
18979
|
+
const reloadAccounts = react.useCallback(async (opts) => {
|
|
18218
18980
|
depositSelectionReloadCountRef.current += 1;
|
|
18219
18981
|
if (depositSelectionReloadCountRef.current === 1) {
|
|
18220
18982
|
dispatch({ type: "SET_DEPOSIT_SELECTION_REFRESHING", value: true });
|
|
18221
18983
|
}
|
|
18222
18984
|
try {
|
|
18223
18985
|
const token = await getAccessToken();
|
|
18224
|
-
if (!token || !activeCredentialId) return;
|
|
18986
|
+
if (!token || !activeCredentialId) return null;
|
|
18225
18987
|
const [accts, prov] = await Promise.all([
|
|
18226
18988
|
fetchAccounts(apiBaseUrl, token, activeCredentialId),
|
|
18227
18989
|
fetchProviders(apiBaseUrl, token)
|
|
@@ -18245,6 +19007,20 @@ function useTransferHandlers(deps) {
|
|
|
18245
19007
|
defaults,
|
|
18246
19008
|
resetSelectedTokenSymbol
|
|
18247
19009
|
});
|
|
19010
|
+
const balancesPromise = fetchBalancesByAccountId(
|
|
19011
|
+
apiBaseUrl,
|
|
19012
|
+
token,
|
|
19013
|
+
activeCredentialId,
|
|
19014
|
+
accts
|
|
19015
|
+
).then((balancesByAccountId) => {
|
|
19016
|
+
dispatch({ type: "BALANCES_LOADED", balancesByAccountId });
|
|
19017
|
+
return balancesByAccountId;
|
|
19018
|
+
}).catch(() => null);
|
|
19019
|
+
if (opts?.awaitBalances) {
|
|
19020
|
+
return await balancesPromise;
|
|
19021
|
+
}
|
|
19022
|
+
void balancesPromise;
|
|
19023
|
+
return null;
|
|
18248
19024
|
} finally {
|
|
18249
19025
|
depositSelectionReloadCountRef.current = Math.max(
|
|
18250
19026
|
0,
|
|
@@ -18440,7 +19216,7 @@ function useSourceSelectionHandlers(_dispatch, orchestrator, options) {
|
|
|
18440
19216
|
const selectSourceChoices = react.useMemo(() => {
|
|
18441
19217
|
if (!pendingSelectSourceAction) return [];
|
|
18442
19218
|
const sourceOptions = pendingSelectSourceAction.metadata?.options ?? [];
|
|
18443
|
-
return buildSelectSourceChoices(sourceOptions, minTransferAmountUsd);
|
|
19219
|
+
return buildSelectSourceChoices(sourceOptions, minTransferAmountUsd, true);
|
|
18444
19220
|
}, [pendingSelectSourceAction, minTransferAmountUsd]);
|
|
18445
19221
|
const selectSourceUnsupportedChoices = react.useMemo(() => {
|
|
18446
19222
|
if (!pendingSelectSourceAction) return [];
|
|
@@ -18626,13 +19402,6 @@ function buildReauthorizationSessionOptions(params) {
|
|
|
18626
19402
|
return options;
|
|
18627
19403
|
}
|
|
18628
19404
|
|
|
18629
|
-
// src/tokenAuthorizationRunOptions.ts
|
|
18630
|
-
function buildDesktopTokenAuthorizationOnlyRunOptions(chainName, tokenSymbol) {
|
|
18631
|
-
return {
|
|
18632
|
-
autoResolveSource: { chainName, tokenSymbol }
|
|
18633
|
-
};
|
|
18634
|
-
}
|
|
18635
|
-
|
|
18636
19405
|
// src/oneTapDefaults.ts
|
|
18637
19406
|
var DEFAULT_ONE_TAP_ALLOWANCE_USD = 1e4;
|
|
18638
19407
|
async function ensureDefaultOneTapAllowance(apiBaseUrl, token) {
|
|
@@ -18653,7 +19422,7 @@ function resolveReauthorizationTarget(input) {
|
|
|
18653
19422
|
};
|
|
18654
19423
|
}
|
|
18655
19424
|
const wallet = input.account?.wallets.find((candidate) => candidate.id === input.selectedWalletId);
|
|
18656
|
-
const source = wallet?.sources
|
|
19425
|
+
const source = wallet?.sources?.find(
|
|
18657
19426
|
(candidate) => input.selectedTokenSymbol ? candidate.token.symbol === input.selectedTokenSymbol : candidate.token.status === "AUTHORIZED"
|
|
18658
19427
|
);
|
|
18659
19428
|
return {
|
|
@@ -18664,6 +19433,28 @@ function resolveReauthorizationTarget(input) {
|
|
|
18664
19433
|
};
|
|
18665
19434
|
}
|
|
18666
19435
|
|
|
19436
|
+
// src/depositTokenSelection.ts
|
|
19437
|
+
function isSourceAuthorized(balancesByAccountId, target) {
|
|
19438
|
+
const account = target.accountId != null ? balancesByAccountId?.[target.accountId] : void 0;
|
|
19439
|
+
if (!account) return false;
|
|
19440
|
+
const wallet = account.wallets.find((w) => w.id === target.walletId);
|
|
19441
|
+
if (!wallet) return false;
|
|
19442
|
+
const source = wallet.sources.find((s) => s.token.symbol === target.tokenSymbol);
|
|
19443
|
+
return source?.token.status === "AUTHORIZED";
|
|
19444
|
+
}
|
|
19445
|
+
var defaultSettleDelay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
19446
|
+
async function settleUntilTokenAuthorized(reloadAccounts, target, opts = {}) {
|
|
19447
|
+
const maxAttempts = opts.maxAttempts ?? 4;
|
|
19448
|
+
const backoffMs = opts.backoffMs ?? 400;
|
|
19449
|
+
const delay = opts.delay ?? defaultSettleDelay;
|
|
19450
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
19451
|
+
const balances = await reloadAccounts({ awaitBalances: true });
|
|
19452
|
+
if (isSourceAuthorized(balances, target)) return true;
|
|
19453
|
+
if (attempt < maxAttempts - 1) await delay(backoffMs);
|
|
19454
|
+
}
|
|
19455
|
+
return false;
|
|
19456
|
+
}
|
|
19457
|
+
|
|
18667
19458
|
// src/walletConnectLinks.ts
|
|
18668
19459
|
function buildWalletConnectDeeplink(links, walletConnectUri) {
|
|
18669
19460
|
const baseLink = links?.native ?? links?.universal;
|
|
@@ -18795,6 +19586,8 @@ function useProviderHandlers(deps) {
|
|
|
18795
19586
|
setupDepositToken,
|
|
18796
19587
|
tryStartExtensionConnectForReownWallet
|
|
18797
19588
|
} = deps;
|
|
19589
|
+
const setupDepositTokenRef = react.useRef(setupDepositToken);
|
|
19590
|
+
setupDepositTokenRef.current = setupDepositToken;
|
|
18798
19591
|
const checkWalletConnectChainSupport = react.useMemo(
|
|
18799
19592
|
() => deps.checkWalletConnectChainSupport ?? ((reownWalletId, chainId) => checkReownWalletChainSupport(BLINK_WC_PROJECT_ID, reownWalletId, chainId)),
|
|
18800
19593
|
[deps.checkWalletConnectChainSupport]
|
|
@@ -19010,7 +19803,7 @@ function useProviderHandlers(deps) {
|
|
|
19010
19803
|
}
|
|
19011
19804
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19012
19805
|
try {
|
|
19013
|
-
await reloadAccounts();
|
|
19806
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19014
19807
|
} finally {
|
|
19015
19808
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19016
19809
|
}
|
|
@@ -19137,7 +19930,7 @@ function useProviderHandlers(deps) {
|
|
|
19137
19930
|
}
|
|
19138
19931
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19139
19932
|
try {
|
|
19140
|
-
await reloadAccounts();
|
|
19933
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19141
19934
|
} finally {
|
|
19142
19935
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19143
19936
|
}
|
|
@@ -19361,7 +20154,7 @@ function useProviderHandlers(deps) {
|
|
|
19361
20154
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
19362
20155
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19363
20156
|
try {
|
|
19364
|
-
await reloadAccounts();
|
|
20157
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19365
20158
|
} finally {
|
|
19366
20159
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19367
20160
|
}
|
|
@@ -19463,7 +20256,9 @@ function useProviderHandlers(deps) {
|
|
|
19463
20256
|
const result = await orchestrator.run(
|
|
19464
20257
|
sessionId,
|
|
19465
20258
|
{
|
|
19466
|
-
|
|
20259
|
+
// No autoResolveSource: pause at SELECT_SOURCE so LinkTokensScreen
|
|
20260
|
+
// renders with the token preselected (SET_SETUP_DEPOSIT_TOKEN pin
|
|
20261
|
+
// above) and the user can set a spending limit before approving.
|
|
19467
20262
|
// Pin the existing account id so pairing and signing share one
|
|
19468
20263
|
// runtime (see handleSelectWalletConnectWallet).
|
|
19469
20264
|
walletConnectRuntimeKey: runtimeAccountId,
|
|
@@ -19471,10 +20266,11 @@ function useProviderHandlers(deps) {
|
|
|
19471
20266
|
// Require the target token's chain at pairing. A reused session
|
|
19472
20267
|
// that doesn't approve it re-pairs with the chain required; a
|
|
19473
20268
|
// wallet that approves the connection without it fails
|
|
19474
|
-
// OPEN_PROVIDER with a clear error instead of the
|
|
19475
|
-
//
|
|
19476
|
-
//
|
|
19477
|
-
//
|
|
20269
|
+
// OPEN_PROVIDER with a clear error up front instead of stranding the
|
|
20270
|
+
// user on LinkTokensScreen whose only option (SELECT_SOURCE) would
|
|
20271
|
+
// 422 (INVALID_SELECT_SOURCE_SELECTION) on Approve — e.g. authorizing
|
|
20272
|
+
// USDC on HyperEVM (999, a WC-optional chain) with a wallet that
|
|
20273
|
+
// dropped or doesn't support that chain.
|
|
19478
20274
|
walletConnectRequiredChainId: chainId,
|
|
19479
20275
|
walletConnectRequiredChainName: inlineChain.name,
|
|
19480
20276
|
onWalletConnectDisplayUri: (uri) => {
|
|
@@ -19510,11 +20306,17 @@ function useProviderHandlers(deps) {
|
|
|
19510
20306
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
19511
20307
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19512
20308
|
try {
|
|
19513
|
-
await reloadAccounts();
|
|
20309
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19514
20310
|
} finally {
|
|
19515
20311
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19516
20312
|
}
|
|
19517
|
-
|
|
20313
|
+
const confirmed = setupDepositTokenRef.current;
|
|
20314
|
+
dispatch({
|
|
20315
|
+
type: "SELECT_TOKEN",
|
|
20316
|
+
walletId: confirmed?.walletId ?? walletId,
|
|
20317
|
+
tokenSymbol: confirmed?.symbol ?? tokenSymbol,
|
|
20318
|
+
accountId: selectTokenAccountId
|
|
20319
|
+
});
|
|
19518
20320
|
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19519
20321
|
} catch (err) {
|
|
19520
20322
|
captureException(err);
|
|
@@ -19829,10 +20631,7 @@ function useProviderHandlers(deps) {
|
|
|
19829
20631
|
walletDeeplinks: walletDeeplinks ?? null,
|
|
19830
20632
|
providerId: matchedProvider?.id ?? null
|
|
19831
20633
|
});
|
|
19832
|
-
const result = await orchestrator.run(
|
|
19833
|
-
session.id,
|
|
19834
|
-
buildDesktopTokenAuthorizationOnlyRunOptions(inlineChain.name, tokenSymbol)
|
|
19835
|
-
);
|
|
20634
|
+
const result = await orchestrator.run(session.id);
|
|
19836
20635
|
if (result.status === "cancelled") {
|
|
19837
20636
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
19838
20637
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
@@ -19844,14 +20643,25 @@ function useProviderHandlers(deps) {
|
|
|
19844
20643
|
}
|
|
19845
20644
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
19846
20645
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
20646
|
+
const confirmed = setupDepositTokenRef.current;
|
|
20647
|
+
const settleTarget = {
|
|
20648
|
+
accountId: _accountId,
|
|
20649
|
+
walletId: confirmed?.walletId ?? _walletId,
|
|
20650
|
+
tokenSymbol: confirmed?.symbol ?? tokenSymbol
|
|
20651
|
+
};
|
|
19847
20652
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19848
20653
|
try {
|
|
19849
|
-
await reloadAccounts
|
|
20654
|
+
await settleUntilTokenAuthorized(reloadAccounts, settleTarget);
|
|
20655
|
+
dispatch({
|
|
20656
|
+
type: "SELECT_TOKEN",
|
|
20657
|
+
walletId: settleTarget.walletId,
|
|
20658
|
+
tokenSymbol: settleTarget.tokenSymbol,
|
|
20659
|
+
accountId: _accountId
|
|
20660
|
+
});
|
|
20661
|
+
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19850
20662
|
} finally {
|
|
19851
20663
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19852
20664
|
}
|
|
19853
|
-
dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol, accountId: _accountId });
|
|
19854
|
-
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19855
20665
|
} catch (err) {
|
|
19856
20666
|
captureException(err);
|
|
19857
20667
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
@@ -20311,6 +21121,15 @@ function useDataLoadEffect(deps) {
|
|
|
20311
21121
|
hasActiveCredential: !!state.activeCredentialId,
|
|
20312
21122
|
loading: loadingDataRef.current
|
|
20313
21123
|
});
|
|
21124
|
+
appendDebug("warn", "dataLoadEffect run", {
|
|
21125
|
+
loadAction,
|
|
21126
|
+
authenticated,
|
|
21127
|
+
accountsCount: state.accounts.length,
|
|
21128
|
+
hasActiveCredential: !!state.activeCredentialId,
|
|
21129
|
+
loadingRef: loadingDataRef.current,
|
|
21130
|
+
privyAuthenticated: state.privyAuthenticated,
|
|
21131
|
+
activeCredentialId: state.activeCredentialId ? `${state.activeCredentialId.slice(0, 8)}\u2026` : null
|
|
21132
|
+
});
|
|
20314
21133
|
if (loadAction === "reset") {
|
|
20315
21134
|
loadingDataRef.current = false;
|
|
20316
21135
|
dispatch({ type: "DATA_LOAD_END" });
|
|
@@ -20328,13 +21147,21 @@ function useDataLoadEffect(deps) {
|
|
|
20328
21147
|
const load = async () => {
|
|
20329
21148
|
dispatch({ type: "DATA_LOAD_START" });
|
|
20330
21149
|
try {
|
|
21150
|
+
appendDebug("warn", "dataLoad: requesting access token");
|
|
20331
21151
|
const token = await getAccessTokenRef.current();
|
|
21152
|
+
appendDebug("warn", "dataLoad: token result", { hasToken: !!token, cancelled });
|
|
20332
21153
|
if (!token) throw new Error("Not authenticated");
|
|
20333
21154
|
const [prov, accts, chn] = await Promise.all([
|
|
20334
21155
|
fetchProviders(apiBaseUrl, token),
|
|
20335
21156
|
fetchAccounts(apiBaseUrl, token, credentialId),
|
|
20336
21157
|
fetchChains(apiBaseUrl, token)
|
|
20337
21158
|
]);
|
|
21159
|
+
appendDebug("warn", "dataLoad: fetch resolved", {
|
|
21160
|
+
cancelled,
|
|
21161
|
+
providers: prov.length,
|
|
21162
|
+
accounts: accts.length,
|
|
21163
|
+
chains: chn.length
|
|
21164
|
+
});
|
|
20338
21165
|
if (cancelled) return;
|
|
20339
21166
|
const parsedAmt = depositAmountRef.current != null ? depositAmountRef.current : 0;
|
|
20340
21167
|
const priorityContext = resolveDepositPriorityContext(accts, chn, destination);
|
|
@@ -20360,7 +21187,16 @@ function useDataLoadEffect(deps) {
|
|
|
20360
21187
|
resetSelectedTokenSymbol
|
|
20361
21188
|
});
|
|
20362
21189
|
if (clearMobile) clearMobileFlowState();
|
|
21190
|
+
void fetchBalancesByAccountId(apiBaseUrl, token, credentialId, accts).then(
|
|
21191
|
+
(balancesByAccountId) => {
|
|
21192
|
+
dispatch({ type: "BALANCES_LOADED", balancesByAccountId });
|
|
21193
|
+
}
|
|
21194
|
+
);
|
|
20363
21195
|
} catch (err) {
|
|
21196
|
+
appendDebug("error", "dataLoad: threw", {
|
|
21197
|
+
cancelled,
|
|
21198
|
+
message: err instanceof Error ? err.message : String(err)
|
|
21199
|
+
});
|
|
20364
21200
|
if (!cancelled) {
|
|
20365
21201
|
captureException(err);
|
|
20366
21202
|
dispatch({
|
|
@@ -20377,6 +21213,7 @@ function useDataLoadEffect(deps) {
|
|
|
20377
21213
|
};
|
|
20378
21214
|
load();
|
|
20379
21215
|
return () => {
|
|
21216
|
+
appendDebug("warn", "dataLoadEffect cleanup (cancelling in-flight load)");
|
|
20380
21217
|
cancelled = true;
|
|
20381
21218
|
loadingDataRef.current = false;
|
|
20382
21219
|
dispatch({ type: "DATA_LOAD_END" });
|
|
@@ -21512,7 +22349,7 @@ function BlinkPaymentInner({
|
|
|
21512
22349
|
const handleSetDepositToken = react.useCallback((symbol, chainName, walletId, tokenAddress, chainId) => {
|
|
21513
22350
|
dispatch({ type: "SET_SETUP_DEPOSIT_TOKEN", symbol, chainName, walletId, tokenAddress, chainId });
|
|
21514
22351
|
}, []);
|
|
21515
|
-
const handleConfirmSetupDeposit = react.useCallback(async () => {
|
|
22352
|
+
const handleConfirmSetupDeposit = react.useCallback(async (limit) => {
|
|
21516
22353
|
const plan = planConfirmSetupDeposit({
|
|
21517
22354
|
setupDepositToken: state.setupDepositToken,
|
|
21518
22355
|
setupSelectedSourceOption,
|
|
@@ -21522,6 +22359,19 @@ function BlinkPaymentInner({
|
|
|
21522
22359
|
dispatch({ type: "SET_ERROR", error: plan.error });
|
|
21523
22360
|
return;
|
|
21524
22361
|
}
|
|
22362
|
+
const token = await effectiveGetAccessToken();
|
|
22363
|
+
if (!token) {
|
|
22364
|
+
dispatch({ type: "SET_ERROR", error: "Your session expired. Please try again." });
|
|
22365
|
+
return;
|
|
22366
|
+
}
|
|
22367
|
+
const config = "unlimited" in limit ? { unlimitedAllowance: true } : { defaultAllowance: limit.usd, unlimitedAllowance: false };
|
|
22368
|
+
try {
|
|
22369
|
+
await updateUserConfig(apiBaseUrl, token, config);
|
|
22370
|
+
} catch {
|
|
22371
|
+
dispatch({ type: "SET_ERROR", error: "Could not save your spending limit. Please try again." });
|
|
22372
|
+
return;
|
|
22373
|
+
}
|
|
22374
|
+
dispatch({ type: "SET_SETUP_SPENDING_LIMIT", limit });
|
|
21525
22375
|
for (const action of plan.actions) {
|
|
21526
22376
|
dispatch(action);
|
|
21527
22377
|
}
|
|
@@ -21530,6 +22380,8 @@ function BlinkPaymentInner({
|
|
|
21530
22380
|
state.selectedAccountId,
|
|
21531
22381
|
state.setupDepositToken,
|
|
21532
22382
|
setupSelectedSourceOption,
|
|
22383
|
+
effectiveGetAccessToken,
|
|
22384
|
+
apiBaseUrl,
|
|
21533
22385
|
dispatch,
|
|
21534
22386
|
orchestrator
|
|
21535
22387
|
]);
|
|
@@ -21590,6 +22442,9 @@ function BlinkPaymentInner({
|
|
|
21590
22442
|
}
|
|
21591
22443
|
})();
|
|
21592
22444
|
}, [orchestrator]);
|
|
22445
|
+
const handleApprove = react.useCallback(() => {
|
|
22446
|
+
authExecutor.approveAuthorization();
|
|
22447
|
+
}, [authExecutor]);
|
|
21593
22448
|
const handleSetPhase = react.useCallback((phase) => {
|
|
21594
22449
|
clearScreenErrors();
|
|
21595
22450
|
if (phase.step === "deposit") {
|
|
@@ -21611,6 +22466,7 @@ function BlinkPaymentInner({
|
|
|
21611
22466
|
onConfirmSign: transfer.handleConfirmSign,
|
|
21612
22467
|
onRetryMobileStatus: mobileFlow.handleRetryMobileStatus,
|
|
21613
22468
|
onRetryAuthorization: handleAuthorizationRetry,
|
|
22469
|
+
onApprove: handleApprove,
|
|
21614
22470
|
onRetryTransferSigning: transfer.handleRetryTransferSigning,
|
|
21615
22471
|
onBackFromOpenWallet: handleBackFromOpenWallet,
|
|
21616
22472
|
onLogout: handleLogout,
|
|
@@ -21681,6 +22537,7 @@ function BlinkPaymentInner({
|
|
|
21681
22537
|
handleConfirmSetupDeposit,
|
|
21682
22538
|
handleBackFromSetupDeposit,
|
|
21683
22539
|
handleAuthorizationRetry,
|
|
22540
|
+
handleApprove,
|
|
21684
22541
|
disconnectWallets,
|
|
21685
22542
|
state.desktopWait?.walletForegroundLink
|
|
21686
22543
|
]);
|
|
@@ -21720,6 +22577,9 @@ function BlinkPaymentInner({
|
|
|
21720
22577
|
authExecutorError: authExecutor.error,
|
|
21721
22578
|
authExecutorExecuting: authExecutor.executing,
|
|
21722
22579
|
authExecutorCurrentAction: authExecutor.currentAction,
|
|
22580
|
+
authExecutorAwaitingApproval: authExecutor.awaitingApproval,
|
|
22581
|
+
authExecutorApprovalDestinationAddress: authExecutor.approvalDestinationAddress,
|
|
22582
|
+
authApprovalSmartAccountAddress: orchestrator.approvalSmartAccountAddress,
|
|
21723
22583
|
authExecutorCompleted: orchestrator.orchestratorCompleted,
|
|
21724
22584
|
transferSigningSigning: transferSigning.signing,
|
|
21725
22585
|
transferSigningError: transferSigning.error,
|