Commit ff80edf0 authored by jsy1218's avatar jsy1218 Committed by GitHub

v2 quote with fee-on-transfer tax considerations (#144)

* v2 quote with fot tax considerations

* Fix code style issues with Prettier

* fix tax amount check

* fix the quote fot taxation calculation and correct test assertions

* getAmountIn tax calculation bug fix

* Fix code style issues with Prettier

* update the getAmountIn formula derivation to make it clearer

* Fix code style issues with Prettier

* update the reserve as well as getAmountIn amountInAfterTax calculation order

* address feedbacks

* Fix code style issues with Prettier

---------
Co-authored-by: default avatarLint Action <lint-action@samuelmeuli.com>
parent 629a46be
import { Percent } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
export const FACTORY_ADDRESS = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f'
......@@ -12,3 +13,7 @@ export const ONE = JSBI.BigInt(1)
export const FIVE = JSBI.BigInt(5)
export const _997 = JSBI.BigInt(997)
export const _1000 = JSBI.BigInt(1000)
export const BASIS_POINTS = JSBI.BigInt(10000)
export const ZERO_PERCENT = new Percent(ZERO)
export const ONE_HUNDRED_PERCENT = new Percent(ONE)
import { Token, WETH9, Price, CurrencyAmount } from '@uniswap/sdk-core'
import { ChainId, CurrencyAmount, Price, Token, WETH9 } from '@uniswap/sdk-core'
import { InsufficientInputAmountError } from '../errors'
import { computePairAddress, Pair } from './pair'
import { BigNumber } from '@ethersproject/bignumber'
describe('computePairAddress', () => {
it('should correctly compute the pool address', () => {
......@@ -172,6 +173,107 @@ describe('Pair', () => {
)
).toEqual(false)
})
describe('getInputAmount and getOutputAmount', () => {
const BLASTBuyFeeBps = BigNumber.from(400)
const BLASTSellFeeBps = BigNumber.from(10000)
const BLAST = new Token(
ChainId.MAINNET,
'0x3ed643e9032230f01c6c36060e305ab53ad3b482',
18,
'BLAST',
'BLAST',
false,
BLASTBuyFeeBps,
BLASTSellFeeBps
)
const BLASTERSBuyFeeBps = BigNumber.from(300)
const BLASTERSSellFeeBps = BigNumber.from(350)
const BLASTERS = new Token(
ChainId.MAINNET,
'0xab98093C7232E98A47D7270CE0c1c2106f61C73b',
9,
'BLAST',
'BLASTERS',
false,
BLASTERSBuyFeeBps,
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)
})
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)
})
})
})
describe('miscellaneous', () => {
it('getLiquidityMinted:0', async () => {
const tokenA = new Token(3, '0x0000000000000000000000000000000000000001', 18)
......
This diff is collapsed.
......@@ -1706,10 +1706,10 @@
semver "^7.3.2"
tsutils "^3.17.1"
"@uniswap/sdk-core@^4.0.2":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@uniswap/sdk-core/-/sdk-core-4.0.2.tgz#2eca2b5bf00bad74519aef918465c19218285b4b"
integrity sha512-rR5xobsAAP4yMYC7C+0+duVx0pFoDn2lV9kTWpoKgH1WJuw7hD1uDEvuevU2dL89TuixVgGvnYd0QxmrMtsIlg==
"@uniswap/sdk-core@^4.0.7":
version "4.0.7"
resolved "https://registry.yarnpkg.com/@uniswap/sdk-core/-/sdk-core-4.0.7.tgz#90dfd070d7e44494234618af398da158363ae827"
integrity sha512-jscx7KUIWzQatcL5PHY6xy0gEL9IGQcL5h/obxzX9foP2KoNk9cq66Ia8I2Kvpa7zBcPOeW1hU0hJNBq6CzcIQ==
dependencies:
"@ethersproject/address" "^5.0.2"
big.js "^5.2.2"
......
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