Skip to content

Commit 0844a1c

Browse files
committed
Update in response to review comments
1 parent 082e3f0 commit 0844a1c

File tree

3 files changed

+102
-68
lines changed

3 files changed

+102
-68
lines changed

contracts/extensions/Korporatio.sol

Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,14 @@ contract Korporatio is ColonyExtensionMeta {
2929

3030
// Constants
3131

32-
uint256 constant APPLICATION_FEE = 6500 * WAD;
33-
3432
// Events
3533

3634
event ApplicationCreated(uint256 indexed stakeId, address indexed applicant);
3735
event ApplicationCancelled(uint256 indexed stakeId);
3836
event StakeReclaimed(uint256 indexed stakeId);
3937
event StakeSlashed(uint256 indexed stakeId);
4038
event ApplicationUpdated(uint256 indexed stakeId, bytes32 ipfsHash);
41-
event ApplicationSubmitted(uint256 indexed stakeId, bytes32 ipfsHash);
39+
event ApplicationSubmitted(uint256 indexed stakeId);
4240

4341
// Data structures
4442

@@ -52,8 +50,6 @@ contract Korporatio is ColonyExtensionMeta {
5250

5351
address colonyNetworkAddress;
5452

55-
address paymentToken;
56-
uint256 applicationFee;
5753
uint256 stakeFraction;
5854
uint256 claimDelay;
5955

@@ -62,6 +58,11 @@ contract Korporatio is ColonyExtensionMeta {
6258

6359
// Modifiers
6460

61+
modifier onlyApplicant(uint256 _applicationId) {
62+
require(msgSender() == applications[_applicationId].applicant, "korporatio-not-applicant");
63+
_;
64+
}
65+
6566
// Overrides
6667

6768
/// @notice Returns the identifier of the extension
@@ -98,21 +99,12 @@ contract Korporatio is ColonyExtensionMeta {
9899

99100
// Public
100101

101-
function initialise(
102-
address _paymentToken,
103-
uint256 _applicationFee,
104-
uint256 _stakeFraction,
105-
uint256 _claimDelay
106-
)
107-
public
108-
{
102+
function initialise(uint256 _stakeFraction, uint256 _claimDelay) public {
109103
require(
110104
colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Architecture),
111105
"korporatio-not-root-architect"
112106
);
113107

114-
paymentToken = _paymentToken;
115-
applicationFee = _applicationFee;
116108
stakeFraction = _stakeFraction;
117109
claimDelay = _claimDelay;
118110
}
@@ -130,6 +122,8 @@ contract Korporatio is ColonyExtensionMeta {
130122
public
131123
notDeprecated
132124
{
125+
require(stakeFraction > 0, "korporatio-not-initialised");
126+
133127
bytes32 rootHash = IColonyNetwork(colonyNetworkAddress).getReputationRootHash();
134128
uint256 rootSkillId = colony.getDomain(1).skillId;
135129

@@ -166,21 +160,18 @@ contract Korporatio is ColonyExtensionMeta {
166160
emit ApplicationCreated(numApplications, msgSender());
167161
}
168162

169-
function cancelApplication(uint256 _applicationId) public {
170-
require(applications[_applicationId].applicant == msgSender(), "korporatio-cannot-cancel");
171-
163+
function cancelApplication(uint256 _applicationId) public onlyApplicant(_applicationId) {
172164
applications[_applicationId].cancelledAt = block.timestamp;
173165

174166
emit ApplicationCancelled(_applicationId);
175167
}
176168

177-
function reclaimStake(uint256 _applicationId) public {
178-
require(
179-
applications[_applicationId].cancelledAt + claimDelay <= block.timestamp,
180-
"korporatio-cannot-reclaim"
181-
);
169+
function reclaimStake(uint256 _applicationId) public onlyApplicant(_applicationId) {
170+
Application storage application = applications[_applicationId];
171+
require(application.applicant == msgSender(), "korporatio-not-applicant");
172+
require(application.cancelledAt + claimDelay <= block.timestamp, "korporatio-cannot-reclaim");
182173

183-
uint256 stakeAmount = applications[_applicationId].stakeAmount;
174+
uint256 stakeAmount = application.stakeAmount;
184175
delete applications[_applicationId];
185176

186177
colony.deobligateStake(msgSender(), 1, stakeAmount);
@@ -206,35 +197,23 @@ contract Korporatio is ColonyExtensionMeta {
206197
emit StakeSlashed(_applicationId);
207198
}
208199

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

213203
emit ApplicationUpdated(_applicationId, _ipfsHash);
214204
}
215205

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

220210
applications[_applicationId].cancelledAt = block.timestamp;
221211

222-
address metaColony = IColonyNetwork(colonyNetworkAddress).getMetaColony();
223-
require(ERC20(paymentToken).transferFrom(msgSender(), metaColony, applicationFee), "korporatio-transfer-failed");
224-
225-
emit ApplicationSubmitted(_applicationId, _ipfsHash);
212+
emit ApplicationSubmitted(_applicationId);
226213
}
227214

228215
// View
229216

230-
function getPaymentToken() external view returns (address) {
231-
return paymentToken;
232-
}
233-
234-
function getApplicationFee() external view returns (uint256) {
235-
return applicationFee;
236-
}
237-
238217
function getStakeFraction() external view returns (uint256) {
239218
return stakeFraction;
240219
}

test-smoke/colony-storage-consistent.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ contract("Contract Storage", (accounts) => {
150150
console.log("tokenLockingStateHash:", tokenLockingStateHash);
151151

152152
expect(colonyNetworkStateHash).to.equal("0xc4320825b94c76e32b6e2f05dceb178ad070568dd470a469282f620f3152c7ce");
153-
expect(colonyStateHash).to.equal("0xefa81fadaf3890213272d8621d411d8d476997660d320aa9bcf5d6d3b898f3b3");
153+
expect(colonyStateHash).to.equal("0xdb586290061130dacc48f11eb373e4a01baf177c968ab68679d25e1794074f3a");
154154
expect(metaColonyStateHash).to.equal("0x15fab25907cfb6baedeaf1fdabd68678d37584a1817a08dfe77db60db378a508");
155155
expect(miningCycleStateHash).to.equal("0x632d459a2197708bd2dbde87e8275c47dddcdf16d59e3efd21dcef9acb2a7366");
156156
expect(tokenLockingStateHash).to.equal("0x30fbcbfbe589329fe20288101faabe1f60a4610ae0c0effb15526c6b390a8e07");

test/extensions/korporatio.js

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* globals artifacts */
22

3+
const { BN } = require("bn.js");
34
const chai = require("chai");
45
const bnChai = require("bn-chai");
56
const { ethers } = require("ethers");
@@ -16,6 +17,7 @@ const {
1617
forwardTime,
1718
getBlockTime,
1819
expectEvent,
20+
encodeTxData,
1921
} = require("../../helpers/test-helper");
2022

2123
const { setupRandomColony, getMetaTransactionParameters } = require("../../helpers/test-data-generator");
@@ -28,10 +30,12 @@ chai.use(bnChai(web3.utils.BN));
2830
const EtherRouter = artifacts.require("EtherRouter");
2931
const IColonyNetwork = artifacts.require("IColonyNetwork");
3032
const IReputationMiningCycle = artifacts.require("IReputationMiningCycle");
33+
const IVotingReputation = artifacts.require("IVotingReputation");
3134
const Korporatio = artifacts.require("Korporatio");
3235
const TokenLocking = artifacts.require("TokenLocking");
3336

3437
const KORPORATIO = soliditySha3("Korporatio");
38+
const VOTING_REPUTATION = soliditySha3("VotingReputation");
3539

3640
contract("Korporatio", (accounts) => {
3741
let colony;
@@ -60,8 +64,6 @@ contract("Korporatio", (accounts) => {
6064
const USER2 = accounts[2];
6165
const MINER = accounts[5];
6266

63-
const APPLICATION_FEE = WAD.muln(6500);
64-
6567
before(async () => {
6668
const etherRouter = await EtherRouter.deployed();
6769
colonyNetwork = await IColonyNetwork.at(etherRouter.address);
@@ -168,32 +170,34 @@ contract("Korporatio", (accounts) => {
168170
await checkErrorRevert(korporatio.deprecate(true, { from: USER1 }), "ds-auth-unauthorized");
169171
await checkErrorRevert(korporatio.uninstall({ from: USER1 }), "ds-auth-unauthorized");
170172
});
173+
174+
it("cannot create applications unless initialised", async () => {
175+
await checkErrorRevert(
176+
korporatio.createApplication(domain1Key, domain1Value, domain1Mask, domain1Siblings, user0Key, user0Value, user0Mask, user0Siblings, {
177+
from: USER0,
178+
}),
179+
"korporatio-not-initialised"
180+
);
181+
});
171182
});
172183

173184
describe("creating applications", async () => {
174185
beforeEach(async () => {
175-
await korporatio.initialise(token.address, APPLICATION_FEE, WAD.divn(100), SECONDS_PER_DAY, { from: USER0 });
176-
177186
await colony.approveStake(korporatio.address, 1, WAD, { from: USER0 });
187+
188+
await korporatio.initialise(WAD.divn(100), SECONDS_PER_DAY, { from: USER0 });
178189
});
179190

180191
it("can query for configuration params", async () => {
181-
const paymentToken = await korporatio.getPaymentToken();
182-
const applicationFee = await korporatio.getApplicationFee();
183192
const stakeFraction = await korporatio.getStakeFraction();
184193
const claimDelay = await korporatio.getClaimDelay();
185194

186-
expect(paymentToken).to.equal(token.address);
187-
expect(applicationFee).to.eq.BN(APPLICATION_FEE);
188195
expect(stakeFraction).to.eq.BN(WAD.divn(100));
189196
expect(claimDelay).to.eq.BN(SECONDS_PER_DAY);
190197
});
191198

192199
it("cannot set configuration params if not root architect", async () => {
193-
await checkErrorRevert(
194-
korporatio.initialise(token.address, APPLICATION_FEE, WAD.divn(100), SECONDS_PER_DAY, { from: USER1 }),
195-
"korporatio-not-root-architect"
196-
);
200+
await checkErrorRevert(korporatio.initialise(WAD.divn(100), SECONDS_PER_DAY, { from: USER1 }), "korporatio-not-root-architect");
197201
});
198202

199203
it("can create an application", async () => {
@@ -225,7 +229,7 @@ contract("Korporatio", (accounts) => {
225229
});
226230

227231
it("cannot create an application with insufficient rep", async () => {
228-
await korporatio.initialise(token.address, APPLICATION_FEE, WAD, SECONDS_PER_DAY, { from: USER0 });
232+
await korporatio.initialise(WAD, SECONDS_PER_DAY, { from: USER0 });
229233

230234
await checkErrorRevert(
231235
korporatio.createApplication(domain1Key, domain1Value, domain1Mask, domain1Siblings, user0Key, user0Value, user0Mask, user0Siblings, {
@@ -256,7 +260,7 @@ contract("Korporatio", (accounts) => {
256260
const applicationId = await korporatio.getNumApplications();
257261

258262
// Only applicant can cancel
259-
await checkErrorRevert(korporatio.cancelApplication(applicationId, { from: USER1 }), "korporatio-cannot-cancel");
263+
await checkErrorRevert(korporatio.cancelApplication(applicationId, { from: USER1 }), "korporatio-not-applicant");
260264

261265
const tx = await korporatio.cancelApplication(applicationId, { from: USER0 });
262266
const blockTime = await getBlockTime(tx.receipt.blockNumber);
@@ -278,7 +282,7 @@ contract("Korporatio", (accounts) => {
278282

279283
await forwardTime(SECONDS_PER_DAY, this);
280284

281-
await korporatio.reclaimStake(applicationId);
285+
await korporatio.reclaimStake(applicationId, { from: USER0 });
282286

283287
const obligation = await colony.getObligation(USER0, korporatio.address, 1);
284288
expect(obligation).to.be.zero;
@@ -331,6 +335,28 @@ contract("Korporatio", (accounts) => {
331335
await checkErrorRevert(korporatio.slashStake(applicationId, false, { from: USER2 }), "korporatio-caller-not-arbitration");
332336
});
333337

338+
it("can reclaim a stake via arbitration if the extension is deleted", async () => {
339+
const korporatioAddress = korporatio.address;
340+
await korporatio.createApplication(domain1Key, domain1Value, domain1Mask, domain1Siblings, user0Key, user0Value, user0Mask, user0Siblings, {
341+
from: USER0,
342+
});
343+
344+
const lockPre = await tokenLocking.getUserLock(token.address, USER0);
345+
const obligationPre = await colony.getObligation(USER0, korporatioAddress, 1);
346+
expect(obligationPre).to.eq.BN(WAD.divn(100).muln(3));
347+
348+
await colony.uninstallExtension(KORPORATIO, { from: USER0 });
349+
350+
await colony.transferStake(1, UINT256_MAX, korporatioAddress, USER0, 1, obligationPre, USER0, { from: USER1 });
351+
352+
const lockPost = await tokenLocking.getUserLock(token.address, USER0);
353+
const obligationPost = await colony.getObligation(USER0, korporatioAddress, 1);
354+
355+
// Obligation is zeroed out, but token balance is unchanged
356+
expect(obligationPost).to.be.zero;
357+
expect(new BN(lockPre.balance)).to.eq.BN(lockPost.balance);
358+
});
359+
334360
it("can update an application", async () => {
335361
await korporatio.createFreeApplication({ from: USER0 });
336362

@@ -348,27 +374,56 @@ contract("Korporatio", (accounts) => {
348374
await checkErrorRevert(korporatio.updateApplication(applicationId, ipfsHash, { from: USER0 }), "korporatio-stake-cancelled");
349375
});
350376

351-
it("can submit an application and pay the fee", async () => {
352-
await token.mint(USER0, APPLICATION_FEE);
353-
await token.approve(korporatio.address, APPLICATION_FEE);
354-
377+
it("can submit an application", async () => {
355378
await korporatio.createFreeApplication({ from: USER0 });
356379

357380
const applicationId = await korporatio.getNumApplications();
358-
const ipfsHash = soliditySha3("IPFS Hash");
359381

360382
// Cannot submit if not root
361-
await checkErrorRevert(korporatio.submitApplication(applicationId, ipfsHash, { from: USER1 }), "korporatio-caller-not-root");
383+
await checkErrorRevert(korporatio.submitApplication(applicationId, { from: USER1 }), "korporatio-caller-not-root");
384+
385+
const tx = await korporatio.submitApplication(applicationId, { from: USER0 });
386+
await expectEvent(tx, "ApplicationSubmitted", [applicationId]);
387+
388+
// Cannot submit twice
389+
await checkErrorRevert(korporatio.submitApplication(applicationId, { from: USER0 }), "korporatio-stake-cancelled");
390+
});
391+
392+
it("can submit an application via a motion", async () => {
393+
await colony.installExtension(VOTING_REPUTATION, 9);
394+
const votingAddress = await colonyNetwork.getExtensionInstallation(VOTING_REPUTATION, colony.address);
395+
await colony.setArbitrationRole(1, UINT256_MAX, votingAddress, 1, true);
396+
await colony.setRootRole(votingAddress, true);
397+
const voting = await IVotingReputation.at(votingAddress);
398+
399+
await voting.initialise(WAD.divn(1000), 0, 0, WAD, SECONDS_PER_DAY, SECONDS_PER_DAY, SECONDS_PER_DAY, SECONDS_PER_DAY);
400+
401+
await korporatio.createFreeApplication({ from: USER0 });
402+
const applicationId = await korporatio.getNumApplications();
403+
404+
const action = await encodeTxData(korporatio, "submitApplication", [applicationId]);
405+
406+
// Can't create a motion in a subdomain
407+
await colony.addDomain(1, UINT256_MAX, 1);
408+
await checkErrorRevert(
409+
voting.createMotion(2, UINT256_MAX, korporatio.address, action, domain1Key, domain1Value, domain1Mask, domain1Siblings),
410+
"voting-rep-invalid-domain-id"
411+
);
412+
413+
// Only in the root domain
414+
await voting.createMotion(1, UINT256_MAX, korporatio.address, action, domain1Key, domain1Value, domain1Mask, domain1Siblings);
415+
const motionId = await voting.getMotionCount();
362416

363-
const tx = await korporatio.submitApplication(applicationId, ipfsHash, { from: USER0 });
364-
await expectEvent(tx, "ApplicationSubmitted", [applicationId, ipfsHash]);
417+
await colony.approveStake(voting.address, 1, WAD, { from: USER0 });
418+
await voting.stakeMotion(motionId, 1, UINT256_MAX, 1, WAD.muln(3).divn(1000), user0Key, user0Value, user0Mask, user0Siblings, { from: USER0 });
365419

366-
const metaColonyAddress = await colonyNetwork.getMetaColony();
367-
const metaColonyBalance = await token.balanceOf(metaColonyAddress);
368-
expect(metaColonyBalance).to.eq.BN(APPLICATION_FEE);
420+
await forwardTime(SECONDS_PER_DAY, this);
369421

370-
// Cannot submit once cancelled
371-
await checkErrorRevert(korporatio.submitApplication(applicationId, ipfsHash, { from: USER0 }), "korporatio-stake-cancelled");
422+
const tx = await voting.finalizeMotion(motionId);
423+
const finalizedAt = await getBlockTime(tx.blockNumber);
424+
425+
const application = await korporatio.getApplication(applicationId);
426+
expect(application.cancelledAt).to.eq.BN(finalizedAt);
372427
});
373428

374429
it("can submit a stake via metatransactions", async () => {

0 commit comments

Comments
 (0)