Skip to content

Commit

Permalink
Introduce Korporatio extension
Browse files Browse the repository at this point in the history
  • Loading branch information
kronosapiens committed Mar 20, 2023
1 parent b6c9cd2 commit 95aa18a
Show file tree
Hide file tree
Showing 6 changed files with 647 additions and 2 deletions.
253 changes: 253 additions & 0 deletions contracts/extensions/Korporatio.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/*
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

uint256 constant APPLICATION_FEE = 6500 * WAD;

// Events

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

// Data structures

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

// Storage

address colonyNetworkAddress;

address paymentToken;
uint256 applicationFee;
uint256 stakeFraction;
uint256 claimDelay;

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

// Modifiers

// 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(
address _paymentToken,
uint256 _applicationFee,
uint256 _stakeFraction,
uint256 _claimDelay
)
public
{
require(
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Architecture),
"korporatio-not-root-architect"
);

paymentToken = _paymentToken;
applicationFee = _applicationFee;
stakeFraction = _stakeFraction;
claimDelay = _claimDelay;
}

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
{
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, stakeFraction);
require(userReputation >= requiredStake, "korporatio-insufficient-rep");

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

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

emit ApplicationCreated(numApplications, msgSender());
}

function createFreeApplication() 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(applications[_applicationId].applicant == msgSender(), "korporatio-cannot-cancel");

applications[_applicationId].cancelledAt = block.timestamp;

emit ApplicationCancelled(_applicationId);
}

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

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

colony.deobligateStake(msgSender(), 1, stakeAmount);

emit StakeReclaimed(_applicationId);
}

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

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];

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

emit StakeSlashed(_applicationId);
}

function updateApplication(uint256 _applicationId, bytes32 _ipfsHash) public {
require(applications[_applicationId].applicant == msgSender(), "korporatio-not-applicant");
require(applications[_applicationId].cancelledAt == UINT256_MAX, "korporatio-stake-cancelled");

emit ApplicationUpdated(_applicationId, _ipfsHash);
}

function submitApplication(uint256 _applicationId, bytes32 _ipfsHash) public {
require(colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Root), "korporatio-caller-not-root");
require(applications[_applicationId].cancelledAt == UINT256_MAX, "korporatio-stake-cancelled");

applications[_applicationId].cancelledAt = block.timestamp;

address metaColony = IColonyNetwork(colonyNetworkAddress).getMetaColony();
require(ERC20(paymentToken).transferFrom(msgSender(), metaColony, applicationFee), "korporatio-transfer-failed");

emit ApplicationSubmitted(_applicationId, _ipfsHash);
}

// View

function getPaymentToken() external view returns (address) {
return paymentToken;
}

function getApplicationFee() external view returns (uint256) {
return applicationFee;
}

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("0xefa81fadaf3890213272d8621d411d8d476997660d320aa9bcf5d6d3b898f3b3");
expect(metaColonyStateHash).to.equal("0x15fab25907cfb6baedeaf1fdabd68678d37584a1817a08dfe77db60db378a508");
expect(miningCycleStateHash).to.equal("0x632d459a2197708bd2dbde87e8275c47dddcdf16d59e3efd21dcef9acb2a7366");
expect(tokenLockingStateHash).to.equal("0x30fbcbfbe589329fe20288101faabe1f60a4610ae0c0effb15526c6b390a8e07");
Expand Down
Loading

0 comments on commit 95aa18a

Please sign in to comment.