init
Signed-off-by: T-Hax <>
This commit is contained in:
commit
735546619e
828 changed files with 222925 additions and 0 deletions
182
lib/v3-core/test/FullMath.spec.ts
Normal file
182
lib/v3-core/test/FullMath.spec.ts
Normal file
|
@ -0,0 +1,182 @@
|
|||
import { ethers } from 'hardhat'
|
||||
import { FullMathTest } from '../typechain/FullMathTest'
|
||||
import { expect } from './shared/expect'
|
||||
import { Decimal } from 'decimal.js'
|
||||
|
||||
const {
|
||||
BigNumber,
|
||||
constants: { MaxUint256 },
|
||||
} = ethers
|
||||
const Q128 = BigNumber.from(2).pow(128)
|
||||
|
||||
Decimal.config({ toExpNeg: -500, toExpPos: 500 })
|
||||
|
||||
describe('FullMath', () => {
|
||||
let fullMath: FullMathTest
|
||||
before('deploy FullMathTest', async () => {
|
||||
const factory = await ethers.getContractFactory('FullMathTest')
|
||||
fullMath = (await factory.deploy()) as FullMathTest
|
||||
})
|
||||
|
||||
describe('#mulDiv', () => {
|
||||
it('reverts if denominator is 0', async () => {
|
||||
await expect(fullMath.mulDiv(Q128, 5, 0)).to.be.reverted
|
||||
})
|
||||
it('reverts if denominator is 0 and numerator overflows', async () => {
|
||||
await expect(fullMath.mulDiv(Q128, Q128, 0)).to.be.reverted
|
||||
})
|
||||
it('reverts if output overflows uint256', async () => {
|
||||
await expect(fullMath.mulDiv(Q128, Q128, 1)).to.be.reverted
|
||||
})
|
||||
it('reverts if output overflows uint256', async () => {
|
||||
await expect(fullMath.mulDiv(Q128, Q128, 1)).to.be.reverted
|
||||
})
|
||||
it('reverts on overflow with all max inputs', async () => {
|
||||
await expect(fullMath.mulDiv(MaxUint256, MaxUint256, MaxUint256.sub(1))).to.be.reverted
|
||||
})
|
||||
|
||||
it('all max inputs', async () => {
|
||||
expect(await fullMath.mulDiv(MaxUint256, MaxUint256, MaxUint256)).to.eq(MaxUint256)
|
||||
})
|
||||
|
||||
it('accurate without phantom overflow', async () => {
|
||||
const result = Q128.div(3)
|
||||
expect(
|
||||
await fullMath.mulDiv(
|
||||
Q128,
|
||||
/*0.5=*/ BigNumber.from(50).mul(Q128).div(100),
|
||||
/*1.5=*/ BigNumber.from(150).mul(Q128).div(100)
|
||||
)
|
||||
).to.eq(result)
|
||||
})
|
||||
|
||||
it('accurate with phantom overflow', async () => {
|
||||
const result = BigNumber.from(4375).mul(Q128).div(1000)
|
||||
expect(await fullMath.mulDiv(Q128, BigNumber.from(35).mul(Q128), BigNumber.from(8).mul(Q128))).to.eq(result)
|
||||
})
|
||||
|
||||
it('accurate with phantom overflow and repeating decimal', async () => {
|
||||
const result = BigNumber.from(1).mul(Q128).div(3)
|
||||
expect(await fullMath.mulDiv(Q128, BigNumber.from(1000).mul(Q128), BigNumber.from(3000).mul(Q128))).to.eq(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#mulDivRoundingUp', () => {
|
||||
it('reverts if denominator is 0', async () => {
|
||||
await expect(fullMath.mulDivRoundingUp(Q128, 5, 0)).to.be.reverted
|
||||
})
|
||||
it('reverts if denominator is 0 and numerator overflows', async () => {
|
||||
await expect(fullMath.mulDivRoundingUp(Q128, Q128, 0)).to.be.reverted
|
||||
})
|
||||
it('reverts if output overflows uint256', async () => {
|
||||
await expect(fullMath.mulDivRoundingUp(Q128, Q128, 1)).to.be.reverted
|
||||
})
|
||||
it('reverts on overflow with all max inputs', async () => {
|
||||
await expect(fullMath.mulDivRoundingUp(MaxUint256, MaxUint256, MaxUint256.sub(1))).to.be.reverted
|
||||
})
|
||||
|
||||
it('reverts if mulDiv overflows 256 bits after rounding up', async () => {
|
||||
await expect(
|
||||
fullMath.mulDivRoundingUp(
|
||||
'535006138814359',
|
||||
'432862656469423142931042426214547535783388063929571229938474969',
|
||||
'2'
|
||||
)
|
||||
).to.be.reverted
|
||||
})
|
||||
|
||||
it('reverts if mulDiv overflows 256 bits after rounding up case 2', async () => {
|
||||
await expect(
|
||||
fullMath.mulDivRoundingUp(
|
||||
'115792089237316195423570985008687907853269984659341747863450311749907997002549',
|
||||
'115792089237316195423570985008687907853269984659341747863450311749907997002550',
|
||||
'115792089237316195423570985008687907853269984653042931687443039491902864365164'
|
||||
)
|
||||
).to.be.reverted
|
||||
})
|
||||
|
||||
it('all max inputs', async () => {
|
||||
expect(await fullMath.mulDivRoundingUp(MaxUint256, MaxUint256, MaxUint256)).to.eq(MaxUint256)
|
||||
})
|
||||
|
||||
it('accurate without phantom overflow', async () => {
|
||||
const result = Q128.div(3).add(1)
|
||||
expect(
|
||||
await fullMath.mulDivRoundingUp(
|
||||
Q128,
|
||||
/*0.5=*/ BigNumber.from(50).mul(Q128).div(100),
|
||||
/*1.5=*/ BigNumber.from(150).mul(Q128).div(100)
|
||||
)
|
||||
).to.eq(result)
|
||||
})
|
||||
|
||||
it('accurate with phantom overflow', async () => {
|
||||
const result = BigNumber.from(4375).mul(Q128).div(1000)
|
||||
expect(await fullMath.mulDivRoundingUp(Q128, BigNumber.from(35).mul(Q128), BigNumber.from(8).mul(Q128))).to.eq(
|
||||
result
|
||||
)
|
||||
})
|
||||
|
||||
it('accurate with phantom overflow and repeating decimal', async () => {
|
||||
const result = BigNumber.from(1).mul(Q128).div(3).add(1)
|
||||
expect(
|
||||
await fullMath.mulDivRoundingUp(Q128, BigNumber.from(1000).mul(Q128), BigNumber.from(3000).mul(Q128))
|
||||
).to.eq(result)
|
||||
})
|
||||
})
|
||||
|
||||
function pseudoRandomBigNumber() {
|
||||
return BigNumber.from(new Decimal(MaxUint256.toString()).mul(Math.random().toString()).round().toString())
|
||||
}
|
||||
|
||||
// tiny fuzzer. unskip to run
|
||||
it.skip('check a bunch of random inputs against JS implementation', async () => {
|
||||
// generates random inputs
|
||||
const tests = Array(1_000)
|
||||
.fill(null)
|
||||
.map(() => {
|
||||
return {
|
||||
x: pseudoRandomBigNumber(),
|
||||
y: pseudoRandomBigNumber(),
|
||||
d: pseudoRandomBigNumber(),
|
||||
}
|
||||
})
|
||||
.map(({ x, y, d }) => {
|
||||
return {
|
||||
input: {
|
||||
x,
|
||||
y,
|
||||
d,
|
||||
},
|
||||
floored: fullMath.mulDiv(x, y, d),
|
||||
ceiled: fullMath.mulDivRoundingUp(x, y, d),
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all(
|
||||
tests.map(async ({ input: { x, y, d }, floored, ceiled }) => {
|
||||
if (d.eq(0)) {
|
||||
await expect(floored).to.be.reverted
|
||||
await expect(ceiled).to.be.reverted
|
||||
return
|
||||
}
|
||||
|
||||
if (x.eq(0) || y.eq(0)) {
|
||||
await expect(floored).to.eq(0)
|
||||
await expect(ceiled).to.eq(0)
|
||||
} else if (x.mul(y).div(d).gt(MaxUint256)) {
|
||||
await expect(floored).to.be.reverted
|
||||
await expect(ceiled).to.be.reverted
|
||||
} else {
|
||||
expect(await floored).to.eq(x.mul(y).div(d))
|
||||
expect(await ceiled).to.eq(
|
||||
x
|
||||
.mul(y)
|
||||
.div(d)
|
||||
.add(x.mul(y).mod(d).gt(0) ? 1 : 0)
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue