Skip to content

Commit

Permalink
Merge pull request #372 from ParadigmFoundation/refactor/adding-voter…
Browse files Browse the repository at this point in the history
…-proxy

Voting Proxy
Freydal authored Nov 8, 2019
2 parents e7374a5 + ac48ed5 commit d7532db
Showing 8 changed files with 386 additions and 67 deletions.
14 changes: 12 additions & 2 deletions packages/kosu-solidity-tests/test/treasury.ts
Original file line number Diff line number Diff line change
@@ -502,8 +502,8 @@ describe("Treasury", async () => {
const salt = new BigNumber("42");
const vote1 = new BigNumber("1");
const vote2 = new BigNumber("2");
const secret1 = soliditySha3({ t: "uint", v: new BigNumber("1") }, { t: "uint", v: salt });
const secret2 = soliditySha3({ t: "uint", v: new BigNumber("2") }, { t: "uint", v: salt });
const secret1 = soliditySha3({ t: "uint", v: "1" }, { t: "uint", v: salt.toString() });
const secret2 = soliditySha3({ t: "uint", v: "2" }, { t: "uint", v: salt.toString() });

it("should lock a validator after exit", async () => {
await testHelpers.prepareListing("0x010203", {
@@ -595,4 +595,14 @@ describe("Treasury", async () => {
.should.eventually.eq((blockNumber + 4 + 10).toString());
});
});

describe("authorization", () => {
it("should authorize a proxy", async () => {
await treasury.isProxyFor.callAsync(accounts[0], accounts[1]).should.eventually.be.false;
await treasury.authorizeProxy.awaitTransactionSuccessAsync(accounts[1]);
await treasury.isProxyFor.callAsync(accounts[0], accounts[1]).should.eventually.be.true;
await treasury.deauthorizeProxy.awaitTransactionSuccessAsync(accounts[1]);
await treasury.isProxyFor.callAsync(accounts[0], accounts[1]).should.eventually.be.false;
});
});
});
81 changes: 79 additions & 2 deletions packages/kosu-solidity-tests/test/voting.ts
Original file line number Diff line number Diff line change
@@ -31,8 +31,8 @@ describe("Voting", () => {
const vote2 = new BigNumber("2");
const block1 = vote1;
const block2 = vote2;
const secret1 = soliditySha3({ t: "uint", v: new BigNumber("1") }, { t: "uint", v: salt });
const secret2 = soliditySha3({ t: "uint", v: new BigNumber("2") }, { t: "uint", v: salt });
const secret1 = soliditySha3({ t: "uint", v: "1" }, { t: "uint", v: salt.toString() });
const secret2 = soliditySha3({ t: "uint", v: "2" }, { t: "uint", v: salt.toString() });

before(async () => {
voting = contracts.voting;
@@ -112,6 +112,28 @@ describe("Voting", () => {
});
});

describe("commitProxyVote", () => {
let pollId;
beforeEach(async () => {
await treasury.authorizeProxy.awaitTransactionSuccessAsync(accounts[1]);
pollId = await shortPoll();
});

afterEach(async () => {
await treasury.deauthorizeProxy.awaitTransactionSuccessAsync(accounts[1]);
});

it("should allow a user to commit a proxy vote", async () => {
await voting.commitProxyVote.awaitTransactionSuccessAsync(
pollId,
accounts[0],
secret1,
TestValues.fiveEther,
{ from: accounts[1] },
).should.eventually.be.fulfilled;
});
});

describe("revealVote", () => {
let pollId;
beforeEach(async () => {
@@ -164,6 +186,31 @@ describe("Voting", () => {
});
});

describe("revealProxyVote", () => {
let pollId;
beforeEach(async () => {
await treasury.authorizeProxy.awaitTransactionSuccessAsync(accounts[1]);
pollId = await shortPoll();
});

afterEach(async () => {
await treasury.deauthorizeProxy.awaitTransactionSuccessAsync(accounts[1]);
});

it("should allow a user to reveal a proxy vote", async () => {
await voting.commitProxyVote.awaitTransactionSuccessAsync(
pollId,
accounts[0],
secret1,
TestValues.fiveEther,
{ from: accounts[1] },
).should.eventually.be.fulfilled;
await voting.revealProxyVote.awaitTransactionSuccessAsync(pollId, accounts[0], vote1, salt, {
from: accounts[1],
}).should.eventually.be.fulfilled;
});
});

describe("winningOption", () => {
it("should report the correct winningOption", async () => {
await kosuToken.transfer.awaitTransactionSuccessAsync(accounts[1], TestValues.fiveEther);
@@ -186,6 +233,36 @@ describe("Voting", () => {
.should.eventually.eq("2");
});

it("should report the correct winningOption for proxy votes", async () => {
await treasury.authorizeProxy.awaitTransactionSuccessAsync(accounts[1]);
await kosuToken.transfer.awaitTransactionSuccessAsync(accounts[1], TestValues.fiveEther);
await testHelpers.prepareTokens(accounts[0], TestValues.fiveEther);
await testHelpers.prepareTokens(accounts[1], TestValues.fiveEther);
const { pollId } = await testHelpers.variablePoll(2, 2);
await voting.commitProxyVote.awaitTransactionSuccessAsync(
pollId,
accounts[0],
secret1,
TestValues.oneEther,
{ from: accounts[1] },
).should.eventually.be.fulfilled;
await voting.commitVote.awaitTransactionSuccessAsync(pollId, secret2, TestValues.fiveEther, {
from: accounts[1],
}).should.eventually.be.fulfilled;
await voting.revealProxyVote.awaitTransactionSuccessAsync(pollId, accounts[0], vote1, salt, {
from: accounts[1],
}).should.eventually.be.fulfilled;
await voting.revealVote.awaitTransactionSuccessAsync(pollId, vote2, salt, { from: accounts[1] }).should
.eventually.be.fulfilled;

await testHelpers.skipBlocks(new BigNumber(1));
await voting.winningOption
.callAsync(pollId)
.then(x => x.toString())
.should.eventually.eq("2");
await treasury.deauthorizeProxy.awaitTransactionSuccessAsync(accounts[1]);
});

it("should report the first winning option in a tie", async () => {
await kosuToken.transfer.awaitTransactionSuccessAsync(accounts[1], TestValues.fiveEther);
await testHelpers.prepareTokens(accounts[0], TestValues.fiveEther);
107 changes: 80 additions & 27 deletions packages/kosu-system-contracts/contracts/treasury/Treasury.sol
Original file line number Diff line number Diff line change
@@ -12,18 +12,23 @@ contract Treasury is Authorizable {
using SafeMath for uint;

IVoting voting;
KosuToken public kosuToken;

struct TokenLock {
uint value;
uint pollId;
uint tokenLockEnd;
uint pollEarlyUnlock;
}
struct Account {
uint currentBalance;
uint systemBalance;
TokenLock[] tokenLocks;
mapping(address => bool) proxies;
}

mapping(address => Account) private accounts;

KosuToken public kosuToken;
mapping(address => uint) private currentBalances;
mapping(address => uint) private systemBalances;
mapping(address => TokenLock[]) private addressTokenLocks;

/** @dev Initializes the treasury with the kosuToken and authorizedAddresses contracts.
@notice Initializes the treasury with the kosuToken and authorizedAddresses contracts.
@@ -160,20 +165,40 @@ contract Treasury is Authorizable {
setSystemBalance(account, getSystemBalance(account).sub(amount));
}

/** @dev Allows voting contract to register a poll to ensure tokens aren't removed.
@notice Allows voting contract to register a poll to ensure tokens aren't removed.
/** @dev Allows voting contract to register a vote in a poll to ensure tokens aren't removed.
@notice Allows voting contract to register a vote in a poll to ensure tokens aren't removed.
@param account The account voting.
@param pollId The poll the account is voting on.
@param amount Number of tokens contributed.
@param endBlock Block number vote token lock should expire.
@param losingEndBlock Block number vote token lock should expire if vote was in support of a losing option.
*/
function registerVote(address account, uint pollId, uint amount, uint endBlock, uint losingEndBlock) isAuthorized public returns (bool) {
if(systemBalances[account] < amount) {
if(getSystemBalance(account) < amount) {
return false;
}

accounts[account].tokenLocks.push(TokenLock(amount, pollId, endBlock, losingEndBlock));

return true;
}

/** @dev Allows voting contract to register a vote in a poll from a proxy to ensure tokens aren't removed.
@notice Allows voting contract to register a vote in a poll from a proxy to ensure tokens aren't removed.
@param account The account voting.
@param pollId The poll the account is voting on.
@param amount Number of tokens contributed.
@param endBlock Block number vote token lock should expire.
@param losingEndBlock Block number vote token lock should expire if vote was in support of a losing option.
*/
function registerProxyVote(address proxy, address account, uint pollId, uint amount, uint endBlock, uint losingEndBlock) isAuthorized public returns (bool) {
if(getSystemBalance(account) < amount) {
return false;
}

addressTokenLocks[account].push(TokenLock(amount, pollId, endBlock, losingEndBlock));
require(isProxyFor(account, proxy), "not valid proxy");

accounts[account].tokenLocks.push(TokenLock(amount, pollId, endBlock, losingEndBlock));

return true;
}
@@ -185,7 +210,7 @@ contract Treasury is Authorizable {
@param endBlock The end of the lock.
*/
function validatorLock(address account, uint amount, uint endBlock) isAuthorized public {
addressTokenLocks[account].push(TokenLock(
accounts[account].tokenLocks.push(TokenLock(
amount,
0,
endBlock,
@@ -200,9 +225,9 @@ contract Treasury is Authorizable {
*/
function tokenLocksExpire(address account) public returns (uint lastBlock) {
_removeInactiveTokenLocks(msg.sender);
for(uint i = addressTokenLocks[account].length; i > 0; i--) {
if(addressTokenLocks[account][i - 1].tokenLockEnd > lastBlock) {
lastBlock = addressTokenLocks[account][i - 1].tokenLockEnd;
for(uint i = accounts[account].tokenLocks.length; i > 0; i--) {
if(accounts[account].tokenLocks[i - 1].tokenLockEnd > lastBlock) {
lastBlock = accounts[account].tokenLocks[i - 1].tokenLockEnd;
}
}
return lastBlock;
@@ -226,6 +251,34 @@ contract Treasury is Authorizable {
return getCurrentBalance(account);
}

/** @dev Authorize voting proxy for treasury balances.
@notice Authorize voting proxy for treasury balances.
@param proxy Address to allow to vote in proxy.
*/
function authorizeProxy(address proxy) public {
accounts[msg.sender].proxies[proxy] = true;
}

/** @dev Remove authorization of voting proxy for treasury balance.
@notice Remove authorization of voting proxy for treasury balance.
@param proxy Address to remove permission to vote in proxy.
*/
function deauthorizeProxy(address proxy) public {
accounts[msg.sender].proxies[proxy] = false;
}

/** @dev Check proxy authorization for account and proxy addresses.
@notice Check proxy authorization for account and proxy addresses.
@param account User account with tokens.
@param proxy Address to check proxy status.
*/
function isProxyFor(address account, address proxy) public view returns (bool) {
return accounts[account].proxies[proxy];
}

// INTERNAL
/** @dev Bonds tokens by passing the transaction value to the bonding functions of the KosuToken then provides the added balance to the provided account address.
*/
@@ -262,20 +315,20 @@ contract Treasury is Authorizable {
/** @dev Removes expired locks.
*/
function _removeInactiveTokenLocks(address account) internal {
for(uint i = addressTokenLocks[account].length; i > 0; i--) {
TokenLock storage v = addressTokenLocks[account][i - 1];
for(uint i = accounts[account].tokenLocks.length; i > 0; i--) {
TokenLock storage v = accounts[account].tokenLocks[i - 1];
(bool finished, uint winningTokens) = voting.userWinningTokens(v.pollId, msg.sender);
if(v.pollId > 0 && finished && winningTokens == 0) {
v.tokenLockEnd = v.pollEarlyUnlock;
}
if(v.tokenLockEnd < block.number) {
if(i == addressTokenLocks[account].length) {
delete addressTokenLocks[account][i - 1];
addressTokenLocks[account].length = addressTokenLocks[account].length - 1;
if(i == accounts[account].tokenLocks.length) {
delete accounts[account].tokenLocks[i - 1];
accounts[account].tokenLocks.length = accounts[account].tokenLocks.length - 1;
} else {
addressTokenLocks[account][i - 1] = addressTokenLocks[account][addressTokenLocks[account].length - 1];
delete addressTokenLocks[account][addressTokenLocks[account].length - 1];
addressTokenLocks[account].length = addressTokenLocks[account].length - 1;
accounts[account].tokenLocks[i - 1] = accounts[account].tokenLocks[accounts[account].tokenLocks.length - 1];
delete accounts[account].tokenLocks[accounts[account].tokenLocks.length - 1];
accounts[account].tokenLocks.length = accounts[account].tokenLocks.length - 1;
}
}
}
@@ -289,10 +342,10 @@ contract Treasury is Authorizable {
//Updates the systemBalance for the account
uint totalLocked;
uint maxVoteLock;
for(uint i = 0; i < addressTokenLocks[account].length; i++) {
TokenLock storage lock = addressTokenLocks[account][i];
for(uint i = 0; i < accounts[account].tokenLocks.length; i++) {
TokenLock storage lock = accounts[account].tokenLocks[i];
if(lock.pollId == 0) {
totalLocked = totalLocked.add(addressTokenLocks[account][i].value);
totalLocked = totalLocked.add(accounts[account].tokenLocks[i].value);
} else {
if (lock.value > maxVoteLock) {
maxVoteLock = lock.value;
@@ -305,26 +358,26 @@ contract Treasury is Authorizable {
*/
function getSystemBalance(address account) internal view returns (uint) {
//Reports the systemBalance for the account
return systemBalances[account];
return accounts[account].systemBalance;
}

/** @dev Sets the accounts system balance.
*/
function setSystemBalance(address account, uint amount) internal {
systemBalances[account] = amount;
accounts[account].systemBalance = amount;
}

/** @dev Reads the accounts current balance.
*/
function getCurrentBalance(address account) internal view returns (uint) {
//Reports the held balance for the account
return currentBalances[account];
return accounts[account].currentBalance;
}

/** @dev Sets the accounts current balance.
*/
function setCurrentBalance(address account, uint amount) internal {
//Updates the held balance for the account
currentBalances[account] = amount;
accounts[account].currentBalance = amount;
}
}
114 changes: 78 additions & 36 deletions packages/kosu-system-contracts/contracts/voting/Voting.sol
Original file line number Diff line number Diff line change
@@ -93,22 +93,23 @@ contract Voting is IVoting {
@param _tokensToCommit Number of tokens to commit to vote.
*/
function commitVote(uint _pollId, bytes32 _vote, uint _tokensToCommit) public {
//load Poll and Vote
Poll storage p = polls[_pollId];
Vote storage v = p.votes[msg.sender];

//Ensure commit phase hasn't ended, the user has not committed and has adequate balance in the treasury
require(block.number <= p.commitEndBlock, "commit has ended");
require(!p.didCommit[msg.sender], "address committed");
require(treasury.registerVote(msg.sender, _pollId, _tokensToCommit, p.winnerLockEnd, p.loserLockEnd), "insufficient tokens");
require(_tokensToCommit > 0, "must commit tokens to lock");
_commitVote(_pollId, msg.sender, _vote, _tokensToCommit);
}

//Set the tokens committed hidden vote data
v.tokensCommitted = _tokensToCommit;
v.hiddenVote = _vote;

//Track voter address and set did commit.
p.didCommit[msg.sender] = true;
/** @dev Commit a vote in a poll for another address to be later revealed. The salt and option must be retained for a successful reveal.
@notice Commit a vote in a poll for another address to be later revealed. The salt and option must be retained for a successful reveal.
@param _pollId Poll id to commit vote to.
@param _tokenHolder Address to submit commit in proxy of.
@param _vote Hash encoded vote option with salt.
@param _tokensToCommit Number of tokens to commit to vote.
*/
function commitProxyVote(uint _pollId, address _tokenHolder, bytes32 _vote, uint _tokensToCommit) public {
Poll storage p = polls[_pollId];
require(treasury.registerProxyVote(msg.sender, _tokenHolder, _pollId, _tokensToCommit, p.winnerLockEnd, p.loserLockEnd), "insufficient tokens");
_commitVote(_pollId, _tokenHolder, _vote, _tokensToCommit);
}

/** @dev Reveal a previously committed vote by providing the vote option and salt used to generate the vote hash.
@@ -118,32 +119,20 @@ contract Voting is IVoting {
@param _voteSalt Salt used to generate vote hash.
*/
function revealVote(uint _pollId, uint _voteOption, uint _voteSalt) public {
Poll storage p = polls[_pollId];
Vote storage v = p.votes[msg.sender];

// Ensure commit phase has passed, reveal phase has not. User has commited but not revealed. User has adequate balance in the treasury.
require(block.number > p.commitEndBlock, "commit hasn't ended");
require(block.number <= p.revealEndBlock, "reveal has ended");
require(p.didCommit[msg.sender], "address hasn't committed");
require(!p.didReveal[msg.sender], "address has revealed");

// Calculate and compare the commited vote
bytes32 exposedVote = keccak256(abi.encodePacked(_voteOption, _voteSalt));
require(v.hiddenVote == exposedVote, "vote doesn't match");

// Store info from a valid revealed vote. Remove the pending vote.
v.salt = _voteSalt;
v.voteOption = _voteOption;
p.didReveal[msg.sender] = true;
p.voteValues[_voteOption] = p.voteValues[_voteOption].add(v.tokensCommitted);
p.totalRevealedTokens = p.totalRevealedTokens.add(v.tokensCommitted);
_revealVote(_pollId, msg.sender, _voteOption, _voteSalt);
}

// Update winner and tracking
if(p.currentLeadingOption != _voteOption && p.voteValues[_voteOption] > p.leadingTokens) {
p.currentLeadingOption = _voteOption;
}
/** @dev Reveal a previously committed vote for another address by providing the vote option and salt used to generate the vote hash.
@notice Reveal a previously committed vote for another address by providing the vote option and salt used to generate the vote hash.
@param _pollId Poll id to commit vote to.
@param _tokenHolder Address to submit reveal in proxy of.
@param _voteOption Vote option used to generate vote hash.
@param _voteSalt Salt used to generate vote hash.
*/
function revealProxyVote(uint _pollId, address _tokenHolder, uint _voteOption, uint _voteSalt) public {
require(treasury.isProxyFor(_tokenHolder, msg.sender), "not valid proxy");

p.leadingTokens = p.voteValues[p.currentLeadingOption];
_revealVote(_pollId, _tokenHolder, _voteOption, _voteSalt);
}

/** @dev Retrieve the winning option for a finalized poll.
@@ -195,4 +184,57 @@ contract Voting is IVoting {

return (true, tokens);
}

// INTERNAL

/** @dev Commit votes
*/
function _commitVote(uint _pollId, address _tokenHolder, bytes32 _vote, uint _tokensToCommit) internal {
//load Poll and Vote
Poll storage p = polls[_pollId];
Vote storage v = p.votes[_tokenHolder];

//Ensure commit phase hasn't ended, the user has not committed and has adequate balance in the treasury
require(block.number <= p.commitEndBlock, "commit has ended");
require(!p.didCommit[_tokenHolder], "address committed");
require(_tokensToCommit > 0, "must commit tokens to lock");

//Set the tokens committed hidden vote data
v.tokensCommitted = _tokensToCommit;
v.hiddenVote = _vote;

//Track voter address and set did commit.
p.didCommit[_tokenHolder] = true;
}

/** @dev Reveal vote
*/
function _revealVote(uint _pollId, address _tokenHolder, uint _voteOption, uint _voteSalt) internal {
Poll storage p = polls[_pollId];
Vote storage v = p.votes[_tokenHolder];

// Ensure commit phase has passed, reveal phase has not. User has commited but not revealed. User has adequate balance in the treasury.
require(block.number > p.commitEndBlock, "commit hasn't ended");
require(block.number <= p.revealEndBlock, "reveal has ended");
require(p.didCommit[_tokenHolder], "address hasn't committed");
require(!p.didReveal[_tokenHolder], "address has revealed");

// Calculate and compare the commited vote
bytes32 exposedVote = keccak256(abi.encodePacked(_voteOption, _voteSalt));
require(v.hiddenVote == exposedVote, "vote doesn't match");

// Store info from a valid revealed vote. Remove the pending vote.
v.salt = _voteSalt;
v.voteOption = _voteOption;
p.didReveal[_tokenHolder] = true;
p.voteValues[_voteOption] = p.voteValues[_voteOption].add(v.tokensCommitted);
p.totalRevealedTokens = p.totalRevealedTokens.add(v.tokensCommitted);

// Update winner and tracking
if(p.currentLeadingOption != _voteOption && p.voteValues[_voteOption] > p.leadingTokens) {
p.currentLeadingOption = _voteOption;
}

p.leadingTokens = p.voteValues[p.currentLeadingOption];
}
}
34 changes: 34 additions & 0 deletions packages/kosu-wrapper-enhancements/src/Treasury.ts
Original file line number Diff line number Diff line change
@@ -126,6 +126,40 @@ export class Treasury {
return contract.withdraw.awaitTransactionSuccessAsync(new BigNumber(value.toString()));
}

/**
* Authorize an address to vote in proxy
*
* @param _proxy The address to be added as a proxy
* @returns The decoded transaction receipt, after the TX is mined in a block.
*/
public async authorizeProxy(_proxy: string): Promise<TransactionReceiptWithDecodedLogs> {
const contract = await this.getContract();
return contract.authorizeProxy.awaitTransactionSuccessAsync(_proxy);
}

/**
* Remove an address's authorization to vote in proxy
*
* @param _proxy The address to be removed as a proxy
* @returns The decoded transaction receipt, after the TX is mined in a block.
*/
public async deauthorizeProxy(_proxy: string): Promise<TransactionReceiptWithDecodedLogs> {
const contract = await this.getContract();
return contract.deauthorizeProxy.awaitTransactionSuccessAsync(_proxy);
}

/**
* Checks if an address is a valid proxy for another address
*
* @param _account The balance holder
* @param _proxy The possible voting proxy
* @returns The boolean status of proxy voting permission
*/
public async isProxyFor(_account: string, _proxy: string): Promise<boolean> {
const contract = await this.getContract();
return contract.isProxyFor.callAsync(_account, _proxy);
}

/**
* Read the total system balance of KOSU for a provided `address` string.
*
54 changes: 54 additions & 0 deletions packages/kosu-wrapper-enhancements/src/Voting.ts
Original file line number Diff line number Diff line change
@@ -82,6 +82,37 @@ export class Voting {
);
}

/**
* Commits proxy vote to voting contract
*
* @param _pollId uint poll index
* @param _account ethereum address vote will be commited for
* @param _vote encoded vote option
* @param _tokensToCommit uint number of tokens to be commited to vote
*/
public async commitProxyVote(
_pollId: BigNumber,
_account: string,
_vote: string,
_tokensToCommit: BigNumber,
): Promise<TransactionReceiptWithDecodedLogs> {
const contract = await this.getContract();

const systemBalance = await this.treasury.systemBalance(_account);
if (systemBalance.lt(_tokensToCommit)) {
throw new Error(`${_account} doesn't have sufficient tokens for a proxy vote.`);
}

// tslint:disable-next-line: no-console
console.log(`Committing proxy vote for ${_account} of ${_vote} with ${_tokensToCommit} DIGM tokens`);
return contract.commitProxyVote.awaitTransactionSuccessAsync(
new BigNumber(_pollId.toString()),
_account,
_vote,
new BigNumber(_tokensToCommit.toString()),
);
}

/**
* Reveals vote on voting contract
*
@@ -102,6 +133,29 @@ export class Voting {
);
}

/**
* Reveals vote on voting contract
*
* @param _pollId uint poll index
* @param _account ethereum address vote will be revealed for
* @param _voteOption uint representation of vote position
* @param _voteSalt uint salt used to encode vote option
*/
public async revealProxyVote(
_pollId: BigNumber,
_account: string,
_voteOption: BigNumber,
_voteSalt: BigNumber,
): Promise<TransactionReceiptWithDecodedLogs> {
const contract = await this.getContract();
return contract.revealProxyVote.awaitTransactionSuccessAsync(
new BigNumber(_pollId.toString()),
_account,
new BigNumber(_voteOption.toString()),
new BigNumber(_voteSalt.toString()),
);
}

/**
* Reads the winning option for poll
*
10 changes: 10 additions & 0 deletions packages/kosu-wrapper-enhancements/test/treasury_test.ts
Original file line number Diff line number Diff line change
@@ -21,4 +21,14 @@ describe("Treasury", () => {
await kosuToken.releaseTokens(difference);
});
});

describe("proxy authorization", () => {
it("should authorize and deauthorize", async () => {
await treasury.isProxyFor(accounts[0], accounts[1]).should.eventually.eq(false);
await treasury.authorizeProxy(accounts[1]);
await treasury.isProxyFor(accounts[0], accounts[1]).should.eventually.eq(true);
await treasury.deauthorizeProxy(accounts[1]);
await treasury.isProxyFor(accounts[0], accounts[1]).should.eventually.eq(false);
});
});
});
39 changes: 39 additions & 0 deletions packages/kosu-wrapper-enhancements/test/voting_test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { encodeVote } from "@kosu/utils";

import { KosuToken, Treasury, Voting } from "../src";
import { BigNumber } from "bignumber.js";

describe("Voting", () => {
let kosuToken: KosuToken;
@@ -42,4 +43,42 @@ describe("Voting", () => {
.then(x => x.toString())
.should.eventually.eq(voteValue.toString());
});

it("should interact with a Voting poll as proxy", async () => {
const voteValue = TestValues.oneWei;
const voteSalt = TestValues.fiveEther;
const encodedVote = encodeVote(voteValue, voteSalt);
await kosuToken.transfer(accounts[1], TestValues.oneEther);
const t = await treasury.getContract();
const kt = await kosuToken.getContract();
await kt.approve.awaitTransactionSuccessAsync(t.address, TestValues.oneEther, { from: accounts[1] });
await t.deposit.awaitTransactionSuccessAsync(TestValues.oneEther, { from: accounts[1] });
await t.authorizeProxy.sendTransactionAsync(accounts[0], { from: accounts[1] });

const { blockNumber, pollId } = await testHelpers.variablePoll(10, 10);
const commitEnd = blockNumber + 10;
const revealEnd = blockNumber + 21;
await voting.commitProxyVote(pollId, accounts[1], encodedVote, TestValues.oneWei).should.be.fulfilled;
await testHelpers.skipTo(commitEnd);
await voting.revealProxyVote(pollId, accounts[1], voteValue, voteSalt);
await testHelpers.skipTo(revealEnd);
await voting
.winningOption(pollId)
.then(x => x.toString())
.should.eventually.eq(voteValue.toString());
await voting
.totalWinningTokens(pollId)
.then(x => x.toString())
.should.eventually.eq(voteValue.toString());
await voting
.totalRevealedTokens(pollId)
.then(x => x.toString())
.should.eventually.eq(voteValue.toString());
await voting
.userWinningTokens(pollId, accounts[1])
.then(x => x.toString())
.should.eventually.eq(voteValue.toString());

await t.deauthorizeProxy.callAsync(accounts[0], { from: accounts[1] });
});
});

0 comments on commit d7532db

Please sign in to comment.