From f1251d28ce625741dd8486252bd2a3155fb22ec5 Mon Sep 17 00:00:00 2001 From: Juliano Lazzarotto <30806844+stackchain@users.noreply.github.com> Date: Sun, 22 Oct 2023 15:49:37 +0100 Subject: [PATCH 1/2] chore: covered 100% tests, minor improv --- packages/swap/src/helpers/mocks.ts | 68 ++ .../factories/makeOrderCalculations.test.ts | 108 +++ .../orders/factories/makeOrderCalculations.ts | 3 +- .../src/helpers/pools/getBestBuyPool.test.ts | 29 +- .../pools/getBestPoolCalculation.test.ts | 107 +++ .../src/helpers/pools/getBestSellPool.test.ts | 29 +- .../prices/getPairPriceInPtTerms.test.ts | 38 + .../helpers/prices/getPairPriceInPtTerms.ts | 10 +- .../helpers/prices/getPriceAfterFee.test.ts | 25 + .../reactjs/provider/SwapProvider.test.tsx | 330 +++++++- .../translators/reactjs/state/state.test.ts | 761 +++++++++++++----- .../src/translators/reactjs/state/state.ts | 21 +- 12 files changed, 1302 insertions(+), 227 deletions(-) diff --git a/packages/swap/src/helpers/mocks.ts b/packages/swap/src/helpers/mocks.ts index 4af021cf41..666c6a9c0a 100644 --- a/packages/swap/src/helpers/mocks.ts +++ b/packages/swap/src/helpers/mocks.ts @@ -499,6 +499,72 @@ const mockedPools5: Swap.Pool[] = [ }, ] +const mockedPools6: Swap.Pool[] = [ + { + tokenA: {quantity: '100', tokenId: 'tokenA'}, + tokenB: {quantity: '200', tokenId: 'tokenB'}, + ptPriceTokenA: '1', + ptPriceTokenB: '0.5', + fee: '0', + provider: 'vyfi', + batcherFee: {quantity: '0', tokenId: ''}, + deposit: {quantity: '0', tokenId: ''}, + poolId: '0', + lpToken: { + quantity: '0', + tokenId: 'no.token', + }, + }, + { + tokenB: {quantity: '100', tokenId: 'tokenB'}, + tokenA: {quantity: '200', tokenId: 'tokenA'}, + ptPriceTokenB: '0.5', + ptPriceTokenA: '1', + fee: '0', + provider: 'minswap', + batcherFee: {quantity: '0', tokenId: ''}, + deposit: {quantity: '0', tokenId: ''}, + poolId: '1', + lpToken: { + quantity: '0', + tokenId: '0', + }, + }, +] + +const mockedPools7: Swap.Pool[] = [ + { + tokenA: {quantity: '100', tokenId: 'tokenA'}, + tokenB: {quantity: '200', tokenId: 'tokenB'}, + ptPriceTokenA: '0', + ptPriceTokenB: '1', + fee: '0', + provider: 'vyfi', + batcherFee: {quantity: '0', tokenId: ''}, + deposit: {quantity: '0', tokenId: ''}, + poolId: '0', + lpToken: { + quantity: '0', + tokenId: 'no.token', + }, + }, + { + tokenB: {quantity: '100', tokenId: 'tokenB'}, + tokenA: {quantity: '200', tokenId: 'tokenA'}, + ptPriceTokenB: '0', + ptPriceTokenA: '1', + fee: '0', + provider: 'minswap', + batcherFee: {quantity: '0', tokenId: ''}, + deposit: {quantity: '0', tokenId: ''}, + poolId: '1', + lpToken: { + quantity: '0', + tokenId: '0', + }, + }, +] + const mockedOrderCalculations1: SwapOrderCalculation[] = [ { order: { @@ -1723,6 +1789,8 @@ export const mocks = { mockedPools3, mockedPools4, mockedPools5, + mockedPools6, + mockedPools7, mockedOrderCalculations1, mockedOrderCalculations2, diff --git a/packages/swap/src/helpers/orders/factories/makeOrderCalculations.test.ts b/packages/swap/src/helpers/orders/factories/makeOrderCalculations.test.ts index 799701268b..7ac798c522 100644 --- a/packages/swap/src/helpers/orders/factories/makeOrderCalculations.test.ts +++ b/packages/swap/src/helpers/orders/factories/makeOrderCalculations.test.ts @@ -2903,4 +2903,112 @@ describe('makeOrderCalculations', () => { pool: pools[0], } as SwapOrderCalculation) }) + it('should calculate all fees and amounts correctly (case 27, zero pt sell side)', () => { + const pool: Swap.Pool = mocks.mockedPools7[1]! + const pools = [pool] + const amounts = { + sell: { + quantity: '1', + tokenId: 'tokenB', + } as Balance.Amount, + buy: { + quantity: '0', + tokenId: 'tokenA', + } as Balance.Amount, + } + const slippage = 0 + const calculations = makeOrderCalculations({ + orderType: 'market', + amounts: amounts, + limitPrice: undefined, + slippage: slippage, + pools: pools, + primaryTokenId: '', + lpTokenHeld: { + quantity: '50', + tokenId: 'tokenX', + }, + side: 'buy', + frontendFeeTiers, + }) + const expectedCalculation: SwapOrderCalculation = { + order: { + side: 'buy', + slippage: 0, + orderType: 'market', + limitPrice: undefined, + amounts: { + sell: { + quantity: '1', + tokenId: 'tokenB', + }, + buy: { + quantity: '0', + tokenId: 'tokenA', + }, + }, + lpTokenHeld: { + quantity: '50', + tokenId: 'tokenX', + }, + }, + sides: { + sell: { + quantity: '0', + tokenId: 'tokenB', + }, + buy: { + quantity: '0', + tokenId: 'tokenA', + }, + }, + cost: { + batcherFee: { + quantity: '0', + tokenId: '', + }, + deposit: { + quantity: '0', + tokenId: '', + }, + frontendFeeInfo: { + discountTier: undefined, + fee: { + quantity: '0', + tokenId: '', + }, + }, + liquidityFee: { + quantity: '0', + tokenId: 'tokenB', + }, + ptTotalFeeNoFEF: { + tokenId: '', + quantity: '0', + }, + ptTotalFee: { + tokenId: '', + quantity: '0', + }, + }, + buyAmountWithSlippage: { + quantity: '0', + tokenId: 'tokenA', + }, + hasSupply: true, + prices: { + base: '0.5', + market: '0.5', + withSlippage: '0', + withFees: '0', + withFeesAndSlippage: '0', + difference: '-100', + withFeesNoFEF: '0', + withFeesAndSlippageNoFEF: '0', + differenceNoFEF: '-100', + }, + pool: pool, + } + expect(calculations[0]).toStrictEqual(expectedCalculation) + }) }) diff --git a/packages/swap/src/helpers/orders/factories/makeOrderCalculations.ts b/packages/swap/src/helpers/orders/factories/makeOrderCalculations.ts index 1af03d511c..d06bf6af23 100644 --- a/packages/swap/src/helpers/orders/factories/makeOrderCalculations.ts +++ b/packages/swap/src/helpers/orders/factories/makeOrderCalculations.ts @@ -64,8 +64,7 @@ export const makeOrderCalculations = ({ (!Quantities.isZero(buy.quantity) || !Quantities.isZero(sell.quantity)) && Quantities.isZero(poolSupply) const hasSupply = - !Quantities.isGreaterThan(buy.quantity, poolSupply ?? Quantities.zero) && - !supplyRequired + !Quantities.isGreaterThan(buy.quantity, poolSupply) && !supplyRequired // lf is sell side % of quantity ie. XToken 100 * 1% = 1 XToken const liquidityFee: Balance.Amount = getLiquidityProviderFee(pool.fee, sell) diff --git a/packages/swap/src/helpers/pools/getBestBuyPool.test.ts b/packages/swap/src/helpers/pools/getBestBuyPool.test.ts index 05a2aecce2..848a278e82 100644 --- a/packages/swap/src/helpers/pools/getBestBuyPool.test.ts +++ b/packages/swap/src/helpers/pools/getBestBuyPool.test.ts @@ -1,4 +1,4 @@ -import {Balance} from '@yoroi/types' +import {Balance, Swap} from '@yoroi/types' import {getBestBuyPool} from './getBestBuyPool' import {getBuyAmount} from '../orders/amounts/getBuyAmount' @@ -48,6 +48,33 @@ describe('getBestBuyPool', () => { expect(getBestBuyPool([mocks.mockedPools1[0]!], sell)).toBeUndefined() }) + it('should return undefined if the buy quantity turns to be 0', () => { + const pools: Array = [ + { + tokenA: {quantity: '1000000000000000000000', tokenId: 'tokenA'}, + tokenB: {quantity: '1', tokenId: 'tokenB'}, + ptPriceTokenA: '1', + ptPriceTokenB: '0.0695404765', + fee: '0.3', // 0.3% + provider: 'muesliswap_v2', + batcherFee: {quantity: '950000', tokenId: ''}, + deposit: {quantity: '2000000', tokenId: ''}, + poolId: '1', + lpToken: { + quantity: '0', + tokenId: '0', + }, + }, + ] + + const sell: Balance.Amount = { + quantity: '1', + tokenId: 'tokenA', + } + + expect(getBestBuyPool(pools, sell)).toBeUndefined() + }) + it('should return undefined if pools list is empty', () => { const sell: Balance.Amount = { quantity: '1', diff --git a/packages/swap/src/helpers/pools/getBestPoolCalculation.test.ts b/packages/swap/src/helpers/pools/getBestPoolCalculation.test.ts index 52f87e8ab9..4f7ee4689f 100644 --- a/packages/swap/src/helpers/pools/getBestPoolCalculation.test.ts +++ b/packages/swap/src/helpers/pools/getBestPoolCalculation.test.ts @@ -1,4 +1,5 @@ import {mocks} from '../mocks' +import {SwapOrderCalculation} from '../../translators/reactjs/state/state' import {getBestPoolCalculation} from './getBestPoolCalculation' describe('getBestPoolCalculation', () => { @@ -9,4 +10,110 @@ describe('getBestPoolCalculation', () => { expect(bestCalculation?.pool.poolId).toBe('3') }) + + it('should skip no supply pools', () => { + const calculations: ReadonlyArray = [ + { + order: { + side: 'buy', + slippage: 10, + orderType: 'market', + amounts: { + sell: { + quantity: '0', + tokenId: 'tokenA', + }, + buy: { + quantity: '100000001', + tokenId: 'tokenB', + }, + }, + }, + sides: { + buy: { + quantity: '100000001', + tokenId: 'tokenB', + }, + sell: { + quantity: '7157210', + tokenId: 'tokenA', + }, + }, + cost: { + ptTotalFeeNoFEF: { + quantity: '4500000', + tokenId: '', + }, + ptTotalFee: { + quantity: '4500000', + tokenId: '', + }, + batcherFee: { + quantity: '2500000', + tokenId: '', + }, + deposit: { + quantity: '2000000', + tokenId: '', + }, + frontendFeeInfo: { + fee: { + tokenId: '', + quantity: '0', + }, + }, + liquidityFee: { + tokenId: 'tokenA', + quantity: '3579', + }, + }, + buyAmountWithSlippage: { + quantity: '90000000', + tokenId: 'tokenB', + }, + hasSupply: false, + prices: { + base: '0.07101454479564951518', + market: '0.07101454479564951518', + withSlippage: '0.07952455555555555556', + withFees: '0.09657209903427900966', + withFeesAndSlippage: '0.10730233333333333333', + difference: '35.989182655713084861', + withFeesNoFEF: '0.09657209903427900966', + withFeesAndSlippageNoFEF: '0.10730233333333333333', + differenceNoFEF: '35.989182655713084861', + }, + pool: { + tokenA: { + quantity: '973669994', + tokenId: 'tokenA', + }, + tokenB: { + quantity: '13710853133', + tokenId: 'tokenB', + }, + ptPriceTokenA: '1', + ptPriceTokenB: '0.0695404765', + fee: '0.05', + provider: 'sundaeswap', + batcherFee: { + quantity: '2500000', + tokenId: '', + }, + deposit: { + quantity: '2000000', + tokenId: '', + }, + poolId: '6', + lpToken: { + quantity: '0', + tokenId: '0', + }, + }, + }, + ] + const bestCalculation = getBestPoolCalculation(calculations) + + expect(bestCalculation).toBeUndefined() + }) }) diff --git a/packages/swap/src/helpers/pools/getBestSellPool.test.ts b/packages/swap/src/helpers/pools/getBestSellPool.test.ts index 0c6a599ca2..2256bd2df5 100644 --- a/packages/swap/src/helpers/pools/getBestSellPool.test.ts +++ b/packages/swap/src/helpers/pools/getBestSellPool.test.ts @@ -1,4 +1,4 @@ -import {Balance} from '@yoroi/types' +import {Balance, Swap} from '@yoroi/types' import {getBestSellPool} from './getBestSellPool' import {getSellAmount} from '../orders/amounts/getSellAmount' @@ -50,6 +50,33 @@ describe('getBestSellPool', () => { expect(getBestSellPool([mocks.mockedPools4[0]!], sell)).toBeUndefined() }) + it('should return undefined if the sell quantity turns to be 0', () => { + const pools: Array = [ + { + tokenA: {quantity: '0', tokenId: 'tokenA'}, + tokenB: {quantity: '1', tokenId: 'tokenB'}, + ptPriceTokenA: '1', + ptPriceTokenB: '0.0695404765', + fee: '0.3', // 0.3% + provider: 'muesliswap_v2', + batcherFee: {quantity: '950000', tokenId: ''}, + deposit: {quantity: '2000000', tokenId: ''}, + poolId: '1', + lpToken: { + quantity: '0', + tokenId: '0', + }, + }, + ] + + const buy: Balance.Amount = { + quantity: '1000000000000000', + tokenId: 'tokenA', + } + + expect(getBestSellPool(pools, buy)).toBeUndefined() + }) + it('should return undefined if pools list is empty', () => { const sell: Balance.Amount = { quantity: '1', diff --git a/packages/swap/src/helpers/prices/getPairPriceInPtTerms.test.ts b/packages/swap/src/helpers/prices/getPairPriceInPtTerms.test.ts index 13ee88b0af..09747092d3 100644 --- a/packages/swap/src/helpers/prices/getPairPriceInPtTerms.test.ts +++ b/packages/swap/src/helpers/prices/getPairPriceInPtTerms.test.ts @@ -55,4 +55,42 @@ describe('getPriceAfterFee', () => { ptPriceBA: '0.008566', }) }) + + it('should set decimals to 0 if undefined', () => { + // arrange + const pool: Swap.Pool = { + tokenA: {quantity: '1', tokenId: 'tokenA'}, + tokenB: {quantity: '2', tokenId: 'tokenB'}, + ptPriceTokenA: '0.03465765134', + ptPriceTokenB: '3.81247293317', + fee: '0.3', // 0.3% + provider: 'minswap', + batcherFee: {quantity: '950000', tokenId: ''}, + deposit: {quantity: '1', tokenId: ''}, + poolId: '0', + lpToken: { + quantity: '0', + tokenId: '0', + }, + } + + // act + const pricesAB = getPairPriceInPtTerms({ + amountA: pool.tokenA, + decimalsA: 6, + decimalsB: 0, + ptPriceTokenA: '0', + ptPriceTokenB: '8566.52826101672', + sell: { + quantity: '1', + tokenId: 'tokenA', + }, + }) + + // assert + expect(pricesAB).toStrictEqual({ + ptPriceAB: '0', + ptPriceBA: '0.000000', + }) + }) }) diff --git a/packages/swap/src/helpers/prices/getPairPriceInPtTerms.ts b/packages/swap/src/helpers/prices/getPairPriceInPtTerms.ts index c8969e69b4..90288e5c9a 100644 --- a/packages/swap/src/helpers/prices/getPairPriceInPtTerms.ts +++ b/packages/swap/src/helpers/prices/getPairPriceInPtTerms.ts @@ -1,11 +1,13 @@ import {Balance} from '@yoroi/types' import {BigNumber} from 'bignumber.js' + +import {Quantities} from '../../utils/quantities' + export const getPairPriceInPtTerms = ({ sell, amountA, - decimalsA = 0, - decimalsB = 0, - // prices not scaled - visual + decimalsA, + decimalsB, ptPriceTokenA, ptPriceTokenB, precision, @@ -24,7 +26,7 @@ export const getPairPriceInPtTerms = ({ scale: number, ) => { return divisor.isZero() - ? '0' + ? Quantities.zero : dividend .dividedBy(divisor) .toFixed(precision ?? scale, BigNumber.ROUND_DOWN) diff --git a/packages/swap/src/helpers/prices/getPriceAfterFee.test.ts b/packages/swap/src/helpers/prices/getPriceAfterFee.test.ts index 85c2ac6b52..56fe509bf1 100644 --- a/packages/swap/src/helpers/prices/getPriceAfterFee.test.ts +++ b/packages/swap/src/helpers/prices/getPriceAfterFee.test.ts @@ -98,4 +98,29 @@ describe('getPriceAfterFee', () => { const expected = new BigNumber(0) expect(result).toStrictEqual(expected) }) + + // NOTE: it means that if the price is missing the fee wont consider the batcher fee + it('should return not add the feeInSellTerm when pt sell side is 0', () => { + const pool = { + tokenA: {quantity: '143983812522', tokenId: 'tokenA'}, + tokenB: {quantity: '2050476716943', tokenId: 'tokenB'}, + ptPriceTokenA: '0', + ptPriceTokenB: '1', + fee: '0.3', // 0.3% + provider: 'minswap', + batcherFee: {quantity: '1900000', tokenId: ''}, + deposit: {quantity: '1', tokenId: ''}, + poolId: '0', + lpToken: { + quantity: '0', + tokenId: '0', + }, + } as Swap.Pool + const tokenAAmount = '10000000000' + const tokenBAmount = '1000' + const tokenId = 'tokenA' + const result = getPriceAfterFee(pool, tokenAAmount, tokenBAmount, tokenId) + const expected = new BigNumber(10000000) + expect(result).toStrictEqual(expected) + }) }) diff --git a/packages/swap/src/translators/reactjs/provider/SwapProvider.test.tsx b/packages/swap/src/translators/reactjs/provider/SwapProvider.test.tsx index f448f950f1..849abe4f7d 100644 --- a/packages/swap/src/translators/reactjs/provider/SwapProvider.test.tsx +++ b/packages/swap/src/translators/reactjs/provider/SwapProvider.test.tsx @@ -1,12 +1,14 @@ import * as React from 'react' import {QueryClient, QueryClientProvider} from 'react-query' import {renderHook, act} from '@testing-library/react-hooks' +import {AppApi} from '@yoroi/api' import {SwapProvider} from './SwapProvider' import {mockSwapManager, swapManagerMocks} from '../../../manager.mocks' import {SwapState, defaultSwapState} from '../state/state' import {queryClientFixture} from '../../../fixtures/query-client' import {useSwap} from '../hooks/useSwap' +import {mocks} from '../../../helpers/mocks' describe('SwapProvider', () => { let queryClient: QueryClient @@ -111,22 +113,43 @@ describe('SwapProvider', () => { expect(result.current.unsignedTx).toEqual({hash: 'hash'}) }) - it('LimitPriceChanged', () => { - const wrapper = ({children}: any) => ( - - {children} - - ) + describe('LimitPriceChanged', () => { + it('should not update limit price if order type is market', () => { + const wrapper = ({children}: any) => ( + + {children} + + ) - const {result} = renderHook(() => useSwap(), { - wrapper, - }) + const {result} = renderHook(() => useSwap(), { + wrapper, + }) - act(() => { - result.current.limitPriceChanged('3') + act(() => { + result.current.limitPriceChanged('3') + }) + + expect(result.current.orderData.limitPrice).toBeUndefined() }) - expect(result.current.orderData.limitPrice).toBe('3') + it('should update limit price if order type is market', () => { + const wrapper = ({children}: any) => ( + + {children} + + ) + + const {result} = renderHook(() => useSwap(), { + wrapper, + }) + + act(() => { + result.current.orderTypeChanged('limit') + result.current.limitPriceChanged('3') + }) + + expect(result.current.orderData.limitPrice).toBe('3') + }) }) it('SwitchTokens market', () => { @@ -319,4 +342,287 @@ describe('SwapProvider', () => { expect(result.current.orderData).toEqual(defaultSwapState.orderData) expect(result.current.unsignedTx).toBeUndefined() }) + + it('BuyQuantityChanged', () => { + const initialState: SwapState = { + orderData: { + ...defaultSwapState.orderData, + amounts: { + sell: { + quantity: '10', + tokenId: 'policyId.sell', + }, + buy: { + quantity: '20', + tokenId: 'policyId.buy', + }, + }, + }, + unsignedTx: undefined, + } + const wrapper = ({children}: any) => ( + + + {children} + + + ) + + const {result} = renderHook(() => useSwap(), { + wrapper, + }) + + act(() => { + result.current.buyQuantityChanged('30') + }) + + expect(result.current.orderData.amounts).toEqual({ + sell: { + quantity: '10', + tokenId: 'policyId.sell', + }, + buy: { + quantity: '30', + tokenId: 'policyId.buy', + }, + }) + }) + + it('SellQuantityChanged', () => { + const initialState: SwapState = { + orderData: { + ...defaultSwapState.orderData, + amounts: { + sell: { + quantity: '10', + tokenId: 'policyId.sell', + }, + buy: { + quantity: '20', + tokenId: 'policyId.buy', + }, + }, + }, + unsignedTx: undefined, + } + const wrapper = ({children}: any) => ( + + + {children} + + + ) + + const {result} = renderHook(() => useSwap(), { + wrapper, + }) + + act(() => { + result.current.sellQuantityChanged('30') + }) + + expect(result.current.orderData.amounts).toEqual({ + sell: { + quantity: '30', + tokenId: 'policyId.sell', + }, + buy: { + quantity: '20', + tokenId: 'policyId.buy', + }, + }) + }) + + it('LpTokenHeldChanged', () => { + const initialState: SwapState = { + orderData: { + ...defaultSwapState.orderData, + lpTokenHeld: undefined, + }, + unsignedTx: undefined, + } + const wrapper = ({children}: any) => ( + + + {children} + + + ) + + const {result} = renderHook(() => useSwap(), { + wrapper, + }) + + act(() => { + result.current.lpTokenHeldChanged({ + quantity: '30', + tokenId: 'policyId.tokenHeld', + }) + }) + + expect(result.current.orderData.lpTokenHeld).toEqual({ + quantity: '30', + tokenId: 'policyId.tokenHeld', + }) + }) + + it('BuyTokenIdChanged', () => { + const initialState: SwapState = { + orderData: { + ...defaultSwapState.orderData, + amounts: { + sell: { + quantity: '10', + tokenId: 'policyId.sell', + }, + buy: { + quantity: '20', + tokenId: 'policyId.buy', + }, + }, + }, + unsignedTx: undefined, + } + const wrapper = ({children}: any) => ( + + + {children} + + + ) + + const {result} = renderHook(() => useSwap(), { + wrapper, + }) + + act(() => { + result.current.buyTokenIdChanged('new.token') + }) + + expect(result.current.orderData.amounts.buy).toEqual({ + quantity: '20', + tokenId: 'new.token', + }) + }) + + it('SellTokenIdChanged', () => { + const initialState: SwapState = { + orderData: { + ...defaultSwapState.orderData, + amounts: { + sell: { + quantity: '10', + tokenId: 'policyId.sell', + }, + buy: { + quantity: '20', + tokenId: 'policyId.buy', + }, + }, + }, + unsignedTx: undefined, + } + const wrapper = ({children}: any) => ( + + + {children} + + + ) + + const {result} = renderHook(() => useSwap(), { + wrapper, + }) + + act(() => { + result.current.sellTokenIdChanged('new.token') + }) + + expect(result.current.orderData.amounts.sell).toEqual({ + quantity: '10', + tokenId: 'new.token', + }) + }) + + it('PoolPairsChanged', () => { + const initialState: SwapState = { + orderData: { + ...defaultSwapState.orderData, + }, + unsignedTx: undefined, + } + const wrapper = ({children}: any) => ( + + + {children} + + + ) + + const {result} = renderHook(() => useSwap(), { + wrapper, + }) + + act(() => { + result.current.poolPairsChanged(mocks.mockedPools1) + }) + + expect(result.current.orderData.pools).toEqual(mocks.mockedPools1) + }) + + it('PrimaryTokenIdChanged', () => { + const initialState: SwapState = { + orderData: { + ...defaultSwapState.orderData, + }, + unsignedTx: undefined, + } + const wrapper = ({children}: any) => ( + + + {children} + + + ) + + const {result} = renderHook(() => useSwap(), { + wrapper, + }) + + act(() => { + result.current.primaryTokenIdChanged('primary.tokenId') + }) + + expect(result.current.orderData.primartyTokenId).toEqual('primary.tokenId') + }) + + it('FrontendFeeTiersChanged', () => { + const initialState: SwapState = { + orderData: { + ...defaultSwapState.orderData, + }, + unsignedTx: undefined, + } + const wrapper = ({children}: any) => ( + + + {children} + + + ) + + const {result} = renderHook(() => useSwap(), { + wrapper, + }) + + act(() => { + result.current.frontendFeeTiersChanged( + AppApi.mockGetFrontendFees.withFees.muesliswap!, + ) + }) + + expect(result.current.orderData.frontendFeeTiers).toEqual( + AppApi.mockGetFrontendFees.withFees.muesliswap!, + ) + }) }) diff --git a/packages/swap/src/translators/reactjs/state/state.test.ts b/packages/swap/src/translators/reactjs/state/state.test.ts index 1155eea9e8..e8e5302c08 100644 --- a/packages/swap/src/translators/reactjs/state/state.test.ts +++ b/packages/swap/src/translators/reactjs/state/state.test.ts @@ -1,202 +1,575 @@ -// import {produce} from 'immer' - -// import { -// combinedSwapReducers, -// defaultSwapState, -// SwapCreateOrderActionType, -// SwapActionType, -// SwapCreateOrderAction, -// SwapAction, -// SwapState, -// } from './state' -// import {mockSwapStateDefault} from './state.mocks' -// import {mocks} from '../../../helpers/mocks' +import {produce} from 'immer' +import { + combinedSwapReducers, + defaultSwapState, + SwapCreateOrderActionType, + SwapActionType, + SwapCreateOrderAction, + SwapAction, + SwapState, +} from './state' +import {mockSwapStateDefault} from './state.mocks' +import {mocks} from '../../../helpers/mocks' + +// NOTE: the calculations are tested on the factories +// here is just testing the state behaviour describe('State Actions', () => { - // const mockedState: SwapState = { - // ...mockSwapStateDefault, - // orderData: { - // ...mockSwapStateDefault.orderData, - // amounts: { - // sell: { - // quantity: '100000000', - // tokenId: 'tokenA', - // }, - // buy: { - // quantity: '1401162647', - // tokenId: 'tokenB', - // }, - // }, - // limitPrice: undefined, - // slippage: 10, - // type: 'market', - // selectedPoolId: undefined, - // selectedPoolCalculation: undefined, - - // pools: mocks.mockedPools1, - // calculations: mocks.mockedOrderCalculations1, - // bestPoolCalculation: mocks.mockedOrderCalculations1[0], - // }, - // } - // it('unknown', () => { - // const action = {type: 'UNKNOWN'} as any - // const state = combinedSwapReducers(mockSwapStateDefault, action) - // expect(state).toEqual(mockSwapStateDefault) - // }) - - // it('OrderTypeChanged', () => { - // const action: SwapCreateOrderAction = { - // type: SwapCreateOrderActionType.OrderTypeChanged, - // orderType: 'limit', - // } - // const expectedState = produce(mockedState, (draft) => { - // draft.orderData.type = 'limit' - // }) - // const state = combinedSwapReducers(mockedState, action) - // expect(state).toEqual(expectedState) - // }) - - // it('OrderTypeChanged limit', () => { - // const action: SwapCreateOrderAction = { - // type: SwapCreateOrderActionType.OrderTypeChanged, - // orderType: 'market', - // } - - // const limitedState = produce(mockedState, (draft) => { - // draft.orderData.type = 'limit' - // }) - - // const expectedState = produce(limitedState, (draft) => { - // draft.orderData.type = 'market' - // }) - // const state = combinedSwapReducers(limitedState, action) - // expect(state).toEqual(expectedState) - // }) - - // it('UnsignedTxChanged', () => { - // const action: SwapAction = { - // type: SwapActionType.UnsignedTxChanged, - // unsignedTx: {txHash: 'someHash'}, - // } - // const expectedState = produce(mockSwapStateDefault, (draft) => { - // draft.unsignedTx = {txHash: 'someHash'} - // }) - // const state = combinedSwapReducers(mockSwapStateDefault, action) - // expect(state).toEqual(expectedState) - // }) - - // it('ResetState', () => { - // const action: SwapAction = {type: SwapActionType.ResetState} - // const state = combinedSwapReducers(mockSwapStateDefault, action) - // expect(state).toEqual(defaultSwapState) - // }) - - // it('SelectedPoolChanged market should not update the state', () => { - // const action: SwapCreateOrderAction = { - // type: SwapCreateOrderActionType.SelectedPoolChanged, - // poolId: '6', - // } - // const state = combinedSwapReducers(mockedState, action) - // expect(state).toEqual(mockedState) - // }) - - // it('SelectedPoolChanged limit (should updated the selected pool)', () => { - // const action: SwapCreateOrderAction = { - // type: SwapCreateOrderActionType.SelectedPoolChanged, - // poolId: '', - // } - - // const limitedState = produce(mockSwapStateDefault, (draft) => { - // draft.orderData.type = 'limit' - // }) - - // const expectedState = produce(limitedState, (draft) => { - // draft.orderData.selectedPoolId = action.poolId - // }) - // const state = combinedSwapReducers(limitedState, action) - // expect(state).toEqual(expectedState) - // }) - - // it('SlippageChanged', () => { - // const action: SwapCreateOrderAction = { - // type: SwapCreateOrderActionType.SlippageChanged, - // slippage: 2, - // } - // const expectedState = produce(mockSwapStateDefault, (draft) => { - // draft.orderData.slippage = action.slippage - // }) - // const state = combinedSwapReducers(mockSwapStateDefault, action) - // expect(state).toEqual(expectedState) - // }) - - // it('SwitchTokens', () => { - // const action: SwapCreateOrderAction = { - // type: SwapCreateOrderActionType.SwitchTokens, - // } - // const expectedState = produce(mockSwapStateDefault, (draft) => { - // draft.orderData.amounts = { - // sell: mockSwapStateDefault.orderData.amounts.buy, - // buy: mockSwapStateDefault.orderData.amounts.sell, - // } - // }) - // const state = combinedSwapReducers(mockSwapStateDefault, action) - // expect(state).toEqual(expectedState) - // }) - - // it('ResetQuantities', () => { - // const action: SwapCreateOrderAction = { - // type: SwapCreateOrderActionType.ResetQuantities, - // } - // const expectedState = produce(mockSwapStateDefault, (draft) => { - // draft.orderData.amounts = { - // sell: { - // quantity: '0', - // tokenId: mockSwapStateDefault.orderData.amounts.sell.tokenId, - // }, - // buy: { - // quantity: '0', - // tokenId: mockSwapStateDefault.orderData.amounts.buy.tokenId, - // }, - // } - // draft.orderData.limitPrice = undefined - // }) - // const state = combinedSwapReducers(mockSwapStateDefault, action) - // expect(state).toEqual(expectedState) - // }) - - // it('LimitPriceChanged', () => { - // const action: SwapCreateOrderAction = { - // type: SwapCreateOrderActionType.LimitPriceChanged, - // limitPrice: '2', - // } - // const expectedState = produce(mockSwapStateDefault, (draft) => { - // draft.orderData.limitPrice = action.limitPrice - // }) - // const state = combinedSwapReducers(mockSwapStateDefault, action) - // expect(state).toEqual(expectedState) - // }) - - // // - // // TODO: implement - it('SellTokenIdChanged', () => { - expect('todo').toBeDefined() - }) - it('BuyTokenIdChanged', () => { - expect('todo').toBeDefined() - }) - it('SellQuantityChanged', () => { - expect('todo').toBeDefined() - }) - it('BuyQuantityChanged', () => { - expect('todo').toBeDefined() - }) - it('PoolPairsChanged', () => { - expect('todo').toBeDefined() + const mockedState: SwapState = { + ...mockSwapStateDefault, + orderData: { + ...mockSwapStateDefault.orderData, + amounts: { + sell: { + quantity: '100000000', + tokenId: 'tokenA', + }, + buy: { + quantity: '1401162647', + tokenId: 'tokenB', + }, + }, + limitPrice: undefined, + slippage: 10, + type: 'market', + selectedPoolId: undefined, + selectedPoolCalculation: undefined, + + pools: mocks.mockedPools1, + calculations: mocks.mockedOrderCalculations1, + bestPoolCalculation: mocks.mockedOrderCalculations1[0], + }, + } + it('unknown', () => { + const action = {type: 'UNKNOWN'} as any + const state = combinedSwapReducers(mockSwapStateDefault, action) + expect(state).toEqual(mockSwapStateDefault) + }) + + describe('OrderTypeChanged', () => { + it('change to limit', () => { + const stateTypeChanged: SwapState = { + ...mockSwapStateDefault, + orderData: { + ...mockSwapStateDefault.orderData, + amounts: { + sell: { + quantity: '10000', + tokenId: 'tokenA', + }, + buy: { + quantity: '0', + tokenId: 'tokenB', + }, + }, + limitPrice: undefined, + slippage: 0, + type: 'market', + selectedPoolId: undefined, + selectedPoolCalculation: undefined, + + pools: [], + calculations: [], + bestPoolCalculation: undefined, + }, + } + + const action: SwapCreateOrderAction = { + type: SwapCreateOrderActionType.OrderTypeChanged, + orderType: 'limit', + } + const expectedState = produce(stateTypeChanged, (draft) => { + draft.orderData.type = 'limit' + }) + const state = combinedSwapReducers(expectedState, action) + expect(state).toEqual(expectedState) + }) + + it('change to limit and back to market', () => { + const stateTypeChanged: SwapState = { + ...mockSwapStateDefault, + orderData: { + ...mockSwapStateDefault.orderData, + amounts: { + sell: { + quantity: '10000', + tokenId: 'tokenA', + }, + buy: { + quantity: '0', + tokenId: 'tokenB', + }, + }, + limitPrice: undefined, + slippage: 0, + type: 'market', + selectedPoolId: undefined, + selectedPoolCalculation: undefined, + + pools: [], + calculations: [], + bestPoolCalculation: undefined, + }, + } + const stateAfterPools = combinedSwapReducers(stateTypeChanged, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools6, + }) + + const actionLimit: SwapCreateOrderAction = { + type: SwapCreateOrderActionType.OrderTypeChanged, + orderType: 'limit', + } + + const state = combinedSwapReducers(stateAfterPools, actionLimit) + + expect(state.orderData.type).toBe('limit') + expect(state.orderData.limitPrice).toBe('0.5') + expect(state.orderData.amounts.buy.quantity).toBe('198') + + const actionMarket: SwapCreateOrderAction = { + type: SwapCreateOrderActionType.OrderTypeChanged, + orderType: 'market', + } + + const stateBack = combinedSwapReducers(state, actionMarket) + expect(stateBack.orderData.type).toBe('market') + }) + }) + + it('UnsignedTxChanged', () => { + const action: SwapAction = { + type: SwapActionType.UnsignedTxChanged, + unsignedTx: {txHash: 'someHash'}, + } + const expectedState = produce(mockSwapStateDefault, (draft) => { + draft.unsignedTx = {txHash: 'someHash'} + }) + const state = combinedSwapReducers(mockSwapStateDefault, action) + expect(state).toEqual(expectedState) + }) + + it('ResetState', () => { + const action: SwapAction = {type: SwapActionType.ResetState} + const state = combinedSwapReducers(mockSwapStateDefault, action) + expect(state).toEqual(defaultSwapState) + }) + + describe('SelectedPoolChanged', () => { + describe('market', () => { + it('should ignore', () => { + const action: SwapCreateOrderAction = { + type: SwapCreateOrderActionType.SelectedPoolChanged, + poolId: '6', + } + const state = combinedSwapReducers(mockedState, action) + expect(state).toEqual(mockedState) + }) + }) + + describe('limit', () => { + it('should update pool if avaible and reset limit to market price', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + draft.orderData.type = 'limit' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools6, + }) + const updatedLimit = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.LimitPriceChanged, + limitPrice: '2', + }) + + const state = combinedSwapReducers(updatedLimit, { + type: SwapCreateOrderActionType.SelectedPoolChanged, + poolId: '1', + }) + + // change back to market price + expect(state.orderData.limitPrice).toBe('2') + expect(state.orderData.selectedPoolId).toBe('1') + }) + }) + }) + + it('SelectedPoolChanged limit (should updated the selected pool)', () => { + const action: SwapCreateOrderAction = { + type: SwapCreateOrderActionType.SelectedPoolChanged, + poolId: '', + } + + const limitedState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.type = 'limit' + }) + + const expectedState = produce(limitedState, (draft) => { + draft.orderData.selectedPoolId = action.poolId + }) + const state = combinedSwapReducers(limitedState, action) + expect(state).toEqual(expectedState) + }) + + it('SlippageChanged', () => { + const action: SwapCreateOrderAction = { + type: SwapCreateOrderActionType.SlippageChanged, + slippage: 2, + } + const expectedState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.slippage = action.slippage + }) + const state = combinedSwapReducers(mockSwapStateDefault, action) + expect(state).toEqual(expectedState) + }) + + // NOTE: switch tokens recalculate based on sell side + // this cause every switch to update the quantities + // when switchin tokens on a limit order the quantities may become too big + // because the limit price is kept + describe('SwitchTokens', () => { + it('should change buy / sell and derive the calculation accordingly', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools6, + }) + + const state = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.SwitchTokens, + }) + + expect(state.orderData.amounts.buy.tokenId).toBe('tokenA') + expect(state.orderData.amounts.sell.tokenId).toBe('tokenB') + }) + }) + + describe('ResetQuantities (always reset selected pool)', () => { + it('should reset limit to undefined when order is market', () => { + const action: SwapCreateOrderAction = { + type: SwapCreateOrderActionType.ResetQuantities, + } + const expectedState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts = { + sell: { + quantity: '0', + tokenId: mockSwapStateDefault.orderData.amounts.sell.tokenId, + }, + buy: { + quantity: '0', + tokenId: mockSwapStateDefault.orderData.amounts.buy.tokenId, + }, + } + draft.orderData.limitPrice = undefined + }) + const state = combinedSwapReducers(mockSwapStateDefault, action) + expect(state).toEqual(expectedState) + }) + + it('should reset limit to market price when order is limit', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + draft.orderData.type = 'limit' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools6, + }) + const updatedLimit = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.LimitPriceChanged, + limitPrice: '2', + }) + + const state = combinedSwapReducers(updatedLimit, { + type: SwapCreateOrderActionType.ResetQuantities, + }) + + // change back to market price + expect(state.orderData.limitPrice).toBe('0.5') + }) }) + + describe('LimitPriceChanged', () => { + it('should not update when order type is market', () => { + const action: SwapCreateOrderAction = { + type: SwapCreateOrderActionType.LimitPriceChanged, + limitPrice: '2', + } + const state = combinedSwapReducers(mockSwapStateDefault, action) + expect(state.orderData.limitPrice).toBe( + mockSwapStateDefault.orderData.limitPrice, + ) + }) + + it('should update the limit and the buy side when pools are available', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + draft.orderData.type = 'limit' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: [], + }) + + const state = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.LimitPriceChanged, + limitPrice: '2', + }) + + expect(state.orderData.limitPrice).toBe('2') + }) + }) + + describe('SellTokenIdChanged', () => { + it('should reset pools and the derived data', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools1, + }) + const updatedSellQuantity = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.SellQuantityChanged, + quantity: '10000', + }) + + const state = combinedSwapReducers(updatedSellQuantity, { + type: SwapCreateOrderActionType.SellTokenIdChanged, + tokenId: 'x', + }) + + expect(state.orderData.amounts.sell.tokenId).toBe('x') + // should be updated right next with the new pool pairs + expect(state.orderData.pools).toEqual([]) + // should reset everything derived + expect(state.orderData.calculations).toEqual([]) + expect(state.orderData.selectedPoolCalculation).toBeUndefined() + expect(state.orderData.bestPoolCalculation).toBeUndefined() + // type is irrelevant + expect(state.orderData.selectedPoolId).toBeUndefined() + }) + }) + + describe('BuyTokenIdChanged', () => { + it('should reset pools and the derived data', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools1, + }) + const updatedSellQuantity = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.SellQuantityChanged, + quantity: '10000', + }) + + const state = combinedSwapReducers(updatedSellQuantity, { + type: SwapCreateOrderActionType.BuyTokenIdChanged, + tokenId: 'x', + }) + + expect(state.orderData.amounts.buy.tokenId).toBe('x') + // should be updated right next with the new pool pairs + expect(state.orderData.pools).toEqual([]) + // should reset everything derived + expect(state.orderData.calculations).toEqual([]) + expect(state.orderData.selectedPoolCalculation).toBeUndefined() + expect(state.orderData.bestPoolCalculation).toBeUndefined() + // type is irrelevant + expect(state.orderData.selectedPoolId).toBeUndefined() + }) + }) + + describe('SellQuantityChanged', () => { + it('should calculate the buy side if pools are available', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools1, + }) + + const state = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.SellQuantityChanged, + quantity: '10', + }) + + expect(state.orderData.selectedPoolCalculation).toBeDefined() + expect(state.orderData.amounts.buy.quantity).toBe('124') + expect(state.orderData.amounts.sell.quantity).toBe('10') + }) + }) + + describe('BuyQuantityChanged', () => { + it('should calculate the buy side if pools are available', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools1, + }) + + const state = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.BuyQuantityChanged, + quantity: '1', + }) + + expect(state.orderData.selectedPoolCalculation).toBeDefined() + expect(state.orderData.amounts.buy.quantity).toBe('1') + expect(state.orderData.amounts.sell.quantity).toBe('3') + }) + }) + + // NOTE an important assumption is taken when calculating the best pool + // that all poolpairs (pools) object are tokenA/tokenB represents buy/sell sell/buy + // and that the UI will never allow buy and sell tokens to be the same after touched + describe('PoolPairsChanged', () => { + describe('limit order', () => { + it('should reset limit price to market and unselect the selected pool', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools1, + }) + const updatedSellQuantity = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.SellQuantityChanged, + quantity: '10000', + }) + const updatedType = combinedSwapReducers(updatedSellQuantity, { + type: SwapCreateOrderActionType.OrderTypeChanged, + orderType: 'limit', + }) + const updatedLimit = combinedSwapReducers(updatedType, { + type: SwapCreateOrderActionType.LimitPriceChanged, + limitPrice: '1', + }) + + const state = combinedSwapReducers(updatedLimit, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools6, + }) + + expect(state.orderData.limitPrice).toBe('0.5') + expect(state.orderData.selectedPoolId).toBeUndefined() + }) + }) + + describe('no pools', () => { + it('should leave limit price undefined and wont update the buy side', () => { + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools1, + }) + const updatedSellQuantity = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.SellQuantityChanged, + quantity: '10000', + }) + const updatedType = combinedSwapReducers(updatedSellQuantity, { + type: SwapCreateOrderActionType.OrderTypeChanged, + orderType: 'limit', + }) + const updatedLimit = combinedSwapReducers(updatedType, { + type: SwapCreateOrderActionType.LimitPriceChanged, + limitPrice: '1', + }) + + // this would happen when changing the tokenId of one sides + // and there is no pool pairs for the new pair + const state = combinedSwapReducers(updatedLimit, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: [], + }) + + expect(state.orderData.limitPrice).toBeUndefined() + expect(state.orderData.selectedPoolId).toBeUndefined() + expect(state.orderData.amounts.buy.quantity).toBe( + updatedLimit.orderData.amounts.buy.quantity, + ) + }) + }) + }) + + // NOTE: initialized only + // designed to be triggered once only + it('PrimaryTokenIdChanged', () => { + const expectedState = combinedSwapReducers(mockSwapStateDefault, { + type: SwapCreateOrderActionType.PrimaryTokenIdChanged, + tokenId: 'brand.new', + }) + expect(expectedState.orderData.primartyTokenId).toBe('brand.new') + }) + it('LpTokenHeldChanged', () => { - expect('todo').toBeDefined() + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools6, + }) + const updatedSellQuantity = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.SellQuantityChanged, + quantity: '10000', + }) + + const state = combinedSwapReducers(updatedSellQuantity, { + type: SwapCreateOrderActionType.LpTokenHeldChanged, + amount: { + quantity: '100', + tokenId: 'lp.token', + }, + }) + + expect(state.orderData.amounts.buy.quantity).toBe( + updatedSellQuantity.orderData.amounts.buy.quantity, + ) }) + + // NOTE: initialized only + // designed to be triggered once only it('FrontendFeeTiersChanged', () => { - expect('todo').toBeDefined() + const initialState = produce(mockSwapStateDefault, (draft) => { + draft.orderData.amounts.sell.tokenId = 'tokenA' + draft.orderData.amounts.buy.tokenId = 'tokenB' + }) + const updatedPools = combinedSwapReducers(initialState, { + type: SwapCreateOrderActionType.PoolPairsChanged, + pools: mocks.mockedPools6, + }) + const updatedSellQuantity = combinedSwapReducers(updatedPools, { + type: SwapCreateOrderActionType.SellQuantityChanged, + quantity: '10000', + }) + + const state = combinedSwapReducers(updatedSellQuantity, { + type: SwapCreateOrderActionType.FrontendFeeTiersChanged, + feeTiers: [], + }) + + expect(state.orderData.amounts.buy.quantity).toBe( + updatedSellQuantity.orderData.amounts.buy.quantity, + ) }) }) diff --git a/packages/swap/src/translators/reactjs/state/state.ts b/packages/swap/src/translators/reactjs/state/state.ts index da03584362..eb08fe3348 100644 --- a/packages/swap/src/translators/reactjs/state/state.ts +++ b/packages/swap/src/translators/reactjs/state/state.ts @@ -366,6 +366,7 @@ const orderReducer = ( primaryTokenId: state.orderData.primartyTokenId, lpTokenHeld: state.orderData.lpTokenHeld, frontendFeeTiers: state.orderData.frontendFeeTiers, + side: 'sell', }) draft.orderData.bestPoolCalculation = getBestPoolCalculation( @@ -376,16 +377,8 @@ const orderReducer = ( if (draft.orderData.selectedPoolCalculation === undefined) break - if ( - draft.orderData.amounts.sell.tokenId === - draft.orderData.selectedPoolCalculation.pool.tokenA.tokenId - ) { - draft.orderData.amounts.buy = - draft.orderData.selectedPoolCalculation.sides.buy - } else { - draft.orderData.amounts.sell = - draft.orderData.selectedPoolCalculation.sides.sell - } + draft.orderData.amounts.buy = + draft.orderData.selectedPoolCalculation.sides.buy break // when resetting quantities, when order is limit, limit price is the best market price @@ -433,10 +426,10 @@ const orderReducer = ( // this can cause a pool if not enough supply to be selected // must be handled on the UI case SwapCreateOrderActionType.LimitPriceChanged: - draft.orderData.limitPrice = action.limitPrice - if (state.orderData.type === 'market') break + draft.orderData.limitPrice = action.limitPrice + draft.orderData.calculations = makeOrderCalculations({ orderType: state.orderData.type, amounts: state.orderData.amounts, @@ -572,6 +565,7 @@ const orderReducer = ( // when the frontend feee tiers changes, the calculations are updated // buy side needs recalculation since best pool can change + // NOTE: designed to be loaded once at the initialization case SwapCreateOrderActionType.FrontendFeeTiersChanged: draft.orderData.frontendFeeTiers = [...action.feeTiers] @@ -606,7 +600,7 @@ const orderReducer = ( draft.orderData.calculations = makeOrderCalculations({ orderType: state.orderData.type, amounts: state.orderData.amounts, - limitPrice: state.orderData.limitPrice, + limitPrice: undefined, slippage: state.orderData.slippage, pools: draft.orderData.pools, primaryTokenId: state.orderData.primartyTokenId, @@ -634,6 +628,7 @@ const orderReducer = ( draft.orderData.selectedPoolCalculation.sides.buy break + // NOTE: designed to be loaded once at the initialization case SwapCreateOrderActionType.PrimaryTokenIdChanged: draft.orderData.primartyTokenId = action.tokenId break From d9899156238adc258d8a96e577575ec01ab08a6a Mon Sep 17 00:00:00 2001 From: Juliano Lazzarotto <30806844+stackchain@users.noreply.github.com> Date: Sun, 22 Oct 2023 16:04:15 +0100 Subject: [PATCH 2/2] chore: bump coverage back to 100% --- packages/swap/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/swap/package.json b/packages/swap/package.json index e4905f31bf..3cfe6b5bcd 100644 --- a/packages/swap/package.json +++ b/packages/swap/package.json @@ -115,10 +115,10 @@ ], "coverageThreshold": { "global": { - "branches": 60, - "functions": 60, - "lines": 60, - "statements": 60 + "branches": 100, + "functions": 100, + "lines": 100, + "statements": 100 } }, "modulePathIgnorePatterns": [