diff --git a/src/lib/swapper/swappers/ThorchainSwapper/utils/addSlippageToMemo.test.ts b/src/lib/swapper/swappers/ThorchainSwapper/utils/addSlippageToMemo.test.ts deleted file mode 100644 index fd7a8870a0c..00000000000 --- a/src/lib/swapper/swappers/ThorchainSwapper/utils/addSlippageToMemo.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { describe, expect, it } from 'vitest' - -import { addSlippageToMemo } from './addSlippageToMemo' -import { MEMO_PART_DELIMITER } from './constants' - -const RECEIVE_ADDRESS = '0x32DBc9Cf9E8FbCebE1e0a2ecF05Ed86Ca3096Cb6' - -describe('addSlippageToMemo', () => { - it('should add slippage to memo correctly', () => { - const affiliateBps = '100' - const quotedMemo = `=:ETH.ETH:${RECEIVE_ADDRESS}::ss:${affiliateBps}` - const expectedL1AmountOut = '42' // we don't care about this for the purpose of tests - const expectedL1AmountOutMinusSlippage = '41' - - const slippageBps = 100 // 1% - const chainId = 'eip155:1' - const isStreaming = false - - const modifiedMemo = addSlippageToMemo({ - expectedAmountOutThorBaseUnit: expectedL1AmountOut, - quotedMemo, - slippageBps, - isStreaming, - chainId, - affiliateBps, - }) - - expect(modifiedMemo).toBe( - `=${MEMO_PART_DELIMITER}ETH.ETH${MEMO_PART_DELIMITER}${RECEIVE_ADDRESS}${MEMO_PART_DELIMITER}${expectedL1AmountOutMinusSlippage}${MEMO_PART_DELIMITER}ss${MEMO_PART_DELIMITER}${affiliateBps}`, - ) - }) -}) diff --git a/src/lib/swapper/swappers/ThorchainSwapper/utils/addSlippageToMemo.ts b/src/lib/swapper/swappers/ThorchainSwapper/utils/addSlippageToMemo.ts deleted file mode 100644 index bb919df803f..00000000000 --- a/src/lib/swapper/swappers/ThorchainSwapper/utils/addSlippageToMemo.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { ChainId } from '@shapeshiftoss/caip' -import { BigNumber, bn } from 'lib/bignumber/bignumber' -import { subtractBasisPointAmount } from 'state/slices/tradeQuoteSlice/utils' - -import { MEMO_PART_DELIMITER } from './constants' -import { assertIsValidMemo } from './makeSwapMemo/assertIsValidMemo' - -export const addSlippageToMemo = ({ - expectedAmountOutThorBaseUnit, - quotedMemo, - slippageBps, - isStreaming, - chainId, - affiliateBps, -}: { - expectedAmountOutThorBaseUnit: string - quotedMemo: string | undefined - slippageBps: BigNumber.Value - chainId: ChainId - affiliateBps: string - isStreaming: boolean -}) => { - if (!quotedMemo) throw new Error('no memo provided') - - // always use TC auto stream quote (0 limit = 5bps - 50bps, sometimes up to 100bps) - // see: https://discord.com/channels/838986635756044328/1166265575941619742/1166500062101250100 - if (isStreaming) return quotedMemo - - // the missing element is the original limit with (optional, missing) streaming parameters - const [prefix, pool, address, , affiliate, memoAffiliateBps] = - quotedMemo.split(MEMO_PART_DELIMITER) - - const limitWithManualSlippage = subtractBasisPointAmount( - bn(expectedAmountOutThorBaseUnit).toFixed(0, BigNumber.ROUND_DOWN), - slippageBps, - BigNumber.ROUND_DOWN, - ) - - const memo = [prefix, pool, address, limitWithManualSlippage, affiliate, memoAffiliateBps].join( - MEMO_PART_DELIMITER, - ) - - assertIsValidMemo(memo, chainId, affiliateBps) - return memo -} diff --git a/src/lib/swapper/swappers/ThorchainSwapper/utils/getL1quote.ts b/src/lib/swapper/swappers/ThorchainSwapper/utils/getL1quote.ts index cc2f531163e..07820d6b986 100644 --- a/src/lib/swapper/swappers/ThorchainSwapper/utils/getL1quote.ts +++ b/src/lib/swapper/swappers/ThorchainSwapper/utils/getL1quote.ts @@ -25,6 +25,7 @@ import { assertUnreachable, isFulfilled, isRejected } from 'lib/utils' import { assertGetCosmosSdkChainAdapter } from 'lib/utils/cosmosSdk' import { assertGetEvmChainAdapter } from 'lib/utils/evm' import { THOR_PRECISION } from 'lib/utils/thorchain/constants' +import { addLimitToMemo } from 'lib/utils/thorchain/memo/addLimitToMemo' import { assertGetUtxoChainAdapter } from 'lib/utils/utxo' import { convertDecimalPercentageToBasisPoints } from 'state/slices/tradeQuoteSlice/utils' @@ -39,8 +40,8 @@ import type { ThorTradeUtxoOrCosmosQuote, } from '../getThorTradeQuote/getTradeQuote' import type { ThornodeQuoteResponseSuccess } from '../types' -import { addSlippageToMemo } from './addSlippageToMemo' import { THORCHAIN_FIXED_PRECISION } from './constants' +import { getLimitWithManualSlippage } from './getLimitWithManualSlippage' import { getQuote } from './getQuote/getQuote' import { TradeType } from './longTailHelpers' import { getEvmTxFees } from './txFeeHelpers/evmTxFees/getEvmTxFees' @@ -217,22 +218,30 @@ export const getL1quote = async ( affiliateBps, slippageBps, }): Promise => { + if (!quote.memo) throw new Error('no memo provided') + const rate = getRouteRate(expectedAmountOutThorBaseUnit) const buyAmountBeforeFeesCryptoBaseUnit = getRouteBuyAmountBeforeFeesCryptoBaseUnit(quote) - const updatedMemo = addSlippageToMemo({ + const limitWithManualSlippage = getLimitWithManualSlippage({ expectedAmountOutThorBaseUnit, - quotedMemo: quote.memo, slippageBps, - chainId: sellAsset.chainId, - affiliateBps, - isStreaming, }) + + // always use TC auto stream quote (0 limit = 5bps - 50bps, sometimes up to 100bps) + // see: https://discord.com/channels/838986635756044328/1166265575941619742/1166500062101250100 + const memo = isStreaming + ? quote.memo + : addLimitToMemo({ + memo: quote.memo, + limit: limitWithManualSlippage, + }) + const { data, router, vault } = await getEvmThorTxInfo({ sellAsset, sellAmountCryptoBaseUnit, - memo: updatedMemo, + memo, expiry: quote.expiry, }) @@ -244,7 +253,7 @@ export const getL1quote = async ( return { id: uuid(), - memo: updatedMemo, + memo, receiveAddress, affiliateBps, potentialAffiliateBps, @@ -310,22 +319,30 @@ export const getL1quote = async ( affiliateBps, slippageBps, }): Promise => { + if (!quote.memo) throw new Error('no memo provided') + const rate = getRouteRate(expectedAmountOutThorBaseUnit) const buyAmountBeforeFeesCryptoBaseUnit = getRouteBuyAmountBeforeFeesCryptoBaseUnit(quote) - const updatedMemo = addSlippageToMemo({ + const limitWithManualSlippage = getLimitWithManualSlippage({ expectedAmountOutThorBaseUnit, - quotedMemo: quote.memo, slippageBps, - isStreaming, - chainId: sellAsset.chainId, - affiliateBps, }) + + // always use TC auto stream quote (0 limit = 5bps - 50bps, sometimes up to 100bps) + // see: https://discord.com/channels/838986635756044328/1166265575941619742/1166500062101250100 + const memo = isStreaming + ? quote.memo + : addLimitToMemo({ + memo: quote.memo, + limit: limitWithManualSlippage, + }) + const { vault, opReturnData, pubkey } = await getUtxoThorTxInfo({ sellAsset, xpub: (input as GetUtxoTradeQuoteInput).xpub, - memo: updatedMemo, + memo, }) const sellAdapter = assertGetUtxoChainAdapter(sellAsset.chainId) @@ -346,7 +363,7 @@ export const getL1quote = async ( return { id: uuid(), - memo: updatedMemo, + memo, receiveAddress, affiliateBps, potentialAffiliateBps, @@ -409,6 +426,8 @@ export const getL1quote = async ( affiliateBps, slippageBps, }): ThorTradeUtxoOrCosmosQuote => { + if (!quote.memo) throw new Error('no memo provided') + const rate = getRouteRate(expectedAmountOutThorBaseUnit) const buyAmountBeforeFeesCryptoBaseUnit = getRouteBuyAmountBeforeFeesCryptoBaseUnit(quote) @@ -419,18 +438,23 @@ export const getL1quote = async ( outputExponent: buyAsset.precision, }).toFixed() - const updatedMemo = addSlippageToMemo({ + const limitWithManualSlippage = getLimitWithManualSlippage({ expectedAmountOutThorBaseUnit, - quotedMemo: quote.memo, slippageBps, - isStreaming, - chainId: sellAsset.chainId, - affiliateBps, }) + // always use TC auto stream quote (0 limit = 5bps - 50bps, sometimes up to 100bps) + // see: https://discord.com/channels/838986635756044328/1166265575941619742/1166500062101250100 + const memo = isStreaming + ? quote.memo + : addLimitToMemo({ + memo: quote.memo, + limit: limitWithManualSlippage, + }) + return { id: uuid(), - memo: updatedMemo, + memo, receiveAddress, affiliateBps, potentialAffiliateBps, diff --git a/src/lib/swapper/swappers/ThorchainSwapper/utils/getLimitWithManualSlippage.test.ts b/src/lib/swapper/swappers/ThorchainSwapper/utils/getLimitWithManualSlippage.test.ts new file mode 100644 index 00000000000..4668b366d6f --- /dev/null +++ b/src/lib/swapper/swappers/ThorchainSwapper/utils/getLimitWithManualSlippage.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest' + +import { getLimitWithManualSlippage } from './getLimitWithManualSlippage' + +describe('getLimitWithManualSlippage', () => { + it('should remove slippage from expected amount out', () => { + const amountOut = '100' + const expectedLimitWithManualSlippage = '50' + + const slippageBps = 5000 // 50% + + const limitWithManualSlippage = getLimitWithManualSlippage({ + expectedAmountOutThorBaseUnit: amountOut, + slippageBps, + }) + + expect(limitWithManualSlippage).toBe(expectedLimitWithManualSlippage) + }) +}) diff --git a/src/lib/swapper/swappers/ThorchainSwapper/utils/getLimitWithManualSlippage.ts b/src/lib/swapper/swappers/ThorchainSwapper/utils/getLimitWithManualSlippage.ts new file mode 100644 index 00000000000..28d05ce0379 --- /dev/null +++ b/src/lib/swapper/swappers/ThorchainSwapper/utils/getLimitWithManualSlippage.ts @@ -0,0 +1,19 @@ +import { bn } from '@shapeshiftoss/chain-adapters' +import BigNumber from 'bignumber.js' +import { subtractBasisPointAmount } from 'state/slices/tradeQuoteSlice/utils' + +export const getLimitWithManualSlippage = ({ + expectedAmountOutThorBaseUnit, + slippageBps, +}: { + expectedAmountOutThorBaseUnit: string + slippageBps: BigNumber.Value +}) => { + const limitWithManualSlippage = subtractBasisPointAmount( + bn(expectedAmountOutThorBaseUnit).toFixed(0, BigNumber.ROUND_DOWN), + slippageBps, + BigNumber.ROUND_DOWN, + ) + + return limitWithManualSlippage +}