2020-03-30 15:38:07 +00:00
// /*global contract, config, it, assert, artifacts*/
let StakingPoolDAO = artifacts . require ( 'StakingPoolDAO' ) ;
const SNT = artifacts . require ( 'SNT' ) ;
let iuri , jonathan , richard , michael , pascal , eric , andre ;
const VoteStatus = {
NONE : 0 ,
YES : 1 ,
NO : 2
} ;
// For documentation please see https://embark.status.im/docs/contracts_testing.html
config ( {
contracts : {
deploy :
{
"MiniMeToken" : { "deploy" : false } ,
"MiniMeTokenFactory" : { } ,
"SNT" : {
"instanceOf" : "MiniMeToken" ,
"args" : [
"$MiniMeTokenFactory" ,
"0x0000000000000000000000000000000000000000" ,
0 ,
"TestMiniMeToken" ,
18 ,
"STT" ,
true
]
} ,
"StakingPoolDAO" : {
"deploy" : false ,
}
}
}
} , ( _err , accounts ) => {
iuri = accounts [ 0 ] ;
jonathan = accounts [ 1 ] ;
richard = accounts [ 2 ] ;
pascal = accounts [ 3 ] ;
michael = accounts [ 4 ] ;
eric = accounts [ 5 ] ;
andre = accounts [ 6 ] ;
} ) ;
// TODO: add asserts for balances
let StakingPool ;
contract ( "StakingPoolDAO" , function ( ) {
this . timeout ( 0 ) ;
before ( async ( ) => {
// distribute SNT
await SNT . methods . generateTokens ( iuri , "10000000000" ) . send ( { from : iuri } ) ;
await SNT . methods . generateTokens ( jonathan , "10000000000" ) . send ( { from : iuri } ) ;
await SNT . methods . generateTokens ( richard , "10000000000" ) . send ( { from : iuri } ) ;
await SNT . methods . generateTokens ( pascal , "10000000000" ) . send ( { from : iuri } ) ;
await SNT . methods . generateTokens ( michael , "10000000000" ) . send ( { from : iuri } ) ;
await SNT . methods . generateTokens ( eric , "10000000000" ) . send ( { from : iuri } ) ;
// Deploy Staking Pool
2020-04-13 14:04:26 +00:00
StakingPool = await StakingPoolDAO . deploy ( { arguments : [ SNT . options . address , 30 , 20 , 0 , 10 , 0 , 0 , 0 ] } ) . send ( ) ;
2020-03-30 15:38:07 +00:00
const encodedCall = StakingPool . methods . stake ( "10000000000" ) . encodeABI ( ) ;
await web3 . eth . sendTransaction ( { from : iuri , to : StakingPool . options . address , value : "100000000000000000" } ) ;
await SNT . methods . approveAndCall ( StakingPool . options . address , "10000000000" , encodedCall ) . send ( { from : iuri } ) ;
await SNT . methods . approveAndCall ( StakingPool . options . address , "10000000000" , encodedCall ) . send ( { from : jonathan } ) ;
await SNT . methods . approveAndCall ( StakingPool . options . address , "10000000000" , encodedCall ) . send ( { from : richard } ) ;
await SNT . methods . approveAndCall ( StakingPool . options . address , "10000000000" , encodedCall ) . send ( { from : pascal } ) ;
await SNT . methods . approveAndCall ( StakingPool . options . address , "10000000000" , encodedCall ) . send ( { from : michael } ) ;
await SNT . methods . approveAndCall ( StakingPool . options . address , "10000000000" , encodedCall ) . send ( { from : eric } ) ;
// Mine 100 blocks
2020-04-13 14:04:26 +00:00
for ( let i = 0 ; i < 30 ; i ++ ) {
2020-03-30 15:38:07 +00:00
await mineAtTimestamp ( 12345678 ) ;
}
} )
describe ( "proposal creation" , ( ) => {
it ( "non token holders can not submit proposals" , async ( ) => {
const toSend = StakingPool . methods . addProposal ( andre , 1 , "0x" , "0x" ) ;
await assert . reverts ( toSend , { from : andre } , "Returned error: VM Exception while processing transaction: revert Token balance is required to perform this operation" ) ;
} ) ;
it ( "token holders can create proposals" , async ( ) => {
const receipt = await StakingPool . methods . addProposal ( richard , 1 , "0x" , "0x" ) . send ( { from : richard } ) ;
assert . eventEmitted ( receipt , 'NewProposal' ) ;
} ) ;
} ) ;
describe ( "voting" , ( ) => {
before ( async ( ) => {
const balance = await StakingPool . methods . balanceOf ( jonathan ) . call ( ) ;
await StakingPool . methods . transfer ( eric , balance ) . send ( { from : jonathan } ) ;
} ) ;
let proposalId ;
beforeEach ( async ( ) => {
const receipt = await StakingPool . methods . addProposal ( richard , 1 , "0x" , "0x" ) . send ( { from : richard } ) ;
proposalId = receipt . events . NewProposal . returnValues . proposalId ;
} ) ;
it ( "only those having balance can vote on a proposal" , async ( ) => {
const toSend = StakingPool . methods . vote ( proposalId , true ) ;
await assert . reverts ( toSend , { from : jonathan } , "Returned error: VM Exception while processing transaction: revert Not enough tokens at the moment of proposal creation" ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : eric } ) ;
} ) ;
it ( "accounts can vote more than once and results are affected accordingly" , async ( ) => {
let votesY = await StakingPool . methods . votes ( proposalId , true ) . call ( ) ;
let votesN = await StakingPool . methods . votes ( proposalId , true ) . call ( ) ;
let myVote = await StakingPool . methods . voteOf ( richard , proposalId ) . call ( ) ;
assert . strictEqual ( votesY , "0" ) ;
assert . strictEqual ( votesN , "0" ) ;
let receipt = await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
votesY = await StakingPool . methods . votes ( proposalId , true ) . call ( ) ;
votesN = await StakingPool . methods . votes ( proposalId , false ) . call ( ) ;
assert . strictEqual ( votesY , "10000000000" ) ;
assert . strictEqual ( votesN , "0" ) ;
receipt = await StakingPool . methods . vote ( proposalId , false ) . send ( { from : richard } ) ;
votesY = await StakingPool . methods . votes ( proposalId , true ) . call ( ) ;
votesN = await StakingPool . methods . votes ( proposalId , false ) . call ( ) ;
assert . strictEqual ( votesY , "0" ) ;
assert . strictEqual ( votesN , "10000000000" ) ;
} ) ;
it ( "voting is only valid during the period it is active" , async ( ) => {
const toSend = await StakingPool . methods . vote ( proposalId , false ) ;
await toSend . send ( { from : richard } ) ;
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
2020-04-13 14:04:26 +00:00
await assert . reverts ( toSend , { from : richard } , "Returned error: VM Exception while processing transaction: revert Proposal voting has already ended" ) ;
2020-03-30 15:38:07 +00:00
} ) ;
it ( "check that vote result matches what was voted" , async ( ) => {
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : eric } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : michael } ) ;
await StakingPool . methods . vote ( proposalId , false ) . send ( { from : pascal } ) ;
await StakingPool . methods . vote ( proposalId , false ) . send ( { from : richard } ) ;
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
let votesY = await StakingPool . methods . votes ( proposalId , true ) . call ( ) ;
let votesN = await StakingPool . methods . votes ( proposalId , false ) . call ( ) ;
2020-04-13 14:04:26 +00:00
const result = await StakingPool . methods . proposalStatus ( proposalId ) . call ( ) ;
2020-03-30 15:38:07 +00:00
assert . strictEqual ( votesY , "30000000000" ) ;
assert . strictEqual ( votesN , "20000000000" ) ;
assert . strictEqual ( result . approved , true ) ;
assert . strictEqual ( result . executed , false ) ;
} ) ;
} ) ;
describe ( "proposal execution" , ( ) => {
let proposalId ;
beforeEach ( async ( ) => {
const receipt = await StakingPool . methods . addProposal ( "0x00000000000000000000000000000000000000AA" , 12345 , "0x" , "0x" ) . send ( { from : richard } ) ;
proposalId = receipt . events . NewProposal . returnValues . proposalId ;
} ) ;
it ( "active voting proposals cant be executed" , async ( ) => {
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
const toSend = StakingPool . methods . executeTransaction ( proposalId ) ;
await assert . reverts ( toSend , { from : richard } , "Returned error: VM Exception while processing transaction: revert Voting is still active" ) ;
} ) ;
it ( "unapproved proposals cant be executed" , async ( ) => {
await StakingPool . methods . vote ( proposalId , false ) . send ( { from : richard } ) ;
2020-04-13 14:04:26 +00:00
2020-03-30 15:38:07 +00:00
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
const toSend = StakingPool . methods . executeTransaction ( proposalId ) ;
await assert . reverts ( toSend , { from : iuri } , "Returned error: VM Exception while processing transaction: revert Proposal wasn't approved" ) ;
} ) ;
it ( "approved proposals can be executed" , async ( ) => {
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
2020-04-13 14:04:26 +00:00
let result = await StakingPool . methods . proposalStatus ( proposalId ) . call ( ) ;
2020-03-30 15:38:07 +00:00
assert . strictEqual ( result . approved , true ) ;
assert . strictEqual ( result . executed , false ) ;
const receipt = await StakingPool . methods . executeTransaction ( proposalId ) . send ( { from : iuri } ) ;
const destinationBalance = await web3 . eth . getBalance ( "0x00000000000000000000000000000000000000AA" ) ;
assert . strictEqual ( destinationBalance , "12345" ) ;
2020-04-13 14:04:26 +00:00
result = await StakingPool . methods . proposalStatus ( proposalId ) . call ( ) ;
2020-03-30 15:38:07 +00:00
assert . strictEqual ( result . executed , true ) ;
} ) ;
it ( "approved proposals can't be executed twice" , async ( ) => {
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
await StakingPool . methods . executeTransaction ( proposalId ) . send ( { from : iuri } ) ;
await assert . reverts ( StakingPool . methods . executeTransaction ( proposalId ) , { from : iuri } , "Returned error: VM Exception while processing transaction: revert Proposal already executed" ) ;
} ) ;
it ( "approved proposals can't be executed after they expire" , async ( ) => {
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
// Mine 40 blocks
for ( let i = 0 ; i < 40 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
await assert . reverts ( StakingPool . methods . executeTransaction ( proposalId ) , { from : iuri } , "Returned error: VM Exception while processing transaction: revert Proposal is already expired" ) ;
} ) ;
it ( "proposals can execute contract functions" , async ( ) => {
const initialBalance = await SNT . methods . balanceOf ( "0xAA000000000000000000000000000000000000AA" ) . call ( ) ;
assert . strictEqual ( initialBalance , "0" ) ;
const encodedCall = SNT . methods . transfer ( "0xAA000000000000000000000000000000000000AA" , "12345" ) . encodeABI ( ) ;
const receipt = await StakingPool . methods . addProposal ( SNT . options . address , 0 , encodedCall , "0x" ) . send ( { from : richard } ) ;
proposalId = receipt . events . NewProposal . returnValues . proposalId ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
await StakingPool . methods . executeTransaction ( proposalId ) . send ( { from : iuri } ) ;
const finalBalance = await SNT . methods . balanceOf ( "0xAA000000000000000000000000000000000000AA" ) . call ( ) ;
assert . strictEqual ( finalBalance , "12345" ) ;
} ) ;
2020-03-30 15:54:08 +00:00
it ( "requires a minimum participation to execute a proposal" , async ( ) => {
2020-03-30 15:49:51 +00:00
// Change minimum participation
2020-03-30 15:54:08 +00:00
await StakingPool . methods . setMinimumParticipation ( "5000" ) . send ( ) ;
2020-03-30 15:49:51 +00:00
const encodedCall = SNT . methods . transfer ( "0xAA000000000000000000000000000000000000BB" , "12345" ) . encodeABI ( ) ;
const receipt = await StakingPool . methods . addProposal ( SNT . options . address , 0 , encodedCall , "0x" ) . send ( { from : richard } ) ;
proposalId = receipt . events . NewProposal . returnValues . proposalId ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
await assert . reverts ( StakingPool . methods . executeTransaction ( proposalId ) , { from : iuri } , "Returned error: VM Exception while processing transaction: revert Did not meet the minimum required participation" ) ;
} ) ;
it ( "proposal can be executed if it meets the minimum participation" , async ( ) => {
const encodedCall = SNT . methods . transfer ( "0xAA000000000000000000000000000000000000BB" , "12345" ) . encodeABI ( ) ;
const receipt = await StakingPool . methods . addProposal ( SNT . options . address , 0 , encodedCall , "0x" ) . send ( { from : richard } ) ;
proposalId = receipt . events . NewProposal . returnValues . proposalId ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : iuri } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : pascal } ) ;
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
await StakingPool . methods . executeTransaction ( proposalId ) . send ( { from : iuri } ) ;
} ) ;
} ) ;
2020-04-13 14:04:26 +00:00
describe ( "proposal cancelation" , ( ) => {
let proposalId ;
before ( async ( ) => {
// Setting values
await StakingPool . methods . setProposalCancelLength ( "10" ) . send ( ) ;
await StakingPool . methods . setMinimumParticipationForCancel ( "5000" ) . send ( ) ;
await StakingPool . methods . setMinimumCancelApprovalPercentage ( "7500" ) . send ( ) ;
} ) ;
beforeEach ( async ( ) => {
const receipt = await StakingPool . methods . addProposal ( "0x00000000000000000000000000000000000000CC" , 12345 , "0x" , "0x" ) . send ( { from : richard } ) ;
proposalId = receipt . events . NewProposal . returnValues . proposalId ;
} ) ;
it ( "can not execute a proposal that is still in the cancelation period" , async ( ) => {
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : iuri } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : pascal } ) ;
// Mine 25 blocks
for ( let i = 0 ; i < 25 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
const toSend = StakingPool . methods . executeTransaction ( proposalId ) ;
await assert . reverts ( toSend , { from : richard } , "Returned error: VM Exception while processing transaction: revert Voting is still active" ) ;
} ) ;
it ( "can execute a proposal with no cancelation votes" , async ( ) => {
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : iuri } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : pascal } ) ;
// Mine 30 blocks
for ( let i = 0 ; i < 30 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
await StakingPool . methods . executeTransaction ( proposalId ) . send ( { from : richard } ) ;
} ) ;
it ( "can not vote to cancel a proposal before the voting period for it is enabled" , async ( ) => {
const toSend = StakingPool . methods . cancel ( proposalId , true ) ;
await assert . reverts ( toSend , { from : richard } , "Returned error: VM Exception while processing transaction: revert Proposal cancel period has not started yet" ) ;
} ) ;
it ( "can execute a proposal that did not reach the required participation minimum" , async ( ) => {
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : iuri } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : pascal } ) ;
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
await StakingPool . methods . cancel ( proposalId , true ) . send ( { from : iuri } ) ;
await StakingPool . methods . cancel ( proposalId , true ) . send ( { from : richard } ) ;
for ( let i = 0 ; i < 10 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
await StakingPool . methods . executeTransaction ( proposalId ) . send ( { from : richard } ) ;
} ) ;
it ( "cannot execute a proposal that reach the required participation and cancel votes" , async ( ) => {
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : iuri } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : richard } ) ;
await StakingPool . methods . vote ( proposalId , true ) . send ( { from : pascal } ) ;
// Mine 20 blocks
for ( let i = 0 ; i < 20 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
await StakingPool . methods . cancel ( proposalId , true ) . send ( { from : iuri } ) ;
await StakingPool . methods . cancel ( proposalId , true ) . send ( { from : richard } ) ;
await StakingPool . methods . cancel ( proposalId , true ) . send ( { from : pascal } ) ;
for ( let i = 0 ; i < 10 ; i ++ ) {
await mineAtTimestamp ( 12345678 ) ;
}
const toSend = StakingPool . methods . executeTransaction ( proposalId ) ;
await assert . reverts ( toSend , { from : richard } , "Returned error: VM Exception while processing transaction: revert Proposal was canceled" ) ;
const result = await StakingPool . methods . proposalStatus ( proposalId ) . call ( ) ;
assert . strictEqual ( result . approved , true ) ;
assert . strictEqual ( result . canceled , true ) ;
assert . strictEqual ( result . executed , false ) ;
} ) ;
} ) ;
2020-03-30 15:38:07 +00:00
} ) ;