diff --git a/src/contracts/abstractions/Arbitrable.js b/src/contracts/abstractions/Arbitrable.js index 9e94980..5480ba9 100644 --- a/src/contracts/abstractions/Arbitrable.js +++ b/src/contracts/abstractions/Arbitrable.js @@ -1,8 +1,9 @@ -import AbstractContract from '../AbstractContract' import Eth from 'ethjs' import getContractAddress from '../../utils/getContractAddress' +import AbstractContract from '../AbstractContract' + /** * Arbitrable Abstract Contarct API. This wraps an arbitrable contract. It provides * interaction with both the off chain store as well as the arbitrable instance. All @@ -39,7 +40,10 @@ class ArbitrableContract extends AbstractContract { const txCount = await eth.getTransactionCount(account) // determine the contract address WARNING if the nonce changes this will produce a different address const contractAddress = getContractAddress(account, txCount) - const metaEvidenceUri = this._StoreProvider.getMetaEvidenceUri(account, contractAddress) + const metaEvidenceUri = this._StoreProvider.getMetaEvidenceUri( + account, + contractAddress + ) const contractInstance = await this._contractImplementation.constructor.deploy( account, value, @@ -61,8 +65,6 @@ class ArbitrableContract extends AbstractContract { { partyA: account, partyB, - arbitrator: arbitratorAddress, - timeout, email, metaEvidence } @@ -92,7 +94,11 @@ class ArbitrableContract extends AbstractContract { hash ) // construct the unique URI - const evidenceUri = this._StoreProvider.getEvidenceUri(account, contractAddress, evidenceIndex) + const evidenceUri = this._StoreProvider.getEvidenceUri( + account, + contractAddress, + evidenceIndex + ) const txHash = await this._contractImplementation.submitEvidence( account, evidenceUri diff --git a/src/contracts/implementations/arbitrable/Arbitrable.js b/src/contracts/implementations/arbitrable/Arbitrable.js new file mode 100644 index 0000000..6c48511 --- /dev/null +++ b/src/contracts/implementations/arbitrable/Arbitrable.js @@ -0,0 +1,104 @@ +import _ from 'lodash' + +import ContractImplementation from '../../ContractImplementation' +import EventListener from '../../../utils/EventListener' +import httpRequest from '../../../utils/httpRequest' + +/** + * Provides interaction with standard Arbitrable contracts + */ +class Arbitrable extends ContractImplementation { + /** + * Constructor ArbitrableTransaction. + * @param {object} web3Provider instance + * @param {string} contractAddress of the contract + */ + constructor(web3Provider, contractArtifact, contractAddress) { + super(web3Provider, contractArtifact, contractAddress) + } + + /** + * Get the meta evidence for the contract. Arbitrable Transaction can only have + * one meta-evidence that is submitted on contract creation. Look up meta-evidence event + * and make an http request to the resource. + */ + getMetaEvidence = async () => { + const metaEvidenceLog = await EventListener.getEventLogs( + this, + 'MetaEvidence', + 0, + 'latest', + { _metaEvidenceID: 0 } + ) + + if (!metaEvidenceLog[0]) return {} // NOTE better to throw errors for missing meta-evidence? + + const metaEvidenceUri = metaEvidenceLog[0].args._evidence + // FIXME caching issue need a query param to fetch from AWS + const metaEvidenceResponse = await httpRequest( + 'GET', + metaEvidenceUri + '?nocache' + ) + + if (metaEvidenceResponse.status >= 400) + throw new Error(`Unable to fetch meta-evidence at ${metaEvidenceUri}`) + return metaEvidenceResponse.body || metaEvidenceResponse + } + + /** + * Get the evidence submitted in a dispute. + */ + getEvidence = async () => { + await this.loadContract() + const arbitratorAddress = await this.contractInstance.arbitrator() + await this.loadContract() + const disputeId = (await this.contractInstance.disputeID()).toNumber() + + // No evidence yet as there is no dispute + if (_.isNull(disputeId)) return [] + + const evidenceLogs = await EventListener.getEventLogs( + this, + 'Evidence', + 0, + 'latest', + { _disputeID: disputeId, _arbitrator: arbitratorAddress } + ) + + // TODO verify hash and data are valid if hash exists + return Promise.all( + evidenceLogs.map(async evidenceLog => { + const evidenceURI = evidenceLog.args._evidence + const evidence = await httpRequest('GET', evidenceURI) + const submittedAt = (await this._Web3Wrapper.getBlock( + evidenceLog.blockNumber + )).timestamp + return { + ...evidence.body, + ...{ submittedBy: evidenceLog.args._party, submittedAt } + } + }) + ) + } + + /** + * Fetch all standard contract data. + */ + getContractData = async () => { + await this.loadContract() + + const [metaEvidence, partyA, partyB] = await Promise.all([ + this.getMetaEvidence(), + this.contractInstance.partyA(), + this.contractInstance.partyB() + ]) + + return { + partyA, + partyB, + metaEvidence + } + } +} + +export default Arbitrable diff --git a/src/contracts/implementations/arbitrable/ArbitrablePermissionList.js b/src/contracts/implementations/arbitrable/ArbitrablePermissionList.js new file mode 100644 index 0000000..b8226f7 --- /dev/null +++ b/src/contracts/implementations/arbitrable/ArbitrablePermissionList.js @@ -0,0 +1,44 @@ +import ArbitrablePermissionListArtifact from 'kleros-interaction/build/contracts/ArbitrablePermissionList' + +import * as ethConstants from '../../../constants/eth' +import Arbitrable from './Arbitrable' +import deployContractAsync from '../../../utils/deployContractAsync' + +/** + * Provides interaction with an Arbitrable Transaction contract deployed on the blockchain. + */ +class ArbitrablePermissionList extends Arbitrable { + /** + * Constructor ArbitrableTransaction. + * @param {object} web3Provider instance + * @param {string} contractAddress of the contract + */ + constructor(web3Provider, contractAddress) { + super(web3Provider, ArbitrablePermissionListArtifact, contractAddress) + } + + /** + * Deploy ArbitrablePermissionList. TODO + * @param {object} account Ethereum account (default account[0]) + * @param {number} value funds to be placed in contract + * @param {object} web3Provider web3 provider object + * @returns {object} truffle-contract Object | err The deployed contract or an error + */ + // static deploy = async ( + // account, + // value = ethConstants.TRANSACTION.VALUE, + // web3Provider + // ) => { + // // const contractDeployed = await deployContractAsync() + // // + // // return contractDeployed + // } + + getItemByDisputeId = async disputeId => { + await this.loadContract() + + return this.contractInstance.disputeIDToItem(disputeId) + } +} + +export default ArbitrablePermissionList diff --git a/src/contracts/implementations/arbitrable/ArbitrableTransaction.js b/src/contracts/implementations/arbitrable/ArbitrableTransaction.js index c55f39b..b11312d 100644 --- a/src/contracts/implementations/arbitrable/ArbitrableTransaction.js +++ b/src/contracts/implementations/arbitrable/ArbitrableTransaction.js @@ -4,15 +4,13 @@ import _ from 'lodash' import * as ethConstants from '../../../constants/eth' import * as contractConstants from '../../../constants/contract' import * as errorConstants from '../../../constants/error' -import ContractImplementation from '../../ContractImplementation' +import Arbitrable from './Arbitrable' import deployContractAsync from '../../../utils/deployContractAsync' -import EventListener from '../../../utils/EventListener' -import httpRequest from '../../../utils/httpRequest' /** * Provides interaction with an Arbitrable Transaction contract deployed on the blockchain. */ -class ArbitrableTransaction extends ContractImplementation { +class ArbitrableTransaction extends Arbitrable { /** * Constructor ArbitrableTransaction. * @param {object} web3Provider instance @@ -59,72 +57,6 @@ class ArbitrableTransaction extends ContractImplementation { return contractDeployed } - /** - * Get the meta evidence for the contract. Arbitrable Transaction can only have - * one meta-evidence that is submitted on contract creation. Look up meta-evidence event - * and make an http request to the resource. - */ - getMetaEvidence = async () => { - const metaEvidenceLog = await EventListener.getEventLogs( - this, - 'MetaEvidence', - 0, - 'latest', - { _metaEvidenceID: 0 } - ) - - if (!metaEvidenceLog[0]) - return {} // NOTE better to throw errors for missing meta-evidence? - - const metaEvidenceUri = metaEvidenceLog[0].args._evidence - const metaEvidenceResponse = await httpRequest( - 'GET', - metaEvidenceUri - ) - - if (metaEvidenceResponse.status !== 200) - throw new Error(`Unable to fetch meta-evidence at ${metaEvidenceUri}`) - return metaEvidenceResponse.body || {} - } - - /** - * Get the evidence submitted in a dispute. - */ - getEvidence = async () => { - await this.loadContract() - const arbitratorAddress = await this.contractInstance.arbitrator() - await this.loadContract() - const disputeId = (await this.contractInstance.disputeID()).toNumber() - - // No evidence yet as there is no dispute - if (_.isNull(disputeId)) - return [] - - const evidenceLogs = await EventListener.getEventLogs( - this, - 'Evidence', - 0, - 'latest', - { _disputeID: disputeId, _arbitrator: arbitratorAddress } - ) - - // TODO verify hash and data are valid if hash exists - return Promise.all(evidenceLogs.map(async evidenceLog => { - const evidenceURI = evidenceLog.args._evidence - const evidence = await httpRequest( - 'GET', - evidenceURI - ) - const submittedAt = ( - await this._Web3Wrapper.getBlock(evidenceLog.blockNumber) - ).timestamp - return { - ...evidence.body, - ...{ submittedBy: evidenceLog.args._party, submittedAt } - } - })) - } - /** * Pay the party B. To be called when the good is delivered or the service rendered. * @param {string} account - Ethereum account (default account[0]). @@ -202,20 +134,14 @@ class ArbitrableTransaction extends ContractImplementation { * @param {string} url A link to an evidence using its URI. * @returns {string} txHash Hash transaction. */ - submitEvidence = async ( - account = this._Web3Wrapper.getAccount(0), - url - ) => { + submitEvidence = async (account = this._Web3Wrapper.getAccount(0), url) => { await this.loadContract() - const txHashObj = await this.contractInstance.submitEvidence( - url, - { - from: account, - gas: ethConstants.TRANSACTION.GAS, - value: 0 - } - ) + const txHashObj = await this.contractInstance.submitEvidence(url, { + from: account, + gas: ethConstants.TRANSACTION.GAS, + value: 0 + }) return txHashObj.tx } @@ -307,56 +233,6 @@ class ArbitrableTransaction extends ContractImplementation { } } - /** - * Get ruling options from dispute via event - * @param {string} arbitratorAddress address of arbitrator contract - * @param {number} disputeId index of dispute - * @returns {object[]} an array of objects that specify the name and value of the resolution option - */ - getRulingOptions = async (arbitratorAddress, disputeId) => { - await this.loadContract() - - // fetch dispute resolution options - const statusNumber = (await this.contractInstance.status()).toNumber() - - // should this just be !== ? - if (statusNumber < contractConstants.STATUS.DISPUTE_CREATED) return [] - - // FIXME we should have a block number to start from so we don't have to rip through the entire chain - const disputeEvents = await new Promise((resolve, reject) => { - this.contractInstance - .Dispute({}, { fromBlock: 0, toBlock: 'latest' }) - .get((error, eventResult) => { - if (error) reject(error) - - resolve(eventResult) - }) - }) - - const disputeOption = _.filter(disputeEvents, event => { - const optionDisputeId = event.args._disputeID.toNumber() - // filter by arbitrator address and disputeId - return ( - event.args._arbitrator === arbitratorAddress && - optionDisputeId === disputeId - ) - }) - // should only be 1 at this point - if (disputeOption.length !== 1) return [] - - const rulingOptions = disputeOption[0].args._rulingOptions.split(';') - let optionIndex = 0 - const resolutionOptions = rulingOptions.map(option => { - optionIndex += 1 - return { - name: option, - value: optionIndex - } - }) - - return resolutionOptions - } - /** * Data of the contract * @returns {object} Object Data of the contract. diff --git a/src/contracts/implementations/arbitrable/index.js b/src/contracts/implementations/arbitrable/index.js index 5296448..1b58991 100644 --- a/src/contracts/implementations/arbitrable/index.js +++ b/src/contracts/implementations/arbitrable/index.js @@ -1,3 +1,4 @@ import ArbitrableTransaction from './ArbitrableTransaction' +import ArbitrablePermissionList from './ArbitrablePermissionList' -export { ArbitrableTransaction } +export { ArbitrableTransaction, ArbitrablePermissionList } diff --git a/src/resources/Disputes.js b/src/resources/Disputes.js index f5e8368..b3374d4 100644 --- a/src/resources/Disputes.js +++ b/src/resources/Disputes.js @@ -312,10 +312,9 @@ class Disputes { await this._ArbitrableInstance.setContractInstance( arbitrableContractAddress ) - const [arbitrableContractData, evidence, metaEvidence] = await Promise.all([ - this._ArbitrableInstance.getData(account), - this._ArbitrableInstance.getEvidence(), - this._ArbitrableInstance.getMetaEvidence() + const [arbitrableContractData, evidence] = await Promise.all([ + this._ArbitrableInstance.getContractData(), + this._ArbitrableInstance.getEvidence() ]) const contractStoreData = await this._StoreProviderInstance.getContractByAddress( arbitrableContractData.partyA, @@ -412,7 +411,6 @@ class Disputes { return { // Arbitrable Contract Data arbitrableContractAddress, - arbitrableContractStatus: arbitrableContractData.status, arbitratorAddress, partyA: arbitrableContractData.partyA, partyB: arbitrableContractData.partyB, @@ -435,7 +433,7 @@ class Disputes { : undefined, email: contractStoreData ? contractStoreData.email : undefined, evidence, - metaEvidence + metaEvidence: arbitrableContractData.metaEvidence } } } diff --git a/src/utils/StoreProviderWrapper.js b/src/utils/StoreProviderWrapper.js index d48c3f1..cec7f44 100644 --- a/src/utils/StoreProviderWrapper.js +++ b/src/utils/StoreProviderWrapper.js @@ -38,14 +38,15 @@ class StoreProviderWrapper { queueReadRequest = uri => this._storeQueue.fetch(() => httpRequest('GET', uri)) + getMetaEvidenceUri = (userAddress, contractAddress) => + `${ + this._storeUri + }/${userAddress}/contracts/${contractAddress}/meta-evidence` - getMetaEvidenceUri = (userAddress, contractAddress) => ( - `${this._storeUri}/${userAddress}/contracts/${contractAddress}/meta-evidence` - ) - - getEvidenceUri = (userAddress, contractAddress, evidenceIndex) => ( - `${this._storeUri}/${userAddress}/contracts/${contractAddress}/evidence/${evidenceIndex}` - ) + getEvidenceUri = (userAddress, contractAddress, evidenceIndex) => + `${ + this._storeUri + }/${userAddress}/contracts/${contractAddress}/evidence/${evidenceIndex}` // **************************** // // * Read * // @@ -272,7 +273,9 @@ class StoreProviderWrapper { ) if (response.status !== 201) - throw new Error(errorConstants.REQUEST_FAILED('Unable to submit evidence')) + throw new Error( + errorConstants.REQUEST_FAILED('Unable to submit evidence') + ) return response.body.evidenceIndex } diff --git a/src/utils/Web3Wrapper.js b/src/utils/Web3Wrapper.js index 527f929..efc0d56 100644 --- a/src/utils/Web3Wrapper.js +++ b/src/utils/Web3Wrapper.js @@ -19,7 +19,6 @@ class Web3Wrapper { getCoinbase = () => this._web3.eth.coinbase getNonce = async address => { - console.log(this._web3.eth.blockNumber) const nonce = await this._web3.eth.getTransactionCount(address) return nonce } diff --git a/src/utils/getContractAddress.js b/src/utils/getContractAddress.js index 4a8c279..9168872 100644 --- a/src/utils/getContractAddress.js +++ b/src/utils/getContractAddress.js @@ -1,7 +1,6 @@ import ethUtil from 'ethereumjs-util' -const getContractAddress = (account, nonce) => { - return ethUtil.bufferToHex(ethUtil.generateAddress(account, nonce)) -} +const getContractAddress = (account, nonce) => + ethUtil.bufferToHex(ethUtil.generateAddress(account, nonce)) export default getContractAddress diff --git a/src/utils/httpRequest.js b/src/utils/httpRequest.js index 54a3554..c041b54 100644 --- a/src/utils/httpRequest.js +++ b/src/utils/httpRequest.js @@ -1,3 +1,5 @@ +import * as errorConstants from '../constants/error' + /** * Helper method for sending an http requests. * @param {string} verb - HTTP verb to be used in request. E.g. GET, POST, PUT.