Merge branch 'master' into features/batch-voting

This commit is contained in:
bumi 2018-04-24 12:39:16 +00:00 committed by GitHub
commit ce5f5fb8d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 914 additions and 504 deletions

0
.ganache-db/.gitkeep Normal file
View File

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
build
node_modules
node_modules
.ganache-db

View File

@ -22,11 +22,18 @@ We use following solidity contract libraries:
For local development it is recommended to use [ganache-cli](https://github.com/trufflesuite/ganache-cli) (or the [ganache GUI](http://truffleframework.com/ganache/) to run a local development chain.
Using the ganache simulator no full Ethereum node is required.
We default to the port 7545 for development to not get in conflict with the default Ethereum RPC port. Have a look at `ganache-cli` for more configuration options.
We default to:
* port 7545 for development to not get in conflict with the default Ethereum RPC port.
* network ID 100 to stay on the same network id
* store ganache data in .ganache-db to presist the chain data across restarts
* use a fixed Mnemonic code to get the same accounts across restarts
Have a look at `ganache-cli` for more configuration options.
Run your ganache simulator before using Kredits locally:
$ ganache-cli -p 7545
$ npm run ganache (which is: ganache-cli -p 7545 -i 100 --db=./.ganache-db -m kredits)
### Truffle console

View File

@ -1,65 +1,9 @@
let contractCalls = {
Contributors: {
addContributor: [
// make sure to use an IPFS hash of one of the objects in ipfsContent
['0x24dd2aedd8a9fe52ac071b3a23b2fc8f225c185e', '0x272bbfc66166f26cae9c9b96b7f9590e095f02edf342ac2dd71e1667a12116ca', 18, 32, true], // QmQyZJT9uikzDYTZLhhyVZ5ReZVCoMucYzyvDokDJsijhj
['0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', '0x9569ed44826286597982e40bbdff919c6b7752e29d13250efca452644e6b4b25', 18, 32, true] // QmYPu8zvtfDy18ZqHCviVxnKtxycw5UTJLJyk9oAEjWfnL
]
},
Operator: {
addProposal: [
[2, 23, '0x1e1a168d736fc825213144973a8fd5b3cc9f37ad821a8b3d9c3488034bbf69d8', 18, 32], // QmQNA1hhVyL1Vm6HiRxXe9xmc6LUMBDyiNMVgsjThtyevs"
[3, 42, '0x1e1a168d736fc825213144973a8fd5b3cc9f37ad821a8b3d9c3488034bbf69d8', 18, 32], // QmQNA1hhVyL1Vm6HiRxXe9xmc6LUMBDyiNMVgsjThtyevs"
[3, 100, '0x1e1a168d736fc825213144973a8fd5b3cc9f37ad821a8b3d9c3488034bbf69d8', 18, 32] // QmQNA1hhVyL1Vm6HiRxXe9xmc6LUMBDyiNMVgsjThtyevs"
],
vote: [
[1]
]
}
};
let ipfsContent = [
{
"@context": "https://schema.kosmos.org",
"@type": "Contributor",
"kind": "person",
"name": "Râu Cao",
"url": "https://sebastian.kip.pe",
"accounts": [
{
"site": "github.com",
"username": "skddc",
"uid": 842,
"url": "https://github.com/skddc/"
},
]
},
{
"@context": "https://schema.kosmos.org",
"@type": "Contributor",
"kind": "person",
"name": "Bumi",
"url": "https://michaelbumann.com",
"accounts": [
{
"site": "github.com",
"username": "bumi",
"uid": 318,
"url": "https://github.com/bumi/"
},
]
},
{
"@context": "https://schema.kosmos.org",
"@type": "Contribution",
"contributor": {
"ipfs": "QmQ2ZZS2bXgneQfKtVTVxe6dV7pcJuXnTeZJQtoVUFsAtJ"
},
"kind": "dev",
"description": "hacking hacking on kredits",
"url": "https://github.com/67P/kredits-web/pull/11",
"details": {}
}
]
module.exports = { contractCalls, ipfsContent };
let contractCalls = [
['Contributor', 'add', [{ account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', name: 'bumi', isCore: true, kind: 'preson', url: '', github_username: 'bumi', github_uid: 318, wiki_username: 'bumi' }, {gasLimit: 200000}]],
['Contributor', 'add', [{ account: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', name: 'raucau', isCore: true, kind: 'person', url: '', github_username: 'skddc', github_uid: 842, wiki_username: 'raucau' }, {gasLimit: 200000}]],
['Operator', 'addProposal', [{ contributorId: 2, amount: 42, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
['Operator', 'addProposal', [{ contributorId: 3, amount: 23, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
['Operator', 'addProposal', [{contributorId: 3, amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
['Operator', 'vote', ['1', {gasLimit: 250000}]]
];
module.exports = { contractCalls };

View File

@ -49,7 +49,7 @@ contract Contributors is Upgradeable {
return count;
}
function updateContributorAddress(uint id, address oldAccount, address newAccount) public onlyCoreOrOperator {
function updateContributorAccount(uint id, address oldAccount, address newAccount) public onlyCoreOrOperator {
contributorIds[oldAccount] = 0;
contributorIds[newAccount] = id;
contributors[id].account = newAccount;

View File

@ -7,8 +7,8 @@ import './Contributors.sol';
contract Operator is Upgradeable {
struct Proposal {
address creator;
uint recipientId;
address creatorAccount;
uint contributorId;
uint votesCount;
uint votesNeeded;
uint256 amount;
@ -24,9 +24,9 @@ contract Operator is Upgradeable {
mapping(uint256 => Proposal) public proposals;
uint256 public proposalsCount;
event ProposalCreated(uint256 id, address creator, uint recipient, uint256 amount);
event ProposalVoted(uint256 id, address voter, uint256 totalVotes);
event ProposalExecuted(uint256 id, uint recipient, uint256 amount);
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
modifier coreOnly() {
require(contributorsContract().addressIsCore(msg.sender));
@ -55,48 +55,34 @@ contract Operator is Upgradeable {
return contributorsContract().coreContributorsCount();
}
function addContributor(address _address, bytes32 _ipfsHash, uint8 _hashFunction, uint8 _hashSize, bool _isCore) public coreOnly {
contributorsContract().addContributor(_address, _ipfsHash, _hashFunction, _hashSize, _isCore);
}
function addProposal(uint contributorId, uint256 amount, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public {
require(contributorsContract().exists(contributorId));
function updateContributorIpfsHash(uint _id, bytes32 _ipfsHash, uint8 _hashFunction, uint8 _hashSize) public coreOnly {
contributorsContract().updateContributorIpfsHash(_id, _ipfsHash, _hashFunction, _hashSize);
}
function getContributor(uint _id) view public returns (address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) {
bool exists;
(account, ipfsHash, hashFunction, hashSize, isCore, exists) = contributorsContract().contributors(_id);
require(exists);
}
function addProposal(uint _recipient, uint256 _amount, bytes32 _ipfsHash, uint8 _hashFunction, uint8 _hashSize) public returns (uint256 proposalId) {
require(contributorsContract().exists(_recipient));
proposalId = proposalsCount + 1;
uint _votesNeeded = contributorsContract().coreContributorsCount() / 100 * 75;
uint256 proposalId = proposalsCount + 1;
uint256 _votesNeeded = contributorsContract().coreContributorsCount() / 100 * 75;
var p = proposals[proposalId];
p.creator = msg.sender;
p.recipientId = _recipient;
p.amount = _amount;
p.ipfsHash = _ipfsHash;
p.hashFunction = _hashFunction;
p.hashSize = _hashSize;
p.creatorAccount = msg.sender;
p.contributorId = contributorId;
p.amount = amount;
p.ipfsHash = ipfsHash;
p.hashFunction = hashFunction;
p.hashSize = hashSize;
p.votesCount = 0;
p.votesNeeded = _votesNeeded;
p.exists = true;
proposalsCount++;
ProposalCreated(proposalId, msg.sender, p.recipientId, p.amount);
ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
}
function getProposal(uint _proposalId) public view returns (address creator, uint256 recipientId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, uint256[] voterIds, bool exists) {
Proposal storage p = proposals[_proposalId];
function getProposal(uint proposalId) public view returns (uint256 id, address creatorAccount, uint256 contributorId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, uint256[] voterIds, bool exists) {
id = proposalId;
Proposal storage p = proposals[id];
return (
p.creator,
p.recipientId,
id,
p.creatorAccount,
p.contributorId,
p.votesCount,
p.votesNeeded,
p.amount,
@ -109,22 +95,19 @@ contract Operator is Upgradeable {
);
}
function vote(uint256 _proposalId) public coreOnly returns (uint _pId, bool _executed) {
var p = proposals[_proposalId];
function vote(uint256 proposalId) public coreOnly {
var p = proposals[proposalId];
require(!p.executed);
uint256 contributorId = contributorsContract().getContributorIdByAddress(msg.sender);
require(p.votes[contributorId] != true);
p.voterIds.push(contributorId);
p.votes[contributorId] = true;
uint256 voterId = contributorsContract().getContributorIdByAddress(msg.sender);
require(p.votes[voterId] != true);
p.voterIds.push(voterId);
p.votes[voterId] = true;
p.votesCount++;
_executed = false;
_pId = _proposalId;
if (p.votesCount >= p.votesNeeded) {
executeProposal(_proposalId);
_executed = true;
executeProposal(proposalId);
}
ProposalVoted(_pId, msg.sender, p.votesCount);
ProposalVoted(proposalId, voterId, p.votesCount);
}
function batchVote(uint256[] _proposalIds) public coreOnly {
@ -133,15 +116,15 @@ contract Operator is Upgradeable {
}
}
function executeProposal(uint proposalId) private returns (bool) {
function executeProposal(uint proposalId) private {
var p = proposals[proposalId];
require(!p.executed);
require(p.votesCount >= p.votesNeeded);
address recipientAddress = contributorsContract().getContributorAddressById(p.recipientId);
address recipientAddress = contributorsContract().getContributorAddressById(p.contributorId);
tokenContract().mintFor(recipientAddress, p.amount, proposalId);
p.executed = true;
ProposalExecuted(proposalId, p.recipientId, p.amount);
return true;
ProposalExecuted(proposalId, p.contributorId, p.amount);
}
}

View File

@ -17,12 +17,11 @@ contract Token is Upgradeable, BasicToken {
decimals = 18;
}
function mintFor(address _recipient, uint256 _amount, uint _proposalId) onlyRegistryContractFor('Operator') public returns (bool success) {
totalSupply_ = totalSupply_.add(_amount);
balances[_recipient] = balances[_recipient].add(_amount);
function mintFor(address contributorAccount, uint256 amount, uint proposalId) onlyRegistryContractFor('Operator') public {
totalSupply_ = totalSupply_.add(amount);
balances[contributorAccount] = balances[contributorAccount].add(amount);
LogMint(_recipient, _amount, _proposalId);
return true;
LogMint(contributorAccount, amount, proposalId);
}
}

View File

@ -1 +1 @@
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"contributors","outputs":[{"name":"account","type":"address"},{"name":"profileHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"exists","type":"bool"}],"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":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contributorIds","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldProfileHash","type":"bytes32"},{"indexed":false,"name":"newProfileHash","type":"bytes32"}],"name":"ContributorProfileUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldAddress","type":"address"},{"indexed":false,"name":"newAddress","type":"address"}],"name":"ContributorAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"_address","type":"address"}],"name":"ContributorAdded","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"uint256"},{"name":"_oldAddress","type":"address"},{"name":"_newAddress","type":"address"}],"name":"updateContributorAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"uint256"},{"name":"_hashFunction","type":"uint8"},{"name":"_hashSize","type":"uint8"},{"name":"_profileHash","type":"bytes32"}],"name":"updateContributorProfileHash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_hashFunction","type":"uint8"},{"name":"_hashSize","type":"uint8"},{"name":"_profileHash","type":"bytes32"},{"name":"isCore","type":"bool"}],"name":"addContributor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"isCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"exists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"addressIsCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"addressExists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getContributorIdByAddress","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getContributorAddressById","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getContributorById","outputs":[{"name":"account","type":"address"},{"name":"profileHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"exists","type":"bool"},{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"contributors","outputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"exists","type":"bool"}],"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":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contributorIds","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldIpfsHash","type":"bytes32"},{"indexed":false,"name":"newIpfsHash","type":"bytes32"}],"name":"ContributorProfileUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldAccount","type":"address"},{"indexed":false,"name":"newAccount","type":"address"}],"name":"ContributorAccountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"account","type":"address"}],"name":"ContributorAdded","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"oldAccount","type":"address"},{"name":"newAccount","type":"address"}],"name":"updateContributorAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"updateContributorIpfsHash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"}],"name":"addContributor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"isCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"exists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressIsCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressExists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getContributorIdByAddress","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"getContributorAddressById","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getContributorById","outputs":[{"name":"id","type":"uint256"},{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"balance","type":"uint256"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]

View File

@ -1 +1 @@
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"creator","type":"address"},{"name":"recipientId","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":"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":"creator","type":"address"},{"indexed":false,"name":"recipient","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"voter","type":"address"},{"indexed":false,"name":"totalVotes","type":"uint256"}],"name":"ProposalVoted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"recipient","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":"_address","type":"address"},{"name":"_profileHash","type":"bytes32"},{"name":"_hashFunction","type":"uint8"},{"name":"_hashSize","type":"uint8"},{"name":"_isCore","type":"bool"}],"name":"addContributor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"uint256"},{"name":"_profileHash","type":"bytes32"},{"name":"_hashFunction","type":"uint8"},{"name":"_hashSize","type":"uint8"}],"name":"updateContributorProfileHash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getContributor","outputs":[{"name":"account","type":"address"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"profileHash","type":"bytes32"},{"name":"isCore","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposalsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"uint256"},{"name":"_amount","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"},{"name":"_hashFunction","type":"uint8"},{"name":"_hashSize","type":"uint8"}],"name":"addProposal","outputs":[{"name":"proposalId","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_proposalId","type":"uint256"}],"name":"vote","outputs":[{"name":"_pId","type":"uint256"},{"name":"_executed","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_sender","type":"address"},{"name":"_proposalId","type":"uint256"}],"name":"hasVotedFor","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]
[{"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"}]

View File

@ -1 +1 @@
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"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":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"proposalId","type":"uint256"}],"name":"LogMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_proposalId","type":"uint256"}],"name":"mintFor","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"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":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"proposalId","type":"uint256"}],"name":"LogMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"contributorAccount","type":"address"},{"name":"amount","type":"uint256"},{"name":"proposalId","type":"uint256"}],"name":"mintFor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

View File

@ -1 +1 @@
{"100":"0x34262a9471ede40e94497d110652dcf0f26fa0db"}
{"42":"0x205fe1b3dac678b594c5f0535e7d158e38591f93","100":"0xa7fc9b1f678c41396b53904f94f50a42ff44d826"}

View File

@ -1 +1 @@
{"100":"0x901f3e99a170619857b354c101aa97244885a39e"}
{"42":"0x9fd66ee78a5ebe86006f12b37ff59c63f9caa15b","100":"0x95d3bd7d136bb0b7ac9988097e964236f8a9976e"}

View File

@ -1 +1 @@
{"100":"0x9447a29373cf3ce0edf41804820c2a9da0ef4fd9"}
{"42":"0xc270e6ea4fe303df9f1a3d4a132ac425264082e7","100":"0x7458dea485d9d8301e3ce43e8a1ec1456be5ba83"}

View File

@ -1 +1 @@
{"100":"0x8ecf9fa8a1c50179cef966d53aaee3d2a382c932"}
{"42":"0xf71ccf7ab48044ef9ae0b5e6983dbd3266b78b36","100":"0x3fc29fbe40c2d0ca78c7e81342f00226650fe2ad"}

27
lib/contracts/base.js Normal file
View File

@ -0,0 +1,27 @@
class Base {
constructor(contract) {
this.contract = contract;
}
get functions() {
return this.contract.functions;
}
get ipfs() {
if (!this._ipfsAPI) { throw new Error('IPFS API not configured; please set an ipfs instance'); }
return this._ipfsAPI;
}
set ipfs(ipfsAPI) {
this._ipfsAPI = ipfsAPI;
}
on(type, callback) {
let eventMethod = `on${type.toLowerCase()}`;
// Don't use this.contract.events here. Seems to be a bug in ethers.js
this.contract[eventMethod] = callback;
return this;
}
}
module.exports = Base;

View File

@ -0,0 +1,57 @@
const ethers = require('ethers');
const RSVP = require('rsvp');
const ContributorSerializer = require('../serializers/contributor');
const Base = require('./base');
class Contributor extends Base {
all() {
return this.functions.contributorsCount()
.then((count) => {
count = count.toNumber();
let contributors = [];
for (let id = 1; id <= count; id++) {
contributors.push(this.getById(id));
}
return RSVP.all(contributors);
});
}
getById(id) {
id = ethers.utils.bigNumberify(id);
return this.functions.getContributorById(id)
.then((data) => {
// TODO: remove when naming updated on the contract
data.hashDigest = data.ipfsHash;
return data;
})
// Fetch IPFS data if available
.then((data) => {
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
});
}
add(contributorAttr, callOptions = {}) {
let json = ContributorSerializer.serialize(contributorAttr);
// TODO: validate against schema
return this.ipfs
.add(json)
.then((ipfsHashAttr) => {
let contributor = [
contributorAttr.account,
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
contributorAttr.isCore,
];
return this.functions.addContributor(...contributor, callOptions);
});
}
}
module.exports = Contributor;

6
lib/contracts/index.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
Contributors: require('./contributor'),
Operator: require('./operator'),
Token: require('./token'),
Registry: require('./registry')
};

57
lib/contracts/operator.js Normal file
View File

@ -0,0 +1,57 @@
const ethers = require('ethers');
const RSVP = require('rsvp');
const ContributionSerializer = require('../serializers/contribution');
const Base = require('./base');
class Operator extends Base {
all() {
return this.functions.proposalsCount()
.then((count) => {
count = count.toNumber();
let proposals = [];
for (let id = 1; id <= count; id++) {
proposals.push(this.getById(id));
}
return RSVP.all(proposals);
});
}
getById(id) {
id = ethers.utils.bigNumberify(id);
return this.functions.getProposal(id)
.then((data) => {
// TODO: remove when naming updated on the contract
data.hashDigest = data.ipfsHash;
return data;
})
// Fetch IPFS data if available
.then((data) => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
}
addProposal(proposalAttr, callOptions = {}) {
let json = ContributionSerializer.serialize(proposalAttr);
// TODO: validate against schema
return this.ipfs
.add(json)
.then((ipfsHashAttr) => {
let proposal = [
proposalAttr.contributorId,
proposalAttr.amount,
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
];
return this.functions.addProposal(...proposal, callOptions);
});
}
}
module.exports = Operator;

View File

@ -0,0 +1,6 @@
const Base = require('./base');
class Registry extends Base {
}
module.exports = Registry;

7
lib/contracts/token.js Normal file
View File

@ -0,0 +1,7 @@
const Base = require('./base');
class Token extends Base {
}
module.exports = Token;

View File

@ -1 +0,0 @@
module.exports = ["Contributors","Operator","Registry","Token"];

103
lib/kredits.js Normal file
View File

@ -0,0 +1,103 @@
const ethers = require('ethers');
const RSVP = require('rsvp');
const Healthcheck = require('./utils/healthcheck');
const ABIS = {
Contributors: require('./abis/Contributors.json'),
Operator: require('./abis/Operator.json'),
Registry: require('./abis/Registry.json'),
Token: require('./abis/Token.json')
};
const RegistryAddress = require('./addresses/Registry.json');
const Contracts = require('./contracts');
const IPFS = require('./utils/ipfs')
// Helpers
function capitalize(word) {
let [first, ...rest] = word;
return `${first.toUpperCase()}${rest.join('')}`;
}
class Kredits {
constructor(provider, signer, addresses) {
this.provider = provider;
this.signer = signer;
// by default we only need the registry address.
// the rest is loaded from there in the init() function
this.addresses = addresses || {Registry: RegistryAddress[this.provider.chainId.toString()]}; // chaiID must be a string
this.abis = ABIS;
this.contracts = {};
this.ipfs = new IPFS();
}
init(names) {
let contractsToLoad = names || Object.keys(ABIS);
let addressPromises = contractsToLoad.map((contractName) => {
return this.Registry.functions.getProxyFor(contractName).then((address) => {
this.addresses[contractName] = address;
}).catch((error) => {
throw new Error(`Failed to get address for ${contractName} from registry at ${this.Registry.contract.address}
- correct registry? does it have version entry? - ${error.message}`
);
});
});
return RSVP.all(addressPromises).then(() => { return this });
}
static setup(provider, signer, ipfsConfig = null) {
console.log('Kredits.setup() is deprecated use new Kredits().init() instead');
let ipfs = new IPFS(ipfsConfig);
return new Kredits(provider, signer).init().then((kredits) => {
kredits.ipfs = ipfs;
return kredits;
});
}
get Registry() {
return this.contractFor('registry');
}
get Contributor() {
// TODO: rename to contributor
return this.contractFor('contributors');
}
get Operator() {
return this.contractFor('operator');
}
get Token() {
return this.contractFor('token');
}
// Should be private
contractFor(name) {
if (this.contracts[name]) {
return this.contracts[name];
}
const contractName = capitalize(name);
const address = this.addresses[contractName];
const abi = this.abis[contractName];
if (!address || !abi) {
throw new Error(`Address or ABI not found for ${contractName}`);
}
let signerOrProvider = this.signer || this.provider;
let contract = new ethers.Contract(address, abi, signerOrProvider);
this.contracts[name] = new Contracts[contractName](contract);
this.contracts[name].ipfs = this.ipfs;
return this.contracts[name];
}
healthcheck() {
return new Healthcheck(this).check();
}
}
module.exports = Kredits;

View File

@ -0,0 +1,67 @@
/**
* Handle serialization for JSON-LD object of the contribution, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
*
* @class
* @public
*/
class Contribution {
/**
* Deserialize JSON to object
*
* @method
* @public
*/
static deserialize(serialized) {
let {
kind,
description,
details,
url,
} = JSON.parse(serialized.toString('utf8'));
return {
kind,
description,
details,
url,
ipfsData: serialized,
};
}
/**
* Serialize object to JSON
*
* @method
* @public
*/
static serialize(deserialized) {
let {
contributorIpfsHash,
kind,
description,
url,
details
} = deserialized;
let data = {
"@context": "https://schema.kosmos.org",
"@type": "Contribution",
"contributor": {
"ipfs": contributorIpfsHash
},
kind,
description,
"details": details || {}
};
if (url) {
data["url"] = url;
}
// Write it pretty to ipfs
return JSON.stringify(data, null, 2);
}
}
module.exports = Contribution;

View File

@ -0,0 +1,96 @@
/**
* Handle serialization for JSON-LD object of the contributor, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
*
* @class
* @public
*/
class Contributor {
/**
* Deserialize JSON to object
*
* @method
* @public
*/
static deserialize(serialized) {
let {
name,
kind,
url,
accounts,
} = JSON.parse(serialized.toString('utf8'));
let github_username, github_uid, wiki_username;
let github = accounts.find((a) => a.site === 'github.com');
let wiki = accounts.find((a) => a.site === 'wiki.kosmos.org');
if (github) {
(({ username: github_username, uid: github_uid} = github));
}
if (wiki) {
(({ username: wiki_username } = wiki));
}
return {
name,
kind,
url,
accounts,
github_uid,
github_username,
wiki_username,
ipfsData: serialized,
};
}
/**
* Serialize object to JSON
*
* @method
* @public
*/
static serialize(deserialized) {
let {
name,
kind,
url,
github_uid,
github_username,
wiki_username,
} = deserialized;
let data = {
"@context": "https://schema.kosmos.org",
"@type": "Contributor",
kind,
name,
"accounts": []
};
if (url) {
data["url"] = url;
}
if (github_uid) {
data.accounts.push({
"site": "github.com",
"uid": github_uid,
"username": github_username,
"url": `https://github.com/${github_username}`
});
}
if (wiki_username) {
data.accounts.push({
"site": "wiki.kosmos.org",
"username": wiki_username,
"url": `https://wiki.kosmos.org/User:${wiki_username}`
});
}
// Write it pretty to ipfs
return JSON.stringify(data, null, 2);
}
}
module.exports = Contributor;

28
lib/utils/healthcheck.js Normal file
View File

@ -0,0 +1,28 @@
class Healthcheck {
constructor(kredits) {
this.kredits = kredits;
}
check() {
return this.kredits.ipfs._ipfsAPI.id()
.catch((error) => {
throw new Error(`IPFS node not available; config: ${JSON.stringify(this.kredits.ipfs.config)} - ${error.message}`);
})
.then(() => {
let promises = Object.keys(this.kredits.contracts).map((name) => {
let contractWrapper = this.kredits.contracts[name];
return this.kredits.provider.getCode(contractWrapper.contract.address).then((code) => {
// not sure if we always get the same return value if the code is not available
// so checking if it is < 5 long
if (code === '0x00' || code.length < 5) {
throw new Error(`Contract for: ${name} not found at ${contractWrapper.contract.address} on network ${this.kredits.provider.chainId}`);
}
return true;
});
});
return Promise.all(promises);
});
}
}
module.exports = Healthcheck;

62
lib/utils/ipfs.js Normal file
View File

@ -0,0 +1,62 @@
const ipfsAPI = require('ipfs-api');
const multihashes = require('multihashes');
class IPFS {
constructor(config) {
if (!config) {
config = {host: 'localhost', port: '5001', protocol: 'http'};
}
this._ipfsAPI = ipfsAPI(config);
this._config = config;
}
catAndMerge(data, deserialize) {
// if no hash details are found simply return the data; nothing to merge
if (!data.hashSize || data.hashSize === 0) {
return data;
}
// merge ipfsHash (encoded from hashDigest, hashSize, hashFunction)
data.ipfsHash = multihashes.toB58String(this.encodeHash(data));
return this.cat(data.ipfsHash)
.then(deserialize)
.then((attributes) => {
return Object.assign({}, data, attributes);
});
}
add(data) {
return this._ipfsAPI
.add(new this._ipfsAPI.Buffer(data))
.then((res) => {
return this.decodeHash(res[0].hash);
});
}
cat(hashData) {
let ipfsHash = hashData; // default - if it is a string
if (hashData.hasOwnProperty('hashSize')) {
ipfsHash = this.encodeHash(hashData);
}
return this._ipfsAPI.cat(ipfsHash);
}
decodeHash(ipfsHash) {
let multihash = multihashes.decode(multihashes.fromB58String(ipfsHash));
return {
hashDigest: '0x' + multihashes.toHexString(multihash.digest),
hashSize: multihash.length,
hashFunction: multihash.code,
ipfsHash: ipfsHash
};
}
encodeHash(hashData) {
let digest = this._ipfsAPI.Buffer.from(hashData.hashDigest.slice(2), 'hex');
return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize);
}
}
module.exports = IPFS;

443
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,14 +2,14 @@
"name": "kredits-contracts",
"version": "1.0.0",
"description": "Ethereum contracts and npm wrapper for Kredits",
"main": "index.js",
"main": "./lib/kredits.js",
"directories": {
"test": "test"
},
"scripts": {
"build-json": "truffle compile && node ./scripts/build-json.js",
"bootstrap": "truffle migrate --reset && truffle exec scripts/seeds.js && npm run build-json",
"ganache": "ganache-cli -p 7545 -i 100",
"bootstrap": "truffle migrate --reset && npm run build-json && truffle exec scripts/seeds.js",
"ganache": "ganache-cli -p 7545 -i 100 --db=./.ganache-db -m kredits",
"dev": "truffle migrate && npm run build-json",
"test": "echo \"Error: no test specified\" && exit 1"
},
@ -24,9 +24,15 @@
},
"homepage": "https://github.com/67P/truffle-kredits#readme",
"devDependencies": {
"async-each-series": "^1.1.0",
"ganache-cli": "^6.0.3",
"ipfs-api": "^19.0.0",
"promptly": "^3.0.3",
"truffle": "^4.1.3",
"zeppelin-solidity": "^1.7.0"
},
"dependencies": {
"ethers": "3.0.15",
"ipfs-api": "^19.0.0",
"rsvp": "^4.8.2"
}
}

View File

@ -1,47 +1,50 @@
const Registry = artifacts.require('./Registry.sol');
const Operator = artifacts.require('./Operator.sol');
const Contributors = artifacts.require('./Contributors.sol');
const promptly = require('promptly');
const bs58 = require('bs58');
var bs58 = require('bs58');
const ethers = require('ethers');
const Kredits = require('../lib/kredits');
function getBytes32FromMultiash(multihash) {
const decoded = bs58.decode(multihash);
return {
digest: `0x${decoded.slice(2).toString('hex')}`,
hashFunction: decoded[0],
size: decoded[1],
};
async function prompt(message, options) {
if (!options) {
options = {default: ''}
}
return await promptly.prompt(message, options);
}
module.exports = function(callback) {
Registry.deployed().then(async (registry) => {
var operatorAddress = await registry.getProxyFor('Operator');
var contributorsAddress = await registry.getProxyFor('Contributors');
var operator = await Operator.at(operatorAddress);
var contributors = await Contributors.at(contributorsAddress);
const networkId = parseInt(web3.version.network);
const provider = new ethers.providers.Web3Provider(
web3.currentProvider, { chainId: networkId }
);
const kredits = await Kredits.setup(provider, provider.getSigner());
let contributorToAddAddress = process.argv[4];
if(!contributorToAddAddress) {
console.log('please provide an address');
proxess.exit();
}
let ipfsHash = process.argv[5] || 'QmQyZJT9uikzDYTZLhhyVZ5ReZVCoMucYzyvDokDJsijhj';
let contributorMultihash = getBytes32FromMultiash(ipfsHash);
let isCore = true;
let contributorResult = await contributors.addContributor(contributorToAddAddress, contributorMultihash.digest, contributorMultihash.hashFunction, contributorMultihash.size, isCore);
console.log('Contributor added, tx: ', contributorResult.tx);
console.log(`Using contributors at: ${kredits.Contributor.contract.address}`);
let contributorId = await contributors.getContributorIdByAddress(contributorToAddAddress);
let proposalMultihash = getBytes32FromMultiash('QmQNA1hhVyL1Vm6HiRxXe9xmc6LUMBDyiNMVgsjThtyevs');
let proposalResult = await operator.addProposal(contributorId, 23, proposalMultihash.digest, proposalMultihash.hashFunction, proposalMultihash.size);
console.log('Proposal added, tx: ', proposalResult.tx);
let contributorAttributes = {
account: await prompt('Contributor address: ', {}),
name: await prompt('Name: '),
isCore: await prompt('core? y/n') === 'y',
kind: await prompt('Kind (default person): ', {default: 'person'}),
url: await prompt('URL: '),
github_username: await prompt('GitHub username: '),
github_uid: await prompt('GitHub UID: '),
wiki_username: await prompt('Wiki username: '),
};
let proposalId = await operator.proposalsCount();
let votingResult = await operator.vote(proposalId.toNumber()-1);
console.log('Voted for proposal', proposalId.toString(), votingResult.tx);
console.log("\nAdding contributor:");
console.log(contributorAttributes);
kredits.Contributor.add(contributorAttributes, {gasLimit: 250000}).then((result) => {
console.log("\n\nResult:");
console.log(result);
callback();
}).catch((error) => {
console.log('Failed to create contributor');
callback(error);
});
callback();
});
}

View File

@ -1,8 +1,9 @@
const Registry = artifacts.require('./Registry.sol');
const Operator = artifacts.require('./Operator.sol');
const Contributors = artifacts.require('./Contributors.sol');
const promptly = require('promptly');
var bs58 = require('bs58');
const bs58 = require('bs58');
function getBytes32FromMultiash(multihash) {
const decoded = bs58.decode(multihash);
@ -22,12 +23,9 @@ module.exports = function(callback) {
var operator = await Operator.at(operatorAddress);
var contributors = await Contributors.at(contributorsAddress);
let recipientAddress = process.argv[4];
if(!recipientAddress) {
console.log('please provide an address');
process.exit();
}
let ipfsHash = process.argv[5] || 'QmQNA1hhVyL1Vm6HiRxXe9xmc6LUMBDyiNMVgsjThtyevs';
let recipientAddress = await promptly.prompt('Contributor address: ');
let ipfsHash = await promptly.prompt('IPFS hash (blank for default): ', { default: 'QmQNA1hhVyL1Vm6HiRxXe9xmc6LUMBDyiNMVgsjThtyevs' });
let multihash = getBytes32FromMultiash(ipfsHash);
let contributorId = await contributors.getContributorIdByAddress(recipientAddress);

View File

@ -18,14 +18,16 @@ files.forEach((fileName) => {
let abiFile = path.join(abisPath, `${fileName}.json`);
fs.writeFileSync(abiFile, JSON.stringify(file.abi));
let addresseFile = path.join(addressesPath, `${fileName}.json`);
let addresses = Object.keys(file.networks)
.reduce((addresses, key) => {
addresses[key] = file.networks[key].address;
return addresses;
}, {});
fs.writeFileSync(addresseFile, JSON.stringify(addresses));
let indexFile = path.join(libPath, 'index.js');
fs.writeFileSync(indexFile, `module.exports = ${JSON.stringify(files)};`);
if (fileName === 'Registry') {
let addresseFile = path.join(addressesPath, `${fileName}.json`);
let content = fs.readFileSync(addresseFile);
let addresses = Object.keys(file.networks)
.reduce((addresses, key) => {
addresses[key] = file.networks[key].address;
return addresses;
}, JSON.parse(content));
fs.writeFileSync(addresseFile, JSON.stringify(addresses));
}
});
console.log("Don't forget to reaload the JSON files from your application; i.e. restart kredits-web");

View File

@ -1,39 +1,45 @@
const REPL = require('repl');
const promptly = require('promptly');
const ethers = require('ethers');
const Kredits = require('../lib/kredits');
module.exports = function(callback) {
const Registry = artifacts.require('./Registry.sol');
Registry.deployed().then(async (registry) => {
let contractName = process.argv[4];
let method = process.argv[5];
let args = process.argv.slice(6);
if (!contractName) {
console.log("Usage:");
console.log(" truffle exec scripts/cli.js <Contract name> <method to call> [<optional> <arguments>]");
callback();
return;
let contractName = await promptly.prompt('Contract Name: ');
let method = await promptly.prompt('Function: ');
let argumentInput = await promptly.prompt('Arguments (comma separated): ', { default: '' });
let args = [];
if (argumentInput !== '') {
args = argumentInput.split(',').map(a => a.trim());
}
let contractAddress = await registry.getProxyFor(contractName);
console.log(`Using ${contractName} at ${contractAddress}`);
let contract = await artifacts.require(`./${contractName}`).at(contractAddress);
const networkId = parseInt(web3.version.network);
const provider = new ethers.providers.Web3Provider(
web3.currentProvider, { chainId: networkId }
);
const kredits = await Kredits.setup(provider, provider.getSigner());
const contract = kredits[contractName].contract;
console.log(`Using ${contractName} at ${contract.address}`);
console.log(`Calling ${method} with ${JSON.stringify(args)}`);
if (!contract[method]) {
callback(new Error(`Method ${method} is not defined on ${contractName}`));
return;
}
console.log(`Calling ${method} with ${JSON.stringify(args)}`);
contract[method](...args).then((result) => {
console.log("\nResult:");
console.log(result);
console.log("\nStartig a REPL. (type .exit to exit)");
console.log(`defined variables: result, ${contractName}, web3`);
console.log(`defined variables: result, ${contractName}, kredis`);
let r = REPL.start();
r.context.result = result;
r.context[contractName] = contract;
r.context.web3 = web3;
r.context.kredits = kredits;
r.on('exit', () => {
console.log('Bye');

View File

@ -1,35 +0,0 @@
var bs58 = require('bs58');
function getBytes32FromMultiash(multihash) {
const decoded = bs58.decode(multihash);
return {
digest: `0x${decoded.slice(2).toString('hex')}`,
hashFunction: decoded[0],
size: decoded[1],
};
}
function getMultihashFromBytes32(multihash) {
const { digest, hashFunction, size } = multihash;
if (size === 0) return null;
// cut off leading "0x"
const hashBytes = Buffer.from(digest.slice(2), 'hex');
// prepend hashFunction and digest size
//const multihashBytes = new (hashBytes.constructor)(2 + hashBytes.length);
const multihashBytes = new Buffer(2 + hashBytes.length);
console.log(hashBytes.constructor);
multihashBytes[0] = hashFunction;
multihashBytes[1] = size;
multihashBytes.set(hashBytes, 2);
return bs58.encode(multihashBytes);
}
var m = getBytes32FromMultiash(process.argv[2]);
console.log(m)

View File

@ -1,34 +1,39 @@
const path = require('path');
const seeds = require(path.join(__dirname, '..', '/config/seeds.js'));
const IPFS = require('ipfs-api');
var ipfs = IPFS({host: 'localhost', port: '5001', protocol: 'http'})
const ethers = require('ethers');
const Kredits = require('../lib/kredits');
const each = require('async-each-series');
module.exports = function(callback) {
const Registry = artifacts.require('./Registry.sol');
const contracts = {};
Registry.deployed().then((registry) => {
Object.keys(seeds.contractCalls).forEach(async (contract) => {
var address = await registry.getProxyFor(contract);
console.log(`Using ${contract} at ${address}`);
contracts[contract] = await artifacts.require(contract).at(address);
Registry.deployed().then(async (registry) => {
Object.keys(seeds.contractCalls[contract]).forEach((method) => {
seeds.contractCalls[contract][method].forEach((args) => {
console.log(`[Sending] ${contract}.#${method}(${JSON.stringify(args)})`);
contracts[contract][method](...args).then((result) => {
console.log(`[Result] ${contract}.${method}(${JSON.stringify(args)}) => ${result.tx}`);
});
});
const networkId = parseInt(web3.version.network);
const provider = new ethers.providers.Web3Provider(
web3.currentProvider, { chainId: networkId }
);
const kredits = await Kredits.setup(provider, provider.getSigner());
each(seeds.contractCalls, (call, next) => {
let [contractName, method, args] = call;
let contractWrapper = kredits[contractName];
let func;
if (contractWrapper[method]) {
func = contractWrapper[method];
} else {
func = contractWrapper.functions[method];
}
func.apply(contractWrapper, args).then((result) => {
console.log(`[OK] kredits.${contractName}.${method}(${JSON.stringify(args)}) => ${result.hash}`);
next();
}).catch((error) => {
console.log(`[FAILD] kredits.${contractName}.${method}(${JSON.stringify(args)})`);
callback(error)
});
});
}, () => { console.log("\nDone!") });
});
seeds.ipfsContent.forEach((content) => {
ipfs.add(new ipfs.Buffer(JSON.stringify(content))).then((result) => { console.log(`[IPFS] added ${result[0].hash}`) });
});
callback();
}

View File

@ -1,12 +1,12 @@
const promptly = require('promptly');
module.exports = async function(callback) {
let recipient = await promptly.prompt('Recipient address: ');
let amount = await promptly.prompt('Amount: ', {default: '1'});
amount = parseInt(amount);
console.log(`sending ${amount} ETH from ${web3.eth.accounts[0]} to ${recipient}`);
module.exports = function(callback) {
let recipient = process.argv[4];
if (!recipient) {
console.log('Please provide a recipient address');
process.exit();
}
let amount = parseInt(process.argv[5]) || 1;
console.log(recipient);
web3.eth.sendTransaction({to: recipient, value: web3.toWei(amount), from: web3.eth.accounts[0]}, console.log);
callback();

View File

@ -5,6 +5,11 @@ module.exports = {
host: "127.0.0.1",
port: 7545,
network_id: "*" // Match any network id
},
kovan: {
host: "127.0.0.1",
port: 8545,
network_id: "42"
}
}
};