staking-pool/test/stakingPoolDAO_spec.js

395 lines
16 KiB
JavaScript

// /*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
StakingPool = await StakingPoolDAO.deploy({ arguments: [SNT.options.address, 30, 20, 0, 10, 0, 0, 0] }).send();
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
for(let i = 0; i < 30; i++){
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);
}
await assert.reverts(toSend, {from: richard}, "Returned error: VM Exception while processing transaction: revert Proposal voting has already ended");
});
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();
const result = await StakingPool.methods.proposalStatus(proposalId).call();
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});
// 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);
}
let result = await StakingPool.methods.proposalStatus(proposalId).call();
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");
result = await StakingPool.methods.proposalStatus(proposalId).call();
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");
});
it("requires a minimum participation to execute a proposal", async () => {
// Change minimum participation
await StakingPool.methods.setMinimumParticipation("5000").send();
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});
});
});
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);
});
});
});