Files
kredits-web/app/services/kredits.js
T
bumi 3f0b1ccbcd 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.
2018-04-03 18:35:29 +02:00

283 lines
8.1 KiB
JavaScript

import ethers from 'npm:ethers';
import bs58 from 'npm:bs58';
import NpmBuffer from 'npm:buffer';
import RSVP from 'rsvp';
import Ember from 'ember';
import Service from 'ember-service';
import injectService from 'ember-service/inject';
import computed from 'ember-computed';
import config from 'kredits-web/config/environment';
import Proposal from 'kredits-web/models/proposal';
import abis from 'contracts/abis';
import addresses from 'contracts/addresses';
const {
getOwner,
Logger: {
debug,
warn
}
} = Ember;
const Buffer = NpmBuffer.Buffer;
export default Service.extend({
ipfs: injectService(),
web3Instance: null,
web3Provided: false, // Web3 provided (using Mist Browser, Metamask et al.)
web3: function() {
if (this.get('web3Provider')) {
return this.get('web3Provider');
}
let web3Provider;
if (typeof window.web3 !== 'undefined') {
debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
let networkId = parseInt(web3.version.network);
web3Provider = new ethers.providers.Web3Provider(web3.currentProvider, {chainId: networkId});
this.set('web3Provided', true);
} else {
debug('[kredits] Creating new instance from npm module class');
let providerUrl = localStorage.getItem('config:web3ProviderUrl') || config.web3ProviderUrl;
let networkId = web3.version.network;
web3Provider = new ethers.providers.Web3Provider(web3.currentProvider, {chainId: network});
}
this.set('web3Provider', web3Provider);
return web3Provider;
}.property('web3Provider'),
listAccounts: function() {
return this.get('web3').listAccounts();
}.property('web3'),
currentUserAccounts: function() {
// TODO: listAccounts returns now a promise
return [];
return ethers.listAccounts();
// return (this.get('web3Provided') && this.get('web3').eth.accounts) || [];
}.property('web3Provided', 'web3'),
registryContract: computed('web3', function() {
let networkId = this.get('web3').chainId;
let registry = new ethers.Contract(addresses['Registry'][networkId], abis['Registry'], this.get('web3'));
return registry;
}),
contributorsContract: computed('web3', function() {
return this.contractFor('Contributors');
}),
kreditsContract: computed('web3', function() {
return this.contractFor('Operator');
}),
tokenContract: computed('web3', function() {
return this.contractFor('Token');
}),
contractFor(name) {
return this.get('registryContract').functions.getProxyFor(name)
.then((address) => {
debug('[kredits] get contract', name, address);
return new ethers.Contract(address, abis[name], this.get('web3').getSigner());
});
},
getContributorData(id) {
return this.get('contributorsContract')
.then((contract) => contract.contributors(id))
.then((data) => {
debug('[kredits] contributor', data);
let [ address, digest, hashFunction, size, isCore ] = data;
let isCurrentUser = this.get('currentUserAccounts').includes(address);
let profileHash = this.getMultihashFromBytes32({
digest,
hashFunction: hashFunction.toNumber(),
size: size.toNumber()
});
return this.get('tokenContract')
.then((contract) => contract.balanceOf(address))
.then((balance) => {
balance = balance.toNumber();
let contributor = getOwner(this).lookup('model:contributor');
contributor.setProperties({
id,
address,
profileHash,
isCore,
isCurrentUser,
balance
});
// Load data from IPFS
contributor.loadProfile();
return contributor;
});
});
},
getContributors() {
return this.get('contributorsContract')
.then((contract) => contract.contributorsCount())
.then(contributorsCount => {
debug('[kredits] contributorsCount:', contributorsCount.toNumber());
let contributors = [];
for(var id = 1; id <= contributorsCount.toNumber(); id++) {
contributors.push(this.getContributorData(id));
}
return RSVP.all(contributors);
});
},
getProposalData(i) {
return this.get('kreditsContract')
.then((contract) => contract.proposals(i))
.then(p => {
let ipfsHash = this.getMultihashFromBytes32({ digest: p.ipfsHash, hashFunction: p.hashFunction, size: p.hashSize });
let proposal = Proposal.create({
id : i,
creatorAddress : p.creator,
recipientId : p.recipientId.toNumber(),
votesCount : p.votesCount.toNumber(),
votesNeeded : p.votesNeeded.toNumber(),
amount : p.amount.toNumber(),
executed : p.executed,
ipfsHash : contributionIpfsHash
});
if (proposal.get('ipfsHash')) {
// TODO: move ipfs into model
return proposal
.loadContribution(this.get('ipfs'))
.then(() => { return proposal; });
} else {
warn('[kredits] proposal from blockchain is missing IPFS hash', proposal);
return proposal;
}
});
},
getProposals() {
return this.get('kreditsContract')
.then((contract) => contract.proposalsCount())
.then(proposalsCount => {
let proposals = [];
for(var i = 0; i < proposalsCount.toNumber(); i++) {
proposals.push(this.getProposalData(i));
}
return RSVP.all(proposals);
});
},
vote(proposalId) {
debug('[kredits] vote for', proposalId);
return this.get('kreditsContract')
.then((contract) => contract.vote(proposalId))
.then((data) => {
debug('[kredits] vote response', data);
return data;
});
},
// TODO: move into utils
getMultihashFromBytes32(multihash) {
const { digest, hashFunction, size } = multihash;
if (size === 0) {
return;
}
const hashBytes = Buffer.from(digest.slice(2), 'hex');
const multiHashBytes = new (hashBytes.constructor)(2 + hashBytes.length);
multiHashBytes[0] = hashFunction; //contributorData[2];
multiHashBytes[1] = size; //contributorData[3];
multiHashBytes.set(hashBytes, 2);
return bs58.encode(multiHashBytes);
},
getBytes32FromMultihash(multihash) {
const decoded = bs58.decode(multihash);
return {
digest: `0x${decoded.slice(2).toString('hex')}`,
hashFunction: decoded[0],
size: decoded[1],
};
},
addContributor(contributor) {
debug('[kredits] add contributor', contributor);
return this.get('ipfs')
.storeFile(contributor.serialize())
.then(profileHash => {
contributor.setProperties({
profileHash: profileHash,
kredits: 0,
isCurrentUser: this.get('currentUserAccounts').includes(contributor.address)
});
let {
digest, hashFunction, size
} = this.getBytes32FromMultihash(profileHash);
return this.get('kreditsContract')
.then((contract) => {
return contract.addContributor(
contributor.address,
digest,
hashFunction,
size,
contributor.isCore
);
})
.then((data) => {
debug('[kredits] add contributor response', data);
return contributor;
});
});
},
addProposal(proposal) {
const {
recipientAddress,
amount,
url
} = proposal.getProperties('recipientAddress', 'amount', 'url');
return this.get('ipfs')
.storeFile(proposal.serializeContribution())
.then(ipfsHash => {
return this.get('kreditsContract')
.then((contract) => {
return contract.addProposal(
recipientAddress,
amount,
url,
ipfsHash
);
})
.then((data) => {
debug('[kredits] add proposal response', data);
return data;
});
});
},
});