Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Korporatio extension #1122

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
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
244 changes: 244 additions & 0 deletions contracts/extensions/Korporatio.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/*
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 <http://www.gnu.org/licenses/>.
*/

pragma solidity 0.8.19;
pragma experimental ABIEncoderV2;

import "./../../lib/dappsys/erc20.sol";
import "./../colonyNetwork/IColonyNetwork.sol";
import "./ColonyExtensionMeta.sol";

// ignore-file-swc-108


contract Korporatio is ColonyExtensionMeta {

// Constants

// Events

event ApplicationCreated(uint256 indexed stakeId, address indexed applicant);
event ApplicationCancelled(uint256 indexed stakeId);
event StakeReclaimed(uint256 indexed stakeId);
event ApplicationDeleted(uint256 indexed stakeId, bool punish);
event ApplicationUpdated(uint256 indexed stakeId, bytes32 ipfsHash);
event ApplicationSubmitted(uint256 indexed stakeId);

// Data structures

struct Application {
address applicant;
uint256 stakeAmount;
uint256 cancelledAt;
}

// Storage

address colonyNetworkAddress;

uint256 stakeFraction;
uint256 repFraction;
uint256 claimDelay;

uint256 numApplications;
mapping (uint256 => Application) applications;

// Modifiers

modifier onlyApplicant(uint256 _applicationId) {
require(msgSender() == applications[_applicationId].applicant, "korporatio-not-applicant");
_;
}

// Overrides

/// @notice Returns the identifier of the extension
function identifier() public override pure returns (bytes32) {
return keccak256("Korporatio");
}

/// @notice Returns the version of the extension
function version() public override pure returns (uint256) {
return 1;
}

/// @notice Configures the extension
/// @param _colony The colony in which the extension holds permissions
function install(address _colony) public override auth {
require(address(colony) == address(0x0), "extension-already-installed");

colony = IColony(_colony);
colonyNetworkAddress = colony.getColonyNetwork();
}

/// @notice Called when upgrading the extension
function finishUpgrade() public override auth {}

/// @notice Called when deprecating (or undeprecating) the extension
function deprecate(bool _deprecated) public override auth {
deprecated = _deprecated;
}

/// @notice Called when uninstalling the extension
function uninstall() public override auth {
selfdestruct(payable(address(colony)));
}

// Public

function initialise(uint256 _stakeFraction, uint256 _repFraction, uint256 _claimDelay) public {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many of the public functions are missing natspec comments. In particular, be sure to include when something is a wad.

require(
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Architecture),
"korporatio-not-root-architect"
);

stakeFraction = _stakeFraction;
repFraction = _repFraction;
claimDelay = _claimDelay;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three of these are missing validation on the supplied values.

}

function createApplication(
bytes memory _colonyKey,
bytes memory _colonyValue,
uint256 _colonyBranchMask,
bytes32[] memory _colonySiblings,
bytes memory _userKey,
bytes memory _userValue,
uint256 _userBranchMask,
bytes32[] memory _userSiblings
)
public
notDeprecated
{
require(stakeFraction > 0, "korporatio-not-initialised");

bytes32 rootHash = IColonyNetwork(colonyNetworkAddress).getReputationRootHash();
uint256 rootSkillId = colony.getDomain(1).skillId;

uint256 colonyReputation = checkReputation(rootHash, rootSkillId, address(0x0), _colonyKey, _colonyValue, _colonyBranchMask, _colonySiblings);
uint256 userReputation = checkReputation(rootHash, rootSkillId, msgSender(), _userKey, _userValue, _userBranchMask, _userSiblings);

uint256 requiredStake = wmul(colonyReputation, repFraction);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given things key off of stakeAmount, should it be required to be nonzero here to ensure the lifecycle doesn't go off the rails? (After talking to Arren, it should be)

Or possibly nothing should be gated behind stakeAmount as there is the option to create without a stake.

require(userReputation >= requiredStake, "korporatio-insufficient-rep");
kronosapiens marked this conversation as resolved.
Show resolved Hide resolved

applications[++numApplications] = Application({
applicant: msgSender(),
stakeAmount: requiredStake,
cancelledAt: UINT256_MAX
});

colony.obligateStake(msgSender(), 1, requiredStake);

emit ApplicationCreated(numApplications, msgSender());
}

function createApplication() public notDeprecated {
require (
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Root) ||
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Administration),
"korporatio-must-submit-stake"
);

applications[++numApplications] = Application({
applicant: msgSender(),
stakeAmount: 0,
cancelledAt: UINT256_MAX
});

emit ApplicationCreated(numApplications, msgSender());
}

function cancelApplication(uint256 _applicationId) public {
require(msgSender() == applications[_applicationId].applicant, "korporatio-not-applicant");
applications[_applicationId].cancelledAt = block.timestamp;

emit ApplicationCancelled(_applicationId);
}

function reclaimStake(uint256 _applicationId) public {
Application storage application = applications[_applicationId];
require(application.applicant == msgSender(), "korporatio-not-applicant");
require(application.cancelledAt + claimDelay <= block.timestamp, "korporatio-cannot-reclaim");

uint256 stakeAmount = application.stakeAmount;
delete applications[_applicationId];

colony.deobligateStake(msgSender(), 1, stakeAmount);
kronosapiens marked this conversation as resolved.
Show resolved Hide resolved

emit StakeReclaimed(_applicationId);
}

function deleteApplication(uint256 _applicationId, bool _punish) public {
require(applications[_applicationId].stakeAmount > 0, "korporatio-cannot-slash");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should presumably work when an application has been made without staking?


require(
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Arbitration),
"korporatio-caller-not-arbitration"
);

address applicant = applications[_applicationId].applicant;
uint256 stakeAmount = applications[_applicationId].stakeAmount;
delete applications[_applicationId];

if (_punish) {
colony.emitDomainReputationPenalty(1, UINT256_MAX, 1, applicant, -int256(stakeAmount));
colony.transferStake(1, UINT256_MAX, address(this), applicant, 1, stakeAmount, address(0x0));
} else {
colony.deobligateStake(applicant, 1, stakeAmount);
}

emit ApplicationDeleted(_applicationId, _punish);
}

function updateApplication(uint256 _applicationId, bytes32 _ipfsHash) public {
require(applications[_applicationId].cancelledAt == UINT256_MAX, "korporatio-stake-cancelled");
require(
msgSender() == applications[_applicationId].applicant ||
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Root),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still does not meet the specification:

> Anyone else can make change requests to the application, but changes will require a motion.

I get the intention (we give the Voting Extension root permission) but it allows anyone with root permission to update an application.

EDIT: After talking with Arren, it being controlled by the Root permission is acceptable. I've still left my original comment just to (admittedly) let me get on my traditional soapbox and say it is important to either a) Stick to the specification or b) get the specification changed by the owner of it if you disagree with it.

"korporatio-not-applicant-or-root"
);

emit ApplicationUpdated(_applicationId, _ipfsHash);
}

function submitApplication(uint256 _applicationId) public {
require(colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Root), "korporatio-caller-not-root");
kronosapiens marked this conversation as resolved.
Show resolved Hide resolved
require(applications[_applicationId].cancelledAt == UINT256_MAX, "korporatio-stake-cancelled");

applications[_applicationId].cancelledAt = block.timestamp;

emit ApplicationSubmitted(_applicationId);
}

// View

function getStakeFraction() external view returns (uint256) {
return stakeFraction;
}

function getClaimDelay() external view returns (uint256) {
return claimDelay;
}

function getNumApplications() external view returns (uint256) {
return numApplications;
}

function getApplication(uint256 _id) external view returns (Application memory application) {
application = applications[_id];
}
}
2 changes: 2 additions & 0 deletions migrations/9_setup_extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const VotingReputation = artifacts.require("./VotingReputation");
const VotingReputationMisalignedRecovery = artifacts.require("./VotingReputationMisalignedRecovery");
const TokenSupplier = artifacts.require("./TokenSupplier");
const Whitelist = artifacts.require("./Whitelist");
const Korporatio = artifacts.require("./Korporatio");

const Resolver = artifacts.require("./Resolver");
const EtherRouter = artifacts.require("./EtherRouter");
Expand Down Expand Up @@ -53,4 +54,5 @@ module.exports = async function (deployer, network, accounts) {
await addExtension(colonyNetwork, "TokenSupplier", "TokenSupplier", [TokenSupplier]);
await addExtension(colonyNetwork, "IVotingReputation", "VotingReputation", [VotingReputation, VotingReputationMisalignedRecovery]);
await addExtension(colonyNetwork, "Whitelist", "Whitelist", [Whitelist]);
await addExtension(colonyNetwork, "Korporatio", "Korporatio", [Korporatio]);
};
1 change: 1 addition & 0 deletions scripts/check-recovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ walkSync("./contracts/").forEach((contractName) => {
"contracts/extensions/EvaluatedExpenditure.sol",
"contracts/extensions/StakedExpenditure.sol",
"contracts/extensions/FundingQueue.sol",
"contracts/extensions/Korporatio.sol",
"contracts/extensions/OneTxPayment.sol",
"contracts/extensions/ReputationBootstrapper.sol",
"contracts/extensions/StreamingPayments.sol",
Expand Down
1 change: 1 addition & 0 deletions scripts/check-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ walkSync("./contracts/").forEach((contractName) => {
"contracts/extensions/FundingQueue.sol",
"contracts/extensions/ColonyExtension.sol",
"contracts/extensions/ColonyExtensionMeta.sol",
"contracts/extensions/Korporatio.sol",
"contracts/extensions/OneTxPayment.sol",
"contracts/extensions/ReputationBootstrapper.sol",
"contracts/extensions/StreamingPayments.sol",
Expand Down
4 changes: 2 additions & 2 deletions test-smoke/colony-storage-consistent.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ contract("Contract Storage", (accounts) => {
console.log("miningCycleStateHash:", miningCycleStateHash);
console.log("tokenLockingStateHash:", tokenLockingStateHash);

expect(colonyNetworkStateHash).to.equal("0x711cd2107e1466eee7e531b4abee44853d8ac1a0708f31004a615f57f3de819c");
expect(colonyStateHash).to.equal("0x54a0edcb2097270bd95d610dc827869cc827241d131461f58788f7c3257ca151");
expect(colonyNetworkStateHash).to.equal("0xc4320825b94c76e32b6e2f05dceb178ad070568dd470a469282f620f3152c7ce");
expect(colonyStateHash).to.equal("0xdb586290061130dacc48f11eb373e4a01baf177c968ab68679d25e1794074f3a");
expect(metaColonyStateHash).to.equal("0x15fab25907cfb6baedeaf1fdabd68678d37584a1817a08dfe77db60db378a508");
expect(miningCycleStateHash).to.equal("0x632d459a2197708bd2dbde87e8275c47dddcdf16d59e3efd21dcef9acb2a7366");
expect(tokenLockingStateHash).to.equal("0x30fbcbfbe589329fe20288101faabe1f60a4610ae0c0effb15526c6b390a8e07");
Expand Down
Loading