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
|
error?: string
|
||||||
isAssetNetwork: boolean
|
isAssetNetwork: boolean
|
||||||
isV3Asset: boolean
|
isV3Asset: boolean
|
||||||
|
isOwner: boolean
|
||||||
oceanConfig: Config
|
oceanConfig: Config
|
||||||
loading: boolean
|
loading: boolean
|
||||||
fetchAsset: (token?: CancelToken) => Promise<void>
|
fetchAsset: (token?: CancelToken) => Promise<void>
|
||||||
@ -49,6 +50,7 @@ function AssetProvider({
|
|||||||
const [asset, setAsset] = useState<AssetExtended>()
|
const [asset, setAsset] = useState<AssetExtended>()
|
||||||
const [title, setTitle] = useState<string>()
|
const [title, setTitle] = useState<string>()
|
||||||
const [owner, setOwner] = useState<string>()
|
const [owner, setOwner] = useState<string>()
|
||||||
|
const [isOwner, setIsOwner] = useState<boolean>()
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [isAssetNetwork, setIsAssetNetwork] = useState<boolean>()
|
const [isAssetNetwork, setIsAssetNetwork] = useState<boolean>()
|
||||||
@ -140,6 +142,16 @@ function AssetProvider({
|
|||||||
setIsAssetNetwork(isAssetNetwork)
|
setIsAssetNetwork(isAssetNetwork)
|
||||||
}, [chainId, asset?.chainId])
|
}, [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
|
// Load ocean config based on asset network
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
@ -172,6 +184,7 @@ function AssetProvider({
|
|||||||
fetchAsset,
|
fetchAsset,
|
||||||
isAssetNetwork,
|
isAssetNetwork,
|
||||||
isV3Asset,
|
isV3Asset,
|
||||||
|
isOwner,
|
||||||
oceanConfig
|
oceanConfig
|
||||||
} as AssetProviderValue
|
} 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 { 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
|
// declaring into global scope to be able to use this as
|
||||||
// ambiant types despite the above imports
|
// ambiant types despite the above imports
|
||||||
@ -22,10 +22,10 @@ declare global {
|
|||||||
|
|
||||||
interface TokenOrder {
|
interface TokenOrder {
|
||||||
id: string
|
id: string
|
||||||
serviceId: number
|
serviceIndex: number
|
||||||
datatokenId: OrdersDatatoken
|
datatoken: OrdersDatatoken
|
||||||
tx: any
|
tx: any
|
||||||
timestamp: number
|
createdTimestamp: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ComputeResults {
|
interface ComputeResults {
|
||||||
|
@ -8,13 +8,19 @@ import {
|
|||||||
TokensPriceQuery,
|
TokensPriceQuery,
|
||||||
TokensPriceQuery_tokens as TokensPrice
|
TokensPriceQuery_tokens as TokensPrice
|
||||||
} from '../@types/subgraph/TokensPriceQuery'
|
} 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 { AssetExtended } from 'src/@types/AssetExtended'
|
||||||
import { calcInGivenOut } from './pool'
|
import { calcInGivenOut } from './pool'
|
||||||
import { getFixedBuyPrice } from './fixedRateExchange'
|
import { getFixedBuyPrice } from './fixedRateExchange'
|
||||||
import { AccessDetails, OrderPriceAndFees } from 'src/@types/Price'
|
import { AccessDetails, OrderPriceAndFees } from 'src/@types/Price'
|
||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
import { consumeMarketOrderFee } from '../../app.config'
|
import { consumeMarketOrderFee } from '../../app.config'
|
||||||
|
import Web3 from 'web3'
|
||||||
|
|
||||||
const tokensPriceQuery = gql`
|
const tokensPriceQuery = gql`
|
||||||
query TokensPriceQuery($datatokenIds: [ID!], $account: String) {
|
query TokensPriceQuery($datatokenIds: [ID!], $account: String) {
|
||||||
@ -26,13 +32,20 @@ const tokensPriceQuery = gql`
|
|||||||
publishMarketFeeToken
|
publishMarketFeeToken
|
||||||
publishMarketFeeAmount
|
publishMarketFeeAmount
|
||||||
orders(
|
orders(
|
||||||
where: { consumer: $account }
|
where: { payer: $account }
|
||||||
orderBy: createdTimestamp
|
orderBy: createdTimestamp
|
||||||
orderDirection: desc
|
orderDirection: desc
|
||||||
) {
|
) {
|
||||||
tx
|
tx
|
||||||
serviceIndex
|
serviceIndex
|
||||||
createdTimestamp
|
createdTimestamp
|
||||||
|
reuses(orderBy: createdTimestamp, orderDirection: desc) {
|
||||||
|
id
|
||||||
|
caller
|
||||||
|
createdTimestamp
|
||||||
|
tx
|
||||||
|
block
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dispensers {
|
dispensers {
|
||||||
id
|
id
|
||||||
@ -91,13 +104,20 @@ const tokenPriceQuery = gql`
|
|||||||
publishMarketFeeToken
|
publishMarketFeeToken
|
||||||
publishMarketFeeAmount
|
publishMarketFeeAmount
|
||||||
orders(
|
orders(
|
||||||
where: { consumer: $account }
|
where: { payer: $account }
|
||||||
orderBy: createdTimestamp
|
orderBy: createdTimestamp
|
||||||
orderDirection: desc
|
orderDirection: desc
|
||||||
) {
|
) {
|
||||||
tx
|
tx
|
||||||
serviceIndex
|
serviceIndex
|
||||||
createdTimestamp
|
createdTimestamp
|
||||||
|
reuses(orderBy: createdTimestamp, orderDirection: desc) {
|
||||||
|
id
|
||||||
|
caller
|
||||||
|
createdTimestamp
|
||||||
|
tx
|
||||||
|
block
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dispensers {
|
dispensers {
|
||||||
id
|
id
|
||||||
@ -152,19 +172,22 @@ function getAccessDetailsFromTokenPrice(
|
|||||||
timeout?: number
|
timeout?: number
|
||||||
): AccessDetails {
|
): AccessDetails {
|
||||||
const accessDetails = {} as AccessDetails
|
const accessDetails = {} as AccessDetails
|
||||||
if (tokenPrice && tokenPrice.orders && tokenPrice.orders.length > 0) {
|
|
||||||
|
if (tokenPrice?.orders?.length > 0) {
|
||||||
const order = tokenPrice.orders[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
|
// asset is owned if there is an order and asset has timeout 0 (forever) or if the condition is valid
|
||||||
accessDetails.isOwned =
|
accessDetails.isOwned =
|
||||||
timeout === 0 || Date.now() / 1000 - order.createdTimestamp < timeout
|
timeout === 0 || Date.now() / 1000 - order?.createdTimestamp < timeout
|
||||||
accessDetails.validOrderTx = order.tx
|
// 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
|
// TODO: fetch order fee from sub query
|
||||||
accessDetails.publisherMarketOrderFee = tokenPrice.publishMarketFeeAmount
|
accessDetails.publisherMarketOrderFee = tokenPrice?.publishMarketFeeAmount
|
||||||
|
|
||||||
// free is always the best price
|
// free is always the best price
|
||||||
if (tokenPrice.dispensers && tokenPrice.dispensers.length > 0) {
|
if (tokenPrice?.dispensers?.length > 0) {
|
||||||
const dispenser = tokenPrice.dispensers[0]
|
const dispenser = tokenPrice.dispensers[0]
|
||||||
accessDetails.type = 'free'
|
accessDetails.type = 'free'
|
||||||
accessDetails.addressOrId = dispenser.token.id
|
accessDetails.addressOrId = dispenser.token.id
|
||||||
@ -179,10 +202,7 @@ function getAccessDetailsFromTokenPrice(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checking for fixed price
|
// checking for fixed price
|
||||||
if (
|
if (tokenPrice?.fixedRateExchanges?.length > 0) {
|
||||||
tokenPrice.fixedRateExchanges &&
|
|
||||||
tokenPrice.fixedRateExchanges.length > 0
|
|
||||||
) {
|
|
||||||
const fixed = tokenPrice.fixedRateExchanges[0]
|
const fixed = tokenPrice.fixedRateExchanges[0]
|
||||||
accessDetails.type = 'fixed'
|
accessDetails.type = 'fixed'
|
||||||
accessDetails.addressOrId = fixed.exchangeId
|
accessDetails.addressOrId = fixed.exchangeId
|
||||||
@ -203,7 +223,7 @@ function getAccessDetailsFromTokenPrice(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checking for pools
|
// checking for pools
|
||||||
if (tokenPrice.pools && tokenPrice.pools.length > 0) {
|
if (tokenPrice?.pools?.length > 0) {
|
||||||
const pool = tokenPrice.pools[0]
|
const pool = tokenPrice.pools[0]
|
||||||
accessDetails.type = 'dynamic'
|
accessDetails.type = 'dynamic'
|
||||||
accessDetails.addressOrId = pool.id
|
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
|
* @param {AssetExtended} asset
|
||||||
* @return {Promise<OrdePriceAndFee>}
|
* @return {Promise<OrdePriceAndFee>}
|
||||||
*/
|
*/
|
||||||
export async function getOrderPriceAndFees(
|
export async function getOrderPriceAndFees(
|
||||||
asset: AssetExtended,
|
asset: AssetExtended,
|
||||||
accountId: string,
|
accountId?: string,
|
||||||
paramsForPool: CalcInGivenOutParams
|
paramsForPool?: CalcInGivenOutParams,
|
||||||
|
providerFees?: ProviderFees
|
||||||
): Promise<OrderPriceAndFees> {
|
): Promise<OrderPriceAndFees> {
|
||||||
const orderPriceAndFee = {
|
const orderPriceAndFee = {
|
||||||
price: '0',
|
price: '0',
|
||||||
@ -252,14 +273,17 @@ export async function getOrderPriceAndFees(
|
|||||||
} as OrderPriceAndFees
|
} as OrderPriceAndFees
|
||||||
|
|
||||||
// fetch provider fee
|
// fetch provider fee
|
||||||
const initializeData = await ProviderInstance.initialize(
|
|
||||||
|
const initializeData =
|
||||||
|
!providerFees &&
|
||||||
|
(await ProviderInstance.initialize(
|
||||||
asset?.id,
|
asset?.id,
|
||||||
asset.services[0].id,
|
asset?.services[0].id,
|
||||||
0,
|
0,
|
||||||
accountId,
|
accountId,
|
||||||
asset?.services[0].serviceEndpoint
|
asset?.services[0].serviceEndpoint
|
||||||
)
|
))
|
||||||
orderPriceAndFee.providerFee = initializeData.providerFee
|
orderPriceAndFee.providerFee = providerFees || initializeData.providerFee
|
||||||
|
|
||||||
// fetch price and swap fees
|
// fetch price and swap fees
|
||||||
switch (asset?.accessDetails?.type) {
|
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
|
// calculate full price, we assume that all the values are in ocean, otherwise this will be incorrect
|
||||||
orderPriceAndFee.price = new Decimal(orderPriceAndFee.price)
|
orderPriceAndFee.price = new Decimal(+orderPriceAndFee.price || 0)
|
||||||
.add(new Decimal(orderPriceAndFee.consumeMarketOrderFee))
|
.add(new Decimal(+orderPriceAndFee?.consumeMarketOrderFee || 0))
|
||||||
.add(new Decimal(orderPriceAndFee.publisherMarketOrderFee))
|
.add(new Decimal(+orderPriceAndFee?.publisherMarketOrderFee || 0))
|
||||||
.add(new Decimal(orderPriceAndFee.providerFee.providerFeeAmount))
|
|
||||||
.toString()
|
.toString()
|
||||||
return orderPriceAndFee
|
return orderPriceAndFee
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,12 @@ import { transformAssetToAssetSelection } from './assetConvertor'
|
|||||||
|
|
||||||
export const MAXIMUM_NUMBER_OF_PAGES_WITH_RESULTS = 476
|
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 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
|
* @param value the value of the filter
|
||||||
@ -174,7 +180,7 @@ export async function getAssetsFromDidList(
|
|||||||
didList: string[],
|
didList: string[],
|
||||||
chainIds: number[],
|
chainIds: number[],
|
||||||
cancelToken: CancelToken
|
cancelToken: CancelToken
|
||||||
): Promise<any> {
|
): Promise<PagedAssets> {
|
||||||
try {
|
try {
|
||||||
if (!(didList.length > 0)) return
|
if (!(didList.length > 0)) return
|
||||||
|
|
||||||
@ -249,12 +255,15 @@ export async function getAlgorithmDatasetsForCompute(
|
|||||||
): Promise<AssetSelectionAsset[]> {
|
): Promise<AssetSelectionAsset[]> {
|
||||||
const baseQueryParams = {
|
const baseQueryParams = {
|
||||||
chainIds: [datasetChainId],
|
chainIds: [datasetChainId],
|
||||||
filters: [
|
nestedQuery: {
|
||||||
getFilterTerm(
|
must: {
|
||||||
'service.compite.publisherTrustedAlgorithms.did',
|
match: {
|
||||||
algorithmId
|
'services.compute.publisherTrustedAlgorithms.did': {
|
||||||
)
|
query: escapeEsReservedCharacters(algorithmId)
|
||||||
],
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
sortOptions: {
|
sortOptions: {
|
||||||
sortBy: SortTermOptions.Created,
|
sortBy: SortTermOptions.Created,
|
||||||
sortDirection: SortDirectionOptions.Descending
|
sortDirection: SortDirectionOptions.Descending
|
||||||
|
@ -18,7 +18,7 @@ export async function transformAssetToAssetSelection(
|
|||||||
const algoComputeService = getServiceByName(asset, 'compute')
|
const algoComputeService = getServiceByName(asset, 'compute')
|
||||||
|
|
||||||
if (
|
if (
|
||||||
asset?.accessDetails.price &&
|
asset?.accessDetails?.price &&
|
||||||
algoComputeService?.serviceEndpoint === datasetProviderEndpoint
|
algoComputeService?.serviceEndpoint === datasetProviderEndpoint
|
||||||
) {
|
) {
|
||||||
let selected = false
|
let selected = false
|
||||||
@ -29,7 +29,7 @@ export async function transformAssetToAssetSelection(
|
|||||||
})
|
})
|
||||||
const algorithmAsset: AssetSelectionAsset = {
|
const algorithmAsset: AssetSelectionAsset = {
|
||||||
did: asset.id,
|
did: asset.id,
|
||||||
name: asset.datatokens[0].name,
|
name: asset.metadata.name,
|
||||||
price: asset.accessDetails.price,
|
price: asset.accessDetails.price,
|
||||||
checked: selected,
|
checked: selected,
|
||||||
symbol: asset.datatokens[0].symbol
|
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 {
|
import {
|
||||||
Asset,
|
Asset,
|
||||||
ServiceComputeOptions,
|
ServiceComputeOptions,
|
||||||
PublisherTrustedAlgorithm,
|
PublisherTrustedAlgorithm,
|
||||||
getHash
|
getHash,
|
||||||
|
LoggerInstance,
|
||||||
|
ComputeAlgorithm,
|
||||||
|
DDO,
|
||||||
|
Service,
|
||||||
|
ProviderInstance,
|
||||||
|
ComputeEnvironment,
|
||||||
|
ComputeJob
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
import { CancelToken } from 'axios'
|
import { CancelToken } from 'axios'
|
||||||
import { gql } from 'urql'
|
import { gql } from 'urql'
|
||||||
@ -24,6 +20,11 @@ import {
|
|||||||
retrieveDDOListByDIDs
|
retrieveDDOListByDIDs
|
||||||
} from './aquarius'
|
} from './aquarius'
|
||||||
import { fetchDataForMultipleChains } from './subgraph'
|
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`
|
const getComputeOrders = gql`
|
||||||
query ComputeOrders($user: String!) {
|
query ComputeOrders($user: String!) {
|
||||||
@ -72,201 +73,256 @@ async function getAssetMetadata(
|
|||||||
const baseQueryparams = {
|
const baseQueryparams = {
|
||||||
chainIds,
|
chainIds,
|
||||||
filters: [
|
filters: [
|
||||||
getFilterTerm('dataToken', queryDtList),
|
getFilterTerm('services.datatokenAddress', queryDtList),
|
||||||
getFilterTerm('service.type', 'compute'),
|
getFilterTerm('services.type', 'compute'),
|
||||||
getFilterTerm('service.attributes.main.type', 'dataset')
|
getFilterTerm('metadata.type', 'dataset')
|
||||||
],
|
],
|
||||||
ignorePurgatory: true
|
ignorePurgatory: true
|
||||||
} as BaseQueryParams
|
} as BaseQueryParams
|
||||||
const query = generateBaseQuery(baseQueryparams)
|
const query = generateBaseQuery(baseQueryparams)
|
||||||
const result = await queryMetadata(query, cancelToken)
|
const result = await queryMetadata(query, cancelToken)
|
||||||
|
return result?.results
|
||||||
return result.results
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getServiceEndpoints(data: TokenOrder[], assets: Asset[]): string[] {
|
export async function isOrderable(
|
||||||
// const serviceEndpoints: string[] = []
|
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++) {
|
if (datasetService.type === 'compute') {
|
||||||
// try {
|
if (algorithm.meta) {
|
||||||
// const did = web3.utils
|
// check if raw algo is allowed
|
||||||
// .toChecksumAddress(data[i].datatokenId.address)
|
if (datasetService.compute.allowRawAlgorithm) return true
|
||||||
// .replace('0x', 'did:op:')
|
LoggerInstance.error('ERROR: This service does not allow raw algorithm')
|
||||||
// const ddo = assets.filter((x) => x.id === did)[0]
|
return false
|
||||||
// if (ddo === undefined) continue
|
}
|
||||||
|
if (algorithm.documentId) {
|
||||||
// const service = ddo.services.filter(
|
const algoService: Service = getServiceById(
|
||||||
// (x: Service) => x.index === data[i].serviceId
|
algorithmDDO,
|
||||||
// )[0]
|
algorithm.serviceId
|
||||||
|
)
|
||||||
// if (!service || service.type !== 'compute') continue
|
if (algoService && algoService.type === 'compute') {
|
||||||
// const { providerEndpoint } = service
|
if (algoService.serviceEndpoint !== datasetService.serviceEndpoint) {
|
||||||
|
this.logger.error(
|
||||||
// const wasProviderQueried =
|
'ERROR: Both assets with compute service are not served by the same provider'
|
||||||
// serviceEndpoints?.filter((x) => x === providerEndpoint).length > 0
|
)
|
||||||
|
return false
|
||||||
// if (wasProviderQueried) continue
|
}
|
||||||
// serviceEndpoints.push(providerEndpoint)
|
}
|
||||||
// } catch (err) {
|
}
|
||||||
// LoggerInstance.error(err.message)
|
}
|
||||||
// }
|
return true
|
||||||
// }
|
|
||||||
|
|
||||||
// return serviceEndpoints
|
|
||||||
|
|
||||||
return ['dummy']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// async function getProviders(
|
export function getValidUntilTime(
|
||||||
// serviceEndpoints: string[],
|
computeEnvMaxJobDuration: number,
|
||||||
// config: Config,
|
datasetTimeout?: number,
|
||||||
// ocean: Ocean
|
algorithmTimeout?: number
|
||||||
// ): Promise<Provider[]> {
|
) {
|
||||||
// const providers: Provider[] = []
|
const inputValues = []
|
||||||
|
computeEnvMaxJobDuration && inputValues.push(computeEnvMaxJobDuration)
|
||||||
|
datasetTimeout && inputValues.push(datasetTimeout)
|
||||||
|
algorithmTimeout && inputValues.push(algorithmTimeout)
|
||||||
|
|
||||||
// try {
|
const minValue = Math.min(...inputValues)
|
||||||
// for (let i = 0; i < serviceEndpoints?.length; i++) {
|
const mytime = new Date()
|
||||||
// const instanceConfig = {
|
mytime.setMinutes(mytime.getMinutes() + Math.floor(minValue / 60))
|
||||||
// config,
|
return Math.floor(mytime.getTime() / 1000)
|
||||||
// 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)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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(
|
export function getQueryString(
|
||||||
// providers: Provider[],
|
trustedAlgorithmList: PublisherTrustedAlgorithm[],
|
||||||
// account: Account,
|
trustedPublishersList: string[],
|
||||||
// assets: Asset[]
|
chainId?: number
|
||||||
// ): Promise<ComputeJobMetaData[]> {
|
): SearchQuery {
|
||||||
// const computeJobs: ComputeJobMetaData[] = []
|
const algorithmDidList = trustedAlgorithmList?.map((x) => x.did)
|
||||||
|
|
||||||
// for (let i = 0; i < providers.length; i++) {
|
const baseParams = {
|
||||||
// try {
|
chainIds: [chainId],
|
||||||
// const providerComputeJobs = (await providers[i].computeStatus(
|
sort: { sortBy: SortTermOptions.Created },
|
||||||
// '',
|
filters: [getFilterTerm('metadata.type', 'algorithm')]
|
||||||
// account,
|
} as BaseQueryParams
|
||||||
// undefined,
|
algorithmDidList?.length > 0 &&
|
||||||
// undefined,
|
baseParams.filters.push(getFilterTerm('_id', algorithmDidList))
|
||||||
// false
|
trustedPublishersList?.length > 0 &&
|
||||||
// )) as ComputeJob[]
|
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
|
return query
|
||||||
// if (!providerComputeJobs) continue
|
}
|
||||||
// providerComputeJobs.sort((a, b) => {
|
|
||||||
// if (a.dateCreated > b.dateCreated) {
|
|
||||||
// return -1
|
|
||||||
// }
|
|
||||||
// if (a.dateCreated < b.dateCreated) {
|
|
||||||
// return 1
|
|
||||||
// }
|
|
||||||
// return 0
|
|
||||||
// })
|
|
||||||
|
|
||||||
// for (let j = 0; j < providerComputeJobs?.length; j++) {
|
export async function getAlgorithmsForAsset(
|
||||||
// const job = providerComputeJobs[j]
|
asset: Asset,
|
||||||
// const did = job.inputDID[0]
|
token: CancelToken
|
||||||
// const ddo = assets.filter((x) => x.id === did)[0]
|
): 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 = {
|
const gueryResults = await queryMetadata(
|
||||||
// ...job,
|
getQueryString(
|
||||||
// assetName: ddo.metadata.name,
|
computeService.compute.publisherTrustedAlgorithms,
|
||||||
// assetDtSymbol: ddo.dataTokenInfo.symbol,
|
computeService.compute.publisherTrustedAlgorithmPublishers,
|
||||||
// networkId: ddo.chainId
|
asset.chainId
|
||||||
// }
|
),
|
||||||
// computeJobs.push(compJob)
|
token
|
||||||
// }
|
)
|
||||||
// } catch (err) {
|
|
||||||
// LoggerInstance.error(err.message)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return computeJobs
|
const algorithms: Asset[] = gueryResults?.results
|
||||||
// }
|
return algorithms
|
||||||
|
}
|
||||||
|
|
||||||
// function getDtList(data: TokenOrder[]): string[] {
|
export async function getAlgorithmAssetSelectionList(
|
||||||
// const dtList = []
|
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++) {
|
async function getJobs(
|
||||||
// dtList.push(data[i].datatokenId.address)
|
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(
|
providerComputeJobs.forEach((job) => {
|
||||||
// chainIds: number[],
|
const did = job.inputDID[0]
|
||||||
// account: Account,
|
const asset = assets.filter((x) => x.id === did)[0]
|
||||||
// ddo?: Asset,
|
if (asset) {
|
||||||
// token?: CancelToken
|
const compJob: ComputeJobMetaData = {
|
||||||
// ): Promise<ComputeResults> {
|
...job,
|
||||||
// const assetDTAddress = ddo?.dataTokenInfo?.address
|
assetName: asset.metadata.name,
|
||||||
// let computeResult: ComputeResults = {
|
assetDtSymbol: asset.datatokens[0].symbol,
|
||||||
// computeJobs: [],
|
networkId: asset.chainId
|
||||||
// isLoaded: false
|
}
|
||||||
// }
|
computeJobs.push(compJob)
|
||||||
// let isLoading = true
|
}
|
||||||
// const variables = assetDTAddress
|
})
|
||||||
// ? {
|
}
|
||||||
// user: account?.getId().toLowerCase(),
|
} catch (err) {
|
||||||
// datatokenAddress: assetDTAddress.toLowerCase()
|
LoggerInstance.error(err.message)
|
||||||
// }
|
}
|
||||||
// : {
|
// }
|
||||||
// user: account?.getId().toLowerCase()
|
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(
|
const results = await fetchDataForMultipleChains(
|
||||||
// assetDTAddress ? getComputeOrdersByDatatokenAddress : getComputeOrders,
|
assetDTAddress ? getComputeOrdersByDatatokenAddress : getComputeOrders,
|
||||||
// variables,
|
variables,
|
||||||
// assetDTAddress ? [ddo?.chainId] : chainIds
|
assetDTAddress ? [asset?.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) {
|
let tokenOrders: TokenOrder[] = []
|
||||||
// return computeResult
|
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)
|
tokenOrders = tokenOrders.sort(
|
||||||
// const queryDtList = getDtList(data)
|
(a, b) => b.createdTimestamp - a.createdTimestamp
|
||||||
// if (!queryDtList) return
|
)
|
||||||
|
|
||||||
// const assets = await getAssetMetadata(queryDtList, token, chainIds)
|
const datatokenAddressList = tokenOrders.map(
|
||||||
// const serviceEndpoints = getServiceEndpoints(data, assets)
|
(tokenOrder: TokenOrder) => tokenOrder.datatoken.address
|
||||||
// const providers: Provider[] = await getProviders(
|
)
|
||||||
// serviceEndpoints,
|
if (!datatokenAddressList) return
|
||||||
// config,
|
|
||||||
// ocean
|
|
||||||
// )
|
|
||||||
// const computeJobs = await getJobs(providers, account, assets)
|
|
||||||
// isLoading = false
|
|
||||||
// computeResult = {
|
|
||||||
// computeJobs: computeJobs,
|
|
||||||
// isLoaded: isLoading
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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(
|
export async function createTrustedAlgorithmList(
|
||||||
selectedAlgorithms: string[], // list of DIDs,
|
selectedAlgorithms: string[], // list of DIDs,
|
||||||
@ -282,10 +338,16 @@ export async function createTrustedAlgorithmList(
|
|||||||
)
|
)
|
||||||
|
|
||||||
for (const selectedAlgorithm of selectedAssets) {
|
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 = {
|
const trustedAlgorithm = {
|
||||||
did: selectedAlgorithm.id,
|
did: selectedAlgorithm.id,
|
||||||
containerSectionChecksum: getHash(
|
containerSectionChecksum: getHash(
|
||||||
JSON.stringify(selectedAlgorithm.metadata.algorithm.container)
|
JSON.stringify(sanitizedAlgorithmContainer)
|
||||||
),
|
),
|
||||||
filesChecksum: getHash(selectedAlgorithm.services[0].files)
|
filesChecksum: getHash(selectedAlgorithm.services[0].files)
|
||||||
}
|
}
|
||||||
@ -315,3 +377,31 @@ export async function transformComputeFormToServiceComputeOptions(
|
|||||||
|
|
||||||
return privacy
|
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
|
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 {
|
export function mapTimeoutStringToSeconds(timeout: string): number {
|
||||||
switch (timeout) {
|
switch (timeout) {
|
||||||
case 'Forever':
|
case 'Forever':
|
||||||
|
@ -11,9 +11,16 @@ export function getOrderFeedback(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: customize for compute
|
export function getComputeFeedback(
|
||||||
export const computeFeedback: { [key in number]: string } = {
|
baseTokenSymbol?: string,
|
||||||
0: 'Ordering asset...',
|
datatokenSymbol?: string,
|
||||||
1: 'Transfering datatoken.',
|
assetType?: string
|
||||||
2: 'Access granted. Starting job...'
|
): { [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 { AccessDetails } from 'src/@types/Price'
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import { getOceanConfig } from './ocean'
|
import { getOceanConfig } from './ocean'
|
||||||
|
import { consumeMarketPoolSwapFee } from '../../app.config'
|
||||||
import { getDummyWeb3 } from './web3'
|
import { getDummyWeb3 } from './web3'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,7 +29,8 @@ export async function getFixedBuyPrice(
|
|||||||
const fixed = new FixedRateExchange(web3, config.fixedRateExchangeAddress)
|
const fixed = new FixedRateExchange(web3, config.fixedRateExchangeAddress)
|
||||||
const estimatedPrice = await fixed.calcBaseInGivenOutDT(
|
const estimatedPrice = await fixed.calcBaseInGivenOutDT(
|
||||||
accessDetails.addressOrId,
|
accessDetails.addressOrId,
|
||||||
'1'
|
'1',
|
||||||
|
consumeMarketPoolSwapFee
|
||||||
)
|
)
|
||||||
return estimatedPrice
|
return estimatedPrice
|
||||||
}
|
}
|
||||||
|
@ -112,21 +112,6 @@ export async function setNftMetadata(
|
|||||||
// theoretically used by aquarius or provider, not implemented yet, will remain hardcoded
|
// theoretically used by aquarius or provider, not implemented yet, will remain hardcoded
|
||||||
const flags = '0x2'
|
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(
|
const setMetadataTx = await nft.setMetadata(
|
||||||
asset.nftAddress,
|
asset.nftAddress,
|
||||||
accountId,
|
accountId,
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
approve,
|
approve,
|
||||||
|
approveWei,
|
||||||
Datatoken,
|
Datatoken,
|
||||||
FreOrderParams,
|
FreOrderParams,
|
||||||
|
LoggerInstance,
|
||||||
OrderParams,
|
OrderParams,
|
||||||
|
ProviderComputeInitialize,
|
||||||
|
ProviderFees,
|
||||||
ProviderInstance
|
ProviderInstance
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||||
@ -15,35 +19,44 @@ import {
|
|||||||
consumeMarketOrderFee,
|
consumeMarketOrderFee,
|
||||||
consumeMarketFixedSwapFee
|
consumeMarketFixedSwapFee
|
||||||
} from '../../app.config'
|
} 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
|
* For pool you need to buy the datatoken beforehand, this always assumes you want to order the first service
|
||||||
* @param web3
|
* @param web3
|
||||||
* @param asset
|
* @param asset
|
||||||
|
* @param orderPriceAndFees
|
||||||
* @param accountId
|
* @param accountId
|
||||||
|
* @param providerFees
|
||||||
|
* @param computeConsumerAddress
|
||||||
* @returns {TransactionReceipt} receipt of the order
|
* @returns {TransactionReceipt} receipt of the order
|
||||||
*/
|
*/
|
||||||
export async function order(
|
export async function order(
|
||||||
web3: Web3,
|
web3: Web3,
|
||||||
asset: AssetExtended,
|
asset: AssetExtended,
|
||||||
orderPriceAndFees: OrderPriceAndFees,
|
orderPriceAndFees: OrderPriceAndFees,
|
||||||
accountId: string
|
accountId: string,
|
||||||
|
providerFees?: ProviderFees,
|
||||||
|
computeConsumerAddress?: string
|
||||||
): Promise<TransactionReceipt> {
|
): Promise<TransactionReceipt> {
|
||||||
const datatoken = new Datatoken(web3)
|
const datatoken = new Datatoken(web3)
|
||||||
const config = getOceanConfig(asset.chainId)
|
const config = getOceanConfig(asset.chainId)
|
||||||
|
|
||||||
const initializeData = await ProviderInstance.initialize(
|
const initializeData =
|
||||||
|
!providerFees &&
|
||||||
|
(await ProviderInstance.initialize(
|
||||||
asset.id,
|
asset.id,
|
||||||
asset.services[0].id,
|
asset.services[0].id,
|
||||||
0,
|
0,
|
||||||
accountId,
|
accountId,
|
||||||
asset.services[0].serviceEndpoint
|
asset.services[0].serviceEndpoint
|
||||||
)
|
))
|
||||||
|
|
||||||
const orderParams = {
|
const orderParams = {
|
||||||
consumer: accountId,
|
consumer: computeConsumerAddress || accountId,
|
||||||
serviceIndex: 0,
|
serviceIndex: 0,
|
||||||
_providerFee: initializeData.providerFee,
|
_providerFee: providerFees || initializeData.providerFee,
|
||||||
_consumeMarketFee: {
|
_consumeMarketFee: {
|
||||||
consumeMarketFeeAddress: marketFeeAddress,
|
consumeMarketFeeAddress: marketFeeAddress,
|
||||||
consumeMarketFeeAmount: consumeMarketOrderFee,
|
consumeMarketFeeAmount: consumeMarketOrderFee,
|
||||||
@ -51,7 +64,6 @@ export async function order(
|
|||||||
}
|
}
|
||||||
} as OrderParams
|
} as OrderParams
|
||||||
|
|
||||||
// TODO: we need to approve provider fee
|
|
||||||
switch (asset.accessDetails?.type) {
|
switch (asset.accessDetails?.type) {
|
||||||
case 'fixed': {
|
case 'fixed': {
|
||||||
// this assumes all fees are in ocean
|
// this assumes all fees are in ocean
|
||||||
@ -87,9 +99,9 @@ export async function order(
|
|||||||
const tx = await datatoken.startOrder(
|
const tx = await datatoken.startOrder(
|
||||||
asset.accessDetails.datatoken.address,
|
asset.accessDetails.datatoken.address,
|
||||||
accountId,
|
accountId,
|
||||||
accountId,
|
computeConsumerAddress || accountId,
|
||||||
0,
|
0,
|
||||||
initializeData.providerFee
|
providerFees || initializeData.providerFee
|
||||||
)
|
)
|
||||||
return tx
|
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 {
|
import {
|
||||||
|
ComputeAlgorithm,
|
||||||
|
ComputeAsset,
|
||||||
|
ComputeEnvironment,
|
||||||
downloadFileBrowser,
|
downloadFileBrowser,
|
||||||
FileInfo,
|
FileInfo,
|
||||||
LoggerInstance,
|
LoggerInstance,
|
||||||
|
ProviderComputeInitializeResults,
|
||||||
ProviderInstance
|
ProviderInstance
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||||
import Web3 from 'web3'
|
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 ?!?!?!
|
// TODO: Why do we have these one line functions ?!?!?!
|
||||||
export async function getEncryptedFiles(
|
export async function getEncryptedFiles(
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData'
|
import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData'
|
||||||
import { UserSalesQuery as UsersSalesList } from '../@types/subgraph/UserSalesQuery'
|
import { UserSalesQuery as UsersSalesList } from '../@types/subgraph/UserSalesQuery'
|
||||||
import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery'
|
import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery'
|
||||||
import { calcSingleOutGivenPoolIn, getLiquidityByShares } from './pool'
|
import { calcSingleOutGivenPoolIn } from './pool'
|
||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
import { MAX_DECIMALS } from './constants'
|
import { MAX_DECIMALS } from './constants'
|
||||||
|
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
margin-top: calc(var(--spacer) / 2);
|
margin-top: calc(var(--spacer) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actionsCenter {
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.help {
|
.help {
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
|
@ -28,7 +28,8 @@ interface ButtonBuyProps {
|
|||||||
type?: 'submit'
|
type?: 'submit'
|
||||||
priceType?: string
|
priceType?: string
|
||||||
algorithmPriceType?: string
|
algorithmPriceType?: string
|
||||||
algorithmConsumableStatus?: number
|
isAlgorithmConsumable?: boolean
|
||||||
|
hasProviderFee?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we need to take a look at these messages
|
// TODO: we need to take a look at these messages
|
||||||
@ -75,7 +76,8 @@ function getComputeAssetHelpText(
|
|||||||
dtBalanceSelectedComputeAsset?: string,
|
dtBalanceSelectedComputeAsset?: string,
|
||||||
selectedComputeAssettLowPoolLiquidity?: boolean,
|
selectedComputeAssettLowPoolLiquidity?: boolean,
|
||||||
selectedComputeAssetType?: string,
|
selectedComputeAssetType?: string,
|
||||||
algorithmConsumableStatus?: number
|
isAlgorithmConsumable?: boolean,
|
||||||
|
hasProviderFee?: boolean
|
||||||
) {
|
) {
|
||||||
const computeAssetHelpText = getConsumeHelpText(
|
const computeAssetHelpText = getConsumeHelpText(
|
||||||
dtBalance,
|
dtBalance,
|
||||||
@ -90,14 +92,9 @@ function getComputeAssetHelpText(
|
|||||||
)
|
)
|
||||||
const computeAlgoHelpText =
|
const computeAlgoHelpText =
|
||||||
(!dtSymbolSelectedComputeAsset && !dtBalanceSelectedComputeAsset) ||
|
(!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
|
: hasPreviousOrderSelectedComputeAsset
|
||||||
? `You already bought the selected ${selectedComputeAssetType}, allowing you to use it without paying again.`
|
? `You already bought the selected ${selectedComputeAssetType}, allowing you to use it without paying again.`
|
||||||
: hasDatatokenSelectedComputeAsset
|
: hasDatatokenSelectedComputeAsset
|
||||||
@ -107,11 +104,14 @@ function getComputeAssetHelpText(
|
|||||||
: isBalanceSufficient === false
|
: isBalanceSufficient === false
|
||||||
? ''
|
? ''
|
||||||
: `Additionally, you will buy 1 ${dtSymbolSelectedComputeAsset} for the ${selectedComputeAssetType} and spend it back to its publisher and pool.`
|
: `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
|
const computeHelpText = selectedComputeAssettLowPoolLiquidity
|
||||||
? computeAlgoHelpText
|
? computeAlgoHelpText
|
||||||
: lowPoolLiquidity
|
: lowPoolLiquidity
|
||||||
? computeAssetHelpText
|
? computeAssetHelpText
|
||||||
: `${computeAssetHelpText} ${computeAlgoHelpText}`
|
: `${computeAssetHelpText} ${computeAlgoHelpText} ${providerFeeHelpText}`
|
||||||
return computeHelpText
|
return computeHelpText
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +140,8 @@ export default function ButtonBuy({
|
|||||||
type,
|
type,
|
||||||
priceType,
|
priceType,
|
||||||
algorithmPriceType,
|
algorithmPriceType,
|
||||||
algorithmConsumableStatus
|
isAlgorithmConsumable,
|
||||||
|
hasProviderFee
|
||||||
}: ButtonBuyProps): ReactElement {
|
}: ButtonBuyProps): ReactElement {
|
||||||
const buttonText =
|
const buttonText =
|
||||||
action === 'download'
|
action === 'download'
|
||||||
@ -149,7 +150,9 @@ export default function ButtonBuy({
|
|||||||
: priceType === 'free'
|
: priceType === 'free'
|
||||||
? 'Get'
|
? 'Get'
|
||||||
: `Buy ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}`
|
: `Buy ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}`
|
||||||
: hasPreviousOrder && hasPreviousOrderSelectedComputeAsset
|
: hasPreviousOrder &&
|
||||||
|
hasPreviousOrderSelectedComputeAsset &&
|
||||||
|
!hasProviderFee
|
||||||
? 'Start Compute Job'
|
? 'Start Compute Job'
|
||||||
: priceType === 'free' && algorithmPriceType === 'free'
|
: priceType === 'free' && algorithmPriceType === 'free'
|
||||||
? 'Order Compute Job'
|
? 'Order Compute Job'
|
||||||
@ -166,6 +169,7 @@ export default function ButtonBuy({
|
|||||||
type={type}
|
type={type}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
className={action === 'compute' ? styles.actionsCenter : ''}
|
||||||
>
|
>
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</Button>
|
</Button>
|
||||||
@ -198,7 +202,8 @@ export default function ButtonBuy({
|
|||||||
dtBalanceSelectedComputeAsset,
|
dtBalanceSelectedComputeAsset,
|
||||||
selectedComputeAssetLowPoolLiquidity,
|
selectedComputeAssetLowPoolLiquidity,
|
||||||
selectedComputeAssetType,
|
selectedComputeAssetType,
|
||||||
algorithmConsumableStatus
|
isAlgorithmConsumable,
|
||||||
|
hasProviderFee
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -12,6 +12,7 @@ export default function Blockies({
|
|||||||
className
|
className
|
||||||
}: BlockiesProps): ReactElement {
|
}: BlockiesProps): ReactElement {
|
||||||
if (!accountId) return null
|
if (!accountId) return null
|
||||||
|
|
||||||
const blockies = toDataUrl(accountId)
|
const blockies = toDataUrl(accountId)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -5,7 +5,6 @@ import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
|
|||||||
import AssetComputeList from '@shared/AssetList/AssetComputeList'
|
import AssetComputeList from '@shared/AssetList/AssetComputeList'
|
||||||
import { useCancelToken } from '@hooks/useCancelToken'
|
import { useCancelToken } from '@hooks/useCancelToken'
|
||||||
import { getServiceByName } from '@utils/ddo'
|
import { getServiceByName } from '@utils/ddo'
|
||||||
import { Asset } from '@oceanprotocol/lib'
|
|
||||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||||
|
|
||||||
export default function AlgorithmDatasetsListForCompute({
|
export default function AlgorithmDatasetsListForCompute({
|
||||||
@ -15,9 +14,9 @@ export default function AlgorithmDatasetsListForCompute({
|
|||||||
asset: AssetExtended
|
asset: AssetExtended
|
||||||
algorithmDid: string
|
algorithmDid: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const newCancelToken = useCancelToken()
|
||||||
const [datasetsForCompute, setDatasetsForCompute] =
|
const [datasetsForCompute, setDatasetsForCompute] =
|
||||||
useState<AssetSelectionAsset[]>()
|
useState<AssetSelectionAsset[]>()
|
||||||
const newCancelToken = useCancelToken()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!asset) return
|
if (!asset) return
|
||||||
@ -37,7 +36,7 @@ export default function AlgorithmDatasetsListForCompute({
|
|||||||
setDatasetsForCompute(datasets)
|
setDatasetsForCompute(datasets)
|
||||||
}
|
}
|
||||||
asset.metadata.type === 'algorithm' && getDatasetsAllowedForCompute()
|
asset.metadata.type === 'algorithm' && getDatasetsAllowedForCompute()
|
||||||
}, [asset?.metadata?.type])
|
}, [asset, algorithmDid, newCancelToken])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.datasetsContainer}>
|
<div className={styles.datasetsContainer}>
|
||||||
|
@ -10,13 +10,18 @@ import { useAsset } from '@context/Asset'
|
|||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
import content from '../../../../../content/pages/startComputeDataset.json'
|
import content from '../../../../../content/pages/startComputeDataset.json'
|
||||||
import { Asset } from '@oceanprotocol/lib'
|
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 { useMarketMetadata } from '@context/MarketMetadata'
|
||||||
import Alert from '@shared/atoms/Alert'
|
import Alert from '@shared/atoms/Alert'
|
||||||
|
|
||||||
export default function FormStartCompute({
|
export default function FormStartCompute({
|
||||||
algorithms,
|
algorithms,
|
||||||
ddoListAlgorithms,
|
ddoListAlgorithms,
|
||||||
|
selectedAlgorithmAsset,
|
||||||
setSelectedAlgorithm,
|
setSelectedAlgorithm,
|
||||||
isLoading,
|
isLoading,
|
||||||
isComputeButtonDisabled,
|
isComputeButtonDisabled,
|
||||||
@ -35,13 +40,17 @@ export default function FormStartCompute({
|
|||||||
selectedComputeAssetType,
|
selectedComputeAssetType,
|
||||||
selectedComputeAssetTimeout,
|
selectedComputeAssetTimeout,
|
||||||
stepText,
|
stepText,
|
||||||
algorithmConsumeDetails,
|
|
||||||
isConsumable,
|
isConsumable,
|
||||||
consumableFeedback
|
consumableFeedback,
|
||||||
|
datasetOrderPriceAndFees,
|
||||||
|
algoOrderPriceAndFees,
|
||||||
|
providerFeeAmount,
|
||||||
|
validUntil
|
||||||
}: {
|
}: {
|
||||||
algorithms: AssetSelectionAsset[]
|
algorithms: AssetSelectionAsset[]
|
||||||
ddoListAlgorithms: Asset[]
|
ddoListAlgorithms: Asset[]
|
||||||
setSelectedAlgorithm: React.Dispatch<React.SetStateAction<Asset>>
|
selectedAlgorithmAsset: AssetExtended
|
||||||
|
setSelectedAlgorithm: React.Dispatch<React.SetStateAction<AssetExtended>>
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
isComputeButtonDisabled: boolean
|
isComputeButtonDisabled: boolean
|
||||||
hasPreviousOrder: boolean
|
hasPreviousOrder: boolean
|
||||||
@ -59,19 +68,27 @@ export default function FormStartCompute({
|
|||||||
selectedComputeAssetType?: string
|
selectedComputeAssetType?: string
|
||||||
selectedComputeAssetTimeout?: string
|
selectedComputeAssetTimeout?: string
|
||||||
stepText: string
|
stepText: string
|
||||||
algorithmConsumeDetails: AccessDetails
|
|
||||||
isConsumable: boolean
|
isConsumable: boolean
|
||||||
consumableFeedback: string
|
consumableFeedback: string
|
||||||
|
datasetOrderPriceAndFees?: OrderPriceAndFees
|
||||||
|
algoOrderPriceAndFees?: OrderPriceAndFees
|
||||||
|
providerFeeAmount?: string
|
||||||
|
validUntil?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { siteContent } = useMarketMetadata()
|
const { siteContent } = useMarketMetadata()
|
||||||
|
const { accountId, balance } = useWeb3()
|
||||||
const { isValid, values }: FormikContextType<{ algorithm: string }> =
|
const { isValid, values }: FormikContextType<{ algorithm: string }> =
|
||||||
useFormikContext()
|
useFormikContext()
|
||||||
const { asset, isAssetNetwork } = useAsset()
|
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 [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>(false)
|
||||||
const { accountId, balance } = useWeb3()
|
|
||||||
const [algorithmConsumableStatus, setAlgorithmConsumableStatus] =
|
|
||||||
useState<number>()
|
|
||||||
|
|
||||||
function getAlgorithmAsset(algorithmId: string): Asset {
|
function getAlgorithmAsset(algorithmId: string): Asset {
|
||||||
let assetDdo = null
|
let assetDdo = null
|
||||||
@ -82,50 +99,79 @@ export default function FormStartCompute({
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!values.algorithm) return
|
if (!values.algorithm || !accountId || !isConsumable) return
|
||||||
const algorithmDDO = getAlgorithmAsset(values.algorithm)
|
|
||||||
setSelectedAlgorithm(algorithmDDO)
|
|
||||||
|
|
||||||
if (!accountId || !isConsumable) return
|
async function fetchAlgorithmAssetExtended() {
|
||||||
async function checkIsConsumable() {
|
const algorithmAsset = getAlgorithmAsset(values.algorithm)
|
||||||
// const consumable = await ocean.assets.isConsumable(
|
const accessDetails = await getAccessDetails(
|
||||||
// algorithmDDO as any,
|
algorithmAsset.chainId,
|
||||||
// accountId.toLowerCase()
|
algorithmAsset.services[0].datatokenAddress,
|
||||||
// )
|
algorithmAsset.services[0].timeout,
|
||||||
// if (consumable) setAlgorithmConsumableStatus(consumable.status)
|
accountId
|
||||||
|
)
|
||||||
|
const extendedAlgoAsset: AssetExtended = {
|
||||||
|
...algorithmAsset,
|
||||||
|
accessDetails
|
||||||
}
|
}
|
||||||
checkIsConsumable()
|
setSelectedAlgorithm(extendedAlgoAsset)
|
||||||
|
}
|
||||||
|
fetchAlgorithmAssetExtended()
|
||||||
}, [values.algorithm, accountId, isConsumable])
|
}, [values.algorithm, accountId, isConsumable])
|
||||||
|
|
||||||
//
|
//
|
||||||
// Set price for calculation output
|
// Set price for calculation output
|
||||||
//
|
//
|
||||||
useEffect(() => {
|
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 =
|
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 =
|
const priceAlgo =
|
||||||
hasPreviousOrderSelectedComputeAsset || hasDatatokenSelectedComputeAsset
|
hasPreviousOrderSelectedComputeAsset || hasDatatokenSelectedComputeAsset
|
||||||
? 0
|
? new Decimal(0)
|
||||||
: Number(algorithmConsumeDetails.price)
|
: new Decimal(
|
||||||
|
algoOrderPriceAndFees?.price ||
|
||||||
setTotalPrice((priceDataset + priceAlgo).toString())
|
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,
|
asset?.accessDetails,
|
||||||
algorithmConsumeDetails,
|
selectedAlgorithmAsset?.accessDetails,
|
||||||
hasPreviousOrder,
|
hasPreviousOrder,
|
||||||
hasDatatoken,
|
hasDatatoken,
|
||||||
hasPreviousOrderSelectedComputeAsset,
|
hasPreviousOrderSelectedComputeAsset,
|
||||||
hasDatatokenSelectedComputeAsset
|
hasDatatokenSelectedComputeAsset,
|
||||||
|
datasetOrderPriceAndFees,
|
||||||
|
algoOrderPriceAndFees,
|
||||||
|
providerFeeAmount
|
||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!totalPrice) return
|
if (!totalPrice || !balance?.ocean || !dtBalance) return
|
||||||
|
|
||||||
setIsBalanceSufficient(
|
setIsBalanceSufficient(
|
||||||
compareAsBN(balance.ocean, `${totalPrice}`) || Number(dtBalance) >= 1
|
compareAsBN(balance.ocean, `${totalPrice}`) || Number(dtBalance) >= 1
|
||||||
)
|
)
|
||||||
}, [totalPrice])
|
}, [totalPrice, balance?.ocean, dtBalance])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form className={styles.form}>
|
<Form className={styles.form}>
|
||||||
@ -140,6 +186,7 @@ export default function FormStartCompute({
|
|||||||
{...field}
|
{...field}
|
||||||
options={algorithms}
|
options={algorithms}
|
||||||
component={Input}
|
component={Input}
|
||||||
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@ -152,9 +199,13 @@ export default function FormStartCompute({
|
|||||||
hasDatatoken={hasDatatoken}
|
hasDatatoken={hasDatatoken}
|
||||||
selectedComputeAssetTimeout={selectedComputeAssetTimeout}
|
selectedComputeAssetTimeout={selectedComputeAssetTimeout}
|
||||||
hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset}
|
hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset}
|
||||||
algorithmConsumeDetails={algorithmConsumeDetails}
|
algorithmConsumeDetails={selectedAlgorithmAsset?.accessDetails}
|
||||||
symbol={oceanSymbol}
|
symbol={oceanSymbol}
|
||||||
totalPrice={Number.parseFloat(totalPrice)}
|
totalPrice={totalPrice}
|
||||||
|
datasetOrderPrice={datasetOrderPrice}
|
||||||
|
algoOrderPrice={algoOrderPrice}
|
||||||
|
providerFeeAmount={providerFeeAmount}
|
||||||
|
validUntil={validUntil}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ButtonBuy
|
<ButtonBuy
|
||||||
@ -164,7 +215,7 @@ export default function FormStartCompute({
|
|||||||
!isValid ||
|
!isValid ||
|
||||||
!isBalanceSufficient ||
|
!isBalanceSufficient ||
|
||||||
!isAssetNetwork ||
|
!isAssetNetwork ||
|
||||||
algorithmConsumableStatus > 0
|
!selectedAlgorithmAsset?.accessDetails?.isPurchasable
|
||||||
}
|
}
|
||||||
hasPreviousOrder={hasPreviousOrder}
|
hasPreviousOrder={hasPreviousOrder}
|
||||||
hasDatatoken={hasDatatoken}
|
hasDatatoken={hasDatatoken}
|
||||||
@ -187,11 +238,14 @@ export default function FormStartCompute({
|
|||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
type="submit"
|
type="submit"
|
||||||
priceType={asset?.accessDetails?.type}
|
priceType={asset?.accessDetails?.type}
|
||||||
algorithmPriceType={algorithmConsumeDetails?.type}
|
algorithmPriceType={selectedAlgorithmAsset?.accessDetails?.type}
|
||||||
isBalanceSufficient={isBalanceSufficient}
|
isBalanceSufficient={isBalanceSufficient}
|
||||||
isConsumable={isConsumable}
|
isConsumable={isConsumable}
|
||||||
consumableFeedback={consumableFeedback}
|
consumableFeedback={consumableFeedback}
|
||||||
algorithmConsumableStatus={algorithmConsumableStatus}
|
isAlgorithmConsumable={
|
||||||
|
selectedAlgorithmAsset?.accessDetails?.isPurchasable
|
||||||
|
}
|
||||||
|
hasProviderFee={providerFeeAmount && providerFeeAmount !== '0'}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
)
|
)
|
||||||
|
@ -36,8 +36,9 @@
|
|||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
padding-top: calc(var(--spacer) / 7);
|
padding-top: calc(var(--spacer) / 7);
|
||||||
padding-bottom: calc(var(--spacer) / 7);
|
padding-bottom: calc(var(--spacer) / 7);
|
||||||
display: flex;
|
display: grid;
|
||||||
justify-content: space-between;
|
grid-template-columns: 5% 1fr auto;
|
||||||
|
column-gap: calc(var(--spacer) / 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
.priceRow:last-child {
|
.priceRow:last-child {
|
||||||
@ -47,8 +48,14 @@
|
|||||||
|
|
||||||
.sign {
|
.sign {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 5%;
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
font-size: var(--font-size-base);
|
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 Tooltip from '@shared/atoms/Tooltip'
|
||||||
import styles from './PriceOutput.module.css'
|
import styles from './PriceOutput.module.css'
|
||||||
import { AccessDetails } from 'src/@types/Price'
|
import { AccessDetails } from 'src/@types/Price'
|
||||||
|
import { MAX_DECIMALS } from '@utils/constants'
|
||||||
|
import Decimal from 'decimal.js'
|
||||||
|
|
||||||
interface PriceOutputProps {
|
interface PriceOutputProps {
|
||||||
totalPrice: number
|
totalPrice: string
|
||||||
hasPreviousOrder: boolean
|
hasPreviousOrder: boolean
|
||||||
hasDatatoken: boolean
|
hasDatatoken: boolean
|
||||||
symbol: string
|
symbol: string
|
||||||
@ -15,6 +17,10 @@ interface PriceOutputProps {
|
|||||||
hasDatatokenSelectedComputeAsset: boolean
|
hasDatatokenSelectedComputeAsset: boolean
|
||||||
algorithmConsumeDetails: AccessDetails
|
algorithmConsumeDetails: AccessDetails
|
||||||
selectedComputeAssetTimeout: string
|
selectedComputeAssetTimeout: string
|
||||||
|
datasetOrderPrice?: number
|
||||||
|
algoOrderPrice?: number
|
||||||
|
providerFeeAmount?: string
|
||||||
|
validUntil?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function Row({
|
function Row({
|
||||||
@ -23,18 +29,21 @@ function Row({
|
|||||||
hasDatatoken,
|
hasDatatoken,
|
||||||
symbol,
|
symbol,
|
||||||
timeout,
|
timeout,
|
||||||
sign
|
sign,
|
||||||
|
type
|
||||||
}: {
|
}: {
|
||||||
price: number
|
price: string
|
||||||
hasPreviousOrder?: boolean
|
hasPreviousOrder?: boolean
|
||||||
hasDatatoken?: boolean
|
hasDatatoken?: boolean
|
||||||
symbol?: string
|
symbol?: string
|
||||||
timeout?: string
|
timeout?: string
|
||||||
sign?: string
|
sign?: string
|
||||||
|
type?: string
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.priceRow}>
|
<div className={styles.priceRow}>
|
||||||
<div className={styles.sign}>{sign}</div>
|
<div className={styles.sign}>{sign}</div>
|
||||||
|
<div className={styles.type}>{type}</div>
|
||||||
<div>
|
<div>
|
||||||
<PriceUnit
|
<PriceUnit
|
||||||
price={hasPreviousOrder || hasDatatoken ? '0' : `${price}`}
|
price={hasPreviousOrder || hasDatatoken ? '0' : `${price}`}
|
||||||
@ -62,7 +71,11 @@ export default function PriceOutput({
|
|||||||
hasPreviousOrderSelectedComputeAsset,
|
hasPreviousOrderSelectedComputeAsset,
|
||||||
hasDatatokenSelectedComputeAsset,
|
hasDatatokenSelectedComputeAsset,
|
||||||
algorithmConsumeDetails,
|
algorithmConsumeDetails,
|
||||||
selectedComputeAssetTimeout
|
selectedComputeAssetTimeout,
|
||||||
|
datasetOrderPrice,
|
||||||
|
algoOrderPrice,
|
||||||
|
providerFeeAmount,
|
||||||
|
validUntil
|
||||||
}: PriceOutputProps): ReactElement {
|
}: PriceOutputProps): ReactElement {
|
||||||
const { asset } = useAsset()
|
const { asset } = useAsset()
|
||||||
|
|
||||||
@ -76,17 +89,34 @@ export default function PriceOutput({
|
|||||||
<Row
|
<Row
|
||||||
hasPreviousOrder={hasPreviousOrder}
|
hasPreviousOrder={hasPreviousOrder}
|
||||||
hasDatatoken={hasDatatoken}
|
hasDatatoken={hasDatatoken}
|
||||||
price={Number.parseFloat(asset?.accessDetails?.price)}
|
price={new Decimal(
|
||||||
|
datasetOrderPrice || asset?.accessDetails?.price || 0
|
||||||
|
)
|
||||||
|
.toDecimalPlaces(MAX_DECIMALS)
|
||||||
|
.toString()}
|
||||||
timeout={assetTimeout}
|
timeout={assetTimeout}
|
||||||
symbol={symbol}
|
symbol={symbol}
|
||||||
|
type="DATASET"
|
||||||
/>
|
/>
|
||||||
<Row
|
<Row
|
||||||
hasPreviousOrder={hasPreviousOrderSelectedComputeAsset}
|
hasPreviousOrder={hasPreviousOrderSelectedComputeAsset}
|
||||||
hasDatatoken={hasDatatokenSelectedComputeAsset}
|
hasDatatoken={hasDatatokenSelectedComputeAsset}
|
||||||
price={Number.parseFloat(algorithmConsumeDetails?.price)}
|
price={new Decimal(
|
||||||
|
algoOrderPrice || algorithmConsumeDetails?.price || 0
|
||||||
|
)
|
||||||
|
.toDecimalPlaces(MAX_DECIMALS)
|
||||||
|
.toString()}
|
||||||
timeout={selectedComputeAssetTimeout}
|
timeout={selectedComputeAssetTimeout}
|
||||||
symbol={symbol}
|
symbol={symbol}
|
||||||
sign="+"
|
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="=" />
|
<Row price={totalPrice} symbol={symbol} sign="=" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,232 +1,291 @@
|
|||||||
import React, { useState, ReactElement, useEffect, useCallback } from 'react'
|
import React, { useState, ReactElement, useEffect } from 'react'
|
||||||
import {
|
import {
|
||||||
Asset,
|
Asset,
|
||||||
DDO,
|
DDO,
|
||||||
PublisherTrustedAlgorithm,
|
FileInfo,
|
||||||
FileInfo
|
Datatoken,
|
||||||
|
ProviderInstance,
|
||||||
|
ComputeAsset,
|
||||||
|
ZERO_ADDRESS,
|
||||||
|
ComputeEnvironment,
|
||||||
|
LoggerInstance,
|
||||||
|
ComputeAlgorithm,
|
||||||
|
ComputeOutput,
|
||||||
|
ProviderComputeInitializeResults,
|
||||||
|
unitsToAmount
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import Price from '@shared/Price'
|
import Price from '@shared/Price'
|
||||||
import FileIcon from '@shared/FileIcon'
|
import FileIcon from '@shared/FileIcon'
|
||||||
import Alert from '@shared/atoms/Alert'
|
import Alert from '@shared/atoms/Alert'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
import {
|
|
||||||
generateBaseQuery,
|
|
||||||
getFilterTerm,
|
|
||||||
queryMetadata
|
|
||||||
} from '@utils/aquarius'
|
|
||||||
import { Formik } from 'formik'
|
import { Formik } from 'formik'
|
||||||
import { getInitialValues, validationSchema } from './_constants'
|
import { getInitialValues, validationSchema } from './_constants'
|
||||||
import axios from 'axios'
|
|
||||||
import FormStartComputeDataset from './FormComputeDataset'
|
import FormStartComputeDataset from './FormComputeDataset'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import SuccessConfetti from '@shared/SuccessConfetti'
|
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 { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
|
||||||
import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute'
|
import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute'
|
||||||
import { getPreviousOrders } from '@utils/subgraph'
|
|
||||||
import AssetActionHistoryTable from '../AssetActionHistoryTable'
|
import AssetActionHistoryTable from '../AssetActionHistoryTable'
|
||||||
import ComputeJobs from '../../../Profile/History/ComputeJobs'
|
import ComputeJobs from '../../../Profile/History/ComputeJobs'
|
||||||
import { useCancelToken } from '@hooks/useCancelToken'
|
import { useCancelToken } from '@hooks/useCancelToken'
|
||||||
import { useIsMounted } from '@hooks/useIsMounted'
|
import { Decimal } from 'decimal.js'
|
||||||
import { SortTermOptions } from '../../../../@types/aquarius/SearchQuery'
|
import { useAbortController } from '@hooks/useAbortController'
|
||||||
import { getAccessDetails } from '@utils/accessDetailsAndPricing'
|
import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing'
|
||||||
import { AccessDetails } from 'src/@types/Price'
|
import { OrderPriceAndFees } from 'src/@types/Price'
|
||||||
import { transformAssetToAssetSelection } from '@utils/assetConvertor'
|
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 { useMarketMetadata } from '@context/MarketMetadata'
|
||||||
|
import { getPoolData } from '@context/Pool/_utils'
|
||||||
|
import { getDummyWeb3 } from '@utils/web3'
|
||||||
|
import { initializeProviderForCompute } from '@utils/provider'
|
||||||
|
|
||||||
export default function Compute({
|
export default function Compute({
|
||||||
ddo,
|
asset,
|
||||||
accessDetails,
|
|
||||||
dtBalance,
|
dtBalance,
|
||||||
file,
|
file,
|
||||||
fileIsLoading,
|
fileIsLoading,
|
||||||
isConsumable,
|
|
||||||
consumableFeedback
|
consumableFeedback
|
||||||
}: {
|
}: {
|
||||||
ddo: Asset
|
asset: AssetExtended
|
||||||
accessDetails: AccessDetails
|
|
||||||
dtBalance: string
|
dtBalance: string
|
||||||
file: FileInfo
|
file: FileInfo
|
||||||
fileIsLoading?: boolean
|
fileIsLoading?: boolean
|
||||||
isConsumable?: boolean
|
|
||||||
consumableFeedback?: string
|
consumableFeedback?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { appConfig } = useMarketMetadata()
|
const { accountId, web3 } = useWeb3()
|
||||||
const { accountId } = useWeb3()
|
const { getOpcFeeForToken } = useMarketMetadata()
|
||||||
const [isJobStarting, setIsJobStarting] = useState(false)
|
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 [error, setError] = useState<string>()
|
||||||
|
|
||||||
const [algorithmList, setAlgorithmList] = useState<AssetSelectionAsset[]>()
|
const [algorithmList, setAlgorithmList] = useState<AssetSelectionAsset[]>()
|
||||||
const [ddoAlgorithmList, setDdoAlgorithmList] = useState<Asset[]>()
|
const [ddoAlgorithmList, setDdoAlgorithmList] = useState<Asset[]>()
|
||||||
const [selectedAlgorithmAsset, setSelectedAlgorithmAsset] = useState<Asset>()
|
const [selectedAlgorithmAsset, setSelectedAlgorithmAsset] =
|
||||||
|
useState<AssetExtended>()
|
||||||
const [hasAlgoAssetDatatoken, setHasAlgoAssetDatatoken] = useState<boolean>()
|
const [hasAlgoAssetDatatoken, setHasAlgoAssetDatatoken] = useState<boolean>()
|
||||||
const [isPublished, setIsPublished] = useState(false)
|
const [algorithmDTBalance, setAlgorithmDTBalance] = useState<string>()
|
||||||
const [hasPreviousDatasetOrder, setHasPreviousDatasetOrder] = useState(false)
|
|
||||||
const [previousDatasetOrderId, setPreviousDatasetOrderId] = useState<string>()
|
const [validOrderTx, setValidOrderTx] = useState('')
|
||||||
const [hasPreviousAlgorithmOrder, setHasPreviousAlgorithmOrder] =
|
const [validAlgorithmOrderTx, setValidAlgorithmOrderTx] = useState('')
|
||||||
useState(false)
|
|
||||||
const [algorithmDTBalance, setalgorithmDTBalance] = useState<string>()
|
|
||||||
const [algorithmConsumeDetails, setAlgorithmConsumeDetails] =
|
|
||||||
useState<AccessDetails>()
|
|
||||||
const [previousAlgorithmOrderId, setPreviousAlgorithmOrderId] =
|
|
||||||
useState<string>()
|
|
||||||
const [datasetTimeout, setDatasetTimeout] = useState<string>()
|
|
||||||
const [algorithmTimeout, setAlgorithmTimeout] = useState<string>()
|
|
||||||
const newCancelToken = useCancelToken()
|
|
||||||
const hasDatatoken = Number(dtBalance) >= 1
|
|
||||||
const isMounted = useIsMounted()
|
|
||||||
const [isConsumablePrice, setIsConsumablePrice] = useState(true)
|
const [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 =
|
const isComputeButtonDisabled =
|
||||||
isJobStarting === true ||
|
isOrdering === true ||
|
||||||
file === null ||
|
file === null ||
|
||||||
(!hasPreviousDatasetOrder && !hasDatatoken && !isConsumablePrice) ||
|
(!validOrderTx && !hasDatatoken && !isConsumablePrice) ||
|
||||||
(!hasPreviousAlgorithmOrder &&
|
(!validAlgorithmOrderTx &&
|
||||||
!hasAlgoAssetDatatoken &&
|
!hasAlgoAssetDatatoken &&
|
||||||
!isAlgoConsumablePrice)
|
!isConsumableaAlgorithmPrice)
|
||||||
|
|
||||||
const service = ddo?.services[0]
|
async function checkAssetDTBalance(asset: DDO): Promise<boolean> {
|
||||||
const { timeout } = service
|
if (!asset?.services[0].datatokenAddress) return
|
||||||
|
const web3 = await getDummyWeb3(asset?.chainId)
|
||||||
async function checkPreviousOrders(ddo: DDO) {
|
const datatokenInstance = new Datatoken(web3)
|
||||||
const { type } = ddo.metadata
|
const dtBalance = await datatokenInstance.balance(
|
||||||
|
asset?.services[0].datatokenAddress,
|
||||||
const orderId = await getPreviousOrders(
|
accountId
|
||||||
ddo.services[0].datatokenAddress?.toLowerCase(),
|
|
||||||
accountId?.toLowerCase(),
|
|
||||||
timeout.toString()
|
|
||||||
)
|
)
|
||||||
|
setAlgorithmDTBalance(new Decimal(dtBalance).toString())
|
||||||
if (!isMounted()) return
|
const hasAlgoDt = Number(dtBalance) >= 1
|
||||||
if (type === 'algorithm') {
|
setHasAlgoAssetDatatoken(hasAlgoDt)
|
||||||
setPreviousAlgorithmOrderId(orderId)
|
return hasAlgoDt
|
||||||
setHasPreviousAlgorithmOrder(!!orderId)
|
|
||||||
} else {
|
|
||||||
setPreviousDatasetOrderId(orderId)
|
|
||||||
setHasPreviousDatasetOrder(!!orderId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkAssetDTBalance(asset: DDO) {
|
async function initPriceAndFees() {
|
||||||
// const AssetDtBalance = await ocean.datatokens.balance(
|
try {
|
||||||
// asset.services[0].datatokenAddress,
|
const computeEnv = await getComputeEnviroment(asset)
|
||||||
// accountId
|
if (!computeEnv || !computeEnv.id)
|
||||||
// )
|
throw new Error(`Error getting compute environments!`)
|
||||||
// setalgorithmDTBalance(AssetDtBalance)
|
|
||||||
// setHasAlgoAssetDatatoken(Number(AssetDtBalance) >= 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getQuerryString(
|
setComputeEnv(computeEnv)
|
||||||
trustedAlgorithmList: PublisherTrustedAlgorithm[],
|
const initializedProvider = await initializeProviderForCompute(
|
||||||
chainId?: number
|
asset,
|
||||||
): SearchQuery {
|
selectedAlgorithmAsset,
|
||||||
const algorithmDidList = trustedAlgorithmList.map((x) => x.did)
|
accountId,
|
||||||
|
computeEnv
|
||||||
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 (
|
if (
|
||||||
!computeService.compute ||
|
!initializedProvider ||
|
||||||
!computeService.compute.publisherTrustedAlgorithms ||
|
!initializedProvider?.datasets ||
|
||||||
computeService.compute.publisherTrustedAlgorithms.length === 0
|
!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 = []
|
setComputeStatusText(
|
||||||
} else {
|
getComputeFeedback(
|
||||||
const gueryResults = await queryMetadata(
|
asset.accessDetails?.baseToken?.symbol,
|
||||||
getQuerryString(
|
asset.accessDetails?.datatoken?.symbol,
|
||||||
computeService.compute.publisherTrustedAlgorithms,
|
asset.metadata.type
|
||||||
ddo.chainId
|
)[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
|
lpSwapFee: poolData?.liquidityProviderSwapFee,
|
||||||
)
|
publishMarketSwapFee:
|
||||||
setDdoAlgorithmList(gueryResults.results)
|
asset?.accessDetails?.publisherMarketOrderFee,
|
||||||
|
consumeMarketSwapFee: '0'
|
||||||
algorithmSelectionList = await transformAssetToAssetSelection(
|
|
||||||
computeService?.serviceEndpoint,
|
|
||||||
gueryResults.results,
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
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 (
|
if (
|
||||||
!hasPreviousAlgorithmOrder &&
|
selectedAlgorithmAsset?.accessDetails?.addressOrId !== ZERO_ADDRESS &&
|
||||||
getServiceByName(selectedAlgorithmAsset, 'compute')
|
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')) {
|
}, [asset])
|
||||||
checkPreviousOrders(selectedAlgorithmAsset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ocean && checkAssetDTBalance(selectedAlgorithmAsset)
|
|
||||||
}, [ddo, selectedAlgorithmAsset, accountId, hasPreviousAlgorithmOrder])
|
|
||||||
|
|
||||||
// Output errors in toast UI
|
// Output errors in toast UI
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -235,168 +294,131 @@ export default function Compute({
|
|||||||
toast.error(newError)
|
toast.error(newError)
|
||||||
}, [error])
|
}, [error])
|
||||||
|
|
||||||
// async function startJob(algorithmId: string) {
|
async function startJob(): Promise<void> {
|
||||||
// try {
|
try {
|
||||||
// if (!ocean) return
|
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)
|
setComputeStatusText(
|
||||||
// setIsPublished(false)
|
getComputeFeedback(
|
||||||
// setError(undefined)
|
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')
|
setComputeStatusText(
|
||||||
// const serviceAlgo = getServiceByName(selectedAlgorithmAsset, 'access')
|
getComputeFeedback(
|
||||||
// ? getServiceByName(selectedAlgorithmAsset, 'access')
|
selectedAlgorithmAsset.accessDetails.baseToken?.symbol,
|
||||||
// : getServiceByName(selectedAlgorithmAsset, 'compute')
|
selectedAlgorithmAsset.accessDetails.datatoken?.symbol,
|
||||||
|
selectedAlgorithmAsset.metadata.type
|
||||||
|
)[
|
||||||
|
selectedAlgorithmAsset.accessDetails?.type === 'fixed'
|
||||||
|
? 2
|
||||||
|
: selectedAlgorithmAsset.accessDetails?.type === 'dynamic'
|
||||||
|
? 1
|
||||||
|
: 3
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
// const computeAlgorithm: ComputeAlgorithm = {
|
const algorithmOrderTx = await handleComputeOrder(
|
||||||
// did: selectedAlgorithmAsset.id,
|
web3,
|
||||||
// serviceIndex: serviceAlgo.index,
|
selectedAlgorithmAsset,
|
||||||
// dataToken: selectedAlgorithmAsset.services[0].datatokenAddress
|
algoOrderPriceAndFees,
|
||||||
// }
|
accountId,
|
||||||
// const allowed = await ocean.compute.isOrderable(
|
hasAlgoAssetDatatoken,
|
||||||
// ddo.id,
|
initializedProviderResponse.algorithm,
|
||||||
// computeService.index,
|
computeEnv.consumerAddress
|
||||||
// computeAlgorithm
|
)
|
||||||
// )
|
if (!algorithmOrderTx) throw new Error('Failed to order algorithm.')
|
||||||
// LoggerInstance.log('[compute] Is data set orderable?', allowed)
|
|
||||||
|
|
||||||
// if (!allowed) {
|
LoggerInstance.log('[compute] Starting compute job.')
|
||||||
// setError(
|
const computeAsset: ComputeAsset = {
|
||||||
// 'Data set is not orderable in combination with selected algorithm.'
|
documentId: asset.id,
|
||||||
// )
|
serviceId: asset.services[0].id,
|
||||||
// LoggerInstance.error(
|
transferTxId: datasetOrderTx
|
||||||
// '[compute] Error starting compute job. Dataset is not orderable in combination with selected algorithm.'
|
}
|
||||||
// )
|
computeAlgorithm.transferTxId = algorithmOrderTx
|
||||||
// return
|
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) {
|
LoggerInstance.log('[compute] Starting compute job response: ', response)
|
||||||
// const tx = await buyDT('1', price, ddo)
|
setIsOrdered(true)
|
||||||
// if (!tx) {
|
setRefetchJobs(!refetchJobs)
|
||||||
// setError('Error buying datatoken.')
|
initPriceAndFees()
|
||||||
// LoggerInstance.error('[compute] Error buying datatoken for data set ', ddo.id)
|
} catch (error) {
|
||||||
// return
|
setError(error.message)
|
||||||
// }
|
LoggerInstance.error(`[compute] ${error.message} `)
|
||||||
// }
|
} finally {
|
||||||
|
setIsOrdering(false)
|
||||||
// if (!hasPreviousAlgorithmOrder && !hasAlgoAssetDatatoken) {
|
}
|
||||||
// const tx = await buyDT('1', algorithmPrice, selectedAlgorithmAsset)
|
}
|
||||||
// if (!tx) {
|
|
||||||
// setError('Error buying datatoken.')
|
|
||||||
// LoggerInstance.error(
|
|
||||||
// '[compute] Error buying datatoken for algorithm ',
|
|
||||||
// selectedAlgorithmAsset.id
|
|
||||||
// )
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TODO: pricingError is always undefined even upon errors during buyDT for whatever reason.
|
|
||||||
// // So manually drop out above, but ideally could be replaced with this alone.
|
|
||||||
// if (pricingError) {
|
|
||||||
// setError(pricingError)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const assetOrderId = hasPreviousDatasetOrder
|
|
||||||
// ? previousDatasetOrderId
|
|
||||||
// : await ocean.compute.orderAsset(
|
|
||||||
// accountId,
|
|
||||||
// ddo.id,
|
|
||||||
// computeService.index,
|
|
||||||
// computeAlgorithm,
|
|
||||||
// appConfig.marketFeeAddress,
|
|
||||||
// undefined,
|
|
||||||
// null,
|
|
||||||
// false
|
|
||||||
// )
|
|
||||||
|
|
||||||
// assetOrderId &&
|
|
||||||
// LoggerInstance.log(
|
|
||||||
// `[compute] Got ${
|
|
||||||
// hasPreviousDatasetOrder ? 'existing' : 'new'
|
|
||||||
// } order ID for dataset: `,
|
|
||||||
// assetOrderId
|
|
||||||
// )
|
|
||||||
|
|
||||||
// const algorithmAssetOrderId = hasPreviousAlgorithmOrder
|
|
||||||
// ? previousAlgorithmOrderId
|
|
||||||
// : await ocean.compute.orderAlgorithm(
|
|
||||||
// algorithmId,
|
|
||||||
// serviceAlgo.type,
|
|
||||||
// accountId,
|
|
||||||
// serviceAlgo.index,
|
|
||||||
// appConfig.marketFeeAddress,
|
|
||||||
// undefined,
|
|
||||||
// null,
|
|
||||||
// false
|
|
||||||
// )
|
|
||||||
|
|
||||||
// algorithmAssetOrderId &&
|
|
||||||
// LoggerInstance.log(
|
|
||||||
// `[compute] Got ${
|
|
||||||
// hasPreviousAlgorithmOrder ? 'existing' : 'new'
|
|
||||||
// } order ID for algorithm: `,
|
|
||||||
// algorithmAssetOrderId
|
|
||||||
// )
|
|
||||||
|
|
||||||
// if (!assetOrderId || !algorithmAssetOrderId) {
|
|
||||||
// setError('Error ordering assets.')
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// computeAlgorithm.transferTxId = algorithmAssetOrderId
|
|
||||||
// LoggerInstance.log('[compute] Starting compute job.')
|
|
||||||
|
|
||||||
// const output: ComputeOutput = {
|
|
||||||
// publishAlgorithmLog: true,
|
|
||||||
// publishOutput: true
|
|
||||||
// }
|
|
||||||
// const response = await ocean.compute.start(
|
|
||||||
// ddo.id,
|
|
||||||
// assetOrderId,
|
|
||||||
// ddo.services[0].datatokenAddress,
|
|
||||||
// account,
|
|
||||||
// computeAlgorithm,
|
|
||||||
// output,
|
|
||||||
// `${computeService.index}`,
|
|
||||||
// computeService.type
|
|
||||||
// )
|
|
||||||
|
|
||||||
// if (!response) {
|
|
||||||
// setError('Error starting compute job.')
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// LoggerInstance.log('[compute] Starting compute job response: ', response)
|
|
||||||
|
|
||||||
// await checkPreviousOrders(selectedAlgorithmAsset)
|
|
||||||
// await checkPreviousOrders(ddo)
|
|
||||||
// setIsPublished(true)
|
|
||||||
// } catch (error) {
|
|
||||||
// await checkPreviousOrders(selectedAlgorithmAsset)
|
|
||||||
// await checkPreviousOrders(ddo)
|
|
||||||
// setError('Failed to start job!')
|
|
||||||
// LoggerInstance.error('[compute] Failed to start job: ', error.message)
|
|
||||||
// } finally {
|
|
||||||
// setIsJobStarting(false)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
<FileIcon file={file} isLoading={fileIsLoading} small />
|
<FileIcon file={file} isLoading={fileIsLoading} small />
|
||||||
<Price accessDetails={accessDetails} conversion size="large" />
|
<Price accessDetails={asset?.accessDetails} conversion />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{ddo.metadata.type === 'algorithm' ? (
|
{asset.metadata.type === 'algorithm' ? (
|
||||||
<>
|
<>
|
||||||
<Alert
|
<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!"
|
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"
|
state="info"
|
||||||
/>
|
/>
|
||||||
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} asset={ddo} />
|
<AlgorithmDatasetsListForCompute
|
||||||
|
algorithmDid={asset.id}
|
||||||
|
asset={asset}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Formik
|
<Formik
|
||||||
@ -404,48 +426,64 @@ export default function Compute({
|
|||||||
validateOnMount
|
validateOnMount
|
||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
onSubmit={async (values) => {
|
onSubmit={async (values) => {
|
||||||
// await startJob(values.algorithm)
|
if (!values.algorithm) return
|
||||||
|
await startJob()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormStartComputeDataset
|
<FormStartComputeDataset
|
||||||
algorithms={algorithmList}
|
algorithms={algorithmList}
|
||||||
ddoListAlgorithms={ddoAlgorithmList}
|
ddoListAlgorithms={ddoAlgorithmList}
|
||||||
|
selectedAlgorithmAsset={selectedAlgorithmAsset}
|
||||||
setSelectedAlgorithm={setSelectedAlgorithmAsset}
|
setSelectedAlgorithm={setSelectedAlgorithmAsset}
|
||||||
isLoading={isJobStarting}
|
isLoading={isOrdering || isRequestingAlgoOrderPrice}
|
||||||
isComputeButtonDisabled={isComputeButtonDisabled}
|
isComputeButtonDisabled={isComputeButtonDisabled}
|
||||||
hasPreviousOrder={hasPreviousDatasetOrder}
|
hasPreviousOrder={validOrderTx !== undefined}
|
||||||
hasDatatoken={hasDatatoken}
|
hasDatatoken={hasDatatoken}
|
||||||
dtBalance={dtBalance}
|
dtBalance={dtBalance}
|
||||||
datasetLowPoolLiquidity={!isConsumablePrice}
|
datasetLowPoolLiquidity={!isConsumablePrice}
|
||||||
assetType={ddo?.metadata.type}
|
assetType={asset?.metadata.type}
|
||||||
assetTimeout={datasetTimeout}
|
assetTimeout={secondsToString(asset?.services[0].timeout)}
|
||||||
hasPreviousOrderSelectedComputeAsset={hasPreviousAlgorithmOrder}
|
hasPreviousOrderSelectedComputeAsset={
|
||||||
|
validAlgorithmOrderTx !== undefined
|
||||||
|
}
|
||||||
hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken}
|
hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken}
|
||||||
oceanSymbol={accessDetails?.baseToken?.symbol || ''}
|
oceanSymbol={
|
||||||
|
asset?.accessDetails?.baseToken?.symbol ||
|
||||||
|
selectedAlgorithmAsset?.accessDetails?.baseToken?.symbol ||
|
||||||
|
'OCEAN'
|
||||||
|
}
|
||||||
dtSymbolSelectedComputeAsset={
|
dtSymbolSelectedComputeAsset={
|
||||||
selectedAlgorithmAsset?.datatokens[0]?.symbol
|
selectedAlgorithmAsset?.datatokens[0]?.symbol
|
||||||
}
|
}
|
||||||
dtBalanceSelectedComputeAsset={algorithmDTBalance}
|
dtBalanceSelectedComputeAsset={algorithmDTBalance}
|
||||||
selectedComputeAssetLowPoolLiquidity={!isAlgoConsumablePrice}
|
|
||||||
selectedComputeAssetType="algorithm"
|
selectedComputeAssetType="algorithm"
|
||||||
selectedComputeAssetTimeout={algorithmTimeout}
|
selectedComputeAssetTimeout={secondsToString(
|
||||||
|
selectedAlgorithmAsset?.services[0]?.timeout
|
||||||
|
)}
|
||||||
// lazy comment when removing pricingStepText
|
// lazy comment when removing pricingStepText
|
||||||
stepText={'pricingStepText' || 'Starting Compute Job...'}
|
stepText={computeStatusText}
|
||||||
algorithmConsumeDetails={algorithmConsumeDetails}
|
isConsumable={isConsumablePrice}
|
||||||
isConsumable={isConsumable}
|
|
||||||
consumableFeedback={consumableFeedback}
|
consumableFeedback={consumableFeedback}
|
||||||
|
datasetOrderPriceAndFees={datasetOrderPriceAndFees}
|
||||||
|
algoOrderPriceAndFees={algoOrderPriceAndFees}
|
||||||
|
providerFeeAmount={providerFeeAmount}
|
||||||
|
validUntil={computeValidUntil}
|
||||||
/>
|
/>
|
||||||
</Formik>
|
</Formik>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<footer className={styles.feedback}>
|
<footer className={styles.feedback}>
|
||||||
{isPublished && (
|
{isOrdered && (
|
||||||
<SuccessConfetti success="Your job started successfully! Watch the progress below or on your profile." />
|
<SuccessConfetti success="Your job started successfully! Watch the progress below or on your profile." />
|
||||||
)}
|
)}
|
||||||
</footer>
|
</footer>
|
||||||
{accountId && accessDetails?.datatoken && (
|
{accountId && asset?.accessDetails?.datatoken && (
|
||||||
<AssetActionHistoryTable title="Your Compute Jobs">
|
<AssetActionHistoryTable title="Your Compute Jobs">
|
||||||
<ComputeJobs minimal />
|
<ComputeJobs
|
||||||
|
minimal
|
||||||
|
assetChainIds={[asset?.chainId]}
|
||||||
|
refetchJobs={refetchJobs}
|
||||||
|
/>
|
||||||
</AssetActionHistoryTable>
|
</AssetActionHistoryTable>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -113,8 +113,7 @@ export default function AssetActions({
|
|||||||
|
|
||||||
const UseContent = isCompute ? (
|
const UseContent = isCompute ? (
|
||||||
<Compute
|
<Compute
|
||||||
ddo={asset}
|
asset={asset}
|
||||||
accessDetails={asset?.accessDetails}
|
|
||||||
dtBalance={dtBalance}
|
dtBalance={dtBalance}
|
||||||
file={fileMetadata}
|
file={fileMetadata}
|
||||||
fileIsLoading={fileIsLoading}
|
fileIsLoading={fileIsLoading}
|
||||||
|
@ -3,12 +3,12 @@ import { useAsset } from '@context/Asset'
|
|||||||
import ExplorerLink from '@shared/ExplorerLink'
|
import ExplorerLink from '@shared/ExplorerLink'
|
||||||
import Time from '@shared/atoms/Time'
|
import Time from '@shared/atoms/Time'
|
||||||
import { gql, OperationContext, useQuery } from 'urql'
|
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 { getQueryContext } from '@utils/subgraph'
|
||||||
import styles from './EditHistory.module.css'
|
import styles from './EditHistory.module.css'
|
||||||
|
|
||||||
const getReceipts = gql`
|
const getReceipts = gql`
|
||||||
query ReceiptData($address: String!) {
|
query NftUpdate($address: String!) {
|
||||||
nftUpdates(
|
nftUpdates(
|
||||||
where: { nft: $address }
|
where: { nft: $address }
|
||||||
orderBy: timestamp
|
orderBy: timestamp
|
||||||
@ -30,8 +30,8 @@ export default function EditHistory({
|
|||||||
receipts,
|
receipts,
|
||||||
setReceipts
|
setReceipts
|
||||||
}: {
|
}: {
|
||||||
receipts: ReceiptData[]
|
receipts: NftUpdate[]
|
||||||
setReceipts: (receipts: ReceiptData[]) => void
|
setReceipts: (receipts: NftUpdate[]) => void
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { asset } = useAsset()
|
const { asset } = useAsset()
|
||||||
|
|
||||||
|
@ -35,20 +35,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
margin-bottom: calc(var(--spacer) * 1.5);
|
margin-bottom: calc(var(--spacer) * 1.5);
|
||||||
margin-left: -2rem;
|
padding: calc(var(--spacer) / 3) var(--spacer);
|
||||||
margin-right: -2rem;
|
|
||||||
padding: calc(var(--spacer) / 4) var(--spacer);
|
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
border-bottom: 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 NetworkName from '@shared/NetworkName'
|
||||||
import content from '../../../../content/purgatory.json'
|
import content from '../../../../content/purgatory.json'
|
||||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||||
import { useWeb3 } from '@context/Web3'
|
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
|
import Button from '@shared/atoms/Button'
|
||||||
|
|
||||||
export default function AssetContent({
|
export default function AssetContent({
|
||||||
asset
|
asset
|
||||||
}: {
|
}: {
|
||||||
asset: AssetExtended
|
asset: AssetExtended
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [isOwner, setIsOwner] = useState(false)
|
const { isInPurgatory, purgatoryData, isOwner, isAssetNetwork } = useAsset()
|
||||||
const { accountId } = useWeb3()
|
|
||||||
const { isInPurgatory, purgatoryData, owner, isAssetNetwork } = useAsset()
|
|
||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
const [receipts, setReceipts] = useState([])
|
const [receipts, setReceipts] = useState([])
|
||||||
const [nftPublisher, setNftPublisher] = useState<string>()
|
const [nftPublisher, setNftPublisher] = useState<string>()
|
||||||
@ -38,13 +36,6 @@ export default function AssetContent({
|
|||||||
)
|
)
|
||||||
}, [receipts])
|
}, [receipts])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!accountId || !owner) return
|
|
||||||
|
|
||||||
const isOwner = accountId.toLowerCase() === owner.toLowerCase()
|
|
||||||
setIsOwner(isOwner)
|
|
||||||
}, [accountId, owner, asset])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.networkWrap}>
|
<div className={styles.networkWrap}>
|
||||||
@ -84,9 +75,9 @@ export default function AssetContent({
|
|||||||
<AssetActions asset={asset} />
|
<AssetActions asset={asset} />
|
||||||
{isOwner && isAssetNetwork && (
|
{isOwner && isAssetNetwork && (
|
||||||
<div className={styles.ownerActions}>
|
<div className={styles.ownerActions}>
|
||||||
<Link href={`/asset/${asset?.id}/edit`}>
|
<Button style="text" size="small" to={`/asset/${asset?.id}/edit`}>
|
||||||
<a>Edit</a>
|
Edit Asset
|
||||||
</Link>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { ReactElement, useState, useEffect } from 'react'
|
import React, { ReactElement, useState, useEffect } from 'react'
|
||||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
|
||||||
import { useAsset } from '@context/Asset'
|
import { useAsset } from '@context/Asset'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import Tabs from '@shared/atoms/Tabs'
|
import Tabs from '@shared/atoms/Tabs'
|
||||||
@ -33,7 +32,7 @@ export default function Edit({ uri }: { uri: string }): ReactElement {
|
|||||||
{
|
{
|
||||||
title: 'Edit Compute Settings',
|
title: 'Edit Compute Settings',
|
||||||
content: <EditComputeDataset asset={asset} />,
|
content: <EditComputeDataset asset={asset} />,
|
||||||
disabled: !isCompute
|
disabled: !isCompute || asset?.metadata?.type === 'algorithm'
|
||||||
}
|
}
|
||||||
].filter((tab) => tab !== undefined)
|
].filter((tab) => tab !== undefined)
|
||||||
|
|
||||||
|
@ -62,6 +62,20 @@
|
|||||||
.assetMeta code {
|
.assetMeta code {
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
font-size: var(--font-size-small);
|
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 {
|
.meta {
|
||||||
|
@ -33,7 +33,8 @@ function Asset({
|
|||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
<p className={styles.assetMeta}>
|
<p className={styles.assetMeta}>
|
||||||
{symbol} | <code>{did}</code>
|
<span className={styles.assetMeta}> {`${symbol} | `}</span>
|
||||||
|
<code className={styles.assetMeta}>{did}</code>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -41,9 +42,11 @@ function Asset({
|
|||||||
|
|
||||||
function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
|
function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
|
||||||
const { appConfig } = useMarketMetadata()
|
const { appConfig } = useMarketMetadata()
|
||||||
|
const newCancelToken = useCancelToken()
|
||||||
|
|
||||||
const [algoName, setAlgoName] = useState<string>()
|
const [algoName, setAlgoName] = useState<string>()
|
||||||
const [algoDtSymbol, setAlgoDtSymbol] = useState<string>()
|
const [algoDtSymbol, setAlgoDtSymbol] = useState<string>()
|
||||||
const newCancelToken = useCancelToken()
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getAlgoMetadata() {
|
async function getAlgoMetadata() {
|
||||||
const ddo = await retrieveAsset(job.algoDID, newCancelToken())
|
const ddo = await retrieveAsset(job.algoDID, newCancelToken())
|
||||||
@ -51,7 +54,7 @@ function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
|
|||||||
setAlgoName(ddo?.metadata.name)
|
setAlgoName(ddo?.metadata.name)
|
||||||
}
|
}
|
||||||
getAlgoMetadata()
|
getAlgoMetadata()
|
||||||
}, [appConfig.metadataCacheUri, job.algoDID])
|
}, [appConfig.metadataCacheUri, job.algoDID, newCancelToken])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -97,12 +100,6 @@ export default function Details({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<MetaItem title="Job ID" content={<code>{job.jobId}</code>} />
|
<MetaItem title="Job ID" content={<code>{job.jobId}</code>} />
|
||||||
{/* {job.resultsDid && (
|
|
||||||
<MetaItem
|
|
||||||
title="Published Results DID"
|
|
||||||
content={<code>{job.resultsDid}</code>}
|
|
||||||
/>
|
|
||||||
)} */}
|
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
@ -3,6 +3,12 @@
|
|||||||
border-bottom-left-radius: var(--border-radius) !important;
|
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 {
|
.help {
|
||||||
margin-top: calc(var(--spacer) / 3);
|
margin-top: calc(var(--spacer) / 3);
|
||||||
}
|
}
|
||||||
|
@ -1,90 +1,108 @@
|
|||||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
import {
|
||||||
import React, { ReactElement, useState } from 'react'
|
ComputeResultType,
|
||||||
import Loader from '@shared/atoms/Loader'
|
downloadFileBrowser,
|
||||||
|
LoggerInstance,
|
||||||
|
Provider
|
||||||
|
} from '@oceanprotocol/lib'
|
||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { ListItem } from '@shared/atoms/Lists'
|
import { ListItem } from '@shared/atoms/Lists'
|
||||||
import Button from '@shared/atoms/Button'
|
import Button from '@shared/atoms/Button'
|
||||||
import styles from './Results.module.css'
|
import styles from './Results.module.css'
|
||||||
import FormHelp from '@shared/FormInput/Help'
|
import FormHelp from '@shared/FormInput/Help'
|
||||||
import content from '../../../../../content/pages/history.json'
|
import content from '../../../../../content/pages/history.json'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import { useCancelToken } from '@hooks/useCancelToken'
|
||||||
|
import { retrieveAsset } from '@utils/aquarius'
|
||||||
|
|
||||||
export default function Results({
|
export default function Results({
|
||||||
job
|
job
|
||||||
}: {
|
}: {
|
||||||
job: ComputeJobMetaData
|
job: ComputeJobMetaData
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { accountId } = useWeb3()
|
const providerInstance = new Provider()
|
||||||
|
const { accountId, web3 } = useWeb3()
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [hasFetched, setHasFetched] = useState(false)
|
|
||||||
const isFinished = job.dateFinished !== null
|
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
|
if (!accountId || !job) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
// const jobStatus = await ocean.compute.status(
|
const jobResult = await providerInstance.getComputeResultUrl(
|
||||||
// account,
|
datasetProvider,
|
||||||
// job.did,
|
web3,
|
||||||
// undefined,
|
accountId,
|
||||||
// undefined,
|
job.jobId,
|
||||||
// job.jobId
|
resultIndex
|
||||||
// )
|
)
|
||||||
// if (jobStatus?.length > 0) {
|
await downloadFileBrowser(jobResult)
|
||||||
// job.algorithmLogUrl = jobStatus[0].algorithmLogUrl
|
|
||||||
// job.resultsUrl = jobStatus[0].resultsUrl
|
|
||||||
// }
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
LoggerInstance.error(error.message)
|
LoggerInstance.error(error.message)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
setHasFetched(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.results}>
|
<div className={styles.results}>
|
||||||
{hasFetched ? (
|
<h4 className={styles.title}>Results</h4>
|
||||||
|
{isFinished ? (
|
||||||
<ul>
|
<ul>
|
||||||
<ListItem>
|
{job.results &&
|
||||||
{/* {job.algorithmLogUrl ? (
|
Array.isArray(job.results) &&
|
||||||
<a href={job.algorithmLogUrl} target="_blank" rel="noreferrer">
|
job.results.map((jobResult, i) =>
|
||||||
View Log
|
jobResult.filename ? (
|
||||||
</a>
|
<ListItem key={i}>
|
||||||
) : (
|
<Button
|
||||||
'No logs found.'
|
style="text"
|
||||||
)} */}
|
size="small"
|
||||||
</ListItem>
|
onClick={() => downloadResults(i)}
|
||||||
|
download
|
||||||
{/* {job.resultsUrl &&
|
>
|
||||||
Array.isArray(job.resultsUrl) &&
|
{getDownloadButtonValue(jobResult.type)}
|
||||||
job.resultsUrl.map((url, i) =>
|
</Button>
|
||||||
url ? (
|
|
||||||
<ListItem key={job.jobId}>
|
|
||||||
<a href={url} target="_blank" rel="noreferrer">
|
|
||||||
View Result {i + 1}
|
|
||||||
</a>
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
) : (
|
) : (
|
||||||
<ListItem>No results found.</ListItem>
|
<ListItem>No results found.</ListItem>
|
||||||
)
|
)
|
||||||
)} */}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<p> Waiting for results...</p>
|
||||||
style="primary"
|
|
||||||
size="small"
|
|
||||||
onClick={() => getResults()}
|
|
||||||
disabled={isLoading || !isFinished}
|
|
||||||
>
|
|
||||||
{isLoading ? (
|
|
||||||
<Loader />
|
|
||||||
) : !isFinished ? (
|
|
||||||
'Waiting for results...'
|
|
||||||
) : (
|
|
||||||
'Get Results'
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
<FormHelp className={styles.help}>{content.compute.storage}</FormHelp>
|
<FormHelp className={styles.help}>{content.compute.storage}</FormHelp>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import React, { ReactElement, useEffect, useState, useCallback } from 'react'
|
import React, { ReactElement, useEffect, useState, useCallback } from 'react'
|
||||||
import Time from '@shared/atoms/Time'
|
import Time from '@shared/atoms/Time'
|
||||||
import Link from 'next/link'
|
|
||||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||||
import Dotdotdot from 'react-dotdotdot'
|
|
||||||
import Table from '@shared/atoms/Table'
|
import Table from '@shared/atoms/Table'
|
||||||
import Button from '@shared/atoms/Button'
|
import Button from '@shared/atoms/Button'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
@ -10,10 +8,12 @@ import Details from './Details'
|
|||||||
import Refresh from '@images/refresh.svg'
|
import Refresh from '@images/refresh.svg'
|
||||||
import { useUserPreferences } from '@context/UserPreferences'
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
import NetworkName from '@shared/NetworkName'
|
import NetworkName from '@shared/NetworkName'
|
||||||
// import { getComputeJobs } from '@utils/compute'
|
import { getComputeJobs } from '@utils/compute'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { useAsset } from '@context/Asset'
|
import { useAsset } from '@context/Asset'
|
||||||
import { useIsMounted } from '@hooks/useIsMounted'
|
import { useIsMounted } from '@hooks/useIsMounted'
|
||||||
|
import { useCancelToken } from '@hooks/useCancelToken'
|
||||||
|
import AssetListTitle from '@shared/AssetList/AssetListTitle'
|
||||||
|
|
||||||
export function Status({ children }: { children: string }): ReactElement {
|
export function Status({ children }: { children: string }): ReactElement {
|
||||||
return <div className={styles.status}>{children}</div>
|
return <div className={styles.status}>{children}</div>
|
||||||
@ -23,13 +23,7 @@ const columns = [
|
|||||||
{
|
{
|
||||||
name: 'Data Set',
|
name: 'Data Set',
|
||||||
selector: function getAssetRow(row: ComputeJobMetaData) {
|
selector: function getAssetRow(row: ComputeJobMetaData) {
|
||||||
return (
|
return <AssetListTitle did={row.inputDID[0]} title={row.assetName} />
|
||||||
<Dotdotdot clamp={2}>
|
|
||||||
<Link href={`/asset/${row.inputDID[0]}`}>
|
|
||||||
<a>{row.assetName}</a>
|
|
||||||
</Link>
|
|
||||||
</Dotdotdot>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -69,18 +63,23 @@ const columns = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export default function ComputeJobs({
|
export default function ComputeJobs({
|
||||||
minimal
|
minimal,
|
||||||
|
assetChainIds,
|
||||||
|
refetchJobs
|
||||||
}: {
|
}: {
|
||||||
minimal?: boolean
|
minimal?: boolean
|
||||||
|
assetChainIds?: number[]
|
||||||
|
refetchJobs?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { accountId, networkId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { asset } = useAsset()
|
const { asset } = useAsset()
|
||||||
const { chainIds } = useUserPreferences()
|
const { chainIds } = useUserPreferences()
|
||||||
|
const isMounted = useIsMounted()
|
||||||
|
const newCancelToken = useCancelToken()
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
|
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
|
||||||
const isMounted = useIsMounted()
|
const [columnsMinimal] = useState([columns[4], columns[5], columns[3]])
|
||||||
|
|
||||||
const columnsMinimal = [columns[4], columns[5], columns[3]]
|
|
||||||
|
|
||||||
const fetchJobs = useCallback(async () => {
|
const fetchJobs = useCallback(async () => {
|
||||||
if (!chainIds || chainIds.length === 0 || !accountId) {
|
if (!chainIds || chainIds.length === 0 || !accountId) {
|
||||||
@ -90,17 +89,22 @@ export default function ComputeJobs({
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
// const jobs = await getComputeJobs(chainIds, accountId, ddo)
|
const jobs = await getComputeJobs(
|
||||||
// isMounted() && setJobs(jobs.computeJobs)
|
assetChainIds || chainIds,
|
||||||
// setIsLoading(jobs.isLoaded)
|
accountId,
|
||||||
|
asset,
|
||||||
|
newCancelToken()
|
||||||
|
)
|
||||||
|
isMounted() && setJobs(jobs.computeJobs)
|
||||||
|
setIsLoading(!jobs.isLoaded)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
LoggerInstance.error(error.message)
|
LoggerInstance.error(error.message)
|
||||||
}
|
}
|
||||||
}, [chainIds, accountId, asset, isMounted])
|
}, [chainIds, accountId, asset, isMounted, assetChainIds, newCancelToken])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchJobs()
|
fetchJobs()
|
||||||
}, [fetchJobs])
|
}, [fetchJobs, refetchJobs])
|
||||||
|
|
||||||
return accountId ? (
|
return accountId ? (
|
||||||
<>
|
<>
|
||||||
|
@ -41,8 +41,8 @@ export const wizardSteps: StepContent[] = [
|
|||||||
const computeOptions: ServiceComputeOptions = {
|
const computeOptions: ServiceComputeOptions = {
|
||||||
allowRawAlgorithm: false,
|
allowRawAlgorithm: false,
|
||||||
allowNetworkAccess: true,
|
allowNetworkAccess: true,
|
||||||
publisherTrustedAlgorithmPublishers: null,
|
publisherTrustedAlgorithmPublishers: [],
|
||||||
publisherTrustedAlgorithms: null
|
publisherTrustedAlgorithms: []
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialValues: FormPublishData = {
|
export const initialValues: FormPublishData = {
|
||||||
|
@ -135,7 +135,8 @@ export async function transformPublishFormToDdo(
|
|||||||
: getAlgorithmContainerPreset(dockerImage).tag,
|
: getAlgorithmContainerPreset(dockerImage).tag,
|
||||||
checksum:
|
checksum:
|
||||||
dockerImage === 'custom'
|
dockerImage === 'custom'
|
||||||
? dockerImageCustomChecksum
|
? // ? dockerImageCustomChecksum
|
||||||
|
''
|
||||||
: getAlgorithmContainerPreset(dockerImage).checksum
|
: getAlgorithmContainerPreset(dockerImage).checksum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user