diff --git a/packages/swap/src/helpers/orders/factories/makeOrderCalculations.test.ts b/packages/swap/src/helpers/orders/factories/makeOrderCalculations.test.ts index 60fbc091fe..0f92ca2463 100644 --- a/packages/swap/src/helpers/orders/factories/makeOrderCalculations.test.ts +++ b/packages/swap/src/helpers/orders/factories/makeOrderCalculations.test.ts @@ -3389,4 +3389,126 @@ describe('makeOrderCalculations', () => { } expect(calculations[0]).toStrictEqual(expectedCalculation) }) + it('should calculate fees and amounts correctly (case 28, pt at buy side)', () => { + const pool: Swap.Pool = mocks.mockedPools7[1]! + const pools = [pool] + const amounts = { + sell: { + quantity: '1000000', + 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, + tokens: { + sellInfo: { + decimals: 0, + id: 'tokenB', + }, + buyInfo: { + decimals: 0, + id: 'tokenA', + }, + ptInfo: { + decimals: 6, + id: 'tokenA', + }, + priceDenomination: 0, + }, + lpTokenHeld: { + quantity: '50', + tokenId: 'tokenX', + }, + side: 'sell', + frontendFeeTiers, + }) + const expectedCalculation: SwapOrderCalculation = { + order: { + side: 'sell', + slippage: 0, + orderType: 'market', + limitPrice: undefined, + amounts: { + sell: { + quantity: '1000000', + tokenId: 'tokenB', + }, + buy: { + quantity: '0', + tokenId: 'tokenA', + }, + }, + lpTokenHeld: { + quantity: '50', + tokenId: 'tokenX', + }, + }, + sides: { + sell: { + quantity: '1000000', + tokenId: 'tokenB', + }, + buy: { + quantity: '199', + tokenId: 'tokenA', + }, + }, + cost: { + batcherFee: { + quantity: '0', + tokenId: '', + }, + deposit: { + quantity: '0', + tokenId: '', + }, + frontendFeeInfo: { + discountTier: undefined, + fee: { + quantity: '0', + tokenId: 'tokenA', + }, + }, + liquidityFee: { + quantity: '0', + tokenId: 'tokenB', + }, + ptTotalFeeNoFEF: { + tokenId: 'tokenA', + quantity: '0', + }, + ptTotalFee: { + tokenId: 'tokenA', + quantity: '0', + }, + }, + buyAmountWithSlippage: { + quantity: '199', + tokenId: 'tokenA', + }, + hasSupply: true, + prices: { + base: '0.5', + market: '0.5', + withSlippage: '5025.12562814070351758794', + withFees: '5025.12562814070351758794', + withFeesAndSlippage: '5025.12562814070351758794', + difference: '1004925.125628140703517588', + withFeesNoFEF: '5025.12562814070351758794', + withFeesAndSlippageNoFEF: '5025.12562814070351758794', + differenceNoFEF: '1004925.125628140703517588', + }, + 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 bb56adf719..57a102a544 100644 --- a/packages/swap/src/helpers/orders/factories/makeOrderCalculations.ts +++ b/packages/swap/src/helpers/orders/factories/makeOrderCalculations.ts @@ -39,6 +39,8 @@ export const makeOrderCalculations = ({ }>): Array => { const isLimit = orderType === 'limit' const maybeLimitPrice = isLimit ? limitPrice : undefined + const isSellPt = amounts.sell.tokenId === tokens.ptInfo.id + const isBuyPt = amounts.buy.tokenId === tokens.ptInfo.id const calculations = pools.map((pool) => { const buy = @@ -60,8 +62,9 @@ export const makeOrderCalculations = ({ tokenId: buy.tokenId, } - // pools that with not enough supply will be filtered out const isBuyTokenA = buy.tokenId === pool.tokenA.tokenId + + // pools that with not enough supply will be filtered out const poolSupply = isBuyTokenA ? pool.tokenA.quantity : pool.tokenB.quantity const supplyRequired = (!Quantities.isZero(buy.quantity) || !Quantities.isZero(sell.quantity)) && @@ -72,24 +75,32 @@ export const makeOrderCalculations = ({ // lf is sell side % of quantity ie. XToken 100 * 1% = 1 XToken const liquidityFee: Balance.Amount = getLiquidityProviderFee(pool.fee, sell) + // whether sell or buy is PT, then we use the quantity as frontend fee base + // otherwise we derive from the ptPrice of the pool of the sell side const ptPriceSell = isBuyTokenA ? new BigNumber(pool.ptPriceTokenB) : new BigNumber(pool.ptPriceTokenA) + const sellQuantity = new BigNumber(sell.quantity) + const sellPrice = new BigNumber(ptPriceSell) + const sellInPtTerms = ptPriceSell.isZero() + ? Quantities.zero + : asQuantity( + sellQuantity + .dividedBy(new BigNumber(1).dividedBy(sellPrice)) + .toString(), + ) + const ptQuantity = isSellPt + ? sell.quantity + : isBuyPt + ? buy.quantity + : sellInPtTerms - // ffee is based on PT value range + LP holding range (sides may need conversion, when none is PT) - // TODO: it needs update, prices by muesli are provided in ADA, quantities in atomic units + // ffee is based on PT value range + LP holding range const frontendFeeInfo = getFrontendFee({ lpTokenHeld, - primaryTokenId: tokens.ptInfo.id, - sellInPrimaryTokenValue: { + ptAmount: { tokenId: tokens.ptInfo.id, - quantity: ptPriceSell.isZero() - ? Quantities.zero - : asQuantity( - new BigNumber(sell.quantity) - .dividedBy(ptPriceSell) - .integerValue(BigNumber.ROUND_CEIL), - ), + quantity: ptQuantity, }, feeTiers: frontendFeeTiers, }) @@ -101,12 +112,14 @@ export const makeOrderCalculations = ({ ? Quantities.zero : new BigNumber(pool.batcherFee.quantity) .dividedBy(ptPriceSell) - .integerValue(BigNumber.ROUND_CEIL), + .integerValue(BigNumber.ROUND_CEIL) + .toString(), frontendFee: ptPriceSell.isZero() ? Quantities.zero : new BigNumber(frontendFeeInfo.fee.quantity) .dividedBy(ptPriceSell) - .integerValue(BigNumber.ROUND_CEIL), + .integerValue(BigNumber.ROUND_CEIL) + .toString(), } const priceWithSlippage = Quantities.isZero(buyAmountWithSlippage.quantity)