tornado-anonymity-mining/test/rewardSwap.test.js

191 lines
7.0 KiB
JavaScript

/* global artifacts, web3, contract */
require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should()
const { toBN } = require('web3-utils')
const { takeSnapshot, revertSnapshot, mineBlock } = require('../scripts/ganacheHelper')
const { tornadoFormula, reverseTornadoFormula } = require('../src/utils')
const Torn = artifacts.require('TORNMock')
const RewardSwap = artifacts.require('RewardSwapMock')
const tornConfig = require('torn-token')
const RLP = require('rlp')
const MONTH = toBN(60 * 60 * 24 * 30)
const DURATION = toBN(60 * 60 * 24 * 365)
// Set time to beginning of a second
async function timeReset() {
const delay = 1000 - new Date().getMilliseconds()
await new Promise((resolve) => setTimeout(resolve, delay))
await mineBlock()
}
async function getNextAddr(sender, offset = 0) {
const nonce = await web3.eth.getTransactionCount(sender)
return (
'0x' +
web3.utils
.sha3(RLP.encode([sender, Number(nonce) + Number(offset)]))
.slice(12)
.substring(14)
)
}
contract('RewardSwap', (accounts) => {
let torn
let rewardSwap
let amount
const tornCap = toBN(tornConfig.torn.cap)
const miningCap = toBN(tornConfig.torn.distribution.miningV2.amount)
const initialTornBalance = toBN(tornConfig.miningV2.initialBalance)
let yearLiquidity
let delta = toBN(100000) // 0.0000000000001 torn error
// eslint-disable-next-line no-unused-vars
const sender = accounts[0]
const recipient = accounts[1]
// eslint-disable-next-line no-unused-vars
const relayer = accounts[2]
let snapshotId
const thirtyDays = 30 * 24 * 3600
const poolWeight = 1e11
before(async () => {
const swapExpectedAddr = await getNextAddr(accounts[0], 1)
torn = await Torn.new(sender, thirtyDays, [
{ to: swapExpectedAddr, amount: miningCap.toString() },
{ to: sender, amount: tornCap.sub(miningCap).toString() },
])
rewardSwap = await RewardSwap.new(
torn.address,
sender,
miningCap.toString(),
initialTornBalance.toString(),
poolWeight,
)
yearLiquidity = miningCap.sub(initialTornBalance)
amount = toBN(await rewardSwap.poolWeight()).mul(toBN(7)) // 10**10
snapshotId = await takeSnapshot()
})
beforeEach(async () => {
await timeReset()
})
describe('#formula test', () => {
it('should work', async () => {
let formulaReturn
let expectedReturn
amount = amount.mul(toBN(5))
for (let i = 1; i < 11; i++) {
amount = amount.div(toBN(i))
formulaReturn = tornadoFormula({ balance: initialTornBalance, amount })
expectedReturn = await rewardSwap.getExpectedReturn(amount)
expectedReturn.sub(formulaReturn).should.be.lte.BN(delta)
}
})
})
describe('#constructor', () => {
it('should initialize', async () => {
const tokenFromContract = await rewardSwap.torn()
tokenFromContract.should.be.equal(torn.address)
})
})
it('should return as expected', async () => {
const balanceBefore = await torn.balanceOf(recipient)
const expectedReturn = await rewardSwap.getExpectedReturn(amount)
await rewardSwap.swap(recipient, amount, { from: sender })
const balanceAfter = await torn.balanceOf(recipient)
balanceAfter.sub(balanceBefore).should.be.eq.BN(expectedReturn)
})
it('reverse rate', async () => {
const tokens = await rewardSwap.getExpectedReturn(amount)
const balance = await rewardSwap.tornVirtualBalance()
const points = reverseTornadoFormula({ balance, tokens })
points.sub(amount).should.be.lt.BN(toBN(1))
})
it('should be approximately additive', async () => {
const amount = toBN(10).pow(toBN(10)).mul(toBN(2))
const delta = toBN('1000') // max floating point error
const balanceBefore1 = await torn.balanceOf(recipient)
await rewardSwap.swap(recipient, amount, { from: sender })
const balanceAfter1 = await torn.balanceOf(recipient)
await revertSnapshot(snapshotId.result)
snapshotId = await takeSnapshot()
const balanceBefore2 = await torn.balanceOf(recipient)
await rewardSwap.swap(recipient, amount.div(toBN(2)), { from: sender })
await rewardSwap.swap(recipient, amount.div(toBN(2)), { from: sender })
const balanceAfter2 = await torn.balanceOf(recipient)
balanceBefore1.sub(balanceBefore2).should.be.lt.BN(delta)
balanceAfter1.sub(balanceAfter2).should.be.lt.BN(delta)
})
describe('#swap', () => {
it('should work as uniswap without vested tokens', async () => {
const startTimestamp = await rewardSwap.startTimestamp()
await rewardSwap.setTimestamp(startTimestamp)
const expectedTokens = await rewardSwap.getExpectedReturn(amount)
const formulaReturn = tornadoFormula({ balance: initialTornBalance, amount })
expectedTokens.sub(formulaReturn).should.be.lte.BN(delta)
const tornVirtualBalance = await rewardSwap.tornVirtualBalance()
tornVirtualBalance.should.be.eq.BN(initialTornBalance)
const balanceBefore = await torn.balanceOf(recipient)
await rewardSwap.swap(recipient, amount, { from: sender })
const balanceAfter = await torn.balanceOf(recipient)
balanceAfter.should.be.eq.BN(balanceBefore.add(expectedTokens))
})
it('should work with vested tokens (a half of year passed)', async () => {
let startTimestamp = await rewardSwap.startTimestamp()
const currentTimestamp = startTimestamp.add(DURATION.div(toBN(2)))
await rewardSwap.setTimestamp(currentTimestamp)
const tornVirtualBalance = await rewardSwap.tornVirtualBalance()
tornVirtualBalance.should.be.eq.BN(yearLiquidity.div(toBN(2)).add(initialTornBalance))
const formulaReturn = tornadoFormula({ balance: tornVirtualBalance, amount })
const expectedTokens = await rewardSwap.getExpectedReturn(amount)
expectedTokens.sub(formulaReturn).should.be.lte.BN(delta)
const balanceBefore = await torn.balanceOf(recipient)
await rewardSwap.swap(recipient, amount, { from: sender })
const balanceAfter = await torn.balanceOf(recipient)
balanceAfter.should.be.eq.BN(balanceBefore.add(expectedTokens))
})
it('should not add any tokens after one year', async () => {
let startTimestamp = await rewardSwap.startTimestamp()
let currentTimestamp = startTimestamp.add(DURATION)
await rewardSwap.setTimestamp(currentTimestamp)
let tornVirtualBalance = await rewardSwap.tornVirtualBalance()
tornVirtualBalance.should.be.eq.BN(miningCap)
const formulaReturn = tornadoFormula({ balance: tornVirtualBalance, amount })
const expectedTokens = await rewardSwap.getExpectedReturn(amount)
expectedTokens.sub(formulaReturn).should.be.lte.BN(delta)
currentTimestamp = currentTimestamp.add(MONTH)
await rewardSwap.setTimestamp(currentTimestamp)
tornVirtualBalance = await rewardSwap.tornVirtualBalance()
tornVirtualBalance.should.be.eq.BN(miningCap)
})
})
afterEach(async () => {
await revertSnapshot(snapshotId.result)
// eslint-disable-next-line require-atomic-updates
snapshotId = await takeSnapshot()
})
})