Skip to content

Commit 99e9fc3

Browse files
committed
Introduce Korporatio extension
1 parent 4621331 commit 99e9fc3

File tree

6 files changed

+647
-2
lines changed

6 files changed

+647
-2
lines changed
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
This file is part of The Colony Network.
3+
4+
The Colony Network is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
The Colony Network is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with The Colony Network. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
pragma solidity 0.7.3;
19+
pragma experimental ABIEncoderV2;
20+
21+
import "./../../lib/dappsys/erc20.sol";
22+
import "./../colonyNetwork/IColonyNetwork.sol";
23+
import "./ColonyExtensionMeta.sol";
24+
25+
// ignore-file-swc-108
26+
27+
28+
contract Korporatio is ColonyExtensionMeta {
29+
30+
// Constants
31+
32+
uint256 constant APPLICATION_FEE = 6500 * WAD;
33+
34+
// Events
35+
36+
event ApplicationCreated(uint256 indexed stakeId, address indexed applicant);
37+
event ApplicationCancelled(uint256 indexed stakeId);
38+
event StakeReclaimed(uint256 indexed stakeId);
39+
event StakeSlashed(uint256 indexed stakeId);
40+
event ApplicationUpdated(uint256 indexed stakeId, bytes32 ipfsHash);
41+
event ApplicationSubmitted(uint256 indexed stakeId, bytes32 ipfsHash);
42+
43+
// Data structures
44+
45+
struct Application {
46+
address applicant;
47+
uint256 stakeAmount;
48+
uint256 cancelledAt;
49+
}
50+
51+
// Storage
52+
53+
address colonyNetworkAddress;
54+
55+
address paymentToken;
56+
uint256 applicationFee;
57+
uint256 stakeFraction;
58+
uint256 claimDelay;
59+
60+
uint256 numApplications;
61+
mapping (uint256 => Application) applications;
62+
63+
// Modifiers
64+
65+
// Overrides
66+
67+
/// @notice Returns the identifier of the extension
68+
function identifier() public override pure returns (bytes32) {
69+
return keccak256("Korporatio");
70+
}
71+
72+
/// @notice Returns the version of the extension
73+
function version() public override pure returns (uint256) {
74+
return 1;
75+
}
76+
77+
/// @notice Configures the extension
78+
/// @param _colony The colony in which the extension holds permissions
79+
function install(address _colony) public override auth {
80+
require(address(colony) == address(0x0), "extension-already-installed");
81+
82+
colony = IColony(_colony);
83+
colonyNetworkAddress = colony.getColonyNetwork();
84+
}
85+
86+
/// @notice Called when upgrading the extension
87+
function finishUpgrade() public override auth {}
88+
89+
/// @notice Called when deprecating (or undeprecating) the extension
90+
function deprecate(bool _deprecated) public override auth {
91+
deprecated = _deprecated;
92+
}
93+
94+
/// @notice Called when uninstalling the extension
95+
function uninstall() public override auth {
96+
selfdestruct(address(uint160(address(colony))));
97+
}
98+
99+
// Public
100+
101+
function initialise(
102+
address _paymentToken,
103+
uint256 _applicationFee,
104+
uint256 _stakeFraction,
105+
uint256 _claimDelay
106+
)
107+
public
108+
{
109+
require(
110+
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Architecture),
111+
"korporatio-not-root-architect"
112+
);
113+
114+
paymentToken = _paymentToken;
115+
applicationFee = _applicationFee;
116+
stakeFraction = _stakeFraction;
117+
claimDelay = _claimDelay;
118+
}
119+
120+
function createApplication(
121+
bytes memory _colonyKey,
122+
bytes memory _colonyValue,
123+
uint256 _colonyBranchMask,
124+
bytes32[] memory _colonySiblings,
125+
bytes memory _userKey,
126+
bytes memory _userValue,
127+
uint256 _userBranchMask,
128+
bytes32[] memory _userSiblings
129+
)
130+
public
131+
notDeprecated
132+
{
133+
bytes32 rootHash = IColonyNetwork(colonyNetworkAddress).getReputationRootHash();
134+
uint256 rootSkillId = colony.getDomain(1).skillId;
135+
136+
uint256 colonyReputation = checkReputation(rootHash, rootSkillId, address(0x0), _colonyKey, _colonyValue, _colonyBranchMask, _colonySiblings);
137+
uint256 userReputation = checkReputation(rootHash, rootSkillId, msgSender(), _userKey, _userValue, _userBranchMask, _userSiblings);
138+
139+
uint256 requiredStake = wmul(colonyReputation, stakeFraction);
140+
require(userReputation >= requiredStake, "korporatio-insufficient-rep");
141+
142+
applications[++numApplications] = Application({
143+
applicant: msgSender(),
144+
stakeAmount: requiredStake,
145+
cancelledAt: UINT256_MAX
146+
});
147+
148+
colony.obligateStake(msgSender(), 1, requiredStake);
149+
150+
emit ApplicationCreated(numApplications, msgSender());
151+
}
152+
153+
function createFreeApplication() public notDeprecated {
154+
require (
155+
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Root) ||
156+
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Administration),
157+
"korporatio-must-submit-stake"
158+
);
159+
160+
applications[++numApplications] = Application({
161+
applicant: msgSender(),
162+
stakeAmount: 0,
163+
cancelledAt: UINT256_MAX
164+
});
165+
166+
emit ApplicationCreated(numApplications, msgSender());
167+
}
168+
169+
function cancelApplication(uint256 _applicationId) public {
170+
require(applications[_applicationId].applicant == msgSender(), "korporatio-cannot-cancel");
171+
172+
applications[_applicationId].cancelledAt = block.timestamp;
173+
174+
emit ApplicationCancelled(_applicationId);
175+
}
176+
177+
function reclaimStake(uint256 _applicationId) public {
178+
require(
179+
applications[_applicationId].cancelledAt + claimDelay <= block.timestamp,
180+
"korporatio-cannot-reclaim"
181+
);
182+
183+
uint256 stakeAmount = applications[_applicationId].stakeAmount;
184+
delete applications[_applicationId];
185+
186+
colony.deobligateStake(msgSender(), 1, stakeAmount);
187+
188+
emit StakeReclaimed(_applicationId);
189+
}
190+
191+
function slashStake(uint256 _applicationId, bool _punish) public {
192+
require(applications[_applicationId].stakeAmount > 0, "korporatio-cannot-slash");
193+
194+
require(
195+
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Arbitration),
196+
"korporatio-caller-not-arbitration"
197+
);
198+
199+
address applicant = applications[_applicationId].applicant;
200+
uint256 stakeAmount = applications[_applicationId].stakeAmount;
201+
delete applications[_applicationId];
202+
203+
colony.transferStake(1, UINT256_MAX, address(this), applicant, 1, stakeAmount, address(0x0));
204+
if (_punish) { colony.emitDomainReputationPenalty(1, UINT256_MAX, 1, applicant, -int256(stakeAmount)); }
205+
206+
emit StakeSlashed(_applicationId);
207+
}
208+
209+
function updateApplication(uint256 _applicationId, bytes32 _ipfsHash) public {
210+
require(applications[_applicationId].applicant == msgSender(), "korporatio-not-applicant");
211+
require(applications[_applicationId].cancelledAt == UINT256_MAX, "korporatio-stake-cancelled");
212+
213+
emit ApplicationUpdated(_applicationId, _ipfsHash);
214+
}
215+
216+
function submitApplication(uint256 _applicationId, bytes32 _ipfsHash) public {
217+
require(colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Root), "korporatio-caller-not-root");
218+
require(applications[_applicationId].cancelledAt == UINT256_MAX, "korporatio-stake-cancelled");
219+
220+
applications[_applicationId].cancelledAt = block.timestamp;
221+
222+
address metaColony = IColonyNetwork(colonyNetworkAddress).getMetaColony();
223+
require(ERC20(paymentToken).transferFrom(msgSender(), metaColony, applicationFee), "korporatio-transfer-failed");
224+
225+
emit ApplicationSubmitted(_applicationId, _ipfsHash);
226+
}
227+
228+
// View
229+
230+
function getPaymentToken() external view returns (address) {
231+
return paymentToken;
232+
}
233+
234+
function getApplicationFee() external view returns (uint256) {
235+
return applicationFee;
236+
}
237+
238+
function getStakeFraction() external view returns (uint256) {
239+
return stakeFraction;
240+
}
241+
242+
function getClaimDelay() external view returns (uint256) {
243+
return claimDelay;
244+
}
245+
246+
function getNumApplications() external view returns (uint256) {
247+
return numApplications;
248+
}
249+
250+
function getApplication(uint256 _id) external view returns (Application memory application) {
251+
application = applications[_id];
252+
}
253+
}

migrations/9_setup_extensions.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const VotingReputation = artifacts.require("./VotingReputation");
1515
const VotingReputationMisalignedRecovery = artifacts.require("./VotingReputationMisalignedRecovery");
1616
const TokenSupplier = artifacts.require("./TokenSupplier");
1717
const Whitelist = artifacts.require("./Whitelist");
18+
const Korporatio = artifacts.require("./Korporatio");
1819

1920
const Resolver = artifacts.require("./Resolver");
2021
const EtherRouter = artifacts.require("./EtherRouter");
@@ -53,4 +54,5 @@ module.exports = async function (deployer, network, accounts) {
5354
await addExtension(colonyNetwork, "TokenSupplier", "TokenSupplier", [TokenSupplier]);
5455
await addExtension(colonyNetwork, "IVotingReputation", "VotingReputation", [VotingReputation, VotingReputationMisalignedRecovery]);
5556
await addExtension(colonyNetwork, "Whitelist", "Whitelist", [Whitelist]);
57+
await addExtension(colonyNetwork, "Korporatio", "Korporatio", [Korporatio]);
5658
};

scripts/check-recovery.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ walkSync("./contracts/").forEach((contractName) => {
5353
"contracts/extensions/EvaluatedExpenditure.sol",
5454
"contracts/extensions/StakedExpenditure.sol",
5555
"contracts/extensions/FundingQueue.sol",
56+
"contracts/extensions/Korporatio.sol",
5657
"contracts/extensions/OneTxPayment.sol",
5758
"contracts/extensions/ReputationBootstrapper.sol",
5859
"contracts/extensions/StreamingPayments.sol",

scripts/check-storage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ walkSync("./contracts/").forEach((contractName) => {
3333
"contracts/extensions/FundingQueue.sol",
3434
"contracts/extensions/ColonyExtension.sol",
3535
"contracts/extensions/ColonyExtensionMeta.sol",
36+
"contracts/extensions/Korporatio.sol",
3637
"contracts/extensions/OneTxPayment.sol",
3738
"contracts/extensions/ReputationBootstrapper.sol",
3839
"contracts/extensions/StreamingPayments.sol",

test-smoke/colony-storage-consistent.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ contract("Contract Storage", (accounts) => {
149149
console.log("miningCycleStateHash:", miningCycleStateHash);
150150
console.log("tokenLockingStateHash:", tokenLockingStateHash);
151151

152-
expect(colonyNetworkStateHash).to.equal("0x2d9b33d6c639d46a7c9b73e5e488cc624886105a574d05c3e465c6e6da27f75f");
153-
expect(colonyStateHash).to.equal("0x54a0edcb2097270bd95d610dc827869cc827241d131461f58788f7c3257ca151");
152+
expect(colonyNetworkStateHash).to.equal("0x7539cecc0ff5cc09e32b43de9d7972ad2b4fc876942783481dcbe2905682837f");
153+
expect(colonyStateHash).to.equal("0xefa81fadaf3890213272d8621d411d8d476997660d320aa9bcf5d6d3b898f3b3");
154154
expect(metaColonyStateHash).to.equal("0x15fab25907cfb6baedeaf1fdabd68678d37584a1817a08dfe77db60db378a508");
155155
expect(miningCycleStateHash).to.equal("0x632d459a2197708bd2dbde87e8275c47dddcdf16d59e3efd21dcef9acb2a7366");
156156
expect(tokenLockingStateHash).to.equal("0x30fbcbfbe589329fe20288101faabe1f60a4610ae0c0effb15526c6b390a8e07");

0 commit comments

Comments
 (0)