diff --git a/src/hooks/useBuy/README.md b/src/hooks/useBuy/README.md new file mode 100644 index 0000000..8680615 --- /dev/null +++ b/src/hooks/useBuy/README.md @@ -0,0 +1,38 @@ +# `useBuy` + +Buy datatoken. + +## Usage + +```tsx +import React from 'react' +import { useOcean, useConsume } from '@oceanprotocol/react' + +const did = 'did:op:0x000000000' + +export default function MyComponent() { + const { accountId } = useOcean() + + // Get metadata for this asset + const { title, price, ddo } = useMetadata(did) + + // Consume helpers + const { consume, consumeStep } = useConsume() + + async function handleDownload() { + await consume(did, ddo.dataToken, 'access') + } + + return ( +
+

{title}

+

Price: {price}

+ +

Your account: {accountId}

+ +
+ ) +} +``` diff --git a/src/hooks/useBuy/index.ts b/src/hooks/useBuy/index.ts new file mode 100644 index 0000000..0518fcf --- /dev/null +++ b/src/hooks/useBuy/index.ts @@ -0,0 +1 @@ +export * from './useBuy' diff --git a/src/hooks/useBuy/useBuy.ts b/src/hooks/useBuy/useBuy.ts new file mode 100644 index 0000000..1c3592a --- /dev/null +++ b/src/hooks/useBuy/useBuy.ts @@ -0,0 +1,110 @@ +import { useState } from 'react' +import { useOcean } from 'providers' +import { feedback } from 'utils' +import { DID, Logger, ServiceType } from '@oceanprotocol/lib' +import { getBestDataTokenPrice } from 'utils/dtUtils' +import { TransactionReceipt } from 'web3-core' +import { Decimal } from 'decimal.js' + +interface UseBuy { + buyDT: ( + dataTokenAddress: string, + dtAmount: number | string, + ) => Promise + consumeStep?: number + consumeStepText?: string + consumeError?: string + isLoading: boolean +} + +export const buyFeedback: { [key in number]: string } = { + 0: '1/2 Approving OCEAN Tokens', + 1: '2/3 Buying Datatoken', + 2: '3/3 Bought Datatoken' +} + +function useBuy(): UseBuy { + const { ocean, account, accountId, config } = useOcean() + const [isLoading, setIsLoading] = useState(false) + const [consumeStep, setConsumeStep] = useState() + const [consumeStepText, setConsumeStepText] = useState() + const [consumeError, setConsumeError] = useState() + + function setStep(index: number) { + setConsumeStep(index) + setConsumeStepText(buyFeedback[index]) + } + + async function buyDT( + dataTokenAddress: string, + dtAmount: number | string, + ): Promise { + if (!ocean || !account || !accountId) return + + setIsLoading(true) + setConsumeError(undefined) + setStep(0) + + try { + const bestPrice = await getBestDataTokenPrice(ocean, dataTokenAddress) + switch (bestPrice?.type) { + case 'pool': { + const price = new Decimal(bestPrice.value).times(1.05).toString() + const maxPrice = new Decimal(bestPrice.value).times(2).toString() + Logger.log('Buying token from pool', bestPrice, account.getId(), price) + const buyResponse = await ocean.pool.buyDT( + account.getId(), + bestPrice.address, + String(dtAmount), + price, + maxPrice + ) + setStep(2) + Logger.log('DT buy response', buyResponse) + return buyResponse + } + case 'exchange': { + if (!config.oceanTokenAddress) { + Logger.error(`'oceanTokenAddress' not set in config`) + return + } + if (!config.fixedRateExchangeAddress) { + Logger.error(`'fixedRateExchangeAddress' not set in config`) + return + } + Logger.log('Buying token from exchange', bestPrice, account.getId()) + await ocean.datatokens.approve( + config.oceanTokenAddress, + config.fixedRateExchangeAddress, + bestPrice.value.toString(), + account.getId() + ) + setStep(1) + const exchange = await ocean.fixedRateExchange.buyDT( + bestPrice.address, + String(dtAmount), + account.getId() + ) + setStep(2) + Logger.log('DT exchange buy response', exchange) + return exchange + } + } + } catch (error) { + setConsumeError(error.message) + Logger.error(error) + } finally { + setConsumeStep(undefined) + setConsumeStepText(undefined) + setIsLoading(false) + } + } + + return { buyDT, consumeStep, consumeStepText, consumeError, isLoading } +} + +export { useBuy, UseBuy } +export default useBuy + + + diff --git a/src/hooks/useCompute/useCompute.ts b/src/hooks/useCompute/useCompute.ts index 2cb10d3..36f4fa1 100644 --- a/src/hooks/useCompute/useCompute.ts +++ b/src/hooks/useCompute/useCompute.ts @@ -4,7 +4,6 @@ import { ComputeValue } from './ComputeOptions' import { Logger, ServiceCompute } from '@oceanprotocol/lib' import { MetadataAlgorithm } from '@oceanprotocol/lib/dist/node/ddo/interfaces/MetadataAlgorithm' import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/ComputeJob' -import { checkAndBuyDT } from 'utils/dtUtils' interface UseCompute { compute: ( @@ -13,7 +12,7 @@ interface UseCompute { dataTokenAddress: string, algorithmRawCode: string, computeContainer: ComputeValue, - marketFeeAddress: string + marketFeeAddress?: string ) => Promise computeStep?: number computeStepText?: string @@ -62,7 +61,7 @@ function useCompute(): UseCompute { dataTokenAddress: string, algorithmRawCode: string, computeContainer: ComputeValue, - marketFeeAddress: string + marketFeeAddress?: string ): Promise { if (!ocean || !account) return @@ -72,50 +71,47 @@ function useCompute(): UseCompute { setIsLoading(true) setStep(0) - await checkAndBuyDT(ocean, dataTokenAddress, account, config) - rawAlgorithmMeta.container = computeContainer - rawAlgorithmMeta.rawcode = algorithmRawCode - - const output = {} - Logger.log( - 'compute order', - accountId, - did, - computeService, - rawAlgorithmMeta, - marketFeeAddress - ) - const tokenTransfer = await ocean.compute.order( - accountId, - did, - computeService.index, - undefined, - rawAlgorithmMeta, - marketFeeAddress - ) - - setStep(1) - // const computeOrder = JSON.parse(order) - // Logger.log('compute order', computeOrder) - // const tokenTransfer = await ocean.datatokens.transferWei( - // computeOrder.dataToken, - // computeOrder.to, - // String(computeOrder.numTokens), - // computeOrder.from - // ) - setStep(2) - const response = await ocean.compute.start( - did, - tokenTransfer, + const userOwnedTokens = await ocean.accounts.getTokenBalance( dataTokenAddress, - account, - undefined, - rawAlgorithmMeta, - output, - `${computeService.index}`, - computeService.type + account ) - return response + if (parseFloat(userOwnedTokens) < 1) { + setComputeError('Not enough datatokens') + } else { + rawAlgorithmMeta.container = computeContainer + rawAlgorithmMeta.rawcode = algorithmRawCode + const output = {} + Logger.log( + 'compute order', + accountId, + did, + computeService, + rawAlgorithmMeta, + marketFeeAddress + ) + const tokenTransfer = await ocean.compute.order( + accountId, + did, + computeService.index, + undefined, + rawAlgorithmMeta, + marketFeeAddress + ) + setStep(1) + setStep(2) + const response = await ocean.compute.start( + did, + tokenTransfer, + dataTokenAddress, + account, + undefined, + rawAlgorithmMeta, + output, + `${computeService.index}`, + computeService.type + ) + return response + } } catch (error) { Logger.error(error) setComputeError(error.message) diff --git a/src/hooks/useConsume/useConsume.ts b/src/hooks/useConsume/useConsume.ts index 1450924..6ec61d4 100644 --- a/src/hooks/useConsume/useConsume.ts +++ b/src/hooks/useConsume/useConsume.ts @@ -2,7 +2,6 @@ import { useState } from 'react' import { useOcean } from 'providers' import { feedback } from 'utils' import { DID, Logger, ServiceType } from '@oceanprotocol/lib' -import { checkAndBuyDT } from 'utils/dtUtils' interface UseConsume { consume: ( @@ -49,38 +48,33 @@ function useConsume(): UseConsume { try { setStep(0) - await checkAndBuyDT(ocean, dataTokenAddress, account, config) - - setStep(1) - const tokenTransfer = await ocean.assets.order( - did as string, - serviceType, - accountId, - undefined, - marketFeeAddress - ) - Logger.log('order created', tokenTransfer) - setStep(2) - // const res = JSON.parse(order) - // Logger.log('order parsed', res) - // Logger.log('ocean.datatokens before transfer', ocean.datatokens) - // const tokenTransfer = await ocean.datatokens.transferWei( - // res.dataToken, - // res.to, - // String(res.numTokens), - // res.from - // ) - // Logger.log('token transfered', tokenTransfer) - setStep(3) - await ocean.assets.download( - did as string, - tokenTransfer, + const userOwnedTokens = await ocean.accounts.getTokenBalance( dataTokenAddress, - account, - '' + account ) - - setStep(4) + if (parseFloat(userOwnedTokens) < 1) { + setConsumeError('Not enough datatokens') + } else { + setStep(1) + const tokenTransfer = await ocean.assets.order( + did as string, + serviceType, + accountId, + undefined, + marketFeeAddress + ) + Logger.log('order created', tokenTransfer) + setStep(2) + setStep(3) + await ocean.assets.download( + did as string, + tokenTransfer, + dataTokenAddress, + account, + '' + ) + setStep(4) + } } catch (error) { setConsumeError(error.message) Logger.error(error) diff --git a/src/hooks/usePublish/PriceOptions.ts b/src/hooks/usePostForSale/usePublish/PriceOptions.ts similarity index 85% rename from src/hooks/usePublish/PriceOptions.ts rename to src/hooks/usePostForSale/usePublish/PriceOptions.ts index c0b6eb8..a30b665 100644 --- a/src/hooks/usePublish/PriceOptions.ts +++ b/src/hooks/usePostForSale/usePublish/PriceOptions.ts @@ -1,6 +1,6 @@ export interface PriceOptions { price: number - tokensToMint: number + dtAmount: number type: 'fixed' | 'dynamic' | string weightOnDataToken: string swapFee: string diff --git a/src/hooks/usePostForSale/usePublish/README.md b/src/hooks/usePostForSale/usePublish/README.md new file mode 100644 index 0000000..cb27151 --- /dev/null +++ b/src/hooks/usePostForSale/usePublish/README.md @@ -0,0 +1,39 @@ +# `usePublish` + +Post asset for sale by creating liquidity pools or fixed rate exchange + +## Usage + +```tsx +import React from 'react' +import { useOcean, usePostforSale } from '@oceanprotocol/react' +import { Metadata } from '@oceanprotocol/lib' + +export default function MyComponent() { + const { accountId } = useOcean() + + // Publish helpers + const { publish, publishStep } = usePostforSale() + + const priceOptions = { + price: 10, + dtAmount: 10, + type: 'fixed', + weightOnDataToken: '', + swapFee: '' + } + + async function handlePostforSale() { + const ddo = await createPricing(dataTokenAddress, priceOptions) + } + + return ( +
+

Post for sale

+ +

Your account: {accountId}

+ +
+ ) +} +``` diff --git a/src/hooks/usePostForSale/usePublish/index.ts b/src/hooks/usePostForSale/usePublish/index.ts new file mode 100644 index 0000000..d3e69d5 --- /dev/null +++ b/src/hooks/usePostForSale/usePublish/index.ts @@ -0,0 +1,2 @@ +export * from './usePostforSale' +export * from './PriceOptions' diff --git a/src/hooks/usePostForSale/usePublish/usePostforSale.ts b/src/hooks/usePostForSale/usePublish/usePostforSale.ts new file mode 100644 index 0000000..3c85737 --- /dev/null +++ b/src/hooks/usePostForSale/usePublish/usePostforSale.ts @@ -0,0 +1,84 @@ +import { DID, DDO, Logger, Metadata } from '@oceanprotocol/lib' +import { + Service, + ServiceComputePrivacy, + ServiceType +} from '@oceanprotocol/lib/dist/node/ddo/interfaces/Service' +import { useState } from 'react' +import { useOcean } from 'providers' +import ProviderStatus from 'providers/OceanProvider/ProviderStatus' +import { publishFeedback } from 'utils' +import { PriceOptions } from './PriceOptions' + +interface UsePostforSale { + createPricing: ( + dataTokenAddress: string, + priceOptions: PriceOptions, + ) => Promise + publishStep?: number + publishStepText?: string + publishError?: string + isLoading: boolean +} + +function usePostforSale(): UsePostforSale { + const { ocean, status, account, accountId, config } = useOcean() + const [isLoading, setIsLoading] = useState(false) + const [publishStep, setPublishStep] = useState() + const [publishStepText, setPublishStepText] = useState() + const [publishError, setPublishError] = useState() + + function setStep(index?: number) { + setPublishStep(index) + index && setPublishStepText(publishFeedback[index]) + } + + async function createPricing( + dataTokenAddress: string, + priceOptions: PriceOptions + ): Promise { + switch (priceOptions.type) { + case 'dynamic': { + await ocean.pool.createDTPool( + accountId, + dataTokenAddress, + priceOptions.dtAmount.toString(), + priceOptions.weightOnDataToken, + priceOptions.swapFee + ) + break + } + case 'fixed': { + if (!config.fixedRateExchangeAddress) { + Logger.error(`'fixedRateExchangeAddress' not set in ccnfig.`) + return null + } + + await ocean.fixedRateExchange.create( + dataTokenAddress, + priceOptions.price.toString(), + accountId + ) + await ocean.datatokens.approve( + dataTokenAddress, + config.fixedRateExchangeAddress, + priceOptions.dtAmount, + accountId + ) + break + } + } + } + + + return { + createPricing, + publishStep, + publishStepText, + isLoading, + publishError + } +} + +export { usePostforSale, UsePostforSale } +export default usePostforSale diff --git a/src/hooks/usePublish/DataTokenOptions.ts b/src/hooks/usePublish/DataTokenOptions.ts index 9bb28fb..2579a9c 100644 --- a/src/hooks/usePublish/DataTokenOptions.ts +++ b/src/hooks/usePublish/DataTokenOptions.ts @@ -1,4 +1,5 @@ export interface DataTokenOptions { + tokensToMint: number cap?: string name?: string symbol?: string diff --git a/src/hooks/usePublish/README.md b/src/hooks/usePublish/README.md index 654c2ca..c1e789f 100644 --- a/src/hooks/usePublish/README.md +++ b/src/hooks/usePublish/README.md @@ -1,6 +1,6 @@ # `usePublish` -Publish data sets, and create data tokens and liquidity pools for them. +Create data tokens, Mint and Publish data sets ## Usage @@ -24,16 +24,12 @@ export default function MyComponent() { } } - const priceOptions = { - price: 10, - tokensToMint: 10, - type: 'fixed', - weightOnDataToken: '', - swapFee: '' + const dataTokenOptions = { + tokensToMint: 10 } async function handlePublish() { - const ddo = await publish(metadata, priceOptions, 'access') + const ddo = await publish(metadata, 'access', dataTokenOptions) } return ( diff --git a/src/hooks/usePublish/index.ts b/src/hooks/usePublish/index.ts index 582f913..968ef5e 100644 --- a/src/hooks/usePublish/index.ts +++ b/src/hooks/usePublish/index.ts @@ -1,3 +1,2 @@ export * from './usePublish' -export * from './PriceOptions' export * from './DataTokenOptions' diff --git a/src/hooks/usePublish/usePublish.ts b/src/hooks/usePublish/usePublish.ts index e89b090..21ce9c6 100644 --- a/src/hooks/usePublish/usePublish.ts +++ b/src/hooks/usePublish/usePublish.ts @@ -5,18 +5,16 @@ import { ServiceType } from '@oceanprotocol/lib/dist/node/ddo/interfaces/Service' import { useState } from 'react' -import { DataTokenOptions } from '.' +import { DataTokenOptions } from './DataTokenOptions' import { useOcean } from 'providers' import ProviderStatus from 'providers/OceanProvider/ProviderStatus' import { publishFeedback } from 'utils' -import { PriceOptions } from './PriceOptions' interface UsePublish { publish: ( asset: Metadata, - priceOptions: PriceOptions, serviceConfigs: ServiceType, - dataTokenOptions?: DataTokenOptions, + dataTokenOptions: DataTokenOptions, providerUri?: string ) => Promise mint: (tokenAddress: string, tokensToMint: string) => void @@ -43,44 +41,6 @@ function usePublish(): UsePublish { await ocean.datatokens.mint(tokenAddress, accountId, tokensToMint) } - async function createPricing( - priceOptions: PriceOptions, - dataTokenAddress: string, - mintedTokens: string - ): Promise { - switch (priceOptions.type) { - case 'dynamic': { - await ocean.pool.createDTPool( - accountId, - dataTokenAddress, - priceOptions.tokensToMint.toString(), - priceOptions.weightOnDataToken, - priceOptions.swapFee - ) - break - } - case 'fixed': { - if (!config.fixedRateExchangeAddress) { - Logger.error(`'fixedRateExchangeAddress' not set in ccnfig.`) - return null - } - - await ocean.fixedRateExchange.create( - dataTokenAddress, - priceOptions.price.toString(), - accountId - ) - await ocean.datatokens.approve( - dataTokenAddress, - config.fixedRateExchangeAddress, - mintedTokens, - accountId - ) - break - } - } - } - /** * Publish an asset.It also creates the datatoken, mints tokens and gives the market allowance * @param {Metadata} asset The metadata of the asset. @@ -91,9 +51,8 @@ function usePublish(): UsePublish { */ async function publish( asset: Metadata, - priceOptions: PriceOptions, serviceType: ServiceType, - dataTokenOptions?: DataTokenOptions, + dataTokenOptions: DataTokenOptions, providerUri?: string ): Promise { if (status !== ProviderStatus.CONNECTED || !ocean || !account) return null @@ -102,7 +61,7 @@ function usePublish(): UsePublish { setPublishError(undefined) try { - const tokensToMint = priceOptions.tokensToMint.toString() + const tokensToMint = dataTokenOptions.tokensToMint.toString() const publishedDate = new Date(Date.now()).toISOString().split('.')[0] + 'Z' @@ -189,8 +148,8 @@ function usePublish(): UsePublish { await mint(ddo.dataToken, tokensToMint) Logger.log(`minted ${tokensToMint} tokens`) - await createPricing(priceOptions, ddo.dataToken, tokensToMint) - setStep(8) + // await createPricing(priceOptions, ddo.dataToken, tokensToMint) + // setStep(8) return ddo } catch (error) { setPublishError(error.message) diff --git a/src/utils/dtUtils.ts b/src/utils/dtUtils.ts index c875f9b..cbb8621 100644 --- a/src/utils/dtUtils.ts +++ b/src/utils/dtUtils.ts @@ -44,6 +44,33 @@ export async function getCheapestPool( } } +export async function getFirstPool( + ocean: Ocean, + dataTokenAddress: string +): Promise { + if (!ocean || !dataTokenAddress) return null + + const tokenPools = await ocean.pool.searchPoolforDT(dataTokenAddress) + + if (tokenPools === undefined || tokenPools.length === 0) { + return { + address: '', + price: 0 + } + } + const firstPoolAddress = tokenPools[0] + const firstPoolPrice = await ocean.pool.getOceanNeeded(firstPoolAddress, '1') + const oceanReserve = await ocean.pool.getOceanReserve(firstPoolAddress) + const dtReserve = await ocean.pool.getDTReserve(firstPoolAddress) + + return { + address: firstPoolAddress, + price: Number(firstPoolPrice), + ocean: Number(oceanReserve), + datatoken: Number(dtReserve) + } +} + export async function getCheapestExchange( ocean: Ocean, dataTokenAddress: string @@ -90,7 +117,7 @@ export async function getBestDataTokenPrice( ocean: Ocean, dataTokenAddress: string ): Promise { - const cheapestPool = await getCheapestPool(ocean, dataTokenAddress) + const cheapestPool = await getFirstPool(ocean, dataTokenAddress) const cheapestExchange = await getCheapestExchange(ocean, dataTokenAddress) Decimal.set({ precision: 5 }) @@ -119,62 +146,3 @@ export async function getBestDataTokenPrice( } as BestPrice } } - -export async function checkAndBuyDT( - ocean: Ocean, - dataTokenAddress: string, - account: Account, - config: Config -): Promise { - const userOwnedTokens = await ocean.accounts.getTokenBalance( - dataTokenAddress, - account - ) - Logger.log(`User has ${userOwnedTokens} tokens`) - if (userOwnedTokens === '0') { - const bestPrice = await getBestDataTokenPrice(ocean, dataTokenAddress) - - switch (bestPrice?.type) { - case 'pool': { - const price = new Decimal(bestPrice.value).times(1.05).toString() - const maxPrice = new Decimal(bestPrice.value).times(2).toString() - Logger.log('Buying token from pool', bestPrice, account.getId(), price) - const buyResponse = await ocean.pool.buyDT( - account.getId(), - bestPrice.address, - '1', - price, - maxPrice - ) - Logger.log('DT buy response', buyResponse) - return buyResponse - } - case 'exchange': { - if (!config.oceanTokenAddress) { - Logger.error(`'oceanTokenAddress' not set in config`) - return - } - - if (!config.fixedRateExchangeAddress) { - Logger.error(`'fixedRateExchangeAddress' not set in config`) - return - } - - Logger.log('Buying token from exchange', bestPrice, account.getId()) - await ocean.datatokens.approve( - config.oceanTokenAddress, - config.fixedRateExchangeAddress, - bestPrice.value.toString(), - account.getId() - ) - const exchange = await ocean.fixedRateExchange.buyDT( - bestPrice.address, - '1', - account.getId() - ) - Logger.log('DT exchange buy response', exchange) - return exchange - } - } - } -} diff --git a/src/utils/index.ts b/src/utils/index.ts index ccc4435..d3114b6 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -31,12 +31,11 @@ export const feedback: { [key in number]: string } = { } export const publishFeedback: { [key in number]: string } = { - 0: '1/6 Creating datatoken ...', - 2: '2/6 Encrypting files ...', - 4: '3/6 Generating proof ...', - 6: '4/6 Storing ddo ...', - 7: '5/6 Minting tokens ...', - 8: '6/6 Asset published succesfully' + 0: '1/5 Creating datatoken ...', + 2: '2/5 Encrypting files ...', + 4: '3/5 Storing ddo ...', + 6: '4/5 Minting tokens ...', + 8: '5/5 Asset published succesfully' } export * from './web3'