Skip to content

Commit c116dac

Browse files
committed
Add VotingToken
1 parent 3d95fc8 commit c116dac

File tree

9 files changed

+1387
-11
lines changed

9 files changed

+1387
-11
lines changed

contracts/colony/ColonyAuthority.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ contract ColonyAuthority is CommonAuthority {
9797
addRoleCapability(ROOT_ROLE, "deprecateExtension(bytes32,bool)");
9898
addRoleCapability(ROOT_ROLE, "uninstallExtension(bytes32)");
9999
addRoleCapability(ROOT_ROLE, "makeArbitraryTransaction(address,bytes)");
100+
addRoleCapability(ROOT_ROLE, "lockToken()");
101+
addRoleCapability(ROOT_ROLE, "unlockTokenForUser(address,uint256)");
100102
addRoleCapability(ARBITRATION_ROLE, "transferStake(uint256,uint256,address,address,uint256,uint256,address)");
101103
addRoleCapability(ARBITRATION_ROLE, "emitDomainReputationPenalty(uint256,uint256,uint256,address,int256)");
102104
addRoleCapability(ARBITRATION_ROLE, "emitSkillReputationPenalty(uint256,address,int256)");

contracts/colony/ColonyFunding.sol

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ import "./ColonyStorage.sol";
2323

2424

2525
contract ColonyFunding is ColonyStorage, PatriciaTreeProofs { // ignore-swc-123
26+
function lockToken() public stoppable auth returns (uint256) {
27+
return ITokenLocking(tokenLockingAddress).lockToken(token);
28+
}
29+
30+
function unlockTokenForUser(address _user, uint256 _lockId) public stoppable auth {
31+
ITokenLocking(tokenLockingAddress).unlockTokenForUser(token, _user, _lockId);
32+
}
33+
2634
function setTaskManagerPayout(uint256 _id, address _token, uint256 _amount) public stoppable self {
2735
setTaskPayout(_id, TaskRole.Manager, _token, _amount);
2836
emit TaskPayoutSet(_id, TaskRole.Manager, _token, _amount);
@@ -290,8 +298,7 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs { // ignore-swc-123
290298
function startNextRewardPayout(address _token, bytes memory key, bytes memory value, uint256 branchMask, bytes32[] memory siblings)
291299
public stoppable auth
292300
{
293-
ITokenLocking tokenLocking = ITokenLocking(tokenLockingAddress);
294-
uint256 totalLockCount = tokenLocking.lockToken(token);
301+
uint256 totalLockCount = lockToken();
295302
uint256 thisPayoutAmount = sub(fundingPots[0].balance[_token], pendingRewardPayments[_token]);
296303
require(thisPayoutAmount > 0, "colony-reward-payout-no-rewards");
297304
pendingRewardPayments[_token] = add(pendingRewardPayments[_token], thisPayoutAmount);
@@ -459,7 +466,7 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs { // ignore-swc-123
459466

460467
uint256 reward = (mul(squareRoots[4], squareRoots[6]) / squareRoots[5]) ** 2;
461468

462-
tokenLocking.unlockTokenForUser(token, msg.sender, payoutId);
469+
unlockTokenForUser(msg.sender, payoutId);
463470

464471
return (payout.tokenAddress, reward);
465472
}

contracts/colony/IColony.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,14 @@ interface IColony is ColonyDataTypes, IRecovery {
215215
/// @param _wad Amount to mint
216216
function mintTokensFor(address _guy, uint256 _wad) external;
217217

218+
/// @notice Lock the colony's token. Secured function to authorised members.
219+
function lockToken() external returns (uint256);
220+
221+
/// @notice Unlock the colony's token for a user. Secured function to authorised members.
222+
/// @param user The user to unlock
223+
/// @param lockId The specific lock to unlock
224+
function unlockTokenForUser(address user, uint256 lockId) external;
225+
218226
/// @notice Register colony's ENS label.
219227
/// @param colonyName The label to register.
220228
/// @param orbitdb The path of the orbitDB database associated with the colony name

contracts/extensions/VotingBase.sol

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ abstract contract VotingBase is ColonyExtension, PatriciaTreeProofs {
203203

204204
function getInfluence(uint256 _motionId, address _user) public view virtual returns (uint256);
205205

206+
function postReveal(uint256 _motionId, address _user) internal virtual;
207+
208+
function postClaim(uint256 _motionId, address _user) internal virtual;
209+
206210
// Public functions (interface)
207211

208212
/// @notice Stake on a motion
@@ -299,11 +303,11 @@ abstract contract VotingBase is ColonyExtension, PatriciaTreeProofs {
299303
require(getMotionState(_motionId) == MotionState.Submit, "voting-base-motion-not-open");
300304
require(_voteSecret != bytes32(0), "voting-base-invalid-secret");
301305

302-
uint256 userRep = getInfluence(_motionId, msg.sender);
306+
uint256 influence = getInfluence(_motionId, msg.sender);
303307

304308
// Count reputation if first submission
305309
if (voteSecrets[_motionId][msg.sender] == bytes32(0)) {
306-
motion.totalVotes = add(motion.totalVotes, userRep);
310+
motion.totalVotes = add(motion.totalVotes, influence);
307311
}
308312

309313
voteSecrets[_motionId][msg.sender] = _voteSecret;
@@ -345,6 +349,7 @@ abstract contract VotingBase is ColonyExtension, PatriciaTreeProofs {
345349

346350
emit MotionEventSet(_motionId, REVEAL_END);
347351
}
352+
348353
tokenLocking.transfer(token, voterReward, msg.sender, true);
349354
}
350355

@@ -423,6 +428,8 @@ abstract contract VotingBase is ColonyExtension, PatriciaTreeProofs {
423428
require(stakerReward > 0, "voting-base-nothing-to-claim");
424429
delete stakes[_motionId][_staker][_vote];
425430

431+
postClaim(_motionId, _staker);
432+
426433
tokenLocking.transfer(token, stakerReward, _staker, true);
427434

428435
if (repPenalty > 0) {
@@ -694,7 +701,7 @@ abstract contract VotingBase is ColonyExtension, PatriciaTreeProofs {
694701
return keccak256(abi.encodePacked(_salt, _vote));
695702
}
696703

697-
function getRequiredStake(uint256 _motionId) internal view returns (uint256) {
704+
function getRequiredStake(uint256 _motionId) public view returns (uint256) {
698705
return wmul(motions[_motionId].maxVotes, totalStakeFraction);
699706
}
700707

contracts/extensions/VotingReputation.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ contract VotingReputation is VotingBase {
6262
return influences[motion.rootHash][motion.skillId][_user];
6363
}
6464

65+
function postReveal(uint256 _motionId, address _user) internal override {}
66+
function postClaim(uint256 _motionId, address _user) internal override {}
67+
6568
/// @notice Create a motion in the root domain
6669
/// @param _altTarget The contract to which we send the action (0x0 for the colony)
6770
/// @param _action A bytes array encoding a function call

contracts/extensions/VotingToken.sol

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,18 @@ contract VotingToken is VotingBase {
3838
// [motionId][user] => tokenBalance
3939
mapping (uint256 => mapping (address => uint256)) influences;
4040

41+
// [motionId] => lockId
42+
mapping (uint256 => uint256) locks;
43+
4144
// Public
4245

4346
function setInfluence(uint256 _motionId) public {
47+
require(
48+
getMotionState(_motionId) == MotionState.Staking ||
49+
getMotionState(_motionId) == MotionState.Submit,
50+
"voting-token-cannot-set-influence"
51+
);
52+
4453
uint256 balance = tokenLocking.getUserLock(token, msg.sender).balance;
4554
influences[_motionId][msg.sender] = balance;
4655
}
@@ -50,6 +59,19 @@ contract VotingToken is VotingBase {
5059
return influences[_motionId][_user];
5160
}
5261

62+
function postReveal(uint256 _motionId, address _user) internal override {
63+
colony.unlockTokenForUser(_user, locks[_motionId]);
64+
}
65+
66+
function postClaim(uint256 _motionId, address _user) internal override {
67+
uint256 lockCount = tokenLocking.getUserLock(token, _user).lockCount;
68+
69+
// Lock may have already been released during reveal
70+
if (lockCount < locks[_motionId]) {
71+
colony.unlockTokenForUser(_user, locks[_motionId]);
72+
}
73+
}
74+
5375
/// @notice Create a motion in the root domain
5476
/// @param _altTarget The contract to which we send the action (0x0 for the colony)
5577
/// @param _action A bytes array encoding a function call
@@ -58,6 +80,11 @@ contract VotingToken is VotingBase {
5880
{
5981
createMotion(_altTarget, _action, 1);
6082
motions[motionCount].maxVotes = ERC20Extended(token).totalSupply();
83+
locks[motionCount] = colony.lockToken();
84+
}
85+
86+
function getLock(uint256 _motionId) public view returns (uint256) {
87+
return locks[_motionId];
6188
}
6289

6390
}

docs/_Interface_IColony.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,18 @@ Install an extension to the colony. Secured function to authorised members.
963963
|version|uint256|The new extension version to install
964964

965965

966+
### `lockToken`
967+
968+
Lock the colony's token. Secured function to authorised members.
969+
970+
971+
972+
**Return Parameters**
973+
974+
|Name|Type|Description|
975+
|---|---|---|
976+
|uint256|uint256|
977+
966978
### `makeArbitraryTransaction`
967979

968980
Execute arbitrary transaction on behalf of the Colony
@@ -1648,6 +1660,19 @@ Uninstall an extension from a colony. Secured function to authorised members.
16481660
|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier
16491661

16501662

1663+
### `unlockTokenForUser`
1664+
1665+
Unlock the colony's token for a user. Secured function to authorised members.
1666+
1667+
1668+
**Parameters**
1669+
1670+
|Name|Type|Description|
1671+
|---|---|---|
1672+
|user|address|The user to unlock
1673+
|lockId|uint256|The specific lock to unlock
1674+
1675+
16511676
### `updateColonyOrbitDB`
16521677

16531678
Update a colony's orbitdb address. Can only be called by a colony with a registered subdomain

test/extensions/voting-rep.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,10 +1047,10 @@ contract("Voting Reputation", (accounts) => {
10471047
});
10481048

10491049
it("can take an action with an arbitrary target", async () => {
1050-
const { colony: otherColony } = await setupRandomColony(colonyNetwork);
1051-
await token.mint(otherColony.address, WAD, { from: USER0 });
1050+
const { colony: otherColony, token: otherToken } = await setupRandomColony(colonyNetwork);
1051+
await otherToken.mint(otherColony.address, WAD);
10521052

1053-
const action = await encodeTxData(colony, "claimColonyFunds", [token.address]);
1053+
const action = await encodeTxData(colony, "claimColonyFunds", [otherToken.address]);
10541054
await voting.createRootMotion(otherColony.address, action, domain1Key, domain1Value, domain1Mask, domain1Siblings);
10551055
motionId = await voting.getMotionCount();
10561056

@@ -1060,12 +1060,12 @@ contract("Voting Reputation", (accounts) => {
10601060

10611061
await forwardTime(STAKE_PERIOD, this);
10621062

1063-
const balanceBefore = await otherColony.getFundingPotBalance(1, token.address);
1063+
const balanceBefore = await otherColony.getFundingPotBalance(1, otherToken.address);
10641064
expect(balanceBefore).to.be.zero;
10651065

10661066
await voting.finalizeMotion(motionId);
10671067

1068-
const balanceAfter = await otherColony.getFundingPotBalance(1, token.address);
1068+
const balanceAfter = await otherColony.getFundingPotBalance(1, otherToken.address);
10691069
expect(balanceAfter).to.eq.BN(WAD);
10701070
});
10711071

0 commit comments

Comments
 (0)