Skip to content

Commit ad7d323

Browse files
fix(order-book): handle decimal protocolFeeBps BigInt conversion (cowprotocol#798)
* fix(order-book): handle decimal protocolFeeBps BigInt conversion * chore: merge main --------- Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: shoom3301 <shoom3301@gmail.com>
1 parent 4e2330f commit ad7d323

3 files changed

Lines changed: 65 additions & 14 deletions

File tree

packages/order-book/src/quoteAmountsAndCosts/getProtocolFeeAmount.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { OrderKind, OrderParameters } from '../generated'
22
import { HUNDRED_THOUSANDS, ONE_HUNDRED_BPS } from './quoteAmountsAndCosts.const'
33

4+
const PROTOCOL_FEE_BPS_SCALE = BigInt(HUNDRED_THOUSANDS)
5+
46
export interface ProtocolFeeAmountParams {
57
orderParams: OrderParameters
68
protocolFeeBps: number
@@ -42,8 +44,13 @@ export function getProtocolFeeAmount(params: ProtocolFeeAmountParams): bigint {
4244
const buyAmount = BigInt(buyAmountStr)
4345
const feeAmount = BigInt(feeAmountStr)
4446

45-
const protocolFeeScale = BigInt(HUNDRED_THOUSANDS)
46-
const protocolFeeBpsBig = BigInt(protocolFeeBps * HUNDRED_THOUSANDS)
47+
const protocolFeeScale = PROTOCOL_FEE_BPS_SCALE
48+
// Keep 5 decimal places of bps precision while avoiding BigInt conversion from non-integer floats.
49+
const protocolFeeBpsBig = BigInt(Math.round(protocolFeeBps * HUNDRED_THOUSANDS))
50+
51+
if (protocolFeeBpsBig <= 0n) {
52+
return 0n
53+
}
4754

4855
if (isSell) {
4956
/**

packages/order-book/src/quoteAmountsAndCosts/getQuoteAmountsAndCosts.test.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,7 @@ describe('Calculation of before/after fees amounts', () => {
235235

236236
expect(result.costs.protocolFee.amount).toBe(expectedProtocolFeeAmount)
237237
// beforeNetworkCosts.buyAmount for SELL = afterProtocolFees.buyAmount = buyAmount + networkCostInBuyCurrency
238-
const networkCostInBuyCurrency =
239-
(buyAfter * BigInt(orderParams.feeAmount)) / BigInt(orderParams.sellAmount)
238+
const networkCostInBuyCurrency = (buyAfter * BigInt(orderParams.feeAmount)) / BigInt(orderParams.sellAmount)
240239
expect(result.beforeNetworkCosts.buyAmount).toBe(buyAfter + networkCostInBuyCurrency)
241240
})
242241

@@ -257,8 +256,7 @@ describe('Calculation of before/after fees amounts', () => {
257256
const expectedProtocolFeeAmount = (buyAfter * protocolBps) / protocolDenominator
258257

259258
// Partner fee is based on beforeAllFees.buyAmount = buyAmount + networkCostInBuyCurrency + protocolFeeAmount
260-
const networkCostInBuyCurrency =
261-
(buyAfter * BigInt(orderParams.feeAmount)) / BigInt(orderParams.sellAmount)
259+
const networkCostInBuyCurrency = (buyAfter * BigInt(orderParams.feeAmount)) / BigInt(orderParams.sellAmount)
262260
const buyBeforeAllFees = buyAfter + networkCostInBuyCurrency + expectedProtocolFeeAmount
263261
const partnerBps = BigInt(partnerFeeBps)
264262
const expectedPartnerFeeAmount = (buyBeforeAllFees * partnerBps) / 10_000n
@@ -304,8 +302,7 @@ describe('Calculation of before/after fees amounts', () => {
304302

305303
expect(result.costs.protocolFee.amount).toBe(expectedProtocolFeeAmount)
306304
// beforeNetworkCosts.buyAmount = buyAmount + networkCostInBuyCurrency
307-
const networkCostInBuyCurrency =
308-
(buyAfter * BigInt(orderParams.feeAmount)) / BigInt(orderParams.sellAmount)
305+
const networkCostInBuyCurrency = (buyAfter * BigInt(orderParams.feeAmount)) / BigInt(orderParams.sellAmount)
309306
expect(result.beforeNetworkCosts.buyAmount).toBe(buyAfter + networkCostInBuyCurrency)
310307

311308
expect(result.costs.protocolFee).toEqual({

packages/trading/src/getQuote.test.ts

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,58 @@ describe('getQuote', () => {
353353
)
354354
})
355355
})
356+
357+
it('Should handle decimal protocolFeeBps from quote response', async () => {
358+
const adapterNames = Object.keys(adapters) as Array<keyof typeof adapters>
359+
const quoteWithProtocolFee = {
360+
...quoteResponseMock,
361+
protocolFeeBps: '0.3',
362+
} as OrderQuoteResponse
363+
getQuoteMock.mockResolvedValue(quoteWithProtocolFee)
364+
365+
for (const adapterName of adapterNames) {
366+
setGlobalAdapter(adapters[adapterName])
367+
const { result } = await getQuoteWithSigner(
368+
{ ...defaultOrderParams, signer: adapters[adapterName].signer },
369+
{},
370+
orderBookApiMock,
371+
)
372+
373+
const buyAfter = BigInt(quoteResponseMock.quote.buyAmount)
374+
const protocolFeeBpsBig = BigInt(Math.round(0.3 * 100_000))
375+
const denominator = 10_000n * 100_000n - protocolFeeBpsBig
376+
const expectedProtocolFeeAmount = (buyAfter * protocolFeeBpsBig) / denominator
377+
378+
expect(result.amountsAndCosts.costs.protocolFee.amount.toString()).toBe(expectedProtocolFeeAmount.toString())
379+
expect(result.amountsAndCosts.costs.protocolFee.bps).toBe(0.3)
380+
}
381+
})
382+
383+
it('Should handle decimal protocolFeeBps from quote response', async () => {
384+
const adapterNames = Object.keys(adapters) as Array<keyof typeof adapters>
385+
const quoteWithProtocolFee = {
386+
...quoteResponseMock,
387+
protocolFeeBps: '0.3',
388+
} as OrderQuoteResponse
389+
getQuoteMock.mockResolvedValue(quoteWithProtocolFee)
390+
391+
for (const adapterName of adapterNames) {
392+
setGlobalAdapter(adapters[adapterName])
393+
const { result } = await getQuoteWithSigner(
394+
{ ...defaultOrderParams, signer: adapters[adapterName].signer },
395+
{},
396+
orderBookApiMock,
397+
)
398+
399+
const buyAfter = BigInt(quoteResponseMock.quote.buyAmount)
400+
const protocolFeeBpsBig = BigInt(Math.round(0.3 * 100_000))
401+
const denominator = 10_000n * 100_000n - protocolFeeBpsBig
402+
const expectedProtocolFeeAmount = (buyAfter * protocolFeeBpsBig) / denominator
403+
404+
expect(result.amountsAndCosts.costs.protocolFee.amount.toString()).toBe(expectedProtocolFeeAmount.toString())
405+
expect(result.amountsAndCosts.costs.protocolFee.bps).toBe(0.3)
406+
}
407+
})
356408
})
357409

358410
describe('Order to sign', () => {
@@ -524,12 +576,7 @@ describe('getQuoteWithoutSigner', () => {
524576
})
525577

526578
it('should return quote results without requiring a signer', async () => {
527-
const { result, orderBookApi } = await getQuoteWithoutSigner(
528-
tradeParams,
529-
quoterParams,
530-
{},
531-
orderBookApiMock,
532-
)
579+
const { result, orderBookApi } = await getQuoteWithoutSigner(tradeParams, quoterParams, {}, orderBookApiMock)
533580

534581
expect(result).toBeDefined()
535582
expect(result.quoteResponse).toEqual(quoteResponseMock)

0 commit comments

Comments
 (0)