Skip to content

Commit

Permalink
finalizeExpenditureViaArbitration
Browse files Browse the repository at this point in the history
  • Loading branch information
area authored and kronosapiens committed Mar 12, 2024
1 parent 250ef53 commit e9e9abe
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 9 deletions.
3 changes: 3 additions & 0 deletions contracts/colony/Colony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP

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

sig = bytes4(keccak256("finalizeExpenditureViaArbitration(uint256,uint256,uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Arbitration), address(this), sig, true);
}

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 @@ -129,7 +129,9 @@ contract ColonyAuthority is CommonAuthority {
// Added in colony v10 (ginger-lwss)
addRoleCapability(ARBITRATION_ROLE, "setExpenditurePayout(uint256,uint256,uint256,uint256,address,uint256)");

// Added in colony v15 (hazel-lwss-2)
addRoleCapability(ARBITRATION_ROLE, "cancelExpenditureViaArbitration(uint256,uint256,uint256)");
addRoleCapability(ARBITRATION_ROLE, "finalizeExpenditureViaArbitration(uint256,uint256,uint256)");
}

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

function finalizeExpenditureViaArbitration(
uint256 _permissionDomainId,
uint256 _childSkillIndex,
uint256 _id
)
public
stoppable
expenditureDraftOrLocked(_id)
authDomain(_permissionDomainId, _childSkillIndex, expenditures[_id].domainId)
{
FundingPot storage fundingPot = fundingPots[expenditures[_id].fundingPotId];
require(fundingPot.payoutsWeCannotMake == 0, "colony-expenditure-not-funded");

expenditures[_id].status = ExpenditureStatus.Finalized;
expenditures[_id].finalizedTimestamp = block.timestamp;

emit ExpenditureFinalized(msgSender(), _id);
}

function finalizeExpenditure(
uint256 _id
) public stoppable expenditureDraftOrLocked(_id) expenditureOnlyOwner(_id) {
Expand Down
10 changes: 10 additions & 0 deletions contracts/colony/IColony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,16 @@ interface IColony is ColonyDataTypes, IRecovery, IBasicMetaTransaction, IMultica
/// @param _id Expenditure identifier
function finalizeExpenditure(uint256 _id) external;

/// @notice Finalizes the expenditure and allows for funds to be claimed. Can only be called by expenditure owner.
/// @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`,
/// @param _id Expenditure identifier
function finalizeExpenditureViaArbitration(
uint256 _permissionDomainId,
uint256 _childSkillIndex,
uint256 _id
) external;

/// @notice Sets the metadata for an expenditure. Can only be called by expenditure owner.
/// @dev Can only be called while expenditure is in draft state.
/// @param _id Id of the expenditure
Expand Down
14 changes: 14 additions & 0 deletions docs/interfaces/icolony.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,20 @@ Finalizes the expenditure and allows for funds to be claimed. Can only be called
|_id|uint256|Expenditure identifier


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

Finalizes the expenditure and allows for funds to be claimed. Can only be called by expenditure owner.


**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`,
|_id|uint256|Expenditure identifier


### `finalizeRewardPayout(uint256 _payoutId)`

Finalises the reward payout. Allows creation of next reward payouts for token that has been used in `_payoutId`. Can only be called when reward payout cycle is finished i.e when 60 days have passed from its creation.
Expand Down
15 changes: 14 additions & 1 deletion scripts/deployOldUpgradeableVersion.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const Promise = require("bluebird");
const exec = Promise.promisify(require("child_process").exec);
const contract = require("@truffle/contract");
const { getColonyEditable, getColonyNetworkEditable, web3GetCode } = require("../helpers/test-helper");
const { ROOT_ROLE, RECOVERY_ROLE, ADMINISTRATION_ROLE, ARCHITECTURE_ROLE } = require("../helpers/constants");
const { ROOT_ROLE, RECOVERY_ROLE, ADMINISTRATION_ROLE, ARCHITECTURE_ROLE, ADDRESS_ZERO } = require("../helpers/constants");

const colonyDeployed = {};
const colonyNetworkDeployed = {};
Expand Down Expand Up @@ -92,7 +92,19 @@ module.exports.deployOldColonyVersion = async (contractName, interfaceName, impl
// Already deployed... if truffle's not snapshotted it away. See if there's any code there.
const { resolverAddress } = colonyDeployed[interfaceName][versionTag];
const code = await web3GetCode(resolverAddress);
console.log(versionTag, "code", code);
if (code !== "0x") {
// Could be a different colony network. Check it's registered with the network.
const resolver = await artifacts.require("Resolver").at(resolverAddress);
const versionImplementationAddress = await resolver.lookup(web3.utils.soliditySha3("version()").slice(0, 10));
const versionImplementation = await artifacts.require("IMetaColony").at(versionImplementationAddress);
const version = await versionImplementation.version();
const registeredResolverAddress = await colonyNetwork.getColonyVersionResolver(version);
if (registeredResolverAddress === ADDRESS_ZERO) {
const metaColonyAddress = await colonyNetwork.getMetaColony();
const metaColony = await artifacts.require("IMetaColony").at(metaColonyAddress);
await metaColony.addNetworkColonyVersion(version, resolverAddress);
}
return colonyDeployed[interfaceName][versionTag];
}
}
Expand Down Expand Up @@ -257,6 +269,7 @@ module.exports.deployOldUpgradeableVersion = async (contractName, interfaceName,

const network = process.env.SOLIDITY_COVERAGE ? "coverage" : "development";
let res;
console.log("deploying");

try {
res = await exec(
Expand Down
5 changes: 2 additions & 3 deletions test-gas-costs/gasCosts.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,8 @@ contract("All", function (accounts) {
// 1 tx payment to one recipient, with skill
await oneTxExtension.makePayment(1, UINT256_MAX, 1, UINT256_MAX, [WORKER], [token.address], [10], 1, localSkillId);

const firstToken = token.address < otherToken.address ? token.address : otherToken.address;
const secondToken = token.address < otherToken.address ? otherToken.address : token.address;

const firstToken = token.address.toLowerCase() < otherToken.address.toLowerCase() ? token.address : otherToken.address;
const secondToken = token.address.toLowerCase() < otherToken.address.toLowerCase() ? otherToken.address : token.address;
// 1 tx payment to one recipient, two tokens
await oneTxExtension.makePayment(1, UINT256_MAX, 1, UINT256_MAX, [WORKER, WORKER], [firstToken, secondToken], [10, 10], 1, 0);

Expand Down
10 changes: 5 additions & 5 deletions test-smoke/colony-storage-consistent.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,11 @@ contract("Contract Storage", (accounts) => {
console.log("miningCycleStateHash:", miningCycleStateHash);
console.log("tokenLockingStateHash:", tokenLockingStateHash);

expect(colonyNetworkStateHash).to.equal("0x8ddc8d3b55aa9dcc332854cfdc05d470306b1352cd1c7cb463149b263e23000e");
expect(colonyStateHash).to.equal("0x545133a18e7ed2a90179ea3661bcca3817b4d545bddbce1dad2eb7a3e1c66111");
expect(metaColonyStateHash).to.equal("0x6c1447525a40a2d3fabea2a758043a52c9d44ee4fdf1e65f956810bdcc19e0cf");
expect(miningCycleStateHash).to.equal("0x5f04f203ae1ca038a9e86f46cadb22f9a5f75d70732b4af7415ef627bfe153e9");
expect(tokenLockingStateHash).to.equal("0x06cb0760dd2c02417a7577013c119523e123aeb2bbc8343d278d2b94fd2652ce");
expect(colonyNetworkStateHash).to.equal("0x7b43f3e7e6cda0d4828db085a635f3bfa5513595d3048b835eac558070b8980f");
expect(colonyStateHash).to.equal("0x3fd9f27a6b7e09500e5ec9314027a47477d03d01b4a2f5c305cd98c74205c647");
expect(metaColonyStateHash).to.equal("0x87a14b838f1db5f0bd5a883cfad2f1ef124cc822ea4c9a124531b54676843864");
expect(miningCycleStateHash).to.equal("0xd59299ca385c8d9795a56de6dcaea40048712832669421091e132db492ee84bc");
expect(tokenLockingStateHash).to.equal("0x871a5dedede31530886db450e3aaec934d643989910a7c225ded0127cecd65e9");
});
});
});
16 changes: 16 additions & 0 deletions test/contracts-network/colony-expenditure.js
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,22 @@ contract("Colony Expenditure", (accounts) => {

await colony.finalizeExpenditure(expenditureId, { from: ADMIN });
});

it("should allow non-owners with arbitration permission to finalise expenditures, but not repeatedly", async () => {
let expenditure = await colony.getExpenditure(expenditureId);
expect(expenditure.owner).to.equal(ADMIN);

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

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

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

describe("when claiming expenditures", () => {
Expand Down
1 change: 1 addition & 0 deletions test/contracts-network/colony-recovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ contract("Colony Recovery", (accounts) => {
await checkErrorRevert(metaColony.cancelExpenditure(0), "colony-in-recovery-mode");
await checkErrorRevert(metaColony.lockExpenditure(0), "colony-in-recovery-mode");
await checkErrorRevert(metaColony.finalizeExpenditure(0), "colony-in-recovery-mode");
await checkErrorRevert(metaColony.finalizeExpenditureViaArbitration(0, 0, 0), "colony-in-recovery-mode");
await checkErrorRevert(metaColony.setExpenditureMetadata(0, ""), "colony-in-recovery-mode");
await checkErrorRevert(metaColony.setExpenditureMetadata(0, 0, 0, ""), "colony-in-recovery-mode");
await checkErrorRevert(metaColony.setExpenditureRecipients(0, [], []), "colony-in-recovery-mode");
Expand Down

0 comments on commit e9e9abe

Please sign in to comment.