laconicd-deprecated/tests/solidity/suites/staking/test/staking.js
2023-03-14 11:18:42 +05:30

177 lines
8.5 KiB
JavaScript

const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow')
const { bn, assertBn, MAX_UINT64 } = require('@aragon/contract-helpers-test/numbers')
const { deploy } = require('./helpers/deploy')(artifacts)
const { approveAndStake } = require('./helpers/helpers')(artifacts)
const { DEFAULT_STAKE_AMOUNT, EMPTY_DATA } = require('./helpers/constants')
const { STAKING_ERRORS } = require('./helpers/errors')
const StakingMock = artifacts.require('StakingMock')
const StandardTokenMock = artifacts.require('StandardTokenMock')
const BadTokenMock = artifacts.require('BadTokenMock')
const getTokenBalance = async (token, account) => await token.balanceOf(account)
contract('Staking app', ([owner, other]) => {
let staking, token, stakingAddress, tokenAddress
beforeEach(async () => {
const initialAmount = DEFAULT_STAKE_AMOUNT.mul(bn(1000))
const tokenContract = await StandardTokenMock.new(owner, initialAmount)
token = tokenContract
tokenAddress = tokenContract.address
await token.mint(other, DEFAULT_STAKE_AMOUNT)
const stakingContract = await StakingMock.new(tokenAddress)
staking = stakingContract
stakingAddress = stakingContract.address
})
it('has correct initial state', async () => {
assert.equal(await staking.token(), tokenAddress, 'Token is wrong')
assert.equal((await staking.totalStaked()).valueOf(), 0, 'Initial total staked amount should be zero')
assert.equal(await staking.supportsHistory(), true, 'history support should match')
})
it('fails deploying if token is not a contract', async () => {
await assertRevert(StakingMock.new(owner)/*, STAKING_ERRORS.ERROR_TOKEN_NOT_CONTRACT */)
})
it('stakes', async () => {
const initialOwnerBalance = await getTokenBalance(token, owner)
const initialStakingBalance = await getTokenBalance(token, stakingAddress)
await approveAndStake({ staking, from: owner })
const finalOwnerBalance = await getTokenBalance(token, owner)
const finalStakingBalance = await getTokenBalance(token, stakingAddress)
assertBn(finalOwnerBalance, initialOwnerBalance.sub(bn(DEFAULT_STAKE_AMOUNT)), 'owner balance should match')
assertBn(finalStakingBalance, initialStakingBalance.add(bn(DEFAULT_STAKE_AMOUNT)), 'Staking app balance should match')
assertBn(await staking.totalStakedFor(owner), bn(DEFAULT_STAKE_AMOUNT), 'staked value should match')
// total stake
assertBn(await staking.totalStaked(), bn(DEFAULT_STAKE_AMOUNT), 'Total stake should match')
})
it('fails staking 0 amount', async () => {
await token.approve(stakingAddress, 1)
await assertRevert(staking.stake(0, EMPTY_DATA)/*, STAKING_ERRORS.ERROR_AMOUNT_ZERO */)
})
it('fails staking more than balance', async () => {
const balance = await getTokenBalance(token, owner)
const amount = balance.add(bn(1))
await token.approve(stakingAddress, amount)
await assertRevert(staking.stake(amount, EMPTY_DATA)/*, STAKING_ERRORS.ERROR_TOKEN_DEPOSIT */)
})
it('stakes for', async () => {
const initialOwnerBalance = await getTokenBalance(token, owner)
const initialOtherBalance = await getTokenBalance(token, other)
const initialStakingBalance = await getTokenBalance(token, stakingAddress)
// allow Staking app to move owner tokens
await token.approve(stakingAddress, DEFAULT_STAKE_AMOUNT)
// stake tokens
await staking.stakeFor(other, DEFAULT_STAKE_AMOUNT, EMPTY_DATA)
const finalOwnerBalance = await getTokenBalance(token, owner)
const finalOtherBalance = await getTokenBalance(token, other)
const finalStakingBalance = await getTokenBalance(token, stakingAddress)
assertBn(finalOwnerBalance, initialOwnerBalance.sub(bn(DEFAULT_STAKE_AMOUNT)), 'owner balance should match')
assertBn(finalOtherBalance, initialOtherBalance, 'other balance should match')
assertBn(finalStakingBalance, initialStakingBalance.add(bn(DEFAULT_STAKE_AMOUNT)), 'Staking app balance should match')
assertBn(await staking.totalStakedFor(owner), bn(0), 'staked value for owner should match')
assertBn(await staking.totalStakedFor(other), bn(DEFAULT_STAKE_AMOUNT), 'staked value for other should match')
})
it('unstakes', async () => {
const initialOwnerBalance = await getTokenBalance(token, owner)
const initialStakingBalance = await getTokenBalance(token, stakingAddress)
await approveAndStake({ staking, from: owner })
// unstake half of them
await staking.unstake(DEFAULT_STAKE_AMOUNT.div(bn(2)), EMPTY_DATA)
const finalOwnerBalance = await getTokenBalance(token, owner)
const finalStakingBalance = await getTokenBalance(token, stakingAddress)
assertBn(finalOwnerBalance, initialOwnerBalance.sub(bn(DEFAULT_STAKE_AMOUNT.div(bn(2)))), 'owner balance should match')
assertBn(finalStakingBalance, initialStakingBalance.add(bn(DEFAULT_STAKE_AMOUNT.div(bn(2)))), 'Staking app balance should match')
assertBn(await staking.totalStakedFor(owner), bn(DEFAULT_STAKE_AMOUNT.div(bn(2))), 'staked value should match')
})
it('fails unstaking 0 amount', async () => {
await approveAndStake({ staking, from: owner })
await assertRevert(staking.unstake(0, EMPTY_DATA)/*, STAKING_ERRORS.ERROR_AMOUNT_ZERO */)
})
it('fails unstaking more than staked', async () => {
await approveAndStake({ staking, from: owner })
await assertRevert(staking.unstake(DEFAULT_STAKE_AMOUNT.add(bn(1)), EMPTY_DATA)/*, STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE */)
})
context('History', async () => {
it('supports history', async () => {
assert.equal(await staking.supportsHistory(), true, 'It should support History')
})
it('has correct "last staked for"', async () => {
const blockNumber = await staking.getBlockNumberPublic()
const lastStaked = blockNumber.add(bn(5))
await staking.setBlockNumber(lastStaked)
await approveAndStake({ staking, from: owner })
assertBn(await staking.lastStakedFor(owner), lastStaked, 'Last staked for should match')
})
it('has correct "total staked for at"', async () => {
const beforeBlockNumber = await staking.getBlockNumberPublic()
const lastStaked = beforeBlockNumber.add(bn(5))
await staking.setBlockNumber(lastStaked)
await approveAndStake({ staking, from: owner })
assertBn(await staking.totalStakedForAt(owner, beforeBlockNumber), bn(0), 'Staked for at before staking should match')
assertBn(await staking.totalStakedForAt(owner, lastStaked), bn(DEFAULT_STAKE_AMOUNT), 'Staked for after staking should match')
})
it('has correct "total staked at"', async () => {
const beforeBlockNumber = await staking.getBlockNumberPublic()
const lastStaked = beforeBlockNumber.add(bn(5))
await staking.setBlockNumber(lastStaked)
await approveAndStake({ staking, from: owner })
await approveAndStake({ staking, from: other })
assertBn(await staking.totalStakedAt(beforeBlockNumber), bn(0), 'Staked for at before should match')
assertBn(await staking.totalStakedAt(lastStaked), bn(DEFAULT_STAKE_AMOUNT.mul(bn(2))), 'Staked for at after staking should match')
})
it('fails to call totalStakedForAt with block number greater than max uint64', async () => {
await assertRevert(staking.totalStakedForAt(owner, MAX_UINT64.add(bn(1)))/*, STAKING_ERRORS.ERROR_BLOCKNUMBER_TOO_BIG */)
})
it('fails to call totalStakedAt with block number greater than max uint64', async () => {
await assertRevert(staking.totalStakedAt(MAX_UINT64.add(bn(1)))/*, STAKING_ERRORS.ERROR_BLOCKNUMBER_TOO_BIG */)
})
})
context('Bad Token', async () => {
let badStaking, badStakingAddress, badToken, badTokenAddress
beforeEach(async () => {
const initialAmount = DEFAULT_STAKE_AMOUNT.mul(bn(1000))
const tokenContract = await BadTokenMock.new(owner, initialAmount)
badToken = tokenContract
badTokenAddress = tokenContract.address
await badToken.mint(other, DEFAULT_STAKE_AMOUNT)
const stakingContract = await StakingMock.new(badTokenAddress)
badStaking = stakingContract
badStakingAddress = stakingContract.address
})
it('fails unstaking because of bad token', async () => {
// allow Staking app to move owner tokens
await badToken.approve(badStakingAddress, DEFAULT_STAKE_AMOUNT, { from: owner })
// stake tokens
await badStaking.stake(DEFAULT_STAKE_AMOUNT, EMPTY_DATA, { from: owner })
// unstake half of them, fails on token transfer
await assertRevert(badStaking.unstake(DEFAULT_STAKE_AMOUNT.div(bn(2)), EMPTY_DATA)/*, STAKING_ERRORS.ERROR_TOKEN_TRANSFER */)
})
})
})