diff --git a/contracts/Contribution.sol b/contracts/Contribution.sol index 45a461c..c7a1f44 100644 --- a/contracts/Contribution.sol +++ b/contracts/Contribution.sol @@ -9,6 +9,7 @@ interface IToken { interface ContributorInterface { function getContributorAddressById(uint32 contributorId) external view returns (address); function getContributorIdByAddress(address contributorAccount) external view returns (uint32); + function addressIsCore(address sender) external view returns (bool); // TODO Maybe use for validation // function exists(uint32 contributorId) public view returns (bool); } @@ -17,11 +18,6 @@ contract Contribution is Initializable { ContributorInterface public contributorContract; IToken public tokenContract; - bytes32 public constant ADD_CONTRIBUTION_ROLE = keccak256("ADD_CONTRIBUTION_ROLE"); - bytes32 public constant VETO_CONTRIBUTION_ROLE = keccak256("VETO_CONTRIBUTION_ROLE"); - - bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb; - struct ContributionData { uint32 contributorId; uint32 amount; @@ -52,17 +48,22 @@ contract Contribution is Initializable { event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount); event ContributionVetoed(uint32 id, address vetoedByAccount); + modifier onlyCore { + require(contributorContract.addressIsCore(msg.sender), "Core only"); + _; + } + function initialize(uint32 blocksToWait_) public initializer { blocksToWait = blocksToWait_; } - // TODO who can call this when? function setTokenContract(address token) public { + require(address(tokenContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only"); tokenContract = IToken(token); } - // TODO who can call this when? function setContributorContract(address contributor) public { + require(address(contributorContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only"); contributorContract = ContributorInterface(contributor); } @@ -151,6 +152,7 @@ contract Contribution is Initializable { function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public{ //require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint32[](0)), 'nope'); + require(balanceOf(msg.sender) > 0, "Must have Kredits"); uint32 contributionId = contributionsCount + 1; ContributionData storage c = contributions[contributionId]; c.exists = true; @@ -174,7 +176,8 @@ contract Contribution is Initializable { emit ContributionAdded(contributionId, contributorId, amount); } - function veto(uint32 contributionId) public { + function veto(uint32 contributionId) public onlyCore { + ContributionData storage c = contributions[contributionId]; require(c.exists, 'NOT_FOUND'); require(!c.claimed, 'ALREADY_CLAIMED'); @@ -201,4 +204,5 @@ contract Contribution is Initializable { function exists(uint32 contributionId) public view returns (bool) { return contributions[contributionId].exists; } + } diff --git a/contracts/Contributor.sol b/contracts/Contributor.sol index c355a2c..edeb8e9 100644 --- a/contracts/Contributor.sol +++ b/contracts/Contributor.sol @@ -11,6 +11,7 @@ interface IContributionBalance { } contract Contributor is Initializable { + address deployer; IContributionBalance public contributionContract; ITokenBalance public tokenContract; @@ -30,18 +31,22 @@ contract Contributor is Initializable { event ContributorAccountUpdated(uint32 id, address oldAccount, address newAccount); event ContributorAdded(uint32 id, address account); - - function initialize() public initializer { - + modifier onlyCore { + require(addressIsCore(msg.sender), "Core only"); + _; } - // TODO who can call this when? - function setContributionContract(address contribution) public { + function initialize() public initializer { + deployer = msg.sender; + } + + function setContributionContract(address contribution) public onlyCore { + require(address(contributionContract) == address(0) || addressIsCore(msg.sender), "Core only"); contributionContract = IContributionBalance(contribution); } - // TODO who can call this when? - function setTokenContract(address token) public { + function setTokenContract(address token) public onlyCore { + require(address(tokenContract) == address(0) || addressIsCore(msg.sender), "Core only"); tokenContract = ITokenBalance(token); } @@ -55,7 +60,7 @@ contract Contributor is Initializable { return count; } - function updateContributorAccount(uint32 id, address oldAccount, address newAccount) public { + function updateContributorAccount(uint32 id, address oldAccount, address newAccount) public onlyCore { require(newAccount != address(0), "invalid new account address"); require(getContributorAddressById(id) == oldAccount, "contributor does not exist"); @@ -65,7 +70,7 @@ contract Contributor is Initializable { emit ContributorAccountUpdated(id, oldAccount, newAccount); } - function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public { + function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public onlyCore { Contributor storage c = contributors[id]; bytes32 oldHashDigest = c.hashDigest; c.hashDigest = hashDigest; @@ -75,7 +80,7 @@ contract Contributor is Initializable { ContributorProfileUpdated(id, oldHashDigest, c.hashDigest); } - function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public { + function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public onlyCore { require(!addressExists(account)); uint32 _id = contributorsCount + 1; assert(!contributors[_id].exists); // this can not be acually @@ -94,7 +99,7 @@ contract Contributor is Initializable { function isCoreTeam(uint32 id) view public returns (bool) { // TODO: for simplicity we simply define the first contributors as core // later this needs to be changed to something more dynamic - return id < 7; + return id > 1 && id < 7 || msg.sender == deployer; } function exists(uint32 id) view public returns (bool) { @@ -137,16 +142,4 @@ contract Contributor is Initializable { exists = c.exists; } - function canPerform(address _who, address _where, bytes32 _what, uint256[] memory _how) public returns (bool) { - address sender = _who; - if (sender == address(0)) { - sender = tx.origin; - } - // _what == keccak256('VOTE_PROPOSAL_ROLE') - if (_what == 0xd61216798314d2fc33e42ff2021d66707b1e38517d3f7166798a9d3a196a9c96) { - return contributorIds[sender] != uint256(0); - } - - return addressIsCore(sender); - } } diff --git a/contracts/Reimbursement.sol b/contracts/Reimbursement.sol index 51421db..0737e2f 100644 --- a/contracts/Reimbursement.sol +++ b/contracts/Reimbursement.sol @@ -2,10 +2,16 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +interface ContributorInterface { + function getContributorAddressById(uint32 contributorId) external view returns (address); + function getContributorIdByAddress(address contributorAccount) external view returns (uint32); + function addressIsCore(address sender) external view returns (bool); + // TODO Maybe use for validation + // function exists(uint32 contributorId) public view returns (bool); +} + contract Reimbursement is Initializable { - bytes32 public constant ADD_REIMBURSEMENT_ROLE = keccak256("ADD_REIMBURSEMENT_ROLE"); - bytes32 public constant VETO_REIMBURSEMENT_ROLE = keccak256("VETO_REIMBURSEMENT_ROLE"); - // bytes32 public constant MANAGE_APPS_ROLE = keccak256("MANAGE_APPS_ROLE"); + ContributorInterface public contributorContract; struct ReimbursementData { uint32 recipientId; @@ -31,6 +37,24 @@ contract Reimbursement is Initializable { blocksToWait = 40320; // 7 days; 15 seconds block time } + modifier onlyCore { + require(contributorContract.addressIsCore(msg.sender), "Core only"); + _; + } + + function setContributorContract(address contributor) public { + require(address(contributorContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only"); + contributorContract = ContributorInterface(contributor); + } + + function getContributorIdByAddress(address contributorAccount) public view returns (uint32) { + return contributorContract.getContributorIdByAddress(contributorAccount); + } + + function getContributorAddressById(uint32 contributorId) public view returns (address) { + return contributorContract.getContributorAddressById(contributorId); + } + function totalAmount(bool confirmedOnly) public view returns (uint256 amount) { for (uint32 i = 1; i <= reimbursementsCount; i++) { ReimbursementData memory r = reimbursements[i]; @@ -57,7 +81,7 @@ contract Reimbursement is Initializable { ); } - function add(uint256 amount, address token, uint32 recipientId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public { + function add(uint256 amount, address token, uint32 recipientId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public onlyCore { uint32 reimbursementId = reimbursementsCount + 1; ReimbursementData storage r = reimbursements[reimbursementId]; r.exists = true; @@ -74,7 +98,7 @@ contract Reimbursement is Initializable { emit ReimbursementAdded(reimbursementId, msg.sender, amount); } - function veto(uint32 reimbursementId) public { + function veto(uint32 reimbursementId) public onlyCore { ReimbursementData storage r = reimbursements[reimbursementId]; require(r.exists, 'NOT_FOUND'); require(block.number < r.confirmedAtBlock, 'VETO_PERIOD_ENDED'); diff --git a/contracts/Token.sol b/contracts/Token.sol index d0b67f2..d5aa872 100644 --- a/contracts/Token.sol +++ b/contracts/Token.sol @@ -3,10 +3,19 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; +interface ContributorInterface { + function getContributorAddressById(uint32 contributorId) external view returns (address); + function getContributorIdByAddress(address contributorAccount) external view returns (uint32); + function addressIsCore(address sender) external view returns (bool); + // TODO Maybe use for validation + // function exists(uint32 contributorId) public view returns (bool); +} + contract Token is Initializable, ERC20Upgradeable { + ContributorInterface public contributorContract; using SafeMathUpgradeable for uint256; - bytes32 public constant MINT_TOKEN_ROLE = keccak256("MINT_TOKEN_ROLE"); + address public contributionContract; event LogMint(address indexed recipient, uint256 amount, uint32 contributionId); @@ -14,7 +23,17 @@ contract Token is Initializable, ERC20Upgradeable { __ERC20_init('Kredits', 'KS'); } + function setContributionContract(address contribution) public { + require(address(contributionContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only"); + contributionContract = contribution; + } + function setContributorContract(address contributor) public { + require(address(contributorContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only"); + contributorContract = ContributorInterface(contributor); + } + function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public { + require(contributionContract == msg.sender, "Only Contribution"); require(amount > 0, "INVALID_AMOUNT"); uint256 amountInWei = amount.mul(1 ether); diff --git a/scripts/create-proxy.js b/scripts/create-proxy.js index 2247029..128e668 100644 --- a/scripts/create-proxy.js +++ b/scripts/create-proxy.js @@ -1,7 +1,7 @@ const { ethers, upgrades } = require("hardhat"); -const path = require('path'); -const fileInject = require('./helpers/file_inject.js'); +const path = require("path"); +const fileInject = require("./helpers/file_inject.js"); async function main() { const network = await hre.ethers.provider.getNetwork(); @@ -16,19 +16,27 @@ async function main() { const contributor = await upgrades.deployProxy(Contributor, []); await contributor.deployed(); console.log("Contributor deployed to:", contributor.address); + console.log("...waiting for 1 confirmation"); + await contributor.deployTransaction.wait(); const blocksToWait = 40320; // 7 days; 15 seconds block time const contribution = await upgrades.deployProxy(Contribution, [blocksToWait]); await contribution.deployed(); console.log("Contribution deployed to:", contribution.address); + console.log("...waiting for 1 confirmation"); + await contribution.deployTransaction.wait(); const token = await upgrades.deployProxy(Token, []); await token.deployed(); console.log("Token deployed to:", token.address); + console.log("...waiting for 1 confirmation"); + await token.deployTransaction.wait(); const reimbursement = await upgrades.deployProxy(Reimbursement, []); await reimbursement.deployed(); console.log("Reimbursement deployed to:", reimbursement.address); + console.log("...waiting for 1 confirmation"); + await reimbursement.deployTransaction.wait(); await contributor.setTokenContract(token.address); await contributor.setContributionContract(contribution.address); @@ -36,6 +44,11 @@ async function main() { await contribution.setTokenContract(token.address); await contribution.setContributorContract(contributor.address); + await token.setContributionContract(contribution.address); + await token.setContributorContract(contributor.address); + + await reimbursement.setContributorContract(contributor.address); + const c = await contributor.contributionContract(); console.log(c); @@ -43,11 +56,11 @@ async function main() { Contributor: contributor.address, Contribution: contribution.address, Token: token.address, - Reimbursement: reimbursement.address + Reimbursement: reimbursement.address, }; - const libPath = path.join(__dirname, '..', 'lib'); - fileInject(path.join(libPath, 'addresses.json'), networkId, addresses); + const libPath = path.join(__dirname, "..", "lib"); + fileInject(path.join(libPath, "addresses.json"), networkId, addresses); } main();