diff --git a/package-lock.json b/package-lock.json index d00169cd9..a1bb84d10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@coingecko/cryptoformat": "^0.4.4", "@loadable/component": "^5.15.2", "@oceanprotocol/art": "^3.2.0", - "@oceanprotocol/lib": "^1.0.0-next.36", + "@oceanprotocol/lib": "^1.0.0-next.37", "@oceanprotocol/typographies": "^0.1.0", "@portis/web3": "^4.0.7", "@tippyjs/react": "^4.2.6", @@ -2723,9 +2723,9 @@ "integrity": "sha512-M/yyKfpWmMRHWPTjvKlrRWUcIbfkRWyHhjHUI1kPggJPPX6ADxApTwtzwVXJ/+WyegcaYc7bqwuclqvg9XPqBQ==" }, "node_modules/@oceanprotocol/lib": { - "version": "1.0.0-next.36", - "resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-1.0.0-next.36.tgz", - "integrity": "sha512-Q0IoLGSRlxAJYBc4aF9eZRdIWigD0+W1we9EVQd7nn4MJBN290VEUVlE88+ZBkjrrE4Wq7E0YGyVlsDwzMQh7Q==", + "version": "1.0.0-next.37", + "resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-1.0.0-next.37.tgz", + "integrity": "sha512-3d3JSSZbBjPnlqzx8LJw6g+Ti4LL96FH/TTOqFCP9D4kbux2gN0KaUdHzI66G6S6vhsxBKOmUyACJcOuH3eBDQ==", "dependencies": { "@oceanprotocol/contracts": "1.0.0-alpha.28", "bignumber.js": "^9.0.2", @@ -21572,15 +21572,16 @@ "integrity": "sha512-M/yyKfpWmMRHWPTjvKlrRWUcIbfkRWyHhjHUI1kPggJPPX6ADxApTwtzwVXJ/+WyegcaYc7bqwuclqvg9XPqBQ==" }, "@oceanprotocol/lib": { - "version": "1.0.0-next.36", - "resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-1.0.0-next.36.tgz", - "integrity": "sha512-Q0IoLGSRlxAJYBc4aF9eZRdIWigD0+W1we9EVQd7nn4MJBN290VEUVlE88+ZBkjrrE4Wq7E0YGyVlsDwzMQh7Q==", + "version": "1.0.0-next.37", + "resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-1.0.0-next.37.tgz", + "integrity": "sha512-3d3JSSZbBjPnlqzx8LJw6g+Ti4LL96FH/TTOqFCP9D4kbux2gN0KaUdHzI66G6S6vhsxBKOmUyACJcOuH3eBDQ==", "requires": { "@oceanprotocol/contracts": "1.0.0-alpha.28", "bignumber.js": "^9.0.2", "cross-fetch": "^3.1.5", "crypto-js": "^4.1.1", "decimal.js": "^10.3.1", + "web3": "^1.7.3", "web3-core": "^1.7.1", "web3-eth-contract": "^1.7.1" } @@ -21683,6 +21684,7 @@ "integrity": "sha512-5vwpq6kbvwkQwKqAoOU3L72GZ3Ta8RRrewKj9OJRolx28KLJJ8Dg9Rf7obRwt5jQA9bkYd8gqzMTrI7H3xLfaw==", "dev": true, "requires": { + "@oclif/config": "^1.15.1", "@oclif/errors": "^1.3.3", "@oclif/parser": "^3.8.3", "@oclif/plugin-help": "^3", diff --git a/package.json b/package.json index f6d30f809..d5e0da04e 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@coingecko/cryptoformat": "^0.4.4", "@loadable/component": "^5.15.2", "@oceanprotocol/art": "^3.2.0", - "@oceanprotocol/lib": "^1.0.0-next.36", + "@oceanprotocol/lib": "^1.0.0-next.37", "@oceanprotocol/typographies": "^0.1.0", "@portis/web3": "^4.0.7", "@tippyjs/react": "^4.2.6", diff --git a/src/@utils/compute.ts b/src/@utils/compute.ts index c858acdf4..2ce5bf090 100644 --- a/src/@utils/compute.ts +++ b/src/@utils/compute.ts @@ -369,3 +369,31 @@ export async function transformComputeFormToServiceComputeOptions( return privacy } + +export async function checkComputeResourcesValidity( + asset: Asset, + accountId: string, + computeEnvMaxJobDuration: number, + datasetTimeout?: number, + algorithmTimeout?: number, + cancelToken?: CancelToken +): Promise { + 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 +} diff --git a/src/@utils/order.ts b/src/@utils/order.ts index 0c48d1be9..323ce1b16 100644 --- a/src/@utils/order.ts +++ b/src/@utils/order.ts @@ -111,3 +111,57 @@ 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 accountId validOrderTx + * @param computeEnv + * @param computeValidUntil + * @param computeConsumerAddress + * @returns {TransactionReceipt} receipt of the order + */ +export async function reuseOrder( + web3: Web3, + asset: AssetExtended, + accountId: string, + validOrderTx: string, + computeEnv: string = null, + computeValidUntil: number = null, + computeConsumerAddress?: string +) { + const datatoken = new Datatoken(web3) + const initializeData = await ProviderInstance.initialize( + asset.id, + asset.services[0].id, + 0, + accountId, + asset.services[0].serviceEndpoint, + null, + null, + computeEnv, + computeValidUntil + ) + const txApprove = await approve( + web3, + accountId, + initializeData.providerFee.providerFeeToken, + asset.accessDetails.datatoken.address, + initializeData.providerFee.providerFeeAmount, + false + ) + if (!txApprove) { + return + } + + const tx = await datatoken.reuseOrder( + asset.accessDetails.datatoken.address, + accountId, + validOrderTx, + initializeData.providerFee + ) + + return tx +} diff --git a/src/components/Asset/AssetActions/Compute/index.tsx b/src/components/Asset/AssetActions/Compute/index.tsx index 28baae72d..1cb4a3f6f 100644 --- a/src/components/Asset/AssetActions/Compute/index.tsx +++ b/src/components/Asset/AssetActions/Compute/index.tsx @@ -28,7 +28,8 @@ import { getAlgorithmAssetSelectionList, getAlgorithmsForAsset, getValidUntilTime, - getComputeEnviroment + getComputeEnviroment, + checkComputeResourcesValidity } from '@utils/compute' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute' @@ -41,7 +42,7 @@ import { useAbortController } from '@hooks/useAbortController' import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing' import { OrderPriceAndFees } from 'src/@types/Price' import { buyDtFromPool } from '@utils/pool' -import { order } from '@utils/order' +import { order, reuseOrder } from '@utils/order' import { AssetExtended } from 'src/@types/AssetExtended' import { getComputeFeedback } from '@utils/feedback' @@ -94,6 +95,7 @@ export default function Compute({ useState() const [isRequestingAlgoOrderPrice, setIsRequestingAlgoOrderPrice] = useState(false) + const [isProviderFeeValid, setIsProviderFeeValid] = useState(false) const isComputeButtonDisabled = isJobStarting === true || file === null || @@ -116,6 +118,15 @@ export default function Compute({ async function initPriceAndFees() { const computeEnv = await getComputeEnviroment(asset) setComputeEnv(computeEnv) + setIsProviderFeeValid( + await checkComputeResourcesValidity( + asset, + accountId, + computeEnv?.maxJobDuration, + asset.services[0].timeout, + selectedAlgorithmAsset.services[0].timeout + ) + ) const validUntil = getValidUntilTime( computeEnv?.maxJobDuration, asset.services[0].timeout, @@ -377,6 +388,26 @@ export default function Compute({ } } + if (isOwned && !isProviderFeeValid) { + const reuseOrderTx = await reuseOrder( + web3, + asset, + accountId, + validOrderTx, + computeEnv?.id, + computeValidUntil + ) + if (!reuseOrderTx) { + toast.error('Failed to pay provider fees!') + return + } + LoggerInstance.log( + '[compute] Reused order: ', + reuseOrderTx.transactionHash + ) + datasetOrderTx = reuseOrderTx.transactionHash + } + LoggerInstance.log('[compute] Starting compute job.') const computeAsset: ComputeAsset = { documentId: asset.id,