diff --git a/README.md b/README.md index eba0edf33..163863524 100644 --- a/README.md +++ b/README.md @@ -406,7 +406,7 @@ The style can be changed by altering the `style` prop in the `PrivacyPreferenceC ## 🏛 License ```text -Copyright 2021 Ocean Protocol Foundation Ltd. +Copyright 2022 Ocean Protocol Foundation Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/package-lock.json b/package-lock.json index aeff71e0a..1793b68fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4668,6 +4668,8 @@ }, "node_modules/@truffle/abi-utils": { "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@truffle/abi-utils/-/abi-utils-0.2.9.tgz", + "integrity": "sha512-Nv4MGsA2vdI7G34nI0DfR/eSd5pbAUu+5EafYNqzgrS46y0LWhbIrSZ1NcM7cbhIrkpUn6OfNk49AjNM67TkSg==", "license": "MIT", "dependencies": { "change-case": "3.0.2", @@ -4681,6 +4683,8 @@ }, "node_modules/@truffle/codec": { "version": "0.11.27", + "resolved": "https://registry.npmjs.org/@truffle/codec/-/codec-0.11.27.tgz", + "integrity": "sha512-zPlbrGSZ975jscoJ4NhQpaJGwJXkasnpSoUAEjzppr6FCLKtutxssy6yfz4EUHaQDTg1SqxlVBfBhqYcrCyjvw==", "license": "MIT", "dependencies": { "@truffle/abi-utils": "^0.2.9", @@ -4750,6 +4754,8 @@ }, "node_modules/@truffle/compile-common": { "version": "0.7.28", + "resolved": "https://registry.npmjs.org/@truffle/compile-common/-/compile-common-0.7.28.tgz", + "integrity": "sha512-mZCEQ6fkOqbKYCJDT82q0vZCxOEsKRQ0zrPfKuSJEb0gF9DXIQcnMkyJpBSWzmyvien9/A7/jPiGQoC7PmNEUg==", "license": "MIT", "dependencies": { "@truffle/error": "^0.1.0", @@ -4758,6 +4764,8 @@ }, "node_modules/@truffle/contract": { "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@truffle/contract/-/contract-4.4.9.tgz", + "integrity": "sha512-LSIxnpFDr824wUp4tiw2UHmDPr8io5UnzjlZX/QKuNIq+BB+JPRmkzItjwtvWzzIG3QOAGiqjtIgp1U6Mab/bw==", "license": "MIT", "dependencies": { "@ensdomains/ensjs": "^2.0.1", @@ -4778,6 +4786,8 @@ }, "node_modules/@truffle/contract-schema": { "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.4.5.tgz", + "integrity": "sha512-heaGV9QWqef259HaF+0is/tsmhlZIbUSWhqvj0iwKmxoN92fghKijWwdVYhPIbsmGlrQuwPTZHSCnaOlO+gsFg==", "license": "MIT", "dependencies": { "ajv": "^6.10.0", @@ -5083,6 +5093,8 @@ }, "node_modules/@truffle/debug-utils": { "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@truffle/debug-utils/-/debug-utils-6.0.9.tgz", + "integrity": "sha512-CVKVsbEWE0TzmTdGcxEeQvNhTKxkojIRox3LWh2Z8ZARtPiWwYl/lmPqfugh1665xmc2NVLI9qyP4ygVFPkvIQ==", "license": "MIT", "dependencies": { "@truffle/codec": "^0.11.27", @@ -5543,6 +5555,8 @@ }, "node_modules/@trufflesuite/chromafi": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@trufflesuite/chromafi/-/chromafi-3.0.0.tgz", + "integrity": "sha512-oqWcOqn8nT1bwlPPfidfzS55vqcIDdpfzo3HbU9EnUmcSTX+I8z0UyUFI3tZQjByVJulbzxHxUGS3ZJPwK/GPQ==", "license": "MIT", "dependencies": { "camelcase": "^4.1.0", @@ -13451,6 +13465,8 @@ }, "node_modules/he": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "license": "MIT", "peer": true, "bin": { @@ -18744,6 +18760,8 @@ }, "node_modules/simple-get": { "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", "license": "MIT", "dependencies": { "decompress-response": "^3.3.0", @@ -24635,6 +24653,8 @@ }, "@truffle/abi-utils": { "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@truffle/abi-utils/-/abi-utils-0.2.9.tgz", + "integrity": "sha512-Nv4MGsA2vdI7G34nI0DfR/eSd5pbAUu+5EafYNqzgrS46y0LWhbIrSZ1NcM7cbhIrkpUn6OfNk49AjNM67TkSg==", "requires": { "change-case": "3.0.2", "faker": "^5.3.1", @@ -24646,6 +24666,8 @@ }, "@truffle/codec": { "version": "0.11.27", + "resolved": "https://registry.npmjs.org/@truffle/codec/-/codec-0.11.27.tgz", + "integrity": "sha512-zPlbrGSZ975jscoJ4NhQpaJGwJXkasnpSoUAEjzppr6FCLKtutxssy6yfz4EUHaQDTg1SqxlVBfBhqYcrCyjvw==", "requires": { "@truffle/abi-utils": "^0.2.9", "@truffle/compile-common": "^0.7.28", @@ -24705,6 +24727,8 @@ }, "@truffle/compile-common": { "version": "0.7.28", + "resolved": "https://registry.npmjs.org/@truffle/compile-common/-/compile-common-0.7.28.tgz", + "integrity": "sha512-mZCEQ6fkOqbKYCJDT82q0vZCxOEsKRQ0zrPfKuSJEb0gF9DXIQcnMkyJpBSWzmyvien9/A7/jPiGQoC7PmNEUg==", "requires": { "@truffle/error": "^0.1.0", "colors": "1.4.0" @@ -24712,6 +24736,8 @@ }, "@truffle/contract": { "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@truffle/contract/-/contract-4.4.9.tgz", + "integrity": "sha512-LSIxnpFDr824wUp4tiw2UHmDPr8io5UnzjlZX/QKuNIq+BB+JPRmkzItjwtvWzzIG3QOAGiqjtIgp1U6Mab/bw==", "requires": { "@ensdomains/ensjs": "^2.0.1", "@truffle/blockchain-utils": "^0.1.0", @@ -24945,6 +24971,8 @@ }, "@truffle/contract-schema": { "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.4.5.tgz", + "integrity": "sha512-heaGV9QWqef259HaF+0is/tsmhlZIbUSWhqvj0iwKmxoN92fghKijWwdVYhPIbsmGlrQuwPTZHSCnaOlO+gsFg==", "requires": { "ajv": "^6.10.0", "debug": "^4.3.1" @@ -24952,6 +24980,8 @@ }, "@truffle/debug-utils": { "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@truffle/debug-utils/-/debug-utils-6.0.9.tgz", + "integrity": "sha512-CVKVsbEWE0TzmTdGcxEeQvNhTKxkojIRox3LWh2Z8ZARtPiWwYl/lmPqfugh1665xmc2NVLI9qyP4ygVFPkvIQ==", "requires": { "@truffle/codec": "^0.11.27", "@trufflesuite/chromafi": "^3.0.0", @@ -25319,6 +25349,8 @@ }, "@trufflesuite/chromafi": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@trufflesuite/chromafi/-/chromafi-3.0.0.tgz", + "integrity": "sha512-oqWcOqn8nT1bwlPPfidfzS55vqcIDdpfzo3HbU9EnUmcSTX+I8z0UyUFI3tZQjByVJulbzxHxUGS3ZJPwK/GPQ==", "requires": { "camelcase": "^4.1.0", "chalk": "^2.3.2", @@ -30872,6 +30904,8 @@ }, "he": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "peer": true }, "header-case": { @@ -34237,6 +34271,8 @@ }, "simple-get": { "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", "requires": { "decompress-response": "^3.3.0", "once": "^1.3.1", diff --git a/src/@utils/aquarius.ts b/src/@utils/aquarius.ts index d751ae464..31f65c128 100644 --- a/src/@utils/aquarius.ts +++ b/src/@utils/aquarius.ts @@ -1,10 +1,5 @@ -import { - Asset, - LoggerInstance, - PublisherTrustedAlgorithm -} from '@oceanprotocol/lib' +import { Asset, LoggerInstance } from '@oceanprotocol/lib' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' -import { PriceList } from './subgraph' import axios, { CancelToken, AxiosResponse } from 'axios' import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData' import { metadataCacheUri } from '../../app.config' @@ -12,7 +7,7 @@ import { SortDirectionOptions, SortTermOptions } from '../@types/aquarius/SearchQuery' -import { getServiceByName } from './ddo' +import { transformAssetToAssetSelection } from './assetConvertor' export const MAXIMUM_NUMBER_OF_PAGES_WITH_RESULTS = 476 @@ -202,58 +197,6 @@ export async function retrieveDDOListByDIDs( } } -export async function transformDDOToAssetSelection( - datasetProviderEndpoint: string, - ddoList: Asset[], - selectedAlgorithms?: PublisherTrustedAlgorithm[], - cancelToken?: CancelToken -): Promise { - const didList: string[] = [] - // const priceList: PriceList = await getAssetsPriceList(ddoList) - const priceList: PriceList = null - const symbolList: any = {} - const didProviderEndpointMap: any = {} - for (const ddo of ddoList) { - didList.push(ddo.id) - symbolList[ddo.id] = ddo.datatokens[0].symbol - const algoComputeService = getServiceByName(ddo, 'compute') - algoComputeService?.serviceEndpoint && - (didProviderEndpointMap[ddo.id] = algoComputeService?.serviceEndpoint) - } - const ddoNames = await getAssetsNames(didList, cancelToken) - const algorithmList: AssetSelectionAsset[] = [] - didList?.forEach((did: string) => { - if ( - priceList[did] && - (!didProviderEndpointMap[did] || - didProviderEndpointMap[did] === datasetProviderEndpoint) - ) { - let selected = false - selectedAlgorithms?.forEach((algorithm: PublisherTrustedAlgorithm) => { - if (algorithm.did === did) { - selected = true - } - }) - selected - ? algorithmList.unshift({ - did: did, - name: ddoNames[did], - price: priceList[did], - checked: selected, - symbol: symbolList[did] - }) - : algorithmList.push({ - did: did, - name: ddoNames[did], - price: priceList[did], - checked: selected, - symbol: symbolList[did] - }) - } - }) - return algorithmList -} - export async function getAlgorithmDatasetsForCompute( algorithmId: string, datasetProviderUri: string, @@ -264,7 +207,7 @@ export async function getAlgorithmDatasetsForCompute( chainIds: [datasetChainId], filters: [ getFilterTerm( - 'service.attributes.main.privacy.publisherTrustedAlgorithms.did', + 'service.compite.publisherTrustedAlgorithms.did', algorithmId ) ], @@ -279,11 +222,10 @@ export async function getAlgorithmDatasetsForCompute( if (computeDatasets.totalResults === 0) return [] - const datasets = await transformDDOToAssetSelection( + const datasets = await transformAssetToAssetSelection( datasetProviderUri, computeDatasets.results, - [], - cancelToken + [] ) return datasets } diff --git a/src/@utils/assetConvertor.ts b/src/@utils/assetConvertor.ts new file mode 100644 index 000000000..0528bcdcf --- /dev/null +++ b/src/@utils/assetConvertor.ts @@ -0,0 +1,43 @@ +import { getAccessDetailsForAssets } from './accessDetailsAndPricing' +import { AssetExtended } from 'src/@types/AssetExtended' +import { PublisherTrustedAlgorithm, Asset } from '@oceanprotocol/lib' +import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' +import { getServiceByName } from './ddo' + +export async function transformAssetToAssetSelection( + datasetProviderEndpoint: string, + assets: Asset[], + selectedAlgorithms?: PublisherTrustedAlgorithm[] +): Promise { + const extendedAssets: AssetExtended[] = await getAccessDetailsForAssets( + assets + ) + const algorithmList: AssetSelectionAsset[] = [] + + for (const asset of extendedAssets) { + const algoComputeService = getServiceByName(asset, 'compute') + + if ( + asset?.accessDetails.price && + algoComputeService?.serviceEndpoint === datasetProviderEndpoint + ) { + let selected = false + selectedAlgorithms?.forEach((algorithm: PublisherTrustedAlgorithm) => { + if (algorithm.did === asset.id) { + selected = true + } + }) + const algorithmAsset: AssetSelectionAsset = { + did: asset.id, + name: asset.datatokens[0].name, + price: asset.accessDetails.price, + checked: selected, + symbol: asset.datatokens[0].symbol + } + selected + ? algorithmList.unshift(algorithmAsset) + : algorithmList.push(algorithmAsset) + } + } + return algorithmList +} diff --git a/src/@utils/compute.ts b/src/@utils/compute.ts index 4f14b6fb6..41e4b5baf 100644 --- a/src/@utils/compute.ts +++ b/src/@utils/compute.ts @@ -1,11 +1,20 @@ +// import { +// ServiceComputePrivacy, +// publisherTrustedAlgorithm as PublisherTrustedAlgorithm, +// Service, +// LoggerInstance, +// Provider, +// Config, +// Ocean, +// Account +// } from '@oceanprotocol/lib' +// import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute' import { Asset, - DDO, - ComputeAlgorithm, - Service, - LoggerInstance, - ProviderInstance, - PublisherTrustedAlgorithm + ServiceComputeOptions, + PublisherTrustedAlgorithm, + getHash, + LoggerInstance } from '@oceanprotocol/lib' import { CancelToken } from 'axios' import { gql } from 'urql' @@ -13,7 +22,7 @@ import { queryMetadata, getFilterTerm, generateBaseQuery, - transformDDOToAssetSelection + retrieveDDOListByDIDs } from './aquarius' import { fetchDataForMultipleChains } from './subgraph' import { getServiceById, getServiceByName } from './ddo' @@ -364,37 +373,50 @@ function getServiceEndpoints(data: TokenOrder[], assets: Asset[]): string[] { // return computeResult // } -// export async function createTrustedAlgorithmList( -// selectedAlgorithms: string[], // list of DIDs -// ocean: Ocean -// ): Promise { -// const trustedAlgorithms = [] +export async function createTrustedAlgorithmList( + selectedAlgorithms: string[], // list of DIDs, + assetChainId: number, + cancelToken: CancelToken +): Promise { + const trustedAlgorithms: PublisherTrustedAlgorithm[] = [] -// for (const selectedAlgorithm of selectedAlgorithms) { -// const trustedAlgorithm = -// await ocean.compute.createPublisherTrustedAlgorithmfromDID( -// selectedAlgorithm -// ) -// trustedAlgorithms.push(trustedAlgorithm) -// } -// return trustedAlgorithms -// } + const selectedAssets = await retrieveDDOListByDIDs( + selectedAlgorithms, + [assetChainId], + cancelToken + ) -// export async function transformComputeFormToServiceComputePrivacy( -// values: ComputePrivacyForm, -// ocean: Ocean -// ): Promise { -// const { allowAllPublishedAlgorithms } = values -// const publisherTrustedAlgorithms = values.allowAllPublishedAlgorithms -// ? [] -// : await createTrustedAlgorithmList(values.publisherTrustedAlgorithms, ocean) + for (const selectedAlgorithm of selectedAssets) { + const trustedAlgorithm = { + did: selectedAlgorithm.id, + containerSectionChecksum: getHash( + JSON.stringify(selectedAlgorithm.metadata.algorithm.container) + ), + filesChecksum: getHash(selectedAlgorithm.services[0].files) + } + trustedAlgorithms.push(trustedAlgorithm) + } + return trustedAlgorithms +} -// const privacy: ServiceComputePrivacy = { -// allowNetworkAccess: false, -// allowRawAlgorithm: false, -// allowAllPublishedAlgorithms, -// publisherTrustedAlgorithms -// } +export async function transformComputeFormToServiceComputeOptions( + values: ComputePrivacyForm, + currentOptions: ServiceComputeOptions, + assetChainId: number, + cancelToken: CancelToken +): Promise { + const publisherTrustedAlgorithms = values.allowAllPublishedAlgorithms + ? [] + : await createTrustedAlgorithmList( + values.publisherTrustedAlgorithms, + assetChainId, + cancelToken + ) -// return privacy -// } + const privacy: ServiceComputeOptions = { + ...currentOptions, + publisherTrustedAlgorithms + } + + return privacy +} diff --git a/src/@utils/dispenser.ts b/src/@utils/dispenser.ts new file mode 100644 index 000000000..cf128adbe --- /dev/null +++ b/src/@utils/dispenser.ts @@ -0,0 +1,48 @@ +import { LoggerInstance, Dispenser, Datatoken } from '@oceanprotocol/lib' +import Web3 from 'web3' +import { TransactionReceipt } from 'web3-core' + +export async function setMinterToPublisher( + web3: Web3, + dispenserAddress: string, + datatokenAddress: string, + accountId: string, + setError: (msg: string) => void +): Promise { + const dispenserInstance = new Dispenser(web3, dispenserAddress) + const status = await dispenserInstance.status(datatokenAddress) + if (!status?.active) return + + const datatokenInstance = new Datatoken(web3) + + const response = await datatokenInstance.removeMinter( + datatokenAddress, + accountId, + accountId + ) + if (!response) { + setError('Updating DDO failed.') + LoggerInstance.error('Failed at cancelMinter') + } + return response +} + +export async function setMinterToDispenser( + web3: Web3, + datatokenAddress: string, + accountId: string, + setError: (msg: string) => void +): Promise { + const datatokenInstance = new Datatoken(web3) + + const response = await datatokenInstance.addMinter( + datatokenAddress, + accountId, + accountId + ) + if (!response) { + setError('Updating DDO failed.') + LoggerInstance.error('Failed at makeMinter') + } + return response +} diff --git a/src/@utils/feedback.ts b/src/@utils/feedback.ts index 2df15688b..c335bddd1 100644 --- a/src/@utils/feedback.ts +++ b/src/@utils/feedback.ts @@ -11,6 +11,13 @@ export function getOrderFeedback( } } +export function getCollectTokensFeedback( + baseTokenSymbol: string, + baseTokenBalance: string +) { + return `Collecting ${baseTokenBalance} ${baseTokenSymbol} from asset ` +} + // TODO: customize for compute export const computeFeedback: { [key in number]: string } = { 0: 'Ordering asset...', diff --git a/src/@utils/nft.ts b/src/@utils/nft.ts index 62ae06711..15f20c036 100644 --- a/src/@utils/nft.ts +++ b/src/@utils/nft.ts @@ -1,4 +1,14 @@ import { SvgWaves } from './SvgWaves' +import { + Asset, + LoggerInstance, + getHash, + Nft, + ProviderInstance, + DDO +} from '@oceanprotocol/lib' +import Web3 from 'web3' +import { TransactionReceipt } from 'web3-core' // https://docs.opensea.io/docs/metadata-standards export interface NftMetadata { @@ -71,3 +81,50 @@ export function generateNftCreateData(nftMetadata: NftMetadata): any { return nftCreateData } + +export async function setNftMetadata( + asset: Asset | DDO, + accountId: string, + web3: Web3, + signal: AbortSignal +): Promise { + const encryptedDdo = await ProviderInstance.encrypt( + asset, + asset.services[0].serviceEndpoint, + signal + ) + LoggerInstance.log('[setNftMetadata] Got encrypted DDO', encryptedDdo) + + const metadataHash = getHash(JSON.stringify(asset)) + const nft = new Nft(web3) + + // theoretically used by aquarius or provider, not implemented yet, will remain hardcoded + const flags = '0x2' + + const estGasSetMetadata = await nft.estGasSetMetadata( + asset.nftAddress, + accountId, + 0, + asset.services[0].serviceEndpoint, + '', + flags, + encryptedDdo, + '0x' + metadataHash, + [] + ) + + console.log('[setNftMetadata] est Gas set metadata --', estGasSetMetadata) + + const setMetadataTx = await nft.setMetadata( + asset.nftAddress, + accountId, + 0, + asset.services[0].serviceEndpoint, + '', + flags, + encryptedDdo, + '0x' + metadataHash + ) + + return setMetadataTx +} diff --git a/src/@utils/subgraph.ts b/src/@utils/subgraph.ts index 71095e067..ec6048ba0 100644 --- a/src/@utils/subgraph.ts +++ b/src/@utils/subgraph.ts @@ -17,6 +17,7 @@ import { OrdersData_orders_datatoken as OrdersDatatoken } from '../@types/subgraph/OrdersData' import { UserSalesQuery as UsersSalesList } from '../@types/subgraph/UserSalesQuery' +import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery' export interface UserLiquidity { price: string @@ -188,6 +189,17 @@ const TopSalesQuery = gql` } ` +const OpcFeesQuery = gql` + query OpcFeesQuery($id: ID!) { + opc(id: $id) { + swapOceanFee + swapNonOceanFee + consumeFee + providerFee + } + } +` + export function getSubgraphUri(chainId: number): string { const config = getOceanConfig(chainId) return config.subgraphUri @@ -242,6 +254,26 @@ export async function fetchDataForMultipleChains( return datas } +export async function getOpcFees(chainId: number) { + let opcFees + const variables = { + id: 1 + } + const context = getQueryContext(chainId) + try { + const response: OperationResult = await fetchData( + OpcFeesQuery, + variables, + context + ) + opcFees = response?.data?.opc + } catch (error) { + console.error('Error fetchData: ', error.message) + throw Error(error.message) + } + return opcFees +} + export async function getPreviousOrders( id: string, account: string, diff --git a/src/components/@shared/ButtonBuy/index.tsx b/src/components/@shared/ButtonBuy/index.tsx index e2db0e640..29d3d83a3 100644 --- a/src/components/@shared/ButtonBuy/index.tsx +++ b/src/components/@shared/ButtonBuy/index.tsx @@ -4,7 +4,7 @@ import styles from './index.module.css' import Loader from '../atoms/Loader' interface ButtonBuyProps { - action: 'download' | 'compute' + action: 'download' | 'compute' | 'collect' disabled: boolean hasPreviousOrder: boolean hasDatatoken: boolean @@ -153,6 +153,8 @@ export default function ButtonBuy({ ? 'Start Compute Job' : priceType === 'free' && algorithmPriceType === 'free' ? 'Order Compute Job' + : action === 'collect' + ? `Collect ${dtBalance} ${dtSymbol}` : `Buy Compute Job` return ( diff --git a/src/components/@shared/FormFields/FilesInput/index.tsx b/src/components/@shared/FormFields/FilesInput/index.tsx index 8336222a2..a852f56e4 100644 --- a/src/components/@shared/FormFields/FilesInput/index.tsx +++ b/src/components/@shared/FormFields/FilesInput/index.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, useState } from 'react' +import React, { ReactElement, useCallback, useEffect, useState } from 'react' import { useField, useFormikContext } from 'formik' import { toast } from 'react-toastify' import FileInfo from './Info' @@ -13,24 +13,43 @@ export default function FilesInput(props: InputProps): ReactElement { const [isLoading, setIsLoading] = useState(false) const { values } = useFormikContext() - function loadFileInfo(url: string) { - const providerUri = values.services[0].providerUrl.url + const loadFileInfo = useCallback( + (url: string) => { + const providerUri = + (values.services && values.services[0].providerUrl.url) || + 'https://provider.mainnet.oceanprotocol.com' - async function validateUrl() { - try { - setIsLoading(true) - const checkedFile = await getFileUrlInfo(url, providerUri) - checkedFile && helpers.setValue([{ url, ...checkedFile[0] }]) - } catch (error) { - toast.error('Could not fetch file info. Please check URL and try again') - console.error(error.message) - } finally { - setIsLoading(false) + async function validateUrl() { + try { + setIsLoading(true) + const checkedFile = await getFileUrlInfo(url, providerUri) + checkedFile && helpers.setValue([{ url, ...checkedFile[0] }]) + } catch (error) { + toast.error( + 'Could not fetch file info. Please check URL and try again' + ) + console.error(error.message) + } finally { + setIsLoading(false) + } } - } - validateUrl() - } + validateUrl() + }, + [helpers, values.services] + ) + + useEffect(() => { + // try load from initial values, kinda hacky but it works + if ( + props.value && + props.value.length > 0 && + typeof props.value[0] === 'string' + ) { + console.log('loadFileInfo eff') + loadFileInfo(props.value[0].toString()) + } + }, [loadFileInfo, props]) async function handleButtonClick(e: React.SyntheticEvent, url: string) { // File example 'https://oceanprotocol.com/tech-whitepaper.pdf' diff --git a/src/components/@shared/FormInput/InputElement.module.css b/src/components/@shared/FormInput/InputElement.module.css index 233643a59..6f541dccd 100644 --- a/src/components/@shared/FormInput/InputElement.module.css +++ b/src/components/@shared/FormInput/InputElement.module.css @@ -38,8 +38,6 @@ color: var(--brand-grey-light); cursor: not-allowed; pointer-events: none; - /* for hiding spin buttons in Firefox */ - -moz-appearance: textfield; } .input[readonly]::-webkit-inner-spin-button, diff --git a/src/components/Asset/AssetActions/Compute/index.tsx b/src/components/Asset/AssetActions/Compute/index.tsx index 1137d57c7..551ccea52 100644 --- a/src/components/Asset/AssetActions/Compute/index.tsx +++ b/src/components/Asset/AssetActions/Compute/index.tsx @@ -29,8 +29,7 @@ import { useWeb3 } from '@context/Web3' import { generateBaseQuery, getFilterTerm, - queryMetadata, - transformDDOToAssetSelection + queryMetadata } from '@utils/aquarius' import { Formik } from 'formik' import { getInitialValues, validationSchema } from './_constants' @@ -59,6 +58,7 @@ import { TransactionReceipt } from 'web3-core' import { useAbortController } from '@hooks/useAbortController' import { getAccessDetails } from '@utils/accessDetailsAndPricing' import { AccessDetails } from 'src/@types/Price' +import { transformAssetToAssetSelection } from '@utils/assetConvertor' export default function Compute({ asset, @@ -143,6 +143,54 @@ export default function Compute({ setHasAlgoAssetDatatoken(Number(AssetDtBalance) >= 1) } + function getQuerryString( + trustedAlgorithmList: PublisherTrustedAlgorithm[], + chainId?: number + ): SearchQuery { + const algorithmDidList = trustedAlgorithmList.map((x) => x.did) + + const baseParams = { + chainIds: [chainId], + sort: { sortBy: SortTermOptions.Created }, + filters: [ + getFilterTerm('service.attributes.main.type', 'algorithm'), + getFilterTerm('id', algorithmDidList) + ] + } as BaseQueryParams + + const query = generateBaseQuery(baseParams) + return query + } + + async function getAlgorithmList(): Promise { + const source = axios.CancelToken.source() + const computeService = ddo.services[0] + let algorithmSelectionList: AssetSelectionAsset[] + if ( + !computeService.compute || + !computeService.compute.publisherTrustedAlgorithms || + computeService.compute.publisherTrustedAlgorithms.length === 0 + ) { + algorithmSelectionList = [] + } else { + const gueryResults = await queryMetadata( + getQuerryString( + computeService.compute.publisherTrustedAlgorithms, + ddo.chainId + ), + source.token + ) + setDdoAlgorithmList(gueryResults.results) + + algorithmSelectionList = await transformAssetToAssetSelection( + computeService?.serviceEndpoint, + gueryResults.results, + [] + ) + } + return algorithmSelectionList + } + const initMetadata = useCallback(async (asset: Asset): Promise => { if (!asset) return const accessDetails = await getAccessDetails( diff --git a/src/components/Asset/AssetActions/Download.module.css b/src/components/Asset/AssetActions/Download.module.css index d6dc45629..4efc7f192 100644 --- a/src/components/Asset/AssetActions/Download.module.css +++ b/src/components/Asset/AssetActions/Download.module.css @@ -16,3 +16,10 @@ width: 100%; margin-top: calc(var(--spacer)); } + +.collect { + width: 100%; + justify-content: center; + text-align: center; + margin-top: calc(var(--spacer) / 2); +} diff --git a/src/components/Asset/AssetActions/Download.tsx b/src/components/Asset/AssetActions/Download.tsx index 3f43f48f9..fd023aaa6 100644 --- a/src/components/Asset/AssetActions/Download.tsx +++ b/src/components/Asset/AssetActions/Download.tsx @@ -7,16 +7,37 @@ import ButtonBuy from '@shared/ButtonBuy' import { secondsToString } from '@utils/ddo' import AlgorithmDatasetsListForCompute from './Compute/AlgorithmDatasetsListForCompute' import styles from './Download.module.css' -import { FileMetadata, LoggerInstance, ZERO_ADDRESS } from '@oceanprotocol/lib' +import { + FileMetadata, + LoggerInstance, + ZERO_ADDRESS, + FixedRateExchange +} from '@oceanprotocol/lib' import { order } from '@utils/order' import { AssetExtended } from 'src/@types/AssetExtended' -import { buyDtFromPool, calculateBuyPrice } from '@utils/pool' +import { buyDtFromPool } from '@utils/pool' import { downloadFile } from '@utils/provider' -import { getOrderFeedback } from '@utils/feedback' +import { getCollectTokensFeedback, getOrderFeedback } from '@utils/feedback' import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing' import { OrderPriceAndFees } from 'src/@types/Price' import { toast } from 'react-toastify' +import { gql, OperationResult } from 'urql' +import { fetchData, getQueryContext } from '@utils/subgraph' +import { getOceanConfig } from '@utils/ocean' +import { FixedRateExchanges } from 'src/@types/subgraph/FixedRateExchanges' +const FixedRateExchangesQuery = gql` + query FixedRateExchanges($user: String, $exchangeId: String) { + fixedRateExchanges(where: { owner: $user, exchangeId: $exchangeId }) { + id + owner { + id + } + exchangeId + baseTokenBalance + } + } +` export default function Download({ asset, file, @@ -40,6 +61,9 @@ export default function Download({ const [isLoading, setIsLoading] = useState(false) const [isOwned, setIsOwned] = useState(false) const [validOrderTx, setValidOrderTx] = useState('') + const [isCollectLoading, setIsCollectLoading] = useState(false) + const [baseTokenBalance, setBaseTokenBalance] = useState(0) + const [collectStatusText, setCollectStatusText] = useState('') const [orderPriceAndFees, setOrderPriceAndFees] = useState() useEffect(() => { @@ -80,6 +104,29 @@ export default function Download({ isOwned ]) + useEffect(() => { + if (!accountId || asset.nft.owner !== accountId) return + const queryContext = getQueryContext(Number(asset.chainId)) + + async function getBaseTokenBalance() { + const variables = { + user: accountId.toLowerCase(), + exchangeId: asset?.accessDetails?.addressOrId + } + const result: OperationResult = await fetchData( + FixedRateExchangesQuery, + variables, + queryContext + ) + result?.data?.fixedRateExchanges[0]?.baseTokenBalance + ? setBaseTokenBalance( + parseInt(result?.data?.fixedRateExchanges[0]?.baseTokenBalance) + ) + : setBaseTokenBalance(0) + } + getBaseTokenBalance() + }, [accountId, asset?.accessDetails?.addressOrId, asset.chainId, asset.nft]) + async function handleOrderOrDownload() { setIsLoading(true) if (isOwned) { @@ -127,6 +174,37 @@ export default function Download({ setIsLoading(false) } + async function handleCollectTokens() { + setIsCollectLoading(true) + const config = getOceanConfig(asset?.chainId) + const fixed = new FixedRateExchange(web3, config.fixedRateExchangeAddress) + try { + setCollectStatusText( + getCollectTokensFeedback( + asset.accessDetails.baseToken?.symbol, + baseTokenBalance.toString() + ) + ) + + const tx = await fixed.collectBT( + accountId, + asset?.accessDetails?.addressOrId + ) + + if (!tx) { + setIsCollectLoading(false) + return + } + setBaseTokenBalance(0) + return tx + } catch (error) { + LoggerInstance.log(error.message) + setIsCollectLoading(false) + } finally { + setIsCollectLoading(false) + } + } + const PurchaseButton = () => ( ) + const CollectTokensButton = () => ( + + ) + return (