@swype-org/react-sdk 0.2.375 → 0.2.399
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1868 -766
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1176 -1009
- package/dist/index.d.ts +1176 -1009
- package/dist/index.js +1861 -767
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
package/dist/index.cjs
CHANGED
|
@@ -13,9 +13,12 @@ var core = require('@wagmi/core');
|
|
|
13
13
|
var viem = require('viem');
|
|
14
14
|
var utils = require('viem/utils');
|
|
15
15
|
var QRCode = require('qrcode');
|
|
16
|
+
var posthog = require('posthog-js');
|
|
16
17
|
var Select = require('@radix-ui/react-select');
|
|
17
18
|
|
|
18
19
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
20
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
|
+
|
|
19
22
|
function _interopNamespace(e) {
|
|
20
23
|
if (e && e.__esModule) return e;
|
|
21
24
|
var n = Object.create(null);
|
|
@@ -35,6 +38,7 @@ function _interopNamespace(e) {
|
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
var QRCode__namespace = /*#__PURE__*/_interopNamespace(QRCode);
|
|
41
|
+
var posthog__default = /*#__PURE__*/_interopDefault(posthog);
|
|
38
42
|
var Select__namespace = /*#__PURE__*/_interopNamespace(Select);
|
|
39
43
|
|
|
40
44
|
var __defProp = Object.defineProperty;
|
|
@@ -78,7 +82,11 @@ var darkTheme = {
|
|
|
78
82
|
shadowLg: "0 18px 44px rgba(0,0,0,0.42)",
|
|
79
83
|
radius: "14px",
|
|
80
84
|
radiusLg: "24px",
|
|
81
|
-
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
|
|
82
90
|
};
|
|
83
91
|
var lightTheme = {
|
|
84
92
|
bg: "#ebf9fb",
|
|
@@ -110,7 +118,11 @@ var lightTheme = {
|
|
|
110
118
|
shadowLg: "0 20px 48px rgba(19, 61, 75, 0.14)",
|
|
111
119
|
radius: "14px",
|
|
112
120
|
radiusLg: "24px",
|
|
113
|
-
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
|
|
114
126
|
};
|
|
115
127
|
var lightTransparentTheme = {
|
|
116
128
|
...lightTheme
|
|
@@ -153,7 +165,13 @@ var lightThemeNew = {
|
|
|
153
165
|
shadowLg: "0 20px 48px rgba(0, 0, 0, 0.14)",
|
|
154
166
|
radius: "14px",
|
|
155
167
|
radiusLg: "24px",
|
|
156
|
-
|
|
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
|
|
157
175
|
};
|
|
158
176
|
var darkThemeNew = { ...lightThemeNew };
|
|
159
177
|
var lightTransparentThemeNew = { ...lightThemeNew };
|
|
@@ -323,7 +341,7 @@ function resolveDepositPriorityContext(accounts, chains, destination) {
|
|
|
323
341
|
for (const account of accounts) {
|
|
324
342
|
for (const wallet of account.wallets) {
|
|
325
343
|
if (wallet.chain.name !== destinationChainName) continue;
|
|
326
|
-
const source = wallet.sources.find(
|
|
344
|
+
const source = (wallet.sources ?? []).find(
|
|
327
345
|
(candidate) => candidate.address.toLowerCase() === destinationTokenAddress
|
|
328
346
|
);
|
|
329
347
|
if (source) {
|
|
@@ -357,6 +375,11 @@ function getWalletAddress(wallet) {
|
|
|
357
375
|
const address = wallet.name.trim();
|
|
358
376
|
return address.length > 0 ? address : null;
|
|
359
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
|
+
}
|
|
360
383
|
function getAddressableWallets(account) {
|
|
361
384
|
return account.wallets.filter((wallet) => getWalletAddress(wallet) != null);
|
|
362
385
|
}
|
|
@@ -364,7 +387,7 @@ function getPreferredDepositWallet(account, transferAmount, priorityContext) {
|
|
|
364
387
|
const wallets = getAddressableWallets(account);
|
|
365
388
|
if (wallets.length === 0) return null;
|
|
366
389
|
const ranked = wallets.map((wallet, walletIndex) => {
|
|
367
|
-
const bestSource = wallet.sources.map((source, sourceIndex) => ({
|
|
390
|
+
const bestSource = (wallet.sources ?? []).map((source, sourceIndex) => ({
|
|
368
391
|
source,
|
|
369
392
|
sourceIndex
|
|
370
393
|
})).sort((a, b) => {
|
|
@@ -389,7 +412,7 @@ function getPreferredDepositWallet(account, transferAmount, priorityContext) {
|
|
|
389
412
|
if (a.wallet.status !== b.wallet.status) {
|
|
390
413
|
return a.wallet.status === "ACTIVE" ? -1 : 1;
|
|
391
414
|
}
|
|
392
|
-
const balanceDiff = b.wallet.balance
|
|
415
|
+
const balanceDiff = (b.wallet.balance?.available.amount ?? 0) - (a.wallet.balance?.available.amount ?? 0);
|
|
393
416
|
if (balanceDiff !== 0) return balanceDiff;
|
|
394
417
|
return a.walletIndex - b.walletIndex;
|
|
395
418
|
});
|
|
@@ -405,7 +428,7 @@ function resolveDepositSelectionAfterRefresh(accounts, transferAmount, prev, pri
|
|
|
405
428
|
const wallet = acct?.wallets.find((w) => w.id === selectedWalletId);
|
|
406
429
|
if (wallet) {
|
|
407
430
|
if (selectedTokenSymbol) {
|
|
408
|
-
const hasToken = wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
|
|
431
|
+
const hasToken = wallet.sources == null || wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
|
|
409
432
|
if (hasToken) {
|
|
410
433
|
return {
|
|
411
434
|
defaults: { accountId: selectedAccountId, walletId: selectedWalletId },
|
|
@@ -443,7 +466,9 @@ function resolveDepositSelection(accounts, transferAmount, selectedAccountId, pr
|
|
|
443
466
|
const eligibleAccounts = getDepositEligibleAccounts(accounts);
|
|
444
467
|
if (eligibleAccounts.length === 0) return null;
|
|
445
468
|
const accountHasSufficientBalanceWallet = (account) => getAddressableWallets(account).some(
|
|
446
|
-
(wallet) => wallet.status === "ACTIVE" && wallet.sources.some(
|
|
469
|
+
(wallet) => wallet.status === "ACTIVE" && (wallet.sources ?? []).some(
|
|
470
|
+
(source) => source.balance.available.amount >= transferAmount
|
|
471
|
+
)
|
|
447
472
|
);
|
|
448
473
|
if (transferAmount <= 0 && selectedAccountId) {
|
|
449
474
|
const selectedAccount = eligibleAccounts.find((account) => account.id === selectedAccountId);
|
|
@@ -465,7 +490,7 @@ function resolveDepositSelection(accounts, transferAmount, selectedAccountId, pr
|
|
|
465
490
|
transferAmount,
|
|
466
491
|
priorityContext
|
|
467
492
|
);
|
|
468
|
-
const preferredSource = preferredWallet ? [...preferredWallet.sources].sort((a, b) => compareDepositSourcePriority(
|
|
493
|
+
const preferredSource = preferredWallet ? [...preferredWallet.sources ?? []].sort((a, b) => compareDepositSourcePriority(
|
|
469
494
|
sourcePriorityInput(preferredWallet.chain.name, a, transferAmount, priorityContext),
|
|
470
495
|
sourcePriorityInput(preferredWallet.chain.name, b, transferAmount, priorityContext)
|
|
471
496
|
))[0] : void 0;
|
|
@@ -531,7 +556,7 @@ function buildNativeUnsupportedEntries(options) {
|
|
|
531
556
|
logoURI: option.logoURI ?? null
|
|
532
557
|
})).filter((entry) => entry.amount > 0);
|
|
533
558
|
}
|
|
534
|
-
function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD) {
|
|
559
|
+
function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD, includeUnfundedTokens = false) {
|
|
535
560
|
const chainChoices = [];
|
|
536
561
|
const chainIndexByName = /* @__PURE__ */ new Map();
|
|
537
562
|
for (const option of options) {
|
|
@@ -572,7 +597,7 @@ function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SE
|
|
|
572
597
|
}
|
|
573
598
|
}
|
|
574
599
|
return chainChoices.map((chain) => {
|
|
575
|
-
const visibleTokens = chain.tokens.filter((t) => isSelectableDepositSourceAmountUsd(t.balance, minTransferAmountUsd));
|
|
600
|
+
const visibleTokens = includeUnfundedTokens ? chain.tokens : chain.tokens.filter((t) => isSelectableDepositSourceAmountUsd(t.balance, minTransferAmountUsd));
|
|
576
601
|
return {
|
|
577
602
|
...chain,
|
|
578
603
|
balance: visibleTokens.reduce((sum, token) => sum + token.balance, 0),
|
|
@@ -1577,11 +1602,11 @@ function detachAccountChangeListeners() {
|
|
|
1577
1602
|
accountChangeBinding = null;
|
|
1578
1603
|
}
|
|
1579
1604
|
function providerKey(selection) {
|
|
1580
|
-
const
|
|
1581
|
-
if (!
|
|
1605
|
+
const providerName2 = selection.providerName?.trim();
|
|
1606
|
+
if (!providerName2) {
|
|
1582
1607
|
throw new Error("Solana wallet metadata is missing providerName.");
|
|
1583
1608
|
}
|
|
1584
|
-
return `${selection.providerId ?? ""}:${
|
|
1609
|
+
return `${selection.providerId ?? ""}:${providerName2.toLowerCase()}`;
|
|
1585
1610
|
}
|
|
1586
1611
|
function assertSupportedProvider(selection) {
|
|
1587
1612
|
const name = selection.providerName?.trim().toLowerCase();
|
|
@@ -1960,464 +1985,197 @@ function screenForPhase(phase) {
|
|
|
1960
1985
|
}
|
|
1961
1986
|
}
|
|
1962
1987
|
|
|
1963
|
-
// src/
|
|
1964
|
-
var
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
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
|
+
}
|
|
1968
2033
|
}
|
|
1969
|
-
}
|
|
1970
|
-
function
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
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}`);
|
|
1978
2052
|
}
|
|
2053
|
+
notify();
|
|
1979
2054
|
}
|
|
1980
|
-
function
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2055
|
+
function subscribeDebug(listener) {
|
|
2056
|
+
listeners.add(listener);
|
|
2057
|
+
return () => {
|
|
2058
|
+
listeners.delete(listener);
|
|
2059
|
+
};
|
|
1984
2060
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
function
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
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
|
-
}
|
|
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));
|
|
2025
2102
|
}
|
|
2026
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2027
|
-
function cleanup() {
|
|
2028
|
-
clearTimeout(timer);
|
|
2029
|
-
clearInterval(closedPoll);
|
|
2030
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}` }
|
|
2031
2149
|
});
|
|
2150
|
+
if (!res.ok) await throwApiError(res);
|
|
2151
|
+
const data = await res.json();
|
|
2152
|
+
return data.items;
|
|
2032
2153
|
}
|
|
2033
|
-
async function
|
|
2034
|
-
|
|
2035
|
-
const res = await
|
|
2036
|
-
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}` }
|
|
2037
2158
|
});
|
|
2038
|
-
if (!res.ok)
|
|
2039
|
-
const
|
|
2040
|
-
|
|
2041
|
-
const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
|
|
2042
|
-
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;
|
|
2043
2162
|
}
|
|
2044
|
-
function
|
|
2045
|
-
|
|
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();
|
|
2046
2170
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
}
|
|
2056
|
-
let settled = false;
|
|
2057
|
-
const timer = setTimeout(() => {
|
|
2058
|
-
cleanup();
|
|
2059
|
-
resolve(null);
|
|
2060
|
-
}, SIGNUP_POPUP_TIMEOUT_MS);
|
|
2061
|
-
function onMessage(event) {
|
|
2062
|
-
if (event.origin !== window.location.origin) return;
|
|
2063
|
-
if (event.source !== popup) return;
|
|
2064
|
-
const data = event.data;
|
|
2065
|
-
if (!data || data.type !== "blink:passkey-signup-complete") return;
|
|
2066
|
-
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2067
|
-
settled = true;
|
|
2068
|
-
cleanup();
|
|
2069
|
-
resolve({
|
|
2070
|
-
accessToken: data.accessToken,
|
|
2071
|
-
credentialId: data.credentialId,
|
|
2072
|
-
publicKey: data.publicKey
|
|
2073
|
-
});
|
|
2074
|
-
}
|
|
2075
|
-
window.addEventListener("message", onMessage);
|
|
2076
|
-
const closedPoll = setInterval(() => {
|
|
2077
|
-
if (popup.closed && !settled) {
|
|
2078
|
-
settled = true;
|
|
2079
|
-
cleanup();
|
|
2080
|
-
resolve(null);
|
|
2081
|
-
}
|
|
2082
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2083
|
-
function cleanup() {
|
|
2084
|
-
clearTimeout(timer);
|
|
2085
|
-
clearInterval(closedPoll);
|
|
2086
|
-
window.removeEventListener("message", onMessage);
|
|
2087
|
-
}
|
|
2088
|
-
});
|
|
2089
|
-
}
|
|
2090
|
-
var LOGIN_POPUP_TIMEOUT_MS = 12e4;
|
|
2091
|
-
function loginWithPasskeyViaPopup() {
|
|
2092
|
-
return new Promise((resolve, reject) => {
|
|
2093
|
-
const popupUrl = `${window.location.origin}/passkey-login`;
|
|
2094
|
-
const popup = window.open(popupUrl, "blink-passkey-login");
|
|
2095
|
-
if (!popup) {
|
|
2096
|
-
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
2097
|
-
return;
|
|
2098
|
-
}
|
|
2099
|
-
let settled = false;
|
|
2100
|
-
const timer = setTimeout(() => {
|
|
2101
|
-
cleanup();
|
|
2102
|
-
resolve(null);
|
|
2103
|
-
}, LOGIN_POPUP_TIMEOUT_MS);
|
|
2104
|
-
function onMessage(event) {
|
|
2105
|
-
if (event.origin !== window.location.origin) return;
|
|
2106
|
-
if (event.source !== popup) return;
|
|
2107
|
-
const data = event.data;
|
|
2108
|
-
if (!data || data.type !== "blink:passkey-login-complete") return;
|
|
2109
|
-
if (typeof data.accessToken !== "string" || typeof data.credentialId !== "string" || typeof data.publicKey !== "string") return;
|
|
2110
|
-
settled = true;
|
|
2111
|
-
cleanup();
|
|
2112
|
-
resolve({
|
|
2113
|
-
accessToken: data.accessToken,
|
|
2114
|
-
credentialId: data.credentialId,
|
|
2115
|
-
publicKey: data.publicKey
|
|
2116
|
-
});
|
|
2117
|
-
}
|
|
2118
|
-
window.addEventListener("message", onMessage);
|
|
2119
|
-
const closedPoll = setInterval(() => {
|
|
2120
|
-
if (popup.closed && !settled) {
|
|
2121
|
-
settled = true;
|
|
2122
|
-
cleanup();
|
|
2123
|
-
resolve(null);
|
|
2124
|
-
}
|
|
2125
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
2126
|
-
function cleanup() {
|
|
2127
|
-
clearTimeout(timer);
|
|
2128
|
-
clearInterval(closedPoll);
|
|
2129
|
-
window.removeEventListener("message", onMessage);
|
|
2130
|
-
}
|
|
2131
|
-
});
|
|
2132
|
-
}
|
|
2133
|
-
|
|
2134
|
-
// src/credentialIdEncoding.ts
|
|
2135
|
-
function credentialIdBase64ToBytes(value) {
|
|
2136
|
-
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
2137
|
-
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
2138
|
-
const raw = atob(padded);
|
|
2139
|
-
const bytes = new Uint8Array(raw.length);
|
|
2140
|
-
for (let i = 0; i < raw.length; i++) {
|
|
2141
|
-
bytes[i] = raw.charCodeAt(i);
|
|
2142
|
-
}
|
|
2143
|
-
return bytes;
|
|
2144
|
-
}
|
|
2145
|
-
|
|
2146
|
-
// src/passkeyRpId.ts
|
|
2147
|
-
function normalizeConfiguredDomain(value) {
|
|
2148
|
-
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
2149
|
-
}
|
|
2150
|
-
function resolveRootDomainFromHostname(hostname) {
|
|
2151
|
-
const trimmedHostname = hostname.trim().toLowerCase();
|
|
2152
|
-
if (!trimmedHostname) {
|
|
2153
|
-
return "localhost";
|
|
2154
|
-
}
|
|
2155
|
-
if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
|
|
2156
|
-
return trimmedHostname;
|
|
2157
|
-
}
|
|
2158
|
-
const parts = trimmedHostname.split(".").filter(Boolean);
|
|
2159
|
-
if (parts.length < 2) {
|
|
2160
|
-
return trimmedHostname;
|
|
2161
|
-
}
|
|
2162
|
-
return parts.slice(-2).join(".");
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
|
-
// src/hooks/passkeyPublic.ts
|
|
2166
|
-
function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
|
|
2167
|
-
return new Promise((resolve) => {
|
|
2168
|
-
if (typeof document === "undefined") {
|
|
2169
|
-
resolve();
|
|
2170
|
-
return;
|
|
2171
|
-
}
|
|
2172
|
-
if (document.hasFocus()) {
|
|
2173
|
-
resolve();
|
|
2174
|
-
return;
|
|
2175
|
-
}
|
|
2176
|
-
const deadline = Date.now() + timeoutMs;
|
|
2177
|
-
const timer = setInterval(() => {
|
|
2178
|
-
if (document.hasFocus()) {
|
|
2179
|
-
clearInterval(timer);
|
|
2180
|
-
resolve();
|
|
2181
|
-
} else if (Date.now() >= deadline) {
|
|
2182
|
-
clearInterval(timer);
|
|
2183
|
-
resolve();
|
|
2184
|
-
}
|
|
2185
|
-
}, intervalMs);
|
|
2186
|
-
});
|
|
2187
|
-
}
|
|
2188
|
-
function toBase64(buffer) {
|
|
2189
|
-
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
2190
|
-
}
|
|
2191
|
-
function readEnvValue(name) {
|
|
2192
|
-
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)) });
|
|
2193
|
-
const metaValue = meta.env?.[name];
|
|
2194
|
-
if (typeof metaValue === "string" && metaValue.trim().length > 0) {
|
|
2195
|
-
return metaValue.trim();
|
|
2196
|
-
}
|
|
2197
|
-
const processValue = globalThis.process?.env?.[name];
|
|
2198
|
-
if (typeof processValue === "string" && processValue.trim().length > 0) {
|
|
2199
|
-
return processValue.trim();
|
|
2200
|
-
}
|
|
2201
|
-
return void 0;
|
|
2202
|
-
}
|
|
2203
|
-
function resolvePasskeyRpId() {
|
|
2204
|
-
const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
|
|
2205
|
-
if (configuredDomain) {
|
|
2206
|
-
return normalizeConfiguredDomain(configuredDomain);
|
|
2207
|
-
}
|
|
2208
|
-
if (typeof window !== "undefined") {
|
|
2209
|
-
return resolveRootDomainFromHostname(window.location.hostname);
|
|
2210
|
-
}
|
|
2211
|
-
return "localhost";
|
|
2212
|
-
}
|
|
2213
|
-
async function deviceHasPasskey(credentialId) {
|
|
2214
|
-
const found = await findDevicePasskey([credentialId]);
|
|
2215
|
-
return found != null;
|
|
2216
|
-
}
|
|
2217
|
-
async function findDevicePasskey(credentialIds) {
|
|
2218
|
-
if (credentialIds.length === 0) return null;
|
|
2219
|
-
try {
|
|
2220
|
-
const challenge = new Uint8Array(32);
|
|
2221
|
-
crypto.getRandomValues(challenge);
|
|
2222
|
-
await waitForDocumentFocus();
|
|
2223
|
-
const assertion = await navigator.credentials.get({
|
|
2224
|
-
publicKey: {
|
|
2225
|
-
challenge,
|
|
2226
|
-
rpId: resolvePasskeyRpId(),
|
|
2227
|
-
allowCredentials: credentialIds.map((id) => ({
|
|
2228
|
-
type: "public-key",
|
|
2229
|
-
id: credentialIdBase64ToBytes(id)
|
|
2230
|
-
})),
|
|
2231
|
-
userVerification: "discouraged",
|
|
2232
|
-
timeout: 3e4
|
|
2233
|
-
}
|
|
2234
|
-
});
|
|
2235
|
-
if (!assertion) return null;
|
|
2236
|
-
return toBase64(assertion.rawId);
|
|
2237
|
-
} catch {
|
|
2238
|
-
return null;
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
|
|
2242
|
-
// src/api.ts
|
|
2243
|
-
var api_exports = {};
|
|
2244
|
-
__export(api_exports, {
|
|
2245
|
-
createAccount: () => createAccount,
|
|
2246
|
-
createAccountAuthorizationSession: () => createAccountAuthorizationSession,
|
|
2247
|
-
createManualTransfer: () => createManualTransfer,
|
|
2248
|
-
createTransfer: () => createTransfer,
|
|
2249
|
-
fetchAccount: () => fetchAccount,
|
|
2250
|
-
fetchAccounts: () => fetchAccounts,
|
|
2251
|
-
fetchAuthorizationSession: () => fetchAuthorizationSession,
|
|
2252
|
-
fetchAuthorizationSessionByToken: () => fetchAuthorizationSessionByToken,
|
|
2253
|
-
fetchChains: () => fetchChains,
|
|
2254
|
-
fetchManualTransferSession: () => fetchManualTransferSession,
|
|
2255
|
-
fetchManualTransferSources: () => fetchManualTransferSources,
|
|
2256
|
-
fetchMerchantPublicKey: () => fetchMerchantPublicKey,
|
|
2257
|
-
fetchProviders: () => fetchProviders,
|
|
2258
|
-
fetchTransfer: () => fetchTransfer,
|
|
2259
|
-
fetchUserConfig: () => fetchUserConfig,
|
|
2260
|
-
postTransferQuote: () => postTransferQuote,
|
|
2261
|
-
probeActionCompletion: () => probeActionCompletion,
|
|
2262
|
-
refreshManualTransferQuote: () => refreshManualTransferQuote,
|
|
2263
|
-
regenerateTransferSignPayload: () => regenerateTransferSignPayload,
|
|
2264
|
-
registerPasskey: () => registerPasskey,
|
|
2265
|
-
reportActionCompletion: () => reportActionCompletion,
|
|
2266
|
-
reportPasskeyActivity: () => reportPasskeyActivity,
|
|
2267
|
-
setAuthorizationSessionPaymentIntentAmount: () => setAuthorizationSessionPaymentIntentAmount,
|
|
2268
|
-
setAuthorizationSessionProvider: () => setAuthorizationSessionProvider,
|
|
2269
|
-
signTransfer: () => signTransfer,
|
|
2270
|
-
updateManualTransferDepositTargetChain: () => updateManualTransferDepositTargetChain,
|
|
2271
|
-
updateUserConfig: () => updateUserConfig,
|
|
2272
|
-
updateUserConfigBySession: () => updateUserConfigBySession,
|
|
2273
|
-
waitForActionTransactionReceipt: () => waitForActionTransactionReceipt
|
|
2274
|
-
});
|
|
2275
|
-
var DEBUG_BUFFER_CAPACITY = 200;
|
|
2276
|
-
var nextId = 1;
|
|
2277
|
-
var entries = [];
|
|
2278
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
2279
|
-
function notify() {
|
|
2280
|
-
for (const listener of listeners) {
|
|
2281
|
-
try {
|
|
2282
|
-
listener();
|
|
2283
|
-
} catch (err) {
|
|
2284
|
-
console.error("[blink-sdk][debug-log] listener threw:", err);
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
|
-
}
|
|
2288
|
-
function appendDebug(level, message, data) {
|
|
2289
|
-
const entry = {
|
|
2290
|
-
id: nextId++,
|
|
2291
|
-
ts: Date.now(),
|
|
2292
|
-
level,
|
|
2293
|
-
message,
|
|
2294
|
-
data
|
|
2295
|
-
};
|
|
2296
|
-
const next = entries.length >= DEBUG_BUFFER_CAPACITY ? entries.slice(entries.length - DEBUG_BUFFER_CAPACITY + 1) : entries.slice();
|
|
2297
|
-
next.push(entry);
|
|
2298
|
-
entries = next;
|
|
2299
|
-
const prefix = "[blink-sdk][debug]";
|
|
2300
|
-
const sink = level === "error" ? console.error : level === "warn" ? console.warn : console.info;
|
|
2301
|
-
if (data !== void 0) {
|
|
2302
|
-
sink(`${prefix} ${message}`, data);
|
|
2303
|
-
} else {
|
|
2304
|
-
sink(`${prefix} ${message}`);
|
|
2305
|
-
}
|
|
2306
|
-
notify();
|
|
2307
|
-
}
|
|
2308
|
-
function subscribeDebug(listener) {
|
|
2309
|
-
listeners.add(listener);
|
|
2310
|
-
return () => {
|
|
2311
|
-
listeners.delete(listener);
|
|
2312
|
-
};
|
|
2313
|
-
}
|
|
2314
|
-
function getDebugEntries() {
|
|
2315
|
-
return entries;
|
|
2316
|
-
}
|
|
2317
|
-
function clearDebugEntries() {
|
|
2318
|
-
entries = [];
|
|
2319
|
-
notify();
|
|
2320
|
-
}
|
|
2321
|
-
function useBlinkDebugLog() {
|
|
2322
|
-
return react.useSyncExternalStore(subscribeDebug, getDebugEntries, getDebugEntries);
|
|
2323
|
-
}
|
|
2324
|
-
|
|
2325
|
-
// src/fetchWithRetry.ts
|
|
2326
|
-
var DEFAULT_MAX_RETRIES = 3;
|
|
2327
|
-
var DEFAULT_BASE_DELAY_MS = 500;
|
|
2328
|
-
var DEFAULT_MAX_JITTER_MS = 200;
|
|
2329
|
-
function isNetworkTypeError(err) {
|
|
2330
|
-
return err instanceof TypeError && /fetch|network|load failed/i.test(err.message);
|
|
2331
|
-
}
|
|
2332
|
-
async function fetchWithRetry(input, init, options) {
|
|
2333
|
-
const maxRetries = DEFAULT_MAX_RETRIES;
|
|
2334
|
-
const baseDelayMs = DEFAULT_BASE_DELAY_MS;
|
|
2335
|
-
const maxJitterMs = DEFAULT_MAX_JITTER_MS;
|
|
2336
|
-
const label = String(input).replace(/https?:\/\/[^/]+/, "");
|
|
2337
|
-
let lastError;
|
|
2338
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2339
|
-
try {
|
|
2340
|
-
return await fetch(input, init);
|
|
2341
|
-
} catch (err) {
|
|
2342
|
-
lastError = err;
|
|
2343
|
-
if (!isNetworkTypeError(err)) {
|
|
2344
|
-
throw err;
|
|
2345
|
-
}
|
|
2346
|
-
if (attempt < maxRetries) {
|
|
2347
|
-
const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * maxJitterMs;
|
|
2348
|
-
appendDebug("warn", `fetchWithRetry: network error, retrying ${label}`, {
|
|
2349
|
-
attempt: attempt + 1,
|
|
2350
|
-
maxRetries,
|
|
2351
|
-
delayMs: Math.round(delay),
|
|
2352
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2353
|
-
});
|
|
2354
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
}
|
|
2358
|
-
throw lastError;
|
|
2359
|
-
}
|
|
2360
|
-
|
|
2361
|
-
// src/apiError.ts
|
|
2362
|
-
var ApiError = class extends Error {
|
|
2363
|
-
status;
|
|
2364
|
-
code;
|
|
2365
|
-
constructor(status, code, message) {
|
|
2366
|
-
super(message);
|
|
2367
|
-
this.name = "ApiError";
|
|
2368
|
-
this.status = status;
|
|
2369
|
-
this.code = code;
|
|
2370
|
-
}
|
|
2371
|
-
};
|
|
2372
|
-
function isApiError(err) {
|
|
2373
|
-
if (err instanceof ApiError) return true;
|
|
2374
|
-
return typeof err === "object" && err !== null && "name" in err && err.name === "ApiError";
|
|
2375
|
-
}
|
|
2376
|
-
var SVM_SIGN_PAYLOAD_EXPIRED_CODE = "SVM_SIGN_PAYLOAD_EXPIRED";
|
|
2377
|
-
function isSvmSignExpiredError(err) {
|
|
2378
|
-
return isApiError(err) && err.code === SVM_SIGN_PAYLOAD_EXPIRED_CODE;
|
|
2379
|
-
}
|
|
2380
|
-
|
|
2381
|
-
// src/api.ts
|
|
2382
|
-
async function throwApiError(res) {
|
|
2383
|
-
const body = await res.json().catch(() => null);
|
|
2384
|
-
const detail = body?.error ?? body;
|
|
2385
|
-
const msg = detail?.message ?? res.statusText;
|
|
2386
|
-
const code = detail?.code ?? String(res.status);
|
|
2387
|
-
throw new ApiError(res.status, code, `${res.status} \u2014 ${code}: ${msg}`);
|
|
2388
|
-
}
|
|
2389
|
-
async function fetchProviders(apiBaseUrl, token) {
|
|
2390
|
-
const headers = {};
|
|
2391
|
-
if (token) {
|
|
2392
|
-
headers.Authorization = `Bearer ${token}`;
|
|
2393
|
-
}
|
|
2394
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/providers`, { headers });
|
|
2395
|
-
if (!res.ok) await throwApiError(res);
|
|
2396
|
-
const data = await res.json();
|
|
2397
|
-
return data.items;
|
|
2398
|
-
}
|
|
2399
|
-
async function fetchChains(apiBaseUrl, token) {
|
|
2400
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/chains`, {
|
|
2401
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2402
|
-
});
|
|
2403
|
-
if (!res.ok) await throwApiError(res);
|
|
2404
|
-
const data = await res.json();
|
|
2405
|
-
return data.items;
|
|
2406
|
-
}
|
|
2407
|
-
async function fetchAccounts(apiBaseUrl, token, credentialId) {
|
|
2408
|
-
const params = new URLSearchParams({ credentialId });
|
|
2409
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts?${params.toString()}`, {
|
|
2410
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2411
|
-
});
|
|
2412
|
-
if (!res.ok) await throwApiError(res);
|
|
2413
|
-
const data = await res.json();
|
|
2414
|
-
return data.items;
|
|
2415
|
-
}
|
|
2416
|
-
async function fetchAccount(apiBaseUrl, token, accountId, credentialId) {
|
|
2417
|
-
const params = new URLSearchParams({ credentialId });
|
|
2418
|
-
const res = await fetchWithRetry(`${apiBaseUrl}/v1/accounts/${accountId}?${params.toString()}`, {
|
|
2419
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
2420
|
-
});
|
|
2171
|
+
async function fetchAccountBalances(apiBaseUrl, token, accountId, credentialId) {
|
|
2172
|
+
const params = new URLSearchParams({ credentialId });
|
|
2173
|
+
const res = await fetchWithRetry(
|
|
2174
|
+
`${apiBaseUrl}/v1/accounts/${accountId}/balances?${params.toString()}`,
|
|
2175
|
+
{
|
|
2176
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2177
|
+
}
|
|
2178
|
+
);
|
|
2421
2179
|
if (!res.ok) await throwApiError(res);
|
|
2422
2180
|
return await res.json();
|
|
2423
2181
|
}
|
|
@@ -2738,44 +2496,323 @@ async function waitForActionTransactionReceipt(apiBaseUrl, actionId, txHash) {
|
|
|
2738
2496
|
headers: { "Content-Type": "application/json" },
|
|
2739
2497
|
body: JSON.stringify({ txHash })
|
|
2740
2498
|
}
|
|
2741
|
-
);
|
|
2742
|
-
if (!res.ok) await throwApiError(res);
|
|
2743
|
-
return await res.json();
|
|
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
|
+
});
|
|
2708
|
+
}
|
|
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;
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
// src/passkeyRpId.ts
|
|
2723
|
+
function normalizeConfiguredDomain(value) {
|
|
2724
|
+
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
2725
|
+
}
|
|
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(".");
|
|
2739
|
+
}
|
|
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;
|
|
2747
|
+
}
|
|
2748
|
+
if (document.hasFocus()) {
|
|
2749
|
+
resolve();
|
|
2750
|
+
return;
|
|
2751
|
+
}
|
|
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
|
+
});
|
|
2744
2763
|
}
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
);
|
|
2754
|
-
if (res.ok) {
|
|
2755
|
-
const session = await res.json();
|
|
2756
|
-
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();
|
|
2757
2772
|
}
|
|
2758
|
-
const
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
const message = detail?.message ?? res.statusText ?? `HTTP ${res.status}`;
|
|
2762
|
-
if (res.status === 422 && code === "DEPOSIT_TX_NOT_FOUND") {
|
|
2763
|
-
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();
|
|
2764
2776
|
}
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
]);
|
|
2772
|
-
if (res.status === 422 && code && approvalNotDetectedCodes.has(code)) {
|
|
2773
|
-
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);
|
|
2774
2783
|
}
|
|
2775
|
-
if (
|
|
2776
|
-
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;
|
|
2777
2815
|
}
|
|
2778
|
-
return { detected: false, reason: "error", status: res.status, code, message };
|
|
2779
2816
|
}
|
|
2780
2817
|
|
|
2781
2818
|
// src/transferPolling.ts
|
|
@@ -4149,14 +4186,14 @@ function isBatchableAction(action) {
|
|
|
4149
4186
|
return BATCHABLE_ACTION_TYPES.has(action.type);
|
|
4150
4187
|
}
|
|
4151
4188
|
function getSolanaWalletSelection(action) {
|
|
4152
|
-
const
|
|
4153
|
-
if (typeof
|
|
4189
|
+
const providerName2 = action.metadata?.providerName;
|
|
4190
|
+
if (typeof providerName2 !== "string" || providerName2.trim() === "") {
|
|
4154
4191
|
throw new Error(`${action.type} metadata is missing providerName.`);
|
|
4155
4192
|
}
|
|
4156
4193
|
const providerId = action.metadata?.providerId;
|
|
4157
4194
|
return {
|
|
4158
4195
|
...typeof providerId === "string" && providerId.trim() !== "" ? { providerId } : {},
|
|
4159
|
-
providerName
|
|
4196
|
+
providerName: providerName2
|
|
4160
4197
|
};
|
|
4161
4198
|
}
|
|
4162
4199
|
function isPhantomEvmProviderAvailable() {
|
|
@@ -5806,6 +5843,43 @@ function useAuthorizationExecutor(options) {
|
|
|
5806
5843
|
}),
|
|
5807
5844
|
[]
|
|
5808
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
|
+
}, []);
|
|
5809
5883
|
const cancelPendingExecution = react.useCallback(() => {
|
|
5810
5884
|
if (selectSourceRejectRef.current) {
|
|
5811
5885
|
selectSourceRejectRef.current(new AuthorizationSessionCancelledError());
|
|
@@ -5813,6 +5887,12 @@ function useAuthorizationExecutor(options) {
|
|
|
5813
5887
|
selectSourceResolverRef.current = null;
|
|
5814
5888
|
setPendingSelectSource(null);
|
|
5815
5889
|
}
|
|
5890
|
+
if (approvalRejectRef.current) {
|
|
5891
|
+
approvalRejectRef.current(new AuthorizationSessionCancelledError());
|
|
5892
|
+
approvalRejectRef.current = null;
|
|
5893
|
+
approvalResolverRef.current = null;
|
|
5894
|
+
}
|
|
5895
|
+
setAwaitingApproval(false);
|
|
5816
5896
|
setError(null);
|
|
5817
5897
|
setCurrentAction(null);
|
|
5818
5898
|
setApproveSplConfirming(null);
|
|
@@ -6215,6 +6295,11 @@ function useAuthorizationExecutor(options) {
|
|
|
6215
6295
|
currentAction,
|
|
6216
6296
|
approveSplConfirming,
|
|
6217
6297
|
pendingSelectSource,
|
|
6298
|
+
awaitingApproval,
|
|
6299
|
+
approvalDestinationAddress,
|
|
6300
|
+
waitForApproval,
|
|
6301
|
+
approveAuthorization,
|
|
6302
|
+
resetApprovalGate,
|
|
6218
6303
|
resolveSelectSource,
|
|
6219
6304
|
cancelPendingExecution,
|
|
6220
6305
|
resetWalletConnect,
|
|
@@ -6509,6 +6594,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6509
6594
|
const [pendingSelectSourceAction, setPendingSelectSourceAction] = react.useState(null);
|
|
6510
6595
|
const [orchestratorCompleted, setOrchestratorCompleted] = react.useState(false);
|
|
6511
6596
|
const [sourceSelectionResolved, setSourceSelectionResolved] = react.useState(false);
|
|
6597
|
+
const [approvalSmartAccountAddress, setApprovalSmartAccountAddress] = react.useState(null);
|
|
6512
6598
|
const selectSourceResolverRef = react.useRef(null);
|
|
6513
6599
|
const selectSourceRejectRef = react.useRef(null);
|
|
6514
6600
|
const submittedApprovePermit2ActionIdsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
@@ -6534,6 +6620,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6534
6620
|
const cancellation = new AuthorizationSessionCancelledError();
|
|
6535
6621
|
setOrchestratorCompleted(false);
|
|
6536
6622
|
setSourceSelectionResolved(false);
|
|
6623
|
+
setApprovalSmartAccountAddress(null);
|
|
6537
6624
|
if (selectSourceRejectRef.current) {
|
|
6538
6625
|
selectSourceRejectRef.current(cancellation);
|
|
6539
6626
|
selectSourceRejectRef.current = null;
|
|
@@ -6556,6 +6643,10 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6556
6643
|
lastRunRef.current = { sessionId, options };
|
|
6557
6644
|
setOrchestratorCompleted(false);
|
|
6558
6645
|
setSourceSelectionResolved(false);
|
|
6646
|
+
setApprovalSmartAccountAddress(null);
|
|
6647
|
+
if (!options?.keepApprovalGate) {
|
|
6648
|
+
authExecutor.resetApprovalGate();
|
|
6649
|
+
}
|
|
6559
6650
|
if (!authExecutor.beginExecution()) {
|
|
6560
6651
|
appendDebug("warn", "orchestrator:run aborted \u2014 already executing");
|
|
6561
6652
|
return { status: "cancelled" };
|
|
@@ -6584,6 +6675,8 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6584
6675
|
let action = mergedPending[0];
|
|
6585
6676
|
if (completedIds.has(action.id)) break;
|
|
6586
6677
|
const ownerSessionId = actionSessionMap.get(action.id) ?? sessionId;
|
|
6678
|
+
const ownerSessionSca = sessions.find((s) => s.id === ownerSessionId)?.session.smartAccountAddress;
|
|
6679
|
+
if (ownerSessionSca) setApprovalSmartAccountAddress(ownerSessionSca);
|
|
6587
6680
|
console.info("[blink-sdk][orchestrator] Next pending action.", {
|
|
6588
6681
|
actionId: action.id,
|
|
6589
6682
|
actionType: action.type,
|
|
@@ -6608,6 +6701,7 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6608
6701
|
const autoResult = createSelectSourceResult(action, options.autoResolveSource);
|
|
6609
6702
|
completedIds.add(action.id);
|
|
6610
6703
|
authExecutor.addResult(autoResult);
|
|
6704
|
+
setSourceSelectionResolved(true);
|
|
6611
6705
|
const reportedSession3 = await reportActionCompletionWithLogging(
|
|
6612
6706
|
apiBaseUrl,
|
|
6613
6707
|
action,
|
|
@@ -6701,6 +6795,13 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6701
6795
|
throw new Error(SIGN_PERMIT2_CONFIRMING_MESSAGE);
|
|
6702
6796
|
}
|
|
6703
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
|
+
}
|
|
6704
6805
|
appendDebug("info", `orchestrator:executeAction start ${action.type}`, {
|
|
6705
6806
|
actionId: action.id,
|
|
6706
6807
|
ownerSessionId
|
|
@@ -6897,7 +6998,8 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6897
6998
|
authExecutor.setError(null);
|
|
6898
6999
|
return run(lastRun.sessionId, {
|
|
6899
7000
|
...lastRun.options,
|
|
6900
|
-
probeBeforePrompt: true
|
|
7001
|
+
probeBeforePrompt: true,
|
|
7002
|
+
keepApprovalGate: true
|
|
6901
7003
|
});
|
|
6902
7004
|
}, [authExecutor, run, cancelPendingFlow]);
|
|
6903
7005
|
return {
|
|
@@ -6907,9 +7009,17 @@ function useAuthorizationOrchestrator(deps) {
|
|
|
6907
7009
|
resolveSelectSource,
|
|
6908
7010
|
sourceSelectionResolved,
|
|
6909
7011
|
orchestratorCompleted,
|
|
7012
|
+
approvalSmartAccountAddress,
|
|
6910
7013
|
cancelPendingFlow
|
|
6911
7014
|
};
|
|
6912
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
|
+
}
|
|
6913
7023
|
function createSelectSourceResult(action, selection) {
|
|
6914
7024
|
return {
|
|
6915
7025
|
actionId: action.id,
|
|
@@ -7880,6 +7990,26 @@ function deriveSourceTypeAndId(state) {
|
|
|
7880
7990
|
}
|
|
7881
7991
|
return { sourceType: "accountId", sourceId: "" };
|
|
7882
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
|
+
}
|
|
7883
8013
|
function clearStaleSelection(state) {
|
|
7884
8014
|
if (state.selectedAccountId == null) return state;
|
|
7885
8015
|
if (state.desktopWait != null || state.standardDesktopInlineOpenWallet) return state;
|
|
@@ -7901,6 +8031,7 @@ function createInitialState(config) {
|
|
|
7901
8031
|
accounts: [],
|
|
7902
8032
|
chains: [],
|
|
7903
8033
|
loadingData: false,
|
|
8034
|
+
balancesLoading: false,
|
|
7904
8035
|
depositSelectionRefreshing: false,
|
|
7905
8036
|
selectedProviderId: null,
|
|
7906
8037
|
selectedAccountId: null,
|
|
@@ -7930,6 +8061,7 @@ function createInitialState(config) {
|
|
|
7930
8061
|
privyAuthenticated: false,
|
|
7931
8062
|
lastResumedAt: 0,
|
|
7932
8063
|
setupDepositToken: null,
|
|
8064
|
+
setupSpendingLimit: null,
|
|
7933
8065
|
guestWalletPrepared: null,
|
|
7934
8066
|
guestWalletDeeplinksPreparing: false,
|
|
7935
8067
|
amountTooLow: null,
|
|
@@ -7949,6 +8081,7 @@ function clearAuthenticatedSessionState(state) {
|
|
|
7949
8081
|
accounts: [],
|
|
7950
8082
|
chains: [],
|
|
7951
8083
|
loadingData: false,
|
|
8084
|
+
balancesLoading: false,
|
|
7952
8085
|
depositSelectionRefreshing: false,
|
|
7953
8086
|
selectedProviderId: null,
|
|
7954
8087
|
selectedAccountId: null,
|
|
@@ -7974,6 +8107,7 @@ function clearAuthenticatedSessionState(state) {
|
|
|
7974
8107
|
mobileTokenAuthorizationPending: false,
|
|
7975
8108
|
linkSettling: false,
|
|
7976
8109
|
setupDepositToken: null,
|
|
8110
|
+
setupSpendingLimit: null,
|
|
7977
8111
|
guestWalletPrepared: null,
|
|
7978
8112
|
guestWalletDeeplinksPreparing: false,
|
|
7979
8113
|
amountTooLow: null,
|
|
@@ -8042,6 +8176,8 @@ function applyAction(state, action) {
|
|
|
8042
8176
|
providers: action.providers,
|
|
8043
8177
|
accounts: action.accounts,
|
|
8044
8178
|
chains: action.chains,
|
|
8179
|
+
// Accounts arrive balance-free; balances follow via BALANCES_LOADED.
|
|
8180
|
+
balancesLoading: true,
|
|
8045
8181
|
depositSelectionRefreshing: false,
|
|
8046
8182
|
initialDataLoaded: true
|
|
8047
8183
|
};
|
|
@@ -8065,10 +8201,12 @@ function applyAction(state, action) {
|
|
|
8065
8201
|
depositSelectionRefreshing: false
|
|
8066
8202
|
};
|
|
8067
8203
|
case "ACCOUNTS_RELOADED": {
|
|
8204
|
+
const hadBalances = hasAnyBalances(state.accounts);
|
|
8068
8205
|
const next = {
|
|
8069
8206
|
...state,
|
|
8070
|
-
accounts: action.accounts,
|
|
8207
|
+
accounts: carryOverBalances(state.accounts, action.accounts),
|
|
8071
8208
|
providers: action.providers,
|
|
8209
|
+
balancesLoading: !hadBalances,
|
|
8072
8210
|
initialDataLoaded: true
|
|
8073
8211
|
};
|
|
8074
8212
|
if (action.defaults) {
|
|
@@ -8080,6 +8218,24 @@ function applyAction(state, action) {
|
|
|
8080
8218
|
}
|
|
8081
8219
|
return clearStaleSelection(next);
|
|
8082
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
|
+
}
|
|
8083
8239
|
case "SET_DEPOSIT_SELECTION_REFRESHING":
|
|
8084
8240
|
return { ...state, depositSelectionRefreshing: action.value };
|
|
8085
8241
|
case "SAVE_SELECTION":
|
|
@@ -8151,6 +8307,7 @@ function applyAction(state, action) {
|
|
|
8151
8307
|
mobileFlow: false,
|
|
8152
8308
|
mobileTokenAuthorizationPending: false,
|
|
8153
8309
|
setupDepositToken: null,
|
|
8310
|
+
setupSpendingLimit: null,
|
|
8154
8311
|
setupFlowScreen: null
|
|
8155
8312
|
};
|
|
8156
8313
|
case "PAY_ENDED":
|
|
@@ -8396,6 +8553,7 @@ function applyAction(state, action) {
|
|
|
8396
8553
|
linkSettling: false,
|
|
8397
8554
|
savedSelection: null,
|
|
8398
8555
|
setupDepositToken: null,
|
|
8556
|
+
setupSpendingLimit: null,
|
|
8399
8557
|
setupFlowScreen: null,
|
|
8400
8558
|
guestWalletPrepared: null,
|
|
8401
8559
|
guestWalletDeeplinksPreparing: false,
|
|
@@ -8461,12 +8619,217 @@ function applyAction(state, action) {
|
|
|
8461
8619
|
...action.chainId != null ? { chainId: action.chainId } : {}
|
|
8462
8620
|
}
|
|
8463
8621
|
};
|
|
8622
|
+
case "SET_SETUP_SPENDING_LIMIT":
|
|
8623
|
+
return { ...state, setupSpendingLimit: action.limit };
|
|
8464
8624
|
case "CLEAR_SETUP_DEPOSIT_TOKEN":
|
|
8465
|
-
return { ...state, setupDepositToken: null };
|
|
8625
|
+
return { ...state, setupDepositToken: null, setupSpendingLimit: null };
|
|
8466
8626
|
default:
|
|
8467
8627
|
return state;
|
|
8468
8628
|
}
|
|
8469
8629
|
}
|
|
8630
|
+
var BLOCKED_SUBSTRINGS = [
|
|
8631
|
+
"bearer",
|
|
8632
|
+
"signature",
|
|
8633
|
+
"signedpayload",
|
|
8634
|
+
"userop",
|
|
8635
|
+
"jwt",
|
|
8636
|
+
"secret",
|
|
8637
|
+
"password",
|
|
8638
|
+
"privatekey",
|
|
8639
|
+
"mnemonic",
|
|
8640
|
+
"seedphrase"
|
|
8641
|
+
];
|
|
8642
|
+
function isBlockedKey(key) {
|
|
8643
|
+
const lower = key.toLowerCase();
|
|
8644
|
+
if (lower === "token" || lower.endsWith("token")) return true;
|
|
8645
|
+
return BLOCKED_SUBSTRINGS.some((blocked) => lower.includes(blocked));
|
|
8646
|
+
}
|
|
8647
|
+
function isReady() {
|
|
8648
|
+
return typeof posthog__default.default !== "undefined" && posthog__default.default.__loaded === true;
|
|
8649
|
+
}
|
|
8650
|
+
function sanitizeProps(props) {
|
|
8651
|
+
if (!props) return props;
|
|
8652
|
+
const clean = {};
|
|
8653
|
+
for (const [key, value] of Object.entries(props)) {
|
|
8654
|
+
if (isBlockedKey(key)) continue;
|
|
8655
|
+
if (value === void 0) continue;
|
|
8656
|
+
clean[key] = value;
|
|
8657
|
+
}
|
|
8658
|
+
return clean;
|
|
8659
|
+
}
|
|
8660
|
+
function registerSuperProps(props) {
|
|
8661
|
+
if (!isReady()) return;
|
|
8662
|
+
try {
|
|
8663
|
+
posthog__default.default.register(sanitizeProps(props) ?? {});
|
|
8664
|
+
} catch {
|
|
8665
|
+
}
|
|
8666
|
+
}
|
|
8667
|
+
function track(event, props) {
|
|
8668
|
+
if (!isReady()) return;
|
|
8669
|
+
try {
|
|
8670
|
+
posthog__default.default.capture(event, sanitizeProps(props));
|
|
8671
|
+
} catch {
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8674
|
+
function identifyUser(privyUserId, props) {
|
|
8675
|
+
if (!isReady() || !privyUserId) return;
|
|
8676
|
+
try {
|
|
8677
|
+
posthog__default.default.identify(privyUserId, sanitizeProps(props));
|
|
8678
|
+
} catch {
|
|
8679
|
+
}
|
|
8680
|
+
}
|
|
8681
|
+
function resolveAnalyticsEnvironment(opts) {
|
|
8682
|
+
const explicit = opts.explicit?.trim();
|
|
8683
|
+
if (explicit) return explicit;
|
|
8684
|
+
const raw = opts.apiBaseUrl ?? "";
|
|
8685
|
+
let host = raw;
|
|
8686
|
+
try {
|
|
8687
|
+
host = new URL(raw).hostname;
|
|
8688
|
+
} catch {
|
|
8689
|
+
}
|
|
8690
|
+
const h = host.toLowerCase();
|
|
8691
|
+
if (h === "" || h.includes("localhost") || h.startsWith("127.")) return "development";
|
|
8692
|
+
if (h.includes("smokebox")) return "smokebox";
|
|
8693
|
+
if (h.includes("sandbox")) return "sandbox";
|
|
8694
|
+
if (h.includes("staging")) return "staging";
|
|
8695
|
+
return "production";
|
|
8696
|
+
}
|
|
8697
|
+
function resetUser() {
|
|
8698
|
+
if (!isReady()) return;
|
|
8699
|
+
try {
|
|
8700
|
+
posthog__default.default.reset();
|
|
8701
|
+
} catch {
|
|
8702
|
+
}
|
|
8703
|
+
}
|
|
8704
|
+
|
|
8705
|
+
// src/analyticsEvents.ts
|
|
8706
|
+
function providerName(state, providerId) {
|
|
8707
|
+
return state.providers.find((p) => p.id === providerId)?.name;
|
|
8708
|
+
}
|
|
8709
|
+
function amountUsd(transfer) {
|
|
8710
|
+
return transfer.amount?.currency === "USD" ? transfer.amount.amount : transfer.amount?.amount;
|
|
8711
|
+
}
|
|
8712
|
+
function sourceChain(transfer) {
|
|
8713
|
+
return transfer.sources?.[0]?.provider?.chainFamily;
|
|
8714
|
+
}
|
|
8715
|
+
function trackAction(action, state, ctx) {
|
|
8716
|
+
const device = ctx.device;
|
|
8717
|
+
switch (action.type) {
|
|
8718
|
+
case "PASSKEY_ACTIVATED":
|
|
8719
|
+
track("auth_succeeded", { method: "passkey", device });
|
|
8720
|
+
break;
|
|
8721
|
+
case "SELECT_PROVIDER":
|
|
8722
|
+
track("wallet_provider_selected", {
|
|
8723
|
+
device,
|
|
8724
|
+
providerId: action.providerId,
|
|
8725
|
+
providerName: providerName(state, action.providerId),
|
|
8726
|
+
transport: device === "desktop" ? "inline" : "deeplink"
|
|
8727
|
+
});
|
|
8728
|
+
break;
|
|
8729
|
+
case "MOBILE_DEEPLINK_READY":
|
|
8730
|
+
track("wallet_deeplink_opened", { device });
|
|
8731
|
+
break;
|
|
8732
|
+
case "SET_SETUP_DEPOSIT_TOKEN":
|
|
8733
|
+
track("token_selected", {
|
|
8734
|
+
device,
|
|
8735
|
+
tokenSymbol: action.symbol,
|
|
8736
|
+
chainName: action.chainName
|
|
8737
|
+
});
|
|
8738
|
+
break;
|
|
8739
|
+
case "FINALIZE_AMOUNT": {
|
|
8740
|
+
const parsed = Number(state.amount);
|
|
8741
|
+
track("amount_entered", {
|
|
8742
|
+
device,
|
|
8743
|
+
amountUsd: Number.isFinite(parsed) ? parsed : void 0
|
|
8744
|
+
});
|
|
8745
|
+
break;
|
|
8746
|
+
}
|
|
8747
|
+
case "PAY_STARTED":
|
|
8748
|
+
track("deposit_clicked", { device });
|
|
8749
|
+
break;
|
|
8750
|
+
case "TRANSFER_CREATED":
|
|
8751
|
+
track("transfer_created", {
|
|
8752
|
+
device,
|
|
8753
|
+
chainFamily: sourceChain(action.transfer)
|
|
8754
|
+
});
|
|
8755
|
+
break;
|
|
8756
|
+
case "TRANSFER_SIGNED":
|
|
8757
|
+
track("transfer_signed", { device });
|
|
8758
|
+
break;
|
|
8759
|
+
case "TRANSFER_COMPLETED":
|
|
8760
|
+
track("transfer_completed", {
|
|
8761
|
+
device,
|
|
8762
|
+
amountUsd: amountUsd(action.transfer),
|
|
8763
|
+
chainFamily: sourceChain(action.transfer)
|
|
8764
|
+
});
|
|
8765
|
+
break;
|
|
8766
|
+
case "CONFIRM_SIGN_SUCCESS":
|
|
8767
|
+
track("confirm_sign_success", { device });
|
|
8768
|
+
break;
|
|
8769
|
+
case "MOBILE_SETUP_COMPLETE":
|
|
8770
|
+
track("wallet_setup_complete", { device });
|
|
8771
|
+
break;
|
|
8772
|
+
case "TRANSFER_FAILED":
|
|
8773
|
+
track("transfer_error", {
|
|
8774
|
+
device,
|
|
8775
|
+
type: "transfer_failed",
|
|
8776
|
+
stage: "processing",
|
|
8777
|
+
message: action.error
|
|
8778
|
+
});
|
|
8779
|
+
break;
|
|
8780
|
+
case "PROCESSING_TIMEOUT":
|
|
8781
|
+
track("transfer_error", {
|
|
8782
|
+
device,
|
|
8783
|
+
type: "processing_timeout",
|
|
8784
|
+
stage: "processing",
|
|
8785
|
+
message: action.error
|
|
8786
|
+
});
|
|
8787
|
+
break;
|
|
8788
|
+
case "SIGN_PAYLOAD_EXPIRED":
|
|
8789
|
+
track("transfer_error", {
|
|
8790
|
+
device,
|
|
8791
|
+
type: "sign_payload_expired",
|
|
8792
|
+
stage: "signing",
|
|
8793
|
+
message: action.error
|
|
8794
|
+
});
|
|
8795
|
+
break;
|
|
8796
|
+
case "PAY_ERROR":
|
|
8797
|
+
track("transfer_error", {
|
|
8798
|
+
device,
|
|
8799
|
+
type: "pay_error",
|
|
8800
|
+
stage: "creation",
|
|
8801
|
+
message: action.error
|
|
8802
|
+
});
|
|
8803
|
+
break;
|
|
8804
|
+
}
|
|
8805
|
+
}
|
|
8806
|
+
function useAnalyticsScreenView(phase, device) {
|
|
8807
|
+
const lastScreenRef = react.useRef(null);
|
|
8808
|
+
const screen = screenForPhase(phase);
|
|
8809
|
+
react.useEffect(() => {
|
|
8810
|
+
if (lastScreenRef.current === screen) return;
|
|
8811
|
+
lastScreenRef.current = screen;
|
|
8812
|
+
track("screen_viewed", { screen, phase: phase.step, device });
|
|
8813
|
+
}, [screen, phase.step, device]);
|
|
8814
|
+
}
|
|
8815
|
+
function usePostHogIdentify() {
|
|
8816
|
+
const { ready, authenticated, user } = reactAuth.usePrivy();
|
|
8817
|
+
const identifiedIdRef = react.useRef(null);
|
|
8818
|
+
react.useEffect(() => {
|
|
8819
|
+
if (!ready) return;
|
|
8820
|
+
if (authenticated && user?.id) {
|
|
8821
|
+
if (identifiedIdRef.current !== user.id) {
|
|
8822
|
+
identifyUser(user.id);
|
|
8823
|
+
identifiedIdRef.current = user.id;
|
|
8824
|
+
}
|
|
8825
|
+
return;
|
|
8826
|
+
}
|
|
8827
|
+
if (identifiedIdRef.current !== null) {
|
|
8828
|
+
resetUser();
|
|
8829
|
+
identifiedIdRef.current = null;
|
|
8830
|
+
}
|
|
8831
|
+
}, [ready, authenticated, user?.id]);
|
|
8832
|
+
}
|
|
8470
8833
|
|
|
8471
8834
|
// src/setupDepositConfirmation.ts
|
|
8472
8835
|
function deriveEffectiveSetupSource(setupDepositToken, setupSelectedSourceOption) {
|
|
@@ -9045,9 +9408,12 @@ function PrimaryButton({
|
|
|
9045
9408
|
}
|
|
9046
9409
|
);
|
|
9047
9410
|
}
|
|
9411
|
+
var BUTTON_MIN_HEIGHT = 60;
|
|
9048
9412
|
var progressButtonStyle = (tokens, paused) => ({
|
|
9049
9413
|
position: "relative",
|
|
9050
9414
|
width: "100%",
|
|
9415
|
+
minHeight: BUTTON_MIN_HEIGHT,
|
|
9416
|
+
boxSizing: "border-box",
|
|
9051
9417
|
padding: "18px 24px",
|
|
9052
9418
|
background: `linear-gradient(180deg, ${tokens.accent}88, ${tokens.accentHover}88)`,
|
|
9053
9419
|
color: tokens.accentText,
|
|
@@ -9084,6 +9450,8 @@ var buttonStyle2 = (tokens, state) => {
|
|
|
9084
9450
|
const gradient = `linear-gradient(180deg, ${tokens.accent}, ${tokens.accentHover})`;
|
|
9085
9451
|
return {
|
|
9086
9452
|
width: "100%",
|
|
9453
|
+
minHeight: BUTTON_MIN_HEIGHT,
|
|
9454
|
+
boxSizing: "border-box",
|
|
9087
9455
|
padding: "18px 24px",
|
|
9088
9456
|
// Layer a faint white film over the gradient on hover rather than using
|
|
9089
9457
|
// `filter: brightness()` — the accent gradient is near-black in the new
|
|
@@ -9331,6 +9699,7 @@ var USDH_LOGO = svgToDataUri(USDH_SVG);
|
|
|
9331
9699
|
var ETH_LOGO = "https://assets.relay.link/icons/currencies/eth.png";
|
|
9332
9700
|
var SOL_LOGO = "https://assets.relay.link/icons/currencies/sol.png";
|
|
9333
9701
|
var MON_LOGO = "https://assets.relay.link/icons/currencies/mon.png";
|
|
9702
|
+
var BNB_LOGO = "https://assets.relay.link/icons/currencies/bnb.png";
|
|
9334
9703
|
var KNOWN_LOGOS = {
|
|
9335
9704
|
metamask: METAMASK_LOGO,
|
|
9336
9705
|
base: BASE_LOGO,
|
|
@@ -9349,7 +9718,8 @@ var TOKEN_LOGOS = {
|
|
|
9349
9718
|
USDH: USDH_LOGO,
|
|
9350
9719
|
ETH: ETH_LOGO,
|
|
9351
9720
|
SOL: SOL_LOGO,
|
|
9352
|
-
MON: MON_LOGO
|
|
9721
|
+
MON: MON_LOGO,
|
|
9722
|
+
BNB: BNB_LOGO
|
|
9353
9723
|
};
|
|
9354
9724
|
var CHAIN_LOGOS = {
|
|
9355
9725
|
base: BASE_CHAIN_LOGO,
|
|
@@ -10008,6 +10378,8 @@ function SourcePill({
|
|
|
10008
10378
|
logo,
|
|
10009
10379
|
name,
|
|
10010
10380
|
balance,
|
|
10381
|
+
nameSlot,
|
|
10382
|
+
balanceSlot,
|
|
10011
10383
|
expanded,
|
|
10012
10384
|
showChevron = true,
|
|
10013
10385
|
icon
|
|
@@ -10015,8 +10387,8 @@ function SourcePill({
|
|
|
10015
10387
|
const { tokens } = useBlinkConfig();
|
|
10016
10388
|
return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: pillStyle2(tokens.bgCardTranslucent), children: [
|
|
10017
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),
|
|
10018
|
-
name && /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelStyle2(tokens.text), children: name }),
|
|
10019
|
-
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 })),
|
|
10020
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(
|
|
10021
10393
|
"path",
|
|
10022
10394
|
{
|
|
@@ -10079,6 +10451,37 @@ var balanceStyle = (color) => ({
|
|
|
10079
10451
|
color,
|
|
10080
10452
|
whiteSpace: "nowrap"
|
|
10081
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
|
+
}
|
|
10082
10485
|
function formatUsdTwoDecimals(value) {
|
|
10083
10486
|
const n = Number(value);
|
|
10084
10487
|
return (Number.isFinite(n) ? n : 0).toLocaleString("en-US", {
|
|
@@ -10779,6 +11182,14 @@ function LockIcon({ size = 24 }) {
|
|
|
10779
11182
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 11V8a4 4 0 1 1 8 0v3", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" })
|
|
10780
11183
|
] });
|
|
10781
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
|
+
}
|
|
10782
11193
|
function FaceIdIcon({ size = 24 }) {
|
|
10783
11194
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
10784
11195
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 9V6a2 2 0 0 1 2-2h3", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }),
|
|
@@ -10792,6 +11203,21 @@ function FaceIdIcon({ size = 24 }) {
|
|
|
10792
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" })
|
|
10793
11204
|
] });
|
|
10794
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
|
+
}
|
|
10795
11221
|
function WalletIcon() {
|
|
10796
11222
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
10797
11223
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -11163,7 +11589,7 @@ function CopyAddressButton({
|
|
|
11163
11589
|
}) {
|
|
11164
11590
|
const { tokens } = useBlinkConfig();
|
|
11165
11591
|
const [hovered, setHovered] = react.useState(false);
|
|
11166
|
-
const short =
|
|
11592
|
+
const short = truncateMiddle(address);
|
|
11167
11593
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11168
11594
|
"button",
|
|
11169
11595
|
{
|
|
@@ -13123,7 +13549,7 @@ var lockBannerTextStyle = (color) => ({
|
|
|
13123
13549
|
function ManualTransferPasskeyScreen({
|
|
13124
13550
|
onCreatePasskey,
|
|
13125
13551
|
onNoThanks,
|
|
13126
|
-
amountUsd,
|
|
13552
|
+
amountUsd: amountUsd2,
|
|
13127
13553
|
loading = false,
|
|
13128
13554
|
error,
|
|
13129
13555
|
onBack,
|
|
@@ -13321,7 +13747,6 @@ var errorBannerStyle6 = (tokens) => ({
|
|
|
13321
13747
|
textAlign: "left",
|
|
13322
13748
|
boxSizing: "border-box"
|
|
13323
13749
|
});
|
|
13324
|
-
var SHIMMER_ROWS = 3;
|
|
13325
13750
|
function LinkTokensScreen({
|
|
13326
13751
|
entries: entries2,
|
|
13327
13752
|
selectedIndex,
|
|
@@ -13334,44 +13759,75 @@ function LinkTokensScreen({
|
|
|
13334
13759
|
approving = false
|
|
13335
13760
|
}) {
|
|
13336
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);
|
|
13337
13766
|
const showShimmer = loading && entries2.length === 0;
|
|
13338
13767
|
const showEmpty = !loading && entries2.length === 0;
|
|
13339
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 };
|
|
13340
13773
|
const approveDisabled = loading || approving || entries2.length === 0 || selectedIndex < 0 || selectedIndex >= entries2.length || !!selected?.notSupported;
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
|
|
13347
|
-
|
|
13348
|
-
|
|
13349
|
-
|
|
13350
|
-
|
|
13351
|
-
|
|
13352
|
-
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
|
|
13357
|
-
|
|
13358
|
-
|
|
13359
|
-
|
|
13360
|
-
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
|
|
13366
|
-
|
|
13367
|
-
|
|
13368
|
-
|
|
13369
|
-
|
|
13370
|
-
|
|
13371
|
-
|
|
13372
|
-
|
|
13373
|
-
|
|
13374
|
-
|
|
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
|
+
}
|
|
13824
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13825
|
+
ScreenLayout,
|
|
13826
|
+
{
|
|
13827
|
+
scrollableBody: false,
|
|
13828
|
+
footer: /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: () => onApprove(currentSelection()), disabled: approveDisabled, loading: approving, children: "Review" }),
|
|
13829
|
+
children: [
|
|
13830
|
+
/* @__PURE__ */ jsxRuntime.jsx(ListScrollbarStyles, { textTertiary: t.textTertiary }),
|
|
13375
13831
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13376
13832
|
ScreenHeader,
|
|
13377
13833
|
{
|
|
@@ -13381,71 +13837,198 @@ function LinkTokensScreen({
|
|
|
13381
13837
|
}
|
|
13382
13838
|
),
|
|
13383
13839
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle10, children: [
|
|
13384
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
13385
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
{
|
|
13389
|
-
"
|
|
13390
|
-
"
|
|
13391
|
-
|
|
13392
|
-
children: [
|
|
13393
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerCircleStyle(t.bgInput, t.border) }),
|
|
13394
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: shimmerInfoStyle, children: [
|
|
13395
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerLineStyle(72, t.bgInput, t.border) }),
|
|
13396
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerLineStyle(48, t.bgInput, t.border) })
|
|
13397
|
-
] }),
|
|
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) }),
|
|
13398
13848
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerLineStyle(56, t.bgInput, t.border) })
|
|
13399
|
-
]
|
|
13400
|
-
}
|
|
13401
|
-
|
|
13402
|
-
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
|
|
13414
|
-
|
|
13415
|
-
|
|
13416
|
-
|
|
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
|
+
] })
|
|
13417
13926
|
] }),
|
|
13418
|
-
|
|
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
|
+
] })
|
|
13419
13951
|
] })
|
|
13420
13952
|
]
|
|
13421
13953
|
}
|
|
13422
13954
|
);
|
|
13423
13955
|
}
|
|
13424
|
-
function
|
|
13425
|
-
|
|
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() }) });
|
|
13426
13963
|
}
|
|
13427
|
-
function
|
|
13428
|
-
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
|
|
13433
|
-
|
|
13434
|
-
|
|
13435
|
-
|
|
13436
|
-
|
|
13437
|
-
|
|
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
|
+
` });
|
|
13438
14002
|
}
|
|
13439
14003
|
var contentStyle10 = {
|
|
13440
14004
|
display: "flex",
|
|
13441
14005
|
flexDirection: "column",
|
|
13442
|
-
alignItems: "center",
|
|
13443
|
-
gap: 16,
|
|
13444
|
-
paddingTop: 8,
|
|
13445
14006
|
flex: 1,
|
|
13446
14007
|
minHeight: 0,
|
|
13447
14008
|
width: "100%"
|
|
13448
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
|
+
};
|
|
13449
14032
|
var wordmarkImgStyle3 = {
|
|
13450
14033
|
height: 22,
|
|
13451
14034
|
width: "auto",
|
|
@@ -13453,9 +14036,9 @@ var wordmarkImgStyle3 = {
|
|
|
13453
14036
|
pointerEvents: "none",
|
|
13454
14037
|
userSelect: "none"
|
|
13455
14038
|
};
|
|
13456
|
-
var headingStyle7 = (color) => ({
|
|
14039
|
+
var headingStyle7 = (color, weight) => ({
|
|
13457
14040
|
fontSize: 24,
|
|
13458
|
-
fontWeight:
|
|
14041
|
+
fontWeight: weight,
|
|
13459
14042
|
lineHeight: "normal",
|
|
13460
14043
|
letterSpacing: 0,
|
|
13461
14044
|
color,
|
|
@@ -13475,68 +14058,155 @@ var listCardStyle = (bg) => ({
|
|
|
13475
14058
|
minHeight: 0,
|
|
13476
14059
|
overflowY: "auto"
|
|
13477
14060
|
});
|
|
13478
|
-
var
|
|
14061
|
+
var pillStyle3 = (bg) => ({
|
|
13479
14062
|
display: "flex",
|
|
13480
14063
|
alignItems: "center",
|
|
13481
14064
|
gap: 16,
|
|
13482
|
-
padding: "12px 16px 12px 12px",
|
|
13483
|
-
background: "transparent",
|
|
13484
|
-
border: "none",
|
|
13485
|
-
borderRadius: 16,
|
|
13486
14065
|
width: "100%",
|
|
13487
|
-
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
|
|
13488
14084
|
};
|
|
13489
|
-
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",
|
|
13490
14113
|
width: 40,
|
|
13491
14114
|
height: 40,
|
|
13492
|
-
borderRadius: "50%",
|
|
13493
14115
|
flexShrink: 0,
|
|
13494
|
-
|
|
13495
|
-
|
|
13496
|
-
|
|
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
|
|
13497
14137
|
});
|
|
13498
|
-
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",
|
|
13499
14148
|
display: "flex",
|
|
13500
|
-
|
|
13501
|
-
gap:
|
|
13502
|
-
flex: 1,
|
|
14149
|
+
alignItems: "center",
|
|
14150
|
+
gap: 8,
|
|
13503
14151
|
minWidth: 0
|
|
13504
14152
|
};
|
|
13505
|
-
var
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
|
|
13509
|
-
|
|
13510
|
-
|
|
13511
|
-
|
|
14153
|
+
var limitAmountStyle = (color, weight) => ({
|
|
14154
|
+
display: "inline-flex",
|
|
14155
|
+
alignItems: "baseline",
|
|
14156
|
+
gap: 1,
|
|
14157
|
+
color,
|
|
14158
|
+
fontSize: "1rem",
|
|
14159
|
+
fontWeight: weight
|
|
13512
14160
|
});
|
|
13513
|
-
var
|
|
13514
|
-
|
|
13515
|
-
|
|
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",
|
|
13516
14169
|
color,
|
|
13517
|
-
|
|
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
|
|
13518
14180
|
});
|
|
13519
|
-
var
|
|
13520
|
-
|
|
13521
|
-
|
|
13522
|
-
|
|
13523
|
-
|
|
13524
|
-
|
|
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) => ({
|
|
13525
14194
|
display: "flex",
|
|
13526
14195
|
flexDirection: "column",
|
|
13527
14196
|
gap: 16,
|
|
13528
|
-
width: "100%"
|
|
13529
|
-
|
|
13530
|
-
|
|
14197
|
+
width: "100%",
|
|
14198
|
+
boxSizing: "border-box",
|
|
14199
|
+
padding: 16,
|
|
14200
|
+
borderRadius: 20,
|
|
14201
|
+
background: bg
|
|
14202
|
+
});
|
|
14203
|
+
var infoRowStyle2 = {
|
|
13531
14204
|
display: "flex",
|
|
13532
14205
|
alignItems: "flex-start",
|
|
13533
|
-
gap:
|
|
13534
|
-
|
|
13535
|
-
borderRadius: 16,
|
|
13536
|
-
width: "100%",
|
|
13537
|
-
boxSizing: "border-box"
|
|
14206
|
+
gap: 14,
|
|
14207
|
+
width: "100%"
|
|
13538
14208
|
};
|
|
13539
|
-
var
|
|
14209
|
+
var infoIconStyle = (color) => ({
|
|
13540
14210
|
flexShrink: 0,
|
|
13541
14211
|
width: 24,
|
|
13542
14212
|
height: 24,
|
|
@@ -13545,14 +14215,60 @@ var lockIconWrapStyle2 = (color) => ({
|
|
|
13545
14215
|
justifyContent: "center",
|
|
13546
14216
|
color
|
|
13547
14217
|
});
|
|
13548
|
-
var
|
|
13549
|
-
|
|
13550
|
-
|
|
13551
|
-
|
|
13552
|
-
|
|
13553
|
-
|
|
13554
|
-
|
|
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,
|
|
14250
|
+
flex: 1,
|
|
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"
|
|
13555
14266
|
});
|
|
14267
|
+
var bannerSlotStyle = {
|
|
14268
|
+
display: "flex",
|
|
14269
|
+
justifyContent: "center",
|
|
14270
|
+
width: "100%"
|
|
14271
|
+
};
|
|
13556
14272
|
function DepositCompleteScreen({ amount }) {
|
|
13557
14273
|
const { tokens } = useBlinkConfig();
|
|
13558
14274
|
return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
|
|
@@ -13614,6 +14330,7 @@ function SelectDepositSourceScreen({
|
|
|
13614
14330
|
tokenOptions,
|
|
13615
14331
|
selectedTokenSymbol,
|
|
13616
14332
|
selectedChainName,
|
|
14333
|
+
balancesLoading,
|
|
13617
14334
|
selectedWalletId,
|
|
13618
14335
|
onPickToken,
|
|
13619
14336
|
useDeeplink,
|
|
@@ -13630,8 +14347,9 @@ function SelectDepositSourceScreen({
|
|
|
13630
14347
|
const [pendingMobileSelection, setPendingMobileSelection] = react.useState(null);
|
|
13631
14348
|
const [preparedAuthorization, setPreparedAuthorization] = react.useState(null);
|
|
13632
14349
|
const [preparingAuthorization, setPreparingAuthorization] = react.useState(false);
|
|
14350
|
+
const isAuthorizedOption = (opt) => !opt.status || opt.status === "AUTHORIZED";
|
|
13633
14351
|
const selectableOptions = tokenOptions.filter(
|
|
13634
|
-
(opt) => opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minTransferAmountUsd)
|
|
14352
|
+
(opt) => isAuthorizedOption(opt) || opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minTransferAmountUsd)
|
|
13635
14353
|
);
|
|
13636
14354
|
const authorized = selectableOptions.filter(
|
|
13637
14355
|
(opt) => !opt.status || opt.status === "AUTHORIZED"
|
|
@@ -13785,6 +14503,30 @@ function SelectDepositSourceScreen({
|
|
|
13785
14503
|
"data-testid": "select-deposit-source-scroll-content",
|
|
13786
14504
|
style: contentStyle12,
|
|
13787
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
|
+
)) }) }),
|
|
13788
14530
|
orderedAccounts.map((account) => {
|
|
13789
14531
|
const authRows = authorized.filter((opt) => rowMatchesSection(opt, account));
|
|
13790
14532
|
const reqRows = requiresAuth.filter((opt) => rowMatchesSection(opt, account)).sort((a, b) => Number(!!a.notSupported) - Number(!!b.notSupported));
|
|
@@ -13999,6 +14741,7 @@ function ShimmerBlock({
|
|
|
13999
14741
|
);
|
|
14000
14742
|
}
|
|
14001
14743
|
function DepositScreen({
|
|
14744
|
+
balancesLoading,
|
|
14002
14745
|
availableBalance,
|
|
14003
14746
|
remainingLimit,
|
|
14004
14747
|
initialAmount,
|
|
@@ -14052,6 +14795,10 @@ function DepositScreen({
|
|
|
14052
14795
|
const showMobileKeypad = isEntryMode && !isDesktop;
|
|
14053
14796
|
const [keypadOpen, setKeypadOpen] = react.useState(showMobileKeypad);
|
|
14054
14797
|
const [tokenPickerOpen, setTokenPickerOpen] = react.useState(false);
|
|
14798
|
+
const hasLoadedQuoteRef = react.useRef(false);
|
|
14799
|
+
if (!quoteLoading) {
|
|
14800
|
+
hasLoadedQuoteRef.current = true;
|
|
14801
|
+
}
|
|
14055
14802
|
const selectableTokenOptions = tokenOptions?.filter(
|
|
14056
14803
|
(opt) => opt.balance == null || isSelectableDepositSourceAmountUsd(opt.balance, minDepositFloor)
|
|
14057
14804
|
) ?? [];
|
|
@@ -14069,11 +14816,11 @@ function DepositScreen({
|
|
|
14069
14816
|
}, []);
|
|
14070
14817
|
const selectedAccount = accounts?.find((a) => a.id === selectedAccountId);
|
|
14071
14818
|
const selectedProviderName = selectedAccount?.name ?? "Wallet";
|
|
14072
|
-
const isLowBalance = availableBalance < minDepositFloor;
|
|
14073
|
-
const insufficientFunds = availableBalance < validationAmount;
|
|
14819
|
+
const isLowBalance = !balancesLoading && availableBalance < minDepositFloor;
|
|
14820
|
+
const insufficientFunds = !balancesLoading && availableBalance < validationAmount;
|
|
14074
14821
|
const needsAuthorization = selectedTokenStatus != null && selectedTokenStatus !== "AUTHORIZED" && !insufficientFunds && !isLowBalance;
|
|
14075
14822
|
const exceedsLimit = remainingLimit != null && validationAmount > remainingLimit && !isLowBalance && !needsAuthorization;
|
|
14076
|
-
const canDeposit = validationAmount >= minDepositFloor && !exceedsLimit && !isLowBalance && !insufficientFunds && !needsAuthorization && !processing;
|
|
14823
|
+
const canDeposit = validationAmount >= minDepositFloor && !balancesLoading && !exceedsLimit && !isLowBalance && !insufficientFunds && !needsAuthorization && !processing;
|
|
14077
14824
|
const hasAccountPill = !!accounts && accounts.length > 0;
|
|
14078
14825
|
const pillClickable = canOpenInlineSheet || !!onSelectToken;
|
|
14079
14826
|
const formattedBalance = selectedTokenSymbol != null ? `$${formatUsdTwoDecimals2(availableBalance)}` : void 0;
|
|
@@ -14096,6 +14843,7 @@ function DepositScreen({
|
|
|
14096
14843
|
accounts: depositSourceAccounts,
|
|
14097
14844
|
selectedAccountId: selectedAccountId ?? null,
|
|
14098
14845
|
tokenOptions: selectableTokenOptions,
|
|
14846
|
+
balancesLoading,
|
|
14099
14847
|
selectedTokenSymbol,
|
|
14100
14848
|
selectedChainName,
|
|
14101
14849
|
selectedWalletId: selectedWalletId ?? null,
|
|
@@ -14117,7 +14865,7 @@ function DepositScreen({
|
|
|
14117
14865
|
const mobileEntryWithKeypad = isEntryMode && !isDesktop && keypadOpen;
|
|
14118
14866
|
const showLiveMobileHero = isEntryMode && !isDesktop && keypadOpen;
|
|
14119
14867
|
const showDesktopInputHero = isEntryMode && isDesktop;
|
|
14120
|
-
const
|
|
14868
|
+
const showFeeRow = validationAmount > 0;
|
|
14121
14869
|
let primaryButton;
|
|
14122
14870
|
if (mobileEntryWithKeypad) {
|
|
14123
14871
|
const canConfirmEntry = canContinue(amountEntryValue ?? "");
|
|
@@ -14247,7 +14995,32 @@ function DepositScreen({
|
|
|
14247
14995
|
"aria-label": tokenAriaLabel,
|
|
14248
14996
|
"aria-expanded": canOpenInlineSheet ? tokenPickerOpen : void 0,
|
|
14249
14997
|
"aria-haspopup": canOpenInlineSheet ? "listbox" : void 0,
|
|
14250
|
-
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(
|
|
14251
15024
|
SourcePill,
|
|
14252
15025
|
{
|
|
14253
15026
|
icon: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -14267,7 +15040,7 @@ function DepositScreen({
|
|
|
14267
15040
|
)
|
|
14268
15041
|
}
|
|
14269
15042
|
),
|
|
14270
|
-
|
|
15043
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": "deposit-fee-row", style: redesignFeeRowStyle, children: showFeeRow && (quoteLoading && !hasLoadedQuoteRef.current ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
14271
15044
|
"div",
|
|
14272
15045
|
{
|
|
14273
15046
|
"data-testid": "deposit-fee-shimmer",
|
|
@@ -14285,7 +15058,7 @@ function DepositScreen({
|
|
|
14285
15058
|
}
|
|
14286
15059
|
)
|
|
14287
15060
|
}
|
|
14288
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" }) })
|
|
15061
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" })) })
|
|
14289
15062
|
] }),
|
|
14290
15063
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: bannerSlotStyle2, children: mobileEntryWithKeypad ? null : showLowFunds ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
14291
15064
|
NotificationBanner,
|
|
@@ -15662,7 +16435,7 @@ function OpenWalletScreen({
|
|
|
15662
16435
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15663
16436
|
ScreenLayout,
|
|
15664
16437
|
{
|
|
15665
|
-
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style:
|
|
16438
|
+
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: footerStackStyle4, children: [
|
|
15666
16439
|
error && /* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: error }),
|
|
15667
16440
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
15668
16441
|
SecondaryButton,
|
|
@@ -15709,7 +16482,7 @@ function OpenWalletScreen({
|
|
|
15709
16482
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15710
16483
|
ScreenLayout,
|
|
15711
16484
|
{
|
|
15712
|
-
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style:
|
|
16485
|
+
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: footerStackStyle4, children: [
|
|
15713
16486
|
error && /* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: error }),
|
|
15714
16487
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
15715
16488
|
SecondaryButton,
|
|
@@ -15793,7 +16566,7 @@ var primaryClusterStyle = {
|
|
|
15793
16566
|
textAlign: "center",
|
|
15794
16567
|
gap: 12
|
|
15795
16568
|
};
|
|
15796
|
-
var
|
|
16569
|
+
var footerStackStyle4 = {
|
|
15797
16570
|
display: "flex",
|
|
15798
16571
|
flexDirection: "column",
|
|
15799
16572
|
gap: 8
|
|
@@ -15921,6 +16694,12 @@ function ApprovingInWalletScreen({
|
|
|
15921
16694
|
chainName,
|
|
15922
16695
|
step,
|
|
15923
16696
|
complete,
|
|
16697
|
+
signing,
|
|
16698
|
+
spendingLimitLabel,
|
|
16699
|
+
destinationAddress,
|
|
16700
|
+
tokenLogoUri,
|
|
16701
|
+
awaitingApproval,
|
|
16702
|
+
onApprove,
|
|
15924
16703
|
error,
|
|
15925
16704
|
onRetry,
|
|
15926
16705
|
onBack,
|
|
@@ -15930,10 +16709,11 @@ function ApprovingInWalletScreen({
|
|
|
15930
16709
|
onOpenWallet
|
|
15931
16710
|
}) {
|
|
15932
16711
|
const { tokens: t } = useBlinkConfig();
|
|
15933
|
-
const showDelayedRetry = useDelayedRetry(error == null && onRetry != null);
|
|
16712
|
+
const showDelayedRetry = useDelayedRetry(!awaitingApproval && error == null && onRetry != null);
|
|
15934
16713
|
const retryVisible = onRetry != null && (error != null || showDelayedRetry);
|
|
15935
16714
|
const token = tokenSymbol ?? "token";
|
|
15936
|
-
const
|
|
16715
|
+
const chain = chainName ?? "chain";
|
|
16716
|
+
const steps = buildSteps(step, tokenSymbol, complete, signing);
|
|
15937
16717
|
const foregroundNavigation = foregroundDeeplink ? classifyWalletDeeplinkNavigation(foregroundDeeplink) : null;
|
|
15938
16718
|
const foregroundWithJs = foregroundNavigation ? shouldOpenWithJavaScript(foregroundNavigation) : false;
|
|
15939
16719
|
const showOpenWalletButton = !!foregroundDeeplink && onOpenWallet != null;
|
|
@@ -15941,11 +16721,7 @@ function ApprovingInWalletScreen({
|
|
|
15941
16721
|
ScreenLayout,
|
|
15942
16722
|
{
|
|
15943
16723
|
scrollableBody: false,
|
|
15944
|
-
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style:
|
|
15945
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: lockBannerStyle3, children: [
|
|
15946
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: lockIconWrapStyle3(t.text), children: /* @__PURE__ */ jsxRuntime.jsx(LockIcon4, {}) }),
|
|
15947
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: lockBannerTextStyle3(t.text), children: "Your passkey is required each time you deposit. Funds cannot move without your approval." })
|
|
15948
|
-
] }),
|
|
16724
|
+
footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: footerStackStyle5, children: [
|
|
15949
16725
|
showOpenWalletButton && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15950
16726
|
SecondaryButton,
|
|
15951
16727
|
{
|
|
@@ -15962,7 +16738,7 @@ function ApprovingInWalletScreen({
|
|
|
15962
16738
|
retryVisible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: retryStackStyle, children: [
|
|
15963
16739
|
error && /* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: error }),
|
|
15964
16740
|
/* @__PURE__ */ jsxRuntime.jsx(OutlineButton, { onClick: onRetry, children: "Retry" })
|
|
15965
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { spinnerOnly: true })
|
|
16741
|
+
] }) : awaitingApproval ? /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onApprove, children: "Approve" }) : /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { spinnerOnly: true })
|
|
15966
16742
|
] }),
|
|
15967
16743
|
children: [
|
|
15968
16744
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -15974,7 +16750,39 @@ function ApprovingInWalletScreen({
|
|
|
15974
16750
|
}
|
|
15975
16751
|
),
|
|
15976
16752
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle16, children: [
|
|
15977
|
-
/* @__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
|
+
] }),
|
|
15978
16786
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: cardStyle2(t.bgRecessed, t.radiusLg), children: [
|
|
15979
16787
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: SPIN_KEYFRAMES_CSS2 }),
|
|
15980
16788
|
steps.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(ChecklistRow, { step: s }, i))
|
|
@@ -15984,6 +16792,32 @@ function ApprovingInWalletScreen({
|
|
|
15984
16792
|
}
|
|
15985
16793
|
);
|
|
15986
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
|
+
}
|
|
15987
16821
|
function ChecklistRow({ step }) {
|
|
15988
16822
|
const { tokens: t } = useBlinkConfig();
|
|
15989
16823
|
const isComplete = step.status === "complete";
|
|
@@ -15996,7 +16830,7 @@ function ChecklistRow({ step }) {
|
|
|
15996
16830
|
fill: t.accentText
|
|
15997
16831
|
}
|
|
15998
16832
|
) }) }) : isActive ? /* @__PURE__ */ jsxRuntime.jsx(RowSpinner, { color: t.accent }) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: pendingBadgeStyle(t.bgRecessed) }),
|
|
15999
|
-
/* @__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 })
|
|
16000
16834
|
] });
|
|
16001
16835
|
}
|
|
16002
16836
|
function RowSpinner({ color }) {
|
|
@@ -16031,22 +16865,28 @@ function RowSpinner({ color }) {
|
|
|
16031
16865
|
}
|
|
16032
16866
|
);
|
|
16033
16867
|
}
|
|
16034
|
-
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) {
|
|
16035
16875
|
const token = tokenSymbol ?? "token";
|
|
16036
|
-
const
|
|
16037
|
-
const
|
|
16876
|
+
const passkeyLabel = "Assign spending right to your passkey";
|
|
16877
|
+
const activeStatus = signing ? "active" : "pending";
|
|
16038
16878
|
if (step === "spl") {
|
|
16039
|
-
return [{ label: passkeyLabel, status: complete ? "complete" :
|
|
16879
|
+
return [{ label: passkeyLabel, status: complete ? "complete" : activeStatus }];
|
|
16040
16880
|
}
|
|
16041
|
-
const approveLabel = `Approve ${token}
|
|
16881
|
+
const approveLabel = `Approve ${token} for spending via Permit2`;
|
|
16042
16882
|
return [
|
|
16043
16883
|
{
|
|
16044
16884
|
label: approveLabel,
|
|
16045
|
-
status: complete || step !== "approve" ? "complete" :
|
|
16885
|
+
status: complete || step !== "approve" ? "complete" : activeStatus
|
|
16046
16886
|
},
|
|
16047
16887
|
{
|
|
16048
16888
|
label: passkeyLabel,
|
|
16049
|
-
status: complete ? "complete" : step === "approve" ? "pending" :
|
|
16889
|
+
status: complete ? "complete" : step === "approve" ? "pending" : activeStatus
|
|
16050
16890
|
}
|
|
16051
16891
|
];
|
|
16052
16892
|
}
|
|
@@ -16062,18 +16902,6 @@ function useDelayedRetry(enabled) {
|
|
|
16062
16902
|
}, [enabled]);
|
|
16063
16903
|
return visible;
|
|
16064
16904
|
}
|
|
16065
|
-
function LockIcon4() {
|
|
16066
|
-
return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16067
|
-
"path",
|
|
16068
|
-
{
|
|
16069
|
-
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",
|
|
16070
|
-
stroke: "currentColor",
|
|
16071
|
-
strokeWidth: "2",
|
|
16072
|
-
strokeLinecap: "round",
|
|
16073
|
-
strokeLinejoin: "round"
|
|
16074
|
-
}
|
|
16075
|
-
) });
|
|
16076
|
-
}
|
|
16077
16905
|
var wordmarkImgStyle5 = {
|
|
16078
16906
|
height: 22,
|
|
16079
16907
|
width: "auto",
|
|
@@ -16091,15 +16919,84 @@ var contentStyle16 = {
|
|
|
16091
16919
|
minHeight: 0,
|
|
16092
16920
|
width: "100%"
|
|
16093
16921
|
};
|
|
16094
|
-
var headingStyle13 = (color) => ({
|
|
16922
|
+
var headingStyle13 = (color, fontWeight) => ({
|
|
16095
16923
|
fontSize: 24,
|
|
16096
|
-
fontWeight
|
|
16924
|
+
fontWeight,
|
|
16097
16925
|
lineHeight: "normal",
|
|
16098
16926
|
letterSpacing: 0,
|
|
16099
16927
|
color,
|
|
16100
|
-
margin: 0,
|
|
16928
|
+
margin: "8px 0 8px",
|
|
16101
16929
|
textAlign: "center"
|
|
16102
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
|
+
};
|
|
16103
17000
|
var cardStyle2 = (bg, radius) => ({
|
|
16104
17001
|
background: bg,
|
|
16105
17002
|
borderRadius: radius,
|
|
@@ -16107,7 +17004,9 @@ var cardStyle2 = (bg, radius) => ({
|
|
|
16107
17004
|
width: "100%",
|
|
16108
17005
|
boxSizing: "border-box",
|
|
16109
17006
|
display: "flex",
|
|
17007
|
+
flex: 1,
|
|
16110
17008
|
flexDirection: "column",
|
|
17009
|
+
justifyContent: "center",
|
|
16111
17010
|
gap: 4
|
|
16112
17011
|
});
|
|
16113
17012
|
var rowStyle6 = {
|
|
@@ -16115,10 +17014,25 @@ var rowStyle6 = {
|
|
|
16115
17014
|
alignItems: "center",
|
|
16116
17015
|
gap: 16,
|
|
16117
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,
|
|
16118
17021
|
borderRadius: 16,
|
|
16119
17022
|
width: "100%",
|
|
16120
17023
|
boxSizing: "border-box"
|
|
16121
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
|
+
});
|
|
16122
17036
|
var completeBadgeStyle = (color) => ({
|
|
16123
17037
|
width: 24,
|
|
16124
17038
|
height: 24,
|
|
@@ -16136,13 +17050,13 @@ var pendingBadgeStyle = (color) => ({
|
|
|
16136
17050
|
background: color,
|
|
16137
17051
|
flexShrink: 0
|
|
16138
17052
|
});
|
|
16139
|
-
var labelStyle7 = (color) => ({
|
|
17053
|
+
var labelStyle7 = (color, fontWeight) => ({
|
|
16140
17054
|
fontSize: "1rem",
|
|
16141
|
-
fontWeight
|
|
17055
|
+
fontWeight,
|
|
16142
17056
|
lineHeight: 1.25,
|
|
16143
17057
|
color
|
|
16144
17058
|
});
|
|
16145
|
-
var
|
|
17059
|
+
var footerStackStyle5 = {
|
|
16146
17060
|
display: "flex",
|
|
16147
17061
|
flexDirection: "column",
|
|
16148
17062
|
gap: 16,
|
|
@@ -16154,32 +17068,6 @@ var retryStackStyle = {
|
|
|
16154
17068
|
gap: 8,
|
|
16155
17069
|
width: "100%"
|
|
16156
17070
|
};
|
|
16157
|
-
var lockBannerStyle3 = {
|
|
16158
|
-
display: "flex",
|
|
16159
|
-
alignItems: "flex-start",
|
|
16160
|
-
gap: 16,
|
|
16161
|
-
padding: "12px 16px 12px 12px",
|
|
16162
|
-
borderRadius: 16,
|
|
16163
|
-
width: "100%",
|
|
16164
|
-
boxSizing: "border-box"
|
|
16165
|
-
};
|
|
16166
|
-
var lockIconWrapStyle3 = (color) => ({
|
|
16167
|
-
flexShrink: 0,
|
|
16168
|
-
width: 24,
|
|
16169
|
-
height: 24,
|
|
16170
|
-
display: "inline-flex",
|
|
16171
|
-
alignItems: "center",
|
|
16172
|
-
justifyContent: "center",
|
|
16173
|
-
color
|
|
16174
|
-
});
|
|
16175
|
-
var lockBannerTextStyle3 = (color) => ({
|
|
16176
|
-
margin: 0,
|
|
16177
|
-
flex: 1,
|
|
16178
|
-
fontSize: 12,
|
|
16179
|
-
fontWeight: 500,
|
|
16180
|
-
lineHeight: "normal",
|
|
16181
|
-
color
|
|
16182
|
-
});
|
|
16183
17071
|
function ConfirmSignScreen({
|
|
16184
17072
|
walletName,
|
|
16185
17073
|
chainFamily,
|
|
@@ -16305,7 +17193,7 @@ function TokenPickerScreen({
|
|
|
16305
17193
|
}
|
|
16306
17194
|
const entries2 = [];
|
|
16307
17195
|
for (const wallet of account.wallets) {
|
|
16308
|
-
for (const source of wallet.sources) {
|
|
17196
|
+
for (const source of wallet.sources ?? []) {
|
|
16309
17197
|
const visibleBalance = source.balance.available.amount;
|
|
16310
17198
|
if (!isSelectableDepositSourceAmountUsd(visibleBalance, minDepositFloor)) continue;
|
|
16311
17199
|
entries2.push({
|
|
@@ -17146,22 +18034,45 @@ function buildOpenWalletScreenProps({
|
|
|
17146
18034
|
function isApprovingInWalletAction(actionType) {
|
|
17147
18035
|
return actionType === "APPROVE_PERMIT2" || actionType === "SIGN_PERMIT2" || actionType === "APPROVE_SPL";
|
|
17148
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
|
+
}
|
|
17149
18047
|
function buildApprovingInWalletScreenProps({
|
|
17150
18048
|
flow,
|
|
17151
18049
|
remote,
|
|
18050
|
+
derived,
|
|
17152
18051
|
handlers
|
|
17153
18052
|
}) {
|
|
17154
18053
|
const { state } = flow;
|
|
17155
18054
|
const setupToken = state.setupDepositToken;
|
|
17156
|
-
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");
|
|
17157
18060
|
const isSolana = setupToken != null && state.chains.find((c) => c.name === setupToken.chainName)?.chainFamily === "svm";
|
|
17158
18061
|
const step = actionType === "SIGN_PERMIT2" ? "sign" : actionType === "APPROVE_SPL" ? "spl" : actionType == null && isSolana ? "spl" : "approve";
|
|
17159
18062
|
const complete = remote.authExecutorCompleted && actionType == null && !remote.authExecutorExecuting;
|
|
18063
|
+
const awaitingApproval = remote.authExecutorAwaitingApproval ?? false;
|
|
18064
|
+
const signing = isApprovingInWalletAction(actionType) && !awaitingApproval && !complete;
|
|
17160
18065
|
return {
|
|
17161
18066
|
tokenSymbol: setupToken?.symbol ?? null,
|
|
17162
18067
|
chainName: setupToken?.chainName ?? null,
|
|
17163
18068
|
step,
|
|
17164
18069
|
complete,
|
|
18070
|
+
signing,
|
|
18071
|
+
spendingLimitLabel,
|
|
18072
|
+
destinationAddress,
|
|
18073
|
+
tokenLogoUri: derived.selectedSource?.token.logoURI ?? null,
|
|
18074
|
+
awaitingApproval,
|
|
18075
|
+
onApprove: handlers.onApprove,
|
|
17165
18076
|
error: flow.state.error || remote.authExecutorError,
|
|
17166
18077
|
onRetry: handlers.onRetryAuthorization,
|
|
17167
18078
|
onLogout: flow.isDesktop ? handlers.onLogout : void 0,
|
|
@@ -17180,7 +18091,7 @@ function buildLinkTokensScreenProps({
|
|
|
17180
18091
|
handlers
|
|
17181
18092
|
}) {
|
|
17182
18093
|
const sourceOptions = remote.pendingSelectSource?.metadata?.options ?? [];
|
|
17183
|
-
const
|
|
18094
|
+
const supportedRows = derived.selectSourceChoices.flatMap(
|
|
17184
18095
|
(chain) => chain.tokens.map((t) => {
|
|
17185
18096
|
const rawOption = sourceOptions.find(
|
|
17186
18097
|
(option) => option.chainName === chain.chainName && option.tokenSymbol === t.tokenSymbol
|
|
@@ -17200,6 +18111,10 @@ function buildLinkTokensScreenProps({
|
|
|
17200
18111
|
};
|
|
17201
18112
|
})
|
|
17202
18113
|
);
|
|
18114
|
+
const rowContexts = [
|
|
18115
|
+
...supportedRows.filter((row) => isVisibleUsdAmountAtTwoDecimals(row.balanceUsd)),
|
|
18116
|
+
...supportedRows.filter((row) => !isVisibleUsdAmountAtTwoDecimals(row.balanceUsd))
|
|
18117
|
+
];
|
|
17203
18118
|
rowContexts.push(
|
|
17204
18119
|
...derived.selectSourceUnsupportedChoices.flatMap(
|
|
17205
18120
|
(chain) => chain.tokens.map((t) => ({
|
|
@@ -17221,6 +18136,17 @@ function buildLinkTokensScreenProps({
|
|
|
17221
18136
|
balanceLabel: `${formatNativeAmount(native.amount)} ${native.tokenSymbol}`
|
|
17222
18137
|
}))
|
|
17223
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
|
+
}
|
|
17224
18150
|
const entries2 = rowContexts.map(
|
|
17225
18151
|
({ symbol, chainName, balanceUsd, notSupported, tokenLogoUri, balanceLabel }) => ({
|
|
17226
18152
|
tokenSymbol: symbol,
|
|
@@ -17233,8 +18159,6 @@ function buildLinkTokensScreenProps({
|
|
|
17233
18159
|
})
|
|
17234
18160
|
);
|
|
17235
18161
|
const firstSupportedIndex = rowContexts.findIndex((row) => !row.notSupported);
|
|
17236
|
-
const selectedTokenSymbol = flow.state.setupDepositToken?.symbol ?? derived.selectSourceRecommended?.tokenSymbol;
|
|
17237
|
-
const selectedChainName = flow.state.setupDepositToken?.chainName ?? derived.selectSourceRecommended?.chainName;
|
|
17238
18162
|
const selectedIndex = (() => {
|
|
17239
18163
|
if (!selectedTokenSymbol || !selectedChainName) {
|
|
17240
18164
|
return firstSupportedIndex;
|
|
@@ -17326,9 +18250,10 @@ function buildDepositScreenProps({
|
|
|
17326
18250
|
const registryChainIds = derived.walletConnectChainIdsByAccount[account.id] ?? null;
|
|
17327
18251
|
const approvedChainIds = account.walletConnect?.approvedChainIds ?? null;
|
|
17328
18252
|
for (const wallet of account.wallets) {
|
|
17329
|
-
for (const source of wallet.sources) {
|
|
18253
|
+
for (const source of wallet.sources ?? []) {
|
|
17330
18254
|
const balance = source.balance.available.amount;
|
|
17331
|
-
|
|
18255
|
+
const isAuthorized = source.token.status === "AUTHORIZED";
|
|
18256
|
+
if (!isAuthorized && !isSelectableDepositSourceAmountUsd(balance, minDepositFloor)) continue;
|
|
17332
18257
|
const chain = flow.state.chains.find((c) => c.name === wallet.chain.name);
|
|
17333
18258
|
const requiresAuth = source.token.status != null && source.token.status !== "AUTHORIZED";
|
|
17334
18259
|
const notSupported = requiresAuth && chain?.commonId != null && registryChainIds != null && !registryChainIds.includes(chain.commonId) && !(approvedChainIds?.includes(chain.commonId) ?? false);
|
|
@@ -17350,7 +18275,10 @@ function buildDepositScreenProps({
|
|
|
17350
18275
|
}
|
|
17351
18276
|
return {
|
|
17352
18277
|
merchantName,
|
|
17353
|
-
|
|
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,
|
|
17354
18282
|
remainingLimit: selectedSource != null ? selectedSource.remainingAllowance ?? null : selectedAccount?.remainingAllowance ?? null,
|
|
17355
18283
|
tokenCount,
|
|
17356
18284
|
initialAmount: parsedAmt,
|
|
@@ -17541,6 +18469,28 @@ function StepRendererContent({
|
|
|
17541
18469
|
screen
|
|
17542
18470
|
}) {
|
|
17543
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]);
|
|
17544
18494
|
switch (screen) {
|
|
17545
18495
|
case "loading":
|
|
17546
18496
|
return /* @__PURE__ */ jsxRuntime.jsx(BlinkLoadingScreen, {});
|
|
@@ -17569,11 +18519,9 @@ function StepRendererContent({
|
|
|
17569
18519
|
case "wallet-picker":
|
|
17570
18520
|
return /* @__PURE__ */ jsxRuntime.jsx(WalletPickerScreen, { ...buildWalletPickerScreenProps(input) });
|
|
17571
18521
|
case "open-wallet": {
|
|
17572
|
-
const
|
|
17573
|
-
const inSigningAction = isApprovingInWalletAction(currentActionType);
|
|
17574
|
-
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;
|
|
17575
18523
|
const settlingPostSign = input.flow.state.linkSettling;
|
|
17576
|
-
if (
|
|
18524
|
+
if (pastSourceSelection || settlingPostSign) {
|
|
17577
18525
|
return /* @__PURE__ */ jsxRuntime.jsx(ApprovingInWalletScreen, { ...buildApprovingInWalletScreenProps(input) });
|
|
17578
18526
|
}
|
|
17579
18527
|
return /* @__PURE__ */ jsxRuntime.jsx(OpenWalletScreen, { ...buildOpenWalletScreenProps(input) });
|
|
@@ -17583,12 +18531,9 @@ function StepRendererContent({
|
|
|
17583
18531
|
if (phase.step === "wallet-setup" && phase.pendingSourceWait) {
|
|
17584
18532
|
return /* @__PURE__ */ jsxRuntime.jsx(LinkTokensScreen, { ...buildLinkTokensScreenProps(input) });
|
|
17585
18533
|
}
|
|
17586
|
-
const
|
|
17587
|
-
input.remote.authExecutorCurrentAction?.type
|
|
17588
|
-
);
|
|
17589
|
-
const justResolvedSelectSource = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null;
|
|
18534
|
+
const pastSourceSelection = input.remote.authExecutorExecuting && input.remote.pendingSelectSource == null && input.remote.sourceSelectionResolved;
|
|
17590
18535
|
const settlingPostSign = input.flow.state.linkSettling;
|
|
17591
|
-
if (
|
|
18536
|
+
if (pastSourceSelection || settlingPostSign) {
|
|
17592
18537
|
return /* @__PURE__ */ jsxRuntime.jsx(ApprovingInWalletScreen, { ...buildApprovingInWalletScreenProps(input) });
|
|
17593
18538
|
}
|
|
17594
18539
|
return /* @__PURE__ */ jsxRuntime.jsx(LinkTokensScreen, { ...buildLinkTokensScreenProps(input) });
|
|
@@ -17672,11 +18617,12 @@ var PaymentErrorBoundary = class extends react.Component {
|
|
|
17672
18617
|
};
|
|
17673
18618
|
function selectedSourceForWallet(selectedWallet, selectedTokenSymbol, depositAmount, priorityContext) {
|
|
17674
18619
|
if (!selectedWallet) return null;
|
|
18620
|
+
const walletSources = selectedWallet.sources ?? [];
|
|
17675
18621
|
if (selectedTokenSymbol) {
|
|
17676
|
-
return
|
|
18622
|
+
return walletSources.find((s) => s.token.symbol === selectedTokenSymbol) ?? null;
|
|
17677
18623
|
}
|
|
17678
18624
|
const walletChainName = selectedWallet.chain.name;
|
|
17679
|
-
const ranked =
|
|
18625
|
+
const ranked = walletSources.map((source, index) => {
|
|
17680
18626
|
return { source, index };
|
|
17681
18627
|
}).sort((a, b) => {
|
|
17682
18628
|
const priority = compareDepositSourcePriority(
|
|
@@ -17724,7 +18670,7 @@ function computeDerivedState(state, depositAmount = 0, priorityContext) {
|
|
|
17724
18670
|
let maxSourceBalance = 0;
|
|
17725
18671
|
for (const acct of state.accounts) {
|
|
17726
18672
|
for (const wallet of acct.wallets) {
|
|
17727
|
-
for (const source of wallet.sources) {
|
|
18673
|
+
for (const source of wallet.sources ?? []) {
|
|
17728
18674
|
if (source.balance.available.amount > maxSourceBalance) {
|
|
17729
18675
|
maxSourceBalance = source.balance.available.amount;
|
|
17730
18676
|
}
|
|
@@ -17734,7 +18680,7 @@ function computeDerivedState(state, depositAmount = 0, priorityContext) {
|
|
|
17734
18680
|
let tokenCount = 0;
|
|
17735
18681
|
for (const acct of state.accounts) {
|
|
17736
18682
|
for (const wallet of acct.wallets) {
|
|
17737
|
-
tokenCount += wallet.sources.filter((s) => s.balance.available.amount > 0).length;
|
|
18683
|
+
tokenCount += (wallet.sources ?? []).filter((s) => s.balance.available.amount > 0).length;
|
|
17738
18684
|
}
|
|
17739
18685
|
}
|
|
17740
18686
|
return {
|
|
@@ -17827,6 +18773,7 @@ function useAuthHandlers(dispatch, apiBaseUrl, popupAuthRef) {
|
|
|
17827
18773
|
dispatch({ type: "PASSKEY_ACTIVATED", credentialId: result.credentialId, publicKey: result.publicKey });
|
|
17828
18774
|
dispatch({ type: "SYNC_PRIVY_SESSION", ready: true, authenticated: true });
|
|
17829
18775
|
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, result.credentialId);
|
|
18776
|
+
track("login_succeeded", { method: "passkey", popup: true });
|
|
17830
18777
|
} catch (err) {
|
|
17831
18778
|
captureException(err);
|
|
17832
18779
|
dispatch({
|
|
@@ -17839,6 +18786,7 @@ function useAuthHandlers(dispatch, apiBaseUrl, popupAuthRef) {
|
|
|
17839
18786
|
}, [dispatch, popupAuthRef]);
|
|
17840
18787
|
const { loginWithPasskey, state: loginState } = reactAuth.useLoginWithPasskey({
|
|
17841
18788
|
onComplete: (params) => {
|
|
18789
|
+
track("login_succeeded", { method: "passkey", popup: false });
|
|
17842
18790
|
handleAuthComplete(params.user);
|
|
17843
18791
|
},
|
|
17844
18792
|
onError: (error) => {
|
|
@@ -17867,6 +18815,7 @@ function useAuthHandlers(dispatch, apiBaseUrl, popupAuthRef) {
|
|
|
17867
18815
|
dispatch({ type: "PASSKEY_ACTIVATED", credentialId: result.credentialId, publicKey: result.publicKey });
|
|
17868
18816
|
dispatch({ type: "SYNC_PRIVY_SESSION", ready: true, authenticated: true });
|
|
17869
18817
|
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, result.credentialId);
|
|
18818
|
+
track("signup_succeeded", { method: "passkey", popup: true });
|
|
17870
18819
|
} catch (err) {
|
|
17871
18820
|
captureException(err);
|
|
17872
18821
|
dispatch({
|
|
@@ -17879,6 +18828,7 @@ function useAuthHandlers(dispatch, apiBaseUrl, popupAuthRef) {
|
|
|
17879
18828
|
}, [dispatch, popupAuthRef]);
|
|
17880
18829
|
const { signupWithPasskey, state: signupState } = reactAuth.useSignupWithPasskey({
|
|
17881
18830
|
onComplete: (params) => {
|
|
18831
|
+
track("signup_succeeded", { method: "passkey", popup: false });
|
|
17882
18832
|
handleAuthComplete(params.user);
|
|
17883
18833
|
},
|
|
17884
18834
|
onError: (error) => {
|
|
@@ -17950,6 +18900,31 @@ function usePasskeyHandlers(dispatch, apiBaseUrl) {
|
|
|
17950
18900
|
};
|
|
17951
18901
|
}
|
|
17952
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
|
+
|
|
17953
18928
|
// src/transferSourceResolution.ts
|
|
17954
18929
|
var STANDARD_TRANSFER_SOURCE_UNAVAILABLE_ERROR = "Selected payment source is no longer available. Choose or link an active wallet before depositing.";
|
|
17955
18930
|
function resolveStandardTransferSource({
|
|
@@ -18001,14 +18976,14 @@ function useTransferHandlers(deps) {
|
|
|
18001
18976
|
const processingStartedAtRef = react.useRef(null);
|
|
18002
18977
|
const pollingTransferIdRef = react.useRef(null);
|
|
18003
18978
|
const depositSelectionReloadCountRef = react.useRef(0);
|
|
18004
|
-
const reloadAccounts = react.useCallback(async () => {
|
|
18979
|
+
const reloadAccounts = react.useCallback(async (opts) => {
|
|
18005
18980
|
depositSelectionReloadCountRef.current += 1;
|
|
18006
18981
|
if (depositSelectionReloadCountRef.current === 1) {
|
|
18007
18982
|
dispatch({ type: "SET_DEPOSIT_SELECTION_REFRESHING", value: true });
|
|
18008
18983
|
}
|
|
18009
18984
|
try {
|
|
18010
18985
|
const token = await getAccessToken();
|
|
18011
|
-
if (!token || !activeCredentialId) return;
|
|
18986
|
+
if (!token || !activeCredentialId) return null;
|
|
18012
18987
|
const [accts, prov] = await Promise.all([
|
|
18013
18988
|
fetchAccounts(apiBaseUrl, token, activeCredentialId),
|
|
18014
18989
|
fetchProviders(apiBaseUrl, token)
|
|
@@ -18032,6 +19007,20 @@ function useTransferHandlers(deps) {
|
|
|
18032
19007
|
defaults,
|
|
18033
19008
|
resetSelectedTokenSymbol
|
|
18034
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;
|
|
18035
19024
|
} finally {
|
|
18036
19025
|
depositSelectionReloadCountRef.current = Math.max(
|
|
18037
19026
|
0,
|
|
@@ -18227,7 +19216,7 @@ function useSourceSelectionHandlers(_dispatch, orchestrator, options) {
|
|
|
18227
19216
|
const selectSourceChoices = react.useMemo(() => {
|
|
18228
19217
|
if (!pendingSelectSourceAction) return [];
|
|
18229
19218
|
const sourceOptions = pendingSelectSourceAction.metadata?.options ?? [];
|
|
18230
|
-
return buildSelectSourceChoices(sourceOptions, minTransferAmountUsd);
|
|
19219
|
+
return buildSelectSourceChoices(sourceOptions, minTransferAmountUsd, true);
|
|
18231
19220
|
}, [pendingSelectSourceAction, minTransferAmountUsd]);
|
|
18232
19221
|
const selectSourceUnsupportedChoices = react.useMemo(() => {
|
|
18233
19222
|
if (!pendingSelectSourceAction) return [];
|
|
@@ -18413,13 +19402,6 @@ function buildReauthorizationSessionOptions(params) {
|
|
|
18413
19402
|
return options;
|
|
18414
19403
|
}
|
|
18415
19404
|
|
|
18416
|
-
// src/tokenAuthorizationRunOptions.ts
|
|
18417
|
-
function buildDesktopTokenAuthorizationOnlyRunOptions(chainName, tokenSymbol) {
|
|
18418
|
-
return {
|
|
18419
|
-
autoResolveSource: { chainName, tokenSymbol }
|
|
18420
|
-
};
|
|
18421
|
-
}
|
|
18422
|
-
|
|
18423
19405
|
// src/oneTapDefaults.ts
|
|
18424
19406
|
var DEFAULT_ONE_TAP_ALLOWANCE_USD = 1e4;
|
|
18425
19407
|
async function ensureDefaultOneTapAllowance(apiBaseUrl, token) {
|
|
@@ -18440,7 +19422,7 @@ function resolveReauthorizationTarget(input) {
|
|
|
18440
19422
|
};
|
|
18441
19423
|
}
|
|
18442
19424
|
const wallet = input.account?.wallets.find((candidate) => candidate.id === input.selectedWalletId);
|
|
18443
|
-
const source = wallet?.sources
|
|
19425
|
+
const source = wallet?.sources?.find(
|
|
18444
19426
|
(candidate) => input.selectedTokenSymbol ? candidate.token.symbol === input.selectedTokenSymbol : candidate.token.status === "AUTHORIZED"
|
|
18445
19427
|
);
|
|
18446
19428
|
return {
|
|
@@ -18451,6 +19433,28 @@ function resolveReauthorizationTarget(input) {
|
|
|
18451
19433
|
};
|
|
18452
19434
|
}
|
|
18453
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
|
+
|
|
18454
19458
|
// src/walletConnectLinks.ts
|
|
18455
19459
|
function buildWalletConnectDeeplink(links, walletConnectUri) {
|
|
18456
19460
|
const baseLink = links?.native ?? links?.universal;
|
|
@@ -18582,6 +19586,8 @@ function useProviderHandlers(deps) {
|
|
|
18582
19586
|
setupDepositToken,
|
|
18583
19587
|
tryStartExtensionConnectForReownWallet
|
|
18584
19588
|
} = deps;
|
|
19589
|
+
const setupDepositTokenRef = react.useRef(setupDepositToken);
|
|
19590
|
+
setupDepositTokenRef.current = setupDepositToken;
|
|
18585
19591
|
const checkWalletConnectChainSupport = react.useMemo(
|
|
18586
19592
|
() => deps.checkWalletConnectChainSupport ?? ((reownWalletId, chainId) => checkReownWalletChainSupport(BLINK_WC_PROJECT_ID, reownWalletId, chainId)),
|
|
18587
19593
|
[deps.checkWalletConnectChainSupport]
|
|
@@ -18649,7 +19655,7 @@ function useProviderHandlers(deps) {
|
|
|
18649
19655
|
return null;
|
|
18650
19656
|
}
|
|
18651
19657
|
const provider = providers.find((p) => p.id === providerId);
|
|
18652
|
-
const
|
|
19658
|
+
const providerName2 = provider?.name ?? "Wallet";
|
|
18653
19659
|
try {
|
|
18654
19660
|
const token = await getAccessToken();
|
|
18655
19661
|
if (!token) throw new Error("Not authenticated");
|
|
@@ -18657,7 +19663,7 @@ function useProviderHandlers(deps) {
|
|
|
18657
19663
|
const accountId = crypto.randomUUID();
|
|
18658
19664
|
const account = await createAccount(apiBaseUrl, token, {
|
|
18659
19665
|
id: accountId,
|
|
18660
|
-
name:
|
|
19666
|
+
name: providerName2,
|
|
18661
19667
|
credentialId: activeCredentialId,
|
|
18662
19668
|
providerId,
|
|
18663
19669
|
...merchantAuthorization ? {
|
|
@@ -18706,7 +19712,7 @@ function useProviderHandlers(deps) {
|
|
|
18706
19712
|
return;
|
|
18707
19713
|
}
|
|
18708
19714
|
const provider = providers.find((p) => p.id === providerId);
|
|
18709
|
-
const
|
|
19715
|
+
const providerName2 = provider?.name ?? "Wallet";
|
|
18710
19716
|
let shouldRestoreDesktopSelectionOnError = shouldSnapshotDesktopSelection;
|
|
18711
19717
|
try {
|
|
18712
19718
|
await resetWalletConnect();
|
|
@@ -18729,7 +19735,7 @@ function useProviderHandlers(deps) {
|
|
|
18729
19735
|
const newAccountId = crypto.randomUUID();
|
|
18730
19736
|
const account = await createAccount(apiBaseUrl, token, {
|
|
18731
19737
|
id: newAccountId,
|
|
18732
|
-
name:
|
|
19738
|
+
name: providerName2,
|
|
18733
19739
|
credentialId: activeCredentialId,
|
|
18734
19740
|
providerId,
|
|
18735
19741
|
...merchantAuthorization ? {
|
|
@@ -18797,7 +19803,7 @@ function useProviderHandlers(deps) {
|
|
|
18797
19803
|
}
|
|
18798
19804
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
18799
19805
|
try {
|
|
18800
|
-
await reloadAccounts();
|
|
19806
|
+
await reloadAccounts({ awaitBalances: true });
|
|
18801
19807
|
} finally {
|
|
18802
19808
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
18803
19809
|
}
|
|
@@ -18924,7 +19930,7 @@ function useProviderHandlers(deps) {
|
|
|
18924
19930
|
}
|
|
18925
19931
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
18926
19932
|
try {
|
|
18927
|
-
await reloadAccounts();
|
|
19933
|
+
await reloadAccounts({ awaitBalances: true });
|
|
18928
19934
|
} finally {
|
|
18929
19935
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
18930
19936
|
}
|
|
@@ -19148,7 +20154,7 @@ function useProviderHandlers(deps) {
|
|
|
19148
20154
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
19149
20155
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19150
20156
|
try {
|
|
19151
|
-
await reloadAccounts();
|
|
20157
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19152
20158
|
} finally {
|
|
19153
20159
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19154
20160
|
}
|
|
@@ -19250,7 +20256,9 @@ function useProviderHandlers(deps) {
|
|
|
19250
20256
|
const result = await orchestrator.run(
|
|
19251
20257
|
sessionId,
|
|
19252
20258
|
{
|
|
19253
|
-
|
|
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.
|
|
19254
20262
|
// Pin the existing account id so pairing and signing share one
|
|
19255
20263
|
// runtime (see handleSelectWalletConnectWallet).
|
|
19256
20264
|
walletConnectRuntimeKey: runtimeAccountId,
|
|
@@ -19258,10 +20266,11 @@ function useProviderHandlers(deps) {
|
|
|
19258
20266
|
// Require the target token's chain at pairing. A reused session
|
|
19259
20267
|
// that doesn't approve it re-pairs with the chain required; a
|
|
19260
20268
|
// wallet that approves the connection without it fails
|
|
19261
|
-
// OPEN_PROVIDER with a clear error instead of the
|
|
19262
|
-
//
|
|
19263
|
-
//
|
|
19264
|
-
//
|
|
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.
|
|
19265
20274
|
walletConnectRequiredChainId: chainId,
|
|
19266
20275
|
walletConnectRequiredChainName: inlineChain.name,
|
|
19267
20276
|
onWalletConnectDisplayUri: (uri) => {
|
|
@@ -19297,11 +20306,17 @@ function useProviderHandlers(deps) {
|
|
|
19297
20306
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
19298
20307
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19299
20308
|
try {
|
|
19300
|
-
await reloadAccounts();
|
|
20309
|
+
await reloadAccounts({ awaitBalances: true });
|
|
19301
20310
|
} finally {
|
|
19302
20311
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19303
20312
|
}
|
|
19304
|
-
|
|
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
|
+
});
|
|
19305
20320
|
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19306
20321
|
} catch (err) {
|
|
19307
20322
|
captureException(err);
|
|
@@ -19616,10 +20631,7 @@ function useProviderHandlers(deps) {
|
|
|
19616
20631
|
walletDeeplinks: walletDeeplinks ?? null,
|
|
19617
20632
|
providerId: matchedProvider?.id ?? null
|
|
19618
20633
|
});
|
|
19619
|
-
const result = await orchestrator.run(
|
|
19620
|
-
session.id,
|
|
19621
|
-
buildDesktopTokenAuthorizationOnlyRunOptions(inlineChain.name, tokenSymbol)
|
|
19622
|
-
);
|
|
20634
|
+
const result = await orchestrator.run(session.id);
|
|
19623
20635
|
if (result.status === "cancelled") {
|
|
19624
20636
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
19625
20637
|
dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: false });
|
|
@@ -19631,14 +20643,25 @@ function useProviderHandlers(deps) {
|
|
|
19631
20643
|
}
|
|
19632
20644
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
19633
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
|
+
};
|
|
19634
20652
|
dispatch({ type: "BEGIN_LINK_SETTLING" });
|
|
19635
20653
|
try {
|
|
19636
|
-
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" });
|
|
19637
20662
|
} finally {
|
|
19638
20663
|
dispatch({ type: "END_LINK_SETTLING" });
|
|
19639
20664
|
}
|
|
19640
|
-
dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol, accountId: _accountId });
|
|
19641
|
-
dispatch({ type: "DISCARD_SAVED_SELECTION" });
|
|
19642
20665
|
} catch (err) {
|
|
19643
20666
|
captureException(err);
|
|
19644
20667
|
dispatch({ type: "DESKTOP_WAIT_CLEARED" });
|
|
@@ -20098,6 +21121,15 @@ function useDataLoadEffect(deps) {
|
|
|
20098
21121
|
hasActiveCredential: !!state.activeCredentialId,
|
|
20099
21122
|
loading: loadingDataRef.current
|
|
20100
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
|
+
});
|
|
20101
21133
|
if (loadAction === "reset") {
|
|
20102
21134
|
loadingDataRef.current = false;
|
|
20103
21135
|
dispatch({ type: "DATA_LOAD_END" });
|
|
@@ -20115,13 +21147,21 @@ function useDataLoadEffect(deps) {
|
|
|
20115
21147
|
const load = async () => {
|
|
20116
21148
|
dispatch({ type: "DATA_LOAD_START" });
|
|
20117
21149
|
try {
|
|
21150
|
+
appendDebug("warn", "dataLoad: requesting access token");
|
|
20118
21151
|
const token = await getAccessTokenRef.current();
|
|
21152
|
+
appendDebug("warn", "dataLoad: token result", { hasToken: !!token, cancelled });
|
|
20119
21153
|
if (!token) throw new Error("Not authenticated");
|
|
20120
21154
|
const [prov, accts, chn] = await Promise.all([
|
|
20121
21155
|
fetchProviders(apiBaseUrl, token),
|
|
20122
21156
|
fetchAccounts(apiBaseUrl, token, credentialId),
|
|
20123
21157
|
fetchChains(apiBaseUrl, token)
|
|
20124
21158
|
]);
|
|
21159
|
+
appendDebug("warn", "dataLoad: fetch resolved", {
|
|
21160
|
+
cancelled,
|
|
21161
|
+
providers: prov.length,
|
|
21162
|
+
accounts: accts.length,
|
|
21163
|
+
chains: chn.length
|
|
21164
|
+
});
|
|
20125
21165
|
if (cancelled) return;
|
|
20126
21166
|
const parsedAmt = depositAmountRef.current != null ? depositAmountRef.current : 0;
|
|
20127
21167
|
const priorityContext = resolveDepositPriorityContext(accts, chn, destination);
|
|
@@ -20147,7 +21187,16 @@ function useDataLoadEffect(deps) {
|
|
|
20147
21187
|
resetSelectedTokenSymbol
|
|
20148
21188
|
});
|
|
20149
21189
|
if (clearMobile) clearMobileFlowState();
|
|
21190
|
+
void fetchBalancesByAccountId(apiBaseUrl, token, credentialId, accts).then(
|
|
21191
|
+
(balancesByAccountId) => {
|
|
21192
|
+
dispatch({ type: "BALANCES_LOADED", balancesByAccountId });
|
|
21193
|
+
}
|
|
21194
|
+
);
|
|
20150
21195
|
} catch (err) {
|
|
21196
|
+
appendDebug("error", "dataLoad: threw", {
|
|
21197
|
+
cancelled,
|
|
21198
|
+
message: err instanceof Error ? err.message : String(err)
|
|
21199
|
+
});
|
|
20151
21200
|
if (!cancelled) {
|
|
20152
21201
|
captureException(err);
|
|
20153
21202
|
dispatch({
|
|
@@ -20164,6 +21213,7 @@ function useDataLoadEffect(deps) {
|
|
|
20164
21213
|
};
|
|
20165
21214
|
load();
|
|
20166
21215
|
return () => {
|
|
21216
|
+
appendDebug("warn", "dataLoadEffect cleanup (cancelling in-flight load)");
|
|
20167
21217
|
cancelled = true;
|
|
20168
21218
|
loadingDataRef.current = false;
|
|
20169
21219
|
dispatch({ type: "DATA_LOAD_END" });
|
|
@@ -20907,7 +21957,7 @@ function BlinkPaymentInner({
|
|
|
20907
21957
|
useWalletConnector: useWalletConnectorProp,
|
|
20908
21958
|
userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
|
|
20909
21959
|
});
|
|
20910
|
-
const [state,
|
|
21960
|
+
const [state, rawDispatch] = react.useReducer(
|
|
20911
21961
|
paymentReducer,
|
|
20912
21962
|
{
|
|
20913
21963
|
depositAmount,
|
|
@@ -20917,6 +21967,30 @@ function BlinkPaymentInner({
|
|
|
20917
21967
|
},
|
|
20918
21968
|
createInitialState
|
|
20919
21969
|
);
|
|
21970
|
+
const device = isDesktop ? "desktop" : "mobile";
|
|
21971
|
+
const stateRef = react.useRef(state);
|
|
21972
|
+
stateRef.current = state;
|
|
21973
|
+
const dispatch = react.useCallback(
|
|
21974
|
+
(action) => {
|
|
21975
|
+
trackAction(action, stateRef.current, { device });
|
|
21976
|
+
rawDispatch(action);
|
|
21977
|
+
},
|
|
21978
|
+
[device]
|
|
21979
|
+
);
|
|
21980
|
+
usePostHogIdentify();
|
|
21981
|
+
useAnalyticsScreenView(state.phase, device);
|
|
21982
|
+
const flowOpenedRef = react.useRef(false);
|
|
21983
|
+
react.useEffect(() => {
|
|
21984
|
+
if (flowOpenedRef.current) return;
|
|
21985
|
+
flowOpenedRef.current = true;
|
|
21986
|
+
registerSuperProps({ device });
|
|
21987
|
+
track("flow_opened", {
|
|
21988
|
+
device,
|
|
21989
|
+
isMobileApp: isMobileApp ?? false,
|
|
21990
|
+
fullWidget: enableFullWidget ?? false,
|
|
21991
|
+
hasAmount: depositAmount != null
|
|
21992
|
+
});
|
|
21993
|
+
}, []);
|
|
20920
21994
|
const polling = useTransferPolling(3e3, effectiveGetAccessToken);
|
|
20921
21995
|
const transferSigning = useTransferSigning(2e3, { getAccessToken: effectiveGetAccessToken });
|
|
20922
21996
|
const mobileFlowRefs = {
|
|
@@ -21275,7 +22349,7 @@ function BlinkPaymentInner({
|
|
|
21275
22349
|
const handleSetDepositToken = react.useCallback((symbol, chainName, walletId, tokenAddress, chainId) => {
|
|
21276
22350
|
dispatch({ type: "SET_SETUP_DEPOSIT_TOKEN", symbol, chainName, walletId, tokenAddress, chainId });
|
|
21277
22351
|
}, []);
|
|
21278
|
-
const handleConfirmSetupDeposit = react.useCallback(async () => {
|
|
22352
|
+
const handleConfirmSetupDeposit = react.useCallback(async (limit) => {
|
|
21279
22353
|
const plan = planConfirmSetupDeposit({
|
|
21280
22354
|
setupDepositToken: state.setupDepositToken,
|
|
21281
22355
|
setupSelectedSourceOption,
|
|
@@ -21285,6 +22359,19 @@ function BlinkPaymentInner({
|
|
|
21285
22359
|
dispatch({ type: "SET_ERROR", error: plan.error });
|
|
21286
22360
|
return;
|
|
21287
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 });
|
|
21288
22375
|
for (const action of plan.actions) {
|
|
21289
22376
|
dispatch(action);
|
|
21290
22377
|
}
|
|
@@ -21293,6 +22380,8 @@ function BlinkPaymentInner({
|
|
|
21293
22380
|
state.selectedAccountId,
|
|
21294
22381
|
state.setupDepositToken,
|
|
21295
22382
|
setupSelectedSourceOption,
|
|
22383
|
+
effectiveGetAccessToken,
|
|
22384
|
+
apiBaseUrl,
|
|
21296
22385
|
dispatch,
|
|
21297
22386
|
orchestrator
|
|
21298
22387
|
]);
|
|
@@ -21353,6 +22442,9 @@ function BlinkPaymentInner({
|
|
|
21353
22442
|
}
|
|
21354
22443
|
})();
|
|
21355
22444
|
}, [orchestrator]);
|
|
22445
|
+
const handleApprove = react.useCallback(() => {
|
|
22446
|
+
authExecutor.approveAuthorization();
|
|
22447
|
+
}, [authExecutor]);
|
|
21356
22448
|
const handleSetPhase = react.useCallback((phase) => {
|
|
21357
22449
|
clearScreenErrors();
|
|
21358
22450
|
if (phase.step === "deposit") {
|
|
@@ -21374,6 +22466,7 @@ function BlinkPaymentInner({
|
|
|
21374
22466
|
onConfirmSign: transfer.handleConfirmSign,
|
|
21375
22467
|
onRetryMobileStatus: mobileFlow.handleRetryMobileStatus,
|
|
21376
22468
|
onRetryAuthorization: handleAuthorizationRetry,
|
|
22469
|
+
onApprove: handleApprove,
|
|
21377
22470
|
onRetryTransferSigning: transfer.handleRetryTransferSigning,
|
|
21378
22471
|
onBackFromOpenWallet: handleBackFromOpenWallet,
|
|
21379
22472
|
onLogout: handleLogout,
|
|
@@ -21444,6 +22537,7 @@ function BlinkPaymentInner({
|
|
|
21444
22537
|
handleConfirmSetupDeposit,
|
|
21445
22538
|
handleBackFromSetupDeposit,
|
|
21446
22539
|
handleAuthorizationRetry,
|
|
22540
|
+
handleApprove,
|
|
21447
22541
|
disconnectWallets,
|
|
21448
22542
|
state.desktopWait?.walletForegroundLink
|
|
21449
22543
|
]);
|
|
@@ -21483,6 +22577,9 @@ function BlinkPaymentInner({
|
|
|
21483
22577
|
authExecutorError: authExecutor.error,
|
|
21484
22578
|
authExecutorExecuting: authExecutor.executing,
|
|
21485
22579
|
authExecutorCurrentAction: authExecutor.currentAction,
|
|
22580
|
+
authExecutorAwaitingApproval: authExecutor.awaitingApproval,
|
|
22581
|
+
authExecutorApprovalDestinationAddress: authExecutor.approvalDestinationAddress,
|
|
22582
|
+
authApprovalSmartAccountAddress: orchestrator.approvalSmartAccountAddress,
|
|
21486
22583
|
authExecutorCompleted: orchestrator.orchestratorCompleted,
|
|
21487
22584
|
transferSigningSigning: transferSigning.signing,
|
|
21488
22585
|
transferSigningError: transferSigning.error,
|
|
@@ -21590,6 +22687,7 @@ exports.getDeviceBiometricUnlockText = getDeviceBiometricUnlockText;
|
|
|
21590
22687
|
exports.getTheme = getTheme;
|
|
21591
22688
|
exports.getThemeBase = getThemeBase;
|
|
21592
22689
|
exports.getWalletCapabilities = getWalletCapabilities;
|
|
22690
|
+
exports.identifyUser = identifyUser;
|
|
21593
22691
|
exports.isAuthorizationSessionCancelled = isAuthorizationSessionCancelled;
|
|
21594
22692
|
exports.isExpectedAuthorizationCancellation = isExpectedAuthorizationCancellation;
|
|
21595
22693
|
exports.isTerminalTransferStatus = isTerminalTransferStatus;
|
|
@@ -21603,11 +22701,15 @@ exports.lightTransparentTheme = lightTransparentTheme;
|
|
|
21603
22701
|
exports.lightTransparentThemeNew = lightTransparentThemeNew;
|
|
21604
22702
|
exports.mapGuestPickerEntries = mapGuestPickerEntries;
|
|
21605
22703
|
exports.replaceOpenProviderForAccountSwitch = replaceOpenProviderForAccountSwitch;
|
|
22704
|
+
exports.resetUser = resetUser;
|
|
22705
|
+
exports.resolveAnalyticsEnvironment = resolveAnalyticsEnvironment;
|
|
21606
22706
|
exports.resolvePasskeyRpId = resolvePasskeyRpId;
|
|
22707
|
+
exports.sanitizeProps = sanitizeProps;
|
|
21607
22708
|
exports.screenForPhase = screenForPhase;
|
|
21608
22709
|
exports.subscribeDebug = subscribeDebug;
|
|
21609
22710
|
exports.supportsAtomicBatch = supportsAtomicBatch;
|
|
21610
22711
|
exports.supportsPaymasterService = supportsPaymasterService;
|
|
22712
|
+
exports.track = track;
|
|
21611
22713
|
exports.useAuthorizationExecutor = useAuthorizationExecutor;
|
|
21612
22714
|
exports.useAuthorizationOrchestrator = useAuthorizationOrchestrator;
|
|
21613
22715
|
exports.useBlinkConfig = useBlinkConfig;
|