diff --git a/contracts/extensions/IColonyExtension.sol b/contracts/extensions/IColonyExtension.sol
new file mode 100644
index 0000000000..95dd0a289c
--- /dev/null
+++ b/contracts/extensions/IColonyExtension.sol
@@ -0,0 +1,36 @@
+/*
+ This file is part of The Colony Network.
+
+ The Colony Network is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The Colony Network is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with The Colony Network. If not, see .
+*/
+
+pragma solidity 0.7.3;
+pragma experimental ABIEncoderV2;
+
+interface IColonyExtension {
+
+ function identifier() external pure returns (bytes32);
+ function version() external pure virtual returns (uint256);
+ function install(address _colony) external virtual;
+ function finishUpgrade() external virtual;
+ function deprecate(bool _deprecated) external virtual;
+ function uninstall() external virtual;
+
+ function getCapabilityRoles(bytes4 _sig) external view virtual returns (bytes32);
+
+ function getDeprecated() external view returns (bool);
+
+ function getColony() external view returns(address);
+
+}
diff --git a/contracts/extensions/VotingReputation/IVotingReputation.sol b/contracts/extensions/VotingReputation/IVotingReputation.sol
new file mode 100644
index 0000000000..8b02497eb3
--- /dev/null
+++ b/contracts/extensions/VotingReputation/IVotingReputation.sol
@@ -0,0 +1,321 @@
+/*
+ This file is part of The Colony Network.
+
+ The Colony Network is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General external License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The Colony Network is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General external License for more details.
+
+ You should have received a copy of the GNU General external License
+ along with The Colony Network. If not, see .
+*/
+
+pragma solidity 0.7.3;
+pragma experimental ABIEncoderV2;
+
+// import "./../colonyNetwork/IColonyNetwork.sol";
+// import "./../colony/ColonyRoles.sol";
+import "./../../common/IBasicMetaTransaction.sol";
+import "./../IColonyExtension.sol";
+import "./VotingReputationDataTypes.sol";
+// import "./../patriciaTree/PatriciaTreeProofs.sol";
+// import "./../tokenLocking/ITokenLocking.sol";
+// import "./ColonyExtension.sol";
+
+
+interface IVotingReputation is IBasicMetaTransaction, IColonyExtension, VotingReputationDataTypes {
+
+ // function getMetatransactionNonce(address userAddress) override external view returns (uint256 nonce);
+
+
+ /// @notice Initialise the extension
+ /// @param _totalStakeFraction The fraction of the domain's reputation we need to stake
+ /// @param _userMinStakeFraction The minimum per-user stake as fraction of total stake
+ /// @param _maxVoteFraction The fraction of the domain's reputation which must submit for quick-end
+ /// @param _voterRewardFraction The fraction of the total stake paid out to voters as rewards
+ /// @param _stakePeriod The length of the staking period in seconds
+ /// @param _submitPeriod The length of the submit period in seconds
+ /// @param _revealPeriod The length of the reveal period in seconds
+ /// @param _escalationPeriod The length of the escalation period in seconds
+ function initialise(
+ uint256 _totalStakeFraction,
+ uint256 _voterRewardFraction,
+ uint256 _userMinStakeFraction,
+ uint256 _maxVoteFraction,
+ uint256 _stakePeriod,
+ uint256 _submitPeriod,
+ uint256 _revealPeriod,
+ uint256 _escalationPeriod
+ )
+ external;
+
+ // // Data structures
+
+ // external functions (interface)
+
+ /// @notice Create a motion
+ /// @param _domainId The domain where we vote on the motion
+ /// @param _childSkillIndex The childSkillIndex pointing to the domain of the action
+ /// @param _altTarget The contract to which we send the action (0x0 for the colony)
+ /// @param _action A bytes array encoding a function call
+ /// @param _key Reputation tree key for the root domain
+ /// @param _value Reputation tree value for the root domain
+ /// @param _branchMask The branchmask of the proof
+ /// @param _siblings The siblings of the proof
+ function createMotion(
+ uint256 _domainId,
+ uint256 _childSkillIndex,
+ address _altTarget,
+ bytes memory _action,
+ bytes memory _key,
+ bytes memory _value,
+ uint256 _branchMask,
+ bytes32[] memory _siblings
+ )
+ external;
+
+ /// @notice Create a motion in the root domain (DEPRECATED)
+ /// @param _altTarget The contract to which we send the action (0x0 for the colony)
+ /// @param _action A bytes array encoding a function call
+ /// @param _key Reputation tree key for the root domain
+ /// @param _value Reputation tree value for the root domain
+ /// @param _branchMask The branchmask of the proof
+ /// @param _siblings The siblings of the proof
+ function createRootMotion(
+ address _altTarget,
+ bytes memory _action,
+ bytes memory _key,
+ bytes memory _value,
+ uint256 _branchMask,
+ bytes32[] memory _siblings
+ )
+ external;
+
+ /// @notice Create a motion in any domain (DEPRECATED)
+ /// @param _domainId The domain where we vote on the motion
+ /// @param _childSkillIndex The childSkillIndex pointing to the domain of the action
+ /// @param _action A bytes array encoding a function call
+ /// @param _key Reputation tree key for the domain
+ /// @param _value Reputation tree value for the domain
+ /// @param _branchMask The branchmask of the proof
+ /// @param _siblings The siblings of the proof
+ function createDomainMotion(
+ uint256 _domainId,
+ uint256 _childSkillIndex,
+ bytes memory _action,
+ bytes memory _key,
+ bytes memory _value,
+ uint256 _branchMask,
+ bytes32[] memory _siblings
+ )
+ external;
+
+ /// @notice Stake on a motion
+ /// @param _motionId The id of the motion
+ /// @param _permissionDomainId The domain where the extension has the arbitration permission
+ /// @param _childSkillIndex For the domain in which the motion is occurring
+ /// @param _vote The side being supported (0 = NAY, 1 = YAY)
+ /// @param _amount The amount of tokens being staked
+ /// @param _key Reputation tree key for the staker/domain
+ /// @param _value Reputation tree value for the staker/domain
+ /// @param _branchMask The branchmask of the proof
+ /// @param _siblings The siblings of the proof
+ function stakeMotion(
+ uint256 _motionId,
+ uint256 _permissionDomainId,
+ uint256 _childSkillIndex,
+ uint256 _vote,
+ uint256 _amount,
+ bytes memory _key,
+ bytes memory _value,
+ uint256 _branchMask,
+ bytes32[] memory _siblings
+ )
+ external;
+
+ /// @notice Submit a vote secret for a motion
+ /// @param _motionId The id of the motion
+ /// @param _voteSecret The hashed vote secret
+ /// @param _key Reputation tree key for the staker/domain
+ /// @param _value Reputation tree value for the staker/domain
+ /// @param _branchMask The branchmask of the proof
+ /// @param _siblings The siblings of the proof
+ function submitVote(
+ uint256 _motionId,
+ bytes32 _voteSecret,
+ bytes memory _key,
+ bytes memory _value,
+ uint256 _branchMask,
+ bytes32[] memory _siblings
+ )
+ external;
+
+ /// @notice Reveal a vote secret for a motion
+ /// @param _motionId The id of the motion
+ /// @param _salt The salt used to hash the vote
+ /// @param _vote The side being supported (0 = NAY, 1 = YAY)
+ /// @param _key Reputation tree key for the staker/domain
+ /// @param _value Reputation tree value for the staker/domain
+ /// @param _branchMask The branchmask of the proof
+ /// @param _siblings The siblings of the proof
+ function revealVote(
+ uint256 _motionId,
+ bytes32 _salt,
+ uint256 _vote,
+ bytes memory _key,
+ bytes memory _value,
+ uint256 _branchMask,
+ bytes32[] memory _siblings
+ )
+ external;
+
+ /// @notice Escalate a motion to a higher domain
+ /// @param _motionId The id of the motion
+ /// @param _newDomainId The desired domain of escalation
+ /// @param _childSkillIndex For the current domain, relative to the escalated domain
+ /// @param _key Reputation tree key for the new domain
+ /// @param _value Reputation tree value for the new domain
+ /// @param _branchMask The branchmask of the proof
+ /// @param _siblings The siblings of the proof
+ function escalateMotion(
+ uint256 _motionId,
+ uint256 _newDomainId,
+ uint256 _childSkillIndex,
+ bytes memory _key,
+ bytes memory _value,
+ uint256 _branchMask,
+ bytes32[] memory _siblings
+ )
+ external;
+
+ function finalizeMotion(uint256 _motionId) external;
+
+
+ /// @notice Return whether a motion, assuming it's in the finalizable state,
+ // is allowed to finalize without the call executing successfully.
+ /// @param _motionId The id of the motion
+ /// @dev We are only expecting this to be called from finalize motion in the contracts.
+ /// It is marked as external only so that the frontend can use it.
+ function failingExecutionAllowed(uint256 _motionId) external view returns (bool);
+
+ /// @notice Claim the staker's reward
+ /// @param _motionId The id of the motion
+ /// @param _permissionDomainId The domain where the extension has the arbitration permission
+ /// @param _childSkillIndex For the domain in which the motion is occurring
+ /// @param _staker The staker whose reward is being claimed
+ /// @param _vote The side being supported (0 = NAY, 1 = YAY)
+ function claimReward(
+ uint256 _motionId,
+ uint256 _permissionDomainId,
+ uint256 _childSkillIndex,
+ address _staker,
+ uint256 _vote
+ )
+ external;
+
+ // external view functions
+
+ /// @notice Get the total stake fraction
+ /// @return The total stake fraction
+ function getTotalStakeFraction() external view returns (uint256);
+
+ /// @notice Get the voter reward fraction
+ /// @return The voter reward fraction
+ function getVoterRewardFraction() external view returns (uint256) ;
+
+ /// @notice Get the user min stake fraction
+ /// @return The user min stake fraction
+ function getUserMinStakeFraction() external view returns (uint256) ;
+
+ /// @notice Get the max vote fraction
+ /// @return The max vote fraction
+ function getMaxVoteFraction() external view returns (uint256);
+
+ /// @notice Get the stake period
+ /// @return The stake period
+ function getStakePeriod() external view returns (uint256);
+
+ /// @notice Get the submit period
+ /// @return The submit period
+ function getSubmitPeriod() external view returns (uint256);
+
+ /// @notice Get the reveal period
+ /// @return The reveal period
+ function getRevealPeriod() external view returns (uint256);
+
+ /// @notice Get the escalation period
+ /// @return The escalation period
+ function getEscalationPeriod() external view returns (uint256);
+
+
+ /// @notice Get the total motion count
+ /// @return The total motion count
+ function getMotionCount() external view returns (uint256) ;
+
+ /// @notice Get the data for a single motion
+ /// @param _motionId The id of the motion
+ /// @return motion The motion struct
+ function getMotion(uint256 _motionId) external view returns (Motion memory motion);
+
+ /// @notice Get a user's stake on a motion
+ /// @param _motionId The id of the motion
+ /// @param _staker The staker address
+ /// @param _vote The side being supported (0 = NAY, 1 = YAY)
+ /// @return The user's stake
+ function getStake(uint256 _motionId, address _staker, uint256 _vote) external view returns (uint256);
+
+ /// @notice Get the number of ongoing motions for a single expenditure / expenditure slot
+ /// @param _structHash The hash of the expenditureId or expenditureId*expenditureSlot
+ /// @return The number of ongoing motions
+ function getExpenditureMotionCount(bytes32 _structHash) external view returns (uint256);
+
+ /// @notice Get the largest past vote on a single expenditure variable
+ /// @param _actionHash The hash of the particular expenditure action
+ /// @return The largest past vote on this variable
+ function getExpenditurePastVote(bytes32 _actionHash) external view returns (uint256);
+
+ /// @notice Get the current state of the motion
+ /// @return The current motion state
+ function getMotionState(uint256 _motionId) external view returns (MotionState) ;
+
+ /// @notice Get the voter reward
+ /// NB This function will only return a meaningful value if in the reveal state.
+ /// Prior to the reveal state, getVoterRewardRange should be used.
+ /// @param _motionId The id of the motion
+ /// @param _voterRep The reputation the voter has in the domain
+ /// @return The voter reward
+ function getVoterReward(uint256 _motionId, uint256 _voterRep) external view returns (uint256) ;
+
+ /// @notice Get the range of potential rewards for a voter on a specific motion, intended to be
+ /// used when the motion is in the reveal state.
+ /// Once a motion is in the reveal state the reward is known, and getVoterRewardRange should be used.
+ /// @param _motionId The id of the motion
+ /// @param _voterRep The reputation the voter has in the domain
+ /// @param _voterAddress The address the user will be voting as
+ /// @return The voter reward
+ function getVoterRewardRange(uint256 _motionId, uint256 _voterRep, address _voterAddress) external view returns (uint256, uint256) ;
+ /// @notice Get the staker reward
+ /// @param _motionId The id of the motion
+ /// @param _staker The staker's address
+ /// @param _vote The vote (0 = NAY, 1 = YAY)
+ /// @return The staker reward and the reputation penalty (if any)
+ function getStakerReward(uint256 _motionId, address _staker, uint256 _vote) external view returns (uint256, uint256);
+
+ function createClaimDelayAction(bytes memory action, uint256 value)
+ external
+ returns (bytes memory);
+
+ function claimMisalignedReward(
+ uint256 _motionId,
+ uint256 _permissionDomainId,
+ uint256 _childSkillIndex,
+ address _staker,
+ uint256 _vote
+ )
+ external;
+}
diff --git a/contracts/extensions/VotingReputation.sol b/contracts/extensions/VotingReputation/VotingReputation.sol
similarity index 96%
rename from contracts/extensions/VotingReputation.sol
rename to contracts/extensions/VotingReputation/VotingReputation.sol
index 30d5ea1aa5..d509369eb5 100644
--- a/contracts/extensions/VotingReputation.sol
+++ b/contracts/extensions/VotingReputation/VotingReputation.sol
@@ -18,28 +18,17 @@
pragma solidity 0.7.3;
pragma experimental ABIEncoderV2;
-import "./../colonyNetwork/IColonyNetwork.sol";
-import "./../colony/ColonyRoles.sol";
-import "./../common/BasicMetaTransaction.sol";
-import "./../common/ERC20Extended.sol";
-import "./../patriciaTree/PatriciaTreeProofs.sol";
-import "./../tokenLocking/ITokenLocking.sol";
-import "./ColonyExtension.sol";
-
-
-contract VotingReputation is ColonyExtension, PatriciaTreeProofs, BasicMetaTransaction {
-
- // Events
- event MotionCreated(uint256 indexed motionId, address creator, uint256 indexed domainId);
- event MotionStaked(uint256 indexed motionId, address indexed staker, uint256 indexed vote, uint256 amount);
- event MotionVoteSubmitted(uint256 indexed motionId, address indexed voter);
- event MotionVoteRevealed(uint256 indexed motionId, address indexed voter, uint256 indexed vote);
- event MotionFinalized(uint256 indexed motionId, bytes action, bool executed);
- event MotionEscalated(uint256 indexed motionId, address escalator, uint256 indexed domainId, uint256 indexed newDomainId);
- event MotionRewardClaimed(uint256 indexed motionId, address indexed staker, uint256 indexed vote, uint256 amount);
- event MotionEventSet(uint256 indexed motionId, uint256 eventIndex);
-
- // Constants
+import "./../../colonyNetwork/IColonyNetwork.sol";
+import "./../../colony/ColonyRoles.sol";
+import "./../../common/BasicMetaTransaction.sol";
+import "./../../common/ERC20Extended.sol";
+import "./../../patriciaTree/PatriciaTreeProofs.sol";
+import "./../../tokenLocking/ITokenLocking.sol";
+import "./../ColonyExtension.sol";
+import "./VotingReputationDataTypes.sol";
+
+
+contract VotingReputation is ColonyExtension, PatriciaTreeProofs, BasicMetaTransaction, VotingReputationDataTypes {
uint256 constant UINT128_MAX = 2**128 - 1;
uint256 constant NAY = 0;
@@ -62,8 +51,6 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs, BasicMetaTrans
"moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,address)"
));
- enum ExtensionState { Deployed, Active, Deprecated }
-
// Initialization data
ExtensionState state;
@@ -129,7 +116,7 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs, BasicMetaTrans
/// @notice Return the version number
/// @return The version number
function version() public pure override returns (uint256) {
- return 5;
+ return 6;
}
/// @notice Install the extension
@@ -221,26 +208,6 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs, BasicMetaTrans
selfdestruct(address(uint160(address(colony))));
}
- // Data structures
- enum MotionState { Null, Staking, Submit, Reveal, Closed, Finalizable, Finalized, Failed }
-
- struct Motion {
- uint64[3] events; // For recording motion lifecycle timestamps (STAKE, SUBMIT, REVEAL)
- bytes32 rootHash;
- uint256 domainId;
- uint256 skillId;
- uint256 skillRep;
- uint256 repSubmitted;
- uint256 paidVoterComp;
- uint256[2] pastVoterComp; // [nay, yay]
- uint256[2] stakes; // [nay, yay]
- uint256[2] votes; // [nay, yay]
- bool escalated;
- bool finalized;
- address altTarget;
- bytes action;
- }
-
// Public functions (interface)
/// @notice Create a motion
diff --git a/contracts/extensions/VotingReputation/VotingReputationDataTypes.sol b/contracts/extensions/VotingReputation/VotingReputationDataTypes.sol
new file mode 100644
index 0000000000..9b7ebc0906
--- /dev/null
+++ b/contracts/extensions/VotingReputation/VotingReputationDataTypes.sol
@@ -0,0 +1,54 @@
+/*
+ This file is part of The Colony Network.
+
+ The Colony Network is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The Colony Network is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with The Colony Network. If not, see .
+*/
+
+pragma solidity 0.7.3;
+import "./../../colony/ColonyDataTypes.sol";
+
+
+interface VotingReputationDataTypes {
+ // Constants
+ enum ExtensionState { Deployed, Active, Deprecated }
+
+ enum MotionState { Null, Staking, Submit, Reveal, Closed, Finalizable, Finalized, Failed }
+
+ struct Motion {
+ uint64[3] events; // For recording motion lifecycle timestamps (STAKE, SUBMIT, REVEAL)
+ bytes32 rootHash;
+ uint256 domainId;
+ uint256 skillId;
+ uint256 skillRep;
+ uint256 repSubmitted;
+ uint256 paidVoterComp;
+ uint256[2] pastVoterComp; // [nay, yay]
+ uint256[2] stakes; // [nay, yay]
+ uint256[2] votes; // [nay, yay]
+ bool escalated;
+ bool finalized;
+ address altTarget;
+ bytes action;
+ }
+
+ // Events
+ event MotionCreated(uint256 indexed motionId, address creator, uint256 indexed domainId);
+ event MotionStaked(uint256 indexed motionId, address indexed staker, uint256 indexed vote, uint256 amount);
+ event MotionVoteSubmitted(uint256 indexed motionId, address indexed voter);
+ event MotionVoteRevealed(uint256 indexed motionId, address indexed voter, uint256 indexed vote);
+ event MotionFinalized(uint256 indexed motionId, bytes action, bool executed);
+ event MotionEscalated(uint256 indexed motionId, address escalator, uint256 indexed domainId, uint256 indexed newDomainId);
+ event MotionRewardClaimed(uint256 indexed motionId, address indexed staker, uint256 indexed vote, uint256 amount);
+ event MotionEventSet(uint256 indexed motionId, uint256 eventIndex);
+}
diff --git a/contracts/extensions/VotingReputation/VotingReputationMisalignedRecovery.sol b/contracts/extensions/VotingReputation/VotingReputationMisalignedRecovery.sol
new file mode 100644
index 0000000000..b21b75424f
--- /dev/null
+++ b/contracts/extensions/VotingReputation/VotingReputationMisalignedRecovery.sol
@@ -0,0 +1,225 @@
+/*
+ This file is part of The Colony Network.
+
+ The Colony Network is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The Colony Network is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with The Colony Network. If not, see .
+*/
+
+pragma solidity 0.7.3;
+pragma experimental ABIEncoderV2;
+
+import "./../../colonyNetwork/IColonyNetwork.sol";
+import "./../../colony/ColonyRoles.sol";
+import "./../../common/BasicMetaTransaction.sol";
+import "./../../common/ERC20Extended.sol";
+import "./../../patriciaTree/PatriciaTreeProofs.sol";
+import "./../../tokenLocking/ITokenLocking.sol";
+import "./../ColonyExtension.sol";
+import "./../../../lib/dappsys/math.sol";
+import "./VotingReputationDataTypes.sol";
+
+contract VotingReputationMisalignedRecovery is PatriciaTreeProofs, DSMath, DSAuth, VotingReputationDataTypes {
+
+ // THIS FILE IS DELIBERATELY WRONG. IF YOU'RE EDITING THIS FILE, AND YOU'VE NOT BEEN EXPLICITLY
+ // TOLD TO DO SO, LEAVE NOW. THERE BE DRAGONS HERE.
+
+ // Constants
+ uint256 constant UINT128_MAX = 2**128 - 1;
+
+ uint256 constant NAY = 0;
+ uint256 constant YAY = 1;
+
+ address resolver; // Align storage with EtherRouter
+
+ IColony colony;
+ bool deprecated;
+
+ ExtensionState state;
+
+ IColonyNetwork colonyNetwork;
+ ITokenLocking tokenLocking;
+ address token;
+
+ // All `Fraction` variables are stored as WADs i.e. fixed-point numbers with 18 digits after the radix. So
+ // 1 WAD = 10**18, which is interpreted as 1.
+
+ uint256 totalStakeFraction; // Fraction of the domain's reputation needed to stake on each side in order to go to a motion.
+ // This can be set to a maximum of 0.5.
+ uint256 voterRewardFraction; // Fraction of staked tokens paid out to voters as rewards. This will be paid from the staked
+ // tokens of the losing side. This can be set to a maximum of 0.5.
+
+ uint256 userMinStakeFraction; // Minimum stake as fraction of required stake. 1 means a single user will be required to
+ // provide the whole stake on each side, which may not be possible depending on totalStakeFraction and the distribution of
+ // reputation in a domain.
+ uint256 maxVoteFraction; // Fraction of total domain reputation that needs to commit votes before closing to further votes.
+ // Setting this to anything other than 1 will mean it is likely not all those eligible to vote will be able to do so.
+
+ // All `Period` variables are second-denominated
+
+ uint256 stakePeriod; // Length of time for staking
+ uint256 submitPeriod; // Length of time for submitting votes
+ uint256 revealPeriod; // Length of time for revealing votes
+ uint256 escalationPeriod; // Length of time for escalating after a vote
+
+ // Here we deliberately recreate the misalignment in the storage slots, so solidity can correctly
+ // find the incorrect data in the mappings.
+ mapping(address => uint256) metatransactionNonces;
+
+ uint256 motionCount;
+ mapping (uint256 => Motion) motions;
+ mapping (uint256 => mapping (address => mapping (uint256 => uint256))) stakes;
+ mapping (uint256 => mapping (address => bytes32)) voteSecrets;
+
+ mapping (bytes32 => uint256) expenditurePastVotes; // expenditure slot signature => voting power
+ mapping (bytes32 => uint256) expenditureMotionCounts; // expenditure struct signature => count
+
+ // Public functions (interface)
+
+ /// @notice Claim the staker's reward
+ /// @param _motionId The id of the motion
+ /// @param _permissionDomainId The domain where the extension has the arbitration permission
+ /// @param _childSkillIndex For the domain in which the motion is occurring
+ /// @param _staker The staker whose reward is being claimed
+ /// @param _vote The side being supported (0 = NAY, 1 = YAY)
+ function claimMisalignedReward(
+ uint256 _motionId,
+ uint256 _permissionDomainId,
+ uint256 _childSkillIndex,
+ address _staker,
+ uint256 _vote
+ )
+ public
+ {
+ Motion storage motion = motions[_motionId];
+ // Motions might have been in any point in their lifecycle, so we lose our restirction
+ // on only being able to call this function on finalized/failed motions. These motions
+ // created while misaligned no longer exist, and cannot proceed through their lifecycle.
+ // require(
+ // getMotionState(_motionId) == MotionState.Finalized ||
+ // getMotionState(_motionId) == MotionState.Failed,
+ // "voting-rep-motion-not-claimable"
+ // );
+
+ (uint256 stakerReward, uint256 repPenalty) = getStakerReward(_motionId, _staker, _vote);
+
+ require(stakes[_motionId][_staker][_vote] > 0, "voting-rep-nothing-to-claim");
+ delete stakes[_motionId][_staker][_vote];
+
+ tokenLocking.transfer(token, stakerReward, _staker, true);
+
+ if (repPenalty > 0) {
+ colony.emitDomainReputationPenalty(
+ _permissionDomainId,
+ _childSkillIndex,
+ motion.domainId,
+ _staker,
+ -int256(repPenalty)
+ );
+ }
+
+ emit MotionRewardClaimed(_motionId, _staker, _vote, stakerReward);
+ }
+
+ /// @notice Get the staker reward
+ /// @param _motionId The id of the motion
+ /// @param _staker The staker's address
+ /// @param _vote The vote (0 = NAY, 1 = YAY)
+ /// @return The staker reward and the reputation penalty (if any)
+ function getStakerReward(uint256 _motionId, address _staker, uint256 _vote) internal view returns (uint256, uint256) {
+ Motion storage motion = motions[_motionId];
+
+ uint256 totalSideStake = add(motion.stakes[_vote], motion.pastVoterComp[_vote]);
+ if (totalSideStake == 0) { return (0, 0); }
+
+ uint256 stakeFraction = wdiv(stakes[_motionId][_staker][_vote], totalSideStake);
+
+ uint256 realStake = wmul(stakeFraction, motion.stakes[_vote]);
+
+ uint256 stakerReward;
+ uint256 repPenalty;
+
+ // If finalized and went to a vote, use vote to determine reward or penalty
+ if (motion.finalized && add(motion.votes[NAY], motion.votes[YAY]) > 0) {
+
+ uint256 loserStake;
+ uint256 winnerStake;
+ if (motion.votes[YAY] > motion.votes[NAY]){
+ loserStake = motion.stakes[NAY];
+ winnerStake = motion.stakes[YAY];
+ } else {
+ loserStake = motion.stakes[YAY];
+ winnerStake = motion.stakes[NAY];
+ }
+
+ loserStake = sub(loserStake, motion.paidVoterComp);
+ uint256 totalVotes = add(motion.votes[NAY], motion.votes[YAY]);
+ uint256 winFraction = wdiv(motion.votes[_vote], totalVotes);
+ uint256 winShare = wmul(winFraction, 2 * WAD); // On a scale of 0-2 WAD
+
+ if (winShare > WAD || (winShare == WAD && _vote == NAY)) {
+ // 50% gets 0% of loser's stake, 100% gets 100% of loser's stake, linear in between
+ stakerReward = wmul(stakeFraction, add(winnerStake, wmul(loserStake, winShare - WAD)));
+ } else {
+ stakerReward = wmul(stakeFraction, wmul(loserStake, winShare));
+ repPenalty = sub(realStake, stakerReward);
+ }
+
+ // Else if finalized, rewards based on stakes alone
+ } else if (motion.finalized) {
+ assert(motion.paidVoterComp == 0);
+ uint256 requiredStake = getRequiredStake(_motionId);
+
+ // Your side fully staked, receive 10% (proportional) of loser's stake
+ if (
+ motion.stakes[_vote] == requiredStake &&
+ motion.stakes[flip(_vote)] < requiredStake
+ ) {
+
+ uint256 loserStake = motion.stakes[flip(_vote)];
+ uint256 totalPenalty = wmul(loserStake, WAD / 10);
+ stakerReward = wmul(stakeFraction, add(requiredStake, totalPenalty));
+
+ // Opponent's side fully staked, pay 10% penalty
+ } else if (
+ motion.stakes[_vote] < requiredStake &&
+ motion.stakes[flip(_vote)] == requiredStake
+ ) {
+
+ uint256 loserStake = motion.stakes[_vote];
+ uint256 totalPenalty = wmul(loserStake, WAD / 10);
+ stakerReward = wmul(stakeFraction, sub(loserStake, totalPenalty));
+ repPenalty = sub(realStake, stakerReward);
+
+ // Neither side fully staked (or no votes were revealed), no reward or penalty
+ } else {
+
+ stakerReward = realStake;
+
+ }
+ } else {
+ // Motion was never finalized. We just return stakes, exactly as if neither
+ // side fully staked or no votes were revealed.
+ stakerReward = realStake;
+ }
+
+ return (stakerReward, repPenalty);
+ }
+
+ function getRequiredStake(uint256 _motionId) internal view returns (uint256) {
+ return wmul(motions[_motionId].skillRep, totalStakeFraction);
+ }
+
+ function flip(uint256 _vote) internal pure returns (uint256) {
+ return sub(1, _vote);
+ }
+}
diff --git a/migrations/9_setup_extensions.js b/migrations/9_setup_extensions.js
index 038e73ad22..264d7c757e 100644
--- a/migrations/9_setup_extensions.js
+++ b/migrations/9_setup_extensions.js
@@ -9,6 +9,7 @@ const EvaluatedExpenditure = artifacts.require("./EvaluatedExpenditure");
const FundingQueue = artifacts.require("./FundingQueue");
const OneTxPayment = artifacts.require("./OneTxPayment");
const VotingReputation = artifacts.require("./VotingReputation");
+const VotingReputationMisalignedRecovery = artifacts.require("./VotingReputationMisalignedRecovery");
const TokenSupplier = artifacts.require("./TokenSupplier");
const Whitelist = artifacts.require("./Whitelist");
@@ -17,17 +18,21 @@ const EtherRouter = artifacts.require("./EtherRouter");
const IColonyNetwork = artifacts.require("./IColonyNetwork");
const IMetaColony = artifacts.require("./IMetaColony");
-async function addExtension(colonyNetwork, name, implementation) {
+async function addExtension(colonyNetwork, interfaceName, extensionName, implementations) {
const metaColonyAddress = await colonyNetwork.getMetaColony();
const metaColony = await IMetaColony.at(metaColonyAddress);
- const NAME_HASH = soliditySha3(name);
- const deployment = await implementation.new();
+ const NAME_HASH = soliditySha3(extensionName);
+ const deployments = await Promise.all(implementations.map((x) => x.new()));
const resolver = await Resolver.new();
- // Computed property names! Fancy!
- await setupEtherRouter(name, { [name]: deployment.address }, resolver);
+
+ const deployedImplementations = {};
+ for (const idx in implementations) {
+ deployedImplementations[implementations[idx].contractName] = deployments[idx].address;
+ }
+ await setupEtherRouter(interfaceName, deployedImplementations, resolver);
await metaColony.addExtensionToNetwork(NAME_HASH, resolver.address);
- console.log(`### ${name} extension installed`);
+ console.log(`### ${extensionName} extension installed`);
}
// eslint-disable-next-line no-unused-vars
@@ -35,11 +40,11 @@ module.exports = async function (deployer, network, accounts) {
const etherRouterDeployed = await EtherRouter.deployed();
const colonyNetwork = await IColonyNetwork.at(etherRouterDeployed.address);
- await addExtension(colonyNetwork, "CoinMachine", CoinMachine);
- await addExtension(colonyNetwork, "EvaluatedExpenditure", EvaluatedExpenditure);
- await addExtension(colonyNetwork, "FundingQueue", FundingQueue);
- await addExtension(colonyNetwork, "OneTxPayment", OneTxPayment);
- await addExtension(colonyNetwork, "VotingReputation", VotingReputation);
- await addExtension(colonyNetwork, "TokenSupplier", TokenSupplier);
- await addExtension(colonyNetwork, "Whitelist", Whitelist);
+ await addExtension(colonyNetwork, "CoinMachine", "CoinMachine", [CoinMachine]);
+ await addExtension(colonyNetwork, "EvaluatedExpenditure", "EvaluatedExpenditure", [EvaluatedExpenditure]);
+ await addExtension(colonyNetwork, "FundingQueue", "FundingQueue", [FundingQueue]);
+ await addExtension(colonyNetwork, "OneTxPayment", "OneTxPayment", [OneTxPayment]);
+ await addExtension(colonyNetwork, "IVotingReputation", "VotingReputation", [VotingReputation, VotingReputationMisalignedRecovery]);
+ await addExtension(colonyNetwork, "TokenSupplier", "TokenSupplier", [TokenSupplier]);
+ await addExtension(colonyNetwork, "Whitelist", "Whitelist", [Whitelist]);
};
diff --git a/test-smoke/colony-storage-consistent.js b/test-smoke/colony-storage-consistent.js
index 288a2a6f02..82cd2af18b 100644
--- a/test-smoke/colony-storage-consistent.js
+++ b/test-smoke/colony-storage-consistent.js
@@ -154,8 +154,8 @@ contract("Contract Storage", (accounts) => {
console.log("miningCycleStateHash:", miningCycleAccount.stateRoot.toString("hex"));
console.log("tokenLockingStateHash:", tokenLockingAccount.stateRoot.toString("hex"));
- expect(colonyNetworkAccount.stateRoot.toString("hex")).to.equal("59a945f801398e8c6953538db14bde68dfc61fb4b90314744d6f3869fa3f6fa1");
- expect(colonyAccount.stateRoot.toString("hex")).to.equal("db3561898fd52285f65cf27eecfdfb556838f7626f5f425d9b293dcf703fae84");
+ expect(colonyNetworkAccount.stateRoot.toString("hex")).to.equal("9e974e7d769aae97eff965d3d57bab30a5298b8f9551833d1aabea9f9014c025");
+ expect(colonyAccount.stateRoot.toString("hex")).to.equal("d3578d76c4570c355716ce38f0913e0dd5d33357be5a1ff43abea2db3f579172");
expect(metaColonyAccount.stateRoot.toString("hex")).to.equal("58f1833f0b94c47c028c91ededb70d6697624ecf98bc2cc7930bf55f40d2d931");
expect(miningCycleAccount.stateRoot.toString("hex")).to.equal("1f3909ac9098d953ec1d197e6d7924384e96209770f445466ea2f0c0c39f4834");
expect(tokenLockingAccount.stateRoot.toString("hex")).to.equal("7ec700a44aef86af735adcb205136940a73bd0507d07d88e93e629dee06f05c3");