Use ethers.js instead of the old web3

the web3 we are using is old and everything will change with web3 1.0
which is currently in beta for ages.
ethers.js seems to be a bit more lightweight and implements pretty much
the same API so let's give it a try.
This commit is contained in:
2018-04-03 18:18:15 +02:00
parent f00ecfd9ab
commit 3f0b1ccbcd
7 changed files with 733 additions and 687 deletions
+2 -2
View File
@@ -18,8 +18,8 @@ export default Component.extend({
inProgress: false, inProgress: false,
isValidAddress: function() { isValidAddress: function() {
return this.get('kredits.web3') // TODO: add proper address validation
.isAddress(this.get('newContributor.address')); return this.get('newContributor.address') !== ''
}.property('kredits.web3', 'newContributor.address'), }.property('kredits.web3', 'newContributor.address'),
isValidName: function() { isValidName: function() {
+2 -1
View File
@@ -18,7 +18,8 @@ export default Component.extend({
inProgress: false, inProgress: false,
isValidRecipient: computed('proposal.recipientAddress', function() { isValidRecipient: computed('proposal.recipientAddress', function() {
return this.get('kredits.web3').isAddress(this.get('proposal.recipientAddress')); // TODO: add proper address validation
return this.get('proposal.recipientAddress') !== ''
}), }),
isValidAmount: computed('proposal.amount', function() { isValidAmount: computed('proposal.amount', function() {
+20 -37
View File
@@ -50,74 +50,57 @@ export default Ember.Controller.extend({
watchContractEvents: function() { watchContractEvents: function() {
this.get('kredits.kreditsContract') this.get('kredits.kreditsContract')
.then((contract) => { .then((contract) => {
let events = contract.get('content').allEvents(); contract.onproposalvoted = this._handleProposalVoted.bind(this); //function(a,b,c) { console.log('voted', a, b, c) }
events.watch((error, data) => { contract.onproposalcreated = this._handleProposalCreated.bind(this);
Ember.Logger.debug('[index] Received contract event', data); contract.onproposalexecuted = this._handleProposalExecuted.bind(this);
// TODO: transfer on the token contract
switch (data.event) {
case 'ProposalCreated':
this._handleProposalCreated(data);
break;
case 'ProposalExecuted':
this._handleProposalExecuted(data);
break;
case 'ProposalVoted':
this._handleProposalVoted(data);
break;
case 'Transfer':
this._handleTransfer(data);
break;
}
});
}); });
}.on('init'), }.on('init'),
_handleProposalCreated(data) { _handleProposalCreated(proposalId, creatorAddress, recipientAddress, amount) {
if (Ember.isPresent(this.get('model.proposals') if (Ember.isPresent(this.get('model.proposals')
.findBy('id', data.args.id.toNumber()))) { .findBy('id', proposalId.toNumber()))) {
Ember.Logger.debug('[index] proposal exists, not adding from event'); Ember.Logger.debug('[index] proposal exists, not adding from event');
return false; return false;
} }
let proposal = Proposal.create({ let proposal = Proposal.create({
id: data.args.id.toNumber(), id: proposalId.toNumber(),
creatorAddress: data.args.creator, creatorAddress: creatorAddress,
recipientAddress: data.args.recipient, recipientAddress: recipientAddress,
recipientName: null, recipientName: null,
votesCount: 0, votesCount: 0,
votesNeeded: 2, votesNeeded: 2,
amount: data.args.amount.toNumber(), amount: amount.toNumber(),
executed: false, executed: false
url: data.args.url,
ipfsHash: data.args.ipfsHash
}); });
this.get('model.proposals').pushObject(proposal); this.get('model.proposals').pushObject(proposal);
}, },
_handleProposalExecuted(data) { _handleProposalExecuted(proposalId, recipientId, amount) {
if (this.get('model.proposals') if (this.get('model.proposals')
.findBy('id', data.args.id.toNumber()) .findBy('id', recipientId.toNumber())
.get('executed')) { .get('executed')) {
Ember.Logger.debug('[index] proposal already executed, not adding from event'); Ember.Logger.debug('[index] proposal already executed, not adding from event');
return false; return false;
} }
this.get('model.proposals') this.get('model.proposals')
.findBy('id', data.args.id.toNumber()) .findBy('id', recipientId.toNumber())
.setProperties({ .setProperties({
'executed': true, 'executed': true,
'votesCount': 2 // TODO use real count 'votesCount': 2 // TODO use real count
}); });
this.get('model.contributors') this.get('model.contributors')
.findBy('address', data.args.recipient) .findBy('id', recipientId)
.incrementProperty('balance', data.args.amount.toNumber()); .incrementProperty('kredits', amount.toNumber());
}, },
_handleProposalVoted(data) { _handleProposalVoted(proposalId, voter, totalVotes) {
this.get('model.proposals') this.get('model.proposals')
.findBy('id', data.args.id.toNumber()) .findBy('id', proposalId.toNumber())
.incrementProperty('votesCount', 1); .incrementProperty('votesCount', 1);
}, },
@@ -134,8 +117,8 @@ export default Ember.Controller.extend({
actions: { actions: {
confirmProposal(proposalId) { confirmProposal(proposalId) {
this.get('kredits').vote(proposalId).then(transactionId => { this.get('kredits').vote(proposalId).then(transaction => {
window.confirm('Vote submitted to Ethereum blockhain: '+transactionId); window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
}); });
} }
+3 -3
View File
@@ -8,8 +8,8 @@ export default Ember.Route.extend({
const kredits = this.get('kredits'); const kredits = this.get('kredits');
if (kredits.get('web3') && kredits.get('web3Provided')) { if (kredits.get('web3') && kredits.get('web3Provided')) {
kredits.get('web3').eth.getAccounts((error, accounts) => { kredits.get('listAccounts').then((accounts) => {
if (error || accounts.length === 0) { if (accounts.length === 0) {
if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) { if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) {
transition.retry(); transition.retry();
} }
@@ -24,7 +24,7 @@ export default Ember.Route.extend({
let kredits = this.get('kredits'); let kredits = this.get('kredits');
let totalSupply = kredits.get('tokenContract') let totalSupply = kredits.get('tokenContract')
.then((contract) => contract.invoke('totalSupply')); .then((contract) => contract.totalSupply());
return Ember.RSVP.hash({ return Ember.RSVP.hash({
contributors: kredits.getContributors(), contributors: kredits.getContributors(),
+39 -64
View File
@@ -1,4 +1,4 @@
import Web3 from 'npm:web3'; import ethers from 'npm:ethers';
import bs58 from 'npm:bs58'; import bs58 from 'npm:bs58';
import NpmBuffer from 'npm:buffer'; import NpmBuffer from 'npm:buffer';
@@ -24,18 +24,6 @@ const {
const Buffer = NpmBuffer.Buffer; const Buffer = NpmBuffer.Buffer;
function contractProxy(object) {
let proxy = Ember.ObjectProxy.extend({
invoke(contractMethod, ...args) {
debug('[kredits] invoke', contractMethod, ...args);
let contract = this.get('content');
return RSVP.denodeify(contract[contractMethod])(...args);
}
});
return proxy.create({ content: object });
}
export default Service.extend({ export default Service.extend({
ipfs: injectService(), ipfs: injectService(),
@@ -44,41 +32,42 @@ export default Service.extend({
web3Provided: false, // Web3 provided (using Mist Browser, Metamask et al.) web3Provided: false, // Web3 provided (using Mist Browser, Metamask et al.)
web3: function() { web3: function() {
if (this.get('web3Instance')) { if (this.get('web3Provider')) {
return this.get('web3Instance'); return this.get('web3Provider');
} }
let web3Instance; let web3Provider;
if (typeof window.web3 !== 'undefined') { if (typeof window.web3 !== 'undefined') {
debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask'); debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
web3Instance = new Web3(window.web3.currentProvider); let networkId = parseInt(web3.version.network);
web3Provider = new ethers.providers.Web3Provider(web3.currentProvider, {chainId: networkId});
this.set('web3Provided', true); this.set('web3Provided', true);
} else { } else {
debug('[kredits] Creating new instance from npm module class'); debug('[kredits] Creating new instance from npm module class');
let providerUrl = localStorage.getItem('config:web3ProviderUrl') || config.web3ProviderUrl; let providerUrl = localStorage.getItem('config:web3ProviderUrl') || config.web3ProviderUrl;
let provider = new Web3.providers.HttpProvider(providerUrl); let networkId = web3.version.network;
web3Instance = new Web3(provider); web3Provider = new ethers.providers.Web3Provider(web3.currentProvider, {chainId: network});
} }
this.set('web3Instance', web3Instance); this.set('web3Provider', web3Provider);
window.web3 = web3Instance; return web3Provider;
}.property('web3Provider'),
return web3Instance; listAccounts: function() {
}.property('web3Instance'), return this.get('web3').listAccounts();
}.property('web3'),
currentUserAccounts: function() { currentUserAccounts: function() {
return (this.get('web3Provided') && this.get('web3').eth.accounts) || []; // TODO: listAccounts returns now a promise
return [];
return ethers.listAccounts();
// return (this.get('web3Provided') && this.get('web3').eth.accounts) || [];
}.property('web3Provided', 'web3'), }.property('web3Provided', 'web3'),
registryContract: computed('web3', function() { registryContract: computed('web3', function() {
let networkId = this.get('web3').version.network; let networkId = this.get('web3').chainId;
let contract = this.get('web3') let registry = new ethers.Contract(addresses['Registry'][networkId], abis['Registry'], this.get('web3'));
.eth return registry;
.contract(abis['Registry'])
.at(addresses['Registry'][networkId]);
return RSVP.resolve(contractProxy(contract));
}), }),
contributorsContract: computed('web3', function() { contributorsContract: computed('web3', function() {
@@ -94,22 +83,16 @@ export default Service.extend({
}), }),
contractFor(name) { contractFor(name) {
return this.get('registryContract') return this.get('registryContract').functions.getProxyFor(name)
.then((contract) => contract.invoke('getProxyFor', name))
.then((address) => { .then((address) => {
debug('[kredits] get contract', name, address); debug('[kredits] get contract', name, address);
let contract = this.get('web3') return new ethers.Contract(address, abis[name], this.get('web3').getSigner());
.eth
.contract(abis[name])
.at(address);
return contractProxy(contract);
}); });
}, },
getContributorData(id) { getContributorData(id) {
return this.get('contributorsContract') return this.get('contributorsContract')
.then((contract) => contract.invoke('contributors', id)) .then((contract) => contract.contributors(id))
.then((data) => { .then((data) => {
debug('[kredits] contributor', data); debug('[kredits] contributor', data);
@@ -120,9 +103,8 @@ export default Service.extend({
hashFunction: hashFunction.toNumber(), hashFunction: hashFunction.toNumber(),
size: size.toNumber() size: size.toNumber()
}); });
return this.get('tokenContract') return this.get('tokenContract')
.then((contract) => contract.invoke('balanceOf', address)) .then((contract) => contract.balanceOf(address))
.then((balance) => { .then((balance) => {
balance = balance.toNumber(); balance = balance.toNumber();
@@ -144,7 +126,7 @@ export default Service.extend({
getContributors() { getContributors() {
return this.get('contributorsContract') return this.get('contributorsContract')
.then((contract) => contract.invoke('contributorsCount')) .then((contract) => contract.contributorsCount())
.then(contributorsCount => { .then(contributorsCount => {
debug('[kredits] contributorsCount:', contributorsCount.toNumber()); debug('[kredits] contributorsCount:', contributorsCount.toNumber());
let contributors = []; let contributors = [];
@@ -159,24 +141,19 @@ export default Service.extend({
getProposalData(i) { getProposalData(i) {
return this.get('kreditsContract') return this.get('kreditsContract')
.then((contract) => contract.invoke('proposals', i)) .then((contract) => contract.proposals(i))
.then(p => { .then(p => {
let ipfsHash = this.getMultihashFromBytes32({ digest: p.ipfsHash, hashFunction: p.hashFunction, size: p.hashSize });
let ipfsHash = this.getMultihashFromBytes32({
digest: p[6],
hashFunction: p[7].toNumber(),
size: p[8].toNumber()
});
let proposal = Proposal.create({ let proposal = Proposal.create({
id : i, id : i,
creatorAddress : p[0], creatorAddress : p.creator,
recipientId : p[1].toNumber(), recipientId : p.recipientId.toNumber(),
votesCount : p[2].toNumber(), votesCount : p.votesCount.toNumber(),
votesNeeded : p[3].toNumber(), votesNeeded : p.votesNeeded.toNumber(),
amount : p[4].toNumber(), amount : p.amount.toNumber(),
executed : p[5], executed : p.executed,
ipfsHash : ipfsHash ipfsHash : contributionIpfsHash
}); });
if (proposal.get('ipfsHash')) { if (proposal.get('ipfsHash')) {
@@ -193,7 +170,7 @@ export default Service.extend({
getProposals() { getProposals() {
return this.get('kreditsContract') return this.get('kreditsContract')
.then((contract) => contract.invoke('proposalsCount')) .then((contract) => contract.proposalsCount())
.then(proposalsCount => { .then(proposalsCount => {
let proposals = []; let proposals = [];
@@ -209,7 +186,7 @@ export default Service.extend({
debug('[kredits] vote for', proposalId); debug('[kredits] vote for', proposalId);
return this.get('kreditsContract') return this.get('kreditsContract')
.then((contract) => contract.invoke('vote', proposalId)) .then((contract) => contract.vote(proposalId))
.then((data) => { .then((data) => {
debug('[kredits] vote response', data); debug('[kredits] vote response', data);
return data; return data;
@@ -262,8 +239,7 @@ export default Service.extend({
return this.get('kreditsContract') return this.get('kreditsContract')
.then((contract) => { .then((contract) => {
return contract.invoke( return contract.addContributor(
'addContributor',
contributor.address, contributor.address,
digest, digest,
hashFunction, hashFunction,
@@ -290,8 +266,7 @@ export default Service.extend({
.then(ipfsHash => { .then(ipfsHash => {
return this.get('kreditsContract') return this.get('kreditsContract')
.then((contract) => { .then((contract) => {
return contract.invoke( return contract.addProposal(
'addProposal',
recipientAddress, recipientAddress,
amount, amount,
url, url,
+663 -577
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -55,12 +55,12 @@
"ember-parachute": "0.1.0", "ember-parachute": "0.1.0",
"ember-resolver": "^2.0.3", "ember-resolver": "^2.0.3",
"ember-truth-helpers": "1.3.0", "ember-truth-helpers": "1.3.0",
"ethers": "^3.0.8",
"ipfs-api": "^19.0.0", "ipfs-api": "^19.0.0",
"kosmos-schemas": "^1.1.2", "kosmos-schemas": "^1.1.2",
"kredits-contracts": "github:67P/truffle-kredits#master", "kredits-contracts": "github:67P/truffle-kredits#master",
"loader.js": "^4.0.10", "loader.js": "^4.0.10",
"tv4": "^1.3.0", "tv4": "^1.3.0"
"web3": "^0.18.2"
}, },
"engines": { "engines": {
"node": ">= 0.12.0" "node": ">= 0.12.0"
@@ -70,5 +70,6 @@
"paths": [ "paths": [
"lib/contracts" "lib/contracts"
] ]
} },
"dependencies": {}
} }