From 75d426f0cc93decfc0e9d63e7be6d585e6381e2f Mon Sep 17 00:00:00 2001 From: bumi Date: Mon, 18 Jun 2018 15:28:50 +0200 Subject: [PATCH 1/4] Add Contribution contract The contribution contract implements an ERC721 interface which represents any contribution. The contributions are non-fungible (as opposed to the Kredits tokens) and can be not be transferred. The contract stores the history of any contribution. Contributions can be claimed which will issue the Kredits tokens to the contributor. This is an early implementation and misses some access control and probably more things. --- contracts/Contribution.sol | 92 +++++++++++++++++++++++++++ contracts/Token.sol | 2 +- migrations/1528988660_contribution.js | 13 ++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 contracts/Contribution.sol create mode 100644 migrations/1528988660_contribution.js diff --git a/contracts/Contribution.sol b/contracts/Contribution.sol new file mode 100644 index 0000000..5fa52b3 --- /dev/null +++ b/contracts/Contribution.sol @@ -0,0 +1,92 @@ +pragma solidity ^0.4.18; + +import "zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol"; +import './upgradeable/Upgradeable.sol'; + +// ToDo: only load interfaces +import './Token.sol'; + +contract Contribution is Upgradeable, ERC721Token { + + struct Contribution { + address contributor; + uint amount; + bool issued; + uint proposalId; + string url; + uint256 claimAfterBlock; + bool exists; + } + string internal name_; + string internal symbol_; + + mapping(uint256 => string) contributionURIs; + + mapping(uint256 => address) contributionOwner; + mapping(address => uint256[]) ownedContributions; + + mapping(uint256 => Contribution) public contributions; + uint256 public contributionsCount; + + event ContributionAdded(uint256 id, address indexed contributor, uint256 amount); + + function tokenContract() view public returns (Token) { + return Token(registry.getProxyFor('Token')); + } + + function name() external view returns (string) { + return name_; + } + + function symbol() external view returns (string) { + return symbol_; + } + + function contributionURI(uint256 contributionId) public view returns (string) { + require(exists(contributionId)); + return contributions[contributionId].url; + } + + function ownerOf(uint256 contributionId) public view returns (address) { + require(exists(contributionId)); + return contributions[contributionId].contributor; + } + + function balanceOf(address contributor) public view returns (uint) { + return ownedContributions[contributor].length; + } + + function add(uint256 amount, uint256 proposalId, address contributor, uint256 blocksToWait, string url) public { + uint contributionId = contributionsCount + 1; + var c = contributions[contributionId]; + c.exists = true; + c.amount = amount; + c.issued = false; + c.proposalId = proposalId; + c.url = url; + c.contributor = contributor; + c.claimAfterBlock = block.number + blocksToWait; + + contributionsCount++; + + contributionOwner[contributionId] = contributor; + ownedContributions[contributor].push(contributionId); + + ContributionAdded(contributionId, contributor, amount); + } + + function claim(uint256 contributionId) public { + var c = contributions[contributionId]; + require(c.exists); + require(!c.issued); + require(block.number > c.claimAfterBlock); + tokenContract().mintFor(c.contributor, c.amount, contributionId); + c.issued = true; + } + + function exists(uint256 contributionId) view public returns (bool) { + return contributions[contributionId].exists; + } + + +} diff --git a/contracts/Token.sol b/contracts/Token.sol index 9d81a9f..61ca124 100644 --- a/contracts/Token.sol +++ b/contracts/Token.sol @@ -17,7 +17,7 @@ contract Token is Upgradeable, BasicToken { decimals = 18; } - function mintFor(address contributorAccount, uint256 amount, uint proposalId) onlyRegistryContractFor('Operator') public { + function mintFor(address contributorAccount, uint256 amount, uint proposalId) onlyRegistryContractFor('Contribution') public { totalSupply_ = totalSupply_.add(amount); balances[contributorAccount] = balances[contributorAccount].add(amount); diff --git a/migrations/1528988660_contribution.js b/migrations/1528988660_contribution.js new file mode 100644 index 0000000..f6cb6a9 --- /dev/null +++ b/migrations/1528988660_contribution.js @@ -0,0 +1,13 @@ +var Registry = artifacts.require('./Registry.sol'); +var Contribution = artifacts.require('./Contribution.sol'); + +module.exports = function(deployer) { + deployer.deploy(Contribution).then(function(contribution) { + console.log('Registry address: ', Registry.address); + console.log('Contribution address: ', Contribution.address); + Registry.deployed().then(function(registry) { + registry.addVersion('Contribution', Contribution.address); + registry.createProxy('Contribution', 1); + }); + }); +}; -- 2.25.1 From 6d9a54b71c739a320a5bb6c33b9d1d051712f43c Mon Sep 17 00:00:00 2001 From: Sebastian Kippe Date: Mon, 18 Jun 2018 18:42:32 +0200 Subject: [PATCH 2/4] Remove whitespace --- contracts/Contribution.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/Contribution.sol b/contracts/Contribution.sol index 5fa52b3..da4de63 100644 --- a/contracts/Contribution.sol +++ b/contracts/Contribution.sol @@ -6,7 +6,7 @@ import './upgradeable/Upgradeable.sol'; // ToDo: only load interfaces import './Token.sol'; -contract Contribution is Upgradeable, ERC721Token { +contract Contribution is Upgradeable, ERC721Token { struct Contribution { address contributor; @@ -19,9 +19,9 @@ contract Contribution is Upgradeable, ERC721Token { } string internal name_; string internal symbol_; - + mapping(uint256 => string) contributionURIs; - + mapping(uint256 => address) contributionOwner; mapping(address => uint256[]) ownedContributions; @@ -68,7 +68,7 @@ contract Contribution is Upgradeable, ERC721Token { c.claimAfterBlock = block.number + blocksToWait; contributionsCount++; - + contributionOwner[contributionId] = contributor; ownedContributions[contributor].push(contributionId); @@ -85,7 +85,7 @@ contract Contribution is Upgradeable, ERC721Token { } function exists(uint256 contributionId) view public returns (bool) { - return contributions[contributionId].exists; + return contributions[contributionId].exists; } -- 2.25.1 From 6b8f718051aada5982d8031662009caca2b89b3f Mon Sep 17 00:00:00 2001 From: bumi Date: Mon, 18 Jun 2018 18:43:00 +0200 Subject: [PATCH 3/4] Create contribution when proposal gets executed --- contracts/Operator.sol | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contracts/Operator.sol b/contracts/Operator.sol index 390f7af..fb6ec2e 100644 --- a/contracts/Operator.sol +++ b/contracts/Operator.sol @@ -3,6 +3,7 @@ pragma solidity ^0.4.18; // ToDo: only load interfaces import './Token.sol'; import './Contributors.sol'; +import './Contribution.sol'; contract Operator is Upgradeable { @@ -47,6 +48,9 @@ contract Operator is Upgradeable { function tokenContract() view public returns (Token) { return Token(registry.getProxyFor('Token')); } + function contributionContract() view public returns (Contribution) { + return Contribution(registry.getProxyFor('Contribution')); + } function contributorsCount() view public returns (uint) { return contributorsContract().contributorsCount(); @@ -122,7 +126,7 @@ contract Operator is Upgradeable { require(!p.executed); require(p.votesCount >= p.votesNeeded); address recipientAddress = contributorsContract().getContributorAddressById(p.contributorId); - tokenContract().mintFor(recipientAddress, p.amount, proposalId); + contributionContract().add(p.amount, proposalId, recipientAddress, 0, ''); p.executed = true; ProposalExecuted(proposalId, p.contributorId, p.amount); } -- 2.25.1 From dae44cac70fc3cbe8be32a28de8d6e36d2127711 Mon Sep 17 00:00:00 2001 From: bumi Date: Sat, 29 Sep 2018 19:46:37 +0200 Subject: [PATCH 4/4] Continue Contribution contract and JS wrapper --- contracts/Contribution.sol | 80 +++++++++++++++++++++++++---------- contracts/Operator.sol | 3 +- lib/abis/Contribution.json | 1 + lib/abis/Operator.json | 2 +- lib/contracts/contribution.js | 51 ++++++++++++++++++++++ lib/contracts/index.js | 1 + lib/kredits.js | 5 +++ scripts/build-json.js | 1 + 8 files changed, 118 insertions(+), 26 deletions(-) create mode 100644 lib/abis/Contribution.json create mode 100644 lib/contracts/contribution.js diff --git a/contracts/Contribution.sol b/contracts/Contribution.sol index da4de63..919b37e 100644 --- a/contracts/Contribution.sol +++ b/contracts/Contribution.sol @@ -1,34 +1,49 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.4.19; import "zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol"; import './upgradeable/Upgradeable.sol'; // ToDo: only load interfaces import './Token.sol'; +import './Contributors.sol'; contract Contribution is Upgradeable, ERC721Token { - struct Contribution { + struct ContributionData { address contributor; uint amount; - bool issued; - uint proposalId; - string url; - uint256 claimAfterBlock; + bool claimed; + bytes32 hashDigest; + uint8 hashFunction; + uint8 hashSize; + string tokenMetadataURL; + uint claimAfterBlock; bool exists; } string internal name_; string internal symbol_; - mapping(uint256 => string) contributionURIs; - mapping(uint256 => address) contributionOwner; mapping(address => uint256[]) ownedContributions; - mapping(uint256 => Contribution) public contributions; + mapping(uint256 => ContributionData) public contributions; uint256 public contributionsCount; event ContributionAdded(uint256 id, address indexed contributor, uint256 amount); + event ContributionClaimed(uint256 id, address indexed contributor, uint256 amount); + + modifier coreOnly() { + require(contributorsContract().addressIsCore(msg.sender)); + _; + } + modifier contributorOnly() { + require(contributorsContract().addressExists(msg.sender)); + _; + } + + function contributorsContract() view public returns (Contributors) { + return Contributors(registry.getProxyFor('Contributors')); + } function tokenContract() view public returns (Token) { return Token(registry.getProxyFor('Token')); @@ -42,11 +57,6 @@ contract Contribution is Upgradeable, ERC721Token { return symbol_; } - function contributionURI(uint256 contributionId) public view returns (string) { - require(exists(contributionId)); - return contributions[contributionId].url; - } - function ownerOf(uint256 contributionId) public view returns (address) { require(exists(contributionId)); return contributions[contributionId].contributor; @@ -56,14 +66,36 @@ contract Contribution is Upgradeable, ERC721Token { return ownedContributions[contributor].length; } - function add(uint256 amount, uint256 proposalId, address contributor, uint256 blocksToWait, string url) public { + function tokenOfOwnerByIndex(address contributor, uint index) public view returns (uint) { + return ownedContributions[contributor][index]; + } + + function tokenMetadata(uint contributionId) public view returns (string) { + return contributions[contributionId].tokenMetadataURL; + } + + function getContribution(uint contributionId) public view returns (uint256 id, address contributor, uint256 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint claimAfterBlock, bool exists) { + id = contributionId; + ContributionData storage c = contributions[id]; + return ( + id, + c.contributor, + c.amount, + c.claimed, + c.hashDigest, + c.hashFunction, + c.hashSize, + c.claimAfterBlock, + c.exists + ); + } + + function add(uint256 amount, address contributor, uint256 blocksToWait) public coreOnly { uint contributionId = contributionsCount + 1; - var c = contributions[contributionId]; + ContributionData storage c = contributions[contributionId]; c.exists = true; c.amount = amount; - c.issued = false; - c.proposalId = proposalId; - c.url = url; + c.claimed = false; c.contributor = contributor; c.claimAfterBlock = block.number + blocksToWait; @@ -71,17 +103,19 @@ contract Contribution is Upgradeable, ERC721Token { contributionOwner[contributionId] = contributor; ownedContributions[contributor].push(contributionId); - + ContributionAdded(contributionId, contributor, amount); } function claim(uint256 contributionId) public { - var c = contributions[contributionId]; + ContributionData storage c = contributions[contributionId]; require(c.exists); - require(!c.issued); + require(!c.claimed); require(block.number > c.claimAfterBlock); + c.claimed = true; tokenContract().mintFor(c.contributor, c.amount, contributionId); - c.issued = true; + + ContributionClaimed(contributionId, c.contributor, c.amount); } function exists(uint256 contributionId) view public returns (bool) { diff --git a/contracts/Operator.sol b/contracts/Operator.sol index fb6ec2e..d4b1542 100644 --- a/contracts/Operator.sol +++ b/contracts/Operator.sol @@ -121,12 +121,11 @@ contract Operator is Upgradeable { } function executeProposal(uint proposalId) private { - var p = proposals[proposalId]; require(!p.executed); require(p.votesCount >= p.votesNeeded); address recipientAddress = contributorsContract().getContributorAddressById(p.contributorId); - contributionContract().add(p.amount, proposalId, recipientAddress, 0, ''); + contributionContract().add(p.amount, recipientAddress, 0); p.executed = true; ProposalExecuted(proposalId, p.contributorId, p.amount); } diff --git a/lib/abis/Contribution.json b/lib/abis/Contribution.json new file mode 100644 index 0000000..64b488c --- /dev/null +++ b/lib/abis/Contribution.json @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"contributions","outputs":[{"name":"contributor","type":"address"},{"name":"amount","type":"uint256"},{"name":"claimed","type":"bool"},{"name":"hashDigest","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"tokenMetadataURL","type":"string"},{"name":"claimAfterBlock","type":"uint256"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"approvedFor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributionsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"tokensOf","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"takeOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":true,"name":"contributor","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ContributionAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":true,"name":"contributor","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ContributionClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_approved","type":"address"},{"indexed":false,"name":"_tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"constant":true,"inputs":[],"name":"contributorsContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"contributionId","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"contributor","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"contributor","type":"address"},{"name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"contributionId","type":"uint256"}],"name":"tokenMetadata","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"contributionId","type":"uint256"}],"name":"getContribution","outputs":[{"name":"id","type":"uint256"},{"name":"contributor","type":"address"},{"name":"amount","type":"uint256"},{"name":"claimed","type":"bool"},{"name":"hashDigest","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"claimAfterBlock","type":"uint256"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"},{"name":"contributor","type":"address"},{"name":"blocksToWait","type":"uint256"}],"name":"add","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"contributionId","type":"uint256"}],"name":"claim","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"contributionId","type":"uint256"}],"name":"exists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/lib/abis/Operator.json b/lib/abis/Operator.json index f099710..fe21bd3 100644 --- a/lib/abis/Operator.json +++ b/lib/abis/Operator.json @@ -1 +1 @@ -[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposalsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"creatorAccount","type":"address"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"voterId","type":"uint256"},{"indexed":false,"name":"totalVotes","type":"uint256"}],"name":"ProposalVoted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalExecuted","type":"event"},{"constant":true,"inputs":[],"name":"contributorsContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"contributorId","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"addProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"getProposal","outputs":[{"name":"id","type":"uint256"},{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"voterIds","type":"uint256[]"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_proposalIds","type":"uint256[]"}],"name":"batchVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file +[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposalsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"creatorAccount","type":"address"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"voterId","type":"uint256"},{"indexed":false,"name":"totalVotes","type":"uint256"}],"name":"ProposalVoted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalExecuted","type":"event"},{"constant":true,"inputs":[],"name":"contributorsContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributionContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"contributorId","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"addProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"getProposal","outputs":[{"name":"id","type":"uint256"},{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"voterIds","type":"uint256[]"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_proposalIds","type":"uint256[]"}],"name":"batchVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/lib/contracts/contribution.js b/lib/contracts/contribution.js new file mode 100644 index 0000000..c7f933d --- /dev/null +++ b/lib/contracts/contribution.js @@ -0,0 +1,51 @@ +const ethers = require('ethers'); +const RSVP = require('rsvp'); + +const ContributionSerializer = require('../serializers/contribution'); +const Base = require('./base'); + +class Contribution extends Base { + all() { + return this.functions.contributionsCount() + .then((count) => { + count = count.toNumber(); + let contributions = []; + + for (let id = 1; id <= count; id++) { + contributions.push(this.getById(id)); + } + + return RSVP.all(contributions); + }); + } + + getById(id) { + id = ethers.utils.bigNumberify(id); + + return this.functions.getContribution(id) + .then((data) => { + return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize); + }); + + } + + getByContributor(contributor) { + return this.functions.balanceOf(contributor) + then((balance) => { + count = balance.toNumber(); + + let contributions = []; + + for (let index = 0; index <= count; index++) { + this.functions.tokenOfOwnerByIndex(contributor, index) + .then((id) => { + contributions.push(this.getById(id)); + }); + } + + return RSVP.all(contributions); + }); + } +} + +module.exports = Contribution; diff --git a/lib/contracts/index.js b/lib/contracts/index.js index a4a1792..ff3dc15 100644 --- a/lib/contracts/index.js +++ b/lib/contracts/index.js @@ -1,5 +1,6 @@ module.exports = { Contributors: require('./contributor'), + Contribution: require('./contribution'), Operator: require('./operator'), Token: require('./token'), Registry: require('./registry') diff --git a/lib/kredits.js b/lib/kredits.js index 9300179..6d17aca 100644 --- a/lib/kredits.js +++ b/lib/kredits.js @@ -5,6 +5,7 @@ const Preflight = require('./utils/preflight'); const ABIS = { Contributors: require('./abis/Contributors.json'), + Contribution: require('./abis/Contribution.json'), Operator: require('./abis/Operator.json'), Registry: require('./abis/Registry.json'), Token: require('./abis/Token.json') @@ -76,6 +77,10 @@ class Kredits { return this.contractFor('token'); } + get Contribution() { + return this.contractFor('contribution'); + } + // Should be private contractFor(name) { if (this.contracts[name]) { diff --git a/scripts/build-json.js b/scripts/build-json.js index db00827..0fb057b 100644 --- a/scripts/build-json.js +++ b/scripts/build-json.js @@ -8,6 +8,7 @@ const addressesPath = path.join(libPath, 'addresses'); const files = [ 'Contributors', + 'Contribution', 'Operator', 'Registry', 'Token' -- 2.25.1