Skip to content

Commit

Permalink
Test DAO / Test Token
Browse files Browse the repository at this point in the history
  • Loading branch information
protinam committed Dec 6, 2017
1 parent abbbd29 commit 0c07955
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 48 deletions.
27 changes: 27 additions & 0 deletions contracts/TestDAO.sol
@@ -0,0 +1,27 @@
/*
<< Test DAO (since we need a token that we can send around in tests) >>
*/

pragma solidity ^0.4.18;

import "./dao/DelegatedShareholderAssociation.sol";

/**
* @title TestDAO
* @author Project Wyvern Developers
*
*
*/
contract TestDAO is DelegatedShareholderAssociation {

string public constant name = "Test DAO";

function TestDAO (ERC20 sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) public {
sharesTokenAddress = sharesAddress;
minimumQuorum = minimumSharesToPassAVote;
debatingPeriodInMinutes = minutesForDebate;
}

}
33 changes: 33 additions & 0 deletions contracts/TestToken.sol
@@ -0,0 +1,33 @@
/*
<< Test Token (for use with the Test DAO) >>
*/

pragma solidity ^0.4.18;

import "zeppelin-solidity/contracts/token/StandardToken.sol";

/**
* @title TestToken
* @author Project Wyvern Developers
*
*
*/
contract TestToken is StandardToken {

uint constant public decimals = 18;
string constant public name = "Test Token";
string constant public symbol = "TST";

uint constant public MINT_AMOUNT = 20000000 * (10 ** decimals);

/**
* @dev Initialize the test token
*/
function TestToken () public {
balances[msg.sender] = MINT_AMOUNT;
totalSupply = MINT_AMOUNT;
}

}
6 changes: 6 additions & 0 deletions contracts/WyvernDAO.sol
Expand Up @@ -8,6 +8,12 @@ pragma solidity ^0.4.18;

import "./dao/DelegatedShareholderAssociation.sol";

/**
* @title WyvernDAO
* @author Project Wyvern Developers
*
*
*/
contract WyvernDAO is DelegatedShareholderAssociation {

string public constant name = "Project Wyvern DAO";
Expand Down
28 changes: 14 additions & 14 deletions contracts/dao/DelegatedShareholderAssociation.sol
Expand Up @@ -49,7 +49,7 @@ contract DelegatedShareholderAssociation is TokenRecipient {
mapping (address => uint) public delegatedAmountsByDelegate;
uint public totalLockedTokens;

event ProposalAdded(uint proposalID, address recipient, uint amount, string description);
event ProposalAdded(uint proposalID, address recipient, uint amount, bytes metadataHash);
event Voted(uint proposalID, bool position, address voter);
event ProposalTallied(uint proposalID, uint result, uint quorum, bool active);
event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, address newSharesTokenAddress);
Expand All @@ -60,7 +60,7 @@ contract DelegatedShareholderAssociation is TokenRecipient {
struct Proposal {
address recipient;
uint amount;
string description;
bytes metadataHash;
uint votingDeadline;
bool executed;
bool proposalPassed;
Expand All @@ -77,7 +77,7 @@ contract DelegatedShareholderAssociation is TokenRecipient {

/* Only shareholders can execute a function with this modifier. */
modifier onlyShareholders {
require(sharesTokenAddress.balanceOf(msg.sender) > 0);
require(ERC20(sharesTokenAddress).balanceOf(msg.sender) > 0);
_;
}

Expand Down Expand Up @@ -105,7 +105,7 @@ contract DelegatedShareholderAssociation is TokenRecipient {
* @param tokensToLock number of tokens to be locked (sending address must have at least this many tokens)
* @param delegate the address to which votes equal to the number of tokens locked will be delegated
*/
function setDelegateAndLockTokens(uint tokensToLock, address delegate) onlyUndelegated public {
function setDelegateAndLockTokens(uint tokensToLock, address delegate) public onlyShareholders onlyUndelegated {
require(ERC20(sharesTokenAddress).transferFrom(msg.sender, address(this), tokensToLock));
lockedDelegatingTokens[msg.sender] = tokensToLock;
delegatedAmountsByDelegate[delegate] = tokensToLock;
Expand Down Expand Up @@ -136,7 +136,7 @@ contract DelegatedShareholderAssociation is TokenRecipient {
* @param minimumSharesToPassAVote proposal can vote only if the sum of shares held by all voters exceed this number
* @param minutesForDebate the minimum amount of delay between when a proposal is made and when it can be executed
*/
function changeVotingRules(uint minimumSharesToPassAVote, uint minutesForDebate) onlySelf public {
function changeVotingRules(uint minimumSharesToPassAVote, uint minutesForDebate) public onlySelf {
if (minimumSharesToPassAVote == 0 ) {
minimumSharesToPassAVote = 1;
}
Expand All @@ -148,17 +148,17 @@ contract DelegatedShareholderAssociation is TokenRecipient {
/**
* Add Proposal
*
* Propose to send `weiAmount / 1e18` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code.
* Propose to send `weiAmount / 1e18` ether to `beneficiary` for `jobMetadataHash`. `transactionBytecode ? Contains : Does not contain` code.
*
* @param beneficiary who to send the ether to
* @param weiAmount amount of ether to send, in wei
* @param jobDescription Description of job
* @param jobMetadataHash Hash of job metadata (IPFS)
* @param transactionBytecode bytecode of transaction
*/
function newProposal(
address beneficiary,
uint weiAmount,
string jobDescription,
bytes jobMetadataHash,
bytes transactionBytecode
)
public
Expand All @@ -169,13 +169,13 @@ contract DelegatedShareholderAssociation is TokenRecipient {
Proposal storage p = proposals[proposalID];
p.recipient = beneficiary;
p.amount = weiAmount;
p.description = jobDescription;
p.metadataHash = jobMetadataHash;
p.proposalHash = keccak256(beneficiary, weiAmount, transactionBytecode);
p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes;
p.executed = false;
p.proposalPassed = false;
p.numberOfVotes = 0;
ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription);
ProposalAdded(proposalID, beneficiary, weiAmount, jobMetadataHash);
numProposals = proposalID+1;

return proposalID;
Expand All @@ -184,25 +184,25 @@ contract DelegatedShareholderAssociation is TokenRecipient {
/**
* Add proposal in Ether
*
* Propose to send `etherAmount` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code.
* Propose to send `etherAmount` ether to `beneficiary` for `jobMetadataHash`. `transactionBytecode ? Contains : Does not contain` code.
* This is a convenience function to use if the amount to be given is in round number of ether units.
*
* @param beneficiary who to send the ether to
* @param etherAmount amount of ether to send
* @param jobDescription Description of job
* @param jobMetadataHash Hash of job metadata (IPFS)
* @param transactionBytecode bytecode of transaction
*/
function newProposalInEther(
address beneficiary,
uint etherAmount,
string jobDescription,
bytes jobMetadataHash,
bytes transactionBytecode
)
public
onlyShareholders
returns (uint proposalID)
{
return newProposal(beneficiary, etherAmount * 1 ether, jobDescription, transactionBytecode);
return newProposal(beneficiary, etherAmount * 1 ether, jobMetadataHash, transactionBytecode);
}

/**
Expand Down
13 changes: 13 additions & 0 deletions migrations/2_deploy_test_token_and_dao.js
@@ -0,0 +1,13 @@
/* global artifacts: false */

const TestToken = artifacts.require('./TestToken.sol')
const TestDAO = artifacts.require('./TestDAO.sol')

module.exports = (deployer, network) => {
if (network === 'development') {
deployer.deploy(TestToken)
.then(() => {
return deployer.deploy(TestDAO, TestToken.address, Math.pow(10, 18) * 1000000, 60 * 24 * 7)
})
}
}
File renamed without changes.
38 changes: 38 additions & 0 deletions test/test-dao.js
@@ -0,0 +1,38 @@
/* global artifacts:false, it:false, contract:false, assert:false */

const TestDAO = artifacts.require('TestDAO')
const TestToken = artifacts.require('TestToken')

const BigNumber = require('bignumber.js')

contract('TestDAO', (accounts) => {
it('should not allow delegation of more shares than owned', () => {
return TestDAO
.deployed()
.then(daoInstance => {
return daoInstance.setDelegateAndLockTokens.call((new BigNumber(Math.pow(10, 18 + 7)).mul(3)), accounts[1])
})
.then(ret => {
assert.equal(true, false, 'Delegation was allowed without shares')
})
.catch(err => {
assert.equal(err.message, 'VM Exception while processing transaction: revert', 'Incorrect error')
})
})

it('should allow share delegation after token allowance', () => {
const amount = new BigNumber(Math.pow(10, 18 + 7))
return TestDAO
.deployed()
.then(daoInstance => {
return TestToken
.deployed()
.then(tokenInstance => {
return tokenInstance.approve.sendTransaction(daoInstance.address, amount)
})
.then(() => {
return daoInstance.setDelegateAndLockTokens.call(amount, accounts[1])
})
})
})
})
18 changes: 18 additions & 0 deletions test/test-token.js
@@ -0,0 +1,18 @@
/* global artifacts:false, it:false, contract:false, assert:false */

const TestToken = artifacts.require('TestToken')

const BigNumber = require('bignumber.js')

contract('TestToken', (accounts) => {
it('should set correct balance', () => {
return TestToken
.deployed()
.then(tokenInstance => {
return tokenInstance.balanceOf.call(accounts[0])
})
.then(amount => {
assert.equal(amount.equals(new BigNumber(2 * Math.pow(10, 18 + 7))), true, 'Incorrect amount')
})
})
})
20 changes: 18 additions & 2 deletions test/dao.js → test/wyvern-dao.js
Expand Up @@ -23,16 +23,32 @@ contract('WyvernDAO', (accounts) => {
})
})

it('should have the right address', () => {
return WyvernDAO
.deployed()
.then(daoInstance => {
return WyvernToken
.deployed()
.then(tokenInstance => {
return daoInstance.sharesTokenAddress.call()
.then(address => {
assert.equal(address, tokenInstance.address, 'Incorrect token address')
})
})
})
})

it('should not allow release twice', () => {
return WyvernToken
.deployed()
.then(tokenInstance => {
return tokenInstance.releaseTokens.call(tokenInstance.address)
return tokenInstance.releaseTokens.sendTransaction(tokenInstance.address)
})
.then(() => {
assert.equal(true, false, 'Tokens were released twice!')
})
.catch(() => {
.catch(err => {
assert.equal(err.message, 'VM Exception while processing transaction: revert', 'Incorrect error')
})
})
})
51 changes: 19 additions & 32 deletions test/token.js → test/wyvern-token.js
Expand Up @@ -117,7 +117,7 @@ contract('WyvernToken', (accounts) => {
})
})

it('should credit valid UTXO', () => {
it('should credit valid UTXO, with correct amount, only once', () => {
const utxo = utxoSet[35997]
const hash = hashUTXO(utxo)
const proof = utxoMerkleTree.getHexProof(Buffer.from(hash.slice(2), 'hex'))
Expand All @@ -133,35 +133,22 @@ contract('WyvernToken', (accounts) => {
.deployed()
.then(instance => {
return instance.redeemUTXO.call('0x' + utxo.txid, utxo.outputIndex, utxo.satoshis, proof, pubKey, keyPair.compressed, v, r, s)
})
.then(amount => {
amount = amount.toNumber()
assert.equal(amount, utxo.satoshis * Math.pow(10, 11), 'UTXO was not credited correctly!')
})
})

it('should credit valid UTXO only once', () => {
const utxo = utxoSet[35997]
const hash = hashUTXO(utxo)
const proof = utxoMerkleTree.getHexProof(Buffer.from(hash.slice(2), 'hex'))
const keyPair = bitcoin.ECPair.fromWIF('WsUAyHvNaCyEcK8bFvzENF8wQe9zumSpJQbqMjmkwtDeYo4cqVsp', network)
const ethAddr = accounts[0].slice(2)
const hashBuf = bitcoin.crypto.sha256(Buffer.from(ethAddr, 'hex'))
var { r, s, v } = ecsign(hashBuf, keyPair.d.toBuffer())
r = '0x' + r.toString('hex')
s = '0x' + s.toString('hex')
const pubKey = '0x' + keyPair.Q.affineX.toBuffer(32).toString('hex') + keyPair.Q.affineY.toBuffer(32).toString('hex')

return WyvernToken
.deployed()
.then(instance => {
return instance.redeemUTXO.call('0x' + utxo.txid, utxo.outputIndex, utxo.satoshis, proof, pubKey, keyPair.compressed, v, r, s)
})
.then(() => {
assert.equal(false, true, 'UTXO was credited twice!')
})
.catch(() => {
assert.equal(true, true, 'Error not thrown')
.then(amount => {
amount = amount.toNumber()
assert.equal(amount, utxo.satoshis * Math.pow(10, 11), 'UTXO was not credited correctly!')
})
.then(() => {
return instance.redeemUTXO.sendTransaction('0x' + utxo.txid, utxo.outputIndex, utxo.satoshis, proof, pubKey, keyPair.compressed, v, r, s)
})
.then(() => {
return instance.redeemUTXO.call('0x' + utxo.txid, utxo.outputIndex, utxo.satoshis, proof, pubKey, keyPair.compressed, v, r, s)
.then(() => {
assert.equal(false, true, 'UTXO was credited twice!')
})
.catch(err => {
assert.equal(err.message, 'VM Exception while processing transaction: revert', 'Incorrect error')
})
})
})
})

Expand All @@ -183,8 +170,8 @@ contract('WyvernToken', (accounts) => {
return instance.redeemUTXO.call('0x' + utxo.txid, utxo.outputIndex + 1, utxo.satoshis, proof, pubKey, keyPair.compressed, v, r, s)
.then(amount => {
assert.equal(false, true, 'UTXO was credited!')
}).catch(() => {
assert.equal(true, true, 'Error not thrown')
}).catch(err => {
assert.equal(err.message, 'VM Exception while processing transaction: revert', 'Incorrect error')
})
})
})
Expand Down

0 comments on commit 0c07955

Please sign in to comment.