@avail-project/widgets 2.0.0 → 2.0.2
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/common/hooks/useNexusError.d.ts +1 -1
- package/dist/common/utils/token-pricing.d.ts +1 -1
- package/dist/common/utils/token-pricing.d.ts.map +1 -1
- package/dist/common/utils/token-pricing.js +4 -1
- package/dist/nexus/NexusProvider.d.ts +4 -2
- package/dist/nexus/NexusProvider.d.ts.map +1 -1
- package/dist/nexus/NexusProvider.js +216 -39
- package/dist/nexus-widget/components/deposit-idle-form.d.ts +2 -1
- package/dist/nexus-widget/components/deposit-idle-form.d.ts.map +1 -1
- package/dist/nexus-widget/components/deposit-idle-form.js +26 -10
- package/dist/nexus-widget/components/nexus-widget-progress-screen.d.ts.map +1 -1
- package/dist/nexus-widget/components/nexus-widget-progress-screen.js +21 -5
- package/dist/nexus-widget/components/pay-with-sources.d.ts.map +1 -1
- package/dist/nexus-widget/components/pay-with-sources.js +43 -54
- package/dist/nexus-widget/components/send-idle-form.d.ts +2 -1
- package/dist/nexus-widget/components/send-idle-form.d.ts.map +1 -1
- package/dist/nexus-widget/components/send-idle-form.js +26 -10
- package/dist/nexus-widget/components/swap-asset-selector.d.ts +1 -1
- package/dist/nexus-widget/components/swap-asset-selector.d.ts.map +1 -1
- package/dist/nexus-widget/components/swap-asset-selector.js +198 -140
- package/dist/nexus-widget/components/swap-idle-form.d.ts +2 -1
- package/dist/nexus-widget/components/swap-idle-form.d.ts.map +1 -1
- package/dist/nexus-widget/components/swap-idle-form.js +3 -3
- package/dist/nexus-widget/components/swap-intent-preview.d.ts +5 -1
- package/dist/nexus-widget/components/swap-intent-preview.d.ts.map +1 -1
- package/dist/nexus-widget/components/swap-intent-preview.js +50 -35
- package/dist/nexus-widget/nexus-widget.d.ts.map +1 -1
- package/dist/nexus-widget/nexus-widget.js +1311 -617
- package/dist/nexus-widget/types.d.ts +1 -1
- package/dist/nexus-widget/types.d.ts.map +1 -1
- package/dist/nexus-widget/utils/deposit-source-selection.d.ts.map +1 -1
- package/dist/nexus-widget/utils/deposit-source-selection.js +68 -4
- package/package.json +5 -5
|
@@ -38,7 +38,7 @@ const SCIENTIFIC_DECIMAL_REGEX = /^-?(?:\d+\.?\d*|\.\d+)e[+-]?\d+$/i;
|
|
|
38
38
|
const QUOTE_REFRESH_INTERVAL_MS = 30000;
|
|
39
39
|
const EXACT_OUT_INPUT_DEBOUNCE_MS = 1300;
|
|
40
40
|
const DRAWER_CLOSE_MS = 220;
|
|
41
|
-
const BALANCE_REFRESH_AFTER_TERMINAL_MS =
|
|
41
|
+
const BALANCE_REFRESH_AFTER_TERMINAL_MS = 3000;
|
|
42
42
|
const MODAL_HEIGHT_TRANSITION_MS = 220;
|
|
43
43
|
const ROOT_HEIGHT_TRANSITION_MS = 140;
|
|
44
44
|
const ASSET_SELECTOR_DRAWER_HEIGHT = "90%";
|
|
@@ -47,8 +47,25 @@ const CONFIGURED_RECEIVE_SELECTOR_BASE_HEIGHT = 170;
|
|
|
47
47
|
const CONFIGURED_RECEIVE_SELECTOR_ROW_HEIGHT = 62;
|
|
48
48
|
const BASIS_POINTS = 10000;
|
|
49
49
|
const PREDICTIVE_EXACT_IN_DISCOUNT_BPS = 50;
|
|
50
|
-
const
|
|
50
|
+
const PREDICTIVE_EXACT_OUT_DESTINATION_BUFFER_PCT = 0.1;
|
|
51
|
+
const PREDICTIVE_EXACT_OUT_DESTINATION_BUFFER_MAX_USD = 2;
|
|
52
|
+
const PREDICTIVE_EXACT_OUT_SOURCE_BUFFER_PCT = 0.02;
|
|
53
|
+
const PREDICTIVE_EXACT_OUT_SOURCE_BUFFER_MAX_USD = 1;
|
|
51
54
|
const PREDICTIVE_QUOTE_DISPLAY_DECIMALS = 8;
|
|
55
|
+
const DEPOSIT_TOKEN_DISPLAY_DECIMALS = 8;
|
|
56
|
+
const ETHEREUM_MAINNET_CHAIN_ID = 1;
|
|
57
|
+
const SDK_EXACT_OUT_STABLE_SYMBOLS = new Set([
|
|
58
|
+
"USDC",
|
|
59
|
+
"USDT",
|
|
60
|
+
"DAI",
|
|
61
|
+
"BUSD",
|
|
62
|
+
"TUSD",
|
|
63
|
+
"FRAX",
|
|
64
|
+
"LUSD",
|
|
65
|
+
"USDD",
|
|
66
|
+
"USDP",
|
|
67
|
+
"GUSD",
|
|
68
|
+
]);
|
|
52
69
|
const SWAP_HISTORY_STORAGE_KEY_PREFIX = "nexus-widget-transaction-history-v1";
|
|
53
70
|
const TIMEOUT_LABEL = "Timed Out";
|
|
54
71
|
const PROGRESS_EVENT_NAMES = {
|
|
@@ -58,20 +75,12 @@ const PROGRESS_EVENT_NAMES = {
|
|
|
58
75
|
SWAP_PLAN_PROGRESS: "swap_plan_progress",
|
|
59
76
|
};
|
|
60
77
|
const PLAN_FINAL_STATES = new Set(["completed", "confirmed", "submitted"]);
|
|
61
|
-
const
|
|
78
|
+
const PLAN_STEP_FUNDS_MAY_HAVE_MOVED_STATES = new Set([
|
|
62
79
|
"completed",
|
|
63
80
|
"confirmed",
|
|
64
81
|
"submitted",
|
|
82
|
+
"failed",
|
|
65
83
|
]);
|
|
66
|
-
const waitForNextPaint = () => new Promise((resolve) => {
|
|
67
|
-
if (typeof window === "undefined" || !window.requestAnimationFrame) {
|
|
68
|
-
resolve();
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
window.requestAnimationFrame(() => {
|
|
72
|
-
window.setTimeout(() => resolve(), 0);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
84
|
const theme = nexusWidgetTheme;
|
|
76
85
|
const tooltipSurface = theme.colors.surface;
|
|
77
86
|
const tooltipText = theme.colors.textStrong;
|
|
@@ -80,7 +89,32 @@ const uiFont = theme.fonts.sans;
|
|
|
80
89
|
const modalHeightTransitionStyle = {
|
|
81
90
|
interpolateSize: "allow-keywords",
|
|
82
91
|
};
|
|
92
|
+
const getCappedTokenDisplayDecimals = (decimals) => {
|
|
93
|
+
const parsedDecimals = Number(decimals);
|
|
94
|
+
if (!Number.isFinite(parsedDecimals) || parsedDecimals < 0) {
|
|
95
|
+
return DEPOSIT_TOKEN_DISPLAY_DECIMALS;
|
|
96
|
+
}
|
|
97
|
+
return Math.min(Math.floor(parsedDecimals), DEPOSIT_TOKEN_DISPLAY_DECIMALS);
|
|
98
|
+
};
|
|
83
99
|
const modalHeightTransition = `height ${MODAL_HEIGHT_TRANSITION_MS}ms ease, max-height ${MODAL_HEIGHT_TRANSITION_MS}ms ease`;
|
|
100
|
+
const normalizeSdkExactOutStableSymbol = (symbol) => (symbol ?? "")
|
|
101
|
+
.trim()
|
|
102
|
+
.toUpperCase()
|
|
103
|
+
.replaceAll("\u20ae", "T")
|
|
104
|
+
.replaceAll(/[^A-Z0-9]/g, "");
|
|
105
|
+
const isSdkExactOutStableSymbol = (symbol) => SDK_EXACT_OUT_STABLE_SYMBOLS.has(normalizeSdkExactOutStableSymbol(symbol));
|
|
106
|
+
const applyPredictiveExactOutBufferUsd = (amount, pct, maxUsd) => Decimal.min(amount.mul(pct), new Decimal(maxUsd));
|
|
107
|
+
const getPredictiveExactOutSourceTargetUsd = (destinationUsdNeedingSources, cachedSourceUsdRatio) => {
|
|
108
|
+
if (destinationUsdNeedingSources.lte(0))
|
|
109
|
+
return new Decimal(0);
|
|
110
|
+
if (cachedSourceUsdRatio?.gt(0)) {
|
|
111
|
+
return destinationUsdNeedingSources.mul(cachedSourceUsdRatio);
|
|
112
|
+
}
|
|
113
|
+
const destinationBuffer = applyPredictiveExactOutBufferUsd(destinationUsdNeedingSources, PREDICTIVE_EXACT_OUT_DESTINATION_BUFFER_PCT, PREDICTIVE_EXACT_OUT_DESTINATION_BUFFER_MAX_USD);
|
|
114
|
+
const destinationBuffered = destinationUsdNeedingSources.plus(destinationBuffer);
|
|
115
|
+
const sourceBuffer = applyPredictiveExactOutBufferUsd(destinationBuffered, PREDICTIVE_EXACT_OUT_SOURCE_BUFFER_PCT, PREDICTIVE_EXACT_OUT_SOURCE_BUFFER_MAX_USD);
|
|
116
|
+
return destinationBuffered.plus(sourceBuffer);
|
|
117
|
+
};
|
|
84
118
|
const getSwapHistoryStorageKey = (ownerAddress) => `${SWAP_HISTORY_STORAGE_KEY_PREFIX}:${ownerAddress?.toLowerCase() || "anonymous"}`;
|
|
85
119
|
const getTokenSelectionKey = (token) => {
|
|
86
120
|
if (!token)
|
|
@@ -90,6 +124,46 @@ const getTokenSelectionKey = (token) => {
|
|
|
90
124
|
}
|
|
91
125
|
return `${token.chainId ?? "unknown"}:${token.contractAddress.toLowerCase()}`;
|
|
92
126
|
};
|
|
127
|
+
const getDisplaySourceTokenKey = (token) => {
|
|
128
|
+
if (!token)
|
|
129
|
+
return "";
|
|
130
|
+
const chainId = token.chainId ?? "unknown";
|
|
131
|
+
const address = token.contractAddress?.toLowerCase();
|
|
132
|
+
return address
|
|
133
|
+
? `${chainId}:${address}`
|
|
134
|
+
: `${chainId}:symbol:${token.symbol.toUpperCase()}`;
|
|
135
|
+
};
|
|
136
|
+
const sumDecimalStrings = (left, right) => {
|
|
137
|
+
const leftAmount = parseDecimalLoose(left) ?? new Decimal(0);
|
|
138
|
+
const rightAmount = parseDecimalLoose(right) ?? new Decimal(0);
|
|
139
|
+
const sum = leftAmount.plus(rightAmount);
|
|
140
|
+
return sum.gt(0) ? sum.toFixed() : undefined;
|
|
141
|
+
};
|
|
142
|
+
const mergeDisplaySourceTokens = (tokens) => {
|
|
143
|
+
const merged = new Map();
|
|
144
|
+
for (const token of tokens) {
|
|
145
|
+
const key = getDisplaySourceTokenKey(token);
|
|
146
|
+
if (!key)
|
|
147
|
+
continue;
|
|
148
|
+
const existing = merged.get(key);
|
|
149
|
+
if (!existing) {
|
|
150
|
+
merged.set(key, token);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const userAmount = sumDecimalStrings(existing.userAmount || existing.balance, token.userAmount || token.balance);
|
|
154
|
+
const userAmountUsd = sumDecimalStrings(existing.userAmountUsd || existing.balanceInFiat, token.userAmountUsd || token.balanceInFiat);
|
|
155
|
+
merged.set(key, {
|
|
156
|
+
...existing,
|
|
157
|
+
balance: userAmount ?? existing.balance,
|
|
158
|
+
balanceInFiat: userAmountUsd ?? existing.balanceInFiat,
|
|
159
|
+
chainLogo: existing.chainLogo || token.chainLogo,
|
|
160
|
+
logo: existing.logo || token.logo,
|
|
161
|
+
userAmount: userAmount ?? existing.userAmount,
|
|
162
|
+
userAmountUsd: userAmountUsd ?? existing.userAmountUsd,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return Array.from(merged.values());
|
|
166
|
+
};
|
|
93
167
|
const getTokenQuoteKey = (token) => {
|
|
94
168
|
if (!token)
|
|
95
169
|
return "";
|
|
@@ -115,6 +189,14 @@ const getSourceTokensQuoteKey = (tokens) => tokens
|
|
|
115
189
|
token.userAmountMode ?? "",
|
|
116
190
|
].join(":"))
|
|
117
191
|
.join("|");
|
|
192
|
+
const getSourceTokenSyncKey = (token) => [
|
|
193
|
+
getTokenSelectionKey(token),
|
|
194
|
+
token.userAmount ?? "",
|
|
195
|
+
token.userAmountUsd ?? "",
|
|
196
|
+
token.balance ?? "",
|
|
197
|
+
token.balanceInFiat ?? "",
|
|
198
|
+
].join(":");
|
|
199
|
+
const getSourceTokenListSyncKey = (tokens) => tokens.map(getSourceTokenSyncKey).join("|");
|
|
118
200
|
const isSameTokenSelection = (a, b) => Boolean(a && b && getTokenSelectionKey(a) === getTokenSelectionKey(b));
|
|
119
201
|
const getDepositConfigIdentity = (deposit) => {
|
|
120
202
|
if (!deposit)
|
|
@@ -152,6 +234,7 @@ const normalizeConfiguredAddress = (value) => {
|
|
|
152
234
|
const text = normalizeConfiguredString(value);
|
|
153
235
|
return text && isAddress(text) ? text : undefined;
|
|
154
236
|
};
|
|
237
|
+
const isPositiveGasLimit = (value) => typeof value === "bigint" && value > BigInt(0);
|
|
155
238
|
const normalizePositiveNumericString = (value) => {
|
|
156
239
|
const text = normalizeConfiguredString(value);
|
|
157
240
|
if (!text)
|
|
@@ -768,12 +851,22 @@ const formatTokenDisplay = (value) => {
|
|
|
768
851
|
const getSwapTokenUsdValue = (token) => parseDecimalLoose(token.userAmountUsd) ??
|
|
769
852
|
parseDecimalLoose(token.balanceInFiat) ??
|
|
770
853
|
new Decimal(0);
|
|
854
|
+
const getSwapTokenBalanceUsdValue = (token) => parseDecimalLoose(token.balanceInFiat) ?? new Decimal(0);
|
|
771
855
|
const sortSwapTokensByUsdDesc = (tokens) => [...tokens].sort((a, b) => {
|
|
772
856
|
const usdDelta = getSwapTokenUsdValue(b).cmp(getSwapTokenUsdValue(a));
|
|
773
857
|
if (usdDelta !== 0)
|
|
774
858
|
return usdDelta;
|
|
775
859
|
return (a.symbol ?? "").localeCompare(b.symbol ?? "");
|
|
776
860
|
});
|
|
861
|
+
const sortDisplaySourcesByBalanceUsdDesc = (tokens) => [...tokens].sort((a, b) => {
|
|
862
|
+
const balanceUsdDelta = getSwapTokenBalanceUsdValue(b).cmp(getSwapTokenBalanceUsdValue(a));
|
|
863
|
+
if (balanceUsdDelta !== 0)
|
|
864
|
+
return balanceUsdDelta;
|
|
865
|
+
const spendUsdDelta = getSwapTokenUsdValue(b).cmp(getSwapTokenUsdValue(a));
|
|
866
|
+
if (spendUsdDelta !== 0)
|
|
867
|
+
return spendUsdDelta;
|
|
868
|
+
return `${a.symbol ?? ""} ${a.chainName ?? ""}`.localeCompare(`${b.symbol ?? ""} ${b.chainName ?? ""}`);
|
|
869
|
+
});
|
|
777
870
|
const getIntentSourceUsdValue = (source) => parseDecimalLoose(source.value) ?? new Decimal(0);
|
|
778
871
|
const sortIntentSourcesByUsdDesc = (sources) => [...sources].sort((a, b) => {
|
|
779
872
|
const usdDelta = getIntentSourceUsdValue(b).cmp(getIntentSourceUsdValue(a));
|
|
@@ -1011,7 +1104,9 @@ const getDisplayDestinationSourceRow = (entry) => {
|
|
|
1011
1104
|
: undefined;
|
|
1012
1105
|
return {
|
|
1013
1106
|
key: `destination-balance-${entry.toToken.chainId}-${entry.toToken.contractAddress}`,
|
|
1014
|
-
|
|
1107
|
+
chainId: entry.toToken.chainId,
|
|
1108
|
+
contractAddress: entry.toToken.contractAddress,
|
|
1109
|
+
tokenLogo: entry.intentData?.destination.token.logo || entry.toToken.logo,
|
|
1015
1110
|
chainLogo: entry.toToken.chainLogo,
|
|
1016
1111
|
symbol: entry.toToken.symbol,
|
|
1017
1112
|
chainName: getShortChainName(entry.toToken.chainId, entry.toToken.chainName),
|
|
@@ -1023,7 +1118,42 @@ const getDisplayDestinationSourceRow = (entry) => {
|
|
|
1023
1118
|
: entry.toToken.balanceInFiat,
|
|
1024
1119
|
};
|
|
1025
1120
|
};
|
|
1121
|
+
const getHistorySourceRowMergeKey = (row) => {
|
|
1122
|
+
const chainKey = row.chainId ?? row.chainName;
|
|
1123
|
+
const address = row.contractAddress?.toLowerCase();
|
|
1124
|
+
return address
|
|
1125
|
+
? `${chainKey}:${address}`
|
|
1126
|
+
: `${chainKey}:symbol:${row.symbol.toUpperCase()}`;
|
|
1127
|
+
};
|
|
1128
|
+
const mergeHistorySourceRows = (rows) => {
|
|
1129
|
+
const merged = new Map();
|
|
1130
|
+
for (const row of rows) {
|
|
1131
|
+
const key = getHistorySourceRowMergeKey(row);
|
|
1132
|
+
const existing = merged.get(key);
|
|
1133
|
+
if (!existing) {
|
|
1134
|
+
merged.set(key, row);
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
merged.set(key, {
|
|
1138
|
+
...existing,
|
|
1139
|
+
amount: sumDecimalStrings(existing.amount, row.amount) ?? existing.amount,
|
|
1140
|
+
chainLogo: existing.chainLogo || row.chainLogo,
|
|
1141
|
+
key: existing.key,
|
|
1142
|
+
tokenLogo: existing.tokenLogo || row.tokenLogo,
|
|
1143
|
+
value: sumDecimalStrings(String(existing.value ?? ""), String(row.value ?? "")),
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
return Array.from(merged.values());
|
|
1147
|
+
};
|
|
1026
1148
|
const getProgressStepType = (step) => String(step?.type ?? step?.typeID ?? "").toUpperCase();
|
|
1149
|
+
const isSourceMovementProgressStep = (type, rawStepType) => rawStepType === "source_swap" ||
|
|
1150
|
+
rawStepType === "eoa_to_ephemeral_transfer" ||
|
|
1151
|
+
rawStepType === "bridge_deposit" ||
|
|
1152
|
+
rawStepType === "vault_deposit" ||
|
|
1153
|
+
type.includes("SOURCE_SWAP") ||
|
|
1154
|
+
type.includes("EOA_TO_EPHEMERAL_TRANSFER") ||
|
|
1155
|
+
type.includes("BRIDGE_DEPOSIT") ||
|
|
1156
|
+
type.includes("VAULT_DEPOSIT");
|
|
1027
1157
|
const isBridgeRefundStepType = (type) => type.includes("BRIDGE_INTENT_SUBMISSION") || type.includes("BRIDGE_DEPOSIT");
|
|
1028
1158
|
const isSwapSkippedStepType = (type) => type.includes("SWAP_SKIPPED");
|
|
1029
1159
|
const isAutoRefundAvailableProgressEvent = (event) => event?.name === PROGRESS_EVENT_NAMES.SWAP_PLAN_PROGRESS &&
|
|
@@ -1160,6 +1290,12 @@ const normalizeSdkIntentString = (value) => {
|
|
|
1160
1290
|
return String(value);
|
|
1161
1291
|
};
|
|
1162
1292
|
const normalizeSdkIntentAmount = (value, fallback = "0") => normalizeSdkIntentString(value) ?? fallback;
|
|
1293
|
+
const getLogoFromMetadata = (metadata) => metadata?.logo ??
|
|
1294
|
+
metadata?.logoURI ??
|
|
1295
|
+
metadata?.logoUri ??
|
|
1296
|
+
metadata?.logoUrl ??
|
|
1297
|
+
metadata?.icon ??
|
|
1298
|
+
"";
|
|
1163
1299
|
const normalizeSdkIntentChain = (chain) => {
|
|
1164
1300
|
const id = Number(chain?.id ?? chain?.chainId);
|
|
1165
1301
|
if (!Number.isFinite(id))
|
|
@@ -1167,7 +1303,7 @@ const normalizeSdkIntentChain = (chain) => {
|
|
|
1167
1303
|
const chainMeta = CHAIN_METADATA[id];
|
|
1168
1304
|
return {
|
|
1169
1305
|
id,
|
|
1170
|
-
logo: chain
|
|
1306
|
+
logo: getLogoFromMetadata(chain) || chainMeta?.logo || "",
|
|
1171
1307
|
name: chain?.name ?? chainMeta?.name ?? "",
|
|
1172
1308
|
};
|
|
1173
1309
|
};
|
|
@@ -1180,6 +1316,7 @@ const normalizeSdkIntentToken = (token, chainId) => {
|
|
|
1180
1316
|
token?.tokenAddress ??
|
|
1181
1317
|
zeroAddress,
|
|
1182
1318
|
decimals: Number.isFinite(decimals) ? decimals : 18,
|
|
1319
|
+
logo: getLogoFromMetadata(token) || undefined,
|
|
1183
1320
|
symbol: token?.symbol ?? token?.tokenSymbol ?? "",
|
|
1184
1321
|
};
|
|
1185
1322
|
};
|
|
@@ -1250,11 +1387,14 @@ const normalizeSwapAndExecuteRequirementIntent = (intent) => {
|
|
|
1250
1387
|
};
|
|
1251
1388
|
const normalizeRenderableSwapIntentData = (rawIntent, bridgeProvider) => {
|
|
1252
1389
|
const direct = normalizeSwapIntentData(rawIntent);
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1390
|
+
const normalizedIntent = direct
|
|
1391
|
+
? null
|
|
1392
|
+
: normalizeSwapIntentData(rawIntent?.normalizedIntent);
|
|
1393
|
+
const nestedSwap = direct || normalizedIntent ? null : normalizeSwapIntentData(rawIntent?.swap);
|
|
1394
|
+
const requirement = direct || normalizedIntent || nestedSwap
|
|
1255
1395
|
? null
|
|
1256
1396
|
: normalizeSwapAndExecuteRequirementIntent(rawIntent);
|
|
1257
|
-
const normalized = direct ?? nestedSwap ?? requirement;
|
|
1397
|
+
const normalized = direct ?? normalizedIntent ?? nestedSwap ?? requirement;
|
|
1258
1398
|
if (!normalized)
|
|
1259
1399
|
return null;
|
|
1260
1400
|
return bridgeProvider === undefined
|
|
@@ -1341,7 +1481,9 @@ const getSourceRows = (entry) => {
|
|
|
1341
1481
|
token.symbol === source.token.symbol));
|
|
1342
1482
|
return {
|
|
1343
1483
|
key: `${source.chain.id}-${source.token.contractAddress}-${index}`,
|
|
1344
|
-
|
|
1484
|
+
chainId: source.chain.id,
|
|
1485
|
+
contractAddress: source.token.contractAddress,
|
|
1486
|
+
tokenLogo: source.token.logo || fallback?.logo,
|
|
1345
1487
|
chainLogo: source.chain.logo || fallback?.chainLogo,
|
|
1346
1488
|
symbol: source.token.symbol,
|
|
1347
1489
|
chainName: getShortChainName(source.chain.id, source.chain.name),
|
|
@@ -1349,12 +1491,14 @@ const getSourceRows = (entry) => {
|
|
|
1349
1491
|
value: source.value,
|
|
1350
1492
|
};
|
|
1351
1493
|
});
|
|
1352
|
-
return displayDestinationSourceRow
|
|
1494
|
+
return mergeHistorySourceRows(displayDestinationSourceRow
|
|
1353
1495
|
? [displayDestinationSourceRow, ...sourceRows]
|
|
1354
|
-
: sourceRows;
|
|
1496
|
+
: sourceRows);
|
|
1355
1497
|
}
|
|
1356
1498
|
const fallbackRows = entry.fromTokens.map((token, index) => ({
|
|
1357
1499
|
key: `${token.chainId}-${token.contractAddress}-${index}`,
|
|
1500
|
+
chainId: token.chainId,
|
|
1501
|
+
contractAddress: token.contractAddress,
|
|
1358
1502
|
tokenLogo: token.logo,
|
|
1359
1503
|
chainLogo: token.chainLogo,
|
|
1360
1504
|
symbol: token.symbol,
|
|
@@ -1362,9 +1506,9 @@ const getSourceRows = (entry) => {
|
|
|
1362
1506
|
amount: token.userAmount || "0",
|
|
1363
1507
|
value: token.balanceInFiat,
|
|
1364
1508
|
}));
|
|
1365
|
-
return displayDestinationSourceRow
|
|
1509
|
+
return mergeHistorySourceRows(displayDestinationSourceRow
|
|
1366
1510
|
? [displayDestinationSourceRow, ...fallbackRows]
|
|
1367
|
-
: fallbackRows;
|
|
1511
|
+
: fallbackRows);
|
|
1368
1512
|
};
|
|
1369
1513
|
function SourceRowsList({ entry, maxHeight = 236, borderTopFirst = true, scrollAfterRows = 4, }) {
|
|
1370
1514
|
const rows = getSourceRows(entry);
|
|
@@ -1437,6 +1581,10 @@ function SwapReceiptPanel({ entry, onDone, }) {
|
|
|
1437
1581
|
const chainName = getShortChainName(destination?.chain.id ?? entry.toToken?.chainId, destination?.chain.name || entry.toToken?.chainName || "");
|
|
1438
1582
|
const depositVenue = entry.opportunity?.title || entry.opportunity?.protocol || chainName;
|
|
1439
1583
|
const amount = destination?.amount || "";
|
|
1584
|
+
const destinationLogo = destination?.token.logo ||
|
|
1585
|
+
entry.toToken?.logo ||
|
|
1586
|
+
entry.opportunity?.tokenLogo ||
|
|
1587
|
+
(isDeposit ? entry.opportunity?.logo : undefined);
|
|
1440
1588
|
const requestedExactOutAmount = (isDeposit || isSend) && entry.requestedToAmount
|
|
1441
1589
|
? entry.requestedToAmount
|
|
1442
1590
|
: undefined;
|
|
@@ -1489,9 +1637,7 @@ function SwapReceiptPanel({ entry, onDone, }) {
|
|
|
1489
1637
|
display: "inline-flex",
|
|
1490
1638
|
marginBottom: "10px",
|
|
1491
1639
|
position: "relative",
|
|
1492
|
-
}, children: [_jsx(MiniLogo, { fontSize: 17, label: tokenSymbol, size: 45, src:
|
|
1493
|
-
? entry.opportunity?.logo || entry.toToken?.logo
|
|
1494
|
-
: entry.toToken?.logo }), _jsx("div", { style: {
|
|
1640
|
+
}, children: [_jsx(MiniLogo, { fontSize: 17, label: tokenSymbol, size: 45, src: destinationLogo }), _jsx("div", { style: {
|
|
1495
1641
|
alignItems: "center",
|
|
1496
1642
|
background: isFailed
|
|
1497
1643
|
? "#E92C2C"
|
|
@@ -1719,7 +1865,10 @@ function SwapHistoryPanel({ entries, now, }) {
|
|
|
1719
1865
|
width: "100%",
|
|
1720
1866
|
}, children: sortedEntries.map((entry) => {
|
|
1721
1867
|
const destination = entry.intentData?.destination;
|
|
1722
|
-
const destinationLogo =
|
|
1868
|
+
const destinationLogo = destination?.token.logo ||
|
|
1869
|
+
entry.toToken?.logo ||
|
|
1870
|
+
entry.opportunity?.tokenLogo ||
|
|
1871
|
+
"";
|
|
1723
1872
|
const destinationChainLogo = destination?.chain.logo || entry.toToken?.chainLogo || "";
|
|
1724
1873
|
const destinationChainName = getShortChainName(destination?.chain.id ?? entry.toToken?.chainId, destination?.chain.name || entry.toToken?.chainName || "");
|
|
1725
1874
|
const destinationSymbol = destination?.token.symbol || entry.toToken?.symbol || "";
|
|
@@ -1865,10 +2014,11 @@ export function NexusWidget(props) {
|
|
|
1865
2014
|
}, type: "button", children: "Reload Page" })] }), children: _jsx(NexusWidgetInner, { ...props }) }));
|
|
1866
2015
|
}
|
|
1867
2016
|
function NexusWidgetInner({ config: rawConfig, embed = true, className, connectedAddress, open: controlledOpen, onOpenChange, defaultOpen = false, onComplete, onConnectClick, onStart, onError, onReceiveAssetChange, onClose, onConnectWallet, }) {
|
|
1868
|
-
const { nexusSDK, bridgableBalance, swapBalance, getFiatValue, resolveTokenUsdRate, swapSupportedChainsAndTokens, supportedChainsAndTokens, fetchSwapBalance, handleInit, swapIntent: providerSwapIntent, network, loading: nexusLoading, } = useNexus();
|
|
2017
|
+
const { nexusSDK, bridgableBalance, swapBalance, swapBalanceLoading, getFiatValue, resolveTokenUsdRate, swapSupportedChainsAndTokens, supportedChainsAndTokens, fetchSwapBalance, handleInit, swapIntent: providerSwapIntent, network, loading: nexusLoading, } = useNexus();
|
|
1869
2018
|
const appConfig = useMemo(() => ({
|
|
1870
2019
|
nexusNetwork: network ?? "mainnet",
|
|
1871
2020
|
}), [network]);
|
|
2021
|
+
const isSwapBalancePending = Boolean(nexusSDK) && (swapBalanceLoading || swapBalance === null);
|
|
1872
2022
|
const normalizedWidgetConfig = useMemo(() => normalizeNexusWidgetConfig(rawConfig), [rawConfig]);
|
|
1873
2023
|
const config = normalizedWidgetConfig.config;
|
|
1874
2024
|
const activeMode = normalizedWidgetConfig.activeMode;
|
|
@@ -1878,7 +2028,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
1878
2028
|
const isConfiguredAmountFixed = normalizedWidgetConfig.isAmountFixed;
|
|
1879
2029
|
const isConfiguredRecipientLocked = normalizedWidgetConfig.isRecipientLocked;
|
|
1880
2030
|
const primaryColor = normalizeNexusWidgetPrimaryColor(appearanceConfig?.primaryColor);
|
|
1881
|
-
const primaryButtonBackground = primaryColor ??
|
|
2031
|
+
const primaryButtonBackground = primaryColor ?? NEXUS_WIDGET_DEFAULT_PRIMARY_COLOR;
|
|
1882
2032
|
const primaryButtonForeground = getReadableTextColor(primaryButtonBackground);
|
|
1883
2033
|
const theme = useMemo(() => ({
|
|
1884
2034
|
...nexusWidgetTheme,
|
|
@@ -1920,7 +2070,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
1920
2070
|
preloadReceiveTokens();
|
|
1921
2071
|
}
|
|
1922
2072
|
}, [nexusSDK]);
|
|
1923
|
-
const { connector, status: walletStatus } = useAccount();
|
|
2073
|
+
const { address: accountAddress, connector, status: walletStatus, } = useAccount();
|
|
1924
2074
|
const { connectors, connectAsync, isPending: isWalletConnectPending, } = useConnect();
|
|
1925
2075
|
const { data: walletClient } = useWalletClient();
|
|
1926
2076
|
const { data: connectorClient } = useConnectorClient();
|
|
@@ -1930,12 +2080,66 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
1930
2080
|
isAddress(connectedAddress) &&
|
|
1931
2081
|
connectedAddress.toLowerCase() !== zeroAddress
|
|
1932
2082
|
? connectedAddress
|
|
1933
|
-
:
|
|
1934
|
-
isAddress(
|
|
1935
|
-
|
|
1936
|
-
?
|
|
2083
|
+
: accountAddress &&
|
|
2084
|
+
isAddress(accountAddress) &&
|
|
2085
|
+
accountAddress.toLowerCase() !== zeroAddress
|
|
2086
|
+
? accountAddress
|
|
2087
|
+
: walletClientAddress &&
|
|
2088
|
+
isAddress(walletClientAddress) &&
|
|
2089
|
+
walletClientAddress.toLowerCase() !== zeroAddress
|
|
2090
|
+
? walletClientAddress
|
|
2091
|
+
: undefined;
|
|
2092
|
+
const getEffectiveWalletProvider = useCallback(async (activeConnector = connector) => {
|
|
2093
|
+
let connectorProvider;
|
|
2094
|
+
try {
|
|
2095
|
+
connectorProvider = await activeConnector?.getProvider();
|
|
2096
|
+
}
|
|
2097
|
+
catch {
|
|
2098
|
+
connectorProvider = undefined;
|
|
2099
|
+
}
|
|
2100
|
+
const connectorClientProvider = connectorClient
|
|
2101
|
+
? {
|
|
2102
|
+
request: (args) => connectorClient.request(args),
|
|
2103
|
+
}
|
|
2104
|
+
: undefined;
|
|
2105
|
+
const walletClientProvider = walletClient
|
|
2106
|
+
? {
|
|
2107
|
+
request: (args) => walletClient.request(args),
|
|
2108
|
+
}
|
|
2109
|
+
: undefined;
|
|
2110
|
+
const windowProvider = typeof window !== "undefined"
|
|
2111
|
+
? window.ethereum
|
|
1937
2112
|
: undefined;
|
|
2113
|
+
const effectiveProvider = connectorProvider &&
|
|
2114
|
+
typeof connectorProvider.request === "function"
|
|
2115
|
+
? connectorProvider
|
|
2116
|
+
: (connectorClientProvider ?? walletClientProvider ?? windowProvider);
|
|
2117
|
+
return effectiveProvider &&
|
|
2118
|
+
typeof effectiveProvider.request === "function"
|
|
2119
|
+
? effectiveProvider
|
|
2120
|
+
: undefined;
|
|
2121
|
+
}, [connector, connectorClient, walletClient]);
|
|
1938
2122
|
const historyStorageKey = getSwapHistoryStorageKey(ownerAddress);
|
|
2123
|
+
useEffect(() => {
|
|
2124
|
+
if (walletStatus !== "connected" || !ownerAddress) {
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
let cancelled = false;
|
|
2128
|
+
void (async () => {
|
|
2129
|
+
const effectiveProvider = await getEffectiveWalletProvider();
|
|
2130
|
+
if (cancelled || !effectiveProvider) {
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
await handleInit(effectiveProvider, ownerAddress);
|
|
2134
|
+
})().catch((error) => {
|
|
2135
|
+
if (!cancelled) {
|
|
2136
|
+
console.error("Unable to initialize Nexus for wallet address:", error);
|
|
2137
|
+
}
|
|
2138
|
+
});
|
|
2139
|
+
return () => {
|
|
2140
|
+
cancelled = true;
|
|
2141
|
+
};
|
|
2142
|
+
}, [getEffectiveWalletProvider, handleInit, ownerAddress, walletStatus]);
|
|
1939
2143
|
// Global form state
|
|
1940
2144
|
const [amount, setAmount] = useState("");
|
|
1941
2145
|
const [recipientAddress, setRecipientAddress] = useState("");
|
|
@@ -1961,7 +2165,6 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
1961
2165
|
: hasCustomSwapRecipient
|
|
1962
2166
|
? recipientAddress
|
|
1963
2167
|
: undefined;
|
|
1964
|
-
const previousDefaultRecipientRef = useRef(defaultRecipientAddress);
|
|
1965
2168
|
// Swap-specific
|
|
1966
2169
|
const [swapType, setSwapType] = useState("exactIn");
|
|
1967
2170
|
const [swapStep, setSwapStep] = useState("idle");
|
|
@@ -1978,6 +2181,12 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
1978
2181
|
const [fromTokens, setFromTokens] = useState([]);
|
|
1979
2182
|
const [sourceSelectionTouched, setSourceSelectionTouched] = useState(false);
|
|
1980
2183
|
const [sourceSelectionRevision, setSourceSelectionRevision] = useState(0);
|
|
2184
|
+
const [sourceSelectionRequiredUsdDisplay, setSourceSelectionRequiredUsdDisplay,] = useState(undefined);
|
|
2185
|
+
const [sourcePickerDraftTokens, setSourcePickerDraftTokens] = useState(null);
|
|
2186
|
+
const sourcePickerDraftTokensRef = useRef(null);
|
|
2187
|
+
const sourcePickerDraftDepositFilterRef = useRef("all");
|
|
2188
|
+
const sourcePickerDraftTouchedRef = useRef(false);
|
|
2189
|
+
const sourcePickerDraftModeRef = useRef("all");
|
|
1981
2190
|
const [exactOutQuoteSourceMode, setExactOutQuoteSourceMode] = useState("all");
|
|
1982
2191
|
const exactOutQuoteSourceModeRef = useRef("all");
|
|
1983
2192
|
const [toToken, setToToken] = useState(undefined);
|
|
@@ -2060,17 +2269,14 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2060
2269
|
setExactOutQuoteSourceModeValue("all");
|
|
2061
2270
|
}, [activeMode, setExactOutQuoteSourceModeValue]);
|
|
2062
2271
|
useEffect(() => {
|
|
2063
|
-
|
|
2064
|
-
previousDefaultRecipientRef.current = defaultRecipientAddress;
|
|
2065
|
-
if (activeMode !== "swap" || !defaultRecipientAddress)
|
|
2272
|
+
if (activeMode !== "swap")
|
|
2066
2273
|
return;
|
|
2067
2274
|
setRecipientAddress((current) => {
|
|
2068
|
-
if (!
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
return current;
|
|
2275
|
+
if (!defaultRecipientAddress)
|
|
2276
|
+
return current ? "" : current;
|
|
2277
|
+
return current.toLowerCase() === defaultRecipientAddress.toLowerCase()
|
|
2278
|
+
? current
|
|
2279
|
+
: defaultRecipientAddress;
|
|
2074
2280
|
});
|
|
2075
2281
|
}, [activeMode, defaultRecipientAddress]);
|
|
2076
2282
|
const { steps, seed, onStepsList, onStepComplete, reset: resetSteps, } = useTransactionSteps();
|
|
@@ -2095,6 +2301,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2095
2301
|
const widgetOpenedFiredRef = useRef(false);
|
|
2096
2302
|
const reachedTerminalRef = useRef(false);
|
|
2097
2303
|
const lastIntentSourceTokensRef = useRef([]);
|
|
2304
|
+
const lastAutoIntentSourceTokensRef = useRef([]);
|
|
2098
2305
|
const immediateQuoteAfterSourceEditRef = useRef(false);
|
|
2099
2306
|
const amountEnteredLastValueRef = useRef("");
|
|
2100
2307
|
const lastInputMethodRef = useRef("typed");
|
|
@@ -2123,11 +2330,14 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2123
2330
|
const [quoteRefreshing, setQuoteRefreshing] = useState(false);
|
|
2124
2331
|
const [receiveMaxCalculating, setReceiveMaxCalculating] = useState(false);
|
|
2125
2332
|
const [maxCalculationPercent, setMaxCalculationPercent] = useState(null);
|
|
2126
|
-
const maxSwapQuoteCacheRef = useRef({});
|
|
2127
2333
|
const intentDestinationUsdRateCacheRef = useRef({});
|
|
2128
2334
|
const intentSymbolUsdRateCacheRef = useRef({});
|
|
2129
2335
|
const predictiveQuoteCacheRef = useRef({});
|
|
2130
2336
|
const predictiveQuoteRunRef = useRef(0);
|
|
2337
|
+
const latestIntentFetchRunIdRef = useRef(0);
|
|
2338
|
+
const intentFetchTimingRef = useRef(null);
|
|
2339
|
+
const intentUiRenderLoggedRunIdRef = useRef(null);
|
|
2340
|
+
const [intentFetchTiming, setIntentFetchTiming] = useState(null);
|
|
2131
2341
|
const [predictiveQuote, setPredictiveQuote] = useState(null);
|
|
2132
2342
|
const maxPercentRunRef = useRef(0);
|
|
2133
2343
|
const [previewQuoteRefreshing, setPreviewQuoteRefreshing] = useState(false);
|
|
@@ -2154,6 +2364,68 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2154
2364
|
const activeQuoteInputKeyRef = useRef("");
|
|
2155
2365
|
// Ref to store swap intent hook allow/deny callbacks
|
|
2156
2366
|
const swapIntentRef = useRef(null);
|
|
2367
|
+
const startIntentFetchTiming = useCallback(({ background, operation, quoteInputKey, runId, }) => {
|
|
2368
|
+
const startTimeMs = Date.now();
|
|
2369
|
+
const timing = {
|
|
2370
|
+
background,
|
|
2371
|
+
operation,
|
|
2372
|
+
quoteInputKey,
|
|
2373
|
+
runId,
|
|
2374
|
+
startTimeMs,
|
|
2375
|
+
walletAddress: ownerAddress ?? null,
|
|
2376
|
+
};
|
|
2377
|
+
latestIntentFetchRunIdRef.current = runId;
|
|
2378
|
+
intentFetchTimingRef.current = timing;
|
|
2379
|
+
intentUiRenderLoggedRunIdRef.current = null;
|
|
2380
|
+
setIntentFetchTiming(timing);
|
|
2381
|
+
console.log("[NexusWidget] intent fetch start", {
|
|
2382
|
+
runId,
|
|
2383
|
+
walletAddress: timing.walletAddress,
|
|
2384
|
+
operation,
|
|
2385
|
+
quoteInputKey,
|
|
2386
|
+
background,
|
|
2387
|
+
dateNow: startTimeMs,
|
|
2388
|
+
date: new Date(startTimeMs),
|
|
2389
|
+
});
|
|
2390
|
+
return timing;
|
|
2391
|
+
}, [ownerAddress]);
|
|
2392
|
+
const finishIntentFetchTiming = useCallback((runId, status = "resolved") => {
|
|
2393
|
+
const timing = intentFetchTimingRef.current;
|
|
2394
|
+
if (!timing || timing.runId !== runId)
|
|
2395
|
+
return;
|
|
2396
|
+
if (timing.endTimeMs !== undefined)
|
|
2397
|
+
return;
|
|
2398
|
+
const endTimeMs = Date.now();
|
|
2399
|
+
if (latestIntentFetchRunIdRef.current !== runId) {
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2402
|
+
const fetchDurationMs = endTimeMs - timing.startTimeMs;
|
|
2403
|
+
const completedTiming = {
|
|
2404
|
+
...timing,
|
|
2405
|
+
endTimeMs,
|
|
2406
|
+
fetchDurationMs,
|
|
2407
|
+
};
|
|
2408
|
+
intentFetchTimingRef.current = completedTiming;
|
|
2409
|
+
setIntentFetchTiming(completedTiming);
|
|
2410
|
+
console.log("[NexusWidget] intent fetch end", {
|
|
2411
|
+
runId,
|
|
2412
|
+
walletAddress: timing.walletAddress,
|
|
2413
|
+
operation: timing.operation,
|
|
2414
|
+
quoteInputKey: timing.quoteInputKey,
|
|
2415
|
+
status,
|
|
2416
|
+
dateNow: endTimeMs,
|
|
2417
|
+
date: new Date(endTimeMs),
|
|
2418
|
+
});
|
|
2419
|
+
console.log("[NexusWidget] intent fetch total", {
|
|
2420
|
+
runId,
|
|
2421
|
+
walletAddress: timing.walletAddress,
|
|
2422
|
+
operation: timing.operation,
|
|
2423
|
+
quoteInputKey: timing.quoteInputKey,
|
|
2424
|
+
totalTimeSeconds: fetchDurationMs / 1000,
|
|
2425
|
+
elapsedMs: fetchDurationMs,
|
|
2426
|
+
dateNow: endTimeMs,
|
|
2427
|
+
});
|
|
2428
|
+
}, []);
|
|
2157
2429
|
useEffect(() => {
|
|
2158
2430
|
swapStepRef.current = swapStep;
|
|
2159
2431
|
}, [swapStep]);
|
|
@@ -2297,6 +2569,112 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2297
2569
|
return () => window.clearInterval(timer);
|
|
2298
2570
|
}, [swapStep]);
|
|
2299
2571
|
const normalizeAddress = (value) => (value ?? "").toLowerCase();
|
|
2572
|
+
const getTokenLookupAddress = (value) => {
|
|
2573
|
+
if (!value)
|
|
2574
|
+
return "";
|
|
2575
|
+
return isNativeTokenAddress(value) ? zeroAddress : value.toLowerCase();
|
|
2576
|
+
};
|
|
2577
|
+
const findBalanceTokenLogo = (chainId, contractAddress, symbol) => {
|
|
2578
|
+
if (!chainId)
|
|
2579
|
+
return "";
|
|
2580
|
+
const lookupAddress = getTokenLookupAddress(contractAddress);
|
|
2581
|
+
const lookupSymbol = symbol?.toUpperCase();
|
|
2582
|
+
let symbolMatchLogo = "";
|
|
2583
|
+
for (const asset of swapBalance ?? []) {
|
|
2584
|
+
for (const breakdown of asset.breakdown ?? []) {
|
|
2585
|
+
if (breakdown.chain?.id !== chainId)
|
|
2586
|
+
continue;
|
|
2587
|
+
const breakdownAddress = getTokenLookupAddress(breakdown.contractAddress);
|
|
2588
|
+
const addressMatches = lookupAddress && breakdownAddress === lookupAddress;
|
|
2589
|
+
const symbolMatches = lookupSymbol &&
|
|
2590
|
+
[breakdown.symbol, asset.symbol]
|
|
2591
|
+
.filter(Boolean)
|
|
2592
|
+
.some((candidate) => candidate.toUpperCase() === lookupSymbol);
|
|
2593
|
+
const logo = getLogoFromMetadata(breakdown) || getLogoFromMetadata(asset);
|
|
2594
|
+
if (addressMatches && logo)
|
|
2595
|
+
return logo;
|
|
2596
|
+
if (!symbolMatchLogo && symbolMatches && logo) {
|
|
2597
|
+
symbolMatchLogo = logo;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
return symbolMatchLogo;
|
|
2602
|
+
};
|
|
2603
|
+
const findSupportedTokenLogo = (chains, chainId, contractAddress, symbol) => {
|
|
2604
|
+
if (!chainId)
|
|
2605
|
+
return "";
|
|
2606
|
+
const chain = chains?.find((item) => Number(item?.id ?? item?.chainId) === chainId);
|
|
2607
|
+
const tokens = chain?.tokens ?? chain?.assets ?? [];
|
|
2608
|
+
const lookupAddress = getTokenLookupAddress(contractAddress);
|
|
2609
|
+
const lookupSymbol = symbol?.toUpperCase();
|
|
2610
|
+
const addressMatch = lookupAddress &&
|
|
2611
|
+
tokens.find((token) => {
|
|
2612
|
+
const tokenAddress = getTokenLookupAddress(token?.contractAddress ?? token?.address ?? token?.tokenAddress);
|
|
2613
|
+
return tokenAddress === lookupAddress;
|
|
2614
|
+
});
|
|
2615
|
+
const symbolMatch = lookupSymbol &&
|
|
2616
|
+
tokens.find((token) => {
|
|
2617
|
+
const tokenSymbol = (token?.symbol ?? token?.tokenSymbol ?? "")
|
|
2618
|
+
.toString()
|
|
2619
|
+
.toUpperCase();
|
|
2620
|
+
return tokenSymbol === lookupSymbol;
|
|
2621
|
+
});
|
|
2622
|
+
return getLogoFromMetadata(addressMatch) || getLogoFromMetadata(symbolMatch);
|
|
2623
|
+
};
|
|
2624
|
+
const resolveTokenLogo = (token, chainId) => {
|
|
2625
|
+
const explicitLogo = getLogoFromMetadata(token);
|
|
2626
|
+
if (explicitLogo)
|
|
2627
|
+
return explicitLogo;
|
|
2628
|
+
const contractAddress = token?.contractAddress ?? token?.address ?? token?.tokenAddress;
|
|
2629
|
+
const symbol = token?.symbol ?? token?.tokenSymbol;
|
|
2630
|
+
const citreaToken = findCitreaReceiveToken({
|
|
2631
|
+
address: contractAddress,
|
|
2632
|
+
chainId,
|
|
2633
|
+
symbol,
|
|
2634
|
+
});
|
|
2635
|
+
const tokenMeta = symbol
|
|
2636
|
+
? TOKEN_METADATA[symbol]
|
|
2637
|
+
: undefined;
|
|
2638
|
+
return (findBalanceTokenLogo(chainId, contractAddress, symbol) ||
|
|
2639
|
+
findSupportedTokenLogo(swapSupportedChainsAndTokens, chainId, contractAddress, symbol) ||
|
|
2640
|
+
findSupportedTokenLogo(supportedChainsAndTokens, chainId, contractAddress, symbol) ||
|
|
2641
|
+
citreaToken?.logo ||
|
|
2642
|
+
tokenMeta?.logo ||
|
|
2643
|
+
(contractAddress && isNativeTokenAddress(contractAddress)
|
|
2644
|
+
? CHAIN_METADATA[chainId ?? 0]?.logo
|
|
2645
|
+
: "") ||
|
|
2646
|
+
"");
|
|
2647
|
+
};
|
|
2648
|
+
const enrichIntentToken = (token, chainId) => {
|
|
2649
|
+
const logo = resolveTokenLogo(token, chainId);
|
|
2650
|
+
return logo && logo !== token.logo ? { ...token, logo } : token;
|
|
2651
|
+
};
|
|
2652
|
+
const enrichTokenOptionLogo = (token) => {
|
|
2653
|
+
if (!token)
|
|
2654
|
+
return undefined;
|
|
2655
|
+
const chainMeta = token.chainId ? CHAIN_METADATA[token.chainId] : undefined;
|
|
2656
|
+
const logo = resolveTokenLogo(token, token.chainId);
|
|
2657
|
+
const next = {
|
|
2658
|
+
...token,
|
|
2659
|
+
chainLogo: token.chainLogo || chainMeta?.logo,
|
|
2660
|
+
logo: token.logo || logo,
|
|
2661
|
+
};
|
|
2662
|
+
if (next.chainLogo === token.chainLogo && next.logo === token.logo) {
|
|
2663
|
+
return token;
|
|
2664
|
+
}
|
|
2665
|
+
return next;
|
|
2666
|
+
};
|
|
2667
|
+
const enrichSwapIntentTokenMetadata = (intent) => ({
|
|
2668
|
+
...intent,
|
|
2669
|
+
destination: {
|
|
2670
|
+
...intent.destination,
|
|
2671
|
+
token: enrichIntentToken(intent.destination.token, intent.destination.chain.id),
|
|
2672
|
+
},
|
|
2673
|
+
sources: (intent.sources ?? []).map((source) => ({
|
|
2674
|
+
...source,
|
|
2675
|
+
token: enrichIntentToken(source.token, source.chain.id),
|
|
2676
|
+
})),
|
|
2677
|
+
});
|
|
2300
2678
|
const buildIntentSourceToken = (source) => {
|
|
2301
2679
|
let matchedAsset;
|
|
2302
2680
|
let matchedBreakdown;
|
|
@@ -2326,7 +2704,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2326
2704
|
const sourceDecimals = isNativeSource && nativeCurrency?.decimals !== undefined
|
|
2327
2705
|
? nativeCurrency.decimals
|
|
2328
2706
|
: source.token.decimals;
|
|
2329
|
-
const sourceLogo =
|
|
2707
|
+
const sourceLogo = resolveTokenLogo(source.token, source.chain.id);
|
|
2330
2708
|
return {
|
|
2331
2709
|
contractAddress: source.token.contractAddress,
|
|
2332
2710
|
symbol: sourceSymbol,
|
|
@@ -2412,8 +2790,9 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2412
2790
|
return undefined;
|
|
2413
2791
|
}
|
|
2414
2792
|
};
|
|
2415
|
-
const minimumSourceUsd = new Decimal(
|
|
2416
|
-
const
|
|
2793
|
+
const minimumSourceUsd = new Decimal(1);
|
|
2794
|
+
const hasMinimumSourceUsdValue = (value) => (parseFiatNumber(value) ?? new Decimal(0)).gte(minimumSourceUsd);
|
|
2795
|
+
const hasMinimumSourceUsdBalance = (token) => hasMinimumSourceUsdValue(token.balanceInFiat);
|
|
2417
2796
|
const filterMinimumSourceUsdTokens = (tokens) => tokens.filter(hasMinimumSourceUsdBalance);
|
|
2418
2797
|
const getTokenUsdRateCacheKeyFromParts = (chainId, contractAddress, symbol) => {
|
|
2419
2798
|
if (!chainId || !symbol)
|
|
@@ -2485,6 +2864,10 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2485
2864
|
if (tokenBalance.gt(0) && fiatBalance.gt(0)) {
|
|
2486
2865
|
return fiatBalance.div(tokenBalance);
|
|
2487
2866
|
}
|
|
2867
|
+
const priceUsd = parseFiatNumber(token.priceUSD);
|
|
2868
|
+
if (priceUsd && priceUsd.gt(0)) {
|
|
2869
|
+
return priceUsd;
|
|
2870
|
+
}
|
|
2488
2871
|
const fallbackRate = getFiatValue(1, token.symbol);
|
|
2489
2872
|
if (Number.isFinite(fallbackRate) && fallbackRate > 0) {
|
|
2490
2873
|
return new Decimal(fallbackRate);
|
|
@@ -2502,42 +2885,80 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2502
2885
|
const rate = parseFiatNumber(cached?.rate);
|
|
2503
2886
|
return rate && rate.gt(0) ? rate : new Decimal(0);
|
|
2504
2887
|
};
|
|
2505
|
-
const
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2888
|
+
const getFeeTokenLookupAddress = (value) => {
|
|
2889
|
+
const lower = (value ?? "").toLowerCase();
|
|
2890
|
+
if (!lower || lower === "0x" || isNativeTokenAddress(lower)) {
|
|
2891
|
+
return zeroAddress;
|
|
2892
|
+
}
|
|
2893
|
+
return lower;
|
|
2894
|
+
};
|
|
2895
|
+
const getTokenUsdRateFromBalances = (chainId, contractAddress, symbol) => {
|
|
2896
|
+
if (!chainId)
|
|
2509
2897
|
return undefined;
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
.
|
|
2898
|
+
const lookupAddress = getFeeTokenLookupAddress(contractAddress);
|
|
2899
|
+
const lookupSymbol = symbol?.toUpperCase();
|
|
2900
|
+
for (const asset of swapBalance ?? []) {
|
|
2901
|
+
for (const breakdown of asset.breakdown ?? []) {
|
|
2902
|
+
if (breakdown.chain?.id !== chainId)
|
|
2903
|
+
continue;
|
|
2904
|
+
const breakdownAddress = getFeeTokenLookupAddress(breakdown.contractAddress);
|
|
2905
|
+
const addressMatches = lookupAddress && breakdownAddress === lookupAddress;
|
|
2906
|
+
const symbolMatches = lookupSymbol &&
|
|
2907
|
+
[breakdown.symbol, asset.symbol]
|
|
2908
|
+
.filter(Boolean)
|
|
2909
|
+
.some((candidate) => candidate.toUpperCase() === lookupSymbol);
|
|
2910
|
+
if (!addressMatches && !symbolMatches)
|
|
2911
|
+
continue;
|
|
2912
|
+
const balance = parseFiatNumber(breakdown.balance);
|
|
2913
|
+
const fiatBalance = parseFiatNumber(breakdown.balanceInFiat);
|
|
2914
|
+
if (balance && fiatBalance && balance.gt(0) && fiatBalance.gt(0)) {
|
|
2915
|
+
return fiatBalance.div(balance);
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
return undefined;
|
|
2514
2920
|
};
|
|
2515
|
-
const
|
|
2516
|
-
if (!
|
|
2517
|
-
return
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2921
|
+
const getTokenUsdRateFromSupportedChains = (chains, chainId, contractAddress, symbol) => {
|
|
2922
|
+
if (!chainId)
|
|
2923
|
+
return undefined;
|
|
2924
|
+
const chain = chains?.find((item) => Number(item?.id ?? item?.chainId) === chainId);
|
|
2925
|
+
const tokens = chain?.tokens ?? chain?.assets ?? [];
|
|
2926
|
+
const lookupAddress = getFeeTokenLookupAddress(contractAddress);
|
|
2927
|
+
const lookupSymbol = symbol?.toUpperCase();
|
|
2928
|
+
const matchedToken = tokens.find((token) => {
|
|
2929
|
+
const tokenAddress = getFeeTokenLookupAddress(token?.contractAddress ?? token?.address ?? token?.tokenAddress);
|
|
2930
|
+
const addressMatches = lookupAddress && tokenAddress === lookupAddress;
|
|
2931
|
+
const tokenSymbol = (token?.symbol ?? token?.tokenSymbol ?? "")
|
|
2932
|
+
.toString()
|
|
2933
|
+
.toUpperCase();
|
|
2934
|
+
const symbolMatches = lookupSymbol && tokenSymbol === lookupSymbol;
|
|
2935
|
+
return addressMatches || symbolMatches;
|
|
2936
|
+
});
|
|
2937
|
+
const priceUsd = parseFiatNumber(matchedToken?.priceUSD ??
|
|
2938
|
+
matchedToken?.priceUsd ??
|
|
2939
|
+
matchedToken?.usdPrice ??
|
|
2940
|
+
matchedToken?.price);
|
|
2941
|
+
return priceUsd && priceUsd.gt(0) ? priceUsd : undefined;
|
|
2523
2942
|
};
|
|
2524
|
-
const
|
|
2525
|
-
const
|
|
2526
|
-
|
|
2943
|
+
const getIntentDestinationGasUsdValue = (intent) => {
|
|
2944
|
+
const gas = intent?.destination?.gas;
|
|
2945
|
+
const explicitValue = parseFiatNumber(gas?.value);
|
|
2946
|
+
if (explicitValue && explicitValue.gt(0))
|
|
2947
|
+
return explicitValue;
|
|
2948
|
+
const amount = parseFiatNumber(gas?.amount);
|
|
2949
|
+
if (!amount || amount.lte(0))
|
|
2950
|
+
return undefined;
|
|
2951
|
+
const chainId = intent?.destination?.chain?.id;
|
|
2952
|
+
const contractAddress = gas?.token?.contractAddress;
|
|
2953
|
+
const symbol = gas?.token?.symbol;
|
|
2954
|
+
const rate = getTokenUsdRateFromBalances(chainId, contractAddress, symbol) ??
|
|
2955
|
+
getTokenUsdRateFromSupportedChains(swapSupportedChainsAndTokens, chainId, contractAddress, symbol) ??
|
|
2956
|
+
getTokenUsdRateFromSupportedChains(supportedChainsAndTokens, chainId, contractAddress, symbol) ??
|
|
2957
|
+
getUsdRateForSymbol(symbol);
|
|
2958
|
+
return rate.gt(0) ? amount.mul(rate) : undefined;
|
|
2527
2959
|
};
|
|
2528
2960
|
const getCachedDestinationUsdRate = (token) => {
|
|
2529
|
-
|
|
2530
|
-
if (intentCachedRate && intentCachedRate.gt(0)) {
|
|
2531
|
-
return intentCachedRate;
|
|
2532
|
-
}
|
|
2533
|
-
const cached = getCachedMaxSwapQuote(token);
|
|
2534
|
-
if (!cached ||
|
|
2535
|
-
!cached.maxUsdAmount ||
|
|
2536
|
-
cached.maxUsdAmount.lte(0) ||
|
|
2537
|
-
cached.maxTokenAmount.lte(0)) {
|
|
2538
|
-
return undefined;
|
|
2539
|
-
}
|
|
2540
|
-
return cached.maxUsdAmount.div(cached.maxTokenAmount);
|
|
2961
|
+
return getCachedIntentUsdRate(token);
|
|
2541
2962
|
};
|
|
2542
2963
|
const resolveUsdRateForSymbol = async (symbol) => {
|
|
2543
2964
|
if (!symbol)
|
|
@@ -2555,79 +2976,6 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2555
2976
|
return new Decimal(0);
|
|
2556
2977
|
}
|
|
2557
2978
|
};
|
|
2558
|
-
const resolveMaxSwapQuote = async (token) => {
|
|
2559
|
-
const key = getMaxSwapQuoteCacheKey(token);
|
|
2560
|
-
if (!key)
|
|
2561
|
-
return undefined;
|
|
2562
|
-
const cached = maxSwapQuoteCacheRef.current[key];
|
|
2563
|
-
if (cached)
|
|
2564
|
-
return cached;
|
|
2565
|
-
const calculateMaxForSwap = nexusSDK?.calculateMaxForSwap;
|
|
2566
|
-
if (typeof calculateMaxForSwap !== "function" || !token.chainId) {
|
|
2567
|
-
return undefined;
|
|
2568
|
-
}
|
|
2569
|
-
const max = await calculateMaxForSwap({
|
|
2570
|
-
toChainId: token.chainId,
|
|
2571
|
-
toTokenAddress: (token.contractAddress || zeroAddress),
|
|
2572
|
-
});
|
|
2573
|
-
const decimals = Number.isFinite(Number(max.decimals))
|
|
2574
|
-
? Number(max.decimals)
|
|
2575
|
-
: token.decimals || 18;
|
|
2576
|
-
const maxAmount = parseFiatNumber(max.maxAmount) ??
|
|
2577
|
-
(max.maxAmountRaw !== undefined
|
|
2578
|
-
? new Decimal(max.maxAmountRaw.toString()).div(new Decimal(10).pow(decimals))
|
|
2579
|
-
: undefined);
|
|
2580
|
-
if (!maxAmount || maxAmount.lte(0))
|
|
2581
|
-
return undefined;
|
|
2582
|
-
const safeMaxAmount = maxAmount.mul(receiveMaxSafetyMultiplier);
|
|
2583
|
-
const destinationRate = await resolveUsdRateForSymbol(max.symbol || token.symbol);
|
|
2584
|
-
let maxUsdAmount = destinationRate.gt(0)
|
|
2585
|
-
? safeMaxAmount.mul(destinationRate)
|
|
2586
|
-
: undefined;
|
|
2587
|
-
if (!maxUsdAmount || maxUsdAmount.lte(0)) {
|
|
2588
|
-
const sourcesUsd = await (max.sources ?? []).reduce(async (sumPromise, source) => {
|
|
2589
|
-
const sum = await sumPromise;
|
|
2590
|
-
const amount = parseFiatNumber(source.amount) ?? new Decimal(0);
|
|
2591
|
-
if (amount.lte(0))
|
|
2592
|
-
return sum;
|
|
2593
|
-
const sourceRate = await resolveUsdRateForSymbol(source.symbol);
|
|
2594
|
-
return sourceRate.gt(0) ? sum.plus(amount.mul(sourceRate)) : sum;
|
|
2595
|
-
}, Promise.resolve(new Decimal(0)));
|
|
2596
|
-
if (sourcesUsd.gt(0)) {
|
|
2597
|
-
maxUsdAmount = sourcesUsd.mul(receiveMaxSafetyMultiplier);
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
const quote = {
|
|
2601
|
-
decimals,
|
|
2602
|
-
maxTokenAmount: safeMaxAmount,
|
|
2603
|
-
maxUsdAmount,
|
|
2604
|
-
symbol: max.symbol || token.symbol,
|
|
2605
|
-
};
|
|
2606
|
-
maxSwapQuoteCacheRef.current[key] = quote;
|
|
2607
|
-
return quote;
|
|
2608
|
-
};
|
|
2609
|
-
const getPercentAmountFromMaxQuote = async (token, pct, preferUsd) => {
|
|
2610
|
-
const maxQuote = await resolveMaxSwapQuote(token);
|
|
2611
|
-
if (!maxQuote)
|
|
2612
|
-
return undefined;
|
|
2613
|
-
const ratio = new Decimal(pct).div(100);
|
|
2614
|
-
if (preferUsd && maxQuote.maxUsdAmount && maxQuote.maxUsdAmount.gt(0)) {
|
|
2615
|
-
return {
|
|
2616
|
-
amount: maxQuote.maxUsdAmount
|
|
2617
|
-
.mul(ratio)
|
|
2618
|
-
.toDecimalPlaces(2, Decimal.ROUND_DOWN)
|
|
2619
|
-
.toFixed(),
|
|
2620
|
-
mode: "usd",
|
|
2621
|
-
};
|
|
2622
|
-
}
|
|
2623
|
-
return {
|
|
2624
|
-
amount: maxQuote.maxTokenAmount
|
|
2625
|
-
.mul(ratio)
|
|
2626
|
-
.toDecimalPlaces(Math.max(0, maxQuote.decimals), Decimal.ROUND_DOWN)
|
|
2627
|
-
.toFixed(),
|
|
2628
|
-
mode: "token",
|
|
2629
|
-
};
|
|
2630
|
-
};
|
|
2631
2979
|
const getTokenUsdValue = (token, fallbackAmount) => {
|
|
2632
2980
|
const amountNumber = parseFiatNumber(token.userAmount || fallbackAmount) ?? new Decimal(0);
|
|
2633
2981
|
if (amountNumber.lte(0))
|
|
@@ -2642,6 +2990,50 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2642
2990
|
};
|
|
2643
2991
|
const getTokenBalanceAmount = (token) => parseFiatNumber(token.balance) ?? new Decimal(0);
|
|
2644
2992
|
const getTokenBalanceUsd = (token) => parseFiatNumber(token.balanceInFiat) ?? new Decimal(0);
|
|
2993
|
+
const getSdkExactOutSourcePriority = (token) => {
|
|
2994
|
+
const isSameChain = Boolean(token.chainId && toToken?.chainId) &&
|
|
2995
|
+
token.chainId === toToken?.chainId;
|
|
2996
|
+
const isSameToken = Boolean(token.contractAddress && toToken?.contractAddress) &&
|
|
2997
|
+
((isNativeTokenAddress(token.contractAddress) &&
|
|
2998
|
+
isNativeTokenAddress(toToken?.contractAddress)) ||
|
|
2999
|
+
token.contractAddress.toLowerCase() ===
|
|
3000
|
+
toToken?.contractAddress.toLowerCase());
|
|
3001
|
+
const isStable = isSdkExactOutStableSymbol(token.symbol);
|
|
3002
|
+
const isGas = isNativeTokenAddress(token.contractAddress);
|
|
3003
|
+
const isEthereum = token.chainId === ETHEREUM_MAINNET_CHAIN_ID;
|
|
3004
|
+
if (isSameChain) {
|
|
3005
|
+
if (isSameToken)
|
|
3006
|
+
return 1;
|
|
3007
|
+
if (isStable)
|
|
3008
|
+
return 2;
|
|
3009
|
+
if (isGas)
|
|
3010
|
+
return 3;
|
|
3011
|
+
return 4;
|
|
3012
|
+
}
|
|
3013
|
+
if (isEthereum) {
|
|
3014
|
+
if (isSameToken)
|
|
3015
|
+
return 8;
|
|
3016
|
+
if (isStable)
|
|
3017
|
+
return 9;
|
|
3018
|
+
if (isGas)
|
|
3019
|
+
return 10;
|
|
3020
|
+
return 11;
|
|
3021
|
+
}
|
|
3022
|
+
if (isSameToken)
|
|
3023
|
+
return 5;
|
|
3024
|
+
if (isStable)
|
|
3025
|
+
return 6;
|
|
3026
|
+
return 7;
|
|
3027
|
+
};
|
|
3028
|
+
const sortExactOutSourcesBySdkPriority = (tokens) => [...tokens].sort((a, b) => {
|
|
3029
|
+
const priorityDiff = getSdkExactOutSourcePriority(a) - getSdkExactOutSourcePriority(b);
|
|
3030
|
+
if (priorityDiff !== 0)
|
|
3031
|
+
return priorityDiff;
|
|
3032
|
+
const usdDiff = getTokenBalanceUsd(b).cmp(getTokenBalanceUsd(a));
|
|
3033
|
+
if (usdDiff !== 0)
|
|
3034
|
+
return usdDiff;
|
|
3035
|
+
return `${a.symbol} ${a.chainName ?? ""}`.localeCompare(`${b.symbol} ${b.chainName ?? ""}`);
|
|
3036
|
+
});
|
|
2645
3037
|
const getTokenAmountForUsd = (token, usdAmount) => {
|
|
2646
3038
|
const rate = getTokenUsdRate(token);
|
|
2647
3039
|
if (rate.lte(0) || usdAmount.lte(0))
|
|
@@ -2974,6 +3366,8 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
2974
3366
|
: [token])
|
|
2975
3367
|
.filter(hasMinimumSourceUsdBalance);
|
|
2976
3368
|
const hasPositiveDecimalInput = (value) => Boolean(parseFiatNumber(value)?.gt(0));
|
|
3369
|
+
const hasPositiveSourceSpend = (token) => hasPositiveDecimalInput(token.userAmount) ||
|
|
3370
|
+
hasPositiveDecimalInput(token.userAmountUsd);
|
|
2977
3371
|
const getReadyExactInSourceTokens = (tokens) => getExactInSourceTokens(tokens).filter((token) => Boolean(token.chainId && token.contractAddress) &&
|
|
2978
3372
|
hasPositiveDecimalInput(token.userAmount));
|
|
2979
3373
|
const hasReadyExactInSwapInput = (tokens, destination) => Boolean(destination?.chainId &&
|
|
@@ -3062,6 +3456,51 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3062
3456
|
}
|
|
3063
3457
|
return getExpandedSourceTokens(tokens).filter(hasGasForSource);
|
|
3064
3458
|
};
|
|
3459
|
+
const getMinimumBalanceSourceTokens = () => filterMinimumSourceUsdTokens(getExpandedSourceTokens(swapBalance
|
|
3460
|
+
? deriveTokenOptions(swapBalance, swapSupportedChainsAndTokens)
|
|
3461
|
+
: []));
|
|
3462
|
+
const getHeldDestinationTokenOption = () => {
|
|
3463
|
+
if (!toToken?.chainId || !toToken.contractAddress)
|
|
3464
|
+
return undefined;
|
|
3465
|
+
for (const asset of swapBalance ?? []) {
|
|
3466
|
+
for (const breakdown of asset.breakdown ?? []) {
|
|
3467
|
+
const chainId = breakdown.chain?.id;
|
|
3468
|
+
if (chainId !== toToken.chainId)
|
|
3469
|
+
continue;
|
|
3470
|
+
const breakdownAddress = breakdown.contractAddress;
|
|
3471
|
+
const addressMatches = breakdownAddress &&
|
|
3472
|
+
(breakdownAddress.toLowerCase() ===
|
|
3473
|
+
toToken.contractAddress.toLowerCase() ||
|
|
3474
|
+
(isNativeTokenAddress(breakdownAddress) &&
|
|
3475
|
+
isNativeTokenAddress(toToken.contractAddress)));
|
|
3476
|
+
const symbolMatches = (breakdown.symbol ?? asset.symbol ?? "").toUpperCase() ===
|
|
3477
|
+
toToken.symbol.toUpperCase();
|
|
3478
|
+
if (!addressMatches && !symbolMatches)
|
|
3479
|
+
continue;
|
|
3480
|
+
const balanceAmount = parseFiatNumber(breakdown.balance);
|
|
3481
|
+
if (!balanceAmount || balanceAmount.lte(0))
|
|
3482
|
+
continue;
|
|
3483
|
+
const chainMeta = CHAIN_METADATA[chainId];
|
|
3484
|
+
const symbol = breakdown.symbol ?? asset.symbol ?? toToken.symbol;
|
|
3485
|
+
const fiatBalance = parseFiatNumber(breakdown.balanceInFiat);
|
|
3486
|
+
return {
|
|
3487
|
+
chainId,
|
|
3488
|
+
chainLogo: chainMeta?.logo ?? breakdown.chain?.logo ?? toToken.chainLogo,
|
|
3489
|
+
chainName: getShortChainName(chainId, chainMeta?.name ?? breakdown.chain?.name ?? toToken.chainName),
|
|
3490
|
+
contractAddress: breakdown.contractAddress ?? toToken.contractAddress,
|
|
3491
|
+
decimals: breakdown.decimals ?? asset.decimals ?? toToken.decimals ?? 18,
|
|
3492
|
+
logo: asset.logo ?? toToken.logo,
|
|
3493
|
+
name: symbol,
|
|
3494
|
+
symbol,
|
|
3495
|
+
balance: `${breakdown.balance} ${symbol}`,
|
|
3496
|
+
balanceInFiat: fiatBalance !== undefined
|
|
3497
|
+
? `$${fiatBalance.toDecimalPlaces(2).toFixed()}`
|
|
3498
|
+
: "$0.00",
|
|
3499
|
+
};
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
return undefined;
|
|
3503
|
+
};
|
|
3065
3504
|
const getDepositDestinationForSourceSelection = () => {
|
|
3066
3505
|
const destination = activeMode === "deposit" ? selectedOpportunity : toToken;
|
|
3067
3506
|
const chainId = destination?.chainId;
|
|
@@ -3160,7 +3599,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3160
3599
|
const getExactOutSourceTokens = (mode = exactOutQuoteSourceModeRef.current, targetAmountUsd) => {
|
|
3161
3600
|
if (activeMode === "deposit") {
|
|
3162
3601
|
const selection = getResolvedDepositSourceSelection({ targetAmountUsd });
|
|
3163
|
-
return getDepositSourceTokensForIds(selection.selectedSourceIds);
|
|
3602
|
+
return getDepositSourceTokensForIds(mode === "all" ? selection.sourcePoolIds : selection.selectedSourceIds);
|
|
3164
3603
|
}
|
|
3165
3604
|
if (activeMode === "send" && mode === "selected" && fromTokens.length > 0) {
|
|
3166
3605
|
return filterMinimumSourceUsdTokens(getExpandedSourceTokens(fromTokens)).filter(hasGasForSource);
|
|
@@ -3194,60 +3633,16 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3194
3633
|
return true;
|
|
3195
3634
|
});
|
|
3196
3635
|
};
|
|
3197
|
-
const getHeldNativeGasSourceForChain = (chainId) => {
|
|
3198
|
-
if (!chainId)
|
|
3199
|
-
return undefined;
|
|
3200
|
-
const nativeSymbol = CHAIN_METADATA[chainId]?.nativeCurrency?.symbol?.toUpperCase();
|
|
3201
|
-
for (const asset of swapBalance ?? []) {
|
|
3202
|
-
for (const breakdown of asset.breakdown ?? []) {
|
|
3203
|
-
if (breakdown.chain?.id !== chainId)
|
|
3204
|
-
continue;
|
|
3205
|
-
const breakdownSymbol = (breakdown.symbol ??
|
|
3206
|
-
asset.symbol ??
|
|
3207
|
-
"").toUpperCase();
|
|
3208
|
-
const assetSymbol = (asset.symbol ?? "").toUpperCase();
|
|
3209
|
-
const isNativeBalance = isNativeTokenAddress(breakdown.contractAddress) ||
|
|
3210
|
-
Boolean(nativeSymbol &&
|
|
3211
|
-
(breakdownSymbol === nativeSymbol || assetSymbol === nativeSymbol));
|
|
3212
|
-
const balance = parseFiatNumber(breakdown.balance) ?? new Decimal(0);
|
|
3213
|
-
if (!isNativeBalance || balance.lte(0))
|
|
3214
|
-
continue;
|
|
3215
|
-
return {
|
|
3216
|
-
chainId,
|
|
3217
|
-
tokenAddress: (breakdown.contractAddress ||
|
|
3218
|
-
zeroAddress),
|
|
3219
|
-
};
|
|
3220
|
-
}
|
|
3221
|
-
}
|
|
3222
|
-
return undefined;
|
|
3223
|
-
};
|
|
3224
3636
|
const getHeldDestinationTokenSource = () => {
|
|
3225
|
-
|
|
3637
|
+
const heldDestinationToken = getHeldDestinationTokenOption();
|
|
3638
|
+
if (!heldDestinationToken?.chainId ||
|
|
3639
|
+
!heldDestinationToken.contractAddress) {
|
|
3226
3640
|
return undefined;
|
|
3227
|
-
for (const asset of swapBalance ?? []) {
|
|
3228
|
-
for (const breakdown of asset.breakdown ?? []) {
|
|
3229
|
-
const chainId = breakdown.chain?.id;
|
|
3230
|
-
if (chainId !== toToken.chainId)
|
|
3231
|
-
continue;
|
|
3232
|
-
const breakdownAddress = breakdown.contractAddress;
|
|
3233
|
-
const addressMatches = breakdownAddress &&
|
|
3234
|
-
(breakdownAddress.toLowerCase() ===
|
|
3235
|
-
toToken.contractAddress.toLowerCase() ||
|
|
3236
|
-
(isNativeTokenAddress(breakdownAddress) &&
|
|
3237
|
-
isNativeTokenAddress(toToken.contractAddress)));
|
|
3238
|
-
const symbolMatches = (breakdown.symbol ?? asset.symbol ?? "").toUpperCase() ===
|
|
3239
|
-
toToken.symbol.toUpperCase();
|
|
3240
|
-
const balance = parseFiatNumber(breakdown.balance) ?? new Decimal(0);
|
|
3241
|
-
if ((!addressMatches && !symbolMatches) || balance.lte(0))
|
|
3242
|
-
continue;
|
|
3243
|
-
return {
|
|
3244
|
-
chainId,
|
|
3245
|
-
tokenAddress: (breakdown.contractAddress ||
|
|
3246
|
-
toToken.contractAddress),
|
|
3247
|
-
};
|
|
3248
|
-
}
|
|
3249
3641
|
}
|
|
3250
|
-
return
|
|
3642
|
+
return {
|
|
3643
|
+
chainId: heldDestinationToken.chainId,
|
|
3644
|
+
tokenAddress: heldDestinationToken.contractAddress,
|
|
3645
|
+
};
|
|
3251
3646
|
};
|
|
3252
3647
|
const shouldSendExactOutSourceAllowlist = () => {
|
|
3253
3648
|
if (activeMode === "deposit") {
|
|
@@ -3262,14 +3657,13 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3262
3657
|
if (activeMode !== "deposit" && activeMode !== "send") {
|
|
3263
3658
|
return buildExplicitSourcesPayload(tokens);
|
|
3264
3659
|
}
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
const explicitSources = buildExplicitSourcesPayload(
|
|
3660
|
+
const sourceTokens = shouldSendExactOutSourceAllowlist()
|
|
3661
|
+
? tokens
|
|
3662
|
+
: getExactOutSourceTokens("all");
|
|
3663
|
+
const explicitSources = buildExplicitSourcesPayload(sourceTokens).sources;
|
|
3269
3664
|
const sources = dedupeSdkSources([
|
|
3270
3665
|
...explicitSources,
|
|
3271
3666
|
getHeldDestinationTokenSource(),
|
|
3272
|
-
getHeldNativeGasSourceForChain(toToken?.chainId),
|
|
3273
3667
|
]);
|
|
3274
3668
|
return sources.length > 0 ? { sources } : {};
|
|
3275
3669
|
};
|
|
@@ -3277,16 +3671,16 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3277
3671
|
if (requiredSourceUsd.lte(0))
|
|
3278
3672
|
return [];
|
|
3279
3673
|
const destinationKey = getTokenSelectionKey(toToken);
|
|
3280
|
-
const candidates = getExactOutSourceTokens(exactOutQuoteSourceModeRef.current, requiredSourceUsd)
|
|
3674
|
+
const candidates = sortExactOutSourcesBySdkPriority(getExactOutSourceTokens(exactOutQuoteSourceModeRef.current, requiredSourceUsd)
|
|
3281
3675
|
.filter((token) => getTokenSelectionKey(token) !== destinationKey)
|
|
3282
|
-
.filter(
|
|
3676
|
+
.filter(hasMinimumSourceUsdBalance));
|
|
3283
3677
|
const sources = [];
|
|
3284
3678
|
let remainingUsd = requiredSourceUsd;
|
|
3285
3679
|
for (const token of candidates) {
|
|
3286
3680
|
if (remainingUsd.lte(0))
|
|
3287
3681
|
break;
|
|
3288
3682
|
const availableUsd = getTokenBalanceUsd(token);
|
|
3289
|
-
if (availableUsd.
|
|
3683
|
+
if (availableUsd.lt(minimumSourceUsd))
|
|
3290
3684
|
continue;
|
|
3291
3685
|
const rate = await resolveUsdRateForToken(token);
|
|
3292
3686
|
if (rate.lte(0))
|
|
@@ -3343,27 +3737,91 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3343
3737
|
if (!amountNumber || amountNumber.lte(0) || !toToken?.symbol) {
|
|
3344
3738
|
return undefined;
|
|
3345
3739
|
}
|
|
3740
|
+
if (activeMode === "deposit" && depositAmountMode === "usd") {
|
|
3741
|
+
return amountNumber;
|
|
3742
|
+
}
|
|
3346
3743
|
const fiatValue = getFiatValue(amountNumber.toNumber(), toToken.symbol);
|
|
3347
3744
|
return Number.isFinite(fiatValue) && fiatValue > 0
|
|
3348
3745
|
? new Decimal(fiatValue)
|
|
3349
3746
|
: undefined;
|
|
3350
3747
|
};
|
|
3351
|
-
const getExactOutAvailableSourceUsd = () => {
|
|
3352
|
-
const
|
|
3353
|
-
|
|
3748
|
+
const getExactOutAvailableSourceUsd = (sourceTokensOverride) => {
|
|
3749
|
+
const sumTokensWithDestinationCredit = (tokens) => {
|
|
3750
|
+
const heldDestinationToken = getHeldDestinationTokenOption();
|
|
3751
|
+
const heldDestinationKey = getTokenSelectionKey(heldDestinationToken);
|
|
3752
|
+
let hasHeldDestinationToken = false;
|
|
3753
|
+
const sourceTotal = tokens.reduce((sum, token) => {
|
|
3354
3754
|
const value = parseFiatNumber(token.balanceInFiat) ?? new Decimal(0);
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3755
|
+
const isHeldDestinationToken = heldDestinationKey &&
|
|
3756
|
+
getTokenSelectionKey(token) === heldDestinationKey;
|
|
3757
|
+
if (isHeldDestinationToken) {
|
|
3758
|
+
hasHeldDestinationToken = true;
|
|
3759
|
+
}
|
|
3760
|
+
return value.gt(0) &&
|
|
3761
|
+
(isHeldDestinationToken || value.gte(minimumSourceUsd))
|
|
3762
|
+
? sum.plus(value)
|
|
3763
|
+
: sum;
|
|
3764
|
+
}, new Decimal(0));
|
|
3765
|
+
if (!heldDestinationToken || hasHeldDestinationToken) {
|
|
3766
|
+
return sourceTotal;
|
|
3767
|
+
}
|
|
3768
|
+
const heldDestinationUsd = parseFiatNumber(heldDestinationToken.balanceInFiat) ?? new Decimal(0);
|
|
3769
|
+
return heldDestinationUsd.gt(0)
|
|
3770
|
+
? sourceTotal.plus(heldDestinationUsd)
|
|
3771
|
+
: sourceTotal;
|
|
3772
|
+
};
|
|
3773
|
+
if (sourceTokensOverride ||
|
|
3774
|
+
exactOutQuoteSourceModeRef.current === "selected") {
|
|
3775
|
+
const selectedSourceTokens = sourceTokensOverride ?? fromTokens;
|
|
3776
|
+
return sumTokensWithDestinationCredit(selectedSourceTokens);
|
|
3360
3777
|
}
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3778
|
+
return getExactOutTotalSourceBalanceUsd();
|
|
3779
|
+
};
|
|
3780
|
+
const getExactOutTotalSourceBalanceUsd = () => {
|
|
3781
|
+
const allSourceTotal = getExactOutAvailableSourceUsd(getMinimumBalanceSourceTokens());
|
|
3365
3782
|
return allSourceTotal.gt(0) ? allSourceTotal : getSwapBalanceTotalUsd();
|
|
3366
3783
|
};
|
|
3784
|
+
const getExactOutIntentSourceUsd = () => {
|
|
3785
|
+
const intentSourceUsd = (intentData?.sources ?? []).reduce((sum, source) => sum.plus(parseFiatNumber(source.value) ?? new Decimal(0)), new Decimal(0));
|
|
3786
|
+
return intentSourceUsd.gt(0) ? intentSourceUsd : undefined;
|
|
3787
|
+
};
|
|
3788
|
+
const getExactOutHeldDestinationUsd = () => {
|
|
3789
|
+
const heldDestinationToken = getHeldDestinationTokenOption();
|
|
3790
|
+
const value = parseFiatNumber(heldDestinationToken?.balanceInFiat);
|
|
3791
|
+
return value && value.gt(0) ? value : new Decimal(0);
|
|
3792
|
+
};
|
|
3793
|
+
const exactOutIntentIncludesHeldDestination = () => {
|
|
3794
|
+
const heldDestinationToken = getHeldDestinationTokenOption();
|
|
3795
|
+
const heldDestinationKey = getTokenSelectionKey(heldDestinationToken);
|
|
3796
|
+
if (!heldDestinationKey)
|
|
3797
|
+
return false;
|
|
3798
|
+
return (intentData?.sources ?? []).some((source) => {
|
|
3799
|
+
const chainId = source.chain?.id;
|
|
3800
|
+
const tokenAddress = source.token?.contractAddress;
|
|
3801
|
+
if (!chainId || !tokenAddress)
|
|
3802
|
+
return false;
|
|
3803
|
+
return (getTokenSelectionKey({
|
|
3804
|
+
chainId,
|
|
3805
|
+
contractAddress: tokenAddress,
|
|
3806
|
+
}) === heldDestinationKey);
|
|
3807
|
+
});
|
|
3808
|
+
};
|
|
3809
|
+
const getExactOutRequiredSourceUsd = () => {
|
|
3810
|
+
const intentSourceUsd = getExactOutIntentSourceUsd();
|
|
3811
|
+
if (intentSourceUsd)
|
|
3812
|
+
return intentSourceUsd;
|
|
3813
|
+
return undefined;
|
|
3814
|
+
};
|
|
3815
|
+
const getExactOutRequiredFundingUsd = () => {
|
|
3816
|
+
const sourceRequiredUsd = getExactOutRequiredSourceUsd();
|
|
3817
|
+
if (sourceRequiredUsd) {
|
|
3818
|
+
const destinationCreditUsd = exactOutIntentIncludesHeldDestination()
|
|
3819
|
+
? new Decimal(0)
|
|
3820
|
+
: getExactOutHeldDestinationUsd();
|
|
3821
|
+
return sourceRequiredUsd.plus(destinationCreditUsd);
|
|
3822
|
+
}
|
|
3823
|
+
return getExactOutRequestedUsd();
|
|
3824
|
+
};
|
|
3367
3825
|
const getExactInSourceDeficitUsd = () => {
|
|
3368
3826
|
if (swapType !== "exactIn" || fromTokens.length === 0)
|
|
3369
3827
|
return undefined;
|
|
@@ -3402,7 +3860,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3402
3860
|
details.availableAmountUsd ??
|
|
3403
3861
|
details.availableAmount ??
|
|
3404
3862
|
details.available) ?? parseLabeledErrorDecimal(errorText, "available");
|
|
3405
|
-
const requestedUsd =
|
|
3863
|
+
const requestedUsd = getExactOutRequiredFundingUsd();
|
|
3406
3864
|
const availableUsd = getExactOutAvailableSourceUsd();
|
|
3407
3865
|
const exactInSourceDeficitUsd = getExactInSourceDeficitUsd();
|
|
3408
3866
|
let missingUsd = exactInSourceDeficitUsd && exactInSourceDeficitUsd.gt(0)
|
|
@@ -3429,11 +3887,11 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3429
3887
|
message: "Add more source balance across your assets",
|
|
3430
3888
|
};
|
|
3431
3889
|
};
|
|
3432
|
-
const buildExactOutSourceBalanceIssue = () => {
|
|
3890
|
+
const buildExactOutSourceBalanceIssue = (sourceTokensOverride) => {
|
|
3433
3891
|
if (activeMode !== "deposit" && activeMode !== "send")
|
|
3434
3892
|
return null;
|
|
3435
|
-
const requestedUsd =
|
|
3436
|
-
const availableUsd = getExactOutAvailableSourceUsd();
|
|
3893
|
+
const requestedUsd = getExactOutRequiredFundingUsd();
|
|
3894
|
+
const availableUsd = getExactOutAvailableSourceUsd(sourceTokensOverride);
|
|
3437
3895
|
if (!requestedUsd || requestedUsd.lte(0) || !availableUsd)
|
|
3438
3896
|
return null;
|
|
3439
3897
|
const missingUsd = requestedUsd.minus(availableUsd);
|
|
@@ -3757,22 +4215,29 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3757
4215
|
...intent,
|
|
3758
4216
|
sources: sortIntentSourcesByUsdDesc(intent.sources ?? []),
|
|
3759
4217
|
};
|
|
3760
|
-
const
|
|
4218
|
+
const enrichedIntent = enrichSwapIntentTokenMetadata(sortedIntent);
|
|
4219
|
+
const sortedIntentSourceTokens = sortSwapTokensByUsdDesc((enrichedIntent.sources ?? []).map(buildIntentSourceToken));
|
|
3761
4220
|
lastSwapIntentRefreshAtRef.current = Date.now();
|
|
3762
4221
|
lastIntentSourceTokensRef.current = sortedIntentSourceTokens;
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
4222
|
+
if (!sourceSelectionTouched &&
|
|
4223
|
+
(activeMode === "send" ||
|
|
4224
|
+
(activeMode === "deposit" && swapType === "exactOut"))) {
|
|
4225
|
+
lastAutoIntentSourceTokensRef.current = sortedIntentSourceTokens;
|
|
4226
|
+
}
|
|
4227
|
+
cacheDestinationUsdRateFromIntent(enrichedIntent);
|
|
4228
|
+
cachePredictiveBaselineFromIntent(enrichedIntent);
|
|
4229
|
+
setIntentData(enrichedIntent);
|
|
4230
|
+
setIntentToAmount(enrichedIntent.destination?.amount || undefined);
|
|
3767
4231
|
setSwapQuoteIssue(null);
|
|
3768
4232
|
if (!sourceSelectionTouched &&
|
|
4233
|
+
swapStepRef.current !== "choose-swap-asset" &&
|
|
3769
4234
|
(activeMode === "send" ||
|
|
3770
4235
|
(activeMode === "deposit" && swapType === "exactOut"))) {
|
|
3771
4236
|
syncingIntentSourcesRef.current = true;
|
|
3772
4237
|
setFromTokens(sortedIntentSourceTokens);
|
|
3773
4238
|
}
|
|
3774
4239
|
try {
|
|
3775
|
-
const bridgeFees =
|
|
4240
|
+
const bridgeFees = enrichedIntent.feesAndBuffer?.bridge;
|
|
3776
4241
|
const bridgeFeeData = bridgeFees && typeof bridgeFees === "object" ? bridgeFees : undefined;
|
|
3777
4242
|
const collectionFee = parseFiatNumber(bridgeFeeData?.collection);
|
|
3778
4243
|
const fulfilmentFee = parseFiatNumber(bridgeFeeData?.fulfilment);
|
|
@@ -3780,20 +4245,29 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3780
4245
|
(collectionFee !== undefined || fulfilmentFee !== undefined
|
|
3781
4246
|
? (collectionFee ?? new Decimal(0)).plus(fulfilmentFee ?? new Decimal(0))
|
|
3782
4247
|
: undefined);
|
|
4248
|
+
const bridgeGasSuppliedFee = parseFiatNumber(bridgeFeeData?.gasSupplied);
|
|
4249
|
+
const destinationGasSuppliedFee = getIntentDestinationGasUsdValue(enrichedIntent);
|
|
4250
|
+
const gasSuppliedFee = bridgeGasSuppliedFee ?? destinationGasSuppliedFee;
|
|
3783
4251
|
const bridgeComponentsTotal = bridgeFeeData
|
|
3784
4252
|
? [
|
|
3785
4253
|
executionGasFee,
|
|
3786
4254
|
parseFiatNumber(bridgeFeeData.protocol),
|
|
3787
4255
|
parseFiatNumber(bridgeFeeData.solver),
|
|
3788
|
-
|
|
4256
|
+
gasSuppliedFee,
|
|
3789
4257
|
].reduce((sum, value) => sum.plus(value ?? new Decimal(0)), new Decimal(0))
|
|
3790
4258
|
: undefined;
|
|
3791
|
-
const
|
|
4259
|
+
const rawBridgeTotal = typeof bridgeFees === "string"
|
|
3792
4260
|
? parseFiatNumber(bridgeFees)
|
|
3793
|
-
:
|
|
4261
|
+
: parseFiatNumber(bridgeFeeData?.total);
|
|
4262
|
+
const bridgeTotal = rawBridgeTotal &&
|
|
4263
|
+
!bridgeGasSuppliedFee &&
|
|
4264
|
+
destinationGasSuppliedFee &&
|
|
4265
|
+
destinationGasSuppliedFee.gt(0)
|
|
4266
|
+
? rawBridgeTotal.plus(destinationGasSuppliedFee)
|
|
4267
|
+
: (rawBridgeTotal ??
|
|
3794
4268
|
(bridgeComponentsTotal && bridgeComponentsTotal.gt(0)
|
|
3795
4269
|
? bridgeComponentsTotal
|
|
3796
|
-
:
|
|
4270
|
+
: destinationGasSuppliedFee));
|
|
3797
4271
|
if (bridgeTotal !== undefined) {
|
|
3798
4272
|
setIntentFeeUsd(bridgeTotal.gt(0) ? bridgeTotal.toDecimalPlaces(6).toFixed() : "0");
|
|
3799
4273
|
}
|
|
@@ -3808,14 +4282,20 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3808
4282
|
}, [
|
|
3809
4283
|
activeMode,
|
|
3810
4284
|
fromTokens,
|
|
4285
|
+
getFiatValue,
|
|
3811
4286
|
sourceSelectionTouched,
|
|
3812
4287
|
swapType,
|
|
3813
4288
|
swapBalance,
|
|
4289
|
+
swapSupportedChainsAndTokens,
|
|
4290
|
+
supportedChainsAndTokens,
|
|
3814
4291
|
toToken,
|
|
3815
4292
|
]);
|
|
3816
4293
|
const handleSwapIntentCallback = useCallback((data, runId, quoteInputKey) => {
|
|
3817
4294
|
const { intent, allow, deny, refresh } = data;
|
|
3818
|
-
const bridgeProvider = normalizeBridgeProvider(data?.bridgeProvider ??
|
|
4295
|
+
const bridgeProvider = normalizeBridgeProvider(data?.bridgeProvider ??
|
|
4296
|
+
intent?.bridgeProvider ??
|
|
4297
|
+
intent?.normalizedIntent?.bridgeProvider ??
|
|
4298
|
+
intent?.swap?.bridgeProvider);
|
|
3819
4299
|
const intentWithBridgeProvider = normalizeRenderableSwapIntentData(intent, bridgeProvider);
|
|
3820
4300
|
logSdkIntentEvent("onIntent", data, {
|
|
3821
4301
|
bridgeProvider,
|
|
@@ -3834,11 +4314,21 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3834
4314
|
deny();
|
|
3835
4315
|
return;
|
|
3836
4316
|
}
|
|
4317
|
+
if (quoteInputKey && activeQuoteInputKeyRef.current !== quoteInputKey) {
|
|
4318
|
+
logSdkIntentEvent("ignored stale quote input onIntent", data, {
|
|
4319
|
+
currentQuoteInputKey: activeQuoteInputKeyRef.current,
|
|
4320
|
+
quoteInputKey,
|
|
4321
|
+
runId,
|
|
4322
|
+
});
|
|
4323
|
+
deny();
|
|
4324
|
+
return;
|
|
4325
|
+
}
|
|
3837
4326
|
if (!intentWithBridgeProvider) {
|
|
3838
4327
|
console.warn("[NexusWidget SDK][intent] Unsupported intent shape", {
|
|
3839
4328
|
intent,
|
|
3840
4329
|
raw: data,
|
|
3841
4330
|
});
|
|
4331
|
+
finishIntentFetchTiming(runId, "failed");
|
|
3842
4332
|
deny();
|
|
3843
4333
|
setIntentLoading(false);
|
|
3844
4334
|
setQuoteRefreshing(false);
|
|
@@ -3847,16 +4337,18 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3847
4337
|
setTxError("Quote unavailable");
|
|
3848
4338
|
return;
|
|
3849
4339
|
}
|
|
3850
|
-
const resolvedQuoteInputKey =
|
|
4340
|
+
const resolvedQuoteInputKey = quoteInputKey;
|
|
3851
4341
|
const normalizedRefresh = typeof refresh === "function"
|
|
3852
4342
|
? async (...args) => {
|
|
3853
4343
|
const refreshed = await refresh(...args);
|
|
3854
4344
|
const refreshedBridgeProvider = normalizeBridgeProvider(refreshed?.bridgeProvider ??
|
|
4345
|
+
refreshed?.normalizedIntent?.bridgeProvider ??
|
|
3855
4346
|
refreshed?.swap?.bridgeProvider ??
|
|
3856
4347
|
bridgeProvider);
|
|
3857
4348
|
return (normalizeRenderableSwapIntentData(refreshed, refreshedBridgeProvider) ?? refreshed);
|
|
3858
4349
|
}
|
|
3859
4350
|
: refresh;
|
|
4351
|
+
finishIntentFetchTiming(runId);
|
|
3860
4352
|
providerSwapIntent.current = {
|
|
3861
4353
|
intent: intentWithBridgeProvider,
|
|
3862
4354
|
allow,
|
|
@@ -3878,7 +4370,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
3878
4370
|
setReceiveMaxCalculating(false);
|
|
3879
4371
|
setPreviewQuoteRefreshing(false);
|
|
3880
4372
|
});
|
|
3881
|
-
}, [applySwapIntent, providerSwapIntent]);
|
|
4373
|
+
}, [applySwapIntent, finishIntentFetchTiming, providerSwapIntent]);
|
|
3882
4374
|
// Deposit-specific
|
|
3883
4375
|
const [selectedOpportunity, setSelectedOpportunity] = useState(() => (activeMode === "deposit" ? configuredDeposit : undefined));
|
|
3884
4376
|
const selectedOpportunityIdentity = getDepositConfigIdentity(selectedOpportunity);
|
|
@@ -4328,11 +4820,16 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4328
4820
|
: undefined;
|
|
4329
4821
|
const usdValue = getFiatValue(Number(amount) || 0, currentAsset?.symbol || "USDC");
|
|
4330
4822
|
const getDepositTokenUsdRate = () => {
|
|
4331
|
-
if (
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4823
|
+
if (selectedOpportunity?.tokenSymbol) {
|
|
4824
|
+
const fiat = getFiatValue(1, selectedOpportunity.tokenSymbol);
|
|
4825
|
+
if (Number.isFinite(fiat) && fiat > 0) {
|
|
4826
|
+
return new Decimal(fiat);
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
if (toToken) {
|
|
4830
|
+
const tokenRate = getTokenUsdRate(toToken);
|
|
4831
|
+
if (tokenRate.gt(0))
|
|
4832
|
+
return tokenRate;
|
|
4336
4833
|
}
|
|
4337
4834
|
return getCachedDestinationUsdRate(toToken) ?? new Decimal(0);
|
|
4338
4835
|
};
|
|
@@ -4356,8 +4853,45 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4356
4853
|
: new Decimal(0);
|
|
4357
4854
|
const depositUsdDisplay = depositUsdDecimal.toDecimalPlaces(2).toFixed();
|
|
4358
4855
|
const depositTokenDisplay = depositTokenAmountForQuote
|
|
4359
|
-
?.toDecimalPlaces(toToken?.decimals
|
|
4856
|
+
?.toDecimalPlaces(getCappedTokenDisplayDecimals(toToken?.decimals), Decimal.ROUND_DOWN)
|
|
4360
4857
|
.toFixed() ?? "0";
|
|
4858
|
+
const isExactOutMode = activeMode === "deposit" || activeMode === "send";
|
|
4859
|
+
const getActiveTotalBalanceUsd = () => isExactOutMode
|
|
4860
|
+
? getExactOutTotalSourceBalanceUsd()
|
|
4861
|
+
: getSwapBalanceTotalUsd();
|
|
4862
|
+
const getTotalBalancePercentUsdAmount = (pct) => {
|
|
4863
|
+
const ratio = isExactOutMode && pct === 100
|
|
4864
|
+
? receiveMaxSafetyMultiplier
|
|
4865
|
+
: new Decimal(pct).div(100);
|
|
4866
|
+
return getActiveTotalBalanceUsd().mul(ratio);
|
|
4867
|
+
};
|
|
4868
|
+
const getExactOutDestinationUsdRate = (token) => {
|
|
4869
|
+
if (activeMode === "deposit") {
|
|
4870
|
+
return getDepositTokenUsdRate();
|
|
4871
|
+
}
|
|
4872
|
+
return getTokenUsdRate(token);
|
|
4873
|
+
};
|
|
4874
|
+
const getExactOutPercentAmountFromBalance = (token, pct, preferUsd) => {
|
|
4875
|
+
const usdAmount = getTotalBalancePercentUsdAmount(pct);
|
|
4876
|
+
if (usdAmount.lte(0))
|
|
4877
|
+
return undefined;
|
|
4878
|
+
if (preferUsd) {
|
|
4879
|
+
return {
|
|
4880
|
+
amount: usdAmount.toDecimalPlaces(2, Decimal.ROUND_DOWN).toFixed(),
|
|
4881
|
+
mode: "usd",
|
|
4882
|
+
};
|
|
4883
|
+
}
|
|
4884
|
+
const rate = getExactOutDestinationUsdRate(token);
|
|
4885
|
+
if (rate.lte(0))
|
|
4886
|
+
return undefined;
|
|
4887
|
+
return {
|
|
4888
|
+
amount: usdAmount
|
|
4889
|
+
.div(rate)
|
|
4890
|
+
.toDecimalPlaces(getCappedTokenDisplayDecimals(token.decimals), Decimal.ROUND_DOWN)
|
|
4891
|
+
.toFixed(),
|
|
4892
|
+
mode: "token",
|
|
4893
|
+
};
|
|
4894
|
+
};
|
|
4361
4895
|
const depositSourceTargetUsdKey = activeMode === "deposit"
|
|
4362
4896
|
? (getDepositSourceTargetUsd()?.toFixed() ?? "")
|
|
4363
4897
|
: "";
|
|
@@ -4394,14 +4928,60 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4394
4928
|
].join(":")
|
|
4395
4929
|
: "",
|
|
4396
4930
|
].join("|");
|
|
4931
|
+
const exactOutAutoSourceInputKey = [
|
|
4932
|
+
activeMode,
|
|
4933
|
+
normalizedQuoteAmountKey,
|
|
4934
|
+
toTokenQuoteKey,
|
|
4935
|
+
quoteRecipientKey.toLowerCase(),
|
|
4936
|
+
activeMode === "deposit"
|
|
4937
|
+
? [
|
|
4938
|
+
depositAmountMode,
|
|
4939
|
+
depositQuoteAmountKey,
|
|
4940
|
+
selectedOpportunityIdentity,
|
|
4941
|
+
depositSourceTargetUsdKey,
|
|
4942
|
+
].join(":")
|
|
4943
|
+
: "",
|
|
4944
|
+
].join("|");
|
|
4945
|
+
activeQuoteInputKeyRef.current = activeQuoteInputKey;
|
|
4946
|
+
useEffect(() => {
|
|
4947
|
+
lastAutoIntentSourceTokensRef.current = [];
|
|
4948
|
+
}, [exactOutAutoSourceInputKey]);
|
|
4397
4949
|
useEffect(() => {
|
|
4398
|
-
activeQuoteInputKeyRef.current = activeQuoteInputKey;
|
|
4399
4950
|
setTxError(null);
|
|
4400
4951
|
}, [activeQuoteInputKey]);
|
|
4401
4952
|
const hasCurrentQuoteIntent = Boolean(intentData &&
|
|
4402
4953
|
swapIntentRef.current &&
|
|
4403
4954
|
swapIntentRef.current.runId === swapRunIdRef.current &&
|
|
4404
4955
|
swapIntentRef.current.quoteInputKey === activeQuoteInputKey);
|
|
4956
|
+
useEffect(() => {
|
|
4957
|
+
if (swapStep !== "idle")
|
|
4958
|
+
return;
|
|
4959
|
+
if (!hasCurrentQuoteIntent)
|
|
4960
|
+
return;
|
|
4961
|
+
if (sourceSelectionTouched)
|
|
4962
|
+
return;
|
|
4963
|
+
if (activeMode !== "send" &&
|
|
4964
|
+
!(activeMode === "deposit" && swapType === "exactOut")) {
|
|
4965
|
+
return;
|
|
4966
|
+
}
|
|
4967
|
+
const nextTokens = lastIntentSourceTokensRef.current;
|
|
4968
|
+
if (nextTokens.length === 0)
|
|
4969
|
+
return;
|
|
4970
|
+
setFromTokens((current) => {
|
|
4971
|
+
if (getSourceTokenListSyncKey(current) ===
|
|
4972
|
+
getSourceTokenListSyncKey(nextTokens)) {
|
|
4973
|
+
return current;
|
|
4974
|
+
}
|
|
4975
|
+
syncingIntentSourcesRef.current = true;
|
|
4976
|
+
return nextTokens;
|
|
4977
|
+
});
|
|
4978
|
+
}, [
|
|
4979
|
+
activeMode,
|
|
4980
|
+
hasCurrentQuoteIntent,
|
|
4981
|
+
sourceSelectionTouched,
|
|
4982
|
+
swapStep,
|
|
4983
|
+
swapType,
|
|
4984
|
+
]);
|
|
4405
4985
|
useEffect(() => {
|
|
4406
4986
|
if (activeMode !== "deposit")
|
|
4407
4987
|
return;
|
|
@@ -4506,15 +5086,17 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4506
5086
|
: activeMode === "send"
|
|
4507
5087
|
? Boolean(hasPositiveDecimalInput(amount) && toToken)
|
|
4508
5088
|
: false;
|
|
4509
|
-
const invalidateExactOutQuoteForRefresh = () => {
|
|
5089
|
+
const invalidateExactOutQuoteForRefresh = (options) => {
|
|
4510
5090
|
immediateQuoteAfterSourceEditRef.current = true;
|
|
4511
|
-
const receiveIssue = buildReceiveAmountIssue(
|
|
5091
|
+
const receiveIssue = buildReceiveAmountIssue({
|
|
5092
|
+
sourceTokens: options?.sourceTokens,
|
|
5093
|
+
});
|
|
4512
5094
|
applyReceiveAmountIssue(receiveIssue);
|
|
4513
5095
|
const configuredIssue = buildConfiguredAmountIssue();
|
|
4514
5096
|
if (configuredIssue) {
|
|
4515
5097
|
clearPreviewForBlockingAmountIssue();
|
|
4516
5098
|
}
|
|
4517
|
-
const sourceBalanceIssue = buildExactOutSourceBalanceIssue();
|
|
5099
|
+
const sourceBalanceIssue = buildExactOutSourceBalanceIssue(options?.sourceTokens);
|
|
4518
5100
|
if (sourceBalanceIssue) {
|
|
4519
5101
|
clearPreviewForBlockingAmountIssue();
|
|
4520
5102
|
setSwapQuoteIssue(sourceBalanceIssue);
|
|
@@ -4666,13 +5248,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4666
5248
|
});
|
|
4667
5249
|
const destinationUsdNeedingSources = Decimal.max(destinationUsd.minus(destinationCoverage?.usd ?? new Decimal(0)), new Decimal(0));
|
|
4668
5250
|
const cachedSourceUsdRatio = parseFiatNumber(baseline?.exactOutSourceUsdPerDestinationUsd);
|
|
4669
|
-
const requiredSourceUsd = destinationUsdNeedingSources
|
|
4670
|
-
? new Decimal(0)
|
|
4671
|
-
: cachedSourceUsdRatio && cachedSourceUsdRatio.gt(0)
|
|
4672
|
-
? destinationUsdNeedingSources.mul(cachedSourceUsdRatio)
|
|
4673
|
-
: destinationUsdNeedingSources
|
|
4674
|
-
.mul(BASIS_POINTS + PREDICTIVE_EXACT_OUT_BUFFER_BPS)
|
|
4675
|
-
.div(BASIS_POINTS);
|
|
5251
|
+
const requiredSourceUsd = getPredictiveExactOutSourceTargetUsd(destinationUsdNeedingSources, cachedSourceUsdRatio);
|
|
4676
5252
|
const sources = requiredSourceUsd.gt(0)
|
|
4677
5253
|
? await buildPredictiveExactOutSources(requiredSourceUsd)
|
|
4678
5254
|
: [];
|
|
@@ -4741,49 +5317,8 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4741
5317
|
requiredDestinationTokenAmount.lte(0)) {
|
|
4742
5318
|
return [];
|
|
4743
5319
|
}
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
const chainId = breakdown.chain?.id;
|
|
4747
|
-
if (chainId !== toToken.chainId)
|
|
4748
|
-
continue;
|
|
4749
|
-
const breakdownAddress = breakdown.contractAddress;
|
|
4750
|
-
const addressMatches = breakdownAddress &&
|
|
4751
|
-
toToken.contractAddress &&
|
|
4752
|
-
(breakdownAddress.toLowerCase() ===
|
|
4753
|
-
toToken.contractAddress.toLowerCase() ||
|
|
4754
|
-
(isNativeTokenAddress(breakdownAddress) &&
|
|
4755
|
-
isNativeTokenAddress(toToken.contractAddress)));
|
|
4756
|
-
const symbolMatches = (breakdown.symbol ?? asset.symbol ?? "").toUpperCase() ===
|
|
4757
|
-
toToken.symbol.toUpperCase();
|
|
4758
|
-
if (!addressMatches && !symbolMatches)
|
|
4759
|
-
continue;
|
|
4760
|
-
const balanceAmount = parseFiatNumber(breakdown.balance);
|
|
4761
|
-
if (!balanceAmount || balanceAmount.lte(0))
|
|
4762
|
-
continue;
|
|
4763
|
-
const chainMeta = CHAIN_METADATA[chainId];
|
|
4764
|
-
const symbol = breakdown.symbol ?? asset.symbol ?? toToken.symbol;
|
|
4765
|
-
const fiatBalance = parseFiatNumber(breakdown.balanceInFiat);
|
|
4766
|
-
if (!fiatBalance || fiatBalance.lt(minimumSourceUsd))
|
|
4767
|
-
continue;
|
|
4768
|
-
return [
|
|
4769
|
-
{
|
|
4770
|
-
chainId,
|
|
4771
|
-
chainLogo: chainMeta?.logo ?? breakdown.chain?.logo ?? toToken.chainLogo,
|
|
4772
|
-
chainName: getShortChainName(chainId, chainMeta?.name ?? breakdown.chain?.name ?? toToken.chainName),
|
|
4773
|
-
contractAddress: breakdown.contractAddress ?? toToken.contractAddress,
|
|
4774
|
-
decimals: breakdown.decimals ?? asset.decimals ?? toToken.decimals ?? 18,
|
|
4775
|
-
logo: asset.logo ?? toToken.logo,
|
|
4776
|
-
name: symbol,
|
|
4777
|
-
symbol,
|
|
4778
|
-
balance: `${breakdown.balance} ${symbol}`,
|
|
4779
|
-
balanceInFiat: fiatBalance !== undefined
|
|
4780
|
-
? `$${fiatBalance.toDecimalPlaces(2).toFixed()}`
|
|
4781
|
-
: "$0.00",
|
|
4782
|
-
},
|
|
4783
|
-
];
|
|
4784
|
-
}
|
|
4785
|
-
}
|
|
4786
|
-
return [];
|
|
5320
|
+
const heldDestinationToken = getHeldDestinationTokenOption();
|
|
5321
|
+
return heldDestinationToken ? [heldDestinationToken] : [];
|
|
4787
5322
|
}, [
|
|
4788
5323
|
activeMode,
|
|
4789
5324
|
requiredDestinationTokenAmount?.toFixed(),
|
|
@@ -4796,6 +5331,120 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4796
5331
|
toToken?.logo,
|
|
4797
5332
|
toToken?.symbol,
|
|
4798
5333
|
]);
|
|
5334
|
+
const setSourcePickerDraftSelection = useCallback((tokens) => {
|
|
5335
|
+
const nextTokens = tokens.map((token) => ({
|
|
5336
|
+
...token,
|
|
5337
|
+
userAmount: "",
|
|
5338
|
+
}));
|
|
5339
|
+
sourcePickerDraftTokensRef.current = nextTokens;
|
|
5340
|
+
setSourcePickerDraftTokens(nextTokens);
|
|
5341
|
+
}, []);
|
|
5342
|
+
const getAutoExactOutSourceTokensForPicker = useCallback(() => {
|
|
5343
|
+
const autoIntentTokens = lastAutoIntentSourceTokensRef.current;
|
|
5344
|
+
if (autoIntentTokens.length > 0) {
|
|
5345
|
+
return autoIntentTokens;
|
|
5346
|
+
}
|
|
5347
|
+
return sourceSelectionTouched ? [] : fromTokens;
|
|
5348
|
+
}, [fromTokens, sourceSelectionTouched]);
|
|
5349
|
+
const resetSourcePickerDraft = useCallback(() => {
|
|
5350
|
+
sourcePickerDraftTokensRef.current = null;
|
|
5351
|
+
setSourcePickerDraftTokens(null);
|
|
5352
|
+
}, []);
|
|
5353
|
+
const beginSourcePickerEdit = useCallback(() => {
|
|
5354
|
+
if (activeMode !== "deposit" && activeMode !== "send") {
|
|
5355
|
+
resetSourcePickerDraft();
|
|
5356
|
+
return;
|
|
5357
|
+
}
|
|
5358
|
+
sourcePickerDraftDepositFilterRef.current = depositSourceFilter;
|
|
5359
|
+
sourcePickerDraftTouchedRef.current = sourceSelectionTouched;
|
|
5360
|
+
sourcePickerDraftModeRef.current = exactOutQuoteSourceModeRef.current;
|
|
5361
|
+
setSourcePickerDraftSelection(fromTokens);
|
|
5362
|
+
}, [
|
|
5363
|
+
activeMode,
|
|
5364
|
+
depositSourceFilter,
|
|
5365
|
+
fromTokens,
|
|
5366
|
+
resetSourcePickerDraft,
|
|
5367
|
+
setSourcePickerDraftSelection,
|
|
5368
|
+
sourceSelectionTouched,
|
|
5369
|
+
]);
|
|
5370
|
+
const handleSourcePickerCancel = useCallback(() => {
|
|
5371
|
+
resetSourcePickerDraft();
|
|
5372
|
+
closeDrawerToIdle();
|
|
5373
|
+
}, [closeDrawerToIdle, resetSourcePickerDraft]);
|
|
5374
|
+
const handleSourcePickerDraftSelectionChange = useCallback((tokens) => {
|
|
5375
|
+
if (activeMode !== "deposit" && activeMode !== "send")
|
|
5376
|
+
return;
|
|
5377
|
+
setSourcePickerDraftSelection(tokens);
|
|
5378
|
+
sourcePickerDraftTouchedRef.current = true;
|
|
5379
|
+
sourcePickerDraftModeRef.current = "selected";
|
|
5380
|
+
if (activeMode === "deposit") {
|
|
5381
|
+
sourcePickerDraftDepositFilterRef.current = "custom";
|
|
5382
|
+
}
|
|
5383
|
+
}, [activeMode, setSourcePickerDraftSelection]);
|
|
5384
|
+
const handleSourcePickerFilterTabSelect = useCallback((tab) => {
|
|
5385
|
+
if (activeMode !== "deposit" && activeMode !== "send")
|
|
5386
|
+
return;
|
|
5387
|
+
const nextFilter = tab === "stables" ? "stablecoins" : tab;
|
|
5388
|
+
if (tab === "all") {
|
|
5389
|
+
sourcePickerDraftDepositFilterRef.current = nextFilter;
|
|
5390
|
+
sourcePickerDraftTouchedRef.current = false;
|
|
5391
|
+
sourcePickerDraftModeRef.current = "all";
|
|
5392
|
+
setSourcePickerDraftSelection(getAutoExactOutSourceTokensForPicker());
|
|
5393
|
+
return;
|
|
5394
|
+
}
|
|
5395
|
+
if (activeMode !== "deposit")
|
|
5396
|
+
return;
|
|
5397
|
+
const selection = getResolvedDepositSourceSelection({
|
|
5398
|
+
filter: nextFilter,
|
|
5399
|
+
isManualSelection: false,
|
|
5400
|
+
});
|
|
5401
|
+
const sourcePoolTokens = getDepositSourceTokensForIds(selection.sourcePoolIds);
|
|
5402
|
+
sourcePickerDraftDepositFilterRef.current = nextFilter;
|
|
5403
|
+
sourcePickerDraftTouchedRef.current = false;
|
|
5404
|
+
sourcePickerDraftModeRef.current = "all";
|
|
5405
|
+
setSourcePickerDraftSelection(sourcePoolTokens);
|
|
5406
|
+
}, [
|
|
5407
|
+
activeMode,
|
|
5408
|
+
getAutoExactOutSourceTokensForPicker,
|
|
5409
|
+
getDepositSourceTokensForIds,
|
|
5410
|
+
getResolvedDepositSourceSelection,
|
|
5411
|
+
setSourcePickerDraftSelection,
|
|
5412
|
+
]);
|
|
5413
|
+
const commitSourcePickerDraft = useCallback((tokens) => {
|
|
5414
|
+
if (activeMode !== "deposit" && activeMode !== "send") {
|
|
5415
|
+
resetSourcePickerDraft();
|
|
5416
|
+
closeDrawerToIdle();
|
|
5417
|
+
return;
|
|
5418
|
+
}
|
|
5419
|
+
const nextTokens = tokens ?? sourcePickerDraftTokensRef.current ?? fromTokens;
|
|
5420
|
+
const normalizedTokens = nextTokens.map((token) => ({
|
|
5421
|
+
...token,
|
|
5422
|
+
userAmount: "",
|
|
5423
|
+
}));
|
|
5424
|
+
setSourceSelectionTouched(sourcePickerDraftTouchedRef.current);
|
|
5425
|
+
setExactOutQuoteSourceModeValue(sourcePickerDraftModeRef.current);
|
|
5426
|
+
if (activeMode === "deposit") {
|
|
5427
|
+
setDepositSourceFilter(sourcePickerDraftDepositFilterRef.current);
|
|
5428
|
+
}
|
|
5429
|
+
invalidateExactOutQuoteForRefresh({
|
|
5430
|
+
sourceTokens: normalizedTokens,
|
|
5431
|
+
});
|
|
5432
|
+
setSourceSelectionRevision((current) => current + 1);
|
|
5433
|
+
setFromTokens(normalizedTokens);
|
|
5434
|
+
resetSourcePickerDraft();
|
|
5435
|
+
closeDrawerToIdle();
|
|
5436
|
+
}, [
|
|
5437
|
+
activeMode,
|
|
5438
|
+
closeDrawerToIdle,
|
|
5439
|
+
fromTokens,
|
|
5440
|
+
invalidateExactOutQuoteForRefresh,
|
|
5441
|
+
resetSourcePickerDraft,
|
|
5442
|
+
setExactOutQuoteSourceModeValue,
|
|
5443
|
+
]);
|
|
5444
|
+
const sourcePickerSelectedTokens = (activeMode === "deposit" || activeMode === "send") &&
|
|
5445
|
+
swapStep === "choose-swap-asset"
|
|
5446
|
+
? (sourcePickerDraftTokens ?? fromTokens)
|
|
5447
|
+
: fromTokens;
|
|
4799
5448
|
useEffect(() => {
|
|
4800
5449
|
if (activeMode !== "deposit" && activeMode !== "send")
|
|
4801
5450
|
return;
|
|
@@ -4949,8 +5598,15 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4949
5598
|
applyConfiguredPrefillsAfterReset();
|
|
4950
5599
|
rotateAttempt();
|
|
4951
5600
|
};
|
|
5601
|
+
const resetSourceSelectionAfterFailure = () => {
|
|
5602
|
+
lastAutoIntentSourceTokensRef.current = [];
|
|
5603
|
+
lastIntentSourceTokensRef.current = [];
|
|
5604
|
+
resetSourcePickerDraft();
|
|
5605
|
+
resetExactOutSourcesToAuto();
|
|
5606
|
+
};
|
|
4952
5607
|
const handleFailureBack = () => {
|
|
4953
5608
|
clearPendingSwapIntent();
|
|
5609
|
+
resetSourceSelectionAfterFailure();
|
|
4954
5610
|
setTxError(null);
|
|
4955
5611
|
void refreshSelectedSourceBalances();
|
|
4956
5612
|
setSwapStep("idle");
|
|
@@ -4959,7 +5615,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4959
5615
|
currentSwapStartedAtRef.current = 0;
|
|
4960
5616
|
rotateAttempt();
|
|
4961
5617
|
};
|
|
4962
|
-
const
|
|
5618
|
+
const clearTerminalInputState = () => {
|
|
4963
5619
|
setAmount("");
|
|
4964
5620
|
setRecipientAddress("");
|
|
4965
5621
|
setTxError(null);
|
|
@@ -4971,6 +5627,9 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4971
5627
|
setSourceSelectionTouched(false);
|
|
4972
5628
|
setDepositSourceFilter("all");
|
|
4973
5629
|
setDepositAmountMode("token");
|
|
5630
|
+
};
|
|
5631
|
+
const resetInputsAfterSuccessfulExecution = () => {
|
|
5632
|
+
clearTerminalInputState();
|
|
4974
5633
|
if (activeMode === "deposit") {
|
|
4975
5634
|
setSelectedOpportunity(configuredDeposit);
|
|
4976
5635
|
setToToken(configuredDeposit
|
|
@@ -4981,6 +5640,17 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
4981
5640
|
setToToken(undefined);
|
|
4982
5641
|
}
|
|
4983
5642
|
};
|
|
5643
|
+
const handleTerminalReceiptDone = () => {
|
|
5644
|
+
clearPendingSwapIntent();
|
|
5645
|
+
clearTerminalInputState();
|
|
5646
|
+
void refreshSelectedSourceBalances();
|
|
5647
|
+
setSwapStep("idle");
|
|
5648
|
+
setCurrentSwapId(null);
|
|
5649
|
+
currentSwapIdRef.current = null;
|
|
5650
|
+
currentSwapStartedAtRef.current = 0;
|
|
5651
|
+
amountEnteredLastValueRef.current = "";
|
|
5652
|
+
rotateAttempt();
|
|
5653
|
+
};
|
|
4984
5654
|
const handleModalOpenChange = useCallback((open) => {
|
|
4985
5655
|
if (!open && swapStepRef.current === "progress")
|
|
4986
5656
|
return;
|
|
@@ -5031,33 +5701,14 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5031
5701
|
throw new Error("No wallet connector available.");
|
|
5032
5702
|
}
|
|
5033
5703
|
await connectAsync({ connector: nextConnector });
|
|
5034
|
-
|
|
5704
|
+
return;
|
|
5035
5705
|
}
|
|
5036
|
-
const
|
|
5037
|
-
?.getProvider()
|
|
5038
|
-
.catch(() => undefined);
|
|
5039
|
-
const connectorClientProvider = connectorClient
|
|
5040
|
-
? {
|
|
5041
|
-
request: (args) => connectorClient.request(args),
|
|
5042
|
-
}
|
|
5043
|
-
: undefined;
|
|
5044
|
-
const walletClientProvider = walletClient
|
|
5045
|
-
? {
|
|
5046
|
-
request: (args) => walletClient.request(args),
|
|
5047
|
-
}
|
|
5048
|
-
: undefined;
|
|
5049
|
-
const windowProvider = typeof window !== "undefined"
|
|
5050
|
-
? window.ethereum
|
|
5051
|
-
: undefined;
|
|
5052
|
-
const effectiveProvider = connectorProvider &&
|
|
5053
|
-
typeof connectorProvider.request === "function"
|
|
5054
|
-
? connectorProvider
|
|
5055
|
-
: (connectorClientProvider ?? walletClientProvider ?? windowProvider);
|
|
5706
|
+
const effectiveProvider = await getEffectiveWalletProvider(activeConnector);
|
|
5056
5707
|
if (!effectiveProvider ||
|
|
5057
5708
|
typeof effectiveProvider.request !== "function") {
|
|
5058
5709
|
throw new Error("Wallet provider is not ready yet.");
|
|
5059
5710
|
}
|
|
5060
|
-
await handleInit(effectiveProvider);
|
|
5711
|
+
await handleInit(effectiveProvider, ownerAddress);
|
|
5061
5712
|
}
|
|
5062
5713
|
catch (error) {
|
|
5063
5714
|
setTxError(error?.message || "Unable to connect wallet.");
|
|
@@ -5267,6 +5918,8 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5267
5918
|
const rawStepType = String(event?.stepType ?? step?.type ?? step?.typeID ?? "").toLowerCase();
|
|
5268
5919
|
const rawState = String(event?.state ?? "").toLowerCase();
|
|
5269
5920
|
const explorerUrl = getPlanStepExplorerUrl(event, step);
|
|
5921
|
+
const sourceMovementStep = isSourceMovementProgressStep(type, rawStepType);
|
|
5922
|
+
const hasSourceMovementTransaction = Boolean(explorerUrl || getPlanStepTransactionHash(event, step));
|
|
5270
5923
|
const intentExplorerUrl = getEventIntentExplorerUrl(appConfig.nexusNetwork, event, step);
|
|
5271
5924
|
patchCurrentIntentExplorerUrl(intentExplorerUrl);
|
|
5272
5925
|
if (type === "TRANSACTION_SENT" ||
|
|
@@ -5280,12 +5933,9 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5280
5933
|
type === "SWAP_SKIPPED") {
|
|
5281
5934
|
markSwapExecutionStarted();
|
|
5282
5935
|
}
|
|
5283
|
-
if (
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
rawStepType === "bridge_deposit" ||
|
|
5287
|
-
type.includes("SOURCE_SWAP") ||
|
|
5288
|
-
type === "BRIDGE_DEPOSIT")) {
|
|
5936
|
+
if (sourceMovementStep &&
|
|
5937
|
+
hasSourceMovementTransaction &&
|
|
5938
|
+
PLAN_STEP_FUNDS_MAY_HAVE_MOVED_STATES.has(rawState)) {
|
|
5289
5939
|
fundsMovedRef.current = true;
|
|
5290
5940
|
}
|
|
5291
5941
|
if (explorerUrl) {
|
|
@@ -5296,11 +5946,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5296
5946
|
type === "TRANSACTION_CONFIRMED") {
|
|
5297
5947
|
mergeExplorerUrls({ destinationExplorerUrl: explorerUrl });
|
|
5298
5948
|
}
|
|
5299
|
-
else if (
|
|
5300
|
-
rawStepType === "eoa_to_ephemeral_transfer" ||
|
|
5301
|
-
rawStepType === "bridge_deposit" ||
|
|
5302
|
-
type.includes("SOURCE_SWAP") ||
|
|
5303
|
-
type === "BRIDGE_DEPOSIT") {
|
|
5949
|
+
else if (sourceMovementStep) {
|
|
5304
5950
|
mergeExplorerUrls({ sourceExplorerUrl: explorerUrl });
|
|
5305
5951
|
}
|
|
5306
5952
|
if (!intentUrlRef.current &&
|
|
@@ -5512,6 +6158,12 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5512
6158
|
quoteInputKey,
|
|
5513
6159
|
runId,
|
|
5514
6160
|
});
|
|
6161
|
+
startIntentFetchTiming({
|
|
6162
|
+
background,
|
|
6163
|
+
operation: "swapWithExactIn",
|
|
6164
|
+
quoteInputKey,
|
|
6165
|
+
runId,
|
|
6166
|
+
});
|
|
5515
6167
|
const result = await nexusSDK.swapWithExactIn(exactInSwapPayload, {
|
|
5516
6168
|
hooks: {
|
|
5517
6169
|
onIntent: (data) => handleSwapIntentCallback(data, runId, quoteInputKey),
|
|
@@ -5540,6 +6192,12 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5540
6192
|
quoteInputKey,
|
|
5541
6193
|
runId,
|
|
5542
6194
|
});
|
|
6195
|
+
startIntentFetchTiming({
|
|
6196
|
+
background,
|
|
6197
|
+
operation: "swapWithExactIn",
|
|
6198
|
+
quoteInputKey,
|
|
6199
|
+
runId,
|
|
6200
|
+
});
|
|
5543
6201
|
const result = await nexusSDK.swapWithExactIn(exactInSwapPayload, {
|
|
5544
6202
|
hooks: {
|
|
5545
6203
|
onIntent: (data) => handleSwapIntentCallback(data, runId, quoteInputKey),
|
|
@@ -5604,12 +6262,15 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5604
6262
|
if (activeMode === "deposit" && selectedOpportunity) {
|
|
5605
6263
|
const user = (ownerAddress ?? connectedAddress);
|
|
5606
6264
|
const executeParams = selectedOpportunity.executeDeposit(selectedOpportunity.tokenSymbol, selectedOpportunity.tokenAddress, amountBigInt, selectedOpportunity.chainId, user);
|
|
6265
|
+
if (!isPositiveGasLimit(executeParams.gas)) {
|
|
6266
|
+
throw new Error("Deposit config executeDeposit must return a positive gas limit.");
|
|
6267
|
+
}
|
|
5607
6268
|
executeConfig = {
|
|
5608
6269
|
to: executeParams.to,
|
|
5609
6270
|
value: executeParams.value,
|
|
5610
6271
|
data: executeParams.data,
|
|
5611
6272
|
tokenApproval: executeParams.tokenApproval,
|
|
5612
|
-
gas: executeParams.gas
|
|
6273
|
+
gas: executeParams.gas,
|
|
5613
6274
|
};
|
|
5614
6275
|
}
|
|
5615
6276
|
else if ((activeMode === "send" || hasCustomSwapRecipient) &&
|
|
@@ -5654,6 +6315,14 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5654
6315
|
quoteInputKey,
|
|
5655
6316
|
runId,
|
|
5656
6317
|
});
|
|
6318
|
+
startIntentFetchTiming({
|
|
6319
|
+
background,
|
|
6320
|
+
operation: isTransferExactOut
|
|
6321
|
+
? "swapAndTransfer exactOut"
|
|
6322
|
+
: "swapAndExecute exactOut",
|
|
6323
|
+
quoteInputKey,
|
|
6324
|
+
runId,
|
|
6325
|
+
});
|
|
5657
6326
|
const result = isTransferExactOut
|
|
5658
6327
|
? await sdkWithOptionalTransfer.swapAndTransfer(exactOutOperationInput, {
|
|
5659
6328
|
onEvent,
|
|
@@ -5704,6 +6373,12 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5704
6373
|
quoteInputKey,
|
|
5705
6374
|
runId,
|
|
5706
6375
|
});
|
|
6376
|
+
startIntentFetchTiming({
|
|
6377
|
+
background,
|
|
6378
|
+
operation: "swapWithExactOut",
|
|
6379
|
+
quoteInputKey,
|
|
6380
|
+
runId,
|
|
6381
|
+
});
|
|
5707
6382
|
const result = await nexusSDK.swapWithExactOut(exactOutSwapInput, {
|
|
5708
6383
|
hooks: {
|
|
5709
6384
|
onIntent: (data) => handleSwapIntentCallback(data, runId, quoteInputKey),
|
|
@@ -5758,6 +6433,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5758
6433
|
if (swapRunIdRef.current !== runId || !isCurrentQuoteInput()) {
|
|
5759
6434
|
return;
|
|
5760
6435
|
}
|
|
6436
|
+
finishIntentFetchTiming(runId, "failed");
|
|
5761
6437
|
if (activeMode === "deposit" && err?.code !== "USER_DENIED_INTENT") {
|
|
5762
6438
|
const hasActiveExecution = swapStepRef.current === "progress" &&
|
|
5763
6439
|
Boolean(currentSwapIdRef.current);
|
|
@@ -5779,19 +6455,26 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5779
6455
|
? "no_eligible_sources"
|
|
5780
6456
|
: !hasActiveExecution
|
|
5781
6457
|
? "quote_failed"
|
|
5782
|
-
:
|
|
6458
|
+
: fundsMovedRef.current
|
|
6459
|
+
? "pending_after_funds_moved"
|
|
6460
|
+
: "execution_failed";
|
|
5783
6461
|
reachedTerminalRef.current = true;
|
|
5784
6462
|
if (fundsMovedRef.current) {
|
|
5785
|
-
trackDeposit("
|
|
6463
|
+
trackDeposit("deposit_pending_after_funds_moved", {
|
|
6464
|
+
errorCode: err?.code ?? "UNKNOWN",
|
|
6465
|
+
errorMessage: errMessage || "Transaction pending.",
|
|
5786
6466
|
intentUrl: intentUrlRef.current,
|
|
6467
|
+
failedAtStep,
|
|
6468
|
+
});
|
|
6469
|
+
}
|
|
6470
|
+
else {
|
|
6471
|
+
trackDeposit("deposit_failed", {
|
|
6472
|
+
errorCode: err?.code ?? "UNKNOWN",
|
|
6473
|
+
errorCategory,
|
|
6474
|
+
errorMessage: errMessage || "Transaction failed.",
|
|
6475
|
+
failedAtStep,
|
|
5787
6476
|
});
|
|
5788
6477
|
}
|
|
5789
|
-
trackDeposit("deposit_failed", {
|
|
5790
|
-
errorCode: err?.code ?? "UNKNOWN",
|
|
5791
|
-
errorCategory,
|
|
5792
|
-
errorMessage: errMessage || "Transaction failed.",
|
|
5793
|
-
failedAtStep,
|
|
5794
|
-
});
|
|
5795
6478
|
}
|
|
5796
6479
|
setQuoteRefreshing(false);
|
|
5797
6480
|
setIntentLoading(false);
|
|
@@ -5879,6 +6562,13 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5879
6562
|
setTxError(null);
|
|
5880
6563
|
return;
|
|
5881
6564
|
}
|
|
6565
|
+
if (hasActiveExecution && fundsMovedRef.current) {
|
|
6566
|
+
showTimeoutReceipt(errorMessage, {
|
|
6567
|
+
failureDescription: "Funds have already moved and the transaction is still pending. Check the intent explorer for the latest status.",
|
|
6568
|
+
});
|
|
6569
|
+
setTxError(null);
|
|
6570
|
+
return;
|
|
6571
|
+
}
|
|
5882
6572
|
if (hasActiveExecution) {
|
|
5883
6573
|
showFailedProgressThenReceipt(errorMessage);
|
|
5884
6574
|
}
|
|
@@ -5896,7 +6586,12 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5896
6586
|
return;
|
|
5897
6587
|
if (syncingIntentSourcesRef.current) {
|
|
5898
6588
|
syncingIntentSourcesRef.current = false;
|
|
5899
|
-
|
|
6589
|
+
if (hasCurrentQuoteIntent) {
|
|
6590
|
+
setIntentLoading(false);
|
|
6591
|
+
setQuoteRefreshing(false);
|
|
6592
|
+
setReceiveMaxCalculating(false);
|
|
6593
|
+
return;
|
|
6594
|
+
}
|
|
5900
6595
|
}
|
|
5901
6596
|
if (hasReceiveAmountQuoteIssue) {
|
|
5902
6597
|
clearPendingSwapIntent(true);
|
|
@@ -5926,10 +6621,11 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5926
6621
|
clearPendingSwapIntent(true, { keepQuoteRefreshing: true });
|
|
5927
6622
|
setQuoteRefreshing(true);
|
|
5928
6623
|
let quoteStarted = false;
|
|
6624
|
+
const quoteDelay = getQuoteRequestDelay();
|
|
5929
6625
|
const timer = window.setTimeout(() => {
|
|
5930
6626
|
quoteStarted = true;
|
|
5931
6627
|
void handleEnterPreview({ background: true });
|
|
5932
|
-
},
|
|
6628
|
+
}, quoteDelay);
|
|
5933
6629
|
return () => {
|
|
5934
6630
|
window.clearTimeout(timer);
|
|
5935
6631
|
if (syncingIntentSourcesRef.current)
|
|
@@ -5947,6 +6643,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5947
6643
|
hasCurrentQuoteIntent,
|
|
5948
6644
|
hasInsufficientSourcesQuoteIssue,
|
|
5949
6645
|
hasReceiveAmountQuoteIssue,
|
|
6646
|
+
getQuoteRequestDelay,
|
|
5950
6647
|
nexusSDK,
|
|
5951
6648
|
recipientAddress,
|
|
5952
6649
|
swapStep,
|
|
@@ -5957,7 +6654,12 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5957
6654
|
return;
|
|
5958
6655
|
if (syncingIntentSourcesRef.current) {
|
|
5959
6656
|
syncingIntentSourcesRef.current = false;
|
|
5960
|
-
|
|
6657
|
+
if (hasCurrentQuoteIntent) {
|
|
6658
|
+
setIntentLoading(false);
|
|
6659
|
+
setQuoteRefreshing(false);
|
|
6660
|
+
setReceiveMaxCalculating(false);
|
|
6661
|
+
return;
|
|
6662
|
+
}
|
|
5961
6663
|
}
|
|
5962
6664
|
if (hasReceiveAmountQuoteIssue) {
|
|
5963
6665
|
clearPendingSwapIntent(true);
|
|
@@ -5990,10 +6692,11 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
5990
6692
|
clearPendingSwapIntent(true, { keepQuoteRefreshing: true });
|
|
5991
6693
|
setQuoteRefreshing(true);
|
|
5992
6694
|
let quoteStarted = false;
|
|
6695
|
+
const quoteDelay = getQuoteRequestDelay();
|
|
5993
6696
|
const timer = window.setTimeout(() => {
|
|
5994
6697
|
quoteStarted = true;
|
|
5995
6698
|
void handleEnterPreview({ background: true });
|
|
5996
|
-
},
|
|
6699
|
+
}, quoteDelay);
|
|
5997
6700
|
return () => {
|
|
5998
6701
|
window.clearTimeout(timer);
|
|
5999
6702
|
if (syncingIntentSourcesRef.current)
|
|
@@ -6011,6 +6714,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6011
6714
|
hasCurrentQuoteIntent,
|
|
6012
6715
|
hasInsufficientSourcesQuoteIssue,
|
|
6013
6716
|
hasReceiveAmountQuoteIssue,
|
|
6717
|
+
getQuoteRequestDelay,
|
|
6014
6718
|
nexusSDK,
|
|
6015
6719
|
sourceSelectionRevision,
|
|
6016
6720
|
selectedOpportunityIdentity,
|
|
@@ -6022,7 +6726,12 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6022
6726
|
return;
|
|
6023
6727
|
if (syncingIntentSourcesRef.current) {
|
|
6024
6728
|
syncingIntentSourcesRef.current = false;
|
|
6025
|
-
|
|
6729
|
+
if (hasCurrentQuoteIntent) {
|
|
6730
|
+
setIntentLoading(false);
|
|
6731
|
+
setQuoteRefreshing(false);
|
|
6732
|
+
setReceiveMaxCalculating(false);
|
|
6733
|
+
return;
|
|
6734
|
+
}
|
|
6026
6735
|
}
|
|
6027
6736
|
if (hasReceiveAmountQuoteIssue) {
|
|
6028
6737
|
clearPendingSwapIntent(true);
|
|
@@ -6052,10 +6761,11 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6052
6761
|
clearPendingSwapIntent(true, { keepQuoteRefreshing: true });
|
|
6053
6762
|
setQuoteRefreshing(true);
|
|
6054
6763
|
let quoteStarted = false;
|
|
6764
|
+
const quoteDelay = getQuoteRequestDelay();
|
|
6055
6765
|
const timer = window.setTimeout(() => {
|
|
6056
6766
|
quoteStarted = true;
|
|
6057
6767
|
void handleEnterPreview({ background: true });
|
|
6058
|
-
},
|
|
6768
|
+
}, quoteDelay);
|
|
6059
6769
|
return () => {
|
|
6060
6770
|
window.clearTimeout(timer);
|
|
6061
6771
|
if (syncingIntentSourcesRef.current)
|
|
@@ -6071,6 +6781,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6071
6781
|
hasCurrentQuoteIntent,
|
|
6072
6782
|
hasInsufficientSourcesQuoteIssue,
|
|
6073
6783
|
hasReceiveAmountQuoteIssue,
|
|
6784
|
+
getQuoteRequestDelay,
|
|
6074
6785
|
nexusSDK,
|
|
6075
6786
|
sourceSelectionRevision,
|
|
6076
6787
|
swapStep,
|
|
@@ -6102,6 +6813,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6102
6813
|
try {
|
|
6103
6814
|
const updatedRaw = await activeIntent.refresh();
|
|
6104
6815
|
const updatedBridgeProvider = normalizeBridgeProvider(updatedRaw?.bridgeProvider ??
|
|
6816
|
+
updatedRaw?.normalizedIntent?.bridgeProvider ??
|
|
6105
6817
|
updatedRaw?.swap?.bridgeProvider ??
|
|
6106
6818
|
activeIntent.intent?.bridgeProvider);
|
|
6107
6819
|
const updated = normalizeRenderableSwapIntentData(updatedRaw, updatedBridgeProvider);
|
|
@@ -6330,7 +7042,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6330
7042
|
return;
|
|
6331
7043
|
}
|
|
6332
7044
|
if (swapStep === "choose-swap-asset") {
|
|
6333
|
-
|
|
7045
|
+
handleSourcePickerCancel();
|
|
6334
7046
|
return;
|
|
6335
7047
|
}
|
|
6336
7048
|
if (swapStep === "choose-receive-asset") {
|
|
@@ -6472,7 +7184,9 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6472
7184
|
if (parsedAmount.gt(0) && rate.gt(0)) {
|
|
6473
7185
|
const converted = depositAmountMode === "token"
|
|
6474
7186
|
? parsedAmount.mul(rate).toDecimalPlaces(2)
|
|
6475
|
-
: parsedAmount
|
|
7187
|
+
: parsedAmount
|
|
7188
|
+
.div(rate)
|
|
7189
|
+
.toDecimalPlaces(getCappedTokenDisplayDecimals(toToken?.decimals), Decimal.ROUND_DOWN);
|
|
6476
7190
|
setAmount(converted.toFixed());
|
|
6477
7191
|
}
|
|
6478
7192
|
clearPendingSwapIntent();
|
|
@@ -6498,87 +7212,33 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6498
7212
|
? "percent_75"
|
|
6499
7213
|
: "percent_max";
|
|
6500
7214
|
if (pct !== 100) {
|
|
6501
|
-
const
|
|
6502
|
-
|
|
6503
|
-
const nextAmount = depositAmountMode === "usd"
|
|
6504
|
-
? usdAmount.toDecimalPlaces(2, Decimal.ROUND_DOWN).toFixed()
|
|
6505
|
-
: formatTokenAmountFromUsd(usdAmount, toToken);
|
|
6506
|
-
if (nextAmount && !shouldUseMaxQuoteFallback) {
|
|
7215
|
+
const nextAmount = getExactOutPercentAmountFromBalance(toToken, pct, depositAmountMode === "usd");
|
|
7216
|
+
if (nextAmount) {
|
|
6507
7217
|
setQuoteRefreshing(false);
|
|
6508
7218
|
setReceiveMaxCalculating(false);
|
|
6509
7219
|
setMaxCalculationPercent(null);
|
|
6510
|
-
|
|
7220
|
+
setDepositAmountMode(nextAmount.mode);
|
|
7221
|
+
handleDepositAmountChange(nextAmount.amount);
|
|
6511
7222
|
return;
|
|
6512
7223
|
}
|
|
6513
7224
|
setQuoteRefreshing(false);
|
|
6514
|
-
setReceiveMaxCalculating(true);
|
|
6515
|
-
setMaxCalculationPercent(pct);
|
|
6516
|
-
try {
|
|
6517
|
-
await waitForNextPaint();
|
|
6518
|
-
const fallback = await getPercentAmountFromMaxQuote(toToken, pct, depositAmountMode === "usd");
|
|
6519
|
-
if (runId !== maxPercentRunRef.current)
|
|
6520
|
-
return;
|
|
6521
|
-
if (!fallback) {
|
|
6522
|
-
setQuoteRefreshing(false);
|
|
6523
|
-
setReceiveMaxCalculating(false);
|
|
6524
|
-
setMaxCalculationPercent(null);
|
|
6525
|
-
setTxError("Unable to calculate this percentage for the deposit asset.");
|
|
6526
|
-
return;
|
|
6527
|
-
}
|
|
6528
|
-
setDepositAmountMode(fallback.mode);
|
|
6529
|
-
setReceiveMaxCalculating(false);
|
|
6530
|
-
setMaxCalculationPercent(null);
|
|
6531
|
-
handleDepositAmountChange(fallback.amount);
|
|
6532
|
-
}
|
|
6533
|
-
catch (error) {
|
|
6534
|
-
if (runId !== maxPercentRunRef.current)
|
|
6535
|
-
return;
|
|
6536
|
-
console.error("Unable to calculate percentage deposit amount", error);
|
|
6537
|
-
setReceiveMaxCalculating(false);
|
|
6538
|
-
setMaxCalculationPercent(null);
|
|
6539
|
-
setQuoteRefreshing(false);
|
|
6540
|
-
if (isInsufficientSourcesError(error)) {
|
|
6541
|
-
setSwapQuoteIssue(buildInsufficientSourcesIssue(error));
|
|
6542
|
-
return;
|
|
6543
|
-
}
|
|
6544
|
-
setTxError(error?.message ||
|
|
6545
|
-
"Unable to calculate this percentage for the deposit asset.");
|
|
6546
|
-
}
|
|
6547
|
-
return;
|
|
6548
|
-
}
|
|
6549
|
-
setQuoteRefreshing(false);
|
|
6550
|
-
setReceiveMaxCalculating(true);
|
|
6551
|
-
setMaxCalculationPercent(100);
|
|
6552
|
-
try {
|
|
6553
|
-
await waitForNextPaint();
|
|
6554
|
-
const maxAmount = await getPercentAmountFromMaxQuote(toToken, 100, depositAmountMode === "usd");
|
|
6555
|
-
if (runId !== maxPercentRunRef.current)
|
|
6556
|
-
return;
|
|
6557
|
-
if (!maxAmount) {
|
|
6558
|
-
setReceiveMaxCalculating(false);
|
|
6559
|
-
setMaxCalculationPercent(null);
|
|
6560
|
-
setQuoteRefreshing(false);
|
|
6561
|
-
setTxError("No depositable amount is available for this deposit.");
|
|
6562
|
-
return;
|
|
6563
|
-
}
|
|
6564
|
-
setDepositAmountMode(maxAmount.mode);
|
|
6565
7225
|
setReceiveMaxCalculating(false);
|
|
6566
7226
|
setMaxCalculationPercent(null);
|
|
6567
|
-
|
|
7227
|
+
setTxError("Unable to calculate this percentage for the deposit asset.");
|
|
7228
|
+
return;
|
|
6568
7229
|
}
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
return;
|
|
6579
|
-
}
|
|
6580
|
-
setTxError(error?.message || "Unable to calculate the max deposit amount.");
|
|
7230
|
+
setQuoteRefreshing(false);
|
|
7231
|
+
setReceiveMaxCalculating(false);
|
|
7232
|
+
setMaxCalculationPercent(null);
|
|
7233
|
+
const maxAmount = getExactOutPercentAmountFromBalance(toToken, 100, depositAmountMode === "usd");
|
|
7234
|
+
if (runId !== maxPercentRunRef.current)
|
|
7235
|
+
return;
|
|
7236
|
+
if (!maxAmount) {
|
|
7237
|
+
setTxError("No depositable amount is available for this deposit.");
|
|
7238
|
+
return;
|
|
6581
7239
|
}
|
|
7240
|
+
setDepositAmountMode(maxAmount.mode);
|
|
7241
|
+
handleDepositAmountChange(maxAmount.amount);
|
|
6582
7242
|
};
|
|
6583
7243
|
const handleSendPercentSelect = async (pct) => {
|
|
6584
7244
|
if (isConfiguredAmountFixed)
|
|
@@ -6600,82 +7260,31 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6600
7260
|
? "percent_75"
|
|
6601
7261
|
: "percent_max";
|
|
6602
7262
|
if (pct !== 100) {
|
|
6603
|
-
const
|
|
6604
|
-
const nextAmount = formatTokenAmountFromUsd(usdAmount, toToken);
|
|
7263
|
+
const nextAmount = getExactOutPercentAmountFromBalance(toToken, pct, false);
|
|
6605
7264
|
if (nextAmount) {
|
|
6606
7265
|
setQuoteRefreshing(false);
|
|
6607
7266
|
setReceiveMaxCalculating(false);
|
|
6608
7267
|
setMaxCalculationPercent(null);
|
|
6609
|
-
handleSendAmountChange(nextAmount);
|
|
7268
|
+
handleSendAmountChange(nextAmount.amount);
|
|
6610
7269
|
return;
|
|
6611
7270
|
}
|
|
6612
7271
|
setQuoteRefreshing(false);
|
|
6613
|
-
setReceiveMaxCalculating(true);
|
|
6614
|
-
setMaxCalculationPercent(pct);
|
|
6615
|
-
try {
|
|
6616
|
-
await waitForNextPaint();
|
|
6617
|
-
const fallback = await getPercentAmountFromMaxQuote(toToken, pct, false);
|
|
6618
|
-
if (runId !== maxPercentRunRef.current)
|
|
6619
|
-
return;
|
|
6620
|
-
if (!fallback) {
|
|
6621
|
-
setQuoteRefreshing(false);
|
|
6622
|
-
setReceiveMaxCalculating(false);
|
|
6623
|
-
setMaxCalculationPercent(null);
|
|
6624
|
-
setTxError("Unable to calculate this percentage for the send asset.");
|
|
6625
|
-
return;
|
|
6626
|
-
}
|
|
6627
|
-
setReceiveMaxCalculating(false);
|
|
6628
|
-
setMaxCalculationPercent(null);
|
|
6629
|
-
handleSendAmountChange(fallback.amount);
|
|
6630
|
-
}
|
|
6631
|
-
catch (error) {
|
|
6632
|
-
if (runId !== maxPercentRunRef.current)
|
|
6633
|
-
return;
|
|
6634
|
-
console.error("Unable to calculate percentage send amount", error);
|
|
6635
|
-
setReceiveMaxCalculating(false);
|
|
6636
|
-
setMaxCalculationPercent(null);
|
|
6637
|
-
setQuoteRefreshing(false);
|
|
6638
|
-
if (isInsufficientSourcesError(error)) {
|
|
6639
|
-
setSwapQuoteIssue(buildInsufficientSourcesIssue(error));
|
|
6640
|
-
return;
|
|
6641
|
-
}
|
|
6642
|
-
setTxError(error?.message ||
|
|
6643
|
-
"Unable to calculate this percentage for the send asset.");
|
|
6644
|
-
}
|
|
6645
|
-
return;
|
|
6646
|
-
}
|
|
6647
|
-
setQuoteRefreshing(false);
|
|
6648
|
-
setReceiveMaxCalculating(true);
|
|
6649
|
-
setMaxCalculationPercent(100);
|
|
6650
|
-
try {
|
|
6651
|
-
await waitForNextPaint();
|
|
6652
|
-
const maxAmount = await getPercentAmountFromMaxQuote(toToken, 100, false);
|
|
6653
|
-
if (runId !== maxPercentRunRef.current)
|
|
6654
|
-
return;
|
|
6655
|
-
if (!maxAmount) {
|
|
6656
|
-
setReceiveMaxCalculating(false);
|
|
6657
|
-
setMaxCalculationPercent(null);
|
|
6658
|
-
setQuoteRefreshing(false);
|
|
6659
|
-
setTxError("No transferable amount is available for this asset.");
|
|
6660
|
-
return;
|
|
6661
|
-
}
|
|
6662
7272
|
setReceiveMaxCalculating(false);
|
|
6663
7273
|
setMaxCalculationPercent(null);
|
|
6664
|
-
|
|
7274
|
+
setTxError("Unable to calculate this percentage for the send asset.");
|
|
7275
|
+
return;
|
|
6665
7276
|
}
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
return;
|
|
6676
|
-
}
|
|
6677
|
-
setTxError(error?.message || "Unable to calculate the max send amount.");
|
|
7277
|
+
setQuoteRefreshing(false);
|
|
7278
|
+
setReceiveMaxCalculating(false);
|
|
7279
|
+
setMaxCalculationPercent(null);
|
|
7280
|
+
const maxAmount = getExactOutPercentAmountFromBalance(toToken, 100, false);
|
|
7281
|
+
if (runId !== maxPercentRunRef.current)
|
|
7282
|
+
return;
|
|
7283
|
+
if (!maxAmount) {
|
|
7284
|
+
setTxError("No transferable amount is available for this asset.");
|
|
7285
|
+
return;
|
|
6678
7286
|
}
|
|
7287
|
+
handleSendAmountChange(maxAmount.amount);
|
|
6679
7288
|
};
|
|
6680
7289
|
// ---------------------------------------------------------------------------
|
|
6681
7290
|
// Render
|
|
@@ -6706,6 +7315,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6706
7315
|
const hasPositiveRootAmount = hasPositiveDecimalInput(amount);
|
|
6707
7316
|
const hasReadySwapQuoteInput = hasReadyExactInSwapInput(fromTokens, toToken);
|
|
6708
7317
|
const needsWalletConnection = !ownerAddress || !nexusSDK;
|
|
7318
|
+
const isExactOutPaymentQuotePending = isExactOutPaymentFlow && (quoteRefreshing || intentLoading);
|
|
6709
7319
|
const walletConnectBusy = walletActionPending ||
|
|
6710
7320
|
nexusLoading ||
|
|
6711
7321
|
isWalletConnectPending ||
|
|
@@ -6727,10 +7337,9 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6727
7337
|
: !hasPositiveRootAmount ||
|
|
6728
7338
|
!toToken ||
|
|
6729
7339
|
receiveMaxCalculating ||
|
|
7340
|
+
isExactOutPaymentQuotePending ||
|
|
6730
7341
|
(!hasCurrentExactOutPaymentIntent &&
|
|
6731
|
-
|
|
6732
|
-
intentLoading ||
|
|
6733
|
-
isQuoteUnavailableForAutoSourceFlow)) ||
|
|
7342
|
+
isQuoteUnavailableForAutoSourceFlow) ||
|
|
6734
7343
|
Boolean(blockingQuoteIssue);
|
|
6735
7344
|
const sendNeedsRecipient = activeMode === "send" && !recipientAddress;
|
|
6736
7345
|
const isSendCtaDisabled = needsWalletConnection
|
|
@@ -6740,10 +7349,9 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6740
7349
|
hasSameOwnerSendRecipient ||
|
|
6741
7350
|
receiveMaxCalculating ||
|
|
6742
7351
|
(!sendNeedsRecipient &&
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
isQuoteUnavailableForAutoSourceFlow)) ||
|
|
7352
|
+
(isExactOutPaymentQuotePending ||
|
|
7353
|
+
(!hasCurrentExactOutPaymentIntent &&
|
|
7354
|
+
isQuoteUnavailableForAutoSourceFlow))) ||
|
|
6747
7355
|
Boolean(blockingQuoteIssue);
|
|
6748
7356
|
const quoteCtaLabel = (fallback) => {
|
|
6749
7357
|
if (needsWalletConnection)
|
|
@@ -6756,7 +7364,8 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6756
7364
|
return configuredAmountIssue.ctaLabel;
|
|
6757
7365
|
if (receiveMaxCalculating)
|
|
6758
7366
|
return "Calculating...";
|
|
6759
|
-
if (
|
|
7367
|
+
if (isExactOutPaymentQuotePending ||
|
|
7368
|
+
(!hasCurrentExactOutPaymentIntent && (quoteRefreshing || intentLoading))) {
|
|
6760
7369
|
return "Fetching quotes...";
|
|
6761
7370
|
}
|
|
6762
7371
|
if (isQuoteUnavailableForAutoSourceFlow)
|
|
@@ -6822,6 +7431,12 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6822
7431
|
const previewToAmountUsd = previewDestinationUsdNumber && previewDestinationUsdNumber.gt(0)
|
|
6823
7432
|
? previewDestinationUsdNumber.toDecimalPlaces(6).toFixed()
|
|
6824
7433
|
: undefined;
|
|
7434
|
+
const previewDestinationGasFeeUsd = (() => {
|
|
7435
|
+
const value = getIntentDestinationGasUsdValue(intentData);
|
|
7436
|
+
return value && value.gt(0)
|
|
7437
|
+
? value.toDecimalPlaces(6, Decimal.ROUND_DOWN).toFixed()
|
|
7438
|
+
: undefined;
|
|
7439
|
+
})();
|
|
6825
7440
|
const predictiveExactInQuote = predictiveQuote?.mode === "exactIn" &&
|
|
6826
7441
|
predictiveQuote.key === getPredictiveQuoteCacheKey("swap", "exactIn")
|
|
6827
7442
|
? predictiveQuote
|
|
@@ -6830,13 +7445,20 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6830
7445
|
predictiveQuote.key === getPredictiveQuoteCacheKey(activeMode, "exactOut")
|
|
6831
7446
|
? predictiveQuote
|
|
6832
7447
|
: null;
|
|
7448
|
+
const shouldUseCurrentExactOutIntentSources = isExactOutPaymentFlow && hasCurrentQuoteIntent && hasIntentSources;
|
|
7449
|
+
const currentExactOutIntentSourceTokens = shouldUseCurrentExactOutIntentSources
|
|
7450
|
+
? sortSwapTokensByUsdDesc((intentData?.sources ?? [])
|
|
7451
|
+
.map(buildIntentSourceToken)
|
|
7452
|
+
.filter(hasPositiveSourceSpend))
|
|
7453
|
+
: [];
|
|
6833
7454
|
const resolvedToToken = toToken ??
|
|
6834
7455
|
(activeMode === "deposit" && selectedOpportunity
|
|
6835
7456
|
? toTokenFromOpportunity(selectedOpportunity)
|
|
6836
7457
|
: undefined);
|
|
6837
|
-
const
|
|
6838
|
-
|
|
6839
|
-
:
|
|
7458
|
+
const enrichedResolvedToToken = enrichTokenOptionLogo(resolvedToToken);
|
|
7459
|
+
const toTokenWithFetchedBalance = enrichedResolvedToToken && destinationBalance
|
|
7460
|
+
? { ...enrichedResolvedToToken, balance: destinationBalance }
|
|
7461
|
+
: enrichedResolvedToToken;
|
|
6840
7462
|
const idleReceiveQuoteAmount = activeMode === "swap" && swapType === "exactIn"
|
|
6841
7463
|
? (intentToAmount ?? predictiveExactInQuote?.toAmount)
|
|
6842
7464
|
: undefined;
|
|
@@ -6861,14 +7483,18 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6861
7483
|
Boolean(predictiveExactOutQuote &&
|
|
6862
7484
|
((predictiveExactOutQuote.sources?.length ?? 0) > 0 ||
|
|
6863
7485
|
destinationBalanceDisplayToken));
|
|
6864
|
-
const baseDisplayFromTokens =
|
|
6865
|
-
?
|
|
6866
|
-
:
|
|
7486
|
+
const baseDisplayFromTokens = shouldUseCurrentExactOutIntentSources
|
|
7487
|
+
? currentExactOutIntentSourceTokens
|
|
7488
|
+
: shouldShowPredictiveExactOutDisplay
|
|
7489
|
+
? (predictiveExactOutQuote?.sources ?? fromTokens)
|
|
7490
|
+
: fromTokens;
|
|
6867
7491
|
const displayFromTokens = (() => {
|
|
6868
|
-
if (
|
|
6869
|
-
(activeMode !== "deposit" && activeMode !== "send")) {
|
|
7492
|
+
if (activeMode !== "deposit" && activeMode !== "send") {
|
|
6870
7493
|
return baseDisplayFromTokens;
|
|
6871
7494
|
}
|
|
7495
|
+
if (!destinationBalanceDisplayToken) {
|
|
7496
|
+
return mergeDisplaySourceTokens(sortDisplaySourcesByBalanceUsdDesc(baseDisplayFromTokens));
|
|
7497
|
+
}
|
|
6872
7498
|
const destinationKey = getTokenSelectionKey(destinationBalanceDisplayToken);
|
|
6873
7499
|
let replacedEmptyDestinationToken = false;
|
|
6874
7500
|
const tokens = baseDisplayFromTokens.map((token) => {
|
|
@@ -6881,21 +7507,124 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6881
7507
|
}
|
|
6882
7508
|
return token;
|
|
6883
7509
|
});
|
|
6884
|
-
|
|
7510
|
+
const displayTokens = replacedEmptyDestinationToken
|
|
6885
7511
|
? tokens
|
|
6886
7512
|
: [...tokens, destinationBalanceDisplayToken];
|
|
7513
|
+
return mergeDisplaySourceTokens(sortDisplaySourcesByBalanceUsdDesc(displayTokens));
|
|
6887
7514
|
})();
|
|
6888
7515
|
const displayExactOutRouteLoading = isExactOutRouteLoading && !shouldShowPredictiveExactOutDisplay;
|
|
6889
|
-
const
|
|
7516
|
+
const isSourcePickerRefreshDisabled = quoteRefreshing || intentLoading || previewQuoteRefreshing;
|
|
7517
|
+
const isIntentSourceUiActionable = activeMode === "deposit"
|
|
7518
|
+
? !isDepositCtaDisabled
|
|
7519
|
+
: activeMode === "send"
|
|
7520
|
+
? !isSendCtaDisabled
|
|
7521
|
+
: !isSwapCtaDisabled;
|
|
7522
|
+
useEffect(() => {
|
|
7523
|
+
if (!intentFetchTiming?.endTimeMs) {
|
|
7524
|
+
return;
|
|
7525
|
+
}
|
|
7526
|
+
if (latestIntentFetchRunIdRef.current !== intentFetchTiming.runId) {
|
|
7527
|
+
return;
|
|
7528
|
+
}
|
|
7529
|
+
if (intentUiRenderLoggedRunIdRef.current === intentFetchTiming.runId) {
|
|
7530
|
+
return;
|
|
7531
|
+
}
|
|
7532
|
+
const hasRenderedSources = displayFromTokens.length > 0 ||
|
|
7533
|
+
hasIntentSources ||
|
|
7534
|
+
Boolean(intentData?.destination);
|
|
7535
|
+
if (!hasRenderedSources)
|
|
7536
|
+
return;
|
|
7537
|
+
if (isSwapBalancePending ||
|
|
7538
|
+
isSourcePickerRefreshDisabled ||
|
|
7539
|
+
quoteRefreshing ||
|
|
7540
|
+
intentLoading ||
|
|
7541
|
+
previewQuoteRefreshing ||
|
|
7542
|
+
receiveMaxCalculating ||
|
|
7543
|
+
displayExactOutRouteLoading ||
|
|
7544
|
+
!isIntentSourceUiActionable) {
|
|
7545
|
+
return;
|
|
7546
|
+
}
|
|
7547
|
+
const renderTimeMs = Date.now();
|
|
7548
|
+
const renderDurationMs = renderTimeMs - intentFetchTiming.startTimeMs;
|
|
7549
|
+
const fetchDurationMs = intentFetchTiming.fetchDurationMs ??
|
|
7550
|
+
intentFetchTiming.endTimeMs - intentFetchTiming.startTimeMs;
|
|
7551
|
+
const differenceMs = renderDurationMs - fetchDurationMs;
|
|
7552
|
+
console.log("[NexusWidget] intent sources UI render complete", {
|
|
7553
|
+
runId: intentFetchTiming.runId,
|
|
7554
|
+
walletAddress: intentFetchTiming.walletAddress,
|
|
7555
|
+
operation: intentFetchTiming.operation,
|
|
7556
|
+
quoteInputKey: intentFetchTiming.quoteInputKey,
|
|
7557
|
+
sourceCount: displayFromTokens.length,
|
|
7558
|
+
dateNow: renderTimeMs,
|
|
7559
|
+
date: new Date(renderTimeMs),
|
|
7560
|
+
totalRenderTimeSeconds: renderDurationMs / 1000,
|
|
7561
|
+
elapsedMs: renderDurationMs,
|
|
7562
|
+
});
|
|
7563
|
+
console.log("[NexusWidget] intent render/fetch difference", {
|
|
7564
|
+
runId: intentFetchTiming.runId,
|
|
7565
|
+
walletAddress: intentFetchTiming.walletAddress,
|
|
7566
|
+
operation: intentFetchTiming.operation,
|
|
7567
|
+
quoteInputKey: intentFetchTiming.quoteInputKey,
|
|
7568
|
+
differenceMs,
|
|
7569
|
+
dateNow: renderTimeMs,
|
|
7570
|
+
date: new Date(renderTimeMs),
|
|
7571
|
+
});
|
|
7572
|
+
intentUiRenderLoggedRunIdRef.current = intentFetchTiming.runId;
|
|
7573
|
+
}, [
|
|
7574
|
+
displayExactOutRouteLoading,
|
|
7575
|
+
displayFromTokens.length,
|
|
7576
|
+
hasIntentSources,
|
|
7577
|
+
intentLoading,
|
|
7578
|
+
intentData?.destination,
|
|
7579
|
+
intentFetchTiming,
|
|
7580
|
+
isDepositCtaDisabled,
|
|
7581
|
+
isIntentSourceUiActionable,
|
|
7582
|
+
isSendCtaDisabled,
|
|
7583
|
+
isSourcePickerRefreshDisabled,
|
|
7584
|
+
isSwapCtaDisabled,
|
|
7585
|
+
isSwapBalancePending,
|
|
7586
|
+
previewQuoteRefreshing,
|
|
7587
|
+
quoteRefreshing,
|
|
7588
|
+
receiveMaxCalculating,
|
|
7589
|
+
]);
|
|
7590
|
+
const totalSwapBalanceUsd = getActiveTotalBalanceUsd()
|
|
6890
7591
|
.toDecimalPlaces(2)
|
|
6891
7592
|
.toFixed();
|
|
6892
|
-
const
|
|
7593
|
+
const requestedOutputUsd = amount && toToken
|
|
6893
7594
|
? getTokenUsdValue({
|
|
6894
7595
|
...toToken,
|
|
6895
7596
|
userAmount: amount,
|
|
6896
7597
|
userAmountMode: "token",
|
|
6897
|
-
}, amount)
|
|
6898
|
-
: 0;
|
|
7598
|
+
}, amount)
|
|
7599
|
+
: new Decimal(0);
|
|
7600
|
+
const sendAmountUsd = requestedOutputUsd.toNumber();
|
|
7601
|
+
const exactOutRequiredUsdAmount = (() => {
|
|
7602
|
+
if (activeMode !== "deposit" && activeMode !== "send")
|
|
7603
|
+
return undefined;
|
|
7604
|
+
const issueMissingUsd = parseFiatNumber(insufficientSourceIssue?.missingUsd);
|
|
7605
|
+
if (issueMissingUsd && issueMissingUsd.gt(0)) {
|
|
7606
|
+
return getExactOutAvailableSourceUsd().plus(issueMissingUsd);
|
|
7607
|
+
}
|
|
7608
|
+
const requiredFundingUsd = getExactOutRequiredFundingUsd();
|
|
7609
|
+
if (requiredFundingUsd?.gt(0))
|
|
7610
|
+
return requiredFundingUsd;
|
|
7611
|
+
if (requestedOutputUsd.gt(0))
|
|
7612
|
+
return requestedOutputUsd;
|
|
7613
|
+
return undefined;
|
|
7614
|
+
})();
|
|
7615
|
+
const exactOutRequiredUsdDisplay = exactOutRequiredUsdAmount
|
|
7616
|
+
?.toDecimalPlaces(2)
|
|
7617
|
+
.toFixed();
|
|
7618
|
+
const assetSelectorRequiredUsdDisplay = (activeMode === "deposit" || activeMode === "send") &&
|
|
7619
|
+
(swapStep === "choose-swap-asset" ||
|
|
7620
|
+
closingDrawerStep === "choose-swap-asset")
|
|
7621
|
+
? (sourceSelectionRequiredUsdDisplay ?? exactOutRequiredUsdDisplay)
|
|
7622
|
+
: exactOutRequiredUsdDisplay;
|
|
7623
|
+
const captureSourceSelectionRequiredUsd = () => {
|
|
7624
|
+
if (activeMode !== "deposit" && activeMode !== "send")
|
|
7625
|
+
return;
|
|
7626
|
+
setSourceSelectionRequiredUsdDisplay(exactOutRequiredUsdDisplay);
|
|
7627
|
+
};
|
|
6899
7628
|
const isIdleSwapQuoteLoading = activeMode === "swap" &&
|
|
6900
7629
|
swapStep === "idle" &&
|
|
6901
7630
|
(quoteRefreshing || intentLoading);
|
|
@@ -6939,6 +7668,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6939
7668
|
swapStep === "choose-receive-asset" ||
|
|
6940
7669
|
swapStep === "enter-recipient" ||
|
|
6941
7670
|
closingDrawerStep !== null;
|
|
7671
|
+
const isPreviewIntentScreen = swapStep === "preview-intent";
|
|
6942
7672
|
const displayedRootContentHeight = hasMeasuredRootContent && rootContentHeight
|
|
6943
7673
|
? Math.max(rootContentHeight, tokenAssetDrawerMinRootContentHeight)
|
|
6944
7674
|
: null;
|
|
@@ -6976,8 +7706,8 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
6976
7706
|
lineHeight: "17px",
|
|
6977
7707
|
margin: "auto",
|
|
6978
7708
|
overflowX: "hidden",
|
|
6979
|
-
overflowY: isDrawerOverlayActive ? "hidden" : "auto",
|
|
6980
|
-
overscrollBehavior: isDrawerOverlayActive ? "contain" : "auto",
|
|
7709
|
+
overflowY: isDrawerOverlayActive || isPreviewIntentScreen ? "hidden" : "auto",
|
|
7710
|
+
overscrollBehavior: isDrawerOverlayActive || isPreviewIntentScreen ? "contain" : "auto",
|
|
6981
7711
|
padding: "16px",
|
|
6982
7712
|
scrollbarColor: `${theme.colors.textEmpty} transparent`,
|
|
6983
7713
|
scrollbarWidth: "thin",
|
|
@@ -7086,7 +7816,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7086
7816
|
swapStep !== "choose-swap-asset" &&
|
|
7087
7817
|
swapStep !== "choose-receive-asset" &&
|
|
7088
7818
|
swapStep !== "enter-recipient" && (_jsxs(_Fragment, { children: [swapStep === "preview-intent" && (_jsx("div", { className: "w-full", style: {
|
|
7089
|
-
maxHeight: "calc(90dvh -
|
|
7819
|
+
maxHeight: "calc(90dvh - 92px)",
|
|
7090
7820
|
minHeight: 0,
|
|
7091
7821
|
overflowX: "hidden",
|
|
7092
7822
|
overflowY: isPreviewTransitioning ? "hidden" : "auto",
|
|
@@ -7094,7 +7824,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7094
7824
|
scrollbarColor: "#C8C8C7 transparent",
|
|
7095
7825
|
scrollbarWidth: "thin",
|
|
7096
7826
|
width: "100%",
|
|
7097
|
-
}, children: _jsx(SwapIntentPreview, { activeMode: activeMode, estimatedTime: "10s", explorerUrls: explorerUrls, fromAmount: amount, fromAmountUsd: previewFromAmountUsd, fromToken: fromTokens[0], fromTokens: fromTokens, intentData: intentData, isLoading: intentLoading, isRefreshing: previewQuoteRefreshing, mode: activeMode, onAccept: handleSwapAccept, onReject: () => {
|
|
7827
|
+
}, children: _jsx(SwapIntentPreview, { activeMode: activeMode, destinationGasFeeUsd: previewDestinationGasFeeUsd, estimatedTime: "10s", explorerUrls: explorerUrls, fromAmount: amount, fromAmountUsd: previewFromAmountUsd, fromToken: fromTokens[0], fromTokens: fromTokens, intentData: intentData, isLoading: intentLoading, isRefreshing: previewQuoteRefreshing, mode: activeMode, onAccept: handleSwapAccept, onReject: () => {
|
|
7098
7828
|
clearPendingSwapIntent();
|
|
7099
7829
|
setSwapStep("idle");
|
|
7100
7830
|
}, onTransitionChange: setIsPreviewTransitioning, opportunity: selectedOpportunity, recipientAddress: transferRecipientAddress, steps: steps, supportedTokenAssets: supportedChainsAndTokens, swapBalances: swapBalance, swapType: swapType, toAmount: previewDestinationAmount, toAmountTokens: previewDestinationAmount
|
|
@@ -7102,13 +7832,13 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7102
7832
|
: undefined, toAmountUsd: previewToAmountUsd, toToken: toTokenWithFetchedBalance, totalFeeUsd: intentFeeUsd }) })), swapStep === "progress" && (_jsx(NexusWidgetProgressScreen, { failedStep: failedProgressStep, fromAmountUsd: previewFromAmountUsd, fromTokens: fromTokens, intentData: intentData, mode: activeMode, opportunity: selectedOpportunity, progressEvents: progressEvents, recipientAddress: transferRecipientAddress, steps: steps, toAmount: previewDestinationAmount, toAmountUsd: previewToAmountUsd, toToken: toTokenWithFetchedBalance })), (swapStep === "success" || swapStep === "failed") &&
|
|
7103
7833
|
currentSwapEntry && (_jsx("div", { className: "animate-in fade-in slide-in-from-bottom-4 duration-500 w-full", children: _jsx(SwapReceiptPanel, { entry: currentSwapEntry, onDone: swapStep === "failed"
|
|
7104
7834
|
? handleFailureBack
|
|
7105
|
-
:
|
|
7835
|
+
: handleTerminalReceiptDone }) }))] })), swapStep === "history" && (_jsx(SwapHistoryPanel, { entries: swapHistory, now: historyNow })), activeMode === "swap" &&
|
|
7106
7836
|
[
|
|
7107
7837
|
"idle",
|
|
7108
7838
|
"choose-swap-asset",
|
|
7109
7839
|
"choose-receive-asset",
|
|
7110
7840
|
"enter-recipient",
|
|
7111
|
-
].includes(swapStep) && (_jsxs(_Fragment, { children: [_jsx(SwapIdleForm, { amount: amount, defaultRecipientAddress: defaultRecipientAddress, fromTokens: fromTokens, isReceiveAmountLoading: isReceiveAmountLoading, isReceiveUsdLoading: isReceiveUsdLoading, isAmountReadOnly: isConfiguredAmountFixed, isDestinationPickerDisabled: isReceiveAssetPickerLocked, hideDestinationTokenDropdownIcon: hideDestinationTokenDropdownIcon, onAmountChange: (val, panel) => {
|
|
7841
|
+
].includes(swapStep) && (_jsxs(_Fragment, { children: [_jsx(SwapIdleForm, { amount: amount, defaultRecipientAddress: defaultRecipientAddress, fromTokens: fromTokens, isReceiveAmountLoading: isReceiveAmountLoading, isReceiveUsdLoading: isReceiveUsdLoading, isAmountReadOnly: isConfiguredAmountFixed, isBalanceLoading: isSwapBalancePending, isDestinationPickerDisabled: isReceiveAssetPickerLocked, hideDestinationTokenDropdownIcon: hideDestinationTokenDropdownIcon, onAmountChange: (val, panel) => {
|
|
7112
7842
|
handleSwapAmountChange(val, panel);
|
|
7113
7843
|
}, onOpenDestPicker: () => openDrawerStep("choose-receive-asset"), onOpenRecipientPicker: handleOpenRecipientEditor, onOpenSourcePicker: (index) => {
|
|
7114
7844
|
if (needsWalletConnection) {
|
|
@@ -7116,6 +7846,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7116
7846
|
return;
|
|
7117
7847
|
}
|
|
7118
7848
|
setEditingAssetIndex(index ?? null);
|
|
7849
|
+
beginSourcePickerEdit();
|
|
7119
7850
|
openDrawerStep("choose-swap-asset");
|
|
7120
7851
|
}, onUpdateTokens: handleSwapTokensUpdate, receiveQuoteAmount: swapType === "exactIn" ? idleReceiveQuoteAmount : undefined, receiveQuoteUsd: idleReceiveQuoteUsd, recipientAddress: effectiveRecipientAddress, sourceRouteMessage: insufficientSourceIssue?.message, sourceRouteStatus: insufficientSourceIssue
|
|
7121
7852
|
? "insufficient"
|
|
@@ -7187,12 +7918,14 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7187
7918
|
"choose-swap-asset",
|
|
7188
7919
|
"choose-receive-asset",
|
|
7189
7920
|
"enter-recipient",
|
|
7190
|
-
].includes(swapStep) && (_jsx(_Fragment, { children: selectedOpportunity && (_jsxs(_Fragment, { children: [_jsx(DepositIdleForm, { amount: amount, amountMode: depositAmountMode, calculatingPercent: maxCalculationPercent, fromTokens: displayFromTokens, isAmountReadOnly: isConfiguredAmountFixed, isCalculatingMax: receiveMaxCalculating, isQuoteRefreshing: !hasCurrentExactOutPaymentIntent &&
|
|
7191
|
-
(quoteRefreshing || intentLoading), onAmountChange: handleDepositAmountChange, onAmountModeToggle: handleDepositAmountModeToggle, onOpenSourcePicker: () => {
|
|
7921
|
+
].includes(swapStep) && (_jsx(_Fragment, { children: selectedOpportunity && (_jsxs(_Fragment, { children: [_jsx(DepositIdleForm, { amount: amount, amountMode: depositAmountMode, calculatingPercent: maxCalculationPercent, fromTokens: displayFromTokens, isAmountReadOnly: isConfiguredAmountFixed, isBalanceLoading: isSwapBalancePending, isCalculatingMax: receiveMaxCalculating, isQuoteRefreshing: !hasCurrentExactOutPaymentIntent &&
|
|
7922
|
+
(quoteRefreshing || intentLoading), isSourcePickerDisabled: isSourcePickerRefreshDisabled, onAmountChange: handleDepositAmountChange, onAmountModeToggle: handleDepositAmountModeToggle, onOpenSourcePicker: () => {
|
|
7192
7923
|
if (needsWalletConnection) {
|
|
7193
7924
|
void handleConnectWallet();
|
|
7194
7925
|
return;
|
|
7195
7926
|
}
|
|
7927
|
+
captureSourceSelectionRequiredUsd();
|
|
7928
|
+
beginSourcePickerEdit();
|
|
7196
7929
|
openDrawerStep("choose-swap-asset");
|
|
7197
7930
|
}, onOpenTokenPicker: () => openDrawerStep("choose-receive-asset"), onSetPercent: handleDepositPercentSelect, routeMessage: insufficientSourceIssue?.message, routeStatus: insufficientSourceIssue
|
|
7198
7931
|
? "insufficient"
|
|
@@ -7238,8 +7971,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7238
7971
|
height: "14px",
|
|
7239
7972
|
width: "14px",
|
|
7240
7973
|
} })) : (needsWalletConnection && walletConnectBusy) ||
|
|
7241
|
-
|
|
7242
|
-
(quoteRefreshing || intentLoading)) ||
|
|
7974
|
+
isExactOutPaymentQuotePending ||
|
|
7243
7975
|
receiveMaxCalculating ? (_jsx(Loader2, { className: "animate-spin", style: {
|
|
7244
7976
|
color: isDepositCtaDisabled
|
|
7245
7977
|
? theme.colors.muted
|
|
@@ -7263,8 +7995,8 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7263
7995
|
"choose-swap-asset",
|
|
7264
7996
|
"choose-receive-asset",
|
|
7265
7997
|
"enter-recipient",
|
|
7266
|
-
].includes(swapStep) && (_jsxs(_Fragment, { children: [_jsx(SendIdleForm, { amount: amount, calculatingPercent: maxCalculationPercent, fromTokens: displayFromTokens, isAmountReadOnly: isConfiguredAmountFixed, isAssetPickerDisabled: isReceiveAssetPickerLocked, hideDestinationTokenDropdownIcon: hideDestinationTokenDropdownIcon, isCalculatingMax: receiveMaxCalculating, isQuoteRefreshing: !hasCurrentExactOutPaymentIntent &&
|
|
7267
|
-
(quoteRefreshing || intentLoading), onAmountChange: handleSendAmountChange, onOpenAssetPicker: () => openDrawerStep("choose-receive-asset"), isRecipientLocked: isConfiguredRecipientLocked, onOpenRecipientPicker: isConfiguredRecipientLocked
|
|
7998
|
+
].includes(swapStep) && (_jsxs(_Fragment, { children: [_jsx(SendIdleForm, { amount: amount, calculatingPercent: maxCalculationPercent, fromTokens: displayFromTokens, isAmountReadOnly: isConfiguredAmountFixed, isAssetPickerDisabled: isReceiveAssetPickerLocked, isBalanceLoading: isSwapBalancePending, hideDestinationTokenDropdownIcon: hideDestinationTokenDropdownIcon, isCalculatingMax: receiveMaxCalculating, isQuoteRefreshing: !hasCurrentExactOutPaymentIntent &&
|
|
7999
|
+
(quoteRefreshing || intentLoading), isSourcePickerDisabled: isSourcePickerRefreshDisabled, onAmountChange: handleSendAmountChange, onOpenAssetPicker: () => openDrawerStep("choose-receive-asset"), isRecipientLocked: isConfiguredRecipientLocked, onOpenRecipientPicker: isConfiguredRecipientLocked
|
|
7268
8000
|
? () => undefined
|
|
7269
8001
|
: handleOpenRecipientEditor, onOpenSourcePicker: () => {
|
|
7270
8002
|
if (needsWalletConnection) {
|
|
@@ -7272,6 +8004,8 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7272
8004
|
return;
|
|
7273
8005
|
}
|
|
7274
8006
|
setEditingAssetIndex(null);
|
|
8007
|
+
captureSourceSelectionRequiredUsd();
|
|
8008
|
+
beginSourcePickerEdit();
|
|
7275
8009
|
openDrawerStep("choose-swap-asset");
|
|
7276
8010
|
}, onSetPercent: handleSendPercentSelect, recipientAddress: recipientAddress || "", routeMessage: insufficientSourceIssue?.message, routeStatus: insufficientSourceIssue
|
|
7277
8011
|
? "insufficient"
|
|
@@ -7320,8 +8054,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7320
8054
|
width: "14px",
|
|
7321
8055
|
} })) : (needsWalletConnection && walletConnectBusy) ||
|
|
7322
8056
|
(!sendNeedsRecipient &&
|
|
7323
|
-
(
|
|
7324
|
-
(quoteRefreshing || intentLoading)) ||
|
|
8057
|
+
(isExactOutPaymentQuotePending ||
|
|
7325
8058
|
receiveMaxCalculating)) ? (_jsx(Loader2, { className: "animate-spin", style: {
|
|
7326
8059
|
color: isSendCtaDisabled
|
|
7327
8060
|
? theme.colors.muted
|
|
@@ -7508,7 +8241,7 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7508
8241
|
display: "flex",
|
|
7509
8242
|
flexDirection: "column",
|
|
7510
8243
|
justifyContent: "flex-end",
|
|
7511
|
-
}, children: [_jsx("div", { onClick:
|
|
8244
|
+
}, children: [_jsx("div", { onClick: handleSourcePickerCancel, style: {
|
|
7512
8245
|
position: "absolute",
|
|
7513
8246
|
top: 0,
|
|
7514
8247
|
left: 0,
|
|
@@ -7546,31 +8279,16 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7546
8279
|
willChange: "height, max-height, transform, opacity",
|
|
7547
8280
|
}, children: _jsx(SwapAssetSelector, { allowSelectedTokenRemoval: false, allowUnified: activeMode === "deposit" ||
|
|
7548
8281
|
activeMode === "send" ||
|
|
7549
|
-
activeMode === "swap", autoSelectFilterTabs: activeMode === "deposit" || activeMode === "send", editingAssetIndex: editingAssetIndex, filterTabBehavior: activeMode === "deposit" ? "source-pool" : "select-all", hideCustomTab: activeMode === "swap",
|
|
7550
|
-
?
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
}
|
|
7560
|
-
: undefined, onDone: closeDrawerToIdle, onFilterTabSelect: activeMode === "deposit"
|
|
7561
|
-
? (tab) => {
|
|
7562
|
-
const nextFilter = tab === "stables" ? "stablecoins" : tab;
|
|
7563
|
-
setDepositSourceFilter(nextFilter);
|
|
7564
|
-
setSourceSelectionTouched(false);
|
|
7565
|
-
setExactOutQuoteSourceModeValue("all");
|
|
7566
|
-
invalidateExactOutQuoteForRefresh();
|
|
7567
|
-
setSourceSelectionRevision((current) => current + 1);
|
|
7568
|
-
const selection = getResolvedDepositSourceSelection({
|
|
7569
|
-
filter: nextFilter,
|
|
7570
|
-
isManualSelection: false,
|
|
7571
|
-
});
|
|
7572
|
-
setFromTokens(getDepositSourceTokensForIds(selection.selectedSourceIds));
|
|
7573
|
-
}
|
|
8282
|
+
activeMode === "swap", autoSelectFilterTabs: activeMode === "deposit" || activeMode === "send", editingAssetIndex: editingAssetIndex, filterTabBehavior: activeMode === "deposit" ? "source-pool" : "select-all", hideCustomTab: activeMode === "swap", initialFilterTab: activeMode === "deposit"
|
|
8283
|
+
? depositSourceFilter === "stablecoins"
|
|
8284
|
+
? "stables"
|
|
8285
|
+
: depositSourceFilter
|
|
8286
|
+
: activeMode === "send" && sourceSelectionTouched
|
|
8287
|
+
? "custom"
|
|
8288
|
+
: "all", isMulti: activeMode === "deposit" || activeMode === "send", lockedTokens: lockedDestinationSourceTokens, onBack: handleSourcePickerCancel, onDone: activeMode === "deposit" || activeMode === "send"
|
|
8289
|
+
? commitSourcePickerDraft
|
|
8290
|
+
: closeDrawerToIdle, onFilterTabSelect: activeMode === "deposit" || activeMode === "send"
|
|
8291
|
+
? handleSourcePickerFilterTabSelect
|
|
7574
8292
|
: undefined, onSelect: (token) => {
|
|
7575
8293
|
if (activeMode === "swap") {
|
|
7576
8294
|
const next = [...fromTokens];
|
|
@@ -7606,71 +8324,43 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7606
8324
|
}
|
|
7607
8325
|
else if (activeMode === "deposit" ||
|
|
7608
8326
|
activeMode === "send") {
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
setDepositSourceFilter("custom");
|
|
7613
|
-
}
|
|
7614
|
-
invalidateExactOutQuoteForRefresh();
|
|
7615
|
-
setSourceSelectionRevision((current) => current + 1);
|
|
7616
|
-
setFromTokens([{ ...token, userAmount: amount }]);
|
|
7617
|
-
closeDrawerToIdle();
|
|
8327
|
+
handleSourcePickerDraftSelectionChange([
|
|
8328
|
+
{ ...token, userAmount: "" },
|
|
8329
|
+
]);
|
|
7618
8330
|
}
|
|
7619
8331
|
}, onSelectionChange: activeMode === "deposit" || activeMode === "send"
|
|
7620
|
-
?
|
|
7621
|
-
setSourceSelectionTouched(true);
|
|
7622
|
-
setExactOutQuoteSourceModeValue("selected");
|
|
7623
|
-
if (activeMode === "deposit") {
|
|
7624
|
-
setDepositSourceFilter("custom");
|
|
7625
|
-
}
|
|
7626
|
-
invalidateExactOutQuoteForRefresh();
|
|
7627
|
-
setSourceSelectionRevision((current) => current + 1);
|
|
7628
|
-
setFromTokens(tokens.map((token) => ({
|
|
7629
|
-
...token,
|
|
7630
|
-
userAmount: "",
|
|
7631
|
-
})));
|
|
7632
|
-
}
|
|
8332
|
+
? handleSourcePickerDraftSelectionChange
|
|
7633
8333
|
: undefined, onToggle: (token) => {
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
8334
|
+
const isDepositOrSendSourcePicker = activeMode === "deposit" || activeMode === "send";
|
|
8335
|
+
const prevTokens = isDepositOrSendSourcePicker
|
|
8336
|
+
? sourcePickerSelectedTokens
|
|
8337
|
+
: fromTokens;
|
|
8338
|
+
const isSameSelection = (a, b) => {
|
|
8339
|
+
if (a.isUnified || b.isUnified) {
|
|
8340
|
+
return Boolean(a.isUnified &&
|
|
8341
|
+
b.isUnified &&
|
|
8342
|
+
a.unifiedSymbol === b.unifiedSymbol);
|
|
7639
8343
|
}
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
}
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
b.contractAddress.toLowerCase() &&
|
|
7655
|
-
a.chainId === b.chainId);
|
|
7656
|
-
};
|
|
7657
|
-
const isDepositOrSendSourcePicker = activeMode === "deposit" || activeMode === "send";
|
|
7658
|
-
const sourceTokens = token.sourceTokens ?? [];
|
|
7659
|
-
const isSameUnifiedGroup = (item) => Boolean(item.isUnified &&
|
|
7660
|
-
token.isUnified &&
|
|
7661
|
-
item.unifiedSymbol === token.unifiedSymbol);
|
|
7662
|
-
const withDefaultAmount = (item) => ({
|
|
7663
|
-
...item,
|
|
7664
|
-
userAmount: activeMode === "swap" && prev.length === 0
|
|
7665
|
-
? amount
|
|
7666
|
-
: "",
|
|
7667
|
-
});
|
|
8344
|
+
return (a.contractAddress.toLowerCase() ===
|
|
8345
|
+
b.contractAddress.toLowerCase() && a.chainId === b.chainId);
|
|
8346
|
+
};
|
|
8347
|
+
const sourceTokens = token.sourceTokens ?? [];
|
|
8348
|
+
const isSameUnifiedGroup = (item) => Boolean(item.isUnified &&
|
|
8349
|
+
token.isUnified &&
|
|
8350
|
+
item.unifiedSymbol === token.unifiedSymbol);
|
|
8351
|
+
const withDefaultAmount = (item) => ({
|
|
8352
|
+
...item,
|
|
8353
|
+
userAmount: activeMode === "swap" && prevTokens.length === 0
|
|
8354
|
+
? amount
|
|
8355
|
+
: "",
|
|
8356
|
+
});
|
|
8357
|
+
const getNextTokens = () => {
|
|
7668
8358
|
if (isDepositOrSendSourcePicker &&
|
|
7669
8359
|
token.isUnified &&
|
|
7670
8360
|
sourceTokens.length > 0) {
|
|
7671
|
-
const hasUnifiedSelection =
|
|
7672
|
-
const areAllChildrenSelected = sourceTokens.every((source) =>
|
|
7673
|
-
const withoutGroup =
|
|
8361
|
+
const hasUnifiedSelection = prevTokens.some(isSameUnifiedGroup);
|
|
8362
|
+
const areAllChildrenSelected = sourceTokens.every((source) => prevTokens.some((item) => isSameSelection(item, source)));
|
|
8363
|
+
const withoutGroup = prevTokens.filter((item) => !isSameUnifiedGroup(item) &&
|
|
7674
8364
|
!sourceTokens.some((source) => isSameSelection(item, source)));
|
|
7675
8365
|
if (hasUnifiedSelection || areAllChildrenSelected) {
|
|
7676
8366
|
return withoutGroup;
|
|
@@ -7681,10 +8371,10 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7681
8371
|
];
|
|
7682
8372
|
}
|
|
7683
8373
|
if (isDepositOrSendSourcePicker && !token.isUnified) {
|
|
7684
|
-
const unifiedSelection =
|
|
8374
|
+
const unifiedSelection = prevTokens.find((item) => item.isUnified &&
|
|
7685
8375
|
item.sourceTokens?.some((source) => isSameSelection(source, token)));
|
|
7686
8376
|
if (unifiedSelection?.sourceTokens?.length) {
|
|
7687
|
-
const withoutUnified =
|
|
8377
|
+
const withoutUnified = prevTokens.filter((item) => !isSameSelection(item, unifiedSelection));
|
|
7688
8378
|
return [
|
|
7689
8379
|
...withoutUnified,
|
|
7690
8380
|
...unifiedSelection.sourceTokens
|
|
@@ -7693,12 +8383,12 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7693
8383
|
];
|
|
7694
8384
|
}
|
|
7695
8385
|
}
|
|
7696
|
-
const exists =
|
|
8386
|
+
const exists = prevTokens.find((item) => isSameSelection(item, token));
|
|
7697
8387
|
if (exists) {
|
|
7698
|
-
return
|
|
8388
|
+
return prevTokens.filter((item) => !isSameSelection(item, token));
|
|
7699
8389
|
}
|
|
7700
8390
|
const tokenSourceKeys = new Set((token.sourceTokens ?? []).map((source) => `${source.chainId}-${source.contractAddress.toLowerCase()}`));
|
|
7701
|
-
const next =
|
|
8391
|
+
const next = prevTokens.filter((existing) => {
|
|
7702
8392
|
if (token.isUnified &&
|
|
7703
8393
|
tokenSourceKeys.has(`${existing.chainId}-${existing.contractAddress.toLowerCase()}`)) {
|
|
7704
8394
|
return false;
|
|
@@ -7712,12 +8402,16 @@ function NexusWidgetInner({ config: rawConfig, embed = true, className, connecte
|
|
|
7712
8402
|
return true;
|
|
7713
8403
|
});
|
|
7714
8404
|
return [...next, withDefaultAmount(token)];
|
|
7715
|
-
}
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
8405
|
+
};
|
|
8406
|
+
const nextTokens = getNextTokens();
|
|
8407
|
+
if (isDepositOrSendSourcePicker) {
|
|
8408
|
+
handleSourcePickerDraftSelectionChange(nextTokens);
|
|
8409
|
+
}
|
|
8410
|
+
else {
|
|
8411
|
+
clearPendingSwapIntent();
|
|
8412
|
+
setFromTokens(nextTokens);
|
|
8413
|
+
}
|
|
8414
|
+
}, preserveSelectedBelowMinimum: false, requiredUsd: assetSelectorRequiredUsdDisplay, selectedTokens: sourcePickerSelectedTokens, showBelowMinimumInline: activeMode === "swap" && swapType === "exactIn", swapBalance: swapBalance, swapSupportedChains: swapSupportedChainsAndTokens, title: activeMode === "deposit" || activeMode === "send"
|
|
7721
8415
|
? "Choose Assets to Pay with"
|
|
7722
8416
|
: "Choose assets to send" }) })] })), (activeMode === "swap" ||
|
|
7723
8417
|
activeMode === "send" ||
|