Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contracts/colony/Colony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,10 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {

sig = bytes4(keccak256("setUserRoles(uint256,uint256,address,uint256,bytes32)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true);

sig = bytes4(keccak256("moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Funding), address(this), sig, true);

}

function checkNotAdditionalProtectedVariable(uint256 _slot) public view recovery {
Expand Down
3 changes: 3 additions & 0 deletions contracts/colony/ColonyAuthority.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ contract ColonyAuthority is CommonAuthority {
addRoleCapability(ROOT_ROLE, "editColony(string)");
addRoleCapability(ROOT_ROLE, "burnTokens(address,uint256)");
addRoleCapability(ROOT_ROLE, "unlockToken()");

// Added in colony v6 (d-lwss)
addRoleCapability(FUNDING_ROLE, "moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)");
}

function addRoleCapability(uint8 role, bytes memory sig) private {
Expand Down
140 changes: 87 additions & 53 deletions contracts/colony/ColonyFunding.sol
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,28 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs { // ignore-swc-123
return (fundingPot.associatedType, fundingPot.associatedTypeId, fundingPot.payoutsWeCannotMake);
}

function moveFundsBetweenPots(
uint256 _permissionDomainId,
uint256 _childSkillIndex,
uint256 _domainId,
uint256 _fromChildSkillIndex,
uint256 _toChildSkillIndex,
uint256 _fromPot,
uint256 _toPot,
uint256 _amount,
address _token
)
public
stoppable
authDomain(_permissionDomainId, _childSkillIndex, _domainId)
validFundingTransfer(_fromPot, _toPot)
{
require(validateDomainInheritance(_domainId, _fromChildSkillIndex, getDomainFromFundingPot(_fromPot)), "colony-invalid-domain-inheritence");
require(validateDomainInheritance(_domainId, _toChildSkillIndex, getDomainFromFundingPot(_toPot)), "colony-invalid-domain-inheritence");

moveFundsBetweenPotsFunctionality(_fromPot, _toPot, _amount, _token);
Comment thread
kronosapiens marked this conversation as resolved.
}

function moveFundsBetweenPots(
uint256 _permissionDomainId,
uint256 _fromChildSkillIndex,
Expand All @@ -220,59 +242,7 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs { // ignore-swc-123
authDomain(_permissionDomainId, _toChildSkillIndex, getDomainFromFundingPot(_toPot))
validFundingTransfer(_fromPot, _toPot)
{
FundingPot storage fromPot = fundingPots[_fromPot];
FundingPot storage toPot = fundingPots[_toPot];

fromPot.balance[_token] = sub(fromPot.balance[_token], _amount);
toPot.balance[_token] = add(toPot.balance[_token], _amount);

if (_fromPot == 1){
// If we're moving from the root pot, then check we haven't dropped below what we need
// to cover any approvals that we've made.
require(fromPot.balance[_token] >= tokenApprovalTotals[_token], "colony-funding-too-many-approvals");
}

// If this pot is associated with a Task or Expenditure, prevent money
// being taken from the pot if the remaining balance is less than
// the amount needed for payouts, unless the task was cancelled.
if (fromPot.associatedType == FundingPotAssociatedType.Task) {
require(
tasks[fromPot.associatedTypeId].status == TaskStatus.Cancelled ||
fromPot.balance[_token] >= fromPot.payouts[_token],
"colony-funding-task-bad-state"
);
}
if (fromPot.associatedType == FundingPotAssociatedType.Expenditure) {
require(
expenditures[fromPot.associatedTypeId].status == ExpenditureStatus.Cancelled ||
fromPot.balance[_token] >= fromPot.payouts[_token],
"colony-funding-expenditure-bad-state"
);
}

if (
fromPot.associatedType == FundingPotAssociatedType.Expenditure ||
fromPot.associatedType == FundingPotAssociatedType.Payment ||
fromPot.associatedType == FundingPotAssociatedType.Task
) {
uint256 fromPotPreviousAmount = add(fromPot.balance[_token], _amount);
updatePayoutsWeCannotMakeAfterPotChange(_fromPot, _token, fromPotPreviousAmount);
}

if (
toPot.associatedType == FundingPotAssociatedType.Expenditure ||
toPot.associatedType == FundingPotAssociatedType.Payment ||
toPot.associatedType == FundingPotAssociatedType.Task
) {
uint256 toPotPreviousAmount = sub(toPot.balance[_token], _amount);
updatePayoutsWeCannotMakeAfterPotChange(_toPot, _token, toPotPreviousAmount);
}

if (_toPot == 0 ) {
nonRewardPotsTotal[_token] = sub(nonRewardPotsTotal[_token], _amount);
}

emit ColonyFundsMovedBetweenFundingPots(msg.sender, _fromPot, _toPot, _amount, _token);
moveFundsBetweenPotsFunctionality(_fromPot, _toPot, _amount, _token);
}

function claimColonyFunds(address _token) public stoppable {
Expand Down Expand Up @@ -480,6 +450,70 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs { // ignore-swc-123
return (payout.tokenAddress, reward);
}

function moveFundsBetweenPotsFunctionality(
uint256 _fromPot,
uint256 _toPot,
uint256 _amount,
address _token
)
internal {
FundingPot storage fromPot = fundingPots[_fromPot];
FundingPot storage toPot = fundingPots[_toPot];

fromPot.balance[_token] = sub(fromPot.balance[_token], _amount);
toPot.balance[_token] = add(toPot.balance[_token], _amount);

if (_fromPot == 1){
// If we're moving from the root pot, then check we haven't dropped below what we need
// to cover any approvals that we've made.
require(fromPot.balance[_token] >= tokenApprovalTotals[_token], "colony-funding-too-many-approvals");
}

// If this pot is associated with a Task or Expenditure, prevent money
// being taken from the pot if the remaining balance is less than
// the amount needed for payouts, unless the task was cancelled.
if (fromPot.associatedType == FundingPotAssociatedType.Task) {
require(
tasks[fromPot.associatedTypeId].status == TaskStatus.Cancelled ||
fromPot.balance[_token] >= fromPot.payouts[_token],
"colony-funding-task-bad-state"
);
}
if (fromPot.associatedType == FundingPotAssociatedType.Expenditure) {
require(
expenditures[fromPot.associatedTypeId].status == ExpenditureStatus.Cancelled ||
fromPot.balance[_token] >= fromPot.payouts[_token],
"colony-funding-expenditure-bad-state"
);
}

if (
fromPot.associatedType == FundingPotAssociatedType.Expenditure ||
fromPot.associatedType == FundingPotAssociatedType.Payment ||
fromPot.associatedType == FundingPotAssociatedType.Task
) {
uint256 fromPotPreviousAmount = add(fromPot.balance[_token], _amount);
updatePayoutsWeCannotMakeAfterPotChange(_fromPot, _token, fromPotPreviousAmount);
}

if (
toPot.associatedType == FundingPotAssociatedType.Expenditure ||
toPot.associatedType == FundingPotAssociatedType.Payment ||
toPot.associatedType == FundingPotAssociatedType.Task
) {
uint256 toPotPreviousAmount = sub(toPot.balance[_token], _amount);
updatePayoutsWeCannotMakeAfterPotChange(_toPot, _token, toPotPreviousAmount);
}

if (_toPot == 0 ) {
nonRewardPotsTotal[_token] = sub(nonRewardPotsTotal[_token], _amount);
}

emit ColonyFundsMovedBetweenFundingPots(msg.sender, _fromPot, _toPot, _amount, _token);
}



function updatePayoutsWeCannotMakeAfterPotChange(uint256 _fundingPotId, address _token, uint _prev) internal {
FundingPot storage tokenPot = fundingPots[_fundingPotId];

Expand Down
24 changes: 24 additions & 0 deletions contracts/colony/IColony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,30 @@ interface IColony is ColonyDataTypes, IRecovery {
/// @return payout Funding pot payout amount
function getFundingPotPayout(uint256 _potId, address _token) external view returns (uint256 payout);

/// @notice Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`.
/// @param _permissionDomainId The domainId in which I have the permission to take this action
/// @param _childSkillIndex The child index in _permissionDomainId where I will be taking this action
/// @param _domainId The domain where I am taking this action, pointed to by _permissionDomainId and _childSkillIndex
/// @param _fromChildSkillIndex In the array of child skills for the skill associated with the domain pointed to by _permissionDomainId + _childSkillIndex,
/// the index of the skill associated with the domain that contains _fromPot
/// @param _toChildSkillIndex The same, but for the _toPot which the funds are being moved to
/// @param _fromPot Funding pot id providing the funds
/// @param _toPot Funding pot id receiving the funds
/// @param _amount Amount of funds
/// @param _token Address of the token, `0x0` value indicates Ether
function moveFundsBetweenPots(
uint256 _permissionDomainId,
uint256 _childSkillIndex,
uint256 _domainId,
uint256 _fromChildSkillIndex,
uint256 _toChildSkillIndex,
uint256 _fromPot,
uint256 _toPot,
uint256 _amount,
address _token
) external;

/// @notice DEPRECATED
/// @notice Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`.
/// @param _permissionDomainId The domainId in which I have the permission to take this action
/// @param _fromChildSkillIndex The child index in `_permissionDomainId` where we can find the domain for `_fromPotId`
Expand Down
55 changes: 22 additions & 33 deletions contracts/extensions/VotingReputation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,13 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {
bytes32(uint256(1)) << uint8(ColonyDataTypes.ColonyRole.Root)
);

bytes4 constant CHANGE_FUNCTION = bytes4(
keccak256("setExpenditureState(uint256,uint256,uint256,uint256,bool[],bytes32[],bytes32)")
);
bytes4 constant CHANGE_FUNCTION_SIG = bytes4(keccak256(
"setExpenditureState(uint256,uint256,uint256,uint256,bool[],bytes32[],bytes32)"
));

bytes4 constant OLD_MOVE_FUNDS_SIG = bytes4(keccak256(
"moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,address)"
));

enum ExtensionState { Deployed, Active, Deprecated }

Expand Down Expand Up @@ -214,6 +218,15 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {

// Public 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,
Expand All @@ -233,6 +246,8 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {
address target = getTarget(_altTarget);
bytes4 action = getSig(_action);

require(action != OLD_MOVE_FUNDS_SIG, "voting-rep-disallowed-function");

uint256 skillId;

if (ColonyRoles(target).getCapabilityRoles(action) | ROOT_ROLES == ROOT_ROLES) {
Expand All @@ -251,7 +266,6 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {
uint256 childSkillId = colonyNetwork.getChildSkillId(skillId, _childSkillIndex);
require(childSkillId == actionDomainSkillId, "voting-rep-invalid-domain-id");
}

}

motionCount += 1;
Expand All @@ -272,7 +286,6 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {
emit MotionCreated(motionCount, msg.sender, _domainId);
}


/// @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
Expand All @@ -293,30 +306,6 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {
createMotion(1, UINT256_MAX, _altTarget, _action, _key, _value, _branchMask, _siblings);
}

/// @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 _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 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,
address _altTarget,
bytes memory _action,
bytes memory _key,
bytes memory _value,
uint256 _branchMask,
bytes32[] memory _siblings
)
public
{
createMotion(_domainId, _childSkillIndex, _altTarget, _action, _key, _value, _branchMask, _siblings);
}

/// @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
Expand All @@ -336,7 +325,7 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {
)
public
{
createDomainMotion(_domainId, _childSkillIndex, address(0x0), _action, _key, _value, _branchMask, _siblings);
createMotion(_domainId, _childSkillIndex, address(0x0), _action, _key, _value, _branchMask, _siblings);
Comment thread
kronosapiens marked this conversation as resolved.
}

/// @notice Stake on a motion
Expand Down Expand Up @@ -391,7 +380,7 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {
_vote == YAY &&
!motion.escalated &&
motion.stakes[YAY] == requiredStake &&
getSig(motion.action) == CHANGE_FUNCTION &&
getSig(motion.action) == CHANGE_FUNCTION_SIG &&
motion.altTarget == address(0x0)
) {
bytes32 structHash = hashExpenditureActionStruct(motion.action);
Expand Down Expand Up @@ -587,7 +576,7 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {
);

if (
getSig(motion.action) == CHANGE_FUNCTION &&
getSig(motion.action) == CHANGE_FUNCTION_SIG &&
getTarget(motion.altTarget) == address(colony)
) {
bytes32 structHash = hashExpenditureActionStruct(motion.action);
Expand Down Expand Up @@ -1021,7 +1010,7 @@ contract VotingReputation is ColonyExtension, PatriciaTreeProofs {
}

function hashExpenditureActionStruct(bytes memory action) internal returns (bytes32 hash) {
assert(getSig(action) == CHANGE_FUNCTION);
assert(getSig(action) == CHANGE_FUNCTION_SIG);

uint256 expenditureId;
uint256 storageSlot;
Expand Down
24 changes: 22 additions & 2 deletions docs/_Interface_IColony.md
Original file line number Diff line number Diff line change
Expand Up @@ -1140,14 +1140,34 @@ Move a given amount: `_amount` of `_token` funds from funding pot with id `_from
|Name|Type|Description|
|---|---|---|
|_permissionDomainId|uint256|The domainId in which I have the permission to take this action
|_fromChildSkillIndex|uint256|The child index in `_permissionDomainId` where we can find the domain for `_fromPotId`
|_toChildSkillIndex|uint256|The child index in `_permissionDomainId` where we can find the domain for `_toPotId`
|_childSkillIndex|uint256|The child index in _permissionDomainId where I will be taking this action
|_domainId|uint256|The domain where I am taking this action, pointed to by _permissionDomainId and _childSkillIndex
|_fromChildSkillIndex|uint256|In the array of child skills for the skill associated with the domain pointed to by _permissionDomainId + _childSkillIndex, the index of the skill associated with the domain that contains _fromPot
|_toChildSkillIndex|uint256|The same, but for the _toPot which the funds are being moved to
|_fromPot|uint256|Funding pot id providing the funds
|_toPot|uint256|Funding pot id receiving the funds
|_amount|uint256|Amount of funds
|_token|address|Address of the token, `0x0` value indicates Ether


### `moveFundsBetweenPots`

Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`.


**Parameters**

|Name|Type|Description|
|---|---|---|
|_permissionDomainId|uint256|The domainId in which I have the permission to take this action
|_fromChildSkillIndex|uint256|
|_toChildSkillIndex|uint256|
|_fromPot|uint256|
|_toPot|uint256|
|_amount|uint256|
|_token|address|


### `obligateStake`

Obligate the user some amount of tokens as a stake.
Expand Down
4 changes: 3 additions & 1 deletion helpers/test-data-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,10 @@ export async function setupFundedTask({
const totalPayouts = managerPayoutBN.add(workerPayoutBN).add(evaluatorPayoutBN);

const childSkillIndex = await getChildSkillIndex(colonyNetwork, colony, 1, task.domainId);
const moveFundsBetweenPots = colony.methods["moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"];

await colony.setFundingRole(1, UINT256_MAX, manager, 1, true);
await colony.moveFundsBetweenPots(1, UINT256_MAX, childSkillIndex, 1, task.fundingPotId, totalPayouts, tokenAddress, { from: manager });
await moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, childSkillIndex, 1, task.fundingPotId, totalPayouts, tokenAddress, { from: manager });
await colony.setAllTaskPayouts(taskId, tokenAddress, managerPayout, evaluatorPayout, workerPayout, { from: manager });
await assignRoles({ colony, taskId, manager, evaluator, worker });

Expand Down
Loading