Commit 53e3b8e9 authored by Miguel Cervera's avatar Miguel Cervera Committed by GitHub

Receive an option to decide if we calculate FOT tax or not (#146)

* Receive an option to decide if we calculate FOT tax or not

* Fix code style issues with Prettier

---------
Co-authored-by: default avatarLint Action <lint-action@samuelmeuli.com>
parent 4411fae6
dist
node_modules
.idea
......@@ -199,78 +199,120 @@ describe('Pair', () => {
BLASTERSSellFeeBps
)
describe('getOutputAmount', () => {
it('getOutputAmount for input token BLASTERS and output token BLAST', () => {
const reserveBlasterAmount = CurrencyAmount.fromRawAmount(BLASTERS, '10000')
const reserveBlastAmount = CurrencyAmount.fromRawAmount(BLAST, '10000')
const pair = new Pair(reserveBlasterAmount, reserveBlastAmount)
const inputBlastersAmount = CurrencyAmount.fromRawAmount(BLASTERS, '100')
const [outputBlastAmount] = pair.getOutputAmount(inputBlastersAmount)
// Theoretical amount out:
// (10000 * 997 * 100 * (1 - 3.5%) / (10000 * 1000 + 997 * 100 * (1 - 3.5%))) * (1 - 4%)
// = 91.48
//
// However in practice, we have round down of precisions in multiple steps
// hence the amount out will be slightly less than 91.48:
//
// inputAmount = 100
// percentAfterSellFeesInDecimal = fraction(9650, 10000)
// inputAmountAfterTax = 100 * fraction(9650, 10000) = 96.5 = 96 (rounded down)
// inputAmountWithFeeAndAfterTax = 96 * 997 = 95712
// numerator = 95712 * 10000 = 957120000
// denominator = 10000 * 1000 + 95712 = 10095712
// outputAmount = 957120000 / 10095712 = 94.8046061536 = 94 (rounded down)
// buyFeePercentInDecimal = fraction(400, 10000)
// percentAfterBuyFeesInDecimal = fraction(9600, 10000)
// outputAmountAfterTax = 94 * fraction(9600, 10000)
// = 94 * 0.96
// = 90.24
// = 90 (rounded down)
const expectedOutputBlastAmount = '0.00000000000000009'
expect(outputBlastAmount.toExact()).toEqual(expectedOutputBlastAmount)
let calculateFotFees: boolean = false
describe('when calculating FOT fees', () => {
beforeEach(() => {
calculateFotFees = true
})
describe('getOutputAmount', () => {
it('getOutputAmount for input token BLASTERS and output token BLAST', () => {
const reserveBlasterAmount = CurrencyAmount.fromRawAmount(BLASTERS, '10000')
const reserveBlastAmount = CurrencyAmount.fromRawAmount(BLAST, '10000')
const pair = new Pair(reserveBlasterAmount, reserveBlastAmount)
const inputBlastersAmount = CurrencyAmount.fromRawAmount(BLASTERS, '100')
const [outputBlastAmount] = pair.getOutputAmount(inputBlastersAmount, calculateFotFees)
// Theoretical amount out:
// (10000 * 997 * 100 * (1 - 3.5%) / (10000 * 1000 + 997 * 100 * (1 - 3.5%))) * (1 - 4%)
// = 91.48
//
// However in practice, we have round down of precisions in multiple steps
// hence the amount out will be slightly less than 91.48:
//
// inputAmount = 100
// percentAfterSellFeesInDecimal = fraction(9650, 10000)
// inputAmountAfterTax = 100 * fraction(9650, 10000) = 96.5 = 96 (rounded down)
// inputAmountWithFeeAndAfterTax = 96 * 997 = 95712
// numerator = 95712 * 10000 = 957120000
// denominator = 10000 * 1000 + 95712 = 10095712
// outputAmount = 957120000 / 10095712 = 94.8046061536 = 94 (rounded down)
// buyFeePercentInDecimal = fraction(400, 10000)
// percentAfterBuyFeesInDecimal = fraction(9600, 10000)
// outputAmountAfterTax = 94 * fraction(9600, 10000)
// = 94 * 0.96
// = 90.24
// = 90 (rounded down)
const expectedOutputBlastAmount = '0.00000000000000009'
expect(outputBlastAmount.toExact()).toEqual(expectedOutputBlastAmount)
})
it('getInputAmount for input token BLASTERS and output token BLAST', () => {
const reserveBlasterAmount = CurrencyAmount.fromRawAmount(BLASTERS, '10000')
const reserveBlastAmount = CurrencyAmount.fromRawAmount(BLAST, '10000')
const pair = new Pair(reserveBlasterAmount, reserveBlastAmount)
const outputBlastAmount = CurrencyAmount.fromRawAmount(BLAST, '91')
const [inputBlasterAmount] = pair.getInputAmount(outputBlastAmount, calculateFotFees)
// Theoretical amount in:
// 10000 * 100 * (1 - 4%) * 1000 / ((10000 - 100 * (1 - 4%)) * 997) / (1 - 3.5%)
// = 100.7483934892
//
// However in practice, we have round up of precisions in multiple steps
// hence the amount out will be slightly more than 100.7483934892:
//
// buyFeePercentInDecimal = fraction(400, 10000)
// percentAfterBuyFeesInDecimal = 1 - fraction(400, 10000) = fraction(9600, 10000)
// outputAmountBeforeTax = 91 / fraction(960000, 10000) + 1
// = 91 / 0.96 + 1
// = 94.7916666667 + 1
// = 94 (rounded down) + 1
// = 95 (rounded up)
// numerator = 10000 * 95 * 1000 = 950000000
// denominator = (10000 - 95) * 997 = 9875285
// inputAmount = 950000000 / 9875285 + 1
// = 96.1997552476 + 1
// = 96 (rounded down) + 1
// = 97 (rounded up)
// sellFeePercentInDecimal = fraction(350, 10000)
// percentAfterSellFeesInDecimal = 1 - fraction(350, 10000) = fraction(9650, 10000)
// inputAmountBeforeTax = (97 / fraction(9650, 10000)) + 1
// = (97 / 0.965) + 1
// = 100.518134715 + 1
// = 100 (rounded down) + 1
// = 101
const expectedInputBlasterAmount = '0.000000101'
expect(inputBlasterAmount.toExact()).toEqual(expectedInputBlasterAmount)
})
})
})
describe('when NOT calculating FOT fees', () => {
beforeEach(() => {
calculateFotFees = false
})
describe('getOutputAmount', () => {
it('getOutputAmount for input token BLASTERS and output token BLAST', () => {
const reserveBlasterAmount = CurrencyAmount.fromRawAmount(BLASTERS, '10000')
const reserveBlastAmount = CurrencyAmount.fromRawAmount(BLAST, '10000')
const pair = new Pair(reserveBlasterAmount, reserveBlastAmount)
const inputBlastersAmount = CurrencyAmount.fromRawAmount(BLASTERS, '100')
const [outputBlastAmount] = pair.getOutputAmount(inputBlastersAmount, calculateFotFees)
const expectedOutputBlastAmount = '0.000000000000000098'
expect(outputBlastAmount.toExact()).toEqual(expectedOutputBlastAmount)
})
it('getInputAmount for input token BLASTERS and output token BLAST', () => {
const reserveBlasterAmount = CurrencyAmount.fromRawAmount(BLASTERS, '10000')
const reserveBlastAmount = CurrencyAmount.fromRawAmount(BLAST, '10000')
const pair = new Pair(reserveBlasterAmount, reserveBlastAmount)
const outputBlastAmount = CurrencyAmount.fromRawAmount(BLAST, '91')
const [inputBlasterAmount] = pair.getInputAmount(outputBlastAmount, calculateFotFees)
it('getInputAmount for input token BLASTERS and output token BLAST', () => {
const reserveBlasterAmount = CurrencyAmount.fromRawAmount(BLASTERS, '10000')
const reserveBlastAmount = CurrencyAmount.fromRawAmount(BLAST, '10000')
const pair = new Pair(reserveBlasterAmount, reserveBlastAmount)
const outputBlastAmount = CurrencyAmount.fromRawAmount(BLAST, '91')
const [inputBlasterAmount] = pair.getInputAmount(outputBlastAmount)
// Theoretical amount in:
// 10000 * 100 * (1 - 4%) * 1000 / ((10000 - 100 * (1 - 4%)) * 997) / (1 - 3.5%)
// = 100.7483934892
//
// However in practice, we have round up of precisions in multiple steps
// hence the amount out will be slightly more than 100.7483934892:
//
// buyFeePercentInDecimal = fraction(400, 10000)
// percentAfterBuyFeesInDecimal = 1 - fraction(400, 10000) = fraction(9600, 10000)
// outputAmountBeforeTax = 91 / fraction(960000, 10000) + 1
// = 91 / 0.96 + 1
// = 94.7916666667 + 1
// = 94 (rounded down) + 1
// = 95 (rounded up)
// numerator = 10000 * 95 * 1000 = 950000000
// denominator = (10000 - 95) * 997 = 9875285
// inputAmount = 950000000 / 9875285 + 1
// = 96.1997552476 + 1
// = 96 (rounded down) + 1
// = 97 (rounded up)
// sellFeePercentInDecimal = fraction(350, 10000)
// percentAfterSellFeesInDecimal = 1 - fraction(350, 10000) = fraction(9650, 10000)
// inputAmountBeforeTax = (97 / fraction(9650, 10000)) + 1
// = (97 / 0.965) + 1
// = 100.518134715 + 1
// = 100 (rounded down) + 1
// = 101
const expectedInputBlasterAmount = '0.000000101'
expect(inputBlasterAmount.toExact()).toEqual(expectedInputBlasterAmount)
const expectedInputBlasterAmount = '0.000000093'
expect(inputBlasterAmount.toExact()).toEqual(expectedInputBlasterAmount)
})
})
})
})
......
......@@ -178,7 +178,10 @@ export class Pair {
*
* @param inputAmount
*/
public getOutputAmount(inputAmount: CurrencyAmount<Token>): [CurrencyAmount<Token>, Pair] {
public getOutputAmount(
inputAmount: CurrencyAmount<Token>,
calculateFotFees: boolean = false
): [CurrencyAmount<Token>, Pair] {
invariant(this.involvesToken(inputAmount.currency), 'TOKEN')
if (JSBI.equal(this.reserve0.quotient, ZERO) || JSBI.equal(this.reserve1.quotient, ZERO)) {
throw new InsufficientReservesError()
......@@ -186,7 +189,7 @@ export class Pair {
const inputReserve = this.reserveOf(inputAmount.currency)
const outputReserve = this.reserveOf(inputAmount.currency.equals(this.token0) ? this.token1 : this.token0)
const percentAfterSellFees = this.derivePercentAfterSellFees(inputAmount)
const percentAfterSellFees = calculateFotFees ? this.derivePercentAfterSellFees(inputAmount) : ZERO_PERCENT
const inputAmountAfterTax = percentAfterSellFees.greaterThan(ZERO_PERCENT)
? CurrencyAmount.fromRawAmount(
inputAmount.currency,
......@@ -206,7 +209,7 @@ export class Pair {
throw new InsufficientInputAmountError()
}
const percentAfterBuyFees = this.derivePercentAfterBuyFees(outputAmount)
const percentAfterBuyFees = calculateFotFees ? this.derivePercentAfterBuyFees(outputAmount) : ZERO_PERCENT
const outputAmountAfterTax = percentAfterBuyFees.greaterThan(ZERO_PERCENT)
? CurrencyAmount.fromRawAmount(
outputAmount.currency,
......@@ -265,9 +268,12 @@ export class Pair {
*
* @param outputAmount
*/
public getInputAmount(outputAmount: CurrencyAmount<Token>): [CurrencyAmount<Token>, Pair] {
public getInputAmount(
outputAmount: CurrencyAmount<Token>,
calculateFotFees: boolean = false
): [CurrencyAmount<Token>, Pair] {
invariant(this.involvesToken(outputAmount.currency), 'TOKEN')
const percentAfterBuyFees = this.derivePercentAfterBuyFees(outputAmount)
const percentAfterBuyFees = calculateFotFees ? this.derivePercentAfterBuyFees(outputAmount) : ZERO_PERCENT
const outputAmountBeforeTax = percentAfterBuyFees.greaterThan(ZERO_PERCENT)
? CurrencyAmount.fromRawAmount(
outputAmount.currency,
......@@ -294,7 +300,7 @@ export class Pair {
JSBI.add(JSBI.divide(numerator, denominator), ONE) // add 1 here is part of the formula, no rounding needed here, since there will not be decimal at this point
)
const percentAfterSellFees = this.derivePercentAfterSellFees(inputAmount)
const percentAfterSellFees = calculateFotFees ? this.derivePercentAfterSellFees(inputAmount) : ZERO_PERCENT
const inputAmountBeforeTax = percentAfterSellFees.greaterThan(ZERO_PERCENT)
? CurrencyAmount.fromRawAmount(
inputAmount.currency,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment