Merge branch 'master' into features/batch-voting
This commit is contained in:
commit
ce5f5fb8d2
0
.ganache-db/.gitkeep
Normal file
0
.ganache-db/.gitkeep
Normal file
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
build
|
||||
node_modules
|
||||
node_modules
|
||||
.ganache-db
|
11
README.mdown
11
README.mdown
@ -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
|
||||
|
||||
|
@ -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 };
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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"}]
|
@ -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"}]
|
@ -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"}]
|
@ -1 +1 @@
|
||||
{"100":"0x34262a9471ede40e94497d110652dcf0f26fa0db"}
|
||||
{"42":"0x205fe1b3dac678b594c5f0535e7d158e38591f93","100":"0xa7fc9b1f678c41396b53904f94f50a42ff44d826"}
|
@ -1 +1 @@
|
||||
{"100":"0x901f3e99a170619857b354c101aa97244885a39e"}
|
||||
{"42":"0x9fd66ee78a5ebe86006f12b37ff59c63f9caa15b","100":"0x95d3bd7d136bb0b7ac9988097e964236f8a9976e"}
|
@ -1 +1 @@
|
||||
{"100":"0x9447a29373cf3ce0edf41804820c2a9da0ef4fd9"}
|
||||
{"42":"0xc270e6ea4fe303df9f1a3d4a132ac425264082e7","100":"0x7458dea485d9d8301e3ce43e8a1ec1456be5ba83"}
|
@ -1 +1 @@
|
||||
{"100":"0x8ecf9fa8a1c50179cef966d53aaee3d2a382c932"}
|
||||
{"42":"0xf71ccf7ab48044ef9ae0b5e6983dbd3266b78b36","100":"0x3fc29fbe40c2d0ca78c7e81342f00226650fe2ad"}
|
27
lib/contracts/base.js
Normal file
27
lib/contracts/base.js
Normal 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;
|
57
lib/contracts/contributor.js
Normal file
57
lib/contracts/contributor.js
Normal 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
6
lib/contracts/index.js
Normal 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
57
lib/contracts/operator.js
Normal 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;
|
6
lib/contracts/registry.js
Normal file
6
lib/contracts/registry.js
Normal file
@ -0,0 +1,6 @@
|
||||
const Base = require('./base');
|
||||
|
||||
class Registry extends Base {
|
||||
}
|
||||
|
||||
module.exports = Registry;
|
7
lib/contracts/token.js
Normal file
7
lib/contracts/token.js
Normal file
@ -0,0 +1,7 @@
|
||||
const Base = require('./base');
|
||||
|
||||
class Token extends Base {
|
||||
}
|
||||
|
||||
module.exports = Token;
|
||||
|
@ -1 +0,0 @@
|
||||
module.exports = ["Contributors","Operator","Registry","Token"];
|
103
lib/kredits.js
Normal file
103
lib/kredits.js
Normal 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;
|
67
lib/serializers/contribution.js
Normal file
67
lib/serializers/contribution.js
Normal 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;
|
96
lib/serializers/contributor.js
Normal file
96
lib/serializers/contributor.js
Normal 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
28
lib/utils/healthcheck.js
Normal 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
62
lib/utils/ipfs.js
Normal 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
443
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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');
|
||||
|
@ -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)
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user