From 15af847a9cc3b82549c8c56f2bfd2cb057864a8a Mon Sep 17 00:00:00 2001 From: Bogdan Fazakas Date: Thu, 12 May 2022 14:39:14 +0300 Subject: [PATCH] wip compute initialize --- src/@utils/accessDetailsAndPricing.ts | 32 +- src/@utils/order.ts | 128 ++++-- src/@utils/provider.ts | 39 ++ .../Asset/AssetActions/Compute/index.tsx | 372 ++++++++++-------- 4 files changed, 345 insertions(+), 226 deletions(-) diff --git a/src/@utils/accessDetailsAndPricing.ts b/src/@utils/accessDetailsAndPricing.ts index 837995f46..e4c681775 100644 --- a/src/@utils/accessDetailsAndPricing.ts +++ b/src/@utils/accessDetailsAndPricing.ts @@ -8,7 +8,12 @@ import { TokensPriceQuery, TokensPriceQuery_tokens as TokensPrice } from '../@types/subgraph/TokensPriceQuery' -import { Asset, LoggerInstance, ProviderInstance } from '@oceanprotocol/lib' +import { + Asset, + LoggerInstance, + ProviderFees, + ProviderInstance +} from '@oceanprotocol/lib' import { AssetExtended } from 'src/@types/AssetExtended' import { calcInGivenOut } from './pool' import { getFixedBuyPrice } from './fixedRateExchange' @@ -238,8 +243,7 @@ export async function getOrderPriceAndFees( asset: AssetExtended, accountId?: string, paramsForPool?: CalcInGivenOutParams, - computeEnv: string = null, - computeValidUntil: number = null + providerFees?: ProviderFees ): Promise { const orderPriceAndFee = { price: '0', @@ -258,18 +262,16 @@ export async function getOrderPriceAndFees( // fetch provider fee - const initializeData = await ProviderInstance.initialize( - asset?.id, - asset.services[0].id, - 0, - accountId, - asset.services[0].serviceEndpoint, - null, - null, - computeEnv, - computeValidUntil - ) - orderPriceAndFee.providerFee = initializeData.providerFee + const initializeData = + !providerFees && + (await ProviderInstance.initialize( + asset?.id, + asset.services[0].id, + 0, + accountId, + asset.services[0].serviceEndpoint + )) + orderPriceAndFee.providerFee = providerFees || initializeData.providerFee // fetch price and swap fees switch (asset?.accessDetails?.type) { diff --git a/src/@utils/order.ts b/src/@utils/order.ts index 235db50df..455d079a4 100644 --- a/src/@utils/order.ts +++ b/src/@utils/order.ts @@ -2,7 +2,11 @@ import { approve, Datatoken, FreOrderParams, + LoggerInstance, OrderParams, + ProviderComputeInitialize, + ProviderComputeInitializeResults, + ProviderFees, ProviderInstance } from '@oceanprotocol/lib' import { AssetExtended } from 'src/@types/AssetExtended' @@ -15,6 +19,8 @@ import { consumeMarketOrderFee, consumeMarketFixedSwapFee } from '../../app.config' +import { buyDtFromPool } from './pool' +import { toast } from 'react-toastify' /** * For pool you need to buy the datatoken beforehand, this always assumes you want to order the first service @@ -31,29 +37,26 @@ export async function order( asset: AssetExtended, orderPriceAndFees: OrderPriceAndFees, accountId: string, - computeEnv: string = null, - computeValidUntil: number = null, + providerFees?: ProviderFees, computeConsumerAddress?: string ): Promise { const datatoken = new Datatoken(web3) const config = getOceanConfig(asset.chainId) - const initializeData = await ProviderInstance.initialize( - asset.id, - asset.services[0].id, - 0, - accountId, - asset.services[0].serviceEndpoint, - null, - null, - computeEnv, - computeValidUntil - ) + const initializeData = + !providerFees && + (await ProviderInstance.initialize( + asset.id, + asset.services[0].id, + 0, + accountId, + asset.services[0].serviceEndpoint + )) const orderParams = { consumer: computeConsumerAddress || accountId, serviceIndex: 0, - _providerFee: initializeData.providerFee, + _providerFee: providerFees || initializeData.providerFee, _consumeMarketFee: { consumeMarketFeeAddress: marketFeeAddress, consumeMarketFeeAmount: consumeMarketOrderFee, @@ -99,7 +102,7 @@ export async function order( accountId, computeConsumerAddress || accountId, 0, - initializeData.providerFee + providerFees || initializeData.providerFee ) return tx } @@ -121,10 +124,8 @@ export async function order( * @param web3 * @param asset * @param accountId - * @param accountId validOrderTx - * @param computeEnv - * @param computeValidUntil - * @param computeConsumerAddress + * @param validOrderTx + * @param providerFees * @returns {TransactionReceipt} receipt of the order */ export async function reuseOrder( @@ -132,28 +133,27 @@ export async function reuseOrder( asset: AssetExtended, accountId: string, validOrderTx: string, - computeEnv: string = null, - computeValidUntil: number = null, - computeConsumerAddress?: string -) { + providerFees?: ProviderFees +): Promise { const datatoken = new Datatoken(web3) - const initializeData = await ProviderInstance.initialize( - asset.id, - asset.services[0].id, - 0, - accountId, - asset.services[0].serviceEndpoint, - null, - null, - computeEnv, - computeValidUntil - ) + const initializeData = + !providerFees && + (await ProviderInstance.initialize( + asset.id, + asset.services[0].id, + 0, + accountId, + asset.services[0].serviceEndpoint + )) + const txApprove = await approve( web3, accountId, - initializeData.providerFee.providerFeeToken, + providerFees.providerFeeToken || + initializeData.providerFee.providerFeeToken, asset.accessDetails.datatoken.address, - initializeData.providerFee.providerFeeAmount, + providerFees.providerFeeAmount || + initializeData.providerFee.providerFeeAmount, false ) if (!txApprove) { @@ -164,8 +164,62 @@ export async function reuseOrder( asset.accessDetails.datatoken.address, accountId, validOrderTx, - initializeData.providerFee + providerFees || initializeData.providerFee ) return tx } + +/** + * Handles order for compute assets for the following scenarios: + * - have validOrder and no providerFees -> then order is valid, providerFees are valid, it returns the valid order value + * - have validOrder and providerFees -> then order is valid but providerFees are not valid, we need to call reuseOrder and pay only providerFees + * - no validOrder -> we need to call order, to pay 1 DT & providerFees + * @param web3 + * @param asset + * @param accountId + * @param computeEnv + * @param computeValidUntil + * @param computeConsumerAddress + * @returns {Promise} tx id + */ +export async function handleComputeOrder( + web3: Web3, + asset: AssetExtended, + orderPriceAndFees: OrderPriceAndFees, + accountId: string, + hasDatatoken: boolean, + initializeData: ProviderComputeInitialize, + computeConsumerAddress?: string +): Promise { + if (initializeData.validOrder && !initializeData.providerFee) { + return initializeData.validOrder + } else if (initializeData.validOrder) { + const tx = await reuseOrder( + web3, + asset, + accountId, + initializeData.validOrder, + initializeData.providerFee + ) + return tx.transactionHash + } else { + if (!hasDatatoken && asset?.accessDetails.type === 'dynamic') { + const poolTx = await buyDtFromPool(asset?.accessDetails, accountId, web3) + LoggerInstance.log('[compute] Buy dt from pool: ', poolTx) + if (!poolTx) { + toast.error('Failed to buy datatoken from pool!') + return + } + } + const tx = await order( + web3, + asset, + orderPriceAndFees, + accountId, + initializeData.providerFee, + computeConsumerAddress + ) + return tx.transactionHash + } +} diff --git a/src/@utils/provider.ts b/src/@utils/provider.ts index 5ba257d25..c83793377 100644 --- a/src/@utils/provider.ts +++ b/src/@utils/provider.ts @@ -1,11 +1,50 @@ import { + ComputeAlgorithm, + ComputeAsset, + ComputeEnvironment, downloadFileBrowser, FileMetadata, LoggerInstance, + ProviderComputeInitializeResults, ProviderInstance } from '@oceanprotocol/lib' +import { da } from 'date-fns/locale' import { AssetExtended } from 'src/@types/AssetExtended' import Web3 from 'web3' +import { getValidUntilTime } from './compute' + +export async function initializeProviderForCompute( + dataset: AssetExtended, + algorithm: AssetExtended, + accountId: string, + computeEnv: ComputeEnvironment = null +): Promise { + const computeAsset: ComputeAsset = { + documentId: dataset.id, + serviceId: dataset.services[0].id, + transferTxId: dataset.accessDetails.validOrderTx + } + const computeAlgo: ComputeAlgorithm = { + documentId: algorithm.id, + serviceId: algorithm.services[0].id, + transferTxId: algorithm.accessDetails.validOrderTx + } + + const validUntil = getValidUntilTime( + computeEnv?.maxJobDuration, + dataset.services[0].timeout, + algorithm.services[0].timeout + ) + + return await ProviderInstance.initializeCompute( + [computeAsset], + computeAlgo, + computeEnv?.id, + validUntil, + dataset.services[0].serviceEndpoint, + accountId + ) +} // TODO: Why do we have these one line functions ?!?!?! export async function getEncryptedFiles( diff --git a/src/components/Asset/AssetActions/Compute/index.tsx b/src/components/Asset/AssetActions/Compute/index.tsx index 51bf6d9d6..74432b8fc 100644 --- a/src/components/Asset/AssetActions/Compute/index.tsx +++ b/src/components/Asset/AssetActions/Compute/index.tsx @@ -10,7 +10,8 @@ import { ComputeEnvironment, LoggerInstance, ComputeAlgorithm, - ComputeOutput + ComputeOutput, + ProviderComputeInitializeResults } from '@oceanprotocol/lib' import { toast } from 'react-toastify' import Price from '@shared/Price' @@ -27,9 +28,7 @@ import { isOrderable, getAlgorithmAssetSelectionList, getAlgorithmsForAsset, - getValidUntilTime, - getComputeEnviroment, - checkComputeResourcesValidity + getComputeEnviroment } from '@utils/compute' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute' @@ -42,13 +41,14 @@ import { useAbortController } from '@hooks/useAbortController' import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing' import { OrderPriceAndFees } from 'src/@types/Price' import { buyDtFromPool } from '@utils/pool' -import { order, reuseOrder } from '@utils/order' +import { handleComputeOrder, order, reuseOrder } from '@utils/order' import { AssetExtended } from 'src/@types/AssetExtended' import { getComputeFeedback } from '@utils/feedback' import { usePool } from '@context/Pool' import { useMarketMetadata } from '@context/MarketMetadata' import { getPoolData } from '@context/Pool/_utils' import { getDummyWeb3 } from '@utils/web3' +import { initializeProviderForCompute } from '@utils/provider' export default function Compute({ asset, @@ -84,7 +84,7 @@ export default function Compute({ const [validAlgorithmOrderTx, setValidAlgorithmOrderTx] = useState('') const hasDatatoken = Number(dtBalance) >= 1 - const isMounted = useIsMounted() + // const isMounted = useIsMounted() const { getOpcFeeForToken } = useMarketMetadata() const { poolData } = usePool() const newCancelToken = useCancelToken() @@ -92,7 +92,9 @@ export default function Compute({ const [isAlgoConsumablePrice, setIsAlgoConsumablePrice] = useState(true) const [computeStatusText, setComputeStatusText] = useState('') const [computeEnv, setComputeEnv] = useState() - const [computeValidUntil, setComputeValidUntil] = useState() + const [initializedProviderResponse, setInitializedProviderResponse] = + useState() + // const [computeValidUntil, setComputeValidUntil] = useState() const [datasetOrderPriceAndFees, setDatasetOrderPriceAndFees] = useState() const [isRequestingDataseOrderPrice, setIsRequestingDataseOrderPrice] = @@ -101,7 +103,7 @@ export default function Compute({ useState() const [isRequestingAlgoOrderPrice, setIsRequestingAlgoOrderPrice] = useState(false) - const [isProviderFeeValid, setIsProviderFeeValid] = useState(false) + // const [isProviderFeeValid, setIsProviderFeeValid] = useState(false) const isComputeButtonDisabled = isJobStarting === true || file === null || @@ -125,21 +127,35 @@ export default function Compute({ async function initPriceAndFees() { const computeEnv = await getComputeEnviroment(asset) setComputeEnv(computeEnv) - setIsProviderFeeValid( - await checkComputeResourcesValidity( - asset, - accountId, - computeEnv?.maxJobDuration, - asset.services[0].timeout, - selectedAlgorithmAsset.services[0].timeout - ) + const initializedProvider = await initializeProviderForCompute( + asset, + selectedAlgorithmAsset, + accountId, + computeEnv ) - const validUntil = getValidUntilTime( - computeEnv?.maxJobDuration, - asset.services[0].timeout, - selectedAlgorithmAsset.services[0].timeout - ) - setComputeValidUntil(validUntil) + setInitializedProviderResponse(initializedProvider) + // setIsProviderFeeValid( + // await checkComputeResourcesValidity( + // asset, + // accountId, + // computeEnv?.maxJobDuration, + // asset.services[0].timeout, + // selectedAlgorithmAsset.services[0].timeout + // ) + // ) + // let datasetOrderTx = await checkComputeResourcesValidity( + // dataset, + // accountId, + // computeEnv?.maxJobDuration, + // asset.services[0].timeout, + // selectedAlgorithmAsset.services[0].timeout + // ) + // const validUntil = getValidUntilTime( + // computeEnv?.maxJobDuration, + // asset.services[0].timeout, + // selectedAlgorithmAsset.services[0].timeout + // ) + // setComputeValidUntil(validUntil) if ( asset?.accessDetails?.addressOrId !== ZERO_ADDRESS || asset?.accessDetails?.type !== 'free' @@ -172,10 +188,8 @@ export default function Compute({ asset, ZERO_ADDRESS, poolParams, - computeEnv?.id, - validUntil + initializedProvider.datasets[0].providerFee ) - console.log('datasetPriceAndFees price and fees', datasetPriceAndFees) if (!datasetPriceAndFees) { setError('Error setting dataset price and fees!') toast.error('Error setting dataset price and fees!') @@ -223,15 +237,13 @@ export default function Compute({ selectedAlgorithmAsset, ZERO_ADDRESS, algoPoolParams, - computeEnv?.id, - validUntil + initializedProvider.algorithm.providerFee ) if (!algorithmOrderPriceAndFees) { setError('Error setting algorithm price and fees!') toast.error('Error setting algorithm price and fees!') return } - console.log('algo price and fees', algorithmOrderPriceAndFees) setAlgoOrderPriceAndFees(algorithmOrderPriceAndFees) setIsRequestingAlgoOrderPrice(false) } @@ -246,7 +258,6 @@ export default function Compute({ }, [asset?.accessDetails]) useEffect(() => { - console.log('selcted algo', selectedAlgorithmAsset) if (!selectedAlgorithmAsset?.accessDetails || !accountId) return setIsConsumablePrice(selectedAlgorithmAsset?.accessDetails?.isPurchasable) @@ -311,153 +322,166 @@ export default function Compute({ return } - let datasetOrderTx - if (isOwned) { - datasetOrderTx = validOrderTx - LoggerInstance.log('[compute] Dataset owned txId:', validOrderTx) - } else { - try { - if (!hasDatatoken && asset?.accessDetails.type === 'dynamic') { - setComputeStatusText( - getComputeFeedback( - asset.accessDetails.baseToken?.symbol, - asset.accessDetails.datatoken?.symbol, - asset.metadata.type - )[1] - ) - const tx = await buyDtFromPool( - asset?.accessDetails, - accountId, - web3 - ) - LoggerInstance.log('[compute] Buy dataset dt from pool: ', tx) - if (!tx) { - toast.error('Failed to buy datatoken from pool!') - return - } - } - LoggerInstance.log( - 'dataset orderPriceAndFees: ', - datasetOrderPriceAndFees - ) - setComputeStatusText( - getComputeFeedback( - asset.accessDetails.baseToken?.symbol, - asset.accessDetails.datatoken?.symbol, - asset.metadata.type - )[asset.accessDetails?.type === 'fixed' ? 3 : 2] - ) - const orderTx = await order( - web3, - asset, - datasetOrderPriceAndFees, - accountId, - computeEnv?.id, - computeValidUntil, - computeEnv?.consumerAddress - ) - if (!orderTx) { - toast.error('Failed to order dataset asset!') - return - } - LoggerInstance.log( - '[compute] Order dataset: ', - orderTx.transactionHash - ) - setIsOwned(true) - setValidOrderTx(orderTx.transactionHash) - datasetOrderTx = orderTx.transactionHash - } catch (e) { - LoggerInstance.log(e.message) - toast.error('Failed to order dataset asset!') - return - } - } + const datasetOrderTx = await handleComputeOrder( + web3, + asset, + datasetOrderPriceAndFees, + accountId, + hasDatatoken, + initializedProviderResponse.datasets[0], + computeEnv.consumerAddress + ) + // if (isOwned) { + // datasetOrderTx = validOrderTx + // LoggerInstance.log('[compute] Dataset owned txId:', validOrderTx) + // } else { + // try { + // if (!hasDatatoken && asset?.accessDetails.type === 'dynamic') { + // setComputeStatusText( + // getComputeFeedback( + // asset.accessDetails.baseToken?.symbol, + // asset.accessDetails.datatoken?.symbol, + // asset.metadata.type + // )[1] + // ) + // const tx = await buyDtFromPool( + // asset?.accessDetails, + // accountId, + // web3 + // ) + // LoggerInstance.log('[compute] Buy dataset dt from pool: ', tx) + // if (!tx) { + // toast.error('Failed to buy datatoken from pool!') + // return + // } + // } + // LoggerInstance.log( + // 'dataset orderPriceAndFees: ', + // datasetOrderPriceAndFees + // ) + // setComputeStatusText( + // getComputeFeedback( + // asset.accessDetails.baseToken?.symbol, + // asset.accessDetails.datatoken?.symbol, + // asset.metadata.type + // )[asset.accessDetails?.type === 'fixed' ? 3 : 2] + // ) + // const orderTx = await order( + // web3, + // asset, + // datasetOrderPriceAndFees, + // accountId, ) + // if (!orderTx) { + // toast.error('Failed to order dataset asset!') + // return + // } + // LoggerInstance.log( + // '[compute] Order dataset: ', + // orderTx.transactionHash + // ) + // setIsOwned(true) + // setValidOrderTx(orderTx.transactionHash) + // datasetOrderTx = orderTx.transactionHash + // } catch (e) { + // LoggerInstance.log(e.message) + // toast.error('Failed to order dataset asset!') + // return + // } + // } - let algorithmOrderTx - if (isAlgorithmOwned) { - algorithmOrderTx = validAlgorithmOrderTx - LoggerInstance.log( - '[compute] Algorithm owned txId:', - validAlgorithmOrderTx - ) - } else { - try { - if ( - !hasAlgoAssetDatatoken && - selectedAlgorithmAsset?.accessDetails?.type === 'dynamic' - ) { - setComputeStatusText( - getComputeFeedback( - selectedAlgorithmAsset.accessDetails.baseToken?.symbol, - selectedAlgorithmAsset.accessDetails.datatoken?.symbol, - selectedAlgorithmAsset.metadata.type - )[1] - ) - const tx = await buyDtFromPool( - selectedAlgorithmAsset?.accessDetails, - accountId, - web3 - ) - LoggerInstance.log('[compute] Buy algorithm dt from pool: ', tx) - if (!tx) { - toast.error('Failed to buy datatoken from pool!') - return - } - } - setComputeStatusText( - getComputeFeedback( - selectedAlgorithmAsset.accessDetails.baseToken?.symbol, - selectedAlgorithmAsset.accessDetails.datatoken?.symbol, - selectedAlgorithmAsset.metadata.type - )[selectedAlgorithmAsset.accessDetails?.type === 'fixed' ? 3 : 2] - ) - const orderTx = await order( - web3, - selectedAlgorithmAsset, - algoOrderPriceAndFees, - accountId, - computeEnv?.id, - computeValidUntil, - computeEnv?.consumerAddress - ) - if (!orderTx) { - toast.error('Failed to order algorithm asset!') - return - } - LoggerInstance.log( - '[compute] Order algorithm: ', - orderTx.transactionHash - ) - setIsAlgorithmOwned(true) - setValidAlgorithmOrderTx(orderTx.transactionHash) - algorithmOrderTx = orderTx.transactionHash - } catch (e) { - LoggerInstance.log(e.message) - toast.error('Failed to order algorithm asset!') - return - } - } + const algorithmOrderTx = await handleComputeOrder( + web3, + selectedAlgorithmAsset, + datasetOrderPriceAndFees, + accountId, + hasDatatoken, + initializedProviderResponse.algorithm, + computeEnv.consumerAddress + ) - if (isOwned && !isProviderFeeValid) { - const reuseOrderTx = await reuseOrder( - web3, - asset, - accountId, - validOrderTx, - computeEnv?.id, - computeValidUntil - ) - if (!reuseOrderTx) { - toast.error('Failed to pay provider fees!') - return - } - LoggerInstance.log( - '[compute] Reused order: ', - reuseOrderTx.transactionHash - ) - datasetOrderTx = reuseOrderTx.transactionHash - } + // if (isAlgorithmOwned) { + // algorithmOrderTx = validAlgorithmOrderTx + // LoggerInstance.log( + // '[compute] Algorithm owned txId:', + // validAlgorithmOrderTx + // ) + // } else { + // try { + // if ( + // !hasAlgoAssetDatatoken && + // selectedAlgorithmAsset?.accessDetails?.type === 'dynamic' + // ) { + // setComputeStatusText( + // getComputeFeedback( + // selectedAlgorithmAsset.accessDetails.baseToken?.symbol, + // selectedAlgorithmAsset.accessDetails.datatoken?.symbol, + // selectedAlgorithmAsset.metadata.type + // )[1] + // ) + // const tx = await buyDtFromPool( + // selectedAlgorithmAsset?.accessDetails, + // accountId, + // web3 + // ) + // LoggerInstance.log('[compute] Buy algorithm dt from pool: ', tx) + // if (!tx) { + // toast.error('Failed to buy datatoken from pool!') + // return + // } + // } + // setComputeStatusText( + // getComputeFeedback( + // selectedAlgorithmAsset.accessDetails.baseToken?.symbol, + // selectedAlgorithmAsset.accessDetails.datatoken?.symbol, + // selectedAlgorithmAsset.metadata.type + // )[selectedAlgorithmAsset.accessDetails?.type === 'fixed' ? 3 : 2] + // ) + // const orderTx = await order( + // web3, + // selectedAlgorithmAsset, + // algoOrderPriceAndFees, + // accountId, + // computeEnv?.id, + // computeValidUntil, + // computeEnv?.consumerAddress + // ) + // if (!orderTx) { + // toast.error('Failed to order algorithm asset!') + // return + // } + // LoggerInstance.log( + // '[compute] Order algorithm: ', + // orderTx.transactionHash + // ) + // setIsAlgorithmOwned(true) + // setValidAlgorithmOrderTx(orderTx.transactionHash) + // algorithmOrderTx = orderTx.transactionHash + // } catch (e) { + // LoggerInstance.log(e.message) + // toast.error('Failed to order algorithm asset!') + // return + // } + // } + + // if (isOwned && !isProviderFeeValid) { + // const reuseOrderTx = await reuseOrder( + // web3, + // asset, + // accountId, + // validOrderTx, + // computeEnv?.id, + // computeValidUntil + // ) + // if (!reuseOrderTx) { + // toast.error('Failed to pay provider fees!') + // return + // } + // LoggerInstance.log( + // '[compute] Reused order: ', + // reuseOrderTx.transactionHash + // ) + // datasetOrderTx = reuseOrderTx.transactionHash + // } LoggerInstance.log('[compute] Starting compute job.') const computeAsset: ComputeAsset = {