mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Restore compute functionality (#1069)
* add balance check and check is consumable * add isOrderable and other helpers * finish start compute job * removed unused methods * add more comments * add pool logic for order * move asset selection to compute helper * small fix * fixed get algo list * refactor start compute job and more fixes * update order params * use compute env and compute consumer address * fix prices * fix algorithms selection list on allowAllPublisher case * fix edit compute settings * update compute resources valid until logic * fixes and cleanups * wip compute jobs * fix compute timeout value * fixed compute jobs logic * fix algo selection list name * fixed compute jobs from profile loading * update start compute flow messages * update set algo access details * update compute message logic * added logs * update package lock * remove logs * fix edit compute checksums for files and container * Fix compute dataset algorithm list (#1194) * fix query Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * remove comment Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * Fix previous order tx (#1197) * rename nft update query Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix previous order Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix build * handle order price, NaN and default 0 * optional value for all fee, prevent breaking when no value * fix aquarius call and added logs * update provider compute status call * remove percentage fee from price sum, depends smart contract calculation (#1249) Co-authored-by: Soon Huat <soon_huat.phan@daimler.com> * fix display of compute datasets with free price * removed to lowerCase on eth address * fix compute jobs section and your jobs * bumo ocean lib to 1.0.0-next.32 * c2d show price with fee, exclude provider fee * wip get results * include loading when calculating data + algo price, tooltip show order price * update get compute url and use oceanjs helper for download * update computeStatus signature to fix build and CI * added logs * refactor setting price and fees for assets * update compute details and compute results UI and style * update flex value * update download buttons style * update download buttons text * bump ocean lib version and lint fixes * get provier uri for compute results based on job input did * use zero adress for price and fees order * some fixes * Add reuse order in start compute flow (#1352) * wip add reuse order logic * add reuse order in start job * added missing check if no jobs found * update lib Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix lint Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> Co-authored-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix fixed rate * fix build * fix your compute jobs section when asset network not selected * disable edit compute settings for algorithms * fix compute jobs infinite loading when no jobs found * fix compute form * show token symbol for free assets also on compute price output * removed swp file * some decimal fixes * partial fix for asset with pool fees, algo not working yet * more decimal fixes * fix algo with pool price and fees fetching * fix selecting algorithms when on different network * fix compute jobs table auto refresh and details modal closing * wip compute initialize * order fixes * fix lint * fix conditions and cleanups * fix compute status text display * init prices and fees after starting a compute job * start/order button tweaks * kick in loader earlier * update compute status feedback messages * fixed initial price * compute jobs refetch and reuse order * remove logs * removed logs and added some explanations * use compute env max duration value in seconds * error handling on intializeCompute and order * removed console logs and added one new check * use optional on initialized provider check * remove toast from provider helper * fix compute env issue on start order * disable job selection during actions execution * temporary fix publish algo with custom docker image * fix provider fee display * remove unnecessary condition * fix alignment based button on action type (#1491) * fix alignment based on action type * moving to CSS modules * send providerFeeAmount as string * remove cast on providerFeeAmount * removed some logs and added few comments * update price output tooltip and total price logic * set providerFee amount only when avaialable * bump oceanlib to 1.1.2 * replace FIleMetadata to fix build * used approveWei for approving provider fees * fix free algo price selection and display * fix provider fee load at first algo selection * update compute help text * fix provider fee approve for free assets * cleanup * remove commented out code * remove unused state * removed unused imports * typos in comments, variables, props * more typos * shorten getAccessDetailsFromTokenPrice() a bit * state & hooks access reordering * Update src/@utils/ddo.ts remove metadata from service type Co-authored-by: Matthias Kretschmann <m@kretschmann.io> * effect dependency fixes * state renaming * effect dependency fixes * compute jobs profile visual fixes * effect dependency fixes * more comments removal * add accountId as a dependency in effect * move isOwner to asset provider * refactor handleComputeOrder for less complexity and more useful error reporting * more proper error throwing * provider fee statement tweak * more obvious edit action * empty array for `publisherTrustedAlgorithms` & `publisherTrustedAlgorithmPublishers` by default * ref #1538 * ref #1539 * don t use initial tx values as valid order use subgraph value * fix algo list fetching * closes #1537 * addresses #1538 * fix disable compute button if algo is consumable * move isOwner check to single effect * Correctly display trusted algorithms in compute asset (#1541) * fix allowed algo * fix trusted algo filter Co-authored-by: mihaisc <mihai.scarlat@smartcontrol.ro> Co-authored-by: Soon Huat <soon_huat.phan@daimler.com> Co-authored-by: Soon Huat <soonhuat.phan@hotmail.com> Co-authored-by: Enzo Vezzaro <enzo-vezzaro@live.it> Co-authored-by: Matthias Kretschmann <m@kretschmann.io> Co-authored-by: mihaisc <mihai@oceanprotocol.com>
This commit is contained in:
parent
d884a9529d
commit
5387b9a3dd
@ -27,6 +27,7 @@ export interface AssetProviderValue {
|
||||
error?: string
|
||||
isAssetNetwork: boolean
|
||||
isV3Asset: boolean
|
||||
isOwner: boolean
|
||||
oceanConfig: Config
|
||||
loading: boolean
|
||||
fetchAsset: (token?: CancelToken) => Promise<void>
|
||||
@ -49,6 +50,7 @@ function AssetProvider({
|
||||
const [asset, setAsset] = useState<AssetExtended>()
|
||||
const [title, setTitle] = useState<string>()
|
||||
const [owner, setOwner] = useState<string>()
|
||||
const [isOwner, setIsOwner] = useState<boolean>()
|
||||
const [error, setError] = useState<string>()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [isAssetNetwork, setIsAssetNetwork] = useState<boolean>()
|
||||
@ -140,6 +142,16 @@ function AssetProvider({
|
||||
setIsAssetNetwork(isAssetNetwork)
|
||||
}, [chainId, asset?.chainId])
|
||||
|
||||
// -----------------------------------
|
||||
// Asset owner check against wallet user
|
||||
// -----------------------------------
|
||||
useEffect(() => {
|
||||
if (!accountId || !owner) return
|
||||
|
||||
const isOwner = accountId?.toLowerCase() === owner.toLowerCase()
|
||||
setIsOwner(isOwner)
|
||||
}, [accountId, owner])
|
||||
|
||||
// -----------------------------------
|
||||
// Load ocean config based on asset network
|
||||
// -----------------------------------
|
||||
@ -172,6 +184,7 @@ function AssetProvider({
|
||||
fetchAsset,
|
||||
isAssetNetwork,
|
||||
isV3Asset,
|
||||
isOwner,
|
||||
oceanConfig
|
||||
} as AssetProviderValue
|
||||
}
|
||||
|
8
src/@types/Compute.d.ts
vendored
8
src/@types/Compute.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
import { ComputeJob } from '@oceanprotocol/lib'
|
||||
import { OrdersData_tokenOrders_datatokenId as OrdersDatatoken } from './apollo/OrdersData'
|
||||
import { OrdersData_orders_datatoken as OrdersDatatoken } from '../@types/subgraph/OrdersData'
|
||||
|
||||
// declaring into global scope to be able to use this as
|
||||
// ambiant types despite the above imports
|
||||
@ -22,10 +22,10 @@ declare global {
|
||||
|
||||
interface TokenOrder {
|
||||
id: string
|
||||
serviceId: number
|
||||
datatokenId: OrdersDatatoken
|
||||
serviceIndex: number
|
||||
datatoken: OrdersDatatoken
|
||||
tx: any
|
||||
timestamp: number
|
||||
createdTimestamp: number
|
||||
}
|
||||
|
||||
interface ComputeResults {
|
||||
|
@ -8,13 +8,19 @@ 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'
|
||||
import { AccessDetails, OrderPriceAndFees } from 'src/@types/Price'
|
||||
import Decimal from 'decimal.js'
|
||||
import { consumeMarketOrderFee } from '../../app.config'
|
||||
import Web3 from 'web3'
|
||||
|
||||
const tokensPriceQuery = gql`
|
||||
query TokensPriceQuery($datatokenIds: [ID!], $account: String) {
|
||||
@ -26,13 +32,20 @@ const tokensPriceQuery = gql`
|
||||
publishMarketFeeToken
|
||||
publishMarketFeeAmount
|
||||
orders(
|
||||
where: { consumer: $account }
|
||||
where: { payer: $account }
|
||||
orderBy: createdTimestamp
|
||||
orderDirection: desc
|
||||
) {
|
||||
tx
|
||||
serviceIndex
|
||||
createdTimestamp
|
||||
reuses(orderBy: createdTimestamp, orderDirection: desc) {
|
||||
id
|
||||
caller
|
||||
createdTimestamp
|
||||
tx
|
||||
block
|
||||
}
|
||||
}
|
||||
dispensers {
|
||||
id
|
||||
@ -91,13 +104,20 @@ const tokenPriceQuery = gql`
|
||||
publishMarketFeeToken
|
||||
publishMarketFeeAmount
|
||||
orders(
|
||||
where: { consumer: $account }
|
||||
where: { payer: $account }
|
||||
orderBy: createdTimestamp
|
||||
orderDirection: desc
|
||||
) {
|
||||
tx
|
||||
serviceIndex
|
||||
createdTimestamp
|
||||
reuses(orderBy: createdTimestamp, orderDirection: desc) {
|
||||
id
|
||||
caller
|
||||
createdTimestamp
|
||||
tx
|
||||
block
|
||||
}
|
||||
}
|
||||
dispensers {
|
||||
id
|
||||
@ -152,19 +172,22 @@ function getAccessDetailsFromTokenPrice(
|
||||
timeout?: number
|
||||
): AccessDetails {
|
||||
const accessDetails = {} as AccessDetails
|
||||
if (tokenPrice && tokenPrice.orders && tokenPrice.orders.length > 0) {
|
||||
|
||||
if (tokenPrice?.orders?.length > 0) {
|
||||
const order = tokenPrice.orders[0]
|
||||
const reusedOrder = order?.reuses?.length > 0 ? order.reuses[0] : null
|
||||
// asset is owned if there is an order and asset has timeout 0 (forever) or if the condition is valid
|
||||
accessDetails.isOwned =
|
||||
timeout === 0 || Date.now() / 1000 - order.createdTimestamp < timeout
|
||||
accessDetails.validOrderTx = order.tx
|
||||
timeout === 0 || Date.now() / 1000 - order?.createdTimestamp < timeout
|
||||
// the last valid order should be the last reuse order tx id if there is one
|
||||
accessDetails.validOrderTx = reusedOrder?.tx || order?.tx
|
||||
}
|
||||
|
||||
// TODO: fetch order fee from sub query
|
||||
accessDetails.publisherMarketOrderFee = tokenPrice.publishMarketFeeAmount
|
||||
accessDetails.publisherMarketOrderFee = tokenPrice?.publishMarketFeeAmount
|
||||
|
||||
// free is always the best price
|
||||
if (tokenPrice.dispensers && tokenPrice.dispensers.length > 0) {
|
||||
if (tokenPrice?.dispensers?.length > 0) {
|
||||
const dispenser = tokenPrice.dispensers[0]
|
||||
accessDetails.type = 'free'
|
||||
accessDetails.addressOrId = dispenser.token.id
|
||||
@ -179,10 +202,7 @@ function getAccessDetailsFromTokenPrice(
|
||||
}
|
||||
|
||||
// checking for fixed price
|
||||
if (
|
||||
tokenPrice.fixedRateExchanges &&
|
||||
tokenPrice.fixedRateExchanges.length > 0
|
||||
) {
|
||||
if (tokenPrice?.fixedRateExchanges?.length > 0) {
|
||||
const fixed = tokenPrice.fixedRateExchanges[0]
|
||||
accessDetails.type = 'fixed'
|
||||
accessDetails.addressOrId = fixed.exchangeId
|
||||
@ -203,7 +223,7 @@ function getAccessDetailsFromTokenPrice(
|
||||
}
|
||||
|
||||
// checking for pools
|
||||
if (tokenPrice.pools && tokenPrice.pools.length > 0) {
|
||||
if (tokenPrice?.pools?.length > 0) {
|
||||
const pool = tokenPrice.pools[0]
|
||||
accessDetails.type = 'dynamic'
|
||||
accessDetails.addressOrId = pool.id
|
||||
@ -227,14 +247,15 @@ function getAccessDetailsFromTokenPrice(
|
||||
}
|
||||
|
||||
/**
|
||||
* This will be used to get price including feed before ordering
|
||||
* This will be used to get price including fees before ordering
|
||||
* @param {AssetExtended} asset
|
||||
* @return {Promise<OrdePriceAndFee>}
|
||||
*/
|
||||
export async function getOrderPriceAndFees(
|
||||
asset: AssetExtended,
|
||||
accountId: string,
|
||||
paramsForPool: CalcInGivenOutParams
|
||||
accountId?: string,
|
||||
paramsForPool?: CalcInGivenOutParams,
|
||||
providerFees?: ProviderFees
|
||||
): Promise<OrderPriceAndFees> {
|
||||
const orderPriceAndFee = {
|
||||
price: '0',
|
||||
@ -252,14 +273,17 @@ export async function getOrderPriceAndFees(
|
||||
} as OrderPriceAndFees
|
||||
|
||||
// fetch provider fee
|
||||
const initializeData = await ProviderInstance.initialize(
|
||||
|
||||
const initializeData =
|
||||
!providerFees &&
|
||||
(await ProviderInstance.initialize(
|
||||
asset?.id,
|
||||
asset.services[0].id,
|
||||
asset?.services[0].id,
|
||||
0,
|
||||
accountId,
|
||||
asset?.services[0].serviceEndpoint
|
||||
)
|
||||
orderPriceAndFee.providerFee = initializeData.providerFee
|
||||
))
|
||||
orderPriceAndFee.providerFee = providerFees || initializeData.providerFee
|
||||
|
||||
// fetch price and swap fees
|
||||
switch (asset?.accessDetails?.type) {
|
||||
@ -286,10 +310,9 @@ export async function getOrderPriceAndFees(
|
||||
}
|
||||
|
||||
// calculate full price, we assume that all the values are in ocean, otherwise this will be incorrect
|
||||
orderPriceAndFee.price = new Decimal(orderPriceAndFee.price)
|
||||
.add(new Decimal(orderPriceAndFee.consumeMarketOrderFee))
|
||||
.add(new Decimal(orderPriceAndFee.publisherMarketOrderFee))
|
||||
.add(new Decimal(orderPriceAndFee.providerFee.providerFeeAmount))
|
||||
orderPriceAndFee.price = new Decimal(+orderPriceAndFee.price || 0)
|
||||
.add(new Decimal(+orderPriceAndFee?.consumeMarketOrderFee || 0))
|
||||
.add(new Decimal(+orderPriceAndFee?.publisherMarketOrderFee || 0))
|
||||
.toString()
|
||||
return orderPriceAndFee
|
||||
}
|
||||
|
@ -11,6 +11,12 @@ import { transformAssetToAssetSelection } from './assetConvertor'
|
||||
|
||||
export const MAXIMUM_NUMBER_OF_PAGES_WITH_RESULTS = 476
|
||||
|
||||
export function escapeEsReservedCharacters(value: string): string {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const pattern = /([\!\*\+\-\=\<\>\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g
|
||||
return value.replace(pattern, '\\$1')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filterField the name of the actual field from the ddo schema e.g. 'id','service.attributes.main.type'
|
||||
* @param value the value of the filter
|
||||
@ -174,7 +180,7 @@ export async function getAssetsFromDidList(
|
||||
didList: string[],
|
||||
chainIds: number[],
|
||||
cancelToken: CancelToken
|
||||
): Promise<any> {
|
||||
): Promise<PagedAssets> {
|
||||
try {
|
||||
if (!(didList.length > 0)) return
|
||||
|
||||
@ -249,12 +255,15 @@ export async function getAlgorithmDatasetsForCompute(
|
||||
): Promise<AssetSelectionAsset[]> {
|
||||
const baseQueryParams = {
|
||||
chainIds: [datasetChainId],
|
||||
filters: [
|
||||
getFilterTerm(
|
||||
'service.compite.publisherTrustedAlgorithms.did',
|
||||
algorithmId
|
||||
)
|
||||
],
|
||||
nestedQuery: {
|
||||
must: {
|
||||
match: {
|
||||
'services.compute.publisherTrustedAlgorithms.did': {
|
||||
query: escapeEsReservedCharacters(algorithmId)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
sortOptions: {
|
||||
sortBy: SortTermOptions.Created,
|
||||
sortDirection: SortDirectionOptions.Descending
|
||||
|
@ -18,7 +18,7 @@ export async function transformAssetToAssetSelection(
|
||||
const algoComputeService = getServiceByName(asset, 'compute')
|
||||
|
||||
if (
|
||||
asset?.accessDetails.price &&
|
||||
asset?.accessDetails?.price &&
|
||||
algoComputeService?.serviceEndpoint === datasetProviderEndpoint
|
||||
) {
|
||||
let selected = false
|
||||
@ -29,7 +29,7 @@ export async function transformAssetToAssetSelection(
|
||||
})
|
||||
const algorithmAsset: AssetSelectionAsset = {
|
||||
did: asset.id,
|
||||
name: asset.datatokens[0].name,
|
||||
name: asset.metadata.name,
|
||||
price: asset.accessDetails.price,
|
||||
checked: selected,
|
||||
symbol: asset.datatokens[0].symbol
|
||||
|
@ -1,19 +1,15 @@
|
||||
// import {
|
||||
// ServiceComputePrivacy,
|
||||
// publisherTrustedAlgorithm as PublisherTrustedAlgorithm,
|
||||
// Service,
|
||||
// LoggerInstance,
|
||||
// Provider,
|
||||
// Config,
|
||||
// Ocean,
|
||||
// Account
|
||||
// } from '@oceanprotocol/lib'
|
||||
// import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
|
||||
import {
|
||||
Asset,
|
||||
ServiceComputeOptions,
|
||||
PublisherTrustedAlgorithm,
|
||||
getHash
|
||||
getHash,
|
||||
LoggerInstance,
|
||||
ComputeAlgorithm,
|
||||
DDO,
|
||||
Service,
|
||||
ProviderInstance,
|
||||
ComputeEnvironment,
|
||||
ComputeJob
|
||||
} from '@oceanprotocol/lib'
|
||||
import { CancelToken } from 'axios'
|
||||
import { gql } from 'urql'
|
||||
@ -24,6 +20,11 @@ import {
|
||||
retrieveDDOListByDIDs
|
||||
} from './aquarius'
|
||||
import { fetchDataForMultipleChains } from './subgraph'
|
||||
import { getServiceById, getServiceByName } from './ddo'
|
||||
import { SortTermOptions } from 'src/@types/aquarius/SearchQuery'
|
||||
import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
|
||||
import { transformAssetToAssetSelection } from './assetConvertor'
|
||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||
|
||||
const getComputeOrders = gql`
|
||||
query ComputeOrders($user: String!) {
|
||||
@ -72,201 +73,256 @@ async function getAssetMetadata(
|
||||
const baseQueryparams = {
|
||||
chainIds,
|
||||
filters: [
|
||||
getFilterTerm('dataToken', queryDtList),
|
||||
getFilterTerm('service.type', 'compute'),
|
||||
getFilterTerm('service.attributes.main.type', 'dataset')
|
||||
getFilterTerm('services.datatokenAddress', queryDtList),
|
||||
getFilterTerm('services.type', 'compute'),
|
||||
getFilterTerm('metadata.type', 'dataset')
|
||||
],
|
||||
ignorePurgatory: true
|
||||
} as BaseQueryParams
|
||||
const query = generateBaseQuery(baseQueryparams)
|
||||
const result = await queryMetadata(query, cancelToken)
|
||||
|
||||
return result.results
|
||||
return result?.results
|
||||
}
|
||||
|
||||
function getServiceEndpoints(data: TokenOrder[], assets: Asset[]): string[] {
|
||||
// const serviceEndpoints: string[] = []
|
||||
export async function isOrderable(
|
||||
asset: Asset | DDO,
|
||||
serviceId: string,
|
||||
algorithm: ComputeAlgorithm,
|
||||
algorithmDDO: Asset | DDO
|
||||
): Promise<boolean> {
|
||||
const datasetService: Service = getServiceById(asset, serviceId)
|
||||
if (!datasetService) return false
|
||||
|
||||
// for (let i = 0; i < data.length; i++) {
|
||||
// try {
|
||||
// const did = web3.utils
|
||||
// .toChecksumAddress(data[i].datatokenId.address)
|
||||
// .replace('0x', 'did:op:')
|
||||
// const ddo = assets.filter((x) => x.id === did)[0]
|
||||
// if (ddo === undefined) continue
|
||||
|
||||
// const service = ddo.services.filter(
|
||||
// (x: Service) => x.index === data[i].serviceId
|
||||
// )[0]
|
||||
|
||||
// if (!service || service.type !== 'compute') continue
|
||||
// const { providerEndpoint } = service
|
||||
|
||||
// const wasProviderQueried =
|
||||
// serviceEndpoints?.filter((x) => x === providerEndpoint).length > 0
|
||||
|
||||
// if (wasProviderQueried) continue
|
||||
// serviceEndpoints.push(providerEndpoint)
|
||||
// } catch (err) {
|
||||
// LoggerInstance.error(err.message)
|
||||
// }
|
||||
// }
|
||||
|
||||
// return serviceEndpoints
|
||||
|
||||
return ['dummy']
|
||||
if (datasetService.type === 'compute') {
|
||||
if (algorithm.meta) {
|
||||
// check if raw algo is allowed
|
||||
if (datasetService.compute.allowRawAlgorithm) return true
|
||||
LoggerInstance.error('ERROR: This service does not allow raw algorithm')
|
||||
return false
|
||||
}
|
||||
if (algorithm.documentId) {
|
||||
const algoService: Service = getServiceById(
|
||||
algorithmDDO,
|
||||
algorithm.serviceId
|
||||
)
|
||||
if (algoService && algoService.type === 'compute') {
|
||||
if (algoService.serviceEndpoint !== datasetService.serviceEndpoint) {
|
||||
this.logger.error(
|
||||
'ERROR: Both assets with compute service are not served by the same provider'
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// async function getProviders(
|
||||
// serviceEndpoints: string[],
|
||||
// config: Config,
|
||||
// ocean: Ocean
|
||||
// ): Promise<Provider[]> {
|
||||
// const providers: Provider[] = []
|
||||
export function getValidUntilTime(
|
||||
computeEnvMaxJobDuration: number,
|
||||
datasetTimeout?: number,
|
||||
algorithmTimeout?: number
|
||||
) {
|
||||
const inputValues = []
|
||||
computeEnvMaxJobDuration && inputValues.push(computeEnvMaxJobDuration)
|
||||
datasetTimeout && inputValues.push(datasetTimeout)
|
||||
algorithmTimeout && inputValues.push(algorithmTimeout)
|
||||
|
||||
// try {
|
||||
// for (let i = 0; i < serviceEndpoints?.length; i++) {
|
||||
// const instanceConfig = {
|
||||
// config,
|
||||
// web3: config.web3Provider,
|
||||
// logger: LoggerInstance,
|
||||
// ocean
|
||||
// }
|
||||
// const provider = await Provider.getInstance(instanceConfig)
|
||||
// await provider.setBaseUrl(serviceEndpoints[i])
|
||||
// const hasSameCompute =
|
||||
// providers.filter((x) => x.computeAddress === provider.computeAddress)
|
||||
// .length > 0
|
||||
// if (!hasSameCompute) providers.push(provider)
|
||||
// }
|
||||
// } catch (err) {
|
||||
// LoggerInstance.error(err.message)
|
||||
// }
|
||||
const minValue = Math.min(...inputValues)
|
||||
const mytime = new Date()
|
||||
mytime.setMinutes(mytime.getMinutes() + Math.floor(minValue / 60))
|
||||
return Math.floor(mytime.getTime() / 1000)
|
||||
}
|
||||
|
||||
// return providers
|
||||
// }
|
||||
export async function getComputeEnviroment(
|
||||
asset: Asset
|
||||
): Promise<ComputeEnvironment> {
|
||||
if (asset?.services[0]?.type !== 'compute') return null
|
||||
try {
|
||||
const computeEnvs = await ProviderInstance.getComputeEnvironments(
|
||||
asset.services[0].serviceEndpoint
|
||||
)
|
||||
if (!computeEnvs[0]) return null
|
||||
return computeEnvs[0]
|
||||
} catch (e) {
|
||||
LoggerInstance.error('[compute] Fetch compute enviroment: ', e.message)
|
||||
}
|
||||
}
|
||||
|
||||
// async function getJobs(
|
||||
// providers: Provider[],
|
||||
// account: Account,
|
||||
// assets: Asset[]
|
||||
// ): Promise<ComputeJobMetaData[]> {
|
||||
// const computeJobs: ComputeJobMetaData[] = []
|
||||
export function getQueryString(
|
||||
trustedAlgorithmList: PublisherTrustedAlgorithm[],
|
||||
trustedPublishersList: string[],
|
||||
chainId?: number
|
||||
): SearchQuery {
|
||||
const algorithmDidList = trustedAlgorithmList?.map((x) => x.did)
|
||||
|
||||
// for (let i = 0; i < providers.length; i++) {
|
||||
// try {
|
||||
// const providerComputeJobs = (await providers[i].computeStatus(
|
||||
// '',
|
||||
// account,
|
||||
// undefined,
|
||||
// undefined,
|
||||
// false
|
||||
// )) as ComputeJob[]
|
||||
const baseParams = {
|
||||
chainIds: [chainId],
|
||||
sort: { sortBy: SortTermOptions.Created },
|
||||
filters: [getFilterTerm('metadata.type', 'algorithm')]
|
||||
} as BaseQueryParams
|
||||
algorithmDidList?.length > 0 &&
|
||||
baseParams.filters.push(getFilterTerm('_id', algorithmDidList))
|
||||
trustedPublishersList?.length > 0 &&
|
||||
baseParams.filters.push(getFilterTerm('nft.owner', trustedPublishersList))
|
||||
const query = generateBaseQuery(baseParams)
|
||||
|
||||
// // means the provider uri is not good, so we ignore it and move on
|
||||
// if (!providerComputeJobs) continue
|
||||
// providerComputeJobs.sort((a, b) => {
|
||||
// if (a.dateCreated > b.dateCreated) {
|
||||
// return -1
|
||||
// }
|
||||
// if (a.dateCreated < b.dateCreated) {
|
||||
// return 1
|
||||
// }
|
||||
// return 0
|
||||
// })
|
||||
return query
|
||||
}
|
||||
|
||||
// for (let j = 0; j < providerComputeJobs?.length; j++) {
|
||||
// const job = providerComputeJobs[j]
|
||||
// const did = job.inputDID[0]
|
||||
// const ddo = assets.filter((x) => x.id === did)[0]
|
||||
export async function getAlgorithmsForAsset(
|
||||
asset: Asset,
|
||||
token: CancelToken
|
||||
): Promise<Asset[]> {
|
||||
const computeService: Service = getServiceByName(asset, 'compute')
|
||||
|
||||
// if (!ddo) continue
|
||||
if (
|
||||
!computeService.compute ||
|
||||
(computeService.compute.publisherTrustedAlgorithms?.length === 0 &&
|
||||
computeService.compute.publisherTrustedAlgorithmPublishers?.length === 0)
|
||||
) {
|
||||
return []
|
||||
}
|
||||
|
||||
// const compJob: ComputeJobMetaData = {
|
||||
// ...job,
|
||||
// assetName: ddo.metadata.name,
|
||||
// assetDtSymbol: ddo.dataTokenInfo.symbol,
|
||||
// networkId: ddo.chainId
|
||||
// }
|
||||
// computeJobs.push(compJob)
|
||||
// }
|
||||
// } catch (err) {
|
||||
// LoggerInstance.error(err.message)
|
||||
// }
|
||||
// }
|
||||
const gueryResults = await queryMetadata(
|
||||
getQueryString(
|
||||
computeService.compute.publisherTrustedAlgorithms,
|
||||
computeService.compute.publisherTrustedAlgorithmPublishers,
|
||||
asset.chainId
|
||||
),
|
||||
token
|
||||
)
|
||||
|
||||
// return computeJobs
|
||||
// }
|
||||
const algorithms: Asset[] = gueryResults?.results
|
||||
return algorithms
|
||||
}
|
||||
|
||||
// function getDtList(data: TokenOrder[]): string[] {
|
||||
// const dtList = []
|
||||
export async function getAlgorithmAssetSelectionList(
|
||||
asset: Asset,
|
||||
algorithms: Asset[]
|
||||
): Promise<AssetSelectionAsset[]> {
|
||||
const computeService: Service = getServiceByName(asset, 'compute')
|
||||
let algorithmSelectionList: AssetSelectionAsset[]
|
||||
if (!computeService.compute) {
|
||||
algorithmSelectionList = []
|
||||
} else {
|
||||
algorithmSelectionList = await transformAssetToAssetSelection(
|
||||
computeService?.serviceEndpoint,
|
||||
algorithms,
|
||||
[]
|
||||
)
|
||||
}
|
||||
return algorithmSelectionList
|
||||
}
|
||||
|
||||
// for (let i = 0; i < data.length; i++) {
|
||||
// dtList.push(data[i].datatokenId.address)
|
||||
// }
|
||||
async function getJobs(
|
||||
providerUrls: string[],
|
||||
accountId: string,
|
||||
assets: Asset[]
|
||||
): Promise<ComputeJobMetaData[]> {
|
||||
const computeJobs: ComputeJobMetaData[] = []
|
||||
// commented loop since we decide how to filter jobs
|
||||
// for await (const providerUrl of providerUrls) {
|
||||
try {
|
||||
const providerComputeJobs = (await ProviderInstance.computeStatus(
|
||||
providerUrls[0],
|
||||
accountId
|
||||
)) as ComputeJob[]
|
||||
|
||||
// return dtList
|
||||
// }
|
||||
if (providerComputeJobs) {
|
||||
providerComputeJobs.sort((a, b) => {
|
||||
if (a.dateCreated > b.dateCreated) {
|
||||
return -1
|
||||
}
|
||||
if (a.dateCreated < b.dateCreated) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
|
||||
// export async function getComputeJobs(
|
||||
// chainIds: number[],
|
||||
// account: Account,
|
||||
// ddo?: Asset,
|
||||
// token?: CancelToken
|
||||
// ): Promise<ComputeResults> {
|
||||
// const assetDTAddress = ddo?.dataTokenInfo?.address
|
||||
// let computeResult: ComputeResults = {
|
||||
// computeJobs: [],
|
||||
// isLoaded: false
|
||||
// }
|
||||
// let isLoading = true
|
||||
// const variables = assetDTAddress
|
||||
// ? {
|
||||
// user: account?.getId().toLowerCase(),
|
||||
// datatokenAddress: assetDTAddress.toLowerCase()
|
||||
// }
|
||||
// : {
|
||||
// user: account?.getId().toLowerCase()
|
||||
providerComputeJobs.forEach((job) => {
|
||||
const did = job.inputDID[0]
|
||||
const asset = assets.filter((x) => x.id === did)[0]
|
||||
if (asset) {
|
||||
const compJob: ComputeJobMetaData = {
|
||||
...job,
|
||||
assetName: asset.metadata.name,
|
||||
assetDtSymbol: asset.datatokens[0].symbol,
|
||||
networkId: asset.chainId
|
||||
}
|
||||
computeJobs.push(compJob)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
LoggerInstance.error(err.message)
|
||||
}
|
||||
// }
|
||||
return computeJobs
|
||||
}
|
||||
export async function getComputeJobs(
|
||||
chainIds: number[],
|
||||
accountId: string,
|
||||
asset?: AssetExtended,
|
||||
cancelToken?: CancelToken
|
||||
): Promise<ComputeResults> {
|
||||
if (!accountId) return
|
||||
const assetDTAddress = asset?.datatokens[0]?.address
|
||||
const computeResult: ComputeResults = {
|
||||
computeJobs: [],
|
||||
isLoaded: false
|
||||
}
|
||||
const variables = assetDTAddress
|
||||
? {
|
||||
user: accountId.toLowerCase(),
|
||||
datatokenAddress: assetDTAddress.toLowerCase()
|
||||
}
|
||||
: {
|
||||
user: accountId.toLowerCase()
|
||||
}
|
||||
|
||||
// const result = await fetchDataForMultipleChains(
|
||||
// assetDTAddress ? getComputeOrdersByDatatokenAddress : getComputeOrders,
|
||||
// variables,
|
||||
// assetDTAddress ? [ddo?.chainId] : chainIds
|
||||
// )
|
||||
// let data: TokenOrder[] = []
|
||||
// for (let i = 0; i < result.length; i++) {
|
||||
// if (!result[i]?.tokenOrders || result[i].tokenOrders.length === 0) continue
|
||||
// result[i]?.tokenOrders.forEach((tokenOrder: TokenOrder) => {
|
||||
// data.push(tokenOrder)
|
||||
// })
|
||||
// }
|
||||
// if (!ocean || !account || !data) return
|
||||
const results = await fetchDataForMultipleChains(
|
||||
assetDTAddress ? getComputeOrdersByDatatokenAddress : getComputeOrders,
|
||||
variables,
|
||||
assetDTAddress ? [asset?.chainId] : chainIds
|
||||
)
|
||||
|
||||
// if (data.length === 0) {
|
||||
// return computeResult
|
||||
// }
|
||||
let tokenOrders: TokenOrder[] = []
|
||||
results.map((result) =>
|
||||
result.orders.forEach((tokenOrder: TokenOrder) =>
|
||||
tokenOrders.push(tokenOrder)
|
||||
)
|
||||
)
|
||||
if (tokenOrders.length === 0) {
|
||||
computeResult.isLoaded = true
|
||||
return computeResult
|
||||
}
|
||||
|
||||
// data = data.sort((a, b) => b.timestamp - a.timestamp)
|
||||
// const queryDtList = getDtList(data)
|
||||
// if (!queryDtList) return
|
||||
tokenOrders = tokenOrders.sort(
|
||||
(a, b) => b.createdTimestamp - a.createdTimestamp
|
||||
)
|
||||
|
||||
// const assets = await getAssetMetadata(queryDtList, token, chainIds)
|
||||
// const serviceEndpoints = getServiceEndpoints(data, assets)
|
||||
// const providers: Provider[] = await getProviders(
|
||||
// serviceEndpoints,
|
||||
// config,
|
||||
// ocean
|
||||
// )
|
||||
// const computeJobs = await getJobs(providers, account, assets)
|
||||
// isLoading = false
|
||||
// computeResult = {
|
||||
// computeJobs: computeJobs,
|
||||
// isLoaded: isLoading
|
||||
// }
|
||||
const datatokenAddressList = tokenOrders.map(
|
||||
(tokenOrder: TokenOrder) => tokenOrder.datatoken.address
|
||||
)
|
||||
if (!datatokenAddressList) return
|
||||
|
||||
// return computeResult
|
||||
// }
|
||||
const assets = await getAssetMetadata(
|
||||
datatokenAddressList,
|
||||
cancelToken,
|
||||
chainIds
|
||||
)
|
||||
|
||||
const providerUrls: string[] = []
|
||||
assets.forEach((asset: Asset) =>
|
||||
providerUrls.push(asset.services[0].serviceEndpoint)
|
||||
)
|
||||
|
||||
computeResult.computeJobs = await getJobs(providerUrls, accountId, assets)
|
||||
computeResult.isLoaded = true
|
||||
|
||||
return computeResult
|
||||
}
|
||||
|
||||
export async function createTrustedAlgorithmList(
|
||||
selectedAlgorithms: string[], // list of DIDs,
|
||||
@ -282,10 +338,16 @@ export async function createTrustedAlgorithmList(
|
||||
)
|
||||
|
||||
for (const selectedAlgorithm of selectedAssets) {
|
||||
const sanitizedAlgorithmContainer = {
|
||||
entrypoint: selectedAlgorithm.metadata.algorithm.container.entrypoint,
|
||||
image: selectedAlgorithm.metadata.algorithm.container.image,
|
||||
tag: selectedAlgorithm.metadata.algorithm.container.tag,
|
||||
checksum: selectedAlgorithm.metadata.algorithm.container.checksum
|
||||
}
|
||||
const trustedAlgorithm = {
|
||||
did: selectedAlgorithm.id,
|
||||
containerSectionChecksum: getHash(
|
||||
JSON.stringify(selectedAlgorithm.metadata.algorithm.container)
|
||||
JSON.stringify(sanitizedAlgorithmContainer)
|
||||
),
|
||||
filesChecksum: getHash(selectedAlgorithm.services[0].files)
|
||||
}
|
||||
@ -315,3 +377,31 @@ export async function transformComputeFormToServiceComputeOptions(
|
||||
|
||||
return privacy
|
||||
}
|
||||
|
||||
export async function checkComputeResourcesValidity(
|
||||
asset: Asset,
|
||||
accountId: string,
|
||||
computeEnvMaxJobDuration: number,
|
||||
datasetTimeout?: number,
|
||||
algorithmTimeout?: number,
|
||||
cancelToken?: CancelToken
|
||||
): Promise<boolean> {
|
||||
const jobs = await getComputeJobs(
|
||||
[asset?.chainId],
|
||||
accountId,
|
||||
asset,
|
||||
cancelToken
|
||||
)
|
||||
if (jobs.computeJobs.length <= 0) return false
|
||||
const inputValues = []
|
||||
computeEnvMaxJobDuration && inputValues.push(computeEnvMaxJobDuration * 60)
|
||||
datasetTimeout && inputValues.push(datasetTimeout)
|
||||
algorithmTimeout && inputValues.push(algorithmTimeout)
|
||||
const minValue = Math.min(...inputValues)
|
||||
const jobStartDate = new Date(
|
||||
parseInt(jobs.computeJobs[0].dateCreated) * 1000
|
||||
)
|
||||
jobStartDate.setMinutes(jobStartDate.getMinutes() + Math.floor(minValue / 60))
|
||||
const currentTime = new Date().getTime() / 1000
|
||||
return Math.floor(jobStartDate.getTime() / 1000) > currentTime
|
||||
}
|
||||
|
@ -10,6 +10,13 @@ export function getServiceByName(
|
||||
return service
|
||||
}
|
||||
|
||||
export function getServiceById(ddo: Asset | DDO, serviceId: string): Service {
|
||||
if (!ddo) return
|
||||
|
||||
const service = ddo.services.find((s) => s.id === serviceId)
|
||||
return service
|
||||
}
|
||||
|
||||
export function mapTimeoutStringToSeconds(timeout: string): number {
|
||||
switch (timeout) {
|
||||
case 'Forever':
|
||||
|
@ -11,9 +11,16 @@ export function getOrderFeedback(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: customize for compute
|
||||
export const computeFeedback: { [key in number]: string } = {
|
||||
0: 'Ordering asset...',
|
||||
1: 'Transfering datatoken.',
|
||||
2: 'Access granted. Starting job...'
|
||||
export function getComputeFeedback(
|
||||
baseTokenSymbol?: string,
|
||||
datatokenSymbol?: string,
|
||||
assetType?: string
|
||||
): { [key in number]: string } {
|
||||
return {
|
||||
0: `Setting price and fees for ${assetType}`,
|
||||
1: `Approving ${datatokenSymbol} and ordering ${assetType} `,
|
||||
2: `Approving ${baseTokenSymbol} and ordering ${assetType}`,
|
||||
3: `Ordering ${assetType}`,
|
||||
4: 'Generating signature. Starting compute job ...'
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { FixedRateExchange, PriceAndFees } from '@oceanprotocol/lib'
|
||||
import { AccessDetails } from 'src/@types/Price'
|
||||
import Web3 from 'web3'
|
||||
import { getOceanConfig } from './ocean'
|
||||
import { consumeMarketPoolSwapFee } from '../../app.config'
|
||||
import { getDummyWeb3 } from './web3'
|
||||
|
||||
/**
|
||||
@ -28,7 +29,8 @@ export async function getFixedBuyPrice(
|
||||
const fixed = new FixedRateExchange(web3, config.fixedRateExchangeAddress)
|
||||
const estimatedPrice = await fixed.calcBaseInGivenOutDT(
|
||||
accessDetails.addressOrId,
|
||||
'1'
|
||||
'1',
|
||||
consumeMarketPoolSwapFee
|
||||
)
|
||||
return estimatedPrice
|
||||
}
|
||||
|
@ -112,21 +112,6 @@ export async function setNftMetadata(
|
||||
// theoretically used by aquarius or provider, not implemented yet, will remain hardcoded
|
||||
const flags = '0x2'
|
||||
|
||||
const estGasSetMetadata = await nft.estGasSetMetadata(
|
||||
asset.nftAddress,
|
||||
accountId,
|
||||
0,
|
||||
asset.services[0].serviceEndpoint,
|
||||
'',
|
||||
flags,
|
||||
encryptedDdo,
|
||||
'0x' + metadataHash,
|
||||
[]
|
||||
)
|
||||
LoggerInstance.log(
|
||||
'[setNftMetadata] est Gas set metadata --',
|
||||
estGasSetMetadata
|
||||
)
|
||||
const setMetadataTx = await nft.setMetadata(
|
||||
asset.nftAddress,
|
||||
accountId,
|
||||
|
@ -1,8 +1,12 @@
|
||||
import {
|
||||
approve,
|
||||
approveWei,
|
||||
Datatoken,
|
||||
FreOrderParams,
|
||||
LoggerInstance,
|
||||
OrderParams,
|
||||
ProviderComputeInitialize,
|
||||
ProviderFees,
|
||||
ProviderInstance
|
||||
} from '@oceanprotocol/lib'
|
||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||
@ -15,35 +19,44 @@ 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
|
||||
* @param web3
|
||||
* @param asset
|
||||
* @param orderPriceAndFees
|
||||
* @param accountId
|
||||
* @param providerFees
|
||||
* @param computeConsumerAddress
|
||||
* @returns {TransactionReceipt} receipt of the order
|
||||
*/
|
||||
export async function order(
|
||||
web3: Web3,
|
||||
asset: AssetExtended,
|
||||
orderPriceAndFees: OrderPriceAndFees,
|
||||
accountId: string
|
||||
accountId: string,
|
||||
providerFees?: ProviderFees,
|
||||
computeConsumerAddress?: string
|
||||
): Promise<TransactionReceipt> {
|
||||
const datatoken = new Datatoken(web3)
|
||||
const config = getOceanConfig(asset.chainId)
|
||||
|
||||
const initializeData = await ProviderInstance.initialize(
|
||||
const initializeData =
|
||||
!providerFees &&
|
||||
(await ProviderInstance.initialize(
|
||||
asset.id,
|
||||
asset.services[0].id,
|
||||
0,
|
||||
accountId,
|
||||
asset.services[0].serviceEndpoint
|
||||
)
|
||||
))
|
||||
|
||||
const orderParams = {
|
||||
consumer: accountId,
|
||||
consumer: computeConsumerAddress || accountId,
|
||||
serviceIndex: 0,
|
||||
_providerFee: initializeData.providerFee,
|
||||
_providerFee: providerFees || initializeData.providerFee,
|
||||
_consumeMarketFee: {
|
||||
consumeMarketFeeAddress: marketFeeAddress,
|
||||
consumeMarketFeeAmount: consumeMarketOrderFee,
|
||||
@ -51,7 +64,6 @@ export async function order(
|
||||
}
|
||||
} as OrderParams
|
||||
|
||||
// TODO: we need to approve provider fee
|
||||
switch (asset.accessDetails?.type) {
|
||||
case 'fixed': {
|
||||
// this assumes all fees are in ocean
|
||||
@ -87,9 +99,9 @@ export async function order(
|
||||
const tx = await datatoken.startOrder(
|
||||
asset.accessDetails.datatoken.address,
|
||||
accountId,
|
||||
accountId,
|
||||
computeConsumerAddress || accountId,
|
||||
0,
|
||||
initializeData.providerFee
|
||||
providerFees || initializeData.providerFee
|
||||
)
|
||||
return tx
|
||||
}
|
||||
@ -105,3 +117,176 @@ export async function order(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called when having a valid order, but with expired provider access, requires approval of the provider fee
|
||||
* @param web3
|
||||
* @param asset
|
||||
* @param accountId
|
||||
* @param validOrderTx
|
||||
* @param providerFees
|
||||
* @returns {TransactionReceipt} receipt of the order
|
||||
*/
|
||||
export async function reuseOrder(
|
||||
web3: Web3,
|
||||
asset: AssetExtended,
|
||||
accountId: string,
|
||||
validOrderTx: string,
|
||||
providerFees?: ProviderFees
|
||||
): Promise<TransactionReceipt> {
|
||||
const datatoken = new Datatoken(web3)
|
||||
const initializeData =
|
||||
!providerFees &&
|
||||
(await ProviderInstance.initialize(
|
||||
asset.id,
|
||||
asset.services[0].id,
|
||||
0,
|
||||
accountId,
|
||||
asset.services[0].serviceEndpoint
|
||||
))
|
||||
|
||||
const tx = await datatoken.reuseOrder(
|
||||
asset.accessDetails.datatoken.address,
|
||||
accountId,
|
||||
validOrderTx,
|
||||
providerFees || initializeData.providerFee
|
||||
)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
async function approveProviderFee(
|
||||
asset: AssetExtended,
|
||||
accountId: string,
|
||||
web3: Web3,
|
||||
providerFeeAmount: string
|
||||
): Promise<string> {
|
||||
const baseToken =
|
||||
asset?.accessDetails?.type === 'free'
|
||||
? getOceanConfig(asset.chainId).oceanTokenAddress
|
||||
: asset?.accessDetails?.baseToken?.address
|
||||
const txApproveWei = await approveWei(
|
||||
web3,
|
||||
accountId,
|
||||
baseToken,
|
||||
asset?.accessDetails?.datatoken?.address,
|
||||
providerFeeAmount
|
||||
)
|
||||
return txApproveWei as string // thanks ocean.js
|
||||
}
|
||||
|
||||
async function startOrder(
|
||||
web3: Web3,
|
||||
asset: AssetExtended,
|
||||
orderPriceAndFees: OrderPriceAndFees,
|
||||
accountId: string,
|
||||
hasDatatoken: boolean,
|
||||
initializeData: ProviderComputeInitialize,
|
||||
computeConsumerAddress?: string
|
||||
): Promise<TransactionReceipt> {
|
||||
if (!hasDatatoken && asset?.accessDetails.type === 'dynamic') {
|
||||
const poolTx = await buyDtFromPool(asset?.accessDetails, accountId, web3)
|
||||
LoggerInstance.log('[compute] Bought datatoken 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
|
||||
)
|
||||
LoggerInstance.log('[compute] Asset ordered:', tx)
|
||||
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 orderPriceAndFees
|
||||
* @param accountId
|
||||
* @param hasDatatoken
|
||||
* @param initializeData
|
||||
* @param computeConsumerAddress
|
||||
* @returns {Promise<string>} tx id
|
||||
*/
|
||||
export async function handleComputeOrder(
|
||||
web3: Web3,
|
||||
asset: AssetExtended,
|
||||
orderPriceAndFees: OrderPriceAndFees,
|
||||
accountId: string,
|
||||
hasDatatoken: boolean,
|
||||
initializeData: ProviderComputeInitialize,
|
||||
computeConsumerAddress?: string
|
||||
): Promise<string> {
|
||||
LoggerInstance.log(
|
||||
'[compute] Handle compute order for asset type: ',
|
||||
asset.metadata.type
|
||||
)
|
||||
LoggerInstance.log('[compute] Using initializeData: ', initializeData)
|
||||
|
||||
try {
|
||||
// Return early when valid order is found, and no provider fees
|
||||
// are to be paid
|
||||
if (initializeData?.validOrder && !initializeData.providerFee) {
|
||||
LoggerInstance.log(
|
||||
'[compute] Has valid order: ',
|
||||
initializeData.validOrder
|
||||
)
|
||||
return asset?.accessDetails?.validOrderTx
|
||||
}
|
||||
|
||||
// Approve potential Provider fee amount first
|
||||
if (initializeData?.providerFee?.providerFeeAmount !== '0') {
|
||||
const txApproveProvider = await approveProviderFee(
|
||||
asset,
|
||||
accountId,
|
||||
web3,
|
||||
initializeData.providerFee.providerFeeAmount
|
||||
)
|
||||
|
||||
if (!txApproveProvider)
|
||||
throw new Error('Failed to approve provider fees!')
|
||||
|
||||
LoggerInstance.log('[compute] Approved provider fees:', txApproveProvider)
|
||||
}
|
||||
|
||||
if (initializeData?.validOrder) {
|
||||
LoggerInstance.log('[compute] Calling reuseOrder ...', initializeData)
|
||||
const txReuseOrder = await reuseOrder(
|
||||
web3,
|
||||
asset,
|
||||
accountId,
|
||||
initializeData.validOrder,
|
||||
initializeData.providerFee
|
||||
)
|
||||
if (!txReuseOrder) throw new Error('Failed to reuse order!')
|
||||
LoggerInstance.log('[compute] Reused order:', txReuseOrder)
|
||||
return txReuseOrder?.transactionHash
|
||||
}
|
||||
|
||||
LoggerInstance.log('[compute] Calling order ...', initializeData)
|
||||
const txStartOrder = await startOrder(
|
||||
web3,
|
||||
asset,
|
||||
orderPriceAndFees,
|
||||
accountId,
|
||||
hasDatatoken,
|
||||
initializeData,
|
||||
computeConsumerAddress
|
||||
)
|
||||
LoggerInstance.log('[compute] Order succeeded', txStartOrder)
|
||||
return txStartOrder?.transactionHash
|
||||
} catch (error) {
|
||||
toast.error(error.message)
|
||||
LoggerInstance.error(`[compute] ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,54 @@
|
||||
import {
|
||||
ComputeAlgorithm,
|
||||
ComputeAsset,
|
||||
ComputeEnvironment,
|
||||
downloadFileBrowser,
|
||||
FileInfo,
|
||||
LoggerInstance,
|
||||
ProviderComputeInitializeResults,
|
||||
ProviderInstance
|
||||
} from '@oceanprotocol/lib'
|
||||
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<ProviderComputeInitializeResults> {
|
||||
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
|
||||
)
|
||||
|
||||
try {
|
||||
return await ProviderInstance.initializeCompute(
|
||||
[computeAsset],
|
||||
computeAlgo,
|
||||
computeEnv?.id,
|
||||
validUntil,
|
||||
dataset.services[0].serviceEndpoint,
|
||||
accountId
|
||||
)
|
||||
} catch (error) {
|
||||
LoggerInstance.error(`Error initializing provider for the compute job!`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Why do we have these one line functions ?!?!?!
|
||||
export async function getEncryptedFiles(
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData'
|
||||
import { UserSalesQuery as UsersSalesList } from '../@types/subgraph/UserSalesQuery'
|
||||
import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery'
|
||||
import { calcSingleOutGivenPoolIn, getLiquidityByShares } from './pool'
|
||||
import { calcSingleOutGivenPoolIn } from './pool'
|
||||
import Decimal from 'decimal.js'
|
||||
import { MAX_DECIMALS } from './constants'
|
||||
|
||||
|
@ -3,6 +3,11 @@
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
}
|
||||
|
||||
.actionsCenter {
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.help {
|
||||
font-size: var(--font-size-mini);
|
||||
color: var(--color-secondary);
|
||||
|
@ -28,7 +28,8 @@ interface ButtonBuyProps {
|
||||
type?: 'submit'
|
||||
priceType?: string
|
||||
algorithmPriceType?: string
|
||||
algorithmConsumableStatus?: number
|
||||
isAlgorithmConsumable?: boolean
|
||||
hasProviderFee?: boolean
|
||||
}
|
||||
|
||||
// TODO: we need to take a look at these messages
|
||||
@ -75,7 +76,8 @@ function getComputeAssetHelpText(
|
||||
dtBalanceSelectedComputeAsset?: string,
|
||||
selectedComputeAssettLowPoolLiquidity?: boolean,
|
||||
selectedComputeAssetType?: string,
|
||||
algorithmConsumableStatus?: number
|
||||
isAlgorithmConsumable?: boolean,
|
||||
hasProviderFee?: boolean
|
||||
) {
|
||||
const computeAssetHelpText = getConsumeHelpText(
|
||||
dtBalance,
|
||||
@ -90,14 +92,9 @@ function getComputeAssetHelpText(
|
||||
)
|
||||
const computeAlgoHelpText =
|
||||
(!dtSymbolSelectedComputeAsset && !dtBalanceSelectedComputeAsset) ||
|
||||
isConsumable === false
|
||||
isConsumable === false ||
|
||||
isAlgorithmConsumable === false
|
||||
? ''
|
||||
: algorithmConsumableStatus === 1
|
||||
? 'The selected algorithm has been temporarily disabled by the publisher, please try again later.'
|
||||
: algorithmConsumableStatus === 2
|
||||
? 'Access denied, your wallet address is not found on the selected algorithm allow list.'
|
||||
: algorithmConsumableStatus === 3
|
||||
? 'Access denied, your wallet address is found on the selected algorithm deny list.'
|
||||
: hasPreviousOrderSelectedComputeAsset
|
||||
? `You already bought the selected ${selectedComputeAssetType}, allowing you to use it without paying again.`
|
||||
: hasDatatokenSelectedComputeAsset
|
||||
@ -107,11 +104,14 @@ function getComputeAssetHelpText(
|
||||
: isBalanceSufficient === false
|
||||
? ''
|
||||
: `Additionally, you will buy 1 ${dtSymbolSelectedComputeAsset} for the ${selectedComputeAssetType} and spend it back to its publisher and pool.`
|
||||
const providerFeeHelpText = hasProviderFee
|
||||
? 'In order to start the job you also need to pay the fees for renting the c2d resources.'
|
||||
: 'C2D resources required to start the job are available, no payment required for those fees.'
|
||||
const computeHelpText = selectedComputeAssettLowPoolLiquidity
|
||||
? computeAlgoHelpText
|
||||
: lowPoolLiquidity
|
||||
? computeAssetHelpText
|
||||
: `${computeAssetHelpText} ${computeAlgoHelpText}`
|
||||
: `${computeAssetHelpText} ${computeAlgoHelpText} ${providerFeeHelpText}`
|
||||
return computeHelpText
|
||||
}
|
||||
|
||||
@ -140,7 +140,8 @@ export default function ButtonBuy({
|
||||
type,
|
||||
priceType,
|
||||
algorithmPriceType,
|
||||
algorithmConsumableStatus
|
||||
isAlgorithmConsumable,
|
||||
hasProviderFee
|
||||
}: ButtonBuyProps): ReactElement {
|
||||
const buttonText =
|
||||
action === 'download'
|
||||
@ -149,7 +150,9 @@ export default function ButtonBuy({
|
||||
: priceType === 'free'
|
||||
? 'Get'
|
||||
: `Buy ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}`
|
||||
: hasPreviousOrder && hasPreviousOrderSelectedComputeAsset
|
||||
: hasPreviousOrder &&
|
||||
hasPreviousOrderSelectedComputeAsset &&
|
||||
!hasProviderFee
|
||||
? 'Start Compute Job'
|
||||
: priceType === 'free' && algorithmPriceType === 'free'
|
||||
? 'Order Compute Job'
|
||||
@ -166,6 +169,7 @@ export default function ButtonBuy({
|
||||
type={type}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={action === 'compute' ? styles.actionsCenter : ''}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
@ -198,7 +202,8 @@ export default function ButtonBuy({
|
||||
dtBalanceSelectedComputeAsset,
|
||||
selectedComputeAssetLowPoolLiquidity,
|
||||
selectedComputeAssetType,
|
||||
algorithmConsumableStatus
|
||||
isAlgorithmConsumable,
|
||||
hasProviderFee
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
@ -12,6 +12,7 @@ export default function Blockies({
|
||||
className
|
||||
}: BlockiesProps): ReactElement {
|
||||
if (!accountId) return null
|
||||
|
||||
const blockies = toDataUrl(accountId)
|
||||
|
||||
return (
|
||||
|
@ -5,7 +5,6 @@ import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
|
||||
import AssetComputeList from '@shared/AssetList/AssetComputeList'
|
||||
import { useCancelToken } from '@hooks/useCancelToken'
|
||||
import { getServiceByName } from '@utils/ddo'
|
||||
import { Asset } from '@oceanprotocol/lib'
|
||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||
|
||||
export default function AlgorithmDatasetsListForCompute({
|
||||
@ -15,9 +14,9 @@ export default function AlgorithmDatasetsListForCompute({
|
||||
asset: AssetExtended
|
||||
algorithmDid: string
|
||||
}): ReactElement {
|
||||
const newCancelToken = useCancelToken()
|
||||
const [datasetsForCompute, setDatasetsForCompute] =
|
||||
useState<AssetSelectionAsset[]>()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
useEffect(() => {
|
||||
if (!asset) return
|
||||
@ -37,7 +36,7 @@ export default function AlgorithmDatasetsListForCompute({
|
||||
setDatasetsForCompute(datasets)
|
||||
}
|
||||
asset.metadata.type === 'algorithm' && getDatasetsAllowedForCompute()
|
||||
}, [asset?.metadata?.type])
|
||||
}, [asset, algorithmDid, newCancelToken])
|
||||
|
||||
return (
|
||||
<div className={styles.datasetsContainer}>
|
||||
|
@ -10,13 +10,18 @@ import { useAsset } from '@context/Asset'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
import content from '../../../../../content/pages/startComputeDataset.json'
|
||||
import { Asset } from '@oceanprotocol/lib'
|
||||
import { AccessDetails } from 'src/@types/Price'
|
||||
import { OrderPriceAndFees } from 'src/@types/Price'
|
||||
import { getAccessDetails } from '@utils/accessDetailsAndPricing'
|
||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||
import Decimal from 'decimal.js'
|
||||
import { MAX_DECIMALS } from '@utils/constants'
|
||||
import { useMarketMetadata } from '@context/MarketMetadata'
|
||||
import Alert from '@shared/atoms/Alert'
|
||||
|
||||
export default function FormStartCompute({
|
||||
algorithms,
|
||||
ddoListAlgorithms,
|
||||
selectedAlgorithmAsset,
|
||||
setSelectedAlgorithm,
|
||||
isLoading,
|
||||
isComputeButtonDisabled,
|
||||
@ -35,13 +40,17 @@ export default function FormStartCompute({
|
||||
selectedComputeAssetType,
|
||||
selectedComputeAssetTimeout,
|
||||
stepText,
|
||||
algorithmConsumeDetails,
|
||||
isConsumable,
|
||||
consumableFeedback
|
||||
consumableFeedback,
|
||||
datasetOrderPriceAndFees,
|
||||
algoOrderPriceAndFees,
|
||||
providerFeeAmount,
|
||||
validUntil
|
||||
}: {
|
||||
algorithms: AssetSelectionAsset[]
|
||||
ddoListAlgorithms: Asset[]
|
||||
setSelectedAlgorithm: React.Dispatch<React.SetStateAction<Asset>>
|
||||
selectedAlgorithmAsset: AssetExtended
|
||||
setSelectedAlgorithm: React.Dispatch<React.SetStateAction<AssetExtended>>
|
||||
isLoading: boolean
|
||||
isComputeButtonDisabled: boolean
|
||||
hasPreviousOrder: boolean
|
||||
@ -59,19 +68,27 @@ export default function FormStartCompute({
|
||||
selectedComputeAssetType?: string
|
||||
selectedComputeAssetTimeout?: string
|
||||
stepText: string
|
||||
algorithmConsumeDetails: AccessDetails
|
||||
isConsumable: boolean
|
||||
consumableFeedback: string
|
||||
datasetOrderPriceAndFees?: OrderPriceAndFees
|
||||
algoOrderPriceAndFees?: OrderPriceAndFees
|
||||
providerFeeAmount?: string
|
||||
validUntil?: string
|
||||
}): ReactElement {
|
||||
const { siteContent } = useMarketMetadata()
|
||||
const { accountId, balance } = useWeb3()
|
||||
const { isValid, values }: FormikContextType<{ algorithm: string }> =
|
||||
useFormikContext()
|
||||
const { asset, isAssetNetwork } = useAsset()
|
||||
const [totalPrice, setTotalPrice] = useState(asset?.accessDetails?.price)
|
||||
|
||||
const [totalPrice, setTotalPrice] = useState('0')
|
||||
const [datasetOrderPrice, setDatasetOrderPrice] = useState(
|
||||
asset?.accessDetails?.price
|
||||
)
|
||||
const [algoOrderPrice, setAlgoOrderPrice] = useState(
|
||||
selectedAlgorithmAsset?.accessDetails?.price
|
||||
)
|
||||
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>(false)
|
||||
const { accountId, balance } = useWeb3()
|
||||
const [algorithmConsumableStatus, setAlgorithmConsumableStatus] =
|
||||
useState<number>()
|
||||
|
||||
function getAlgorithmAsset(algorithmId: string): Asset {
|
||||
let assetDdo = null
|
||||
@ -82,50 +99,79 @@ export default function FormStartCompute({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!values.algorithm) return
|
||||
const algorithmDDO = getAlgorithmAsset(values.algorithm)
|
||||
setSelectedAlgorithm(algorithmDDO)
|
||||
if (!values.algorithm || !accountId || !isConsumable) return
|
||||
|
||||
if (!accountId || !isConsumable) return
|
||||
async function checkIsConsumable() {
|
||||
// const consumable = await ocean.assets.isConsumable(
|
||||
// algorithmDDO as any,
|
||||
// accountId.toLowerCase()
|
||||
// )
|
||||
// if (consumable) setAlgorithmConsumableStatus(consumable.status)
|
||||
async function fetchAlgorithmAssetExtended() {
|
||||
const algorithmAsset = getAlgorithmAsset(values.algorithm)
|
||||
const accessDetails = await getAccessDetails(
|
||||
algorithmAsset.chainId,
|
||||
algorithmAsset.services[0].datatokenAddress,
|
||||
algorithmAsset.services[0].timeout,
|
||||
accountId
|
||||
)
|
||||
const extendedAlgoAsset: AssetExtended = {
|
||||
...algorithmAsset,
|
||||
accessDetails
|
||||
}
|
||||
checkIsConsumable()
|
||||
setSelectedAlgorithm(extendedAlgoAsset)
|
||||
}
|
||||
fetchAlgorithmAssetExtended()
|
||||
}, [values.algorithm, accountId, isConsumable])
|
||||
|
||||
//
|
||||
// Set price for calculation output
|
||||
//
|
||||
useEffect(() => {
|
||||
if (!asset?.accessDetails || !algorithmConsumeDetails) return
|
||||
if (!asset?.accessDetails || !selectedAlgorithmAsset?.accessDetails) return
|
||||
|
||||
setDatasetOrderPrice(
|
||||
datasetOrderPriceAndFees?.price || asset.accessDetails.price
|
||||
)
|
||||
setAlgoOrderPrice(
|
||||
algoOrderPriceAndFees?.price ||
|
||||
selectedAlgorithmAsset?.accessDetails.price
|
||||
)
|
||||
const priceDataset =
|
||||
hasPreviousOrder || hasDatatoken ? 0 : Number(asset.accessDetails.price)
|
||||
hasPreviousOrder || hasDatatoken
|
||||
? new Decimal(0)
|
||||
: new Decimal(
|
||||
datasetOrderPriceAndFees?.price || asset.accessDetails.price
|
||||
).toDecimalPlaces(MAX_DECIMALS)
|
||||
const priceAlgo =
|
||||
hasPreviousOrderSelectedComputeAsset || hasDatatokenSelectedComputeAsset
|
||||
? 0
|
||||
: Number(algorithmConsumeDetails.price)
|
||||
|
||||
setTotalPrice((priceDataset + priceAlgo).toString())
|
||||
? new Decimal(0)
|
||||
: new Decimal(
|
||||
algoOrderPriceAndFees?.price ||
|
||||
selectedAlgorithmAsset.accessDetails.price
|
||||
).toDecimalPlaces(MAX_DECIMALS)
|
||||
const providerFees = providerFeeAmount
|
||||
? new Decimal(providerFeeAmount).toDecimalPlaces(MAX_DECIMALS)
|
||||
: new Decimal(0)
|
||||
const totalPrice = priceDataset
|
||||
.plus(priceAlgo)
|
||||
.plus(providerFees)
|
||||
.toDecimalPlaces(MAX_DECIMALS)
|
||||
.toString()
|
||||
setTotalPrice(totalPrice)
|
||||
}, [
|
||||
asset?.accessDetails,
|
||||
algorithmConsumeDetails,
|
||||
selectedAlgorithmAsset?.accessDetails,
|
||||
hasPreviousOrder,
|
||||
hasDatatoken,
|
||||
hasPreviousOrderSelectedComputeAsset,
|
||||
hasDatatokenSelectedComputeAsset
|
||||
hasDatatokenSelectedComputeAsset,
|
||||
datasetOrderPriceAndFees,
|
||||
algoOrderPriceAndFees,
|
||||
providerFeeAmount
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (!totalPrice) return
|
||||
if (!totalPrice || !balance?.ocean || !dtBalance) return
|
||||
|
||||
setIsBalanceSufficient(
|
||||
compareAsBN(balance.ocean, `${totalPrice}`) || Number(dtBalance) >= 1
|
||||
)
|
||||
}, [totalPrice])
|
||||
}, [totalPrice, balance?.ocean, dtBalance])
|
||||
|
||||
return (
|
||||
<Form className={styles.form}>
|
||||
@ -140,6 +186,7 @@ export default function FormStartCompute({
|
||||
{...field}
|
||||
options={algorithms}
|
||||
component={Input}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -152,9 +199,13 @@ export default function FormStartCompute({
|
||||
hasDatatoken={hasDatatoken}
|
||||
selectedComputeAssetTimeout={selectedComputeAssetTimeout}
|
||||
hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset}
|
||||
algorithmConsumeDetails={algorithmConsumeDetails}
|
||||
algorithmConsumeDetails={selectedAlgorithmAsset?.accessDetails}
|
||||
symbol={oceanSymbol}
|
||||
totalPrice={Number.parseFloat(totalPrice)}
|
||||
totalPrice={totalPrice}
|
||||
datasetOrderPrice={datasetOrderPrice}
|
||||
algoOrderPrice={algoOrderPrice}
|
||||
providerFeeAmount={providerFeeAmount}
|
||||
validUntil={validUntil}
|
||||
/>
|
||||
|
||||
<ButtonBuy
|
||||
@ -164,7 +215,7 @@ export default function FormStartCompute({
|
||||
!isValid ||
|
||||
!isBalanceSufficient ||
|
||||
!isAssetNetwork ||
|
||||
algorithmConsumableStatus > 0
|
||||
!selectedAlgorithmAsset?.accessDetails?.isPurchasable
|
||||
}
|
||||
hasPreviousOrder={hasPreviousOrder}
|
||||
hasDatatoken={hasDatatoken}
|
||||
@ -187,11 +238,14 @@ export default function FormStartCompute({
|
||||
isLoading={isLoading}
|
||||
type="submit"
|
||||
priceType={asset?.accessDetails?.type}
|
||||
algorithmPriceType={algorithmConsumeDetails?.type}
|
||||
algorithmPriceType={selectedAlgorithmAsset?.accessDetails?.type}
|
||||
isBalanceSufficient={isBalanceSufficient}
|
||||
isConsumable={isConsumable}
|
||||
consumableFeedback={consumableFeedback}
|
||||
algorithmConsumableStatus={algorithmConsumableStatus}
|
||||
isAlgorithmConsumable={
|
||||
selectedAlgorithmAsset?.accessDetails?.isPurchasable
|
||||
}
|
||||
hasProviderFee={providerFeeAmount && providerFeeAmount !== '0'}
|
||||
/>
|
||||
</Form>
|
||||
)
|
||||
|
@ -36,8 +36,9 @@
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-top: calc(var(--spacer) / 7);
|
||||
padding-bottom: calc(var(--spacer) / 7);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
display: grid;
|
||||
grid-template-columns: 5% 1fr auto;
|
||||
column-gap: calc(var(--spacer) / 10);
|
||||
}
|
||||
|
||||
.priceRow:last-child {
|
||||
@ -47,8 +48,14 @@
|
||||
|
||||
.sign {
|
||||
display: inline-block;
|
||||
width: 5%;
|
||||
text-align: left;
|
||||
color: var(--color-secondary);
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
|
||||
.type {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
color: var(--color-secondary);
|
||||
font-size: var(--font-size-mini);
|
||||
}
|
||||
|
@ -4,9 +4,11 @@ import PriceUnit from '@shared/Price/PriceUnit'
|
||||
import Tooltip from '@shared/atoms/Tooltip'
|
||||
import styles from './PriceOutput.module.css'
|
||||
import { AccessDetails } from 'src/@types/Price'
|
||||
import { MAX_DECIMALS } from '@utils/constants'
|
||||
import Decimal from 'decimal.js'
|
||||
|
||||
interface PriceOutputProps {
|
||||
totalPrice: number
|
||||
totalPrice: string
|
||||
hasPreviousOrder: boolean
|
||||
hasDatatoken: boolean
|
||||
symbol: string
|
||||
@ -15,6 +17,10 @@ interface PriceOutputProps {
|
||||
hasDatatokenSelectedComputeAsset: boolean
|
||||
algorithmConsumeDetails: AccessDetails
|
||||
selectedComputeAssetTimeout: string
|
||||
datasetOrderPrice?: number
|
||||
algoOrderPrice?: number
|
||||
providerFeeAmount?: string
|
||||
validUntil?: string
|
||||
}
|
||||
|
||||
function Row({
|
||||
@ -23,18 +29,21 @@ function Row({
|
||||
hasDatatoken,
|
||||
symbol,
|
||||
timeout,
|
||||
sign
|
||||
sign,
|
||||
type
|
||||
}: {
|
||||
price: number
|
||||
price: string
|
||||
hasPreviousOrder?: boolean
|
||||
hasDatatoken?: boolean
|
||||
symbol?: string
|
||||
timeout?: string
|
||||
sign?: string
|
||||
type?: string
|
||||
}) {
|
||||
return (
|
||||
<div className={styles.priceRow}>
|
||||
<div className={styles.sign}>{sign}</div>
|
||||
<div className={styles.type}>{type}</div>
|
||||
<div>
|
||||
<PriceUnit
|
||||
price={hasPreviousOrder || hasDatatoken ? '0' : `${price}`}
|
||||
@ -62,7 +71,11 @@ export default function PriceOutput({
|
||||
hasPreviousOrderSelectedComputeAsset,
|
||||
hasDatatokenSelectedComputeAsset,
|
||||
algorithmConsumeDetails,
|
||||
selectedComputeAssetTimeout
|
||||
selectedComputeAssetTimeout,
|
||||
datasetOrderPrice,
|
||||
algoOrderPrice,
|
||||
providerFeeAmount,
|
||||
validUntil
|
||||
}: PriceOutputProps): ReactElement {
|
||||
const { asset } = useAsset()
|
||||
|
||||
@ -76,17 +89,34 @@ export default function PriceOutput({
|
||||
<Row
|
||||
hasPreviousOrder={hasPreviousOrder}
|
||||
hasDatatoken={hasDatatoken}
|
||||
price={Number.parseFloat(asset?.accessDetails?.price)}
|
||||
price={new Decimal(
|
||||
datasetOrderPrice || asset?.accessDetails?.price || 0
|
||||
)
|
||||
.toDecimalPlaces(MAX_DECIMALS)
|
||||
.toString()}
|
||||
timeout={assetTimeout}
|
||||
symbol={symbol}
|
||||
type="DATASET"
|
||||
/>
|
||||
<Row
|
||||
hasPreviousOrder={hasPreviousOrderSelectedComputeAsset}
|
||||
hasDatatoken={hasDatatokenSelectedComputeAsset}
|
||||
price={Number.parseFloat(algorithmConsumeDetails?.price)}
|
||||
price={new Decimal(
|
||||
algoOrderPrice || algorithmConsumeDetails?.price || 0
|
||||
)
|
||||
.toDecimalPlaces(MAX_DECIMALS)
|
||||
.toString()}
|
||||
timeout={selectedComputeAssetTimeout}
|
||||
symbol={symbol}
|
||||
sign="+"
|
||||
type="ALGORITHM"
|
||||
/>
|
||||
<Row
|
||||
price={providerFeeAmount} // initializeCompute.provider fee amount
|
||||
timeout={`${validUntil} seconds`} // valid until value
|
||||
symbol={symbol}
|
||||
sign="+"
|
||||
type="C2D RESOURCES"
|
||||
/>
|
||||
<Row price={totalPrice} symbol={symbol} sign="=" />
|
||||
</div>
|
||||
|
@ -1,232 +1,291 @@
|
||||
import React, { useState, ReactElement, useEffect, useCallback } from 'react'
|
||||
import React, { useState, ReactElement, useEffect } from 'react'
|
||||
import {
|
||||
Asset,
|
||||
DDO,
|
||||
PublisherTrustedAlgorithm,
|
||||
FileInfo
|
||||
FileInfo,
|
||||
Datatoken,
|
||||
ProviderInstance,
|
||||
ComputeAsset,
|
||||
ZERO_ADDRESS,
|
||||
ComputeEnvironment,
|
||||
LoggerInstance,
|
||||
ComputeAlgorithm,
|
||||
ComputeOutput,
|
||||
ProviderComputeInitializeResults,
|
||||
unitsToAmount
|
||||
} 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 { getServiceByName, secondsToString } from '@utils/ddo'
|
||||
import {
|
||||
isOrderable,
|
||||
getAlgorithmAssetSelectionList,
|
||||
getAlgorithmsForAsset,
|
||||
getComputeEnviroment
|
||||
} from '@utils/compute'
|
||||
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 { Decimal } from 'decimal.js'
|
||||
import { useAbortController } from '@hooks/useAbortController'
|
||||
import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing'
|
||||
import { OrderPriceAndFees } from 'src/@types/Price'
|
||||
import { handleComputeOrder } 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({
|
||||
ddo,
|
||||
accessDetails,
|
||||
asset,
|
||||
dtBalance,
|
||||
file,
|
||||
fileIsLoading,
|
||||
isConsumable,
|
||||
consumableFeedback
|
||||
}: {
|
||||
ddo: Asset
|
||||
accessDetails: AccessDetails
|
||||
asset: AssetExtended
|
||||
dtBalance: string
|
||||
file: FileInfo
|
||||
fileIsLoading?: boolean
|
||||
isConsumable?: boolean
|
||||
consumableFeedback?: string
|
||||
}): ReactElement {
|
||||
const { appConfig } = useMarketMetadata()
|
||||
const { accountId } = useWeb3()
|
||||
const [isJobStarting, setIsJobStarting] = useState(false)
|
||||
const { accountId, web3 } = useWeb3()
|
||||
const { getOpcFeeForToken } = useMarketMetadata()
|
||||
const { poolData } = usePool()
|
||||
const newAbortController = useAbortController()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
const [isOrdering, setIsOrdering] = useState(false)
|
||||
const [isOrdered, setIsOrdered] = useState(false)
|
||||
const [error, setError] = useState<string>()
|
||||
|
||||
const [algorithmList, setAlgorithmList] = useState<AssetSelectionAsset[]>()
|
||||
const [ddoAlgorithmList, setDdoAlgorithmList] = useState<Asset[]>()
|
||||
const [selectedAlgorithmAsset, setSelectedAlgorithmAsset] = useState<Asset>()
|
||||
const [selectedAlgorithmAsset, setSelectedAlgorithmAsset] =
|
||||
useState<AssetExtended>()
|
||||
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 [algorithmDTBalance, setAlgorithmDTBalance] = useState<string>()
|
||||
|
||||
const [validOrderTx, setValidOrderTx] = useState('')
|
||||
const [validAlgorithmOrderTx, setValidAlgorithmOrderTx] = useState('')
|
||||
|
||||
const [isConsumablePrice, setIsConsumablePrice] = useState(true)
|
||||
const [isAlgoConsumablePrice, setIsAlgoConsumablePrice] = useState(true)
|
||||
const [isConsumableaAlgorithmPrice, setIsConsumableAlgorithmPrice] =
|
||||
useState(true)
|
||||
const [computeStatusText, setComputeStatusText] = useState('')
|
||||
const [computeEnv, setComputeEnv] = useState<ComputeEnvironment>()
|
||||
const [initializedProviderResponse, setInitializedProviderResponse] =
|
||||
useState<ProviderComputeInitializeResults>()
|
||||
const [providerFeeAmount, setProviderFeeAmount] = useState<string>('0')
|
||||
const [computeValidUntil, setComputeValidUntil] = useState<string>('0')
|
||||
const [datasetOrderPriceAndFees, setDatasetOrderPriceAndFees] =
|
||||
useState<OrderPriceAndFees>()
|
||||
const [algoOrderPriceAndFees, setAlgoOrderPriceAndFees] =
|
||||
useState<OrderPriceAndFees>()
|
||||
const [isRequestingAlgoOrderPrice, setIsRequestingAlgoOrderPrice] =
|
||||
useState(false)
|
||||
const [refetchJobs, setRefetchJobs] = useState(false)
|
||||
|
||||
const hasDatatoken = Number(dtBalance) >= 1
|
||||
const isComputeButtonDisabled =
|
||||
isJobStarting === true ||
|
||||
isOrdering === true ||
|
||||
file === null ||
|
||||
(!hasPreviousDatasetOrder && !hasDatatoken && !isConsumablePrice) ||
|
||||
(!hasPreviousAlgorithmOrder &&
|
||||
(!validOrderTx && !hasDatatoken && !isConsumablePrice) ||
|
||||
(!validAlgorithmOrderTx &&
|
||||
!hasAlgoAssetDatatoken &&
|
||||
!isAlgoConsumablePrice)
|
||||
!isConsumableaAlgorithmPrice)
|
||||
|
||||
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()
|
||||
async function checkAssetDTBalance(asset: DDO): Promise<boolean> {
|
||||
if (!asset?.services[0].datatokenAddress) return
|
||||
const web3 = await getDummyWeb3(asset?.chainId)
|
||||
const datatokenInstance = new Datatoken(web3)
|
||||
const dtBalance = await datatokenInstance.balance(
|
||||
asset?.services[0].datatokenAddress,
|
||||
accountId
|
||||
)
|
||||
|
||||
if (!isMounted()) return
|
||||
if (type === 'algorithm') {
|
||||
setPreviousAlgorithmOrderId(orderId)
|
||||
setHasPreviousAlgorithmOrder(!!orderId)
|
||||
} else {
|
||||
setPreviousDatasetOrderId(orderId)
|
||||
setHasPreviousDatasetOrder(!!orderId)
|
||||
}
|
||||
setAlgorithmDTBalance(new Decimal(dtBalance).toString())
|
||||
const hasAlgoDt = Number(dtBalance) >= 1
|
||||
setHasAlgoAssetDatatoken(hasAlgoDt)
|
||||
return hasAlgoDt
|
||||
}
|
||||
|
||||
async function checkAssetDTBalance(asset: DDO) {
|
||||
// const AssetDtBalance = await ocean.datatokens.balance(
|
||||
// asset.services[0].datatokenAddress,
|
||||
// accountId
|
||||
// )
|
||||
// setalgorithmDTBalance(AssetDtBalance)
|
||||
// setHasAlgoAssetDatatoken(Number(AssetDtBalance) >= 1)
|
||||
}
|
||||
async function initPriceAndFees() {
|
||||
try {
|
||||
const computeEnv = await getComputeEnviroment(asset)
|
||||
if (!computeEnv || !computeEnv.id)
|
||||
throw new Error(`Error getting compute environments!`)
|
||||
|
||||
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[]
|
||||
setComputeEnv(computeEnv)
|
||||
const initializedProvider = await initializeProviderForCompute(
|
||||
asset,
|
||||
selectedAlgorithmAsset,
|
||||
accountId,
|
||||
computeEnv
|
||||
)
|
||||
if (
|
||||
!computeService.compute ||
|
||||
!computeService.compute.publisherTrustedAlgorithms ||
|
||||
computeService.compute.publisherTrustedAlgorithms.length === 0
|
||||
!initializedProvider ||
|
||||
!initializedProvider?.datasets ||
|
||||
!initializedProvider?.algorithm
|
||||
)
|
||||
throw new Error(`Error initializing provider for the compute job!`)
|
||||
|
||||
setInitializedProviderResponse(initializedProvider)
|
||||
setProviderFeeAmount(
|
||||
await unitsToAmount(
|
||||
web3,
|
||||
initializedProvider?.datasets?.[0]?.providerFee?.providerFeeToken,
|
||||
initializedProvider?.datasets?.[0]?.providerFee?.providerFeeAmount
|
||||
)
|
||||
)
|
||||
const computeDuration = (
|
||||
parseInt(initializedProvider?.datasets?.[0]?.providerFee?.validUntil) -
|
||||
Math.floor(Date.now() / 1000)
|
||||
).toString()
|
||||
setComputeValidUntil(computeDuration)
|
||||
|
||||
if (
|
||||
asset?.accessDetails?.addressOrId !== ZERO_ADDRESS &&
|
||||
asset?.accessDetails?.type !== 'free' &&
|
||||
initializedProvider?.datasets?.[0]?.providerFee
|
||||
) {
|
||||
algorithmSelectionList = []
|
||||
} else {
|
||||
const gueryResults = await queryMetadata(
|
||||
getQuerryString(
|
||||
computeService.compute.publisherTrustedAlgorithms,
|
||||
ddo.chainId
|
||||
setComputeStatusText(
|
||||
getComputeFeedback(
|
||||
asset.accessDetails?.baseToken?.symbol,
|
||||
asset.accessDetails?.datatoken?.symbol,
|
||||
asset.metadata.type
|
||||
)[0]
|
||||
)
|
||||
const poolParams =
|
||||
asset?.accessDetails?.type === 'dynamic'
|
||||
? {
|
||||
tokenInLiquidity: poolData?.baseTokenLiquidity,
|
||||
tokenOutLiquidity: poolData?.datatokenLiquidity,
|
||||
tokenOutAmount: '1',
|
||||
opcFee: getOpcFeeForToken(
|
||||
asset?.accessDetails?.baseToken.address,
|
||||
asset?.chainId
|
||||
),
|
||||
source.token
|
||||
)
|
||||
setDdoAlgorithmList(gueryResults.results)
|
||||
|
||||
algorithmSelectionList = await transformAssetToAssetSelection(
|
||||
computeService?.serviceEndpoint,
|
||||
gueryResults.results,
|
||||
[]
|
||||
)
|
||||
lpSwapFee: poolData?.liquidityProviderSwapFee,
|
||||
publishMarketSwapFee:
|
||||
asset?.accessDetails?.publisherMarketOrderFee,
|
||||
consumeMarketSwapFee: '0'
|
||||
}
|
||||
return algorithmSelectionList
|
||||
: null
|
||||
const datasetPriceAndFees = await getOrderPriceAndFees(
|
||||
asset,
|
||||
ZERO_ADDRESS,
|
||||
poolParams,
|
||||
initializedProvider?.datasets?.[0]?.providerFee
|
||||
)
|
||||
if (!datasetPriceAndFees)
|
||||
throw new Error('Error setting dataset price and fees!')
|
||||
|
||||
setDatasetOrderPriceAndFees(datasetPriceAndFees)
|
||||
}
|
||||
|
||||
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')
|
||||
selectedAlgorithmAsset?.accessDetails?.addressOrId !== ZERO_ADDRESS &&
|
||||
selectedAlgorithmAsset?.accessDetails?.type !== 'free' &&
|
||||
initializedProvider?.algorithm?.providerFee
|
||||
) {
|
||||
checkPreviousOrders(selectedAlgorithmAsset)
|
||||
setComputeStatusText(
|
||||
getComputeFeedback(
|
||||
selectedAlgorithmAsset?.accessDetails?.baseToken?.symbol,
|
||||
selectedAlgorithmAsset?.accessDetails?.datatoken?.symbol,
|
||||
selectedAlgorithmAsset?.metadata?.type
|
||||
)[0]
|
||||
)
|
||||
let algoPoolParams = null
|
||||
if (selectedAlgorithmAsset?.accessDetails?.type === 'dynamic') {
|
||||
const response = await getPoolData(
|
||||
selectedAlgorithmAsset.chainId,
|
||||
selectedAlgorithmAsset.accessDetails?.addressOrId,
|
||||
selectedAlgorithmAsset?.nft.owner,
|
||||
accountId || ''
|
||||
)
|
||||
algoPoolParams = {
|
||||
tokenInLiquidity: response?.poolData?.baseTokenLiquidity,
|
||||
tokenOutLiquidity: response?.poolData?.datatokenLiquidity,
|
||||
tokenOutAmount: '1',
|
||||
opcFee: getOpcFeeForToken(
|
||||
selectedAlgorithmAsset?.accessDetails?.baseToken.address,
|
||||
selectedAlgorithmAsset?.chainId
|
||||
),
|
||||
lpSwapFee: response?.poolData?.liquidityProviderSwapFee,
|
||||
publishMarketSwapFee:
|
||||
selectedAlgorithmAsset?.accessDetails?.publisherMarketOrderFee,
|
||||
consumeMarketSwapFee: '0'
|
||||
}
|
||||
}
|
||||
const algorithmOrderPriceAndFees = await getOrderPriceAndFees(
|
||||
selectedAlgorithmAsset,
|
||||
ZERO_ADDRESS,
|
||||
algoPoolParams,
|
||||
initializedProvider.algorithm.providerFee
|
||||
)
|
||||
if (!algorithmOrderPriceAndFees)
|
||||
throw new Error('Error setting algorithm price and fees!')
|
||||
|
||||
setAlgoOrderPriceAndFees(algorithmOrderPriceAndFees)
|
||||
}
|
||||
} catch (error) {
|
||||
setError(error.message)
|
||||
LoggerInstance.error(`[compute] ${error.message} `)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!asset?.accessDetails || !accountId) return
|
||||
|
||||
setIsConsumablePrice(asset?.accessDetails?.isPurchasable)
|
||||
setValidOrderTx(asset?.accessDetails?.validOrderTx)
|
||||
}, [asset?.accessDetails, accountId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedAlgorithmAsset?.accessDetails || !accountId) return
|
||||
|
||||
setIsRequestingAlgoOrderPrice(true)
|
||||
setIsConsumableAlgorithmPrice(
|
||||
selectedAlgorithmAsset?.accessDetails?.isPurchasable
|
||||
)
|
||||
setValidAlgorithmOrderTx(
|
||||
selectedAlgorithmAsset?.accessDetails?.validOrderTx
|
||||
)
|
||||
setAlgoOrderPriceAndFees(null)
|
||||
|
||||
async function initSelectedAlgo() {
|
||||
await checkAssetDTBalance(selectedAlgorithmAsset)
|
||||
await initPriceAndFees()
|
||||
setIsRequestingAlgoOrderPrice(false)
|
||||
}
|
||||
|
||||
initSelectedAlgo()
|
||||
}, [selectedAlgorithmAsset, accountId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!asset) return
|
||||
getAlgorithmsForAsset(asset, newCancelToken()).then((algorithmsAssets) => {
|
||||
setDdoAlgorithmList(algorithmsAssets)
|
||||
getAlgorithmAssetSelectionList(asset, algorithmsAssets).then(
|
||||
(algorithmSelectionList) => {
|
||||
setAlgorithmList(algorithmSelectionList)
|
||||
}
|
||||
)
|
||||
})
|
||||
} else if (getServiceByName(selectedAlgorithmAsset, 'compute')) {
|
||||
checkPreviousOrders(selectedAlgorithmAsset)
|
||||
}
|
||||
}
|
||||
// ocean && checkAssetDTBalance(selectedAlgorithmAsset)
|
||||
}, [ddo, selectedAlgorithmAsset, accountId, hasPreviousAlgorithmOrder])
|
||||
}, [asset])
|
||||
|
||||
// Output errors in toast UI
|
||||
useEffect(() => {
|
||||
@ -235,168 +294,131 @@ export default function Compute({
|
||||
toast.error(newError)
|
||||
}, [error])
|
||||
|
||||
// async function startJob(algorithmId: string) {
|
||||
// try {
|
||||
// if (!ocean) return
|
||||
async function startJob(): Promise<void> {
|
||||
try {
|
||||
setIsOrdering(true)
|
||||
setIsOrdered(false)
|
||||
setError(undefined)
|
||||
const computeService = getServiceByName(asset, 'compute')
|
||||
const computeAlgorithm: ComputeAlgorithm = {
|
||||
documentId: selectedAlgorithmAsset.id,
|
||||
serviceId: selectedAlgorithmAsset.services[0].id
|
||||
}
|
||||
const allowed = await isOrderable(
|
||||
asset,
|
||||
computeService.id,
|
||||
computeAlgorithm,
|
||||
selectedAlgorithmAsset
|
||||
)
|
||||
LoggerInstance.log('[compute] Is data set orderable?', allowed)
|
||||
if (!allowed)
|
||||
throw new Error(
|
||||
'Data set is not orderable in combination with selected algorithm.'
|
||||
)
|
||||
|
||||
// setIsJobStarting(true)
|
||||
// setIsPublished(false)
|
||||
// setError(undefined)
|
||||
setComputeStatusText(
|
||||
getComputeFeedback(
|
||||
asset.accessDetails.baseToken?.symbol,
|
||||
asset.accessDetails.datatoken?.symbol,
|
||||
asset.metadata.type
|
||||
)[
|
||||
asset.accessDetails?.type === 'fixed'
|
||||
? 2
|
||||
: asset.accessDetails?.type === 'dynamic'
|
||||
? 1
|
||||
: 3
|
||||
]
|
||||
)
|
||||
const datasetOrderTx = await handleComputeOrder(
|
||||
web3,
|
||||
asset,
|
||||
datasetOrderPriceAndFees,
|
||||
accountId,
|
||||
hasDatatoken,
|
||||
initializedProviderResponse.datasets[0],
|
||||
computeEnv.consumerAddress
|
||||
)
|
||||
if (!datasetOrderTx) throw new Error('Failed to order dataset.')
|
||||
|
||||
// const computeService = getServiceByName(ddo, 'compute')
|
||||
// const serviceAlgo = getServiceByName(selectedAlgorithmAsset, 'access')
|
||||
// ? getServiceByName(selectedAlgorithmAsset, 'access')
|
||||
// : getServiceByName(selectedAlgorithmAsset, 'compute')
|
||||
setComputeStatusText(
|
||||
getComputeFeedback(
|
||||
selectedAlgorithmAsset.accessDetails.baseToken?.symbol,
|
||||
selectedAlgorithmAsset.accessDetails.datatoken?.symbol,
|
||||
selectedAlgorithmAsset.metadata.type
|
||||
)[
|
||||
selectedAlgorithmAsset.accessDetails?.type === 'fixed'
|
||||
? 2
|
||||
: selectedAlgorithmAsset.accessDetails?.type === 'dynamic'
|
||||
? 1
|
||||
: 3
|
||||
]
|
||||
)
|
||||
|
||||
// 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)
|
||||
const algorithmOrderTx = await handleComputeOrder(
|
||||
web3,
|
||||
selectedAlgorithmAsset,
|
||||
algoOrderPriceAndFees,
|
||||
accountId,
|
||||
hasAlgoAssetDatatoken,
|
||||
initializedProviderResponse.algorithm,
|
||||
computeEnv.consumerAddress
|
||||
)
|
||||
if (!algorithmOrderTx) throw new Error('Failed to order algorithm.')
|
||||
|
||||
// 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
|
||||
// }
|
||||
LoggerInstance.log('[compute] Starting compute job.')
|
||||
const computeAsset: ComputeAsset = {
|
||||
documentId: asset.id,
|
||||
serviceId: asset.services[0].id,
|
||||
transferTxId: datasetOrderTx
|
||||
}
|
||||
computeAlgorithm.transferTxId = algorithmOrderTx
|
||||
const output: ComputeOutput = {
|
||||
publishAlgorithmLog: true,
|
||||
publishOutput: true
|
||||
}
|
||||
setComputeStatusText(getComputeFeedback()[4])
|
||||
const response = await ProviderInstance.computeStart(
|
||||
asset.services[0].serviceEndpoint,
|
||||
web3,
|
||||
accountId,
|
||||
computeEnv?.id,
|
||||
computeAsset,
|
||||
computeAlgorithm,
|
||||
newAbortController(),
|
||||
null,
|
||||
output
|
||||
)
|
||||
if (!response) throw new Error('Error starting compute job.')
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
LoggerInstance.log('[compute] Starting compute job response: ', response)
|
||||
setIsOrdered(true)
|
||||
setRefetchJobs(!refetchJobs)
|
||||
initPriceAndFees()
|
||||
} catch (error) {
|
||||
setError(error.message)
|
||||
LoggerInstance.error(`[compute] ${error.message} `)
|
||||
} finally {
|
||||
setIsOrdering(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.info}>
|
||||
<FileIcon file={file} isLoading={fileIsLoading} small />
|
||||
<Price accessDetails={accessDetails} conversion size="large" />
|
||||
<Price accessDetails={asset?.accessDetails} conversion />
|
||||
</div>
|
||||
|
||||
{ddo.metadata.type === 'algorithm' ? (
|
||||
{asset.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} />
|
||||
<AlgorithmDatasetsListForCompute
|
||||
algorithmDid={asset.id}
|
||||
asset={asset}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Formik
|
||||
@ -404,48 +426,64 @@ export default function Compute({
|
||||
validateOnMount
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={async (values) => {
|
||||
// await startJob(values.algorithm)
|
||||
if (!values.algorithm) return
|
||||
await startJob()
|
||||
}}
|
||||
>
|
||||
<FormStartComputeDataset
|
||||
algorithms={algorithmList}
|
||||
ddoListAlgorithms={ddoAlgorithmList}
|
||||
selectedAlgorithmAsset={selectedAlgorithmAsset}
|
||||
setSelectedAlgorithm={setSelectedAlgorithmAsset}
|
||||
isLoading={isJobStarting}
|
||||
isLoading={isOrdering || isRequestingAlgoOrderPrice}
|
||||
isComputeButtonDisabled={isComputeButtonDisabled}
|
||||
hasPreviousOrder={hasPreviousDatasetOrder}
|
||||
hasPreviousOrder={validOrderTx !== undefined}
|
||||
hasDatatoken={hasDatatoken}
|
||||
dtBalance={dtBalance}
|
||||
datasetLowPoolLiquidity={!isConsumablePrice}
|
||||
assetType={ddo?.metadata.type}
|
||||
assetTimeout={datasetTimeout}
|
||||
hasPreviousOrderSelectedComputeAsset={hasPreviousAlgorithmOrder}
|
||||
assetType={asset?.metadata.type}
|
||||
assetTimeout={secondsToString(asset?.services[0].timeout)}
|
||||
hasPreviousOrderSelectedComputeAsset={
|
||||
validAlgorithmOrderTx !== undefined
|
||||
}
|
||||
hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken}
|
||||
oceanSymbol={accessDetails?.baseToken?.symbol || ''}
|
||||
oceanSymbol={
|
||||
asset?.accessDetails?.baseToken?.symbol ||
|
||||
selectedAlgorithmAsset?.accessDetails?.baseToken?.symbol ||
|
||||
'OCEAN'
|
||||
}
|
||||
dtSymbolSelectedComputeAsset={
|
||||
selectedAlgorithmAsset?.datatokens[0]?.symbol
|
||||
}
|
||||
dtBalanceSelectedComputeAsset={algorithmDTBalance}
|
||||
selectedComputeAssetLowPoolLiquidity={!isAlgoConsumablePrice}
|
||||
selectedComputeAssetType="algorithm"
|
||||
selectedComputeAssetTimeout={algorithmTimeout}
|
||||
selectedComputeAssetTimeout={secondsToString(
|
||||
selectedAlgorithmAsset?.services[0]?.timeout
|
||||
)}
|
||||
// lazy comment when removing pricingStepText
|
||||
stepText={'pricingStepText' || 'Starting Compute Job...'}
|
||||
algorithmConsumeDetails={algorithmConsumeDetails}
|
||||
isConsumable={isConsumable}
|
||||
stepText={computeStatusText}
|
||||
isConsumable={isConsumablePrice}
|
||||
consumableFeedback={consumableFeedback}
|
||||
datasetOrderPriceAndFees={datasetOrderPriceAndFees}
|
||||
algoOrderPriceAndFees={algoOrderPriceAndFees}
|
||||
providerFeeAmount={providerFeeAmount}
|
||||
validUntil={computeValidUntil}
|
||||
/>
|
||||
</Formik>
|
||||
)}
|
||||
|
||||
<footer className={styles.feedback}>
|
||||
{isPublished && (
|
||||
{isOrdered && (
|
||||
<SuccessConfetti success="Your job started successfully! Watch the progress below or on your profile." />
|
||||
)}
|
||||
</footer>
|
||||
{accountId && accessDetails?.datatoken && (
|
||||
{accountId && asset?.accessDetails?.datatoken && (
|
||||
<AssetActionHistoryTable title="Your Compute Jobs">
|
||||
<ComputeJobs minimal />
|
||||
<ComputeJobs
|
||||
minimal
|
||||
assetChainIds={[asset?.chainId]}
|
||||
refetchJobs={refetchJobs}
|
||||
/>
|
||||
</AssetActionHistoryTable>
|
||||
)}
|
||||
</>
|
||||
|
@ -113,8 +113,7 @@ export default function AssetActions({
|
||||
|
||||
const UseContent = isCompute ? (
|
||||
<Compute
|
||||
ddo={asset}
|
||||
accessDetails={asset?.accessDetails}
|
||||
asset={asset}
|
||||
dtBalance={dtBalance}
|
||||
file={fileMetadata}
|
||||
fileIsLoading={fileIsLoading}
|
||||
|
@ -3,12 +3,12 @@ import { useAsset } from '@context/Asset'
|
||||
import ExplorerLink from '@shared/ExplorerLink'
|
||||
import Time from '@shared/atoms/Time'
|
||||
import { gql, OperationContext, useQuery } from 'urql'
|
||||
import { ReceiptData_nftUpdates as ReceiptData } from '../../../@types/subgraph/ReceiptData'
|
||||
import { NftUpdate_nftUpdates as NftUpdate } from '../../../@types/subgraph/NftUpdate'
|
||||
import { getQueryContext } from '@utils/subgraph'
|
||||
import styles from './EditHistory.module.css'
|
||||
|
||||
const getReceipts = gql`
|
||||
query ReceiptData($address: String!) {
|
||||
query NftUpdate($address: String!) {
|
||||
nftUpdates(
|
||||
where: { nft: $address }
|
||||
orderBy: timestamp
|
||||
@ -30,8 +30,8 @@ export default function EditHistory({
|
||||
receipts,
|
||||
setReceipts
|
||||
}: {
|
||||
receipts: ReceiptData[]
|
||||
setReceipts: (receipts: ReceiptData[]) => void
|
||||
receipts: NftUpdate[]
|
||||
setReceipts: (receipts: NftUpdate[]) => void
|
||||
}): ReactElement {
|
||||
const { asset } = useAsset()
|
||||
|
||||
|
@ -35,20 +35,7 @@
|
||||
text-align: center;
|
||||
margin-top: var(--spacer);
|
||||
margin-bottom: calc(var(--spacer) * 1.5);
|
||||
margin-left: -2rem;
|
||||
margin-right: -2rem;
|
||||
padding: calc(var(--spacer) / 4) var(--spacer);
|
||||
padding: calc(var(--spacer) / 3) var(--spacer);
|
||||
border-top: 1px solid var(--border-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.ownerActions a,
|
||||
.ownerActions button {
|
||||
color: var(--color-secondary);
|
||||
margin-left: calc(var(--spacer) / 4);
|
||||
margin-right: calc(var(--spacer) / 4);
|
||||
}
|
||||
|
||||
.separator {
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
@ -15,17 +15,15 @@ import styles from './index.module.css'
|
||||
import NetworkName from '@shared/NetworkName'
|
||||
import content from '../../../../content/purgatory.json'
|
||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
import Web3 from 'web3'
|
||||
import Button from '@shared/atoms/Button'
|
||||
|
||||
export default function AssetContent({
|
||||
asset
|
||||
}: {
|
||||
asset: AssetExtended
|
||||
}): ReactElement {
|
||||
const [isOwner, setIsOwner] = useState(false)
|
||||
const { accountId } = useWeb3()
|
||||
const { isInPurgatory, purgatoryData, owner, isAssetNetwork } = useAsset()
|
||||
const { isInPurgatory, purgatoryData, isOwner, isAssetNetwork } = useAsset()
|
||||
const { debug } = useUserPreferences()
|
||||
const [receipts, setReceipts] = useState([])
|
||||
const [nftPublisher, setNftPublisher] = useState<string>()
|
||||
@ -38,13 +36,6 @@ export default function AssetContent({
|
||||
)
|
||||
}, [receipts])
|
||||
|
||||
useEffect(() => {
|
||||
if (!accountId || !owner) return
|
||||
|
||||
const isOwner = accountId.toLowerCase() === owner.toLowerCase()
|
||||
setIsOwner(isOwner)
|
||||
}, [accountId, owner, asset])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.networkWrap}>
|
||||
@ -84,9 +75,9 @@ export default function AssetContent({
|
||||
<AssetActions asset={asset} />
|
||||
{isOwner && isAssetNetwork && (
|
||||
<div className={styles.ownerActions}>
|
||||
<Link href={`/asset/${asset?.id}/edit`}>
|
||||
<a>Edit</a>
|
||||
</Link>
|
||||
<Button style="text" size="small" to={`/asset/${asset?.id}/edit`}>
|
||||
Edit Asset
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { ReactElement, useState, useEffect } from 'react'
|
||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||
import { useAsset } from '@context/Asset'
|
||||
import styles from './index.module.css'
|
||||
import Tabs from '@shared/atoms/Tabs'
|
||||
@ -33,7 +32,7 @@ export default function Edit({ uri }: { uri: string }): ReactElement {
|
||||
{
|
||||
title: 'Edit Compute Settings',
|
||||
content: <EditComputeDataset asset={asset} />,
|
||||
disabled: !isCompute
|
||||
disabled: !isCompute || asset?.metadata?.type === 'algorithm'
|
||||
}
|
||||
].filter((tab) => tab !== undefined)
|
||||
|
||||
|
@ -62,6 +62,20 @@
|
||||
.assetMeta code {
|
||||
color: var(--color-secondary);
|
||||
font-size: var(--font-size-small);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
flex: 6;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.assetMeta span {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.assetMeta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.meta {
|
||||
|
@ -33,7 +33,8 @@ function Asset({
|
||||
</a>
|
||||
</h3>
|
||||
<p className={styles.assetMeta}>
|
||||
{symbol} | <code>{did}</code>
|
||||
<span className={styles.assetMeta}> {`${symbol} | `}</span>
|
||||
<code className={styles.assetMeta}>{did}</code>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
@ -41,9 +42,11 @@ function Asset({
|
||||
|
||||
function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
|
||||
const { appConfig } = useMarketMetadata()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
const [algoName, setAlgoName] = useState<string>()
|
||||
const [algoDtSymbol, setAlgoDtSymbol] = useState<string>()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
useEffect(() => {
|
||||
async function getAlgoMetadata() {
|
||||
const ddo = await retrieveAsset(job.algoDID, newCancelToken())
|
||||
@ -51,7 +54,7 @@ function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
|
||||
setAlgoName(ddo?.metadata.name)
|
||||
}
|
||||
getAlgoMetadata()
|
||||
}, [appConfig.metadataCacheUri, job.algoDID])
|
||||
}, [appConfig.metadataCacheUri, job.algoDID, newCancelToken])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -97,12 +100,6 @@ export default function Details({
|
||||
/>
|
||||
)}
|
||||
<MetaItem title="Job ID" content={<code>{job.jobId}</code>} />
|
||||
{/* {job.resultsDid && (
|
||||
<MetaItem
|
||||
title="Published Results DID"
|
||||
content={<code>{job.resultsDid}</code>}
|
||||
/>
|
||||
)} */}
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
|
@ -3,6 +3,12 @@
|
||||
border-bottom-left-radius: var(--border-radius) !important;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: var(--font-size-base);
|
||||
color: var(--font-color-text);
|
||||
margin-bottom: calc(var(--spacer) / 3);
|
||||
}
|
||||
|
||||
.help {
|
||||
margin-top: calc(var(--spacer) / 3);
|
||||
}
|
||||
|
@ -1,90 +1,108 @@
|
||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||
import React, { ReactElement, useState } from 'react'
|
||||
import Loader from '@shared/atoms/Loader'
|
||||
import {
|
||||
ComputeResultType,
|
||||
downloadFileBrowser,
|
||||
LoggerInstance,
|
||||
Provider
|
||||
} from '@oceanprotocol/lib'
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import { ListItem } from '@shared/atoms/Lists'
|
||||
import Button from '@shared/atoms/Button'
|
||||
import styles from './Results.module.css'
|
||||
import FormHelp from '@shared/FormInput/Help'
|
||||
import content from '../../../../../content/pages/history.json'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
import { useCancelToken } from '@hooks/useCancelToken'
|
||||
import { retrieveAsset } from '@utils/aquarius'
|
||||
|
||||
export default function Results({
|
||||
job
|
||||
}: {
|
||||
job: ComputeJobMetaData
|
||||
}): ReactElement {
|
||||
const { accountId } = useWeb3()
|
||||
const providerInstance = new Provider()
|
||||
const { accountId, web3 } = useWeb3()
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [hasFetched, setHasFetched] = useState(false)
|
||||
const isFinished = job.dateFinished !== null
|
||||
|
||||
async function getResults() {
|
||||
const [datasetProvider, setDatasetProvider] = useState<string>()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
useEffect(() => {
|
||||
async function getAssetMetadata() {
|
||||
const ddo = await retrieveAsset(job.inputDID[0], newCancelToken())
|
||||
setDatasetProvider(ddo.services[0].serviceEndpoint)
|
||||
}
|
||||
getAssetMetadata()
|
||||
}, [job.inputDID, newCancelToken])
|
||||
|
||||
function getDownloadButtonValue(type: ComputeResultType): string {
|
||||
let buttonName
|
||||
switch (type) {
|
||||
case 'output':
|
||||
buttonName = 'results'
|
||||
break
|
||||
case 'algorithmLog':
|
||||
buttonName = 'algorithm logs'
|
||||
break
|
||||
case 'configrationLog':
|
||||
buttonName = 'configuration logs'
|
||||
break
|
||||
case 'publishLog':
|
||||
buttonName = 'publish logs'
|
||||
break
|
||||
default:
|
||||
buttonName = 'results'
|
||||
break
|
||||
}
|
||||
return buttonName
|
||||
}
|
||||
|
||||
async function downloadResults(resultIndex: number) {
|
||||
if (!accountId || !job) return
|
||||
|
||||
try {
|
||||
setIsLoading(true)
|
||||
// const jobStatus = await ocean.compute.status(
|
||||
// account,
|
||||
// job.did,
|
||||
// undefined,
|
||||
// undefined,
|
||||
// job.jobId
|
||||
// )
|
||||
// if (jobStatus?.length > 0) {
|
||||
// job.algorithmLogUrl = jobStatus[0].algorithmLogUrl
|
||||
// job.resultsUrl = jobStatus[0].resultsUrl
|
||||
// }
|
||||
const jobResult = await providerInstance.getComputeResultUrl(
|
||||
datasetProvider,
|
||||
web3,
|
||||
accountId,
|
||||
job.jobId,
|
||||
resultIndex
|
||||
)
|
||||
await downloadFileBrowser(jobResult)
|
||||
} catch (error) {
|
||||
LoggerInstance.error(error.message)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
setHasFetched(true)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.results}>
|
||||
{hasFetched ? (
|
||||
<h4 className={styles.title}>Results</h4>
|
||||
{isFinished ? (
|
||||
<ul>
|
||||
<ListItem>
|
||||
{/* {job.algorithmLogUrl ? (
|
||||
<a href={job.algorithmLogUrl} target="_blank" rel="noreferrer">
|
||||
View Log
|
||||
</a>
|
||||
) : (
|
||||
'No logs found.'
|
||||
)} */}
|
||||
</ListItem>
|
||||
|
||||
{/* {job.resultsUrl &&
|
||||
Array.isArray(job.resultsUrl) &&
|
||||
job.resultsUrl.map((url, i) =>
|
||||
url ? (
|
||||
<ListItem key={job.jobId}>
|
||||
<a href={url} target="_blank" rel="noreferrer">
|
||||
View Result {i + 1}
|
||||
</a>
|
||||
{job.results &&
|
||||
Array.isArray(job.results) &&
|
||||
job.results.map((jobResult, i) =>
|
||||
jobResult.filename ? (
|
||||
<ListItem key={i}>
|
||||
<Button
|
||||
style="text"
|
||||
size="small"
|
||||
onClick={() => downloadResults(i)}
|
||||
download
|
||||
>
|
||||
{getDownloadButtonValue(jobResult.type)}
|
||||
</Button>
|
||||
</ListItem>
|
||||
) : (
|
||||
<ListItem>No results found.</ListItem>
|
||||
)
|
||||
)} */}
|
||||
)}
|
||||
</ul>
|
||||
) : (
|
||||
<Button
|
||||
style="primary"
|
||||
size="small"
|
||||
onClick={() => getResults()}
|
||||
disabled={isLoading || !isFinished}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Loader />
|
||||
) : !isFinished ? (
|
||||
'Waiting for results...'
|
||||
) : (
|
||||
'Get Results'
|
||||
)}
|
||||
</Button>
|
||||
<p> Waiting for results...</p>
|
||||
)}
|
||||
<FormHelp className={styles.help}>{content.compute.storage}</FormHelp>
|
||||
</div>
|
||||
|
@ -1,8 +1,6 @@
|
||||
import React, { ReactElement, useEffect, useState, useCallback } from 'react'
|
||||
import Time from '@shared/atoms/Time'
|
||||
import Link from 'next/link'
|
||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||
import Dotdotdot from 'react-dotdotdot'
|
||||
import Table from '@shared/atoms/Table'
|
||||
import Button from '@shared/atoms/Button'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
@ -10,10 +8,12 @@ import Details from './Details'
|
||||
import Refresh from '@images/refresh.svg'
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
import NetworkName from '@shared/NetworkName'
|
||||
// import { getComputeJobs } from '@utils/compute'
|
||||
import { getComputeJobs } from '@utils/compute'
|
||||
import styles from './index.module.css'
|
||||
import { useAsset } from '@context/Asset'
|
||||
import { useIsMounted } from '@hooks/useIsMounted'
|
||||
import { useCancelToken } from '@hooks/useCancelToken'
|
||||
import AssetListTitle from '@shared/AssetList/AssetListTitle'
|
||||
|
||||
export function Status({ children }: { children: string }): ReactElement {
|
||||
return <div className={styles.status}>{children}</div>
|
||||
@ -23,13 +23,7 @@ const columns = [
|
||||
{
|
||||
name: 'Data Set',
|
||||
selector: function getAssetRow(row: ComputeJobMetaData) {
|
||||
return (
|
||||
<Dotdotdot clamp={2}>
|
||||
<Link href={`/asset/${row.inputDID[0]}`}>
|
||||
<a>{row.assetName}</a>
|
||||
</Link>
|
||||
</Dotdotdot>
|
||||
)
|
||||
return <AssetListTitle did={row.inputDID[0]} title={row.assetName} />
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -69,18 +63,23 @@ const columns = [
|
||||
]
|
||||
|
||||
export default function ComputeJobs({
|
||||
minimal
|
||||
minimal,
|
||||
assetChainIds,
|
||||
refetchJobs
|
||||
}: {
|
||||
minimal?: boolean
|
||||
assetChainIds?: number[]
|
||||
refetchJobs?: boolean
|
||||
}): ReactElement {
|
||||
const { accountId, networkId } = useWeb3()
|
||||
const { accountId } = useWeb3()
|
||||
const { asset } = useAsset()
|
||||
const { chainIds } = useUserPreferences()
|
||||
const isMounted = useIsMounted()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
|
||||
const isMounted = useIsMounted()
|
||||
|
||||
const columnsMinimal = [columns[4], columns[5], columns[3]]
|
||||
const [columnsMinimal] = useState([columns[4], columns[5], columns[3]])
|
||||
|
||||
const fetchJobs = useCallback(async () => {
|
||||
if (!chainIds || chainIds.length === 0 || !accountId) {
|
||||
@ -90,17 +89,22 @@ export default function ComputeJobs({
|
||||
}
|
||||
try {
|
||||
setIsLoading(true)
|
||||
// const jobs = await getComputeJobs(chainIds, accountId, ddo)
|
||||
// isMounted() && setJobs(jobs.computeJobs)
|
||||
// setIsLoading(jobs.isLoaded)
|
||||
const jobs = await getComputeJobs(
|
||||
assetChainIds || chainIds,
|
||||
accountId,
|
||||
asset,
|
||||
newCancelToken()
|
||||
)
|
||||
isMounted() && setJobs(jobs.computeJobs)
|
||||
setIsLoading(!jobs.isLoaded)
|
||||
} catch (error) {
|
||||
LoggerInstance.error(error.message)
|
||||
}
|
||||
}, [chainIds, accountId, asset, isMounted])
|
||||
}, [chainIds, accountId, asset, isMounted, assetChainIds, newCancelToken])
|
||||
|
||||
useEffect(() => {
|
||||
fetchJobs()
|
||||
}, [fetchJobs])
|
||||
}, [fetchJobs, refetchJobs])
|
||||
|
||||
return accountId ? (
|
||||
<>
|
||||
|
@ -41,8 +41,8 @@ export const wizardSteps: StepContent[] = [
|
||||
const computeOptions: ServiceComputeOptions = {
|
||||
allowRawAlgorithm: false,
|
||||
allowNetworkAccess: true,
|
||||
publisherTrustedAlgorithmPublishers: null,
|
||||
publisherTrustedAlgorithms: null
|
||||
publisherTrustedAlgorithmPublishers: [],
|
||||
publisherTrustedAlgorithms: []
|
||||
}
|
||||
|
||||
export const initialValues: FormPublishData = {
|
||||
|
@ -135,7 +135,8 @@ export async function transformPublishFormToDdo(
|
||||
: getAlgorithmContainerPreset(dockerImage).tag,
|
||||
checksum:
|
||||
dockerImage === 'custom'
|
||||
? dockerImageCustomChecksum
|
||||
? // ? dockerImageCustomChecksum
|
||||
''
|
||||
: getAlgorithmContainerPreset(dockerImage).checksum
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user