Correct inter-contract associations #199

Closed
bumi wants to merge 3 commits from correct-contract-associations into master
17 changed files with 85 additions and 100 deletions

View File

@ -14,6 +14,11 @@
"name": "Veto contributions",
"id": "VETO_CONTRIBUTION_ROLE",
"params": []
},
{
"name": "Manage connected apps",
"id": "MANAGE_APPS_ROLE",
"params": []
}
],
"environments": {

View File

@ -17,12 +17,10 @@ interface ContributorInterface {
contract Contribution is AragonApp {
bytes32 public constant ADD_CONTRIBUTION_ROLE = keccak256("ADD_CONTRIBUTION_ROLE");
bytes32 public constant VETO_CONTRIBUTION_ROLE = keccak256("VETO_CONTRIBUTION_ROLE");
bytes32 public constant MANAGE_APPS_ROLE = keccak256("MANAGE_APPS_ROLE");
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
bytes32[5] public appIds;
IToken public kreditsToken;
ContributorInterface public kreditsContributor;
struct ContributionData {
uint32 contributorId;
@ -54,25 +52,17 @@ contract Contribution is AragonApp {
event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount);
event ContributionVetoed(uint32 id, address vetoedByAccount);
function initialize(bytes32[5] _appIds) public onlyInit {
appIds = _appIds;
function initialize(address _token, address _contributor) public onlyInit {
kreditsToken = IToken(_token);
kreditsContributor = ContributorInterface(_contributor);
blocksToWait = 40320; // 7 days; 15 seconds block time
initialized();
}
function getContract(uint8 appId) public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
}
function getContributorIdByAddress(address contributorAccount) public view returns (uint32) {
address contributor = getContract(uint8(Apps.Contributor));
return ContributorInterface(contributor).getContributorIdByAddress(contributorAccount);
}
function getContributorAddressById(uint32 contributorId) public view returns (address) {
address contributor = getContract(uint8(Apps.Contributor));
return ContributorInterface(contributor).getContributorAddressById(contributorId);
function setApps(address _token, address _contributor) public isInitialized auth(MANAGE_APPS_ROLE) {
kreditsToken = IToken(_token);
kreditsContributor = ContributorInterface(_contributor);
}
//
@ -90,18 +80,18 @@ contract Contribution is AragonApp {
// Balance is amount of ERC271 tokens, not amount of kredits
function balanceOf(address owner) public view returns (uint256) {
require(owner != address(0));
uint32 contributorId = getContributorIdByAddress(owner);
uint32 contributorId = kreditsContributor.getContributorIdByAddress(owner);
return ownedContributions[contributorId].length;
}
function ownerOf(uint32 contributionId) public view returns (address) {
require(exists(contributionId));
uint32 contributorId = contributions[contributionId].contributorId;
return getContributorAddressById(contributorId);
return kreditsContributor.getContributorAddressById(contributorId);
}
function tokenOfOwnerByIndex(address owner, uint32 index) public view returns (uint32) {
uint32 contributorId = getContributorIdByAddress(owner);
uint32 contributorId = kreditsContributor.getContributorIdByAddress(owner);
return ownedContributions[contributorId][index];
}
@ -193,10 +183,9 @@ contract Contribution is AragonApp {
require(block.number >= c.confirmedAtBlock, 'NOT_CLAIMABLE');
c.claimed = true;
address token = getContract(uint8(Apps.Token));
address contributorAccount = getContributorAddressById(c.contributorId);
address contributorAccount = kreditsContributor.getContributorAddressById(c.contributorId);
uint256 amount = uint256(c.amount);
IToken(token).mintFor(contributorAccount, amount, contributionId);
kreditsToken.mintFor(contributorAccount, amount, contributionId);
emit ContributionClaimed(contributionId, c.contributorId, c.amount);
}

View File

@ -4,6 +4,11 @@
"name": "Manage contributors",
"id": "MANAGE_CONTRIBUTORS_ROLE",
"params": []
},
{
"name": "Manage connected apps",
"id": "MANAGE_APPS_ROLE",
"params": []
}
],
"environments": {

View File

@ -12,8 +12,11 @@ interface IContributionBalance {
}
contract Contributor is AragonApp {
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
bytes32 public constant MANAGE_CONTRIBUTORS_ROLE = keccak256("MANAGE_CONTRIBUTORS_ROLE");
bytes32 public constant MANAGE_APPS_ROLE = keccak256("MANAGE_APPS_ROLE");
ITokenBalance public kreditsToken;
IContributionBalance public kreditsContribution;
struct Contributor {
address account;
@ -27,23 +30,19 @@ contract Contributor is AragonApp {
mapping (uint32 => Contributor) public contributors;
uint32 public contributorsCount;
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
bytes32[5] public appIds;
event ContributorProfileUpdated(uint32 id, bytes32 oldHashDigest, bytes32 newHashDigest); // what should be logged
event ContributorAccountUpdated(uint32 id, address oldAccount, address newAccount);
event ContributorAdded(uint32 id, address account);
function initialize(address root, bytes32[5] _appIds) public onlyInit {
appIds = _appIds;
function initialize(address _contribution, address _token) public onlyInit {
kreditsToken = ITokenBalance(_token);
kreditsContribution = IContributionBalance(_contribution);
initialized();
}
function getContract(uint8 appId) public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
function setApps(address _contribution, address _token) public isInitialized auth(MANAGE_APPS_ROLE) {
kreditsToken = ITokenBalance(_token);
kreditsContribution = IContributionBalance(_contribution);
}
function coreContributorsCount() public view returns (uint32) {
@ -132,11 +131,9 @@ contract Contributor is AragonApp {
hashFunction = c.hashFunction;
hashSize = c.hashSize;
isCore = isCoreTeam(id);
address token = getContract(uint8(Apps.Token));
balance = ITokenBalance(token).balanceOf(c.account);
address contribution = getContract(uint8(Apps.Contribution));
totalKreditsEarned = IContributionBalance(contribution).totalKreditsEarnedByContributor(_id, true);
contributionsCount = IContributionBalance(contribution).balanceOf(c.account);
balance = kreditsToken.balanceOf(c.account);
totalKreditsEarned = kreditsContribution.totalKreditsEarnedByContributor(_id, true);
contributionsCount = kreditsContribution.balanceOf(c.account);
exists = c.exists;
}

View File

@ -9,6 +9,11 @@
"name": "Vote proposals",
"id": "VOTE_PROPOSAL_ROLE",
"params": []
},
{
"name": "Manage connected apps",
"id": "MANAGE_APPS_ROLE",
"params": []
}
],
"environments": {

View File

@ -17,11 +17,10 @@ contract Proposal is AragonApp {
bytes32 public constant ADD_PROPOSAL_ROLE = keccak256("ADD_PROPOSAL_ROLE");
bytes32 public constant VOTE_PROPOSAL_ROLE = keccak256("VOTE_PROPOSAL_ROLE");
bytes32 public constant MANAGE_APPS_ROLE = keccak256("MANAGE_APPS_ROLE");
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
bytes32[5] public appIds;
IContributor public kreditsContributor;
IContribution public kreditsContribution;
struct Proposal {
address creatorAccount;
@ -46,18 +45,19 @@ contract Proposal is AragonApp {
event ProposalVoted(uint32 id, uint32 voterId, uint16 totalVotes);
event ProposalExecuted(uint32 id, uint32 contributorId, uint32 amount);
function initialize(bytes32[5] _appIds) public onlyInit {
appIds = _appIds;
function initialize(address _contributor, address _contribution) public onlyInit {
kreditsContributor = IContributor(_contributor);
kreditsContribution = IContribution(_contribution);
initialized();
}
function getContract(uint8 appId) public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
function setApps(address _contributor, address _contribution) public isInitialized auth(MANAGE_APPS_ROLE) {
kreditsContributor = IContributor(_contributor);
kreditsContribution = IContribution(_contribution);
}
function addProposal(uint32 contributorId, uint32 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) {
require(IContributor(getContract(uint8(Apps.Contributor))).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
require(kreditsContributor.exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
uint32 proposalId = proposalsCount + 1;
uint16 _votesNeeded = 1; //contributorsContract().coreContributorsCount() / 100 * 75;
@ -99,7 +99,7 @@ contract Proposal is AragonApp {
function vote(uint32 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
Proposal storage p = proposals[proposalId];
require(!p.executed, 'ALREADY_EXECUTED');
uint32 voterId = IContributor(getContract(uint8(Apps.Contributor))).getContributorIdByAddress(msg.sender);
uint32 voterId = kreditsContributor.getContributorIdByAddress(msg.sender);
require(p.votes[voterId] != true, 'ALREADY_VOTED');
p.voterIds.push(voterId);
p.votes[voterId] = true;
@ -123,7 +123,7 @@ contract Proposal is AragonApp {
require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES');
p.executed = true;
IContribution(getContract(uint8(Apps.Contribution))).add(p.amount, p.contributorId, p.hashDigest, p.hashFunction, p.hashSize);
kreditsContribution.add(p.amount, p.contributorId, p.hashDigest, p.hashFunction, p.hashSize);
emit ProposalExecuted(proposalId, p.contributorId, p.amount);
}

View File

@ -28,9 +28,7 @@ contract Reimbursement is AragonApp {
event ReimbursementAdded(uint32 id, address indexed addedByAccount, uint256 amount);
event ReimbursementVetoed(uint32 id, address vetoedByAccount);
// TODO: remove _appIds when those are removed from the kreditskit
// using the appids to find other apps is wrong according to aragon
function initialize(bytes32[5] _appIds) public onlyInit {
function initialize() public onlyInit {
blocksToWait = 40320; // 7 days; 15 seconds block time
initialized();
}

View File

@ -6,14 +6,9 @@ import "./ERC20Token.sol";
contract Token is ERC20Token, AragonApp {
bytes32 public constant MINT_TOKEN_ROLE = keccak256("MINT_TOKEN_ROLE");
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
bytes32[5] public appIds;
event LogMint(address indexed recipient, uint256 amount, uint32 contributionId);
function initialize(bytes32[5] _appIds) public onlyInit {
appIds = _appIds;
function initialize() public onlyInit {
name = 'Kredits';
symbol = '₭S';
decimals = 18;

View File

@ -14,15 +14,17 @@ import "../apps/reimbursement/contracts/Reimbursement.sol";
contract KreditsKit is KitBase {
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
bytes32[5] public appIds;
bytes32 constant internal CONTRIBUTION_APP_ID = 0x09f5274cba299b46c5be722ef672d10eef7a2ef980b612aef529d74fb9da7643;
bytes32 constant internal CONTRIBUTOR_APP_ID = 0x8e50972b062e83b48dbb2a68d8a058f2a07227ca183c144dc974e6da3186d7e9;
bytes32 constant internal PROPOSAL_APP_ID= 0xb48bc8b4e539823f3be98d67f4130c07b5d29cc998993debcdea15c6faf4cf8a;
bytes32 constant internal REIMBURSEMENT_APP_ID = 0x1103c160cab5c23100981f67c020a021d46a894a4f262b6e1180b335a639d3d2;
bytes32 constant internal TOKEN_APP_ID = 0x82c0e483537d703bb6f0fc799d2cc60d8f62edcb0f6d26d5571a92be8485b112;
event DeployInstance(address dao);
event InstalledApp(address dao, address appProxy, bytes32 appId);
constructor (DAOFactory _fac, ENS _ens, bytes32[5] _appIds) public KitBase(_fac, _ens) {
appIds = _appIds;
constructor (DAOFactory _fac, ENS _ens) public KitBase(_fac, _ens) {
// appIds = _appIds;
}
function newInstance() public returns (Kernel dao) {
@ -32,25 +34,26 @@ contract KreditsKit is KitBase {
acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this);
Contributor contributor = Contributor(_installApp(dao, appIds[uint8(Apps.Contributor)]));
contributor.initialize(root, appIds);
Contribution contribution = Contribution(_installApp(dao, CONTRIBUTION_APP_ID));
Contributor contributor = Contributor(_installApp(dao, CONTRIBUTOR_APP_ID));
Proposal proposal = Proposal(_installApp(dao, PROPOSAL_APP_ID));
Reimbursement reimbursement = Reimbursement(_installApp(dao,REIMBURSEMENT_APP_ID));
Token token = Token(_installApp(dao, TOKEN_APP_ID));
token.initialize();
contributor.initialize(contribution, token);
acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), this);
Token token = Token(_installApp(dao, appIds[uint8(Apps.Token)]));
token.initialize(appIds);
Contribution contribution = Contribution(_installApp(dao, appIds[uint8(Apps.Contribution)]));
contribution.initialize(appIds);
contribution.initialize(token, contributor);
acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this);
acl.createPermission(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), this);
acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE());
Proposal proposal = Proposal(_installApp(dao, appIds[uint8(Apps.Proposal)]));
proposal.initialize(appIds);
proposal.initialize(contributor, contribution);
Reimbursement reimbursement = Reimbursement(_installApp(dao, appIds[uint8(Apps.Reimbursement)]));
reimbursement.initialize(appIds);
reimbursement.initialize();
acl.createPermission(root, reimbursement, reimbursement.ADD_REIMBURSEMENT_ROLE(), this);
acl.createPermission(root, reimbursement, reimbursement.VETO_REIMBURSEMENT_ROLE(), this);
@ -80,7 +83,6 @@ contract KreditsKit is KitBase {
acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE());
acl.setPermissionManager(root, token, token.MINT_TOKEN_ROLE());
cleanupDAOPermissions(dao, acl, root);
emit DeployInstance(dao);

View File

@ -1,13 +0,0 @@
pragma solidity 0.4.24;
import "@aragon/os/contracts/apm/APMNamehash.sol";
contract APMNamehashOpen is APMNamehash {
bytes32 public constant OPEN_TITLE = keccak256("open");
bytes32 public constant OPEN_APM_NODE = keccak256(abi.encodePacked(APM_NODE, OPEN_TITLE));
function apmNamehashOpen(string name) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(OPEN_APM_NODE, keccak256(name)));
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -37,8 +37,10 @@ class KreditsKit {
return this.contract.functions.newInstance(options).then(transaction => {
return transaction.wait().then(result => {
const deployEvent = result.events.find(e => e.event === 'DeployInstance');
const installedApps = result.events.filter(e => e.event === 'InstalledApp').map(e => e.args);
return {
daoAddress: deployEvent.args.dao,
installedApps: installedApps,
transactionHash: transaction.hash,
};
});

View File

@ -55,7 +55,7 @@ module.exports = async function(callback) {
appIds[contractName] = namehash(`kredits-${app}.${apm}`)
})
KreditsKit.new(daoFactory.address, ensAddr, Object.values(appIds)).then((kreditsKit) => {
KreditsKit.new(daoFactory.address, ensAddr).then((kreditsKit) => {
console.log(`Deployed KreditsKit at: ${kreditsKit.address}`);
fileInject(path.join(__dirname, '..', 'lib/addresses/KreditsKit.json'), networkId, kreditsKit.address);