diff --git a/app/components/add-proposal/component.js b/app/components/add-proposal/component.js index e605fd6..aba9926 100644 --- a/app/components/add-proposal/component.js +++ b/app/components/add-proposal/component.js @@ -53,7 +53,7 @@ export default Component.extend({ saved.then(() => { this.reset(); window.scroll(0,0); - window.alert('Contributor added.'); + window.alert('Proposal added.'); }); } } diff --git a/app/controllers/index.js b/app/controllers/index.js index fce8ada..1f5ca43 100644 --- a/app/controllers/index.js +++ b/app/controllers/index.js @@ -8,13 +8,11 @@ export default Controller.extend({ init() { this._super(...arguments); - this.get('kredits.kreditsContract') - .then((contract) => { - contract.onproposalvoted = this._handleProposalVoted.bind(this); - contract.onproposalcreated = this._handleProposalCreated.bind(this); - contract.onproposalexecuted = this._handleProposalExecuted.bind(this); - // TODO: transfer on the token contract - }); + let contract = this.get('kredits.kredits').Operator.contract; + contract.onproposalvoted = this._handleProposalVoted.bind(this); + contract.onproposalcreated = this._handleProposalCreated.bind(this); + contract.onproposalexecuted = this._handleProposalExecuted.bind(this); + // TODO: transfer on the token contract }, contributors: alias('model.contributors'), @@ -50,7 +48,7 @@ export default Controller.extend({ return; } - proposal = this.get('kredits').getProposalById(proposalId); + proposal = this.get('kredits.kredits').Operator.getById(proposalId); this.get('proposals').pushObject(proposal); }, @@ -70,7 +68,7 @@ export default Controller.extend({ this.get('contributors') .findBy('id', recipientId.toString()) - .incrementProperty('balance', amount.toNumber()); + .incrementProperty('balance', amount); }, _handleProposalVoted(proposalId, voter, totalVotes) { diff --git a/app/lib/kredits/contracts/base.js b/app/lib/kredits/contracts/base.js new file mode 100644 index 0000000..a7a35aa --- /dev/null +++ b/app/lib/kredits/contracts/base.js @@ -0,0 +1,10 @@ +export default class Base { + constructor(contract) { + this.contract = contract; + } + + get functions() { + return this.contract.functions; + } + +} diff --git a/app/lib/kredits/contracts/contributor.js b/app/lib/kredits/contracts/contributor.js new file mode 100644 index 0000000..3e8ab1e --- /dev/null +++ b/app/lib/kredits/contracts/contributor.js @@ -0,0 +1,61 @@ +import ethers from 'npm:ethers'; +import RSVP from 'rsvp'; + +import Kredits from '../kredits'; +import ContributorSerializer from '../serializers/contributor'; + +import Base from './base'; + +export default 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 as soon as the contract provides the id + data.id = id; + // TODO: rename address to account + data.address = data.account; + + return data; + }) + // Fetch IPFS data if available + .then((data) => { + return Kredits.ipfs.catAndMerge(data, ContributorSerializer.deserialize); + }); + } + + add(contributorAttr) { + let json = ContributorSerializer.serialize(contributorAttr); + // TODO: validate against schema + + return Kredits.ipfs + .add(json) + .then((ipfsHashAttr) => { + let contributor = [ + contributorAttr.address, + ipfsHashAttr.ipfsHash, + ipfsHashAttr.hashFunction, + ipfsHashAttr.hashSize, + contributorAttr.isCore, + ]; + + console.log('[kredits] addContributor', ...contributor); + return this.functions.addContributor(...contributor); + }); + } +} diff --git a/app/lib/kredits/contracts/index.js b/app/lib/kredits/contracts/index.js new file mode 100644 index 0000000..4ef4162 --- /dev/null +++ b/app/lib/kredits/contracts/index.js @@ -0,0 +1,9 @@ +import Contributor from './contributor'; +import Operator from './operator'; +import Token from './token'; + +export default { + Contributors: Contributor, + Operator, + Token +}; diff --git a/app/lib/kredits/contracts/operator.js b/app/lib/kredits/contracts/operator.js new file mode 100644 index 0000000..5a98943 --- /dev/null +++ b/app/lib/kredits/contracts/operator.js @@ -0,0 +1,61 @@ +import ethers from 'npm:ethers'; +import RSVP from 'rsvp'; + +import Kredits from '../kredits'; +import ContributionSerializer from '../serializers/contribution'; + +import Base from './base'; + +export default class Operator extends Base { + all() { + return this.functions.proposalsCount() + .then((count) => { + count = count.toNumber(); + let proposals = []; + + for (let id = 0; id < count; id++) { + proposals.push(this.getById(id)); + } + + return RSVP.all(proposals); + }); + } + + getById(id) { + id = ethers.utils.bigNumberify(id); + + return this.functions.proposals(id) + .then((data) => { + // TODO: remove as soon as the contract provides the id + data.id = id; + // TODO: rename creatorAddress to creator + data.creatorAddress = data.creator; + + return data; + }) + // Fetch IPFS data if available + .then((data) => { + return Kredits.ipfs.catAndMerge(data, ContributionSerializer.deserialize); + }); + } + + addProposal(proposalAttr) { + let json = ContributionSerializer.serialize(proposalAttr); + // TODO: validate against schema + + return Kredits.ipfs + .add(json) + .then((ipfsHashAttr) => { + let proposal = [ + proposalAttr.recipientId, + proposalAttr.amount, + ipfsHashAttr.ipfsHash, + ipfsHashAttr.hashFunction, + ipfsHashAttr.hashSize, + ]; + + console.log('[kredits] addProposal', ...proposal); + return this.functions.addProposal(...proposal); + }); + } +} diff --git a/app/lib/kredits/contracts/token.js b/app/lib/kredits/contracts/token.js new file mode 100644 index 0000000..42c97ee --- /dev/null +++ b/app/lib/kredits/contracts/token.js @@ -0,0 +1,4 @@ +import Base from './base'; + +export default class Token extends Base { +} diff --git a/app/lib/kredits/index.js b/app/lib/kredits/index.js index 7042bcf..eeb0011 100644 --- a/app/lib/kredits/index.js +++ b/app/lib/kredits/index.js @@ -1,10 +1 @@ -import ContributionSerializer from './serializers/contribution'; -import ContributorSerializer from './serializers/contributor'; -import { fromBytes32, toBytes32 } from './utils/multihash'; - -export { - ContributionSerializer, - ContributorSerializer, - fromBytes32, - toBytes32 -}; +export { default } from './kredits'; diff --git a/app/lib/kredits/kredits.js b/app/lib/kredits/kredits.js new file mode 100644 index 0000000..b087371 --- /dev/null +++ b/app/lib/kredits/kredits.js @@ -0,0 +1,77 @@ +import ethers from 'npm:ethers'; +import RSVP from 'rsvp'; + +import abis from 'contracts/abis'; +import addresses from 'contracts/addresses'; + +import contracts from './contracts'; +import IPFS from './utils/ipfs'; + +// Helpers +function capitalize(word) { + let [first, ...rest] = word; + return `${first.toUpperCase()}${rest.join('')}`; +} + +export default class Kredits { + constructor(provider, signer, addresses) { + this.provider = provider; + this.signer = signer; + + // Initialize our registry contract + this.addresses = addresses; + this.contracts = {}; + } + + static setup(provider, signer, ipfsConfig) { + this.ipfsConfig = ipfsConfig; + this.ipfs = new IPFS(ipfsConfig); + + let registryContract = this.initRegistryContract(provider); + + let addresses = Object.keys(contracts).reduce((mem, name) => { + let contractName = capitalize(name); + mem[contractName] = registryContract.functions.getProxyFor(contractName); + return mem; + }, {}); + + return RSVP.hash(addresses) + .then((addresses) => { + return new Kredits(provider, signer, addresses); + }); + } + + static initRegistryContract(provider) { + let address = addresses['Registry'][provider.chainId]; + let abi = abis['Registry']; + console.log('Initialize registry contract:', address, abi, provider); + return new ethers.Contract(address, abi, provider); + } + + 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]; + } + + let contractName = capitalize(name); + let address = this.addresses[contractName]; + let contract = new ethers.Contract(address, abis[contractName], this.signer); + this.contracts[name] = new contracts[contractName](contract); + + return this.contracts[name]; + } +} diff --git a/app/lib/kredits/serializers/contribution.js b/app/lib/kredits/serializers/contribution.js index 0802eea..2bed268 100644 --- a/app/lib/kredits/serializers/contribution.js +++ b/app/lib/kredits/serializers/contribution.js @@ -18,7 +18,7 @@ export default class Contributor { description, details, url, - } = JSON.parse(serialized); + } = JSON.parse(serialized.toString('utf8')); return { kind, diff --git a/app/lib/kredits/serializers/contributor.js b/app/lib/kredits/serializers/contributor.js index 80df182..5967903 100644 --- a/app/lib/kredits/serializers/contributor.js +++ b/app/lib/kredits/serializers/contributor.js @@ -18,7 +18,7 @@ export default class Contributor { kind, url, accounts, - } = JSON.parse(serialized); + } = JSON.parse(serialized.toString('utf8')); let github_username, github_uid, wiki_username; let github = accounts.find((a) => a.site === 'github.com'); diff --git a/app/lib/kredits/utils/ipfs.js b/app/lib/kredits/utils/ipfs.js new file mode 100644 index 0000000..5a2a7e4 --- /dev/null +++ b/app/lib/kredits/utils/ipfs.js @@ -0,0 +1,55 @@ +import ipfsAPI from 'npm:ipfs-api'; +import multihashes from 'npm:multihashes'; + +export default class IPFS { + + constructor(config) { + 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; + } + return this.cat(data) + .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 { + ipfsHash: '0x' + multihashes.toHexString(multihash.digest), + hashSize: multihash.length, + hashFunction: multihash.code, + sourceHash: ipfsHash + }; + } + + encodeHash(hashData) { + let digest = this._ipfsAPI.Buffer.from(hashData.ipfsHash.slice(2), 'hex'); + return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize); + } + + +} diff --git a/app/lib/kredits/utils/multihash.js b/app/lib/kredits/utils/multihash.js deleted file mode 100644 index f49e3e0..0000000 --- a/app/lib/kredits/utils/multihash.js +++ /dev/null @@ -1,33 +0,0 @@ -import bs58 from 'npm:bs58'; -import NpmBuffer from 'npm:buffer'; -const Buffer = NpmBuffer.Buffer; - -function fromBytes32({ digest, hashFunction, hashSize }) { - if (hashSize === 0) { - return; - } - - const hashBytes = Buffer.from(digest.slice(2), 'hex'); - const multiHashBytes = new (hashBytes.constructor)(2 + hashBytes.length); - - multiHashBytes[0] = hashFunction; - multiHashBytes[1] = hashSize; - multiHashBytes.set(hashBytes, 2); - - return bs58.encode(multiHashBytes); -} - -function toBytes32(string) { - const decoded = bs58.decode(string); - - return { - digest: `0x${decoded.slice(2).toString('hex')}`, - hashFunction: decoded[0], - hashSize: decoded[1], - }; -} - -export { - fromBytes32, - toBytes32 -}; diff --git a/app/models/contributor.js b/app/models/contributor.js index 1c3801a..127f44f 100644 --- a/app/models/contributor.js +++ b/app/models/contributor.js @@ -1,12 +1,13 @@ import computed from 'ember-computed'; import EmberObject from 'ember-object'; +import bignumber from 'kredits-web/utils/cps/bignumber'; export default EmberObject.extend({ // Contract - id: null, + id: bignumber('idRaw', 'toString'), // TODO: Should we rename it to account like in the contract? address: null, - balance: 0, + balance: bignumber('balanceRaw', 'toNumber'), isCore: false, ipfsHash: null, diff --git a/app/models/proposal.js b/app/models/proposal.js index c52d162..88594e4 100644 --- a/app/models/proposal.js +++ b/app/models/proposal.js @@ -1,14 +1,15 @@ import EmberObject from 'ember-object'; import { alias } from 'ember-computed'; +import bignumber from 'kredits-web/utils/cps/bignumber'; export default EmberObject.extend({ // Contract - id: null, + id: bignumber('idRaw', 'toString'), creatorAddress: null, - recipientId: null, - amount: null, - votesCount: null, - votesNeeded: null, + recipientId: bignumber('recipientIdRaw', 'toString'), + amount: bignumber('amountRaw', 'toNumber'), + votesCount: bignumber('votesCountRaw', 'toNumber'), + votesNeeded: bignumber('votesNeededRaw', 'toNumber'), executed: null, ipfsHash: null, diff --git a/app/routes/application.js b/app/routes/application.js index 764b2eb..9371c64 100644 --- a/app/routes/application.js +++ b/app/routes/application.js @@ -7,7 +7,7 @@ export default Route.extend({ beforeModel(transition) { const kredits = this.get('kredits'); - return kredits.initEthProvider().then(() => { + return kredits.setup().then(() => { if (kredits.get('accountNeedsUnlock')) { if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) { transition.retry(); diff --git a/app/routes/index.js b/app/routes/index.js index d032132..437e4f3 100644 --- a/app/routes/index.js +++ b/app/routes/index.js @@ -9,8 +9,7 @@ export default Ember.Route.extend({ newContributor.set('kind', 'person'); let kredits = this.get('kredits'); - let totalSupply = kredits.get('tokenContract') - .then((contract) => contract.totalSupply()); + let totalSupply = kredits.get('kredits').Token.functions.totalSupply(); return Ember.RSVP.hash({ contributors: kredits.getContributors(), diff --git a/app/services/ipfs.js b/app/services/ipfs.js deleted file mode 100644 index 0f4606f..0000000 --- a/app/services/ipfs.js +++ /dev/null @@ -1,34 +0,0 @@ -import Ember from 'ember'; -import ipfsAPI from 'npm:ipfs-api'; -import config from 'kredits-web/config/environment'; - -export default Ember.Service.extend({ - - ipfsInstance: null, - - ipfs: function() { - if (this.get('ipfsInstance')) { - return this.get('ipfsInstance'); - } - let ipfs = ipfsAPI(config.ipfs); - this.set('ipfsInstance', ipfs); - return ipfs; - }.property('ipfsInstance'), - - storeFile(content) { - let ipfs = this.get('ipfs'); - return ipfs.add(new ipfs.Buffer(content)).then(res => { - Ember.Logger.debug('[ipfs] stored content in IPFS', content, res[0].hash); - return res[0].hash; - }); - }, - - getFile(hash) { - return this.get('ipfs').cat(hash).then(res => { - return res.toString('utf8'); - }, err => { - Ember.Logger.warn('[ipfs] error trying to fetch file', hash, err); - }); - } - -}); diff --git a/app/services/kredits.js b/app/services/kredits.js index e0a8334..8f4f477 100644 --- a/app/services/kredits.js +++ b/app/services/kredits.js @@ -1,36 +1,21 @@ import ethers from 'npm:ethers'; - +import Kredits from 'kredits-web/lib/kredits'; import RSVP from 'rsvp'; import Ember from 'ember'; import Service from 'ember-service'; -import injectService from 'ember-service/inject'; import computed, { alias } from 'ember-computed'; import { isEmpty, isPresent } from 'ember-utils'; import config from 'kredits-web/config/environment'; -import abis from 'contracts/abis'; -import addresses from 'contracts/addresses'; -import { - ContributionSerializer, - ContributorSerializer, - fromBytes32, - toBytes32 -} from 'kredits-web/lib/kredits'; - const { getOwner, Logger: { - debug, - error + debug } } = Ember; - export default Service.extend({ - - ipfs: injectService(), - ethProvider: null, currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded. currentUser: null, @@ -48,7 +33,7 @@ export default Service.extend({ // this is called called in the routes beforeModel(). So it is initialized before everything else // and we can rely on the ethProvider and the potential currentUserAccounts to be available initEthProvider: function() { - return new Ember.RSVP.Promise((resolve) => { + return new RSVP.Promise((resolve) => { let ethProvider; let networkId; if (typeof window.web3 !== 'undefined') { @@ -58,14 +43,7 @@ export default Service.extend({ ethProvider.listAccounts().then((accounts) => { this.set('currentUserAccounts', accounts); this.set('ethProvider', ethProvider); - if (accounts.length > 0) { - this.get('getCurrentUser').then((contributorData) => { - this.set('currentUser', contributorData); - resolve(ethProvider); - }); - } else { - resolve(ethProvider); - } + resolve(ethProvider); }); } else { debug('[kredits] Creating new instance from npm module class'); @@ -79,36 +57,24 @@ export default Service.extend({ }); }, - registryContract: computed('ethProvider', function() { - let networkId = this.get('ethProvider').chainId; - let registry = new ethers.Contract( - addresses['Registry'][networkId], - abis['Registry'], - this.get('ethProvider') - ); - return registry; - }), + setup() { + return this.initEthProvider().then((ethProvider) => { + let signer = ethProvider.getSigner(); + return Kredits.setup(ethProvider, signer, config.ipfs).then((kredits) => { + this.set('kredits', kredits); - contributorsContract: computed('ethProvider', function() { - return this.contractFor('Contributors'); - }), - - kreditsContract: computed('ethProvider', function() { - return this.contractFor('Operator'); - }), - - tokenContract: computed('ethProvider', 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('ethProvider').getSigner()); - }); + // TODO: Cleanup + if (this.get('currentUserAccounts').length > 0) { + this.get('getCurrentUser').then((contributorData) => { + this.set('currentUser', contributorData); + }); + } + return kredits; + }); + }); }, + // TODO: Only assign valid attributes buildModel(name, attributes) { debug('[kredits] build', name, attributes); let model = getOwner(this).lookup(`model:${name}`); @@ -122,237 +88,69 @@ export default Service.extend({ return model; }, - getContributorById(id) { - id = ethers.utils.bigNumberify(id); - - return this.get('contributorsContract') - .then((contract) => contract.getContributorById(id)) - .then(this.reassembleIpfsHash) - // Set basic data - .then(({ account: address, balance, ipfsHash, isCore }) => { - let isCurrentUser = this.get('currentUserAccounts').includes(address); - - return { - id: id, - address, - balance: balance.toNumber(), - ipfsHash, - isCore, - isCurrentUser, - }; - }) - // Fetch IPFS data if available - .then((data) => { - return this.fetchAndMergeIpfsData(data, ContributorSerializer); - }) - .then((attributes) => { - return this.buildModel('contributor', attributes); - }); - }, - - getContributors() { - return this.get('contributorsContract') - .then((contract) => contract.contributorsCount()) - .then((count) => { - count = count.toNumber(); - debug('[kredits] contributors count:', count); - let contributors = []; - - for(var id = 1; id <= count; id++) { - contributors.push(this.getContributorById(id)); - } - - return RSVP.all(contributors); - }); - }, - - reassembleIpfsHash(data) { - let { ipfsHash: digest, hashFunction, hashSize } = data; - data.ipfsHash = fromBytes32({ digest, hashFunction, hashSize }); - delete data.hashFunction; - delete data.hashSize; - - return data; - }, - - /** - * Loads the contributor's profile data from IPFS and returns the attributes - * - * @method - * @public - */ - fetchAndMergeIpfsData(data, Serializer) { - let ipfsHash = data.ipfsHash; - - if (!ipfsHash) { - return data; - } - - return this.get('ipfs') - .getFile(ipfsHash) - .then(Serializer.deserialize) - .then((attributes) => { - debug('[kredits] fetched ipfs data:', attributes); - return Object.assign({}, data, attributes); - }) - .catch((err) => { - error('[kredits] error trying to fetch', ipfsHash, err); - }); - }, - - getProposalById(id) { - id = ethers.utils.bigNumberify(id); - - return this.get('kreditsContract') - .then((contract) => contract.proposals(id)) - .then(this.reassembleIpfsHash) - // Set basic data - .then(({ - creator: creatorAddress, - recipientId, - votesCount, - votesNeeded, - amount, - executed, - ipfsHash, - }) => { - return { - id: id, - creatorAddress, - recipientId: recipientId.toString(), - votesCount: votesCount.toNumber(), - votesNeeded: votesNeeded.toNumber(), - amount: amount.toNumber(), - executed, - ipfsHash - }; - }) - // Fetch IPFS data if available - .then((data) => { - return this.fetchAndMergeIpfsData(data, ContributionSerializer); - }) - .then((attributes) => { - return this.buildModel('proposal', attributes); - }); - }, - - getProposals() { - return this.get('kreditsContract') - .then((contract) => contract.proposalsCount()) - .then((count) => { - count = count.toNumber(); - debug('[kredits] proposals count:', count); - let proposals = []; - - for(var i = 0; i < count; i++) { - proposals.push(this.getProposalById(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: extract common logic to module addContributor(attributes) { debug('[kredits] add contributor', attributes); - let json = ContributorSerializer.serialize(attributes); - // TODO: validate against schema - - return this.get('ipfs') - .storeFile(json) - // Set ipfsHash - .then((ipfsHash) => { - attributes.ipfsHash = ipfsHash; - return attributes; - }) - .then((attributes) => { - return this.get('contributorsContract') - .then((contract) => { - let { address, isCore, ipfsHash } = attributes; - let { digest, hashFunction, hashSize } = toBytes32(ipfsHash); - - let contributor = [ - address, - digest, - hashFunction, - hashSize, - isCore, - ]; - debug('[kredits] addContributor', ...contributor); - return contract.addContributor(...contributor); - }); - }) + return this.get('kredits').Contributor.add(attributes) .then((data) => { debug('[kredits] add contributor response', data); return this.buildModel('contributor', attributes); }); }, + getContributors() { + return this.get('kredits').Contributor.all() + .then((contributors) => { + return contributors.map((contributor) => { + return this.buildModel('contributor', contributor); + }); + }); + }, + addProposal(attributes) { debug('[kredits] add proposal', attributes); - let json = ContributionSerializer.serialize(attributes); - // TODO: validate against schema - - return this.get('ipfs') - .storeFile(json) - // Set ipfsHash - .then((ipfsHash) => { - delete attributes.contributorIpfsHash; - attributes.ipfsHash = ipfsHash; - return attributes; - }) - .then((attributes) => { - return this.get('kreditsContract') - .then((contract) => { - let { recipientId, amount, ipfsHash } = attributes; - let { digest, hashFunction, hashSize } = toBytes32(ipfsHash); - - let proposal = [ - recipientId, - amount, - digest, - hashFunction, - hashSize, - ]; - debug('[kredits] addProposal', ...proposal); - return contract.addProposal(...proposal); - }); - }) + return this.get('kredits').Operator.addProposal(attributes) .then((data) => { debug('[kredits] add proposal response', data); return this.buildModel('proposal', attributes); }); }, + getProposals() { + return this.get('kredits').Operator.all() + .then((proposals) => { + return proposals.map((proposal) => { + return this.buildModel('proposal', proposal); + }); + }); + }, + + vote(proposalId) { + debug('[kredits] vote for', proposalId); + + return this.get('kredits').Operator.functions.vote(proposalId) + .then((data) => { + debug('[kredits] vote response', data); + return data; + }); + }, + + // TODO: Cleanup getCurrentUser: computed('ethProvider', function() { if (isEmpty(this.get('currentUserAccounts'))) { return RSVP.resolve(); } - return this.get('contributorsContract') - .then((contract) => { - return contract.getContributorIdByAddress(this.get('currentUserAccounts.firstObject')) - .then((id) => { - id = id.toNumber(); - // check if the user is a contributor or not - if (id === 0) { - return RSVP.resolve(); - } else { - return this.getContributorById(id); - } - }); + return this.get('kredits').Contributor + .functions.getContributorIdByAddress(this.get('currentUserAccounts.firstObject')) + .then((id) => { + id = id.toNumber(); + // check if the user is a contributor or not + if (id === 0) { + return RSVP.resolve(); + } else { + return this.get('kredits').Contributor.getById(id); + } }); }) }); diff --git a/app/templates/index.hbs b/app/templates/index.hbs index cf22e2a..5ad6e8b 100644 --- a/app/templates/index.hbs +++ b/app/templates/index.hbs @@ -68,7 +68,7 @@