454 lines
15 KiB
TypeScript
454 lines
15 KiB
TypeScript
import React, { useState, ReactElement, useEffect, useCallback } from 'react'
|
|
import {
|
|
Asset,
|
|
DDO,
|
|
PublisherTrustedAlgorithm,
|
|
FileMetadata
|
|
} from '@oceanprotocol/lib'
|
|
import { toast } from 'react-toastify'
|
|
import Price from '@shared/Price'
|
|
import FileIcon from '@shared/FileIcon'
|
|
import Alert from '@shared/atoms/Alert'
|
|
import { useWeb3 } from '@context/Web3'
|
|
import {
|
|
generateBaseQuery,
|
|
getFilterTerm,
|
|
queryMetadata
|
|
} from '@utils/aquarius'
|
|
import { Formik } from 'formik'
|
|
import { getInitialValues, validationSchema } from './_constants'
|
|
import axios from 'axios'
|
|
import FormStartComputeDataset from './FormComputeDataset'
|
|
import styles from './index.module.css'
|
|
import SuccessConfetti from '@shared/SuccessConfetti'
|
|
import { getServiceByName } from '@utils/ddo'
|
|
import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
|
|
import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute'
|
|
import { getPreviousOrders } from '@utils/subgraph'
|
|
import AssetActionHistoryTable from '../AssetActionHistoryTable'
|
|
import ComputeJobs from '../../../Profile/History/ComputeJobs'
|
|
import { useCancelToken } from '@hooks/useCancelToken'
|
|
import { useIsMounted } from '@hooks/useIsMounted'
|
|
import { SortTermOptions } from '../../../../@types/aquarius/SearchQuery'
|
|
import { getAccessDetails } from '@utils/accessDetailsAndPricing'
|
|
import { AccessDetails } from 'src/@types/Price'
|
|
import { transformAssetToAssetSelection } from '@utils/assetConvertor'
|
|
import { useMarketMetadata } from '@context/MarketMetadata'
|
|
|
|
export default function Compute({
|
|
ddo,
|
|
accessDetails,
|
|
dtBalance,
|
|
file,
|
|
fileIsLoading,
|
|
isConsumable,
|
|
consumableFeedback
|
|
}: {
|
|
ddo: Asset
|
|
accessDetails: AccessDetails
|
|
dtBalance: string
|
|
file: FileMetadata
|
|
fileIsLoading?: boolean
|
|
isConsumable?: boolean
|
|
consumableFeedback?: string
|
|
}): ReactElement {
|
|
const { appConfig } = useMarketMetadata()
|
|
const { accountId } = useWeb3()
|
|
const [isJobStarting, setIsJobStarting] = useState(false)
|
|
const [error, setError] = useState<string>()
|
|
|
|
const [algorithmList, setAlgorithmList] = useState<AssetSelectionAsset[]>()
|
|
const [ddoAlgorithmList, setDdoAlgorithmList] = useState<Asset[]>()
|
|
const [selectedAlgorithmAsset, setSelectedAlgorithmAsset] = useState<Asset>()
|
|
const [hasAlgoAssetDatatoken, setHasAlgoAssetDatatoken] = useState<boolean>()
|
|
const [isPublished, setIsPublished] = useState(false)
|
|
const [hasPreviousDatasetOrder, setHasPreviousDatasetOrder] = useState(false)
|
|
const [previousDatasetOrderId, setPreviousDatasetOrderId] = useState<string>()
|
|
const [hasPreviousAlgorithmOrder, setHasPreviousAlgorithmOrder] =
|
|
useState(false)
|
|
const [algorithmDTBalance, setalgorithmDTBalance] = useState<string>()
|
|
const [algorithmConsumeDetails, setAlgorithmConsumeDetails] =
|
|
useState<AccessDetails>()
|
|
const [previousAlgorithmOrderId, setPreviousAlgorithmOrderId] =
|
|
useState<string>()
|
|
const [datasetTimeout, setDatasetTimeout] = useState<string>()
|
|
const [algorithmTimeout, setAlgorithmTimeout] = useState<string>()
|
|
const newCancelToken = useCancelToken()
|
|
const hasDatatoken = Number(dtBalance) >= 1
|
|
const isMounted = useIsMounted()
|
|
const [isConsumablePrice, setIsConsumablePrice] = useState(true)
|
|
const [isAlgoConsumablePrice, setIsAlgoConsumablePrice] = useState(true)
|
|
const isComputeButtonDisabled =
|
|
isJobStarting === true ||
|
|
file === null ||
|
|
(!hasPreviousDatasetOrder && !hasDatatoken && !isConsumablePrice) ||
|
|
(!hasPreviousAlgorithmOrder &&
|
|
!hasAlgoAssetDatatoken &&
|
|
!isAlgoConsumablePrice)
|
|
|
|
const service = ddo?.services[0]
|
|
const { timeout } = service
|
|
|
|
async function checkPreviousOrders(ddo: DDO) {
|
|
const { type } = ddo.metadata
|
|
|
|
const orderId = await getPreviousOrders(
|
|
ddo.services[0].datatokenAddress?.toLowerCase(),
|
|
accountId?.toLowerCase(),
|
|
timeout.toString()
|
|
)
|
|
|
|
if (!isMounted()) return
|
|
if (type === 'algorithm') {
|
|
setPreviousAlgorithmOrderId(orderId)
|
|
setHasPreviousAlgorithmOrder(!!orderId)
|
|
} else {
|
|
setPreviousDatasetOrderId(orderId)
|
|
setHasPreviousDatasetOrder(!!orderId)
|
|
}
|
|
}
|
|
|
|
async function checkAssetDTBalance(asset: DDO) {
|
|
// const AssetDtBalance = await ocean.datatokens.balance(
|
|
// asset.services[0].datatokenAddress,
|
|
// accountId
|
|
// )
|
|
// setalgorithmDTBalance(AssetDtBalance)
|
|
// setHasAlgoAssetDatatoken(Number(AssetDtBalance) >= 1)
|
|
}
|
|
|
|
function getQuerryString(
|
|
trustedAlgorithmList: PublisherTrustedAlgorithm[],
|
|
chainId?: number
|
|
): SearchQuery {
|
|
const algorithmDidList = trustedAlgorithmList.map((x) => x.did)
|
|
|
|
const baseParams = {
|
|
chainIds: [chainId],
|
|
sort: { sortBy: SortTermOptions.Created },
|
|
filters: [
|
|
getFilterTerm('service.attributes.main.type', 'algorithm'),
|
|
getFilterTerm('id', algorithmDidList)
|
|
]
|
|
} as BaseQueryParams
|
|
|
|
const query = generateBaseQuery(baseParams)
|
|
return query
|
|
}
|
|
|
|
async function getAlgorithmList(): Promise<AssetSelectionAsset[]> {
|
|
const source = axios.CancelToken.source()
|
|
const computeService = ddo.services[0]
|
|
let algorithmSelectionList: AssetSelectionAsset[]
|
|
if (
|
|
!computeService.compute ||
|
|
!computeService.compute.publisherTrustedAlgorithms ||
|
|
computeService.compute.publisherTrustedAlgorithms.length === 0
|
|
) {
|
|
algorithmSelectionList = []
|
|
} else {
|
|
const gueryResults = await queryMetadata(
|
|
getQuerryString(
|
|
computeService.compute.publisherTrustedAlgorithms,
|
|
ddo.chainId
|
|
),
|
|
source.token
|
|
)
|
|
setDdoAlgorithmList(gueryResults.results)
|
|
|
|
algorithmSelectionList = await transformAssetToAssetSelection(
|
|
computeService?.serviceEndpoint,
|
|
gueryResults.results,
|
|
[]
|
|
)
|
|
}
|
|
return algorithmSelectionList
|
|
}
|
|
|
|
const initMetadata = useCallback(async (ddo: Asset): Promise<void> => {
|
|
if (!ddo) return
|
|
const accessDetails = await getAccessDetails(
|
|
ddo.chainId,
|
|
ddo.services[0].datatokenAddress
|
|
)
|
|
setAlgorithmConsumeDetails(accessDetails)
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (!algorithmConsumeDetails) return
|
|
|
|
setIsAlgoConsumablePrice(algorithmConsumeDetails.isPurchasable)
|
|
}, [algorithmConsumeDetails])
|
|
|
|
useEffect(() => {
|
|
if (!accessDetails) return
|
|
|
|
setIsConsumablePrice(accessDetails.isPurchasable)
|
|
}, [accessDetails])
|
|
|
|
// useEffect(() => {
|
|
// setDatasetTimeout(secondsToString(timeout))
|
|
// }, [ddo])
|
|
|
|
useEffect(() => {
|
|
if (!ddo) return
|
|
getAlgorithmList().then((algorithms) => {
|
|
setAlgorithmList(algorithms)
|
|
})
|
|
}, [ddo])
|
|
|
|
useEffect(() => {
|
|
if (!accountId) return
|
|
checkPreviousOrders(ddo)
|
|
}, [ddo, accountId])
|
|
|
|
useEffect(() => {
|
|
if (!selectedAlgorithmAsset) return
|
|
|
|
initMetadata(selectedAlgorithmAsset)
|
|
|
|
const { timeout } = ddo.services[0]
|
|
|
|
// setAlgorithmTimeout(secondsToString(timeout))
|
|
|
|
if (accountId) {
|
|
if (getServiceByName(selectedAlgorithmAsset, 'access')) {
|
|
checkPreviousOrders(selectedAlgorithmAsset).then(() => {
|
|
if (
|
|
!hasPreviousAlgorithmOrder &&
|
|
getServiceByName(selectedAlgorithmAsset, 'compute')
|
|
) {
|
|
checkPreviousOrders(selectedAlgorithmAsset)
|
|
}
|
|
})
|
|
} else if (getServiceByName(selectedAlgorithmAsset, 'compute')) {
|
|
checkPreviousOrders(selectedAlgorithmAsset)
|
|
}
|
|
}
|
|
// ocean && checkAssetDTBalance(selectedAlgorithmAsset)
|
|
}, [ddo, selectedAlgorithmAsset, accountId, hasPreviousAlgorithmOrder])
|
|
|
|
// Output errors in toast UI
|
|
useEffect(() => {
|
|
const newError = error
|
|
if (!newError) return
|
|
toast.error(newError)
|
|
}, [error])
|
|
|
|
// async function startJob(algorithmId: string) {
|
|
// try {
|
|
// if (!ocean) return
|
|
|
|
// setIsJobStarting(true)
|
|
// setIsPublished(false)
|
|
// setError(undefined)
|
|
|
|
// const computeService = getServiceByName(ddo, 'compute')
|
|
// const serviceAlgo = getServiceByName(selectedAlgorithmAsset, 'access')
|
|
// ? getServiceByName(selectedAlgorithmAsset, 'access')
|
|
// : getServiceByName(selectedAlgorithmAsset, 'compute')
|
|
|
|
// const computeAlgorithm: ComputeAlgorithm = {
|
|
// did: selectedAlgorithmAsset.id,
|
|
// serviceIndex: serviceAlgo.index,
|
|
// dataToken: selectedAlgorithmAsset.services[0].datatokenAddress
|
|
// }
|
|
// const allowed = await ocean.compute.isOrderable(
|
|
// ddo.id,
|
|
// computeService.index,
|
|
// computeAlgorithm
|
|
// )
|
|
// LoggerInstance.log('[compute] Is data set orderable?', allowed)
|
|
|
|
// if (!allowed) {
|
|
// setError(
|
|
// 'Data set is not orderable in combination with selected algorithm.'
|
|
// )
|
|
// LoggerInstance.error(
|
|
// '[compute] Error starting compute job. Dataset is not orderable in combination with selected algorithm.'
|
|
// )
|
|
// return
|
|
// }
|
|
|
|
// if (!hasPreviousDatasetOrder && !hasDatatoken) {
|
|
// const tx = await buyDT('1', price, ddo)
|
|
// if (!tx) {
|
|
// setError('Error buying datatoken.')
|
|
// LoggerInstance.error('[compute] Error buying datatoken for data set ', ddo.id)
|
|
// return
|
|
// }
|
|
// }
|
|
|
|
// if (!hasPreviousAlgorithmOrder && !hasAlgoAssetDatatoken) {
|
|
// const tx = await buyDT('1', algorithmPrice, selectedAlgorithmAsset)
|
|
// if (!tx) {
|
|
// setError('Error buying datatoken.')
|
|
// LoggerInstance.error(
|
|
// '[compute] Error buying datatoken for algorithm ',
|
|
// selectedAlgorithmAsset.id
|
|
// )
|
|
// return
|
|
// }
|
|
// }
|
|
|
|
// // TODO: pricingError is always undefined even upon errors during buyDT for whatever reason.
|
|
// // So manually drop out above, but ideally could be replaced with this alone.
|
|
// if (pricingError) {
|
|
// setError(pricingError)
|
|
// return
|
|
// }
|
|
|
|
// const assetOrderId = hasPreviousDatasetOrder
|
|
// ? previousDatasetOrderId
|
|
// : await ocean.compute.orderAsset(
|
|
// accountId,
|
|
// ddo.id,
|
|
// computeService.index,
|
|
// computeAlgorithm,
|
|
// appConfig.marketFeeAddress,
|
|
// undefined,
|
|
// null,
|
|
// false
|
|
// )
|
|
|
|
// assetOrderId &&
|
|
// LoggerInstance.log(
|
|
// `[compute] Got ${
|
|
// hasPreviousDatasetOrder ? 'existing' : 'new'
|
|
// } order ID for dataset: `,
|
|
// assetOrderId
|
|
// )
|
|
|
|
// const algorithmAssetOrderId = hasPreviousAlgorithmOrder
|
|
// ? previousAlgorithmOrderId
|
|
// : await ocean.compute.orderAlgorithm(
|
|
// algorithmId,
|
|
// serviceAlgo.type,
|
|
// accountId,
|
|
// serviceAlgo.index,
|
|
// appConfig.marketFeeAddress,
|
|
// undefined,
|
|
// null,
|
|
// false
|
|
// )
|
|
|
|
// algorithmAssetOrderId &&
|
|
// LoggerInstance.log(
|
|
// `[compute] Got ${
|
|
// hasPreviousAlgorithmOrder ? 'existing' : 'new'
|
|
// } order ID for algorithm: `,
|
|
// algorithmAssetOrderId
|
|
// )
|
|
|
|
// if (!assetOrderId || !algorithmAssetOrderId) {
|
|
// setError('Error ordering assets.')
|
|
// return
|
|
// }
|
|
|
|
// computeAlgorithm.transferTxId = algorithmAssetOrderId
|
|
// LoggerInstance.log('[compute] Starting compute job.')
|
|
|
|
// const output: ComputeOutput = {
|
|
// publishAlgorithmLog: true,
|
|
// publishOutput: true
|
|
// }
|
|
// const response = await ocean.compute.start(
|
|
// ddo.id,
|
|
// assetOrderId,
|
|
// ddo.services[0].datatokenAddress,
|
|
// account,
|
|
// computeAlgorithm,
|
|
// output,
|
|
// `${computeService.index}`,
|
|
// computeService.type
|
|
// )
|
|
|
|
// if (!response) {
|
|
// setError('Error starting compute job.')
|
|
// return
|
|
// }
|
|
|
|
// LoggerInstance.log('[compute] Starting compute job response: ', response)
|
|
|
|
// await checkPreviousOrders(selectedAlgorithmAsset)
|
|
// await checkPreviousOrders(ddo)
|
|
// setIsPublished(true)
|
|
// } catch (error) {
|
|
// await checkPreviousOrders(selectedAlgorithmAsset)
|
|
// await checkPreviousOrders(ddo)
|
|
// setError('Failed to start job!')
|
|
// LoggerInstance.error('[compute] Failed to start job: ', error.message)
|
|
// } finally {
|
|
// setIsJobStarting(false)
|
|
// }
|
|
// }
|
|
|
|
return (
|
|
<>
|
|
<div className={styles.info}>
|
|
<FileIcon file={file} isLoading={fileIsLoading} small />
|
|
<Price accessDetails={accessDetails} conversion size="large" />
|
|
</div>
|
|
|
|
{ddo.metadata.type === 'algorithm' ? (
|
|
<>
|
|
<Alert
|
|
text="This algorithm has been set to private by the publisher and can't be downloaded. You can run it against any allowed data sets though!"
|
|
state="info"
|
|
/>
|
|
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} asset={ddo} />
|
|
</>
|
|
) : (
|
|
<Formik
|
|
initialValues={getInitialValues()}
|
|
validateOnMount
|
|
validationSchema={validationSchema}
|
|
onSubmit={async (values) => {
|
|
// await startJob(values.algorithm)
|
|
}}
|
|
>
|
|
<FormStartComputeDataset
|
|
algorithms={algorithmList}
|
|
ddoListAlgorithms={ddoAlgorithmList}
|
|
setSelectedAlgorithm={setSelectedAlgorithmAsset}
|
|
isLoading={isJobStarting}
|
|
isComputeButtonDisabled={isComputeButtonDisabled}
|
|
hasPreviousOrder={hasPreviousDatasetOrder}
|
|
hasDatatoken={hasDatatoken}
|
|
dtBalance={dtBalance}
|
|
datasetLowPoolLiquidity={!isConsumablePrice}
|
|
assetType={ddo?.metadata.type}
|
|
assetTimeout={datasetTimeout}
|
|
hasPreviousOrderSelectedComputeAsset={hasPreviousAlgorithmOrder}
|
|
hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken}
|
|
oceanSymbol={accessDetails?.baseToken?.symbol || ''}
|
|
dtSymbolSelectedComputeAsset={
|
|
selectedAlgorithmAsset?.datatokens[0]?.symbol
|
|
}
|
|
dtBalanceSelectedComputeAsset={algorithmDTBalance}
|
|
selectedComputeAssetLowPoolLiquidity={!isAlgoConsumablePrice}
|
|
selectedComputeAssetType="algorithm"
|
|
selectedComputeAssetTimeout={algorithmTimeout}
|
|
// lazy comment when removing pricingStepText
|
|
stepText={'pricingStepText' || 'Starting Compute Job...'}
|
|
algorithmConsumeDetails={algorithmConsumeDetails}
|
|
isConsumable={isConsumable}
|
|
consumableFeedback={consumableFeedback}
|
|
/>
|
|
</Formik>
|
|
)}
|
|
|
|
<footer className={styles.feedback}>
|
|
{isPublished && (
|
|
<SuccessConfetti success="Your job started successfully! Watch the progress below or on your profile." />
|
|
)}
|
|
</footer>
|
|
{accountId && accessDetails?.datatoken && (
|
|
<AssetActionHistoryTable title="Your Compute Jobs">
|
|
<ComputeJobs minimal />
|
|
</AssetActionHistoryTable>
|
|
)}
|
|
</>
|
|
)
|
|
}
|