1
0
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:
Bogdan Fazakas 2022-06-23 18:53:05 +03:00 committed by GitHub
parent d884a9529d
commit 5387b9a3dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1297 additions and 778 deletions

View File

@ -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
}

View File

@ -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 {

View File

@ -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(
asset?.id,
asset.services[0].id,
0,
accountId,
asset?.services[0].serviceEndpoint
)
orderPriceAndFee.providerFee = initializeData.providerFee
const initializeData =
!providerFees &&
(await ProviderInstance.initialize(
asset?.id,
asset?.services[0].id,
0,
accountId,
asset?.services[0].serviceEndpoint
))
orderPriceAndFee.providerFee = providerFees || initializeData.providerFee
// fetch price and swap fees
switch (asset?.accessDetails?.type) {
@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
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
}
// const service = ddo.services.filter(
// (x: Service) => x.index === data[i].serviceId
// )[0]
export function getValidUntilTime(
computeEnvMaxJobDuration: number,
datasetTimeout?: number,
algorithmTimeout?: number
) {
const inputValues = []
computeEnvMaxJobDuration && inputValues.push(computeEnvMaxJobDuration)
datasetTimeout && inputValues.push(datasetTimeout)
algorithmTimeout && inputValues.push(algorithmTimeout)
// if (!service || service.type !== 'compute') continue
// const { providerEndpoint } = service
const minValue = Math.min(...inputValues)
const mytime = new Date()
mytime.setMinutes(mytime.getMinutes() + Math.floor(minValue / 60))
return Math.floor(mytime.getTime() / 1000)
}
// const wasProviderQueried =
// serviceEndpoints?.filter((x) => x === providerEndpoint).length > 0
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)
}
}
// if (wasProviderQueried) continue
// serviceEndpoints.push(providerEndpoint)
// } catch (err) {
// LoggerInstance.error(err.message)
// }
export function getQueryString(
trustedAlgorithmList: PublisherTrustedAlgorithm[],
trustedPublishersList: string[],
chainId?: number
): SearchQuery {
const algorithmDidList = trustedAlgorithmList?.map((x) => x.did)
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)
return query
}
export async function getAlgorithmsForAsset(
asset: Asset,
token: CancelToken
): Promise<Asset[]> {
const computeService: Service = getServiceByName(asset, 'compute')
if (
!computeService.compute ||
(computeService.compute.publisherTrustedAlgorithms?.length === 0 &&
computeService.compute.publisherTrustedAlgorithmPublishers?.length === 0)
) {
return []
}
const gueryResults = await queryMetadata(
getQueryString(
computeService.compute.publisherTrustedAlgorithms,
computeService.compute.publisherTrustedAlgorithmPublishers,
asset.chainId
),
token
)
const algorithms: Asset[] = gueryResults?.results
return algorithms
}
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
}
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[]
if (providerComputeJobs) {
providerComputeJobs.sort((a, b) => {
if (a.dateCreated > b.dateCreated) {
return -1
}
if (a.dateCreated < b.dateCreated) {
return 1
}
return 0
})
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 serviceEndpoints
return ['dummy']
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()
}
// async function getProviders(
// serviceEndpoints: string[],
// config: Config,
// ocean: Ocean
// ): Promise<Provider[]> {
// const providers: Provider[] = []
const results = await fetchDataForMultipleChains(
assetDTAddress ? getComputeOrdersByDatatokenAddress : getComputeOrders,
variables,
assetDTAddress ? [asset?.chainId] : chainIds
)
// 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)
// }
let tokenOrders: TokenOrder[] = []
results.map((result) =>
result.orders.forEach((tokenOrder: TokenOrder) =>
tokenOrders.push(tokenOrder)
)
)
if (tokenOrders.length === 0) {
computeResult.isLoaded = true
return computeResult
}
// return providers
// }
tokenOrders = tokenOrders.sort(
(a, b) => b.createdTimestamp - a.createdTimestamp
)
// async function getJobs(
// providers: Provider[],
// account: Account,
// assets: Asset[]
// ): Promise<ComputeJobMetaData[]> {
// const computeJobs: ComputeJobMetaData[] = []
const datatokenAddressList = tokenOrders.map(
(tokenOrder: TokenOrder) => tokenOrder.datatoken.address
)
if (!datatokenAddressList) return
// for (let i = 0; i < providers.length; i++) {
// try {
// const providerComputeJobs = (await providers[i].computeStatus(
// '',
// account,
// undefined,
// undefined,
// false
// )) as ComputeJob[]
const assets = await getAssetMetadata(
datatokenAddressList,
cancelToken,
chainIds
)
// // 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
// })
const providerUrls: string[] = []
assets.forEach((asset: Asset) =>
providerUrls.push(asset.services[0].serviceEndpoint)
)
// 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]
computeResult.computeJobs = await getJobs(providerUrls, accountId, assets)
computeResult.isLoaded = true
// if (!ddo) continue
// const compJob: ComputeJobMetaData = {
// ...job,
// assetName: ddo.metadata.name,
// assetDtSymbol: ddo.dataTokenInfo.symbol,
// networkId: ddo.chainId
// }
// computeJobs.push(compJob)
// }
// } catch (err) {
// LoggerInstance.error(err.message)
// }
// }
// return computeJobs
// }
// function getDtList(data: TokenOrder[]): string[] {
// const dtList = []
// for (let i = 0; i < data.length; i++) {
// dtList.push(data[i].datatokenId.address)
// }
// return dtList
// }
// 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()
// }
// 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
// if (data.length === 0) {
// return computeResult
// }
// data = data.sort((a, b) => b.timestamp - a.timestamp)
// const queryDtList = getDtList(data)
// if (!queryDtList) return
// 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
// }
// return computeResult
// }
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
}

View File

@ -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':

View File

@ -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 ...'
}
}

View File

@ -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
}

View File

@ -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,

View File

@ -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(
asset.id,
asset.services[0].id,
0,
accountId,
asset.services[0].serviceEndpoint
)
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}`)
}
}

View File

@ -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(

View File

@ -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'

View File

@ -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);

View File

@ -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>
</>

View File

@ -12,6 +12,7 @@ export default function Blockies({
className
}: BlockiesProps): ReactElement {
if (!accountId) return null
const blockies = toDataUrl(accountId)
return (

View File

@ -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}>

View File

@ -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
}
setSelectedAlgorithm(extendedAlgoAsset)
}
checkIsConsumable()
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>
)

View File

@ -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);
}

View File

@ -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>

View File

@ -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[]
if (
!computeService.compute ||
!computeService.compute.publisherTrustedAlgorithms ||
computeService.compute.publisherTrustedAlgorithms.length === 0
) {
algorithmSelectionList = []
} else {
const gueryResults = await queryMetadata(
getQuerryString(
computeService.compute.publisherTrustedAlgorithms,
ddo.chainId
),
source.token
setComputeEnv(computeEnv)
const initializedProvider = await initializeProviderForCompute(
asset,
selectedAlgorithmAsset,
accountId,
computeEnv
)
setDdoAlgorithmList(gueryResults.results)
algorithmSelectionList = await transformAssetToAssetSelection(
computeService?.serviceEndpoint,
gueryResults.results,
[]
if (
!initializedProvider ||
!initializedProvider?.datasets ||
!initializedProvider?.algorithm
)
}
return algorithmSelectionList
}
throw new Error(`Error initializing provider for the compute job!`)
const initMetadata = useCallback(async (ddo: Asset): Promise<void> => {
if (!ddo) return
const accessDetails = await getAccessDetails(
ddo.chainId,
ddo.services[0].datatokenAddress
)
setAlgorithmConsumeDetails(accessDetails)
}, [])
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)
useEffect(() => {
if (!algorithmConsumeDetails) return
if (
asset?.accessDetails?.addressOrId !== ZERO_ADDRESS &&
asset?.accessDetails?.type !== 'free' &&
initializedProvider?.datasets?.[0]?.providerFee
) {
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
),
lpSwapFee: poolData?.liquidityProviderSwapFee,
publishMarketSwapFee:
asset?.accessDetails?.publisherMarketOrderFee,
consumeMarketSwapFee: '0'
}
: null
const datasetPriceAndFees = await getOrderPriceAndFees(
asset,
ZERO_ADDRESS,
poolParams,
initializedProvider?.datasets?.[0]?.providerFee
)
if (!datasetPriceAndFees)
throw new Error('Error setting dataset price and fees!')
setIsAlgoConsumablePrice(algorithmConsumeDetails.isPurchasable)
}, [algorithmConsumeDetails])
useEffect(() => {
if (!accessDetails) return
setIsConsumablePrice(accessDetails.isPurchasable)
}, [accessDetails])
// useEffect(() => {
// setDatasetTimeout(secondsToString(timeout))
// }, [ddo])
useEffect(() => {
if (!ddo) return
getAlgorithmList().then((algorithms) => {
setAlgorithmList(algorithms)
})
}, [ddo])
useEffect(() => {
if (!accountId) return
checkPreviousOrders(ddo)
}, [ddo, accountId])
useEffect(() => {
if (!selectedAlgorithmAsset) return
initMetadata(selectedAlgorithmAsset)
const { timeout } = ddo.services[0]
// setAlgorithmTimeout(secondsToString(timeout))
if (accountId) {
if (getServiceByName(selectedAlgorithmAsset, 'access')) {
checkPreviousOrders(selectedAlgorithmAsset).then(() => {
if (
!hasPreviousAlgorithmOrder &&
getServiceByName(selectedAlgorithmAsset, 'compute')
) {
checkPreviousOrders(selectedAlgorithmAsset)
}
})
} else if (getServiceByName(selectedAlgorithmAsset, 'compute')) {
checkPreviousOrders(selectedAlgorithmAsset)
setDatasetOrderPriceAndFees(datasetPriceAndFees)
}
if (
selectedAlgorithmAsset?.accessDetails?.addressOrId !== ZERO_ADDRESS &&
selectedAlgorithmAsset?.accessDetails?.type !== 'free' &&
initializedProvider?.algorithm?.providerFee
) {
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} `)
}
// ocean && checkAssetDTBalance(selectedAlgorithmAsset)
}, [ddo, selectedAlgorithmAsset, accountId, hasPreviousAlgorithmOrder])
}
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)
}
)
})
}, [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>
)}
</>

View File

@ -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}

View File

@ -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()

View File

@ -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);
}

View File

@ -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>

View File

@ -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)

View File

@ -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 {

View File

@ -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>
</>

View File

@ -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);
}

View File

@ -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>

View File

@ -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 ? (
<>

View File

@ -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 = {

View File

@ -135,7 +135,8 @@ export async function transformPublishFormToDdo(
: getAlgorithmContainerPreset(dockerImage).tag,
checksum:
dockerImage === 'custom'
? dockerImageCustomChecksum
? // ? dockerImageCustomChecksum
''
: getAlgorithmContainerPreset(dockerImage).checksum
}
}