laconicd/tests-solidity/suites/staking/test/locking/funds_flows.js
Brett Sun c9639c3860
tests: add solidity test suites (#487)
* tests: add solidity test suite

* tests: remove require strings

* Update tests-solidity/init-test-node.sh

* Update tests-solidity/init-test-node.sh

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
2020-09-01 17:16:28 -04:00

309 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {
UserState,
approveAndStakeWithState,
approveStakeAndLockWithState,
unstakeWithState,
unlockWithState,
unlockFromManagerWithState,
transferWithState,
transferAndUnstakeWithState,
slashWithState,
slashAndUnstakeWithState,
slashFromContractWithState,
slashAndUnstakeFromContractWithState,
checkInvariants,
} = require('../helpers/helpers')(artifacts)
const { DEFAULT_STAKE_AMOUNT, DEFAULT_LOCK_AMOUNT, EMPTY_DATA, ZERO_ADDRESS } = require('../helpers/constants')
contract('Staking app, Locking funds flows', ([_, owner, user1, user2, user3]) => {
let staking, lockManager, users, managers, token
beforeEach(async () => {
const deployment = await deploy(owner)
staking = deployment.staking
lockManager = deployment.lockManager
token = deployment.token
// fund users and create user state objects
users = []
const userAddresses = [user1, user2, user3]
await Promise.all(userAddresses.map(async (userAddress, index) => {
const amount = DEFAULT_STAKE_AMOUNT.mul(bn(userAddresses.length - index))
users.push(new UserState(userAddress, amount))
await token.transfer(userAddress, amount, { from: owner })
}))
// managers
managers = users.reduce((result, user) => {
result.push(user.address)
return result
}, [])
managers.push(lockManager.address)
})
describe('same origin and destiny', () => {
context('when user hasnt staked', () => {
it('check invariants', async () => {
await checkInvariants({ staking, users, managers })
})
})
context('when user has staked', () => {
const stakeAmount = DEFAULT_STAKE_AMOUNT
beforeEach('stakes', async () => {
await checkInvariants({ staking, users, managers })
await approveAndStakeWithState({ staking, amount: stakeAmount, user: users[0] })
await checkInvariants({ staking, users, managers })
})
context('when user hasnt locked', () => {
it('unstakes half', async () => {
await unstakeWithState({ staking, unstakeAmount: stakeAmount.div(bn(2)), user: users[0] })
await checkInvariants({ staking, users, managers })
})
it('unstakes all', async () => {
await unstakeWithState({ staking, unstakeAmount: stakeAmount, user: users[0] })
await checkInvariants({ staking, users, managers })
})
})
context('when user has locked', () => {
const lockAmount = DEFAULT_LOCK_AMOUNT
const moveFunds = ({ isContract, canUnlock = false }) => {
let lockManagerAddress
beforeEach('stakes and locks', async () => {
lockManagerAddress = isContract ? lockManager.address : user3
if (isContract && canUnlock) {
await lockManager.setResult(true)
}
await checkInvariants({ staking, users, managers })
await approveStakeAndLockWithState({
staking,
manager: lockManagerAddress,
stakeAmount,
allowanceAmount: stakeAmount,
lockAmount,
user: users[0]
})
await checkInvariants({ staking, users, managers })
})
const unstake = async (unstakeAmount) => {
await unstakeWithState({ staking, unstakeAmount, user: users[0] })
await checkInvariants({ staking, users, managers })
}
it('unstakes remaining', async () => {
await unstake(stakeAmount.sub(lockAmount))
})
const unlockAndUnstake = async (unlockAmount) => {
await unlockWithState({ staking, managerAddress: lockManagerAddress, unlockAmount, user: users[0]})
await unstake(stakeAmount.sub(lockAmount.sub(unlockAmount)))
}
const unlockAndUnstakeFromManager = async (unlockAmount) => {
await unlockFromManagerWithState({ staking, lockManager, unlockAmount, user: users[0]})
await unstake(stakeAmount.sub(lockAmount.sub(unlockAmount)))
}
if (canUnlock) {
it('owner unlocks half and then unstakes', async () => {
await unlockAndUnstake(lockAmount.div(bn(2)))
})
it('owner unlocks all and then unstakes', async () => {
await unlockAndUnstake(lockAmount)
})
} else {
it('owner cannot unlock', async () => {
await assertRevert(staking.unlock(users[0].address, lockManagerAddress, bn(1), { from: users[0].address }))
})
if (isContract) {
it('manager unlocks half and then owner unstakes', async () => {
await unlockAndUnstakeFromManager(lockAmount.div(bn(2)))
})
it('manager unlocks all and then owner unstakes', async () => {
await unlockAndUnstakeFromManager(lockAmount)
})
}
}
}
context('when lock manager is EOA', () => {
moveFunds({ isContract: false, canUnlock: false })
})
context('when lock manager is contract', () => {
context('when lock manager allows to unlock', () => {
moveFunds({ isContract: true, canUnlock: true })
})
context('when lock manager doesnt allow to unlock', () => {
moveFunds({ isContract: true, canUnlock: false })
})
})
})
})
})
describe('different origin and destiny', () => {
context('when user hasnt staked', () => {
it('check invariants', async () => {
await checkInvariants({ staking, users, managers })
})
})
context('when user has staked', () => {
const stakeAmount = DEFAULT_STAKE_AMOUNT
beforeEach('stakes', async () => {
await checkInvariants({ staking, users, managers })
await approveAndStakeWithState({ staking, amount: stakeAmount, user: users[0] })
await checkInvariants({ staking, users, managers })
})
context('when user hasnt locked', () => {
context('to staking balance', () => {
it('transfers half', async () => {
await transferWithState({ staking, transferAmount: stakeAmount.div(bn(2)), userFrom: users[0], userTo: users[1] })
await checkInvariants({ staking, users, managers })
})
it('transfers all', async () => {
await transferWithState({ staking, transferAmount: stakeAmount, userFrom: users[0], userTo: users[1] })
await checkInvariants({ staking, users, managers })
})
})
context('to external wallet', () => {
it('transfers half', async () => {
await transferAndUnstakeWithState({ staking, transferAmount: stakeAmount.div(bn(2)), userFrom: users[0], userTo: users[1] })
await checkInvariants({ staking, users, managers })
})
it('transfers all', async () => {
await transferAndUnstakeWithState({ staking, transferAmount: stakeAmount, userFrom: users[0], userTo: users[1] })
await checkInvariants({ staking, users, managers })
})
})
})
context('when user has locked', () => {
const lockAmount = DEFAULT_LOCK_AMOUNT
const moveFunds = ({ isContract, canUnlock = false, toStaking }) => {
let lockManagerAddress
beforeEach('stakes and locks', async () => {
lockManagerAddress = isContract ? lockManager.address : user3
if (isContract && canUnlock) {
await lockManager.setResult(true)
}
await checkInvariants({ staking, users, managers })
await approveStakeAndLockWithState({
staking,
manager: lockManagerAddress,
stakeAmount,
allowanceAmount: stakeAmount,
lockAmount,
user: users[0]
})
await checkInvariants({ staking, users, managers })
})
const transfer = async (transferAmount) => {
await transferWithState({ staking, transferAmount, userFrom: users[0], userTo: users[1] })
await checkInvariants({ staking, users, managers })
}
it('transfers remaining', async () => {
await transfer(stakeAmount.sub(lockAmount))
})
const slashAndTransfer = async (slashAmount) => {
if (toStaking) {
await slashWithState({ staking, slashAmount, userFrom: users[0], userTo: users[1], managerAddress: lockManagerAddress })
} else {
await slashAndUnstakeWithState({ staking, slashAmount, userFrom: users[0], userTo: users[1], managerAddress: lockManagerAddress })
}
await transfer(stakeAmount.sub(lockAmount.sub(slashAmount)))
}
const slashAndTransferFromContract = async (slashAmount) => {
if (toStaking) {
await slashFromContractWithState({ staking, slashAmount, userFrom: users[0], userTo: users[1], lockManager })
} else {
await slashAndUnstakeFromContractWithState({ staking, slashAmount, userFrom: users[0], userTo: users[1], lockManager })
}
await transfer(stakeAmount.sub(lockAmount.sub(slashAmount)))
}
if (isContract) {
it('manager unlockes half and then owner transfers', async () => {
await slashAndTransferFromContract(lockAmount.div(bn(2)))
})
it('manager slashes all and then owner transfers', async () => {
await slashAndTransferFromContract(lockAmount)
})
} else {
it('manager slashes half and then transfers', async () => {
await slashAndTransfer(lockAmount.div(bn(2)))
})
it('manager slashes all and then transfers', async () => {
await slashAndTransfer(lockAmount)
})
}
}
context('when lock manager is EOA', () => {
context('to staking balance', () => {
moveFunds({ isContract: false, canUnlock: false, toStaking: true })
})
context('to external wallet', () => {
moveFunds({ isContract: false, canUnlock: false, toStaking: false })
})
})
context('when lock manager is contract', () => {
context('when lock manager allows to unlock', () => {
context('to staking balance', () => {
moveFunds({ isContract: true, canUnlock: true, toStaking: true })
})
context('to external wallet', () => {
moveFunds({ isContract: true, canUnlock: true, toStaking: false })
})
})
context('when lock manager doesnt allow to unlock', () => {
context('to staking balance', () => {
moveFunds({ isContract: true, canUnlock: false, toStaking: true })
})
context('to external wallet', () => {
moveFunds({ isContract: true, canUnlock: false, toStaking: false })
})
})
})
})
})
})
})