Skip to content

Commit

Permalink
cancelExpenditureViaArbitration
Browse files Browse the repository at this point in the history
  • Loading branch information
area committed Mar 6, 2024
1 parent 7cd5441 commit af3088b
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 48 deletions.
49 changes: 2 additions & 47 deletions contracts/colony/Colony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
// This function, exactly as defined, is used in build scripts. Take care when updating.
// Version number should be upped with every change in Colony or its dependency contracts or libraries.
// prettier-ignore
function version() public pure returns (uint256 colonyVersion) { return 14; }
function version() public pure returns (uint256 colonyVersion) { return 15; }

function getColonyNetwork() public view returns (address) {
return colonyNetworkAddress;
Expand Down Expand Up @@ -303,57 +303,12 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
emit ColonyUpgraded(msgSender(), currentVersion, _newVersion);
}

// v11 to v12
function finishUpgrade() public always {
ColonyAuthority colonyAuthority = ColonyAuthority(address(authority));
bytes4 sig;

sig = bytes4(keccak256("makeArbitraryTransactions(address[],bytes[],bool)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true);

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

sig = bytes4(keccak256("setExpenditureMetadata(uint256,uint256,uint256,string)"));
sig = bytes4(keccak256("cancelExpenditureViaArbitration(uint256,uint256,uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Arbitration), address(this), sig, true);

// Remove Task & Payment functions
sig = bytes4(keccak256("makeTask(uint256,uint256,bytes32,uint256,uint256,uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Administration), address(this), sig, false);
sig = bytes4(keccak256("addPayment(uint256,uint256,address,address,uint256,uint256,uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Administration), address(this), sig, false);
sig = bytes4(keccak256("setPaymentRecipient(uint256,uint256,uint256,address)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Administration), address(this), sig, false);
sig = bytes4(keccak256("setPaymentSkill(uint256,uint256,uint256,uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Administration), address(this), sig, false);
sig = bytes4(keccak256("setPaymentPayout(uint256,uint256,uint256,address,uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Administration), address(this), sig, false);
sig = bytes4(keccak256("finalizePayment(uint256,uint256,uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Administration), address(this), sig, false);

// Remove Global Skills functions
sig = bytes4(keccak256("addGlobalSkill()"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, false);
sig = bytes4(keccak256("deprecateGlobalSkill(uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, false);

// If OneTxPayment extension is installed, add the new role and upgrade it
bytes32 ONE_TX_PAYMENT = keccak256("OneTxPayment");
IColonyNetwork network = IColonyNetwork(colonyNetworkAddress);
address oneTxPaymentAddress = network.getExtensionInstallation(ONE_TX_PAYMENT, address(this));

if (oneTxPaymentAddress != address(0x0)) {
uint256 installedVersion = ColonyExtension(oneTxPaymentAddress).version();
require(installedVersion >= 5, "colony-upgrade-one-tx-payment-to-6");
if (installedVersion == 5) {
// If installed in root, add arbitration permission
if (colonyAuthority.hasUserRole(oneTxPaymentAddress, 1, uint8(ColonyRole.Administration))) {
colonyAuthority.setUserRole(oneTxPaymentAddress, 1, uint8(ColonyRole.Arbitration), true);
}
// Upgrade extension
network.upgradeExtension(ONE_TX_PAYMENT, 6);
}
}
}

function getMetatransactionNonce(address _user) public view override returns (uint256 nonce) {
Expand Down
2 changes: 2 additions & 0 deletions contracts/colony/ColonyAuthority.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ contract ColonyAuthority is CommonAuthority {

// Added in colony v10 (ginger-lwss)
addRoleCapability(ARBITRATION_ROLE, "setExpenditurePayout(uint256,uint256,uint256,uint256,address,uint256)");

addRoleCapability(ARBITRATION_ROLE, "cancelExpenditureViaArbitration(uint256,uint256,uint256)");
}

function addRoleCapability(uint8 role, bytes memory sig) private {
Expand Down
15 changes: 15 additions & 0 deletions contracts/colony/ColonyExpenditure.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ contract ColonyExpenditure is ColonyStorage {
emit ExpenditureTransferred(msgSender(), _id, _newOwner);
}

function cancelExpenditureViaArbitration(
uint256 _permissionDomainId,
uint256 _childSkillIndex,
uint256 _id
)
public
stoppable
expenditureDraftOrLocked(_id)
authDomain(_permissionDomainId, _childSkillIndex, expenditures[_id].domainId)
{
expenditures[_id].status = ExpenditureStatus.Cancelled;

emit ExpenditureCancelled(msgSender(), _id);
}

function cancelExpenditure(
uint256 _id
) public stoppable expenditureDraft(_id) expenditureOnlyOwner(_id) {
Expand Down
11 changes: 11 additions & 0 deletions contracts/colony/IColony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,17 @@ interface IColony is ColonyDataTypes, IRecovery, IBasicMetaTransaction, IMultica
address _newOwner
) external;

/// @notice Cancels the expenditure and prevents further editing.
/// @param _permissionDomainId The domainId in which I have the permission to take this action
/// @param _childSkillIndex The index that the `_domainId` is relative to `_permissionDomainId`,
/// (only used if `_permissionDomainId` is different to `_domainId`)
/// @param _id Expenditure identifier
function cancelExpenditureViaArbitration(
uint256 _permissionDomainId,
uint256 _childSkillIndex,
uint256 _id
) external;

/// @notice Cancels the expenditure and prevents further editing. Can only be called by expenditure owner.
/// @param _id Expenditure identifier
function cancelExpenditure(uint256 _id) external;
Expand Down
14 changes: 14 additions & 0 deletions docs/interfaces/icolony.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ Cancels the expenditure and prevents further editing. Can only be called by expe
|_id|uint256|Expenditure identifier


### `cancelExpenditureViaArbitration(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id)`

Cancels the expenditure and prevents further editing.


**Parameters**

|Name|Type|Description|
|---|---|---|
|_permissionDomainId|uint256|The domainId in which I have the permission to take this action
|_childSkillIndex|uint256|The index that the `_domainId` is relative to `_permissionDomainId`, (only used if `_permissionDomainId` is different to `_domainId`)
|_id|uint256|Expenditure identifier


### `claimColonyFunds(address _token)`

Move any funds received by the colony in `_token` denomination to the top-level domain pot, siphoning off a small amount to the reward pot. If called against a colony's own token, no fee is taken.
Expand Down
2 changes: 1 addition & 1 deletion helpers/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const INT256_MIN = new BN(2).pow(new BN(255)).mul(new BN(-1));
const INT128_MAX = new BN(2).pow(new BN(127)).sub(new BN(1));
const INT128_MIN = new BN(2).pow(new BN(127)).mul(new BN(-1));

const CURR_VERSION = 14;
const CURR_VERSION = 15;

const RECOVERY_ROLE = 0;
const ROOT_ROLE = 1;
Expand Down
13 changes: 13 additions & 0 deletions scripts/deployOldUpgradeableVersion.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ module.exports.deployColonyVersionGLWSS4 = (colonyNetwork) => {
);
};

module.exports.deployColonyVersionHMWSS = (colonyNetwork) => {
return module.exports.deployOldColonyVersion(
"Colony",
"IMetaColony",
[
// eslint-disable-next-line max-len
"Colony,ColonyDomains,ColonyExpenditure,ColonyFunding,ColonyRewards,ColonyRoles,ContractRecovery,ColonyArbitraryTransaction",
],
"hmwss",
colonyNetwork,
);
};

module.exports.deployColonyNetworkVersionGLWSS4 = () => {
return module.exports.deployOldColonyNetworkVersion(
"",
Expand Down
24 changes: 24 additions & 0 deletions test/contracts-network/colony-expenditure.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,20 @@ contract("Colony Expenditure", (accounts) => {
expect(expenditure.owner).to.equal(USER);
});

it("should allow arbitration users to cancel expenditures", async () => {
await colony.makeExpenditure(1, UINT256_MAX, 1, { from: ADMIN });
const expenditureId = await colony.getExpenditureCount();

let expenditure = await colony.getExpenditure(expenditureId);
expect(expenditure.status).to.eq.BN(DRAFT);

await checkErrorRevert(colony.cancelExpenditureViaArbitration(1, UINT256_MAX, expenditureId, { from: ADMIN }), "ds-auth-unauthorized");
await colony.cancelExpenditureViaArbitration(1, UINT256_MAX, expenditureId, { from: ARBITRATOR });

expenditure = await colony.getExpenditure(expenditureId);
expect(expenditure.status).to.eq.BN(CANCELLED);
});

it("a non-root user cannot setDefaultGlobalClaimDelay", async () => {
await checkErrorRevert(colony.setDefaultGlobalClaimDelay(0, { from: ADMIN }), "ds-auth-unauthorized");
});
Expand Down Expand Up @@ -179,6 +193,7 @@ contract("Colony Expenditure", (accounts) => {
it("should error if the expenditure does not exist", async () => {
await checkErrorRevert(colony.setExpenditureSkills(100, [SLOT0], [localSkillId]), "colony-expenditure-does-not-exist");
await checkErrorRevert(colony.transferExpenditure(100, USER), "colony-expenditure-does-not-exist");
await checkErrorRevert(colony.cancelExpenditure(100, { from: ARBITRATOR }), "colony-expenditure-does-not-exist");
await checkErrorRevert(
colony.transferExpenditureViaArbitration(0, UINT256_MAX, 100, USER, { from: ARBITRATOR }),
"colony-expenditure-does-not-exist",
Expand Down Expand Up @@ -460,6 +475,15 @@ contract("Colony Expenditure", (accounts) => {
await checkErrorRevert(colony.setExpenditurePayout(expenditureId, SLOT0, token.address, WAD, { from: ADMIN }), "colony-expenditure-not-draft");
});

it("should not allow arbitration to cancel an expenditure that has been finalized", async () => {
await colony.finalizeExpenditure(expenditureId, { from: ADMIN });

await checkErrorRevert(
colony.cancelExpenditureViaArbitration(1, UINT256_MAX, expenditureId, { from: ARBITRATOR }),
"colony-expenditure-not-draft-or-locked",
);
});

it("should not allow non-owners to set a payout", async () => {
await checkErrorRevert(colony.setExpenditurePayout(expenditureId, SLOT0, token.address, WAD, { from: USER }), "colony-expenditure-not-owner");
});
Expand Down
3 changes: 3 additions & 0 deletions test/extensions/one-tx-payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const {
downgradeColonyNetwork,
deployColonyVersionGLWSS4,
deployColonyNetworkVersionGLWSS4,
deployColonyVersionHMWSS,
} = require("../../scripts/deployOldUpgradeableVersion");

contract("One transaction payments", (accounts) => {
Expand Down Expand Up @@ -557,6 +558,8 @@ contract("One transaction payments", (accounts) => {
// V5 is `glwss4`,
await deployOldExtensionVersion("OneTxPayment", "OneTxPayment", ["OneTxPayment"], "glwss4", colonyNetwork);
await deployColonyNetworkVersionGLWSS4();
await deployColonyVersionGLWSS4(colonyNetwork);
await deployColonyVersionHMWSS(colonyNetwork);
});

beforeEach(async () => {
Expand Down

0 comments on commit af3088b

Please sign in to comment.