Skip to content

Commit d7e8387

Browse files
committed
feat: dynamic slippage solana persistence
1 parent f3900fd commit d7e8387

File tree

7 files changed

+89
-16
lines changed

7 files changed

+89
-16
lines changed

app/_locales/en/messages.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/_locales/en_GB/messages.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/scripts/controllers/preferences-controller.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export type Preferences = {
106106
};
107107
tokenNetworkFilter: Record<string, boolean>;
108108
dismissSmartAccountSuggestionEnabled: boolean;
109+
solanaSlippage?: number;
109110
};
110111

111112
// Omitting properties that already exist in the PreferencesState, as part of the preferences property.
@@ -200,6 +201,7 @@ export const getDefaultPreferencesControllerState =
200201
sortCallback: 'stringNumeric',
201202
},
202203
tokenNetworkFilter: {},
204+
solanaSlippage: undefined,
203205
},
204206
// ENS decentralized website resolution
205207
ipfsGateway: IPFS_DEFAULT_GATEWAY_URL,
@@ -357,6 +359,10 @@ const controllerMetadata = {
357359
persist: true,
358360
anonymous: true,
359361
},
362+
solanaSlippage: {
363+
persist: true,
364+
anonymous: true,
365+
},
360366
},
361367
},
362368
ipfsGateway: {

ui/ducks/bridge/selectors.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import {
6666
} from '../../../shared/constants/hardware-wallets';
6767
import { toAssetId } from '../../../shared/lib/asset-utils';
6868
import { getRemoteFeatureFlags } from '../../selectors/remote-feature-flags';
69+
import { getPreferences } from '../../selectors';
6970
import {
7071
exchangeRateFromMarketData,
7172
exchangeRatesFromNativeAndCurrencyRates,
@@ -244,6 +245,9 @@ export const getFromAmount = (state: BridgeAppState): string | null =>
244245

245246
export const getSlippage = (state: BridgeAppState) => state.bridge.slippage;
246247

248+
export const getSolanaSlippage = (state: BridgeAppState) =>
249+
getPreferences(state)?.solanaSlippage;
250+
247251
export const getQuoteRequest = (state: BridgeAppState) => {
248252
const { quoteRequest } = state.metamask;
249253
return quoteRequest;
@@ -618,6 +622,40 @@ export const getIsToOrFromSolana = createSelector(
618622
},
619623
);
620624

625+
export const getIsSolanaSwap = createSelector(
626+
getFromChain,
627+
getToChain,
628+
(fromChain, toChain) => {
629+
if (!fromChain?.chainId || !toChain?.chainId) {
630+
return false;
631+
}
632+
633+
const fromChainIsSolana = isSolanaChainId(fromChain.chainId);
634+
const toChainIsSolana = isSolanaChainId(toChain.chainId);
635+
636+
// Return true if BOTH chains are Solana (Solana-to-Solana swap)
637+
return fromChainIsSolana && toChainIsSolana;
638+
},
639+
);
640+
641+
export const getEffectiveSlippage = (state: BridgeAppState) => {
642+
const isSolanaSwap = getIsSolanaSwap(state);
643+
const solanaSlippage = getSolanaSlippage(state);
644+
const regularSlippage = getSlippage(state);
645+
646+
// For Solana transactions (either bridge to/from Solana or Solana-to-Solana swap),
647+
// use solanaSlippage if it's been set by the user, otherwise default to undefined (AUTO)
648+
// Note: We check for both null and undefined since JSON serialization converts undefined to null
649+
if (isSolanaSwap) {
650+
return solanaSlippage === null || solanaSlippage === undefined
651+
? undefined
652+
: solanaSlippage;
653+
}
654+
655+
// For EVM transactions, use the regular slippage
656+
return regularSlippage;
657+
};
658+
621659
export const getHardwareWalletName = (state: BridgeAppState) => {
622660
const type = getHardwareWalletType(state);
623661
switch (type) {

ui/pages/bridge/prepare/bridge-transaction-settings-modal.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ import {
3333
SEVERITIES,
3434
} from '../../../helpers/constants/design-system';
3535
import {
36-
getSlippage,
37-
getIsToOrFromSolana,
36+
getIsSolanaSwap,
37+
getEffectiveSlippage,
3838
} from '../../../ducks/bridge/selectors';
3939
import { setSlippage } from '../../../ducks/bridge/actions';
40+
import { setSolanaSlippage } from '../../../store/actions';
4041
import { useCrossChainSwapsEventTracker } from '../../../hooks/bridge/useCrossChainSwapsEventTracker';
4142
import { MetaMetricsEventName } from '../../../../shared/constants/metametrics';
4243
import { Column, Row, Tooltip } from '../layout';
43-
import { useIsMultichainSwap } from '../hooks/useIsMultichainSwap';
4444

4545
const HARDCODED_SLIPPAGE_OPTIONS = [BRIDGE_DEFAULT_SLIPPAGE, 3];
4646

@@ -52,12 +52,11 @@ export const BridgeTransactionSettingsModal = ({
5252
const trackCrossChainSwapsEvent = useCrossChainSwapsEventTracker();
5353

5454
const dispatch = useDispatch();
55-
const isSwap = useIsMultichainSwap();
56-
const isToOrFromSolana = useSelector(getIsToOrFromSolana);
57-
const slippage = useSelector(getSlippage);
55+
const isSolanaSwap = useSelector(getIsSolanaSwap);
56+
const slippage = useSelector(getEffectiveSlippage);
5857

59-
// For Solana trades, undefined slippage means AUTO
60-
const shouldShowAutoOption = isSwap || isToOrFromSolana;
58+
// AUTO option should only show for Solana-to-Solana swaps
59+
const shouldShowAutoOption = isSolanaSwap;
6160

6261
const [localSlippage, setLocalSlippage] = useState<number | undefined>(
6362
undefined,
@@ -71,7 +70,9 @@ export const BridgeTransactionSettingsModal = ({
7170
// Initialize state when modal opens
7271
useEffect(() => {
7372
if (isOpen) {
74-
const shouldSelectAuto = slippage === undefined && shouldShowAutoOption;
73+
// Note: We check for both null and undefined since JSON serialization converts undefined to null
74+
const shouldSelectAuto =
75+
(slippage === null || slippage === undefined) && shouldShowAutoOption;
7576
setIsAutoSelected(shouldSelectAuto);
7677
setLocalSlippage(shouldSelectAuto ? undefined : slippage);
7778
setCustomSlippage(
@@ -80,7 +81,7 @@ export const BridgeTransactionSettingsModal = ({
8081
: undefined,
8182
);
8283
}
83-
}, [slippage, shouldShowAutoOption, isOpen]);
84+
}, [slippage, shouldShowAutoOption, isOpen, isSolanaSwap]);
8485

8586
const getNotificationConfig = () => {
8687
if (!customSlippage) {
@@ -148,7 +149,7 @@ export const BridgeTransactionSettingsModal = ({
148149
: TextColor.textDefault
149150
}
150151
>
151-
AUTO
152+
{t('swapSlippageAutoDescription')}
152153
</Text>
153154
</Button>
154155
)}
@@ -270,7 +271,8 @@ export const BridgeTransactionSettingsModal = ({
270271
size={ButtonPrimarySize.Md}
271272
variant={TextVariant.bodyMd}
272273
disabled={
273-
(isAutoSelected && slippage === undefined) ||
274+
(isAutoSelected &&
275+
(slippage === null || slippage === undefined)) ||
274276
(!isAutoSelected &&
275277
((customSlippage !== undefined &&
276278
Number(customSlippage.replace(',', '.')) === slippage) ||
@@ -289,7 +291,12 @@ export const BridgeTransactionSettingsModal = ({
289291
value: newSlippage?.toString() ?? 'auto',
290292
},
291293
});
292-
dispatch(setSlippage(newSlippage));
294+
295+
if (isSolanaSwap) {
296+
dispatch(setSolanaSlippage(newSlippage));
297+
} else {
298+
dispatch(setSlippage(newSlippage));
299+
}
293300
onClose();
294301
}
295302
}}

ui/pages/bridge/prepare/prepare-bridge-page.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
getNativeAssetForChainId,
2020
isNativeAddress,
2121
UnifiedSwapBridgeEventName,
22+
BRIDGE_DEFAULT_SLIPPAGE,
2223
} from '@metamask/bridge-controller';
2324
import {
2425
setFromToken,
@@ -38,7 +39,7 @@ import {
3839
getFromChains,
3940
getFromToken,
4041
getQuoteRequest,
41-
getSlippage,
42+
getEffectiveSlippage,
4243
getToChain,
4344
getToChains,
4445
getToToken,
@@ -47,6 +48,7 @@ import {
4748
getValidationErrors,
4849
isBridgeSolanaEnabled,
4950
getIsToOrFromSolana,
51+
getIsSolanaSwap,
5052
getQuoteRefreshRate,
5153
getHardwareWalletName,
5254
getIsQuoteExpired,
@@ -167,7 +169,7 @@ const PrepareBridgePage = () => {
167169
const smartTransactionsEnabled = useSelector(getSmartTransactionsEnabled);
168170

169171
const providerConfig = useMultichainSelector(getMultichainProviderConfig);
170-
const slippage = useSelector(getSlippage);
172+
const slippage = useSelector(getEffectiveSlippage);
171173

172174
const quoteRequest = useSelector(getQuoteRequest);
173175
const {
@@ -350,6 +352,7 @@ const PrepareBridgePage = () => {
350352
]);
351353

352354
const isToOrFromSolana = useSelector(getIsToOrFromSolana);
355+
const isSolanaSwap = useSelector(getIsSolanaSwap);
353356

354357
const isDestinationSolana = useMemo(() => {
355358
if (!toChain?.chainId) {
@@ -520,7 +523,16 @@ const PrepareBridgePage = () => {
520523
timestamp: Date.now(),
521524
});
522525
if (isSwap) {
523-
dispatch(setSlippage(undefined));
526+
// Check if this is a Solana-to-Solana swap at execution time to avoid adding it as a dependency
527+
const currentIsSolanaSwap = isSolanaSwap;
528+
if (currentIsSolanaSwap) {
529+
// For Solana-to-Solana swaps, use undefined (AUTO)
530+
// getEffectiveSlippage will use solanaSlippage preference or default to undefined
531+
dispatch(setSlippage(undefined));
532+
} else {
533+
// For EVM swaps, use the bridge default slippage
534+
dispatch(setSlippage(BRIDGE_DEFAULT_SLIPPAGE));
535+
}
524536
if (fromChain && !toToken) {
525537
dispatch(setToChainId(fromChain.chainId));
526538
dispatch(setToToken(SOLANA_USDC_ASSET));

ui/store/actions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,10 @@ export function setAutoLockTimeLimit(value: number | null) {
35213521
return setPreference('autoLockTimeLimit', value);
35223522
}
35233523

3524+
export function setSolanaSlippage(value: number | undefined) {
3525+
return setPreference('solanaSlippage', value);
3526+
}
3527+
35243528
export function setCompletedOnboarding(): ThunkAction<
35253529
void,
35263530
MetaMaskReduxState,

0 commit comments

Comments
 (0)