Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(smart-slippage): update smart slippage text #4982

Merged
merged 7 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,13 @@ export function HighSuggestedSlippageWarning(props: HighSuggestedSlippageWarning
<WarningContainer {...rest} level={LOW_TIER_FEE}>
<div>
<AlertTriangle size={24} />
Beware! High dynamic slippage suggested ({`${slippageBps / 100}`}%)
<HoverTooltip wrapInContainer content={"It's not thaaat bad. Just to make sure you noticed 😉"}>
Slippage adjusted to {`${slippageBps / 100}`}% to ensure quick execution
<HoverTooltip
wrapInContainer
content={
'CoW Swap dynamically adjusts your slippage tolerance based on current volatility. You can set a custom slippage using the gear icon above.'
}
>
<ErrorStyledInfoIcon />
</HoverTooltip>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ enum DeadlineError {
InvalidInput = 'InvalidInput',
}

const Option = styled(FancyButton) <{ active: boolean }>`
const Option = styled(FancyButton)<{ active: boolean }>`
margin-right: 8px;

:hover {
Expand Down Expand Up @@ -75,7 +75,7 @@ export const Input = styled.input`
text-align: right;
`

export const OptionCustom = styled(FancyButton) <{ active?: boolean; warning?: boolean }>`
export const OptionCustom = styled(FancyButton)<{ active?: boolean; warning?: boolean }>`
height: 2rem;
position: relative;
padding: 0 0.75rem;
Expand All @@ -84,7 +84,7 @@ export const OptionCustom = styled(FancyButton) <{ active?: boolean; warning?: b

:hover {
border: ${({ theme, active, warning }) =>
active && `1px solid ${warning ? darken(theme.error, 0.1) : darken(theme.bg2, 0.1)}`};
active && `1px solid ${warning ? darken(theme.error, 0.1) : darken(theme.bg2, 0.1)}`};
}

input {
Expand All @@ -97,6 +97,7 @@ export const OptionCustom = styled(FancyButton) <{ active?: boolean; warning?: b

const SlippageEmojiContainer = styled.span`
color: #f3841e;

${Media.upToSmall()} {
display: none;
}
Expand Down Expand Up @@ -204,78 +205,84 @@ export function TransactionSettings() {

const placeholderSlippage = isSlippageModified ? defaultSwapSlippage : swapSlippage

function parseSlippageInput(value: string) {
// populate what the user typed and clear the error
setSlippageInput(value)
setSlippageError(false)
const parseSlippageInput = useCallback(
(value: string) => {
// populate what the user typed and clear the error
setSlippageInput(value)
setSlippageError(false)

if (value.length === 0) {
slippageToleranceAnalytics('Default', placeholderSlippage.toFixed(2))
setSwapSlippage(isEoaEthFlow ? percentToBps(minEthFlowSlippage) : null)
} else {
let v = value

// Prevent inserting more than 2 decimal precision
if (value.split('.')[1]?.length > 2) {
// indexOf + 3 because we are cutting it off at `.XX`
v = value.slice(0, value.indexOf('.') + 3)
// Update the input to remove the extra numbers from UI input
setSlippageInput(v)
}
if (value.length === 0) {
slippageToleranceAnalytics('Default', placeholderSlippage.toFixed(2))
setSwapSlippage(isEoaEthFlow ? percentToBps(minEthFlowSlippage) : null)
} else {
let v = value

// Prevent inserting more than 2 decimal precision
if (value.split('.')[1]?.length > 2) {
// indexOf + 3 because we are cutting it off at `.XX`
v = value.slice(0, value.indexOf('.') + 3)
// Update the input to remove the extra numbers from UI input
setSlippageInput(v)
}

const parsed = Math.round(Number.parseFloat(v) * 100)
const parsed = Math.round(Number.parseFloat(v) * 100)

if (
!Number.isInteger(parsed) ||
parsed < (isEoaEthFlow ? minEthFlowSlippageBps : MIN_SLIPPAGE_BPS) ||
parsed > MAX_SLIPPAGE_BPS
) {
if (v !== '.') {
setSlippageError(SlippageError.InvalidInput)
if (
!Number.isInteger(parsed) ||
parsed < (isEoaEthFlow ? minEthFlowSlippageBps : MIN_SLIPPAGE_BPS) ||
parsed > MAX_SLIPPAGE_BPS
) {
if (v !== '.') {
setSlippageError(SlippageError.InvalidInput)
}
}
}

slippageToleranceAnalytics('Custom', parsed)
setSwapSlippage(percentToBps(new Percent(parsed, 10_000)))
}
}
slippageToleranceAnalytics('Custom', parsed)
setSwapSlippage(percentToBps(new Percent(parsed, 10_000)))
}
},
[placeholderSlippage, isEoaEthFlow, minEthFlowSlippage],
)

const tooLow = swapSlippage.lessThan(new Percent(isEoaEthFlow ? minEthFlowSlippageBps : LOW_SLIPPAGE_BPS, 10_000))
const tooHigh = swapSlippage.greaterThan(
new Percent(isEoaEthFlow ? HIGH_ETH_FLOW_SLIPPAGE_BPS : HIGH_SLIPPAGE_BPS, 10_000)
new Percent(isEoaEthFlow ? HIGH_ETH_FLOW_SLIPPAGE_BPS : smartSlippage || HIGH_SLIPPAGE_BPS, 10_000),
)

function parseCustomDeadline(value: string) {
// populate what the user typed and clear the error
setDeadlineInput(value)
setDeadlineError(false)

if (value.length === 0) {
orderExpirationTimeAnalytics('Default', DEFAULT_DEADLINE_FROM_NOW)
setDeadline(DEFAULT_DEADLINE_FROM_NOW)
} else {
try {
const parsed: number = Math.floor(Number.parseFloat(value) * 60)
if (
!Number.isInteger(parsed) || // Check deadline is a number
parsed <
(isEoaEthFlow
? // 10 minute low threshold for eth flow
MINIMUM_ETH_FLOW_DEADLINE_SECONDS
: MINIMUM_ORDER_VALID_TO_TIME_SECONDS) || // Check deadline is not too small
parsed > MAX_DEADLINE_MINUTES * 60 // Check deadline is not too big
) {
const parseCustomDeadline = useCallback(
(value: string) => {
// populate what the user typed and clear the error
setDeadlineInput(value)
setDeadlineError(false)

if (value.length === 0) {
orderExpirationTimeAnalytics('Default', DEFAULT_DEADLINE_FROM_NOW)
setDeadline(DEFAULT_DEADLINE_FROM_NOW)
} else {
try {
const parsed: number = Math.floor(Number.parseFloat(value) * 60)
if (
!Number.isInteger(parsed) || // Check deadline is a number
parsed <
(isEoaEthFlow
? // 10 minute low threshold for eth flow
MINIMUM_ETH_FLOW_DEADLINE_SECONDS
: MINIMUM_ORDER_VALID_TO_TIME_SECONDS) || // Check deadline is not too small
parsed > MAX_DEADLINE_MINUTES * 60 // Check deadline is not too big
) {
setDeadlineError(DeadlineError.InvalidInput)
} else {
orderExpirationTimeAnalytics('Custom', parsed)
setDeadline(parsed)
}
} catch (error: any) {
console.error(error)
setDeadlineError(DeadlineError.InvalidInput)
} else {
orderExpirationTimeAnalytics('Custom', parsed)
setDeadline(parsed)
}
} catch (error: any) {
console.error(error)
setDeadlineError(DeadlineError.InvalidInput)
}
}
}
},
[isEoaEthFlow],
)

const showCustomDeadlineRow = Boolean(chainId)

Expand All @@ -299,14 +306,14 @@ export function TransactionSettings() {
<AutoColumn gap="sm">
<RowFixed>
<ThemedText.Black fontWeight={400} fontSize={14}>
<Trans>MEV protected slippage</Trans>
<Trans>MEV-protected slippage</Trans>
</ThemedText.Black>
<HelpTooltip
text={
// <Trans>Your transaction will revert if the price changes unfavorably by more than this percentage.</Trans>
isEoaEthFlow
? getNativeSlippageTooltip(chainId, [nativeCurrency.symbol, getWrappedToken(nativeCurrency).symbol])
: getNonNativeSlippageTooltip()
: getNonNativeSlippageTooltip(true, true)
}
/>
</RowFixed>
Expand Down Expand Up @@ -366,8 +373,8 @@ export function TransactionSettings() {
<HelpTooltip
text={
<Trans>
Based on recent volatility observed for this token pair, it's recommended to leave the default
to account for price changes.
CoW Swap has dynamically selected this slippage amount to account for current gas prices and
volatility. Changes may result in slower execution.
</Trans>
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export function ConfirmSwapModalSetup(props: ConfirmSwapModalSetupProps) {
: undefined,
slippageTooltip: isEoaEthFlow
? getNativeSlippageTooltip(chainId, [nativeCurrency.symbol])
: getNonNativeSlippageTooltip(),
: getNonNativeSlippageTooltip(isSmartSlippageApplied),
expectReceiveLabel: isExactIn ? 'Expected to receive' : 'Expected to sell',
minReceivedLabel: isExactIn ? 'Minimum receive' : 'Maximum sent',
minReceivedTooltip: getMinimumReceivedTooltip(allowedSlippage, isExactIn),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,40 @@ export const getNativeSlippageTooltip = (chainId: SupportedChainId, symbols: (st
matching, even in volatile market conditions.
<br />
<br />
Orders on CoW Swap are always protected from MEV, so your slippage tolerance cannot be exploited.
</Trans>
)
export const getNonNativeSlippageTooltip = () => (
<Trans>
Your slippage is MEV protected: all orders are submitted with tight spread (0.1%) on-chain.
<br />
<br />
The slippage set enables a resubmission of your order in case of unfavourable price movements.
<br />
<br />
{INPUT_OUTPUT_EXPLANATION}
{symbols?.[0] || 'Native currency'} orders can, in rare cases, be frontrun due to their on-chain component. For more
robust MEV protection, consider wrapping your {symbols?.[0] || 'native currency'} before trading.
</Trans>
)
export const getNonNativeSlippageTooltip = (isDynamic?: boolean, isSettingsModal?: boolean) =>
isDynamic ? (
<Trans>
CoW Swap dynamically adjusts your slippage tolerance to ensure your trade executes quickly while still getting the
best price.{' '}
{isSettingsModal ? (
<>
To override this, enter your desired slippage amount.
<br />
<br />
Either way, your slippage is protected from MEV!
</>
) : (
"Trades are protected from MEV, so your slippage can't be exploited!"
)}
</Trans>
) : (
<Trans>
Your slippage is MEV protected: all orders are submitted with tight spread (0.1%) on-chain.
<br />
<br />
The slippage set enables a resubmission of your order in case of unfavourable price movements.
<br />
<br />
{INPUT_OUTPUT_EXPLANATION}
</Trans>
)

const SUGGESTED_SLIPPAGE_TOOLTIP =
'Based on recent volatility for the selected token pair, this is the suggested slippage for ensuring quick execution of your order.'
'This is the recommended slippage tolerance based on current gas prices & volatility. A lower amount may result in slower execution.'

export interface RowSlippageContentProps {
chainId: SupportedChainId
Expand Down Expand Up @@ -107,7 +124,8 @@ export function RowSlippageContent(props: RowSlippageContentProps) {
} = props

const tooltipContent =
slippageTooltip || (isEoaEthFlow ? getNativeSlippageTooltip(chainId, symbols) : getNonNativeSlippageTooltip())
slippageTooltip ||
(isEoaEthFlow ? getNativeSlippageTooltip(chainId, symbols) : getNonNativeSlippageTooltip(isSmartSlippageApplied))

// In case the user happened to set the same slippage as the suggestion, do not show the suggestion
const suggestedEqualToUserSlippage = smartSlippage && smartSlippage === displaySlippage
Expand All @@ -121,7 +139,7 @@ export function RowSlippageContent(props: RowSlippageContentProps) {
<CenteredDots />
) : (
<>
<LinkStyledButton onClick={setAutoSlippage}>(Suggested: {smartSlippage})</LinkStyledButton>
<LinkStyledButton onClick={setAutoSlippage}>(Recommended: {smartSlippage})</LinkStyledButton>
<HoverTooltip wrapInContainer content={SUGGESTED_SLIPPAGE_TOOLTIP}>
<StyledInfoIcon size={16} />
</HoverTooltip>
Expand Down
Loading