From 60fc9530ae94a5b16b4b885f24fa0f1d2f63b447 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 11 Sep 2020 02:41:20 -0700 Subject: [PATCH] add startOrder --- src/datatokens/Datatokens.ts | 69 +++++++++++++- src/ocean/Assets.ts | 113 +++++++++++++++++++++-- src/ocean/Compute.ts | 11 ++- test/integration/ComputeFlow.test.ts | 30 ++---- test/integration/Marketplaceflow.test.ts | 30 ++---- 5 files changed, 197 insertions(+), 56 deletions(-) diff --git a/src/datatokens/Datatokens.ts b/src/datatokens/Datatokens.ts index 224caf0d..0f4cd537 100644 --- a/src/datatokens/Datatokens.ts +++ b/src/datatokens/Datatokens.ts @@ -5,7 +5,7 @@ import defaultFactoryABI from '@oceanprotocol/contracts/artifacts/DTFactory.json import defaultDatatokensABI from '@oceanprotocol/contracts/artifacts/DataTokenTemplate.json' import wordListDefault from '../data/words.json' - +import { TransactionReceipt } from 'web3-core' /** * Provides a interface to DataTokens */ @@ -350,4 +350,71 @@ export class DataTokens { public fromWei(amount: string): string { return this.web3.utils.fromWei(amount) } + + /** Calculate total fee + * @param {String} dataTokenAddress + * @param {String} amount + * @param {String} mpFeePercentage + * @param {String} address + * @return {Promise} string + */ + public async calculateTotalFee( + dataTokenAddress: string, + amount: string, + mpFeePercentage: string, + address: string + ): Promise { + const datatoken = new this.web3.eth.Contract(this.datatokensABI, dataTokenAddress, { + from: address + }) + const totalFee = await datatoken.methods + .calculateTotalFee( + this.web3.utils.toWei(amount), + this.web3.utils.toWei(mpFeePercentage) + ) + .call() + return this.web3.utils.fromWei(totalFee) + } + + /** Start Order + * @param {String} dataTokenAddress + * @param {String} providerAddress + * @param {String} amount + * @param {String} did + * @param {Number} serviceId + * @param {String} mpFeeAddress + * @param {String} mpFeePercentage + * @param {String} address + * @return {Promise} string + */ + public async startOrder( + dataTokenAddress: string, + providerAddress: string, + amount: string, + did: string, + serviceId: number, + mpFeeAddress: string, + mpFeePercentage: string, + address: string + ): Promise { + const datatoken = new this.web3.eth.Contract(this.datatokensABI, dataTokenAddress, { + from: address + }) + try { + const trxReceipt = await datatoken.methods + .startOrder( + providerAddress, + this.web3.utils.toWei(amount), + String(did), + String(serviceId), + mpFeeAddress, + this.web3.utils.toWei(mpFeePercentage) + ) + .send({ from: address }) + return trxReceipt + } catch (e) { + console.error(e) + return null + } + } } diff --git a/src/ocean/Assets.ts b/src/ocean/Assets.ts index 8181c7b0..3a9cedc7 100644 --- a/src/ocean/Assets.ts +++ b/src/ocean/Assets.ts @@ -11,9 +11,11 @@ import { import { EditableMetadata } from '../ddo/interfaces/EditableMetadata' import Account from './Account' import DID from './DID' -import { SubscribablePromise } from '../utils' +import { SubscribablePromise, didZeroX } from '../utils' import { Instantiable, InstantiableConfig } from '../Instantiable.abstract' import { WebServiceConnector } from './utils/WebServiceConnector' +import { DataTokens } from '../lib' +import BigNumber from 'bignumber.js' export enum CreateProgressStep { CreatingDataToken, @@ -396,11 +398,65 @@ export class Assets extends Instantiable { } } + /** + * Initialize a service + * Can be used to compute totalCost for ordering a service + * @param {String} did + * @param {String} serviceType + * @param {String} consumerAddress + * @param {Number} serviceIndex + * @param {String} mpFeePercent will be converted to Wei + * @param {String} mpAddress mp fee collector address + * @return {Promise} Order details + */ + public async initialize( + did: string, + serviceType: string, + consumerAddress: string, + serviceIndex = -1, + mpFeePercent?: string + ): Promise { + const res = await this.ocean.provider.initialize( + did, + serviceIndex, + serviceType, + consumerAddress + ) + if (res === null) return null + const providerData = JSON.parse(res) + const { datatokens } = this.ocean + const dtCost = new BigNumber(this.web3.utils.fromWei(String(providerData.numTokens))) + const totalFee = new BigNumber( + await datatokens.calculateTotalFee( + providerData.dataToken, + this.web3.utils.fromWei(String(providerData.numTokens)), + mpFeePercent, + consumerAddress + ) + ) + providerData.totalFee = totalFee.toString() + providerData.dtCost = dtCost.toString() + providerData.totalCost = dtCost.plus(totalFee).toString() + return providerData + } + + /** + * Orders & pays for a service + * @param {String} did + * @param {String} serviceType + * @param {String} consumerAddress + * @param {Number} serviceIndex + * @param {String} mpFeePercent will be converted to Wei + * @param {String} mpAddress mp fee collector address + * @return {Promise} transactionHash of the payment + */ public async order( did: string, serviceType: string, consumerAddress: string, - serviceIndex = -1 + serviceIndex = -1, + mpFeePercent?: string, + mpAddress?: string ): Promise { if (serviceIndex === -1) { const service = await this.getServiceByType(did, serviceType) @@ -409,12 +465,53 @@ export class Assets extends Instantiable { const service = await this.getServiceByIndex(did, serviceIndex) serviceType = service.type } - return await this.ocean.provider.initialize( - did, - serviceIndex, - serviceType, - consumerAddress - ) + if (!mpFeePercent) mpFeePercent = '0' + if (!mpAddress) mpAddress = '0x000000000000000000000000000000000000dEaD' + const { datatokens } = this.ocean + try { + const providerData = await this.initialize( + did, + serviceType, + consumerAddress, + serviceIndex, + mpFeePercent + ) + if (!providerData) return null + const balance = new BigNumber( + await datatokens.balance(providerData.dataToken, consumerAddress) + ) + const totalCost = new BigNumber(providerData.totalCost) + if (balance.isLessThanOrEqualTo(totalCost)) { + console.error('Not enough funds') + + return null + } + console.log( + 'Balance:' + + balance.toString() + + '| dtCost:' + + providerData.dtCost.toString() + + '|Fees:' + + providerData.totalFee.toString() + + '|Total:' + + providerData.totalCost.toString() + ) + const txid = await datatokens.startOrder( + providerData.dataToken, + providerData.to, + String(providerData.numTokens), + didZeroX(did), + serviceIndex, + mpAddress, + mpFeePercent, + consumerAddress + ) + if (txid) return txid.transactionHash + else return null + } catch (e) { + console.error(e) + return null + } } // marketplace flow diff --git a/src/ocean/Compute.ts b/src/ocean/Compute.ts index 658e6aec..f580e3de 100644 --- a/src/ocean/Compute.ts +++ b/src/ocean/Compute.ts @@ -87,7 +87,7 @@ export class Compute extends Instantiable { algorithmDataToken?: string ): Promise { output = this.checkOutput(consumerAccount, output) - if (did) { + if (did && txId) { const computeJobsList = await this.ocean.provider.compute( 'post', did, @@ -349,7 +349,9 @@ export class Compute extends Instantiable { datasetDid: string, serviceIndex: number, algorithmDid?: string, - algorithmMeta?: MetadataAlgorithm + algorithmMeta?: MetadataAlgorithm, + mpFeePercent?: string, + mpAddress?: string ): SubscribablePromise { return new SubscribablePromise(async (observer) => { const ddo: DDO = await this.ocean.assets.resolve(datasetDid) @@ -380,7 +382,10 @@ export class Compute extends Instantiable { const order = await this.ocean.assets.order( datasetDid, service.type, - consumerAccount + consumerAccount, + -1, + mpFeePercent, + mpAddress ) return order }) diff --git a/test/integration/ComputeFlow.test.ts b/test/integration/ComputeFlow.test.ts index 1e390f10..533a500c 100644 --- a/test/integration/ComputeFlow.test.ts +++ b/test/integration/ComputeFlow.test.ts @@ -270,16 +270,9 @@ describe('Compute flow', () => { algorithmMeta ) assert(order != null) - const computeOrder = JSON.parse(order) - const tx = await datatoken.transferWei( - computeOrder.dataToken, - computeOrder.to, - String(computeOrder.numTokens), - computeOrder.from - ) const response = await ocean.compute.start( ddo.id, - tx.transactionHash, + order, tokenAddress, bob, undefined, @@ -293,6 +286,7 @@ describe('Compute flow', () => { }) it('Bob should get status of a compute job', async () => { + assert(jobId != null) const response = await ocean.compute.status(bob, ddo.id, jobId) assert(response[0].jobId === jobId) }) @@ -302,6 +296,7 @@ describe('Compute flow', () => { assert(response.length > 0) }) it('Bob should stop compute job', async () => { + assert(jobId != null) await ocean.compute.stop(bob, ddo.id, jobId) const response = await ocean.compute.status(bob, ddo.id, jobId) assert(response[0].stopreq === 1) @@ -338,13 +333,7 @@ describe('Compute flow', () => { serviceAlgo.type, bob.getId() ) - const algoOrder = JSON.parse(orderalgo) - const algoTx = await datatoken.transferWei( - algoOrder.dataToken, - algoOrder.to, - String(algoOrder.numTokens), - algoOrder.from - ) + assert(orderalgo != null) const order = await ocean.compute.order( bob.getId(), ddo.id, @@ -353,16 +342,9 @@ describe('Compute flow', () => { undefined ) assert(order != null) - const computeOrder = JSON.parse(order) - const tx = await datatoken.transferWei( - computeOrder.dataToken, - computeOrder.to, - String(computeOrder.numTokens), - computeOrder.from - ) const response = await ocean.compute.start( ddo.id, - tx.transactionHash, + order, tokenAddress, bob, algorithmAsset.id, @@ -370,7 +352,7 @@ describe('Compute flow', () => { output, computeService.index, computeService.type, - algoTx.transactionHash, + orderalgo, algorithmAsset.dataToken ) jobId = response.jobId diff --git a/test/integration/Marketplaceflow.test.ts b/test/integration/Marketplaceflow.test.ts index 0c50351f..c0ad435e 100644 --- a/test/integration/Marketplaceflow.test.ts +++ b/test/integration/Marketplaceflow.test.ts @@ -159,26 +159,16 @@ describe('Marketplace flow', () => { }) it('Bob consumes asset 1', async () => { - await ocean.assets - .order(ddo.id, accessService.type, bob.getId()) - .then(async (res: any) => { - res = JSON.parse(res) - return await datatoken.transferWei( - res.dataToken, - res.to, - String(res.numTokens), - res.from - ) - }) - .then(async (tx) => { - await ocean.assets.download( - ddo.id, - tx.transactionHash, - tokenAddress, - bob, - './node_modules/my-datasets' - ) - }) + await ocean.assets.order(ddo.id, accessService.type, bob.getId()).then(async (tx) => { + assert(tx != null) + await ocean.assets.download( + ddo.id, + tx, + tokenAddress, + bob, + './node_modules/my-datasets' + ) + }) }) it('owner can list there assets', async () => { const assets = await ocean.assets.ownerAssets(alice.getId())