Skip to content

Commit

Permalink
Set up ReputationBootstrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
kronosapiens committed Sep 23, 2022
1 parent 1593ff1 commit 5998b2a
Show file tree
Hide file tree
Showing 7 changed files with 359 additions and 3 deletions.
137 changes: 137 additions & 0 deletions contracts/extensions/ReputationBootstrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
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.7.3;
pragma experimental ABIEncoderV2;

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

// ignore-file-swc-108


contract ReputationBootstrapper is ColonyExtensionMeta {

// Constants

uint256 constant INT256_MAX = 2**255 - 1;

// Events

event GrantSet(bytes32 hashedPin, uint256 reputationAmount);
event GrantClaimed(address recipient, uint256 reputationAmount, uint256 tokenAmount);

// Data structures

struct Grant {
uint256 amount;
uint256 timestamp;
}

// Storage

address public token;

uint256 decayNumerator;
uint256 decayDenominator;

mapping (bytes32 => Grant) public grants;

// Modifiers

modifier onlyRoot() {
require(colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Root), "reputation-bootsrapper-caller-not-root");
_;
}

// Overrides

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

/// @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);
token = colony.getToken();

address colonyNetwork = colony.getColonyNetwork();
address repCycle = IColonyNetwork(colonyNetwork).getReputationMiningCycle(false);
(decayNumerator, decayDenominator) = IReputationMiningCycle(repCycle).getDecayConstant();
}

/// @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(address(uint160(address(colony))));
}

// Public

function setGrants(bytes32[] memory _hashedSecrets, uint256[] memory _amounts) public onlyRoot {
require(_hashedSecrets.length == _amounts.length, "reputation-bootsrapper-invalid-arguments");

for (uint256 i; i < _hashedSecrets.length; i++) {
require(_amounts[i] <= INT256_MAX, "repuatation-bootsrapper-invalid-amount");
grants[_hashedSecrets[i]] = Grant(_amounts[i], block.timestamp);

emit GrantSet(_hashedSecrets[i], _amounts[i]);
}
}

function claimGrant(uint256 _secret) public {
bytes32 hashedPin = keccak256(abi.encodePacked(_secret));
uint256 grantAmount = grants[hashedPin].amount;
uint256 grantTimestamp = grants[hashedPin].timestamp;

require(grantAmount > 0, "reputation-bootstrapper-nothing-to-claim");

delete grants[hashedPin];

uint256 tokenAmount = min(ERC20(token).balanceOf(address(this)), grantAmount);
require(tokenAmount >= uint256(grantAmount) || tokenAmount <= 0, "reputation-bootstrapper-insufficient-tokens");

for (; grantTimestamp <= block.timestamp - 1 hours; grantTimestamp += 1 hours) {
grantAmount = grantAmount * decayNumerator / decayDenominator;
}

colony.emitDomainReputationReward(1, msgSender(), int256(grantAmount));
require(ERC20(token).transfer(msgSender(), tokenAmount), "reputation-bootstrapper-transfer-failed");

emit GrantClaimed(msgSender(), grantAmount, tokenAmount);
}


}
4 changes: 3 additions & 1 deletion migrations/9_setup_extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const EvaluatedExpenditure = artifacts.require("./EvaluatedExpenditure");
const StakedExpenditure = artifacts.require("./StakedExpenditure");
const FundingQueue = artifacts.require("./FundingQueue");
const OneTxPayment = artifacts.require("./OneTxPayment");
const ReputationBootstrapper = artifacts.require("./ReputationBootstrapper");
const StreamingPayments = artifacts.require("./StreamingPayments");
const VotingReputation = artifacts.require("./VotingReputation");
const VotingReputationMisalignedRecovery = artifacts.require("./VotingReputationMisalignedRecovery");
Expand Down Expand Up @@ -44,9 +45,10 @@ module.exports = async function (deployer, network, accounts) {

await addExtension(colonyNetwork, "CoinMachine", "CoinMachine", [CoinMachine]);
await addExtension(colonyNetwork, "EvaluatedExpenditure", "EvaluatedExpenditure", [EvaluatedExpenditure]);
await addExtension(colonyNetwork, "StakedExpenditure", "StakedExpenditure", [StakedExpenditure]);
await addExtension(colonyNetwork, "FundingQueue", "FundingQueue", [FundingQueue]);
await addExtension(colonyNetwork, "OneTxPayment", "OneTxPayment", [OneTxPayment]);
await addExtension(colonyNetwork, "ReputationBootstrapper", "ReputationBootstrapper", [ReputationBootstrapper]);
await addExtension(colonyNetwork, "StakedExpenditure", "StakedExpenditure", [StakedExpenditure]);
await addExtension(colonyNetwork, "StreamingPayments", "StreamingPayments", [StreamingPayments]);
await addExtension(colonyNetwork, "TokenSupplier", "TokenSupplier", [TokenSupplier]);
await addExtension(colonyNetwork, "IVotingReputation", "VotingReputation", [VotingReputation, VotingReputationMisalignedRecovery]);
Expand Down
1 change: 1 addition & 0 deletions scripts/check-recovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ walkSync("./contracts/").forEach((contractName) => {
"contracts/extensions/StakedExpenditure.sol",
"contracts/extensions/FundingQueue.sol",
"contracts/extensions/OneTxPayment.sol",
"contracts/extensions/ReputationBootstrapper.sol",
"contracts/extensions/StreamingPayments.sol",
"contracts/extensions/TokenSupplier.sol",
"contracts/extensions/votingReputation/VotingReputation.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 @@ -34,6 +34,7 @@ walkSync("./contracts/").forEach((contractName) => {
"contracts/extensions/ColonyExtension.sol",
"contracts/extensions/ColonyExtensionMeta.sol",
"contracts/extensions/OneTxPayment.sol",
"contracts/extensions/ReputationBootstrapper.sol",
"contracts/extensions/StreamingPayments.sol",
"contracts/extensions/TokenSupplier.sol",
"contracts/extensions/votingReputation/VotingReputationMisalignedRecovery.sol",
Expand Down
25 changes: 25 additions & 0 deletions test-gas-costs/gasCosts.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* globals artifacts */

const path = require("path");
const { soliditySha3 } = require("web3-utils");
const { TruffleLoader } = require("../packages/package-utils");

const {
Expand Down Expand Up @@ -49,6 +50,7 @@ const IColonyNetwork = artifacts.require("IColonyNetwork");
const EtherRouter = artifacts.require("EtherRouter");
const ITokenLocking = artifacts.require("ITokenLocking");
const OneTxPayment = artifacts.require("OneTxPayment");
const ReputationBootstrapper = artifacts.require("ReputationBootstrapper");

const REAL_PROVIDER_PORT = process.env.SOLIDITY_COVERAGE ? 8555 : 8545;

Expand Down Expand Up @@ -368,5 +370,28 @@ contract("All", function (accounts) {
await forwardTime(5184001);
await newColony.finalizeRewardPayout(payoutId2);
});

it("when bootstrapping reputation", async function () {
const reputationBootstrapper = await ReputationBootstrapper.new();
await reputationBootstrapper.install(colony.address);
await colony.setRootRole(reputationBootstrapper.address, true);

await reputationBootstrapper.setGrants([soliditySha3(1)], [WAD], { from: MANAGER });

await reputationBootstrapper.claimGrant(1, { from: WORKER });
});

it("when bootstrapping reputation with decay", async function () {
const reputationBootstrapper = await ReputationBootstrapper.new();
await reputationBootstrapper.install(colony.address);
await colony.setRootRole(reputationBootstrapper.address, true);

await reputationBootstrapper.setGrants([soliditySha3(1)], [WAD], { from: MANAGER });

// Reputation decays by half in 90 days
await forwardTime(SECONDS_PER_DAY * 90, this);

await reputationBootstrapper.claimGrant(1, { from: WORKER });
});
});
});
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("0xd274dba9b9c02954cf11a9f6b968560bf03bb7f6a3c2b6844409a9dbf8fb93c2");
expect(colonyStateHash).to.equal("0xa49d332bbdd1951f062b1ffc40bbeb7b3a0a16fd2cd1879fca8348eda7b5b587");
expect(colonyNetworkStateHash).to.equal("0xc6411d9548bb49f27f41b170ec7d811b6d743374d781de006db0a9fdf569ead8");
expect(colonyStateHash).to.equal("0x209ca5da64157f5060f76bd0854c8a22977703a05d29a3a0e1707902cb8942f2");
expect(metaColonyStateHash).to.equal("0xff23657f917385e6a94f328907443fef625f08b8b3224e065a53b690f91be0bb");
expect(miningCycleStateHash).to.equal("0x264d4a83e21fef92f687f9fabacae9370966b0b30ebc15307653c4c3d33a0035");
expect(tokenLockingStateHash).to.equal("0x983a56a52582ce548e98659e15a9baa5387886fcb0ac1185dbd746dfabf00338");
Expand Down
Loading

0 comments on commit 5998b2a

Please sign in to comment.