From 68927ad5f5354d077b0eaae746c4d3e3368fd069 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 28 Aug 2024 09:57:07 +0200 Subject: [PATCH 01/31] edit service in new tab --- content/pages/editService.json | 150 +++++++++++++ src/@context/Asset.tsx | 16 +- src/@types/Price.d.ts | 1 + src/@utils/accessDetailsAndPricing.ts | 14 +- src/components/Asset/Edit/EditService.tsx | 200 ++++++++++++++++++ src/components/Asset/Edit/EditServices.tsx | 33 +++ src/components/Asset/Edit/FormEditService.tsx | 94 ++++++++ src/components/Asset/Edit/_constants.ts | 18 +- src/components/Asset/Edit/_types.ts | 11 + src/components/Asset/Edit/index.tsx | 5 + 10 files changed, 535 insertions(+), 7 deletions(-) create mode 100644 content/pages/editService.json create mode 100644 src/components/Asset/Edit/EditService.tsx create mode 100644 src/components/Asset/Edit/EditServices.tsx create mode 100644 src/components/Asset/Edit/FormEditService.tsx diff --git a/content/pages/editService.json b/content/pages/editService.json new file mode 100644 index 000000000..e1c0059ab --- /dev/null +++ b/content/pages/editService.json @@ -0,0 +1,150 @@ +{ + "form": { + "success": "🎉 Successfully updated. 🎉\n\nUpdates might not show up right away on your asset. In this case, wait some seconds and reload your asset details page in your browser.", + "error": "Updating DDO failed.", + "data": [ + { + "name": "name", + "label": "Service Name", + "placeholder": "Service 1", + "help": "Enter a concise title.", + "required": false + }, + { + "name": "description", + "label": "Service Description", + "help": "Enter a detailed description.", + "type": "textarea", + "rows": 2, + "required": false + }, + { + "name": "price", + "label": "New Price", + "type": "number", + "min": "1", + "placeholder": "0", + "help": "Enter a new price.", + "required": true + }, + { + "name": "files", + "label": "File", + "prominentHelp": false, + "type": "tabs", + "fields": [ + { + "value": "ipfs", + "title": "IPFS", + "label": "CID", + "placeholder": "e.g. bafkreidgvpkjawlxz6sffxzwgooowe5yt7i6wsyg236mfoks77nywkptdq", + "help": "This CID will be stored encrypted after publishing.", + "computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ", + "prominentHelp": true, + "type": "files", + "required": true + }, + { + "value": "arweave", + "title": "Arweave", + "label": "Transaction ID", + "placeholder": "e.g. DBRCL94j3QqdPaUtt4VWRen8rZfJZBb7Ey40iMpXfhtd", + "help": "This Transaction ID will be stored encrypted after publishing.", + "computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ", + "prominentHelp": true, + "type": "files", + "required": true + }, + { + "value": "url", + "title": "URL", + "label": "File", + "placeholder": "e.g. https://file.com/file.json", + "help": "This URL will be stored encrypted after publishing. **Please make sure that the endpoint is accessible over the internet and is not protected by a firewall or by credentials.**", + "computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ", + "prominentHelp": true, + "type": "files", + "required": true, + "innerFields": [ + { + "value": "headers", + "title": "Headers", + "label": "Headers", + "placeholder_value": "Authorization", + "help": "This HEADERS will be stored encrypted after publishing.", + "type": "headers", + "required": true + } + ] + }, + { + "value": "graphql", + "title": "Graphql", + "label": "URL", + "placeholder": "e.g. http://172.15.0.15:8000/subgraphs/name/oceanprotocol/ocean-subgraph", + "help": "This URL will be stored encrypted after publishing.", + "computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ", + "prominentHelp": true, + "type": "files", + "required": true, + "headers": true, + "innerFields": [ + { + "value": "headers", + "title": "Headers", + "label": "Headers", + "placeholder_value": "Authorization", + "help": "This HEADERS will be stored encrypted after publishing.", + "type": "headers", + "required": true + }, + { + "value": "query", + "title": "Query", + "label": "Query", + "placeholder": "query{\n nfts(\n orderBy: createdTimestamp,\n orderDirection:desc\n ){\n id\n symbol\n createdTimestamp\n }\n}", + "help": "This QUERY will be stored encrypted after publishing.", + "type": "codeeditor", + "required": true + } + ] + } + ], + "sortOptions": false, + "required": true + }, + { + "name": "timeout", + "label": "Timeout", + "help": "Define how long buyers should be able to download the dataset again after the initial purchase.", + "type": "select", + "options": ["Forever", "1 day", "1 week", "1 month", "1 year"], + "sortOptions": false, + "required": true + }, + { + "name": "usesConsumerParameters", + "label": "Algorithm custom parameters", + "help": "Algorithm custom parameters are used to define required consumer input before running the algorithm in a Compute-to-Data environment.", + "type": "checkbox", + "options": ["This asset uses algorithm custom parameters"], + "required": false + }, + { + "name": "paymentCollector", + "label": "Payment Collector Address", + "placeholder": "e.g. 0X123ABC...", + "help": "This address will receive the revenue from all sales. More info available in our [docs](https://docs.oceanprotocol.com/core-concepts/datanft-and-datatoken#revenue).", + "required": false + }, + { + "name": "usesServiceConsumerParameters", + "label": "User defined parameters", + "help": "User defined parameters are used to filter or query the published asset.", + "type": "checkbox", + "options": ["This asset uses user defined parameters"], + "required": false + } + ] + } +} diff --git a/src/@context/Asset.tsx b/src/@context/Asset.tsx index 877995e2f..960c6ccb4 100644 --- a/src/@context/Asset.tsx +++ b/src/@context/Asset.tsx @@ -18,7 +18,7 @@ import { useMarketMetadata } from './MarketMetadata' import { assetStateToString } from '@utils/assetState' import { isValidDid } from '@utils/ddo' import { useAddressConfig } from '@hooks/useAddressConfig' -import { useAccount, useNetwork } from 'wagmi' +import { useAccount, useNetwork, useProvider } from 'wagmi' export interface AssetProviderValue { isInPurgatory: boolean @@ -47,6 +47,7 @@ function AssetProvider({ const { appConfig } = useMarketMetadata() const { address: accountId } = useAccount() const { chain } = useNetwork() + const provider = useProvider() const { isDDOWhitelisted } = useAddressConfig() const [isInPurgatory, setIsInPurgatory] = useState(false) @@ -136,7 +137,10 @@ function AssetProvider({ const accessDetails = await Promise.all( asset.services.map((service: Service) => getAccessDetails( - asset.offchain?.stats.services.find((s) => s.serviceId === service.id) + asset.offchain?.stats.services.find( + (s) => s.serviceId === service.id + ), + provider ) ) ) @@ -146,7 +150,13 @@ function AssetProvider({ accessDetails })) LoggerInstance.log(`[asset] Got access details for ${did}`, accessDetails) - }, [asset?.chainId, asset?.offchain?.stats.services, asset?.services, did]) + }, [ + asset?.chainId, + asset?.offchain?.stats.services, + asset?.services, + did, + provider + ]) // ----------------------------------- // 1. Get and set asset based on passed DID diff --git a/src/@types/Price.d.ts b/src/@types/Price.d.ts index 7d809e767..e76cf541a 100644 --- a/src/@types/Price.d.ts +++ b/src/@types/Price.d.ts @@ -48,6 +48,7 @@ declare global { validOrderTx: string publisherMarketOrderFee: string validProviderFees?: ProviderFees + paymentCollector: string } interface PricePublishOptions { diff --git a/src/@utils/accessDetailsAndPricing.ts b/src/@utils/accessDetailsAndPricing.ts index e3c55d73c..acfa203a1 100644 --- a/src/@utils/accessDetailsAndPricing.ts +++ b/src/@utils/accessDetailsAndPricing.ts @@ -14,8 +14,9 @@ import { publisherMarketOrderFee, customProviderUrl } from '../../app.config' -import { Signer } from 'ethers' +import { ethers, Signer } from 'ethers' import { toast } from 'react-toastify' +import { getPaymentCollector } from './ocean' /** * This will be used to get price including fees before ordering @@ -114,8 +115,14 @@ export async function getOrderPriceAndFees( * @returns {Promise} */ export async function getAccessDetails( - serviceStat: ServiceStat | undefined + serviceStat: ServiceStat | undefined, + provider: ethers.providers.Provider ): Promise { + const paymentCollector = await getPaymentCollector( + serviceStat.datatokenAddress, + provider + ) + const accessDetails: AccessDetails = { type: 'NOT_SUPPORTED', price: '0', @@ -136,7 +143,8 @@ export async function getAccessDetails( isOwned: false, validOrderTx: '', isPurchasable: false, - publisherMarketOrderFee: '0' + publisherMarketOrderFee: '0', + paymentCollector } if (serviceStat === undefined || serviceStat.prices.length === 0) { diff --git a/src/components/Asset/Edit/EditService.tsx b/src/components/Asset/Edit/EditService.tsx new file mode 100644 index 000000000..0a6a1eb7c --- /dev/null +++ b/src/components/Asset/Edit/EditService.tsx @@ -0,0 +1,200 @@ +import { ReactElement, useState } from 'react' +import { Formik } from 'formik' +import { + LoggerInstance, + FixedRateExchange, + Datatoken, + Service +} from '@oceanprotocol/lib' +import { getServiceInitialValues } from './_constants' +import { ServiceEditForm } from './_types' +import { useUserPreferences } from '@context/UserPreferences' +import Web3Feedback from '@shared/Web3Feedback' +import { mapTimeoutStringToSeconds, normalizeFile } from '@utils/ddo' +import content from '../../../../content/pages/editService.json' +import { useAbortController } from '@hooks/useAbortController' +import { getOceanConfig } from '@utils/ocean' +import EditFeedback from './EditFeedback' +import { useAsset } from '@context/Asset' +import { setNftMetadata } from '@utils/nft' +import { getEncryptedFiles } from '@utils/provider' +import { useAccount, useNetwork, useSigner } from 'wagmi' +import { transformConsumerParameters } from '@components/Publish/_utils' +import FormEditService from './FormEditService' + +export default function EditService({ + asset, + service, + accessDetails +}: { + asset: AssetExtended + service: Service + accessDetails: AccessDetails +}): ReactElement { + const { debug } = useUserPreferences() + const { fetchAsset, isAssetNetwork } = useAsset() + const { address: accountId } = useAccount() + const { chain } = useNetwork() + const { data: signer } = useSigner() + const newAbortController = useAbortController() + + const [success, setSuccess] = useState() + const [error, setError] = useState() + const hasFeedback = error || success + + async function updateFixedPrice(newPrice: string) { + console.log('updateFixedPrice') + const config = getOceanConfig(asset.chainId) + + const fixedRateInstance = new FixedRateExchange( + config.fixedRateExchangeAddress, + signer + ) + + const setPriceResp = await fixedRateInstance.setRate( + accessDetails.addressOrId, + newPrice.toString() + ) + LoggerInstance.log('[edit] setFixedRate result', setPriceResp) + if (!setPriceResp) { + setError(content.form.error) + LoggerInstance.error(content.form.error) + } + } + + // edit 1 service + async function handleSubmit(values: ServiceEditForm, resetForm: () => void) { + console.log('values', values) + try { + // update fixed price if changed + accessDetails.type === 'fixed' && + values.price !== accessDetails.price && + (await updateFixedPrice(values.price)) + + // update payment collector if changed + if (values.paymentCollector !== accessDetails.paymentCollector) { + console.log('setting payment collector') + const datatoken = new Datatoken(signer) + await datatoken.setPaymentCollector( + service.datatokenAddress, + accountId, + values.paymentCollector + ) + } + + let updatedFiles = service.files + if (values.files[0]?.url) { + console.log('updating files') + const file = { + nftAddress: asset.nftAddress, + datatokenAddress: service.datatokenAddress, + files: [ + normalizeFile(values.files[0].type, values.files[0], chain?.id) + ] + } + + const filesEncrypted = await getEncryptedFiles( + file, + asset.chainId, + service.serviceEndpoint + ) + updatedFiles = filesEncrypted + } + + const updatedService: Service = { + ...service, + name: values.name, + description: values.description, + timeout: mapTimeoutStringToSeconds(values.timeout), + files: updatedFiles // TODO: check if this works + } + if (values.consumerParameters) { + updatedService.consumerParameters = transformConsumerParameters( + values.consumerParameters + ) + } + + // update asset with new service + const serviceIndex = asset.services.findIndex((s) => s.id === service.id) + const updatedAsset = { ...asset } + updatedAsset.services[serviceIndex] = updatedService + + // delete custom helper properties injected in the market so we don't write them on chain + delete (updatedAsset as AssetExtended).accessDetails + delete (updatedAsset as AssetExtended).datatokens + delete (updatedAsset as AssetExtended).stats + delete (updatedAsset as AssetExtended).offchain + + const setMetadataTx = await setNftMetadata( + updatedAsset, + accountId, + signer, + newAbortController() + ) + + if (!setMetadataTx) { + setError(content.form.error) + LoggerInstance.error(content.form.error) + return + } + // Edit succeeded + setSuccess(content.form.success) + resetForm() + } catch (error) { + LoggerInstance.error(error.message) + setError(error.message) + } + } + + return ( + { + // move user's focus to top of screen + window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) + // kick off editing + await handleSubmit(values, resetForm) + }} + > + {({ isSubmitting, values }) => + isSubmitting || hasFeedback ? ( + { + await fetchAsset() + }, + to: `/asset/${asset.id}` + }} + /> + ) : ( + <> + + + + + {/* {debug === true && ( +
+ +
+ )} */} + + ) + } +
+ ) +} diff --git a/src/components/Asset/Edit/EditServices.tsx b/src/components/Asset/Edit/EditServices.tsx new file mode 100644 index 000000000..6113e04ef --- /dev/null +++ b/src/components/Asset/Edit/EditServices.tsx @@ -0,0 +1,33 @@ +import { ReactElement } from 'react' +import EditService from './EditService' +import Button from '@components/@shared/atoms/Button' + +export default function EditServices({ + asset +}: { + asset: AssetExtended +}): ReactElement { + return ( +
+ {asset.services.map((service, index) => ( + + ))} +
+ +
+
+ ) +} diff --git a/src/components/Asset/Edit/FormEditService.tsx b/src/components/Asset/Edit/FormEditService.tsx new file mode 100644 index 000000000..14b268784 --- /dev/null +++ b/src/components/Asset/Edit/FormEditService.tsx @@ -0,0 +1,94 @@ +import { ReactElement } from 'react' +import { Field, Form, useFormikContext } from 'formik' +import Input from '@shared/FormInput' +import FormActions from './FormActions' +import { getFieldContent } from '@utils/form' +import consumerParametersContent from '../../../../content/publish/consumerParameters.json' +import Accordion from '@components/@shared/Accordion' +import { Service } from '@oceanprotocol/lib' +import { ServiceEditForm } from './_types' + +export function checkIfTimeoutInPredefinedValues( + timeout: string, + timeoutOptions: string[] +): boolean { + if (timeoutOptions.indexOf(timeout) > -1) { + return true + } + return false +} + +export default function FormEditService({ + data, + service, + accessDetails +}: { + data: FormFieldContent[] + service: Service + accessDetails: AccessDetails +}): ReactElement { + const { values, setFieldValue } = useFormikContext() + + return ( +
+ +
+ + + + + {accessDetails.type === 'fixed' && ( + + )} + + + + + + + + + {values.usesConsumerParameters && ( + + )} + +
+
+
+ ) +} diff --git a/src/components/Asset/Edit/_constants.ts b/src/components/Asset/Edit/_constants.ts index d8827eb63..b55fc3cac 100644 --- a/src/components/Asset/Edit/_constants.ts +++ b/src/components/Asset/Edit/_constants.ts @@ -5,7 +5,7 @@ import { ServiceComputeOptions } from '@oceanprotocol/lib' import { parseConsumerParameters, secondsToString } from '@utils/ddo' -import { ComputeEditForm, MetadataEditForm } from './_types' +import { ComputeEditForm, MetadataEditForm, ServiceEditForm } from './_types' export function getInitialValues( metadata: Metadata, @@ -44,6 +44,22 @@ export function getInitialValues( } } +export const getServiceInitialValues = ( + service: Service, + accessDetails: AccessDetails +): ServiceEditForm => { + return { + name: service.name, + description: service.description, + price: accessDetails.price, + paymentCollector: accessDetails.paymentCollector, + files: [{ url: '', type: 'hidden' }], + timeout: secondsToString(service.timeout), + usesConsumerParameters: service.consumerParameters?.length > 0, + consumerParameters: parseConsumerParameters(service.consumerParameters) + } +} + export function getComputeSettingsInitialValues({ publisherTrustedAlgorithms, publisherTrustedAlgorithmPublishers diff --git a/src/components/Asset/Edit/_types.ts b/src/components/Asset/Edit/_types.ts index 81f9ab08f..d69b24ae1 100644 --- a/src/components/Asset/Edit/_types.ts +++ b/src/components/Asset/Edit/_types.ts @@ -23,6 +23,17 @@ export interface MetadataEditForm { license?: string } +export interface ServiceEditForm { + name: string + description: string + price: string + paymentCollector: string + files: FileInfo[] + timeout: string + usesConsumerParameters: boolean + consumerParameters: FormConsumerParameter[] +} + export interface ComputeEditForm { allowAllPublishedAlgorithms: boolean publisherTrustedAlgorithms: string[] diff --git a/src/components/Asset/Edit/index.tsx b/src/components/Asset/Edit/index.tsx index ef34c62f5..d804c1134 100644 --- a/src/components/Asset/Edit/index.tsx +++ b/src/components/Asset/Edit/index.tsx @@ -9,6 +9,7 @@ import Loader from '@shared/atoms/Loader' import Alert from '@shared/atoms/Alert' import contentPage from '../../../../content/pages/edit.json' import Container from '@shared/atoms/Container' +import EditServices from './EditServices' export default function Edit({ uri }: { uri: string }): ReactElement { const { asset, error, isInPurgatory, title, isOwner } = useAsset() @@ -34,6 +35,10 @@ export default function Edit({ uri }: { uri: string }): ReactElement { title: 'Edit Metadata', content: }, + { + title: 'Edit Services', + content: + }, ...[ isCompute && asset?.metadata.type !== 'algorithm' ? { From a84a9b018b8620ebfbaea0699dc7809814adaebe Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 28 Aug 2024 19:43:19 +0200 Subject: [PATCH 02/31] elimate the need of scrollbar inside accordion --- src/components/@shared/Accordion/index.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/@shared/Accordion/index.module.css b/src/components/@shared/Accordion/index.module.css index ae9e0c556..67ca5123e 100644 --- a/src/components/@shared/Accordion/index.module.css +++ b/src/components/@shared/Accordion/index.module.css @@ -51,7 +51,7 @@ } .open .content { - max-height: 35rem; + max-height: 1000rem; visibility: visible; overflow-y: scroll; transition: 0.3s max-height; From 64ea3180370fbb9f03a6efff90409e4bf39353b0 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 28 Aug 2024 19:44:11 +0200 Subject: [PATCH 03/31] disable scroll on input type number --- src/components/@shared/FormInput/InputElement/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/@shared/FormInput/InputElement/index.tsx b/src/components/@shared/FormInput/InputElement/index.tsx index fd3144a73..287ca320a 100644 --- a/src/components/@shared/FormInput/InputElement/index.tsx +++ b/src/components/@shared/FormInput/InputElement/index.tsx @@ -41,6 +41,7 @@ const DefaultInput = forwardRef( ref={ref} className={cx({ input: true, [size]: size, [className]: className })} id={props.name} + onWheel={(e) => props.type === 'number' && e.target.blur()} {...props} /> ) From 7b3098a690ac6370c5736627ae9ebd43364d72ae Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 28 Aug 2024 19:58:41 +0200 Subject: [PATCH 04/31] fix price type and remove some code --- src/components/Asset/Edit/EditService.tsx | 4 ++-- src/components/Asset/Edit/FormEditService.tsx | 12 +----------- src/components/Asset/Edit/_types.ts | 2 +- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/components/Asset/Edit/EditService.tsx b/src/components/Asset/Edit/EditService.tsx index 0a6a1eb7c..e67e43e3b 100644 --- a/src/components/Asset/Edit/EditService.tsx +++ b/src/components/Asset/Edit/EditService.tsx @@ -42,7 +42,7 @@ export default function EditService({ const [error, setError] = useState() const hasFeedback = error || success - async function updateFixedPrice(newPrice: string) { + async function updateFixedPrice(newPrice: number) { console.log('updateFixedPrice') const config = getOceanConfig(asset.chainId) @@ -68,7 +68,7 @@ export default function EditService({ try { // update fixed price if changed accessDetails.type === 'fixed' && - values.price !== accessDetails.price && + values.price !== parseFloat(accessDetails.price) && (await updateFixedPrice(values.price)) // update payment collector if changed diff --git a/src/components/Asset/Edit/FormEditService.tsx b/src/components/Asset/Edit/FormEditService.tsx index 14b268784..91a549e12 100644 --- a/src/components/Asset/Edit/FormEditService.tsx +++ b/src/components/Asset/Edit/FormEditService.tsx @@ -8,16 +8,6 @@ import Accordion from '@components/@shared/Accordion' import { Service } from '@oceanprotocol/lib' import { ServiceEditForm } from './_types' -export function checkIfTimeoutInPredefinedValues( - timeout: string, - timeoutOptions: string[] -): boolean { - if (timeoutOptions.indexOf(timeout) > -1) { - return true - } - return false -} - export default function FormEditService({ data, service, @@ -31,7 +21,7 @@ export default function FormEditService({ return (
- +
Date: Thu, 29 Aug 2024 10:15:13 +0200 Subject: [PATCH 05/31] add service functionality --- content/pages/editService.json | 18 ++ src/components/Asset/Edit/AddService.tsx | 237 +++++++++++++++++++ src/components/Asset/Edit/EditService.tsx | 8 - src/components/Asset/Edit/EditServices.tsx | 36 ++- src/components/Asset/Edit/FormAddService.tsx | 136 +++++++++++ src/components/Asset/Edit/_constants.ts | 32 ++- src/components/Asset/Edit/_types.ts | 5 + 7 files changed, 450 insertions(+), 22 deletions(-) create mode 100644 src/components/Asset/Edit/AddService.tsx create mode 100644 src/components/Asset/Edit/FormAddService.tsx diff --git a/content/pages/editService.json b/content/pages/editService.json index e1c0059ab..ea4e8ab0b 100644 --- a/content/pages/editService.json +++ b/content/pages/editService.json @@ -18,6 +18,16 @@ "rows": 2, "required": false }, + { + "name": "access", + "label": "Access Type", + "help": "Choose how you want your files to be accessible for the specified price.", + "type": "boxSelection", + "options": ["Access", "Compute"], + "required": true, + "disclaimer": "Please do not provide downloadable personal data without the consent of the data subjects.", + "disclaimerValues": ["Download"] + }, { "name": "price", "label": "New Price", @@ -27,6 +37,14 @@ "help": "Enter a new price.", "required": true }, + { + "name": "providerUrl", + "label": "Provider URL", + "type": "providerUrl", + "help": "Enter the URL for your custom [provider](https://github.com/oceanprotocol/provider/) or leave as is to use the default one. If you change your provider URL after adding your file, please add & validate your file again.", + "placeholder": "e.g. https://provider.oceanprotocol.com/", + "required": true + }, { "name": "files", "label": "File", diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx new file mode 100644 index 000000000..bb2efb6ed --- /dev/null +++ b/src/components/Asset/Edit/AddService.tsx @@ -0,0 +1,237 @@ +import { ReactElement, useState } from 'react' +import { Formik } from 'formik' +import { + LoggerInstance, + Datatoken, + Service, + Nft, + FreCreationParams, + DispenserParams, + getHash +} from '@oceanprotocol/lib' +import { getNewServiceInitialValues } from './_constants' +import { AddServiceForm } from './_types' +import Web3Feedback from '@shared/Web3Feedback' +import { mapTimeoutStringToSeconds, normalizeFile } from '@utils/ddo' +import content from '../../../../content/pages/editService.json' +import { useAbortController } from '@hooks/useAbortController' +import EditFeedback from './EditFeedback' +import { useAsset } from '@context/Asset' +import { setNftMetadata } from '@utils/nft' +import { getEncryptedFiles } from '@utils/provider' +import { useAccount, useNetwork, useSigner } from 'wagmi' +import { transformConsumerParameters } from '@components/Publish/_utils' +import { marketFeeAddress, publisherMarketFixedSwapFee } from 'app.config' +import { ethers } from 'ethers' +import FormAddService from './FormAddService' + +export default function AddService({ + asset, + onRemove +}: { + asset: AssetExtended + onRemove: () => void +}): ReactElement { + const { fetchAsset, isAssetNetwork } = useAsset() + const { address: accountId } = useAccount() + const { chain } = useNetwork() + const { data: signer } = useSigner() + const newAbortController = useAbortController() + + const [success, setSuccess] = useState() + const [error, setError] = useState() + const hasFeedback = error || success + + // add new service + async function handleSubmit(values: AddServiceForm, resetForm: () => void) { + console.log('values', values) + try { + if (!isAssetNetwork) { + setError('Please switch to the correct network.') + return + } + + // -------------------------------------------------- + // 1. Create Datatoken + // -------------------------------------------------- + const nft = new Nft(signer) + + const datatokenAddress = await nft.createDatatoken( + asset.nftAddress, + accountId, + accountId, + values.paymentCollector, + marketFeeAddress, + process.env.NEXT_PUBLIC_OCEAN_TOKEN_ADDRESS, + publisherMarketFixedSwapFee, + '100000000', + 'DataToken', + 'DT', + 1 + ) + + console.log('Datatoken created.', datatokenAddress) + + // -------------------------------------------------- + // 2. Create Pricing + // -------------------------------------------------- + const datatoken = new Datatoken(signer) + + let pricingTransactionReceipt + if (values.price > 0) { + console.log( + `Creating fixed rate exchange with price ${values.price} for datatoken ${datatokenAddress}` + ) + + const freParams: FreCreationParams = { + fixedRateAddress: process.env.NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS, + baseTokenAddress: process.env.NEXT_PUBLIC_OCEAN_TOKEN_ADDRESS, + owner: accountId, + marketFeeCollector: marketFeeAddress, + baseTokenDecimals: 18, + datatokenDecimals: 18, + fixedRate: ethers.utils + .parseEther(values.price.toString()) + .toString(), + marketFee: publisherMarketFixedSwapFee, + withMint: true + } + + pricingTransactionReceipt = await datatoken.createFixedRate( + datatokenAddress, + accountId, + freParams + ) + } else { + console.log(`Creating dispenser for datatoken ${datatokenAddress}`) + + const dispenserParams: DispenserParams = { + maxTokens: ethers.utils.parseEther('1').toString(), + maxBalance: ethers.utils.parseEther('1').toString(), + withMint: true + } + + pricingTransactionReceipt = await datatoken.createDispenser( + datatokenAddress, + accountId, + process.env.NEXT_PUBLIC_DISPENSER_ADDRESS, + dispenserParams + ) + } + + const tx = await pricingTransactionReceipt.wait() + console.log('Pricing scheme created.') + + // -------------------------------------------------- + // 2. Update DDO + // -------------------------------------------------- + let newFiles = asset.services[0].files // by default it could be the same file as in other services + if (values.files[0]?.url) { + console.log('updating files') + const file = { + nftAddress: asset.nftAddress, + datatokenAddress, + files: [ + normalizeFile(values.files[0].type, values.files[0], chain?.id) + ] + } + + const filesEncrypted = await getEncryptedFiles( + file, + asset.chainId, + values.providerUrl.url + ) + newFiles = filesEncrypted + } + + const newService: Service = { + id: getHash(datatokenAddress + newFiles), + type: values.access, + name: values.name, + description: values.description, + files: newFiles || '', + datatokenAddress, + serviceEndpoint: values.providerUrl.url, + timeout: mapTimeoutStringToSeconds(values.timeout), + // TODO + // ...(access === 'compute' && { + // compute: values.services[0].computeOptions + // }), + consumerParameters: transformConsumerParameters( + values.consumerParameters + ) + } + + // update asset with new service + const updatedAsset = { ...asset } + updatedAsset.services.push(newService) + + // delete custom helper properties injected in the market so we don't write them on chain + delete (updatedAsset as AssetExtended).accessDetails + delete (updatedAsset as AssetExtended).datatokens + delete (updatedAsset as AssetExtended).stats + delete (updatedAsset as AssetExtended).offchain + + const setMetadataTx = await setNftMetadata( + updatedAsset, + accountId, + signer, + newAbortController() + ) + + if (!setMetadataTx) { + setError(content.form.error) + LoggerInstance.error(content.form.error) + return + } + // Edit succeeded + setSuccess(content.form.success) + resetForm() + } catch (error) { + LoggerInstance.error(error.message) + setError(error.message) + } + } + + return ( + { + // move user's focus to top of screen + window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) + // kick off editing + await handleSubmit(values, resetForm) + }} + > + {({ isSubmitting, values }) => + isSubmitting || hasFeedback ? ( + { + await fetchAsset() + }, + to: `/asset/${asset.id}` + }} + /> + ) : ( + <> + + + + + ) + } + + ) +} diff --git a/src/components/Asset/Edit/EditService.tsx b/src/components/Asset/Edit/EditService.tsx index e67e43e3b..9566ef169 100644 --- a/src/components/Asset/Edit/EditService.tsx +++ b/src/components/Asset/Edit/EditService.tsx @@ -8,7 +8,6 @@ import { } from '@oceanprotocol/lib' import { getServiceInitialValues } from './_constants' import { ServiceEditForm } from './_types' -import { useUserPreferences } from '@context/UserPreferences' import Web3Feedback from '@shared/Web3Feedback' import { mapTimeoutStringToSeconds, normalizeFile } from '@utils/ddo' import content from '../../../../content/pages/editService.json' @@ -31,7 +30,6 @@ export default function EditService({ service: Service accessDetails: AccessDetails }): ReactElement { - const { debug } = useUserPreferences() const { fetchAsset, isAssetNetwork } = useAsset() const { address: accountId } = useAccount() const { chain } = useNetwork() @@ -186,12 +184,6 @@ export default function EditService({ accountId={accountId} isAssetNetwork={isAssetNetwork} /> - - {/* {debug === true && ( -
- -
- )} */} ) } diff --git a/src/components/Asset/Edit/EditServices.tsx b/src/components/Asset/Edit/EditServices.tsx index 6113e04ef..036df7d72 100644 --- a/src/components/Asset/Edit/EditServices.tsx +++ b/src/components/Asset/Edit/EditServices.tsx @@ -1,12 +1,15 @@ -import { ReactElement } from 'react' +import { ReactElement, useState } from 'react' import EditService from './EditService' import Button from '@components/@shared/atoms/Button' +import AddService from './AddService' export default function EditServices({ asset }: { asset: AssetExtended }): ReactElement { + const [isAddingNewService, setIsAddingNewService] = useState(false) + return (
{asset.services.map((service, index) => ( @@ -17,17 +20,26 @@ export default function EditServices({ accessDetails={asset.accessDetails[index]} /> ))} -
- -
+ {isAddingNewService ? ( + setIsAddingNewService(false)} + /> + ) : ( +
+ +
+ )}
) } diff --git a/src/components/Asset/Edit/FormAddService.tsx b/src/components/Asset/Edit/FormAddService.tsx new file mode 100644 index 000000000..5162fc8e3 --- /dev/null +++ b/src/components/Asset/Edit/FormAddService.tsx @@ -0,0 +1,136 @@ +import { ReactElement } from 'react' +import { Field, Form, useFormikContext } from 'formik' +import Input from '@shared/FormInput' +import FormActions from './FormActions' +import { getFieldContent } from '@utils/form' +import consumerParametersContent from '../../../../content/publish/consumerParameters.json' +import Accordion from '@components/@shared/Accordion' +import { AddServiceForm } from './_types' +import Button from '@components/@shared/atoms/Button' +import IconDownload from '@images/download.svg' +import IconCompute from '@images/compute.svg' + +export default function FormAddService({ + data, + onRemove +}: { + data: FormFieldContent[] + onRemove: () => void +}): ReactElement { + const { values, setFieldValue } = useFormikContext() + + const accessTypeOptionsTitles = getFieldContent('access', data).options + + const accessTypeOptions = [ + { + name: 'download', + value: accessTypeOptionsTitles[0].toLowerCase(), + title: 'Download', + icon: , + // BoxSelection component is not a Formik component + // so we need to handle checked state manually. + checked: values.access === accessTypeOptionsTitles[0].toLowerCase() + }, + { + name: accessTypeOptionsTitles[1].toLowerCase(), + value: accessTypeOptionsTitles[1].toLowerCase(), + title: accessTypeOptionsTitles[1], + icon: , + checked: values.access === accessTypeOptionsTitles[1].toLowerCase() + } + ] + + return ( + + + +
+ } + > +
+ + + + + + + + + + + + + + + + + + {values.usesConsumerParameters && ( + + )} + +
+
+ + ) +} diff --git a/src/components/Asset/Edit/_constants.ts b/src/components/Asset/Edit/_constants.ts index b55fc3cac..c1dbb7670 100644 --- a/src/components/Asset/Edit/_constants.ts +++ b/src/components/Asset/Edit/_constants.ts @@ -5,7 +5,12 @@ import { ServiceComputeOptions } from '@oceanprotocol/lib' import { parseConsumerParameters, secondsToString } from '@utils/ddo' -import { ComputeEditForm, MetadataEditForm, ServiceEditForm } from './_types' +import { + AddServiceForm, + ComputeEditForm, + MetadataEditForm, + ServiceEditForm +} from './_types' export function getInitialValues( metadata: Metadata, @@ -44,6 +49,29 @@ export function getInitialValues( } } +export const getNewServiceInitialValues = ( + accountId: string, + firstService: Service +): AddServiceForm => { + console.log('firs', firstService) + return { + name: 'New Service', + description: '', + access: 'access', + price: 1, + paymentCollector: accountId, + providerUrl: { + url: firstService.serviceEndpoint, + valid: false, + custom: false + }, + files: [{ url: '', type: 'hidden' }], + timeout: '1 day', + usesConsumerParameters: false, + consumerParameters: [] + } +} + export const getServiceInitialValues = ( service: Service, accessDetails: AccessDetails @@ -51,7 +79,7 @@ export const getServiceInitialValues = ( return { name: service.name, description: service.description, - price: accessDetails.price, + price: parseFloat(accessDetails.price), paymentCollector: accessDetails.paymentCollector, files: [{ url: '', type: 'hidden' }], timeout: secondsToString(service.timeout), diff --git a/src/components/Asset/Edit/_types.ts b/src/components/Asset/Edit/_types.ts index e9073c100..dcf987e50 100644 --- a/src/components/Asset/Edit/_types.ts +++ b/src/components/Asset/Edit/_types.ts @@ -34,6 +34,11 @@ export interface ServiceEditForm { consumerParameters: FormConsumerParameter[] } +export interface AddServiceForm extends ServiceEditForm { + access: 'access' | 'compute' + providerUrl: { url: string; valid: boolean; custom: boolean } +} + export interface ComputeEditForm { allowAllPublishedAlgorithms: boolean publisherTrustedAlgorithms: string[] From 99122753256d8fed121db693f25e81b26885faeb Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 4 Sep 2024 13:39:41 +0200 Subject: [PATCH 06/31] use dummySigner so user doesn't need to be connected to get Assets accessDatails --- src/@context/Asset.tsx | 17 ++++------------- src/@utils/accessDetailsAndPricing.ts | 10 +++++----- src/@utils/ocean/index.ts | 18 +++++++++++------- .../Compute/FormComputeDataset.tsx | 1 + src/components/Asset/AssetContent/MetaFull.tsx | 2 -- 5 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/@context/Asset.tsx b/src/@context/Asset.tsx index 960c6ccb4..81321cd87 100644 --- a/src/@context/Asset.tsx +++ b/src/@context/Asset.tsx @@ -18,7 +18,7 @@ import { useMarketMetadata } from './MarketMetadata' import { assetStateToString } from '@utils/assetState' import { isValidDid } from '@utils/ddo' import { useAddressConfig } from '@hooks/useAddressConfig' -import { useAccount, useNetwork, useProvider } from 'wagmi' +import { useAccount, useNetwork } from 'wagmi' export interface AssetProviderValue { isInPurgatory: boolean @@ -47,7 +47,6 @@ function AssetProvider({ const { appConfig } = useMarketMetadata() const { address: accountId } = useAccount() const { chain } = useNetwork() - const provider = useProvider() const { isDDOWhitelisted } = useAddressConfig() const [isInPurgatory, setIsInPurgatory] = useState(false) @@ -137,10 +136,8 @@ function AssetProvider({ const accessDetails = await Promise.all( asset.services.map((service: Service) => getAccessDetails( - asset.offchain?.stats.services.find( - (s) => s.serviceId === service.id - ), - provider + asset.chainId, + asset.offchain?.stats.services.find((s) => s.serviceId === service.id) ) ) ) @@ -150,13 +147,7 @@ function AssetProvider({ accessDetails })) LoggerInstance.log(`[asset] Got access details for ${did}`, accessDetails) - }, [ - asset?.chainId, - asset?.offchain?.stats.services, - asset?.services, - did, - provider - ]) + }, [asset?.chainId, asset?.offchain?.stats.services, asset?.services, did]) // ----------------------------------- // 1. Get and set asset based on passed DID diff --git a/src/@utils/accessDetailsAndPricing.ts b/src/@utils/accessDetailsAndPricing.ts index acfa203a1..94358929e 100644 --- a/src/@utils/accessDetailsAndPricing.ts +++ b/src/@utils/accessDetailsAndPricing.ts @@ -14,7 +14,7 @@ import { publisherMarketOrderFee, customProviderUrl } from '../../app.config' -import { ethers, Signer } from 'ethers' +import { Signer } from 'ethers' import { toast } from 'react-toastify' import { getPaymentCollector } from './ocean' @@ -115,12 +115,12 @@ export async function getOrderPriceAndFees( * @returns {Promise} */ export async function getAccessDetails( - serviceStat: ServiceStat | undefined, - provider: ethers.providers.Provider + chainId: number, + serviceStat: ServiceStat | undefined ): Promise { const paymentCollector = await getPaymentCollector( - serviceStat.datatokenAddress, - provider + chainId, + serviceStat.datatokenAddress ) const accessDetails: AccessDetails = { diff --git a/src/@utils/ocean/index.ts b/src/@utils/ocean/index.ts index 2dbb57470..9f5063aee 100644 --- a/src/@utils/ocean/index.ts +++ b/src/@utils/ocean/index.ts @@ -1,6 +1,7 @@ -import { ConfigHelper, Config } from '@oceanprotocol/lib' +import { ConfigHelper, Config, Datatoken } from '@oceanprotocol/lib' import { ethers } from 'ethers' import abiDatatoken from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC20TemplateEnterprise.sol/ERC20TemplateEnterprise.json' +import { getDummySigner } from '@utils/wallet' /** This function takes a Config object as an input and returns a new sanitized Config object @@ -57,15 +58,18 @@ export function getDevelopmentConfig(): Config { /** * getPaymentCollector - returns the current paymentCollector - * @param dtAddress datatoken address - * @param provider the ethers.js web3 provider + * @param chainId chain ID + * @param datatokenAddress datatoken address * @return {Promise} */ export async function getPaymentCollector( - dtAddress: string, - provider: ethers.providers.Provider + chainId: number, + datatokenAddress: string ): Promise { - const dtContract = new ethers.Contract(dtAddress, abiDatatoken.abi, provider) - const paymentCollector = await dtContract.getPaymentCollector() + const signer = await getDummySigner(chainId) + + const datatoken = new Datatoken(signer, chainId) + const paymentCollector = await datatoken.getPaymentCollector(datatokenAddress) + return paymentCollector } diff --git a/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx b/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx index 91137768d..1c1c196fc 100644 --- a/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx +++ b/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx @@ -152,6 +152,7 @@ export default function FormStartCompute({ const algoAccessDetails = await Promise.all( algorithmAsset.services.map((service) => getAccessDetails( + algorithmAsset.chainId, algorithmAsset.offchain?.stats.services.find( (s) => s.serviceId === service.id ) diff --git a/src/components/Asset/AssetContent/MetaFull.tsx b/src/components/Asset/AssetContent/MetaFull.tsx index 596c5a244..7b6ae3eb6 100644 --- a/src/components/Asset/AssetContent/MetaFull.tsx +++ b/src/components/Asset/AssetContent/MetaFull.tsx @@ -4,8 +4,6 @@ import styles from './MetaFull.module.css' import Publisher from '@shared/Publisher' import { useAsset } from '@context/Asset' import { Asset, LoggerInstance, Datatoken } from '@oceanprotocol/lib' -import { getPaymentCollector } from '@utils/ocean' -import { useProvider } from 'wagmi' import { getDummySigner } from '@utils/wallet' export default function MetaFull({ ddo }: { ddo: Asset }): ReactElement { From 4eac6f099f60638a9af2d5510c370f69befab641 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 4 Sep 2024 14:49:56 +0200 Subject: [PATCH 07/31] remove service related edits from metadata edits --- content/pages/editMetadata.json | 120 -------------- .../Asset/Edit/DebugEditMetadata.tsx | 16 +- src/components/Asset/Edit/EditMetadata.tsx | 155 +----------------- .../Asset/Edit/FormEditMetadata.tsx | 73 +-------- src/components/Asset/Edit/_constants.ts | 11 -- src/components/Asset/Edit/_types.ts | 8 - 6 files changed, 15 insertions(+), 368 deletions(-) diff --git a/content/pages/editMetadata.json b/content/pages/editMetadata.json index b39d57164..6538fd412 100644 --- a/content/pages/editMetadata.json +++ b/content/pages/editMetadata.json @@ -18,101 +18,6 @@ "rows": 10, "required": true }, - { - "name": "price", - "label": "New Price", - "type": "number", - "min": "1", - "placeholder": "0", - "help": "Enter a new price.", - "required": true - }, - { - "name": "files", - "label": "File", - "prominentHelp": false, - "type": "tabs", - "fields": [ - { - "value": "ipfs", - "title": "IPFS", - "label": "CID", - "placeholder": "e.g. bafkreidgvpkjawlxz6sffxzwgooowe5yt7i6wsyg236mfoks77nywkptdq", - "help": "This CID will be stored encrypted after publishing.", - "computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ", - "prominentHelp": true, - "type": "files", - "required": true - }, - { - "value": "arweave", - "title": "Arweave", - "label": "Transaction ID", - "placeholder": "e.g. DBRCL94j3QqdPaUtt4VWRen8rZfJZBb7Ey40iMpXfhtd", - "help": "This Transaction ID will be stored encrypted after publishing.", - "computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ", - "prominentHelp": true, - "type": "files", - "required": true - }, - { - "value": "url", - "title": "URL", - "label": "File", - "placeholder": "e.g. https://file.com/file.json", - "help": "This URL will be stored encrypted after publishing. **Please make sure that the endpoint is accessible over the internet and is not protected by a firewall or by credentials.**", - "computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ", - "prominentHelp": true, - "type": "files", - "required": true, - "innerFields": [ - { - "value": "headers", - "title": "Headers", - "label": "Headers", - "placeholder_value": "Authorization", - "help": "This HEADERS will be stored encrypted after publishing.", - "type": "headers", - "required": true - } - ] - }, - { - "value": "graphql", - "title": "Graphql", - "label": "URL", - "placeholder": "e.g. http://172.15.0.15:8000/subgraphs/name/oceanprotocol/ocean-subgraph", - "help": "This URL will be stored encrypted after publishing.", - "computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ", - "prominentHelp": true, - "type": "files", - "required": true, - "headers": true, - "innerFields": [ - { - "value": "headers", - "title": "Headers", - "label": "Headers", - "placeholder_value": "Authorization", - "help": "This HEADERS will be stored encrypted after publishing.", - "type": "headers", - "required": true - }, - { - "value": "query", - "title": "Query", - "label": "Query", - "placeholder": "query{\n nfts(\n orderBy: createdTimestamp,\n orderDirection:desc\n ){\n id\n symbol\n createdTimestamp\n }\n}", - "help": "This QUERY will be stored encrypted after publishing.", - "type": "codeeditor", - "required": true - } - ] - } - ], - "sortOptions": false, - "required": true - }, { "name": "links", "label": "Sample file", @@ -133,16 +38,6 @@ ], "required": false }, - - { - "name": "timeout", - "label": "Timeout", - "help": "Define how long buyers should be able to download the dataset again after the initial purchase.", - "type": "select", - "options": ["Forever", "1 day", "1 week", "1 month", "1 year"], - "sortOptions": false, - "required": true - }, { "name": "tags", "label": "New Tags", @@ -172,13 +67,6 @@ "help": "Enter an ETH address and click the ADD button to append to the list. If an ETH address is in the deny list, download or compute of this asset will be denied for that ETH address.", "type": "credentials" }, - { - "name": "paymentCollector", - "label": "Payment Collector Address", - "placeholder": "e.g. 0X123ABC...", - "help": "This address will receive the revenue from all sales. More info available in our [docs](https://docs.oceanprotocol.com/core-concepts/datanft-and-datatoken#revenue).", - "required": false - }, { "name": "assetState", "label": "Asset Status", @@ -193,14 +81,6 @@ "sortOptions": false, "required": false }, - { - "name": "usesServiceConsumerParameters", - "label": "User defined parameters", - "help": "User defined parameters are used to filter or query the published asset.", - "type": "checkbox", - "options": ["This asset uses user defined parameters"], - "required": false - }, { "name": "license", "label": "License", diff --git a/src/components/Asset/Edit/DebugEditMetadata.tsx b/src/components/Asset/Edit/DebugEditMetadata.tsx index be568eb25..5a12b2868 100644 --- a/src/components/Asset/Edit/DebugEditMetadata.tsx +++ b/src/components/Asset/Edit/DebugEditMetadata.tsx @@ -1,8 +1,8 @@ -import { Asset, Credentials, Metadata, Service } from '@oceanprotocol/lib' +import { Asset, Credentials, Metadata } from '@oceanprotocol/lib' import { ReactElement, useEffect, useState } from 'react' import DebugOutput from '@shared/DebugOutput' import { MetadataEditForm } from './_types' -import { mapTimeoutStringToSeconds, previewDebugPatch } from '@utils/ddo' +import { previewDebugPatch } from '@utils/ddo' import { sanitizeUrl } from '@utils/url' import { generateCredentials, @@ -38,16 +38,6 @@ export default function DebugEditMetadata({ : transformConsumerParameters(values.consumerParameters) } - const updatedService: Service = { - ...asset?.services[0], - timeout: mapTimeoutStringToSeconds(values.timeout) - } - if (values?.service?.consumerParameters) { - updatedService.consumerParameters = transformConsumerParameters( - values.service.consumerParameters - ) - } - const updatedCredentials: Credentials = generateCredentials( asset?.credentials, values?.allow, @@ -57,7 +47,6 @@ export default function DebugEditMetadata({ const updatedAsset: Asset = { ...asset, metadata: newMetadata, - services: [updatedService], credentials: updatedCredentials } @@ -65,6 +54,7 @@ export default function DebugEditMetadata({ delete (updatedAsset as AssetExtended).accessDetails delete (updatedAsset as AssetExtended).datatokens delete (updatedAsset as AssetExtended).stats + delete (updatedAsset as AssetExtended).offchain useEffect(() => { setValuePreview(previewDebugPatch(values, asset.chainId)) diff --git a/src/components/Asset/Edit/EditMetadata.tsx b/src/components/Asset/Edit/EditMetadata.tsx index 274f86bd7..bb51bf55a 100644 --- a/src/components/Asset/Edit/EditMetadata.tsx +++ b/src/components/Asset/Edit/EditMetadata.tsx @@ -1,38 +1,22 @@ -import { ReactElement, useState, useEffect } from 'react' +import { ReactElement, useState } from 'react' import { Formik } from 'formik' -import { - LoggerInstance, - FixedRateExchange, - Asset, - Datatoken, - Nft, - Metadata, - Service -} from '@oceanprotocol/lib' +import { LoggerInstance, Asset, Nft, Metadata } from '@oceanprotocol/lib' import { validationSchema } from './_validation' import { getInitialValues } from './_constants' import { MetadataEditForm } from './_types' import { useUserPreferences } from '@context/UserPreferences' import Web3Feedback from '@shared/Web3Feedback' import FormEditMetadata from './FormEditMetadata' -import { mapTimeoutStringToSeconds, normalizeFile } from '@utils/ddo' import styles from './index.module.css' import content from '../../../../content/pages/editMetadata.json' import { useAbortController } from '@hooks/useAbortController' import DebugEditMetadata from './DebugEditMetadata' -import { getOceanConfig, getPaymentCollector } from '@utils/ocean' import EditFeedback from './EditFeedback' import { useAsset } from '@context/Asset' -import { - decodeTokenURI, - setNftMetadata, - setNFTMetadataAndTokenURI -} from '@utils/nft' +import { setNftMetadata } from '@utils/nft' import { sanitizeUrl } from '@utils/url' -import { getEncryptedFiles } from '@utils/provider' import { assetStateToNumber } from '@utils/assetState' -import { setMinterToPublisher, setMinterToDispenser } from '@utils/dispenser' -import { useAccount, useProvider, useNetwork, useSigner } from 'wagmi' +import { useAccount, useSigner } from 'wagmi' import { transformConsumerParameters, generateCredentials @@ -46,62 +30,18 @@ export default function Edit({ const { debug } = useUserPreferences() const { fetchAsset, isAssetNetwork, assetState } = useAsset() const { address: accountId } = useAccount() - const { chain } = useNetwork() - const provider = useProvider() const { data: signer } = useSigner() const newAbortController = useAbortController() const [success, setSuccess] = useState() - const [paymentCollector, setPaymentCollector] = useState() const [error, setError] = useState() - const isComputeType = asset?.services[0]?.type === 'compute' const hasFeedback = error || success - useEffect(() => { - if (!asset || !provider) return - - async function getInitialPaymentCollector() { - try { - const paymentCollector = await getPaymentCollector( - asset.datatokens[0].address, - provider - ) - setPaymentCollector(paymentCollector) - } catch (error) { - LoggerInstance.error( - '[EditMetadata: getInitialPaymentCollector]', - error - ) - } - } - getInitialPaymentCollector() - }, [asset, provider]) - - async function updateFixedPrice(newPrice: string) { - const config = getOceanConfig(asset.chainId) - - const fixedRateInstance = new FixedRateExchange( - config.fixedRateExchangeAddress, - signer - ) - - const setPriceResp = await fixedRateInstance.setRate( - asset.accessDetails.addressOrId, - newPrice.toString() - ) - LoggerInstance.log('[edit] setFixedRate result', setPriceResp) - if (!setPriceResp) { - setError(content.form.error) - LoggerInstance.error(content.form.error) - } - } - async function handleSubmit( values: Partial, resetForm: () => void ) { try { - let updatedFiles = asset.services[0].files const linksTransformed = values.links?.length && values.links[0].valid && [sanitizeUrl(values.links[0].url)] const updatedMetadata: Metadata = { @@ -124,46 +64,6 @@ export default function Edit({ : transformConsumerParameters(values.consumerParameters) } - asset?.accessDetails?.type === 'fixed' && - values.price !== asset.accessDetails.price && - (await updateFixedPrice(values.price)) - - if (values.paymentCollector !== paymentCollector) { - const datatoken = new Datatoken(signer) - await datatoken.setPaymentCollector( - asset?.datatokens[0].address, - accountId, - values.paymentCollector - ) - } - - if (values.files[0]?.url) { - const file = { - nftAddress: asset.nftAddress, - datatokenAddress: asset.services[0].datatokenAddress, - files: [ - normalizeFile(values.files[0].type, values.files[0], chain?.id) - ] - } - - const filesEncrypted = await getEncryptedFiles( - file, - asset.chainId, - asset.services[0].serviceEndpoint - ) - updatedFiles = filesEncrypted - } - const updatedService: Service = { - ...asset.services[0], - timeout: mapTimeoutStringToSeconds(values.timeout), - files: updatedFiles - } - if (values?.service?.consumerParameters) { - updatedService.consumerParameters = transformConsumerParameters( - values.service.consumerParameters - ) - } - const updatedCredentials = generateCredentials( asset?.credentials, values?.allow, @@ -175,43 +75,22 @@ export default function Edit({ ...(asset as Asset), version: '4.1.0', metadata: updatedMetadata, - services: [updatedService], credentials: updatedCredentials } - if ( - asset?.accessDetails?.type === 'free' && - asset?.accessDetails?.isPurchasable - ) { - const tx = await setMinterToPublisher( - signer, - asset?.accessDetails?.datatoken?.address, - accountId, - setError - ) - if (!tx) return - } - // delete custom helper properties injected in the market so we don't write them on chain delete (updatedAsset as AssetExtended).accessDetails delete (updatedAsset as AssetExtended).datatokens delete (updatedAsset as AssetExtended).stats - // TODO: revert to setMetadata function - const setMetadataTx = await setNFTMetadataAndTokenURI( + delete (updatedAsset as AssetExtended).offchain + + const setMetadataTx = await setNftMetadata( updatedAsset, accountId, signer, - decodeTokenURI(asset.nft.tokenURI), newAbortController() ) - // const setMetadataTx = await setNftMetadata( - // updatedAsset, - // accountId, - // signer, - // newAbortController() - // ) - console.log({ state: values.assetState, assetState }) if (values.assetState !== assetState) { const nft = new Nft(signer) @@ -228,17 +107,8 @@ export default function Edit({ setError(content.form.error) LoggerInstance.error(content.form.error) return - } else { - if (asset.accessDetails.type === 'free') { - const tx = await setMinterToDispenser( - signer, - asset?.accessDetails?.datatoken?.address, - accountId, - setError - ) - if (!tx) return - } } + // Edit succeeded setSuccess(content.form.success) resetForm() @@ -253,10 +123,7 @@ export default function Edit({ enableReinitialize initialValues={getInitialValues( asset?.metadata, - asset?.services[0], asset?.credentials, - asset?.accessDetails?.price || '0', - paymentCollector, assetState )} validationSchema={validationSchema} @@ -284,11 +151,7 @@ export default function Edit({ /> ) : ( <> - + -1) { - return true - } - return false -} export default function FormEditMetadata({ - data, - showPrice, - isComputeDataset + data }: { data: FormFieldContent[] - showPrice: boolean - isComputeDataset: boolean }): ReactElement { const { asset } = useAsset() const { values, setFieldValue } = useFormikContext() - // This component is handled by Formik so it's not rendered like a "normal" react component, - // so handleTimeoutCustomOption is called only once. - // https://github.com/oceanprotocol/market/pull/324#discussion_r561132310 - // if (data && values) handleTimeoutCustomOption(data, values) - - const timeoutOptionsArray = data.filter( - (field) => field.name === 'timeout' - )[0].options as string[] - - if (isComputeDataset && timeoutOptionsArray.includes('Forever')) { - const foreverOptionIndex = timeoutOptionsArray.indexOf('Forever') - timeoutOptionsArray.splice(foreverOptionIndex, 1) - } else if (!isComputeDataset && !timeoutOptionsArray.includes('Forever')) { - timeoutOptionsArray.push('Forever') - } - useEffect(() => { const providerUrl = values?.services ? values?.services[0].providerUrl.url @@ -90,20 +59,6 @@ export default function FormEditMetadata({ name="description" /> - {showPrice && ( - - )} - - - - + -
-

Service

- - {(values as unknown as MetadataEditForm).service - .usesConsumerParameters && ( - - )} -
+ ) diff --git a/src/components/Asset/Edit/_constants.ts b/src/components/Asset/Edit/_constants.ts index c1dbb7670..56fa70cfd 100644 --- a/src/components/Asset/Edit/_constants.ts +++ b/src/components/Asset/Edit/_constants.ts @@ -14,26 +14,19 @@ import { export function getInitialValues( metadata: Metadata, - service: Service, credentials: Credentials, - price: string, - paymentCollector: string, assetState: string ): Partial { return { name: metadata?.name, description: metadata?.description, - price, links: [{ url: '', type: 'url' }], - files: [{ url: '', type: 'hidden' }], - timeout: secondsToString(service?.timeout), author: metadata?.author, tags: metadata?.tags, usesConsumerParameters: metadata?.algorithm?.consumerParameters?.length > 0, consumerParameters: parseConsumerParameters( metadata?.algorithm?.consumerParameters ), - paymentCollector, allow: credentials?.allow?.find((credential) => credential.type === 'address') ?.values || [], @@ -41,10 +34,6 @@ export function getInitialValues( credentials?.deny?.find((credential) => credential.type === 'address') ?.values || [], assetState, - service: { - usesConsumerParameters: service?.consumerParameters?.length > 0, - consumerParameters: parseConsumerParameters(service?.consumerParameters) - }, license: metadata?.license } } diff --git a/src/components/Asset/Edit/_types.ts b/src/components/Asset/Edit/_types.ts index dcf987e50..7fb6361f0 100644 --- a/src/components/Asset/Edit/_types.ts +++ b/src/components/Asset/Edit/_types.ts @@ -4,10 +4,6 @@ import { FileInfo } from '@oceanprotocol/lib' export interface MetadataEditForm { name: string description: string - timeout: string - paymentCollector: string - price?: string - files: FileInfo[] links?: FileInfo[] author?: string tags?: string[] @@ -16,10 +12,6 @@ export interface MetadataEditForm { allow?: string[] deny?: string[] assetState?: string - service?: { - usesConsumerParameters?: boolean - consumerParameters?: FormConsumerParameter[] - } license?: string } From 1a28fd65402efb7483fde4ae44b1630b4182fc71 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 4 Sep 2024 14:52:29 +0200 Subject: [PATCH 08/31] remove timeout field --- src/components/Asset/Edit/FormEditMetadata.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/components/Asset/Edit/FormEditMetadata.tsx b/src/components/Asset/Edit/FormEditMetadata.tsx index ff419afc7..8e5e17667 100644 --- a/src/components/Asset/Edit/FormEditMetadata.tsx +++ b/src/components/Asset/Edit/FormEditMetadata.tsx @@ -65,12 +65,6 @@ export default function FormEditMetadata({ name="links" /> - - {asset.metadata.type === 'algorithm' && ( From 95371b8aa1a169d618f7992b045f524359d0e60e Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Fri, 6 Sep 2024 14:05:13 +0200 Subject: [PATCH 09/31] get price details in accessDetails from blockchain rather than offchain record which is not ready yet --- src/@context/Asset.tsx | 7 +- src/@utils/accessDetailsAndPricing.ts | 96 +++++++++---------- src/@utils/ocean/index.ts | 23 +---- .../Compute/FormComputeDataset.tsx | 7 +- 4 files changed, 50 insertions(+), 83 deletions(-) diff --git a/src/@context/Asset.tsx b/src/@context/Asset.tsx index 81321cd87..53f8e29cc 100644 --- a/src/@context/Asset.tsx +++ b/src/@context/Asset.tsx @@ -135,10 +135,7 @@ function AssetProvider({ const accessDetails = await Promise.all( asset.services.map((service: Service) => - getAccessDetails( - asset.chainId, - asset.offchain?.stats.services.find((s) => s.serviceId === service.id) - ) + getAccessDetails(asset.chainId, service) ) ) @@ -147,7 +144,7 @@ function AssetProvider({ accessDetails })) LoggerInstance.log(`[asset] Got access details for ${did}`, accessDetails) - }, [asset?.chainId, asset?.offchain?.stats.services, asset?.services, did]) + }, [asset?.chainId, asset?.services, did]) // ----------------------------------- // 1. Get and set asset based on passed DID diff --git a/src/@utils/accessDetailsAndPricing.ts b/src/@utils/accessDetailsAndPricing.ts index 94358929e..333e74861 100644 --- a/src/@utils/accessDetailsAndPricing.ts +++ b/src/@utils/accessDetailsAndPricing.ts @@ -1,5 +1,7 @@ import { AssetPrice, + Datatoken, + FixedRateExchange, getErrorMessage, LoggerInstance, ProviderFees, @@ -16,7 +18,7 @@ import { } from '../../app.config' import { Signer } from 'ethers' import { toast } from 'react-toastify' -import { getPaymentCollector } from './ocean' +import { getDummySigner } from './wallet' /** * This will be used to get price including fees before ordering @@ -109,24 +111,20 @@ export async function getOrderPriceAndFees( /** * @param {number} chainId - * @param {string} datatokenAddress - * @param {number} timeout timout of the service, this is needed to return order details - * @param {string} account account that wants to buy, is needed to return order details + * @param {Service} service service of which you want access details to * @returns {Promise} */ export async function getAccessDetails( chainId: number, - serviceStat: ServiceStat | undefined + service: Service ): Promise { - const paymentCollector = await getPaymentCollector( - chainId, - serviceStat.datatokenAddress - ) + const signer = await getDummySigner(chainId) + const datatoken = new Datatoken(signer, chainId) + const { datatokenAddress } = service const accessDetails: AccessDetails = { type: 'NOT_SUPPORTED', price: '0', - templateId: 0, addressOrId: '', baseToken: { address: '', @@ -135,56 +133,54 @@ export async function getAccessDetails( decimals: 0 }, datatoken: { - address: '', - name: '', - symbol: '', + address: datatokenAddress, + name: await datatoken.getName(datatokenAddress), + symbol: await datatoken.getSymbol(datatokenAddress), decimals: 0 }, + paymentCollector: await datatoken.getPaymentCollector(datatokenAddress), + // TODO these 5 records + templateId: 1, isOwned: false, - validOrderTx: '', - isPurchasable: false, - publisherMarketOrderFee: '0', - paymentCollector - } - - if (serviceStat === undefined || serviceStat.prices.length === 0) { - return accessDetails + validOrderTx: '', // should be possible to get from ocean-node - orders collection in typesense + isPurchasable: true, + publisherMarketOrderFee: '0' } - const tokenPrice = serviceStat.prices[0] // support only 1 price for now - - if (tokenPrice.type === 'dispenser') { - accessDetails.type = 'free' - accessDetails.addressOrId = tokenPrice.contract - accessDetails.price = '0' - } else if (tokenPrice.type === 'fixedrate') { - accessDetails.type = 'fixed' - accessDetails.addressOrId = tokenPrice.exchangeId - accessDetails.price = tokenPrice.price - accessDetails.baseToken = { - address: tokenPrice.token.address, - name: tokenPrice.token.name, - symbol: tokenPrice.token.symbol, - decimals: tokenPrice.token.decimals + // if there is at least 1 dispenser => service is free and use first dispenser + const dispensers = await datatoken.getDispensers(datatokenAddress) + if (dispensers.length > 0) { + return { + ...accessDetails, + type: 'free', + addressOrId: dispensers[0], + price: '0' } - } else { - // unsupported type - return accessDetails } - accessDetails.datatoken = { - address: serviceStat.datatokenAddress, - name: serviceStat.name, - symbol: serviceStat.symbol + // if there is 0 dispensers and at least 1 fixed rate => use first fixed rate to get the price details + const fixedRates = await datatoken.getFixedRates(datatokenAddress) + if (fixedRates.length > 0) { + const freAddress = fixedRates[0].contractAddress + const exchangeId = fixedRates[0].id + const fre = new FixedRateExchange(freAddress, signer) + const exchange = await fre.getExchange(exchangeId) + + return { + ...accessDetails, + type: 'fixed', + addressOrId: exchangeId, + price: exchange.fixedRate, + baseToken: { + address: exchange.baseToken, + name: await datatoken.getName(exchange.baseToken), // reuse the datatoken instance since it is ERC20 + symbol: await datatoken.getSymbol(exchange.baseToken), + decimals: parseInt(exchange.btDecimals) + } + } } - // TODO - accessDetails.templateId = 1 - accessDetails.isPurchasable = true - accessDetails.isOwned = false - accessDetails.validOrderTx = '' // should be possible to get from ocean-node - orders collection in typesense - accessDetails.publisherMarketOrderFee = '0' - + // no dispensers and no fixed rates => service doesn't have price set up return accessDetails } diff --git a/src/@utils/ocean/index.ts b/src/@utils/ocean/index.ts index 9f5063aee..2fc8292a9 100644 --- a/src/@utils/ocean/index.ts +++ b/src/@utils/ocean/index.ts @@ -1,7 +1,4 @@ -import { ConfigHelper, Config, Datatoken } from '@oceanprotocol/lib' -import { ethers } from 'ethers' -import abiDatatoken from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC20TemplateEnterprise.sol/ERC20TemplateEnterprise.json' -import { getDummySigner } from '@utils/wallet' +import { ConfigHelper, Config } from '@oceanprotocol/lib' /** This function takes a Config object as an input and returns a new sanitized Config object @@ -55,21 +52,3 @@ export function getDevelopmentConfig(): Config { subgraphUri: 'https://v4.subgraph.sepolia.oceanprotocol.com' } as Config } - -/** - * getPaymentCollector - returns the current paymentCollector - * @param chainId chain ID - * @param datatokenAddress datatoken address - * @return {Promise} - */ -export async function getPaymentCollector( - chainId: number, - datatokenAddress: string -): Promise { - const signer = await getDummySigner(chainId) - - const datatoken = new Datatoken(signer, chainId) - const paymentCollector = await datatoken.getPaymentCollector(datatokenAddress) - - return paymentCollector -} diff --git a/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx b/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx index 1c1c196fc..102010e88 100644 --- a/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx +++ b/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx @@ -151,12 +151,7 @@ export default function FormStartCompute({ const algoAccessDetails = await Promise.all( algorithmAsset.services.map((service) => - getAccessDetails( - algorithmAsset.chainId, - algorithmAsset.offchain?.stats.services.find( - (s) => s.serviceId === service.id - ) - ) + getAccessDetails(algorithmAsset.chainId, service) ) ) From d3ff62191b1377e12286fc248a0deb2dad67d517 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Fri, 6 Sep 2024 14:08:10 +0200 Subject: [PATCH 10/31] fix undefined accessDetails --- src/components/Asset/Edit/EditService.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Asset/Edit/EditService.tsx b/src/components/Asset/Edit/EditService.tsx index 9566ef169..56b248562 100644 --- a/src/components/Asset/Edit/EditService.tsx +++ b/src/components/Asset/Edit/EditService.tsx @@ -28,7 +28,7 @@ export default function EditService({ }: { asset: AssetExtended service: Service - accessDetails: AccessDetails + accessDetails: AccessDetails | undefined }): ReactElement { const { fetchAsset, isAssetNetwork } = useAsset() const { address: accountId } = useAccount() @@ -144,6 +144,8 @@ export default function EditService({ } } + if (!accessDetails) return null + return ( Date: Fri, 6 Sep 2024 14:52:08 +0200 Subject: [PATCH 11/31] add some margin --- src/components/Asset/Edit/FormEditService.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Asset/Edit/FormEditService.tsx b/src/components/Asset/Edit/FormEditService.tsx index 91a549e12..6fdecb842 100644 --- a/src/components/Asset/Edit/FormEditService.tsx +++ b/src/components/Asset/Edit/FormEditService.tsx @@ -20,7 +20,7 @@ export default function FormEditService({ const { values, setFieldValue } = useFormikContext() return ( -
+
Date: Fri, 6 Sep 2024 18:12:06 +0200 Subject: [PATCH 12/31] unify fields in edit and add services --- content/pages/editService.json | 4 +- src/components/@shared/FormInput/index.tsx | 7 +++ src/components/Asset/Edit/AddService.tsx | 11 ++-- src/components/Asset/Edit/EditService.tsx | 5 +- src/components/Asset/Edit/FormAddService.tsx | 18 +++--- src/components/Asset/Edit/FormEditService.tsx | 57 ++++++++++++++++--- src/components/Asset/Edit/_constants.ts | 25 +++++--- src/components/Asset/Edit/_types.ts | 10 ++-- 8 files changed, 98 insertions(+), 39 deletions(-) diff --git a/content/pages/editService.json b/content/pages/editService.json index ea4e8ab0b..1c0dba0cf 100644 --- a/content/pages/editService.json +++ b/content/pages/editService.json @@ -23,10 +23,10 @@ "label": "Access Type", "help": "Choose how you want your files to be accessible for the specified price.", "type": "boxSelection", - "options": ["Access", "Compute"], + "options": ["Download", "Compute"], "required": true, "disclaimer": "Please do not provide downloadable personal data without the consent of the data subjects.", - "disclaimerValues": ["Download"] + "disclaimerValues": ["access"] }, { "name": "price", diff --git a/src/components/@shared/FormInput/index.tsx b/src/components/@shared/FormInput/index.tsx index 02321232d..2446aa5c8 100644 --- a/src/components/@shared/FormInput/index.tsx +++ b/src/components/@shared/FormInput/index.tsx @@ -109,6 +109,7 @@ export default function Input(props: Partial): ReactElement { } = props const isFormikField = typeof field !== 'undefined' + console.log('label', label, isFormikField) // TODO: this feels hacky as it assumes nested `values` store. But we can't use the // `useField()` hook in here to get `meta.error` so we have to match against form?.errors? // handling flat and nested data at same time. @@ -124,6 +125,12 @@ export default function Input(props: Partial): ReactElement { useEffect(() => { if (!isFormikField) return + if (label === 'Access Type') { + console.log('isF', isFormikField) + console.log('dis', disclaimer) + console.log('disV', disclaimerValues) + } + if (disclaimer && disclaimerValues) { setDisclaimerVisible( disclaimerValues.includes( diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx index bb2efb6ed..2cca860e8 100644 --- a/src/components/Asset/Edit/AddService.tsx +++ b/src/components/Asset/Edit/AddService.tsx @@ -10,7 +10,7 @@ import { getHash } from '@oceanprotocol/lib' import { getNewServiceInitialValues } from './_constants' -import { AddServiceForm } from './_types' +import { ServiceEditForm } from './_types' import Web3Feedback from '@shared/Web3Feedback' import { mapTimeoutStringToSeconds, normalizeFile } from '@utils/ddo' import content from '../../../../content/pages/editService.json' @@ -43,7 +43,7 @@ export default function AddService({ const hasFeedback = error || success // add new service - async function handleSubmit(values: AddServiceForm, resetForm: () => void) { + async function handleSubmit(values: ServiceEditForm, resetForm: () => void) { console.log('values', values) try { if (!isAssetNetwork) { @@ -153,10 +153,9 @@ export default function AddService({ datatokenAddress, serviceEndpoint: values.providerUrl.url, timeout: mapTimeoutStringToSeconds(values.timeout), - // TODO - // ...(access === 'compute' && { - // compute: values.services[0].computeOptions - // }), + ...(values.access === 'compute' && { + compute: values.compute + }), consumerParameters: transformConsumerParameters( values.consumerParameters ) diff --git a/src/components/Asset/Edit/EditService.tsx b/src/components/Asset/Edit/EditService.tsx index 56b248562..b1eff0303 100644 --- a/src/components/Asset/Edit/EditService.tsx +++ b/src/components/Asset/Edit/EditService.tsx @@ -104,7 +104,10 @@ export default function EditService({ name: values.name, description: values.description, timeout: mapTimeoutStringToSeconds(values.timeout), - files: updatedFiles // TODO: check if this works + files: updatedFiles, // TODO: check if this works + ...(values.access === 'compute' && { + compute: values.compute + }) } if (values.consumerParameters) { updatedService.consumerParameters = transformConsumerParameters( diff --git a/src/components/Asset/Edit/FormAddService.tsx b/src/components/Asset/Edit/FormAddService.tsx index 5162fc8e3..43791e47e 100644 --- a/src/components/Asset/Edit/FormAddService.tsx +++ b/src/components/Asset/Edit/FormAddService.tsx @@ -5,7 +5,7 @@ import FormActions from './FormActions' import { getFieldContent } from '@utils/form' import consumerParametersContent from '../../../../content/publish/consumerParameters.json' import Accordion from '@components/@shared/Accordion' -import { AddServiceForm } from './_types' +import { ServiceEditForm } from './_types' import Button from '@components/@shared/atoms/Button' import IconDownload from '@images/download.svg' import IconCompute from '@images/compute.svg' @@ -17,26 +17,26 @@ export default function FormAddService({ data: FormFieldContent[] onRemove: () => void }): ReactElement { - const { values, setFieldValue } = useFormikContext() + const { values, setFieldValue } = useFormikContext() const accessTypeOptionsTitles = getFieldContent('access', data).options const accessTypeOptions = [ { - name: 'download', - value: accessTypeOptionsTitles[0].toLowerCase(), - title: 'Download', + name: 'access-download', + value: 'access', + title: accessTypeOptionsTitles[0], icon: , // BoxSelection component is not a Formik component // so we need to handle checked state manually. - checked: values.access === accessTypeOptionsTitles[0].toLowerCase() + checked: values.access === 'access' }, { - name: accessTypeOptionsTitles[1].toLowerCase(), - value: accessTypeOptionsTitles[1].toLowerCase(), + name: 'access-compute', + value: 'compute', title: accessTypeOptionsTitles[1], icon: , - checked: values.access === accessTypeOptionsTitles[1].toLowerCase() + checked: values.access === 'compute' } ] diff --git a/src/components/Asset/Edit/FormEditService.tsx b/src/components/Asset/Edit/FormEditService.tsx index 6fdecb842..87995ca36 100644 --- a/src/components/Asset/Edit/FormEditService.tsx +++ b/src/components/Asset/Edit/FormEditService.tsx @@ -7,6 +7,8 @@ import consumerParametersContent from '../../../../content/publish/consumerParam import Accordion from '@components/@shared/Accordion' import { Service } from '@oceanprotocol/lib' import { ServiceEditForm } from './_types' +import IconDownload from '@images/download.svg' +import IconCompute from '@images/compute.svg' export default function FormEditService({ data, @@ -17,8 +19,34 @@ export default function FormEditService({ service: Service accessDetails: AccessDetails }): ReactElement { + const formUniqueId = service.id // because BoxSelection component is not a Formik component const { values, setFieldValue } = useFormikContext() + const accessTypeOptionsTitles = getFieldContent('access', data).options + + const accessTypeOptions = [ + { + name: `access-${formUniqueId}-download`, + value: 'access', + title: accessTypeOptionsTitles[0], + icon: , + // BoxSelection component is not a Formik component + // so we need to handle checked state manually. + checked: values.access === 'access' + }, + { + name: `access-${formUniqueId}-compute`, + value: 'compute', + title: accessTypeOptionsTitles[1], + icon: , + checked: values.access === 'compute' + } + ] + + const handleAccessChange = (value: string) => { + setFieldValue('access', value) + } + return ( @@ -35,13 +63,21 @@ export default function FormEditService({ name="description" /> - {accessDetails.type === 'fixed' && ( - - )} + + + + + { - console.log('firs', firstService) +): ServiceEditForm => { return { name: 'New Service', description: '', access: 'access', + compute: defaultServiceComputeOptions, price: 1, paymentCollector: accountId, providerUrl: { @@ -68,8 +70,15 @@ export const getServiceInitialValues = ( return { name: service.name, description: service.description, + access: service.type as 'access' | 'compute', + compute: service.compute || defaultServiceComputeOptions, price: parseFloat(accessDetails.price), paymentCollector: accessDetails.paymentCollector, + providerUrl: { + url: service.serviceEndpoint, + valid: true, + custom: false + }, files: [{ url: '', type: 'hidden' }], timeout: secondsToString(service.timeout), usesConsumerParameters: service.consumerParameters?.length > 0, diff --git a/src/components/Asset/Edit/_types.ts b/src/components/Asset/Edit/_types.ts index 7fb6361f0..e9a948d7c 100644 --- a/src/components/Asset/Edit/_types.ts +++ b/src/components/Asset/Edit/_types.ts @@ -1,5 +1,5 @@ import { FormConsumerParameter } from '@components/Publish/_types' -import { FileInfo } from '@oceanprotocol/lib' +import { FileInfo, ServiceComputeOptions } from '@oceanprotocol/lib' export interface MetadataEditForm { name: string @@ -18,6 +18,9 @@ export interface MetadataEditForm { export interface ServiceEditForm { name: string description: string + access: 'access' | 'compute' + compute: ServiceComputeOptions + providerUrl: { url: string; valid: boolean; custom: boolean } price: number paymentCollector: string files: FileInfo[] @@ -26,11 +29,6 @@ export interface ServiceEditForm { consumerParameters: FormConsumerParameter[] } -export interface AddServiceForm extends ServiceEditForm { - access: 'access' | 'compute' - providerUrl: { url: string; valid: boolean; custom: boolean } -} - export interface ComputeEditForm { allowAllPublishedAlgorithms: boolean publisherTrustedAlgorithms: string[] From ba98c07150a8b13d77f4649a22556cda625de713 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Fri, 6 Sep 2024 18:27:29 +0200 Subject: [PATCH 13/31] remove weird functions --- src/@utils/dispenser.ts | 43 ------------------- .../Asset/Edit/EditComputeDataset.tsx | 23 ---------- 2 files changed, 66 deletions(-) delete mode 100644 src/@utils/dispenser.ts diff --git a/src/@utils/dispenser.ts b/src/@utils/dispenser.ts deleted file mode 100644 index e4eec5056..000000000 --- a/src/@utils/dispenser.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { LoggerInstance, Datatoken } from '@oceanprotocol/lib' -import { Signer, ethers } from 'ethers' - -export async function setMinterToPublisher( - signer: Signer, - datatokenAddress: string, - accountId: string, - setError: (msg: string) => void -): Promise { - const datatokenInstance = new Datatoken(signer) - - 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( - signer: Signer, - datatokenAddress: string, - accountId: string, - setError: (msg: string) => void -): Promise { - const datatokenInstance = new Datatoken(signer) - - 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/components/Asset/Edit/EditComputeDataset.tsx b/src/components/Asset/Edit/EditComputeDataset.tsx index 6ae662901..878a63757 100644 --- a/src/components/Asset/Edit/EditComputeDataset.tsx +++ b/src/components/Asset/Edit/EditComputeDataset.tsx @@ -15,7 +15,6 @@ import { getComputeSettingsInitialValues } from './_constants' import { computeSettingsValidationSchema } from './_validation' import content from '../../../../content/pages/editComputeDataset.json' import { getServiceByName } from '@utils/ddo' -import { setMinterToPublisher, setMinterToDispenser } from '@utils/dispenser' import { transformComputeFormToServiceComputeOptions } from '@utils/compute' import { useAbortController } from '@hooks/useAbortController' import DebugEditCompute from './DebugEditCompute' @@ -47,18 +46,6 @@ export default function EditComputeDataset({ async function handleSubmit(values: ComputeEditForm, resetForm: () => void) { try { - if ( - asset?.accessDetails?.type === 'free' && - asset?.accessDetails?.isPurchasable - ) { - const tx = await setMinterToPublisher( - signer, - asset?.accessDetails?.datatoken?.address, - accountId, - setError - ) - if (!tx) return - } const newComputeSettings: ServiceComputeOptions = await transformComputeFormToServiceComputeOptions( values, @@ -108,16 +95,6 @@ export default function EditComputeDataset({ setError(content.form.error) LoggerInstance.error(content.form.error) return - } else { - if (asset.accessDetails.type === 'free') { - const tx = await setMinterToDispenser( - signer, - asset?.accessDetails?.datatoken?.address, - accountId, - setError - ) - if (!tx) return - } } // Edit succeeded setSuccess(content.form.success) From 8767e7615225d847b30edba60e51eea15d9733d7 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Fri, 6 Sep 2024 18:35:55 +0200 Subject: [PATCH 14/31] remove logs --- src/components/@shared/FormInput/index.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/components/@shared/FormInput/index.tsx b/src/components/@shared/FormInput/index.tsx index 2446aa5c8..02321232d 100644 --- a/src/components/@shared/FormInput/index.tsx +++ b/src/components/@shared/FormInput/index.tsx @@ -109,7 +109,6 @@ export default function Input(props: Partial): ReactElement { } = props const isFormikField = typeof field !== 'undefined' - console.log('label', label, isFormikField) // TODO: this feels hacky as it assumes nested `values` store. But we can't use the // `useField()` hook in here to get `meta.error` so we have to match against form?.errors? // handling flat and nested data at same time. @@ -125,12 +124,6 @@ export default function Input(props: Partial): ReactElement { useEffect(() => { if (!isFormikField) return - if (label === 'Access Type') { - console.log('isF', isFormikField) - console.log('dis', disclaimer) - console.log('disV', disclaimerValues) - } - if (disclaimer && disclaimerValues) { setDisclaimerVisible( disclaimerValues.includes( From 30143cd399c9d0fe5cf3541ab196d23978d91ec9 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Mon, 9 Sep 2024 16:56:23 +0200 Subject: [PATCH 15/31] change the UI from using Accordion to using Cards --- .../@shared/Accordion/index.module.css | 2 +- src/components/Asset/Edit/AddService.tsx | 6 +- .../Asset/Edit/AddServiceCard.module.css | 23 +++ src/components/Asset/Edit/AddServiceCard.tsx | 14 ++ src/components/Asset/Edit/EditServices.tsx | 84 +++++++---- src/components/Asset/Edit/FormAddService.tsx | 141 +++++++----------- src/components/Asset/Edit/FormEditService.tsx | 121 +++++++-------- 7 files changed, 208 insertions(+), 183 deletions(-) create mode 100644 src/components/Asset/Edit/AddServiceCard.module.css create mode 100644 src/components/Asset/Edit/AddServiceCard.tsx diff --git a/src/components/@shared/Accordion/index.module.css b/src/components/@shared/Accordion/index.module.css index 67ca5123e..ae9e0c556 100644 --- a/src/components/@shared/Accordion/index.module.css +++ b/src/components/@shared/Accordion/index.module.css @@ -51,7 +51,7 @@ } .open .content { - max-height: 1000rem; + max-height: 35rem; visibility: visible; overflow-y: scroll; transition: 0.3s max-height; diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx index 2cca860e8..f807b9d28 100644 --- a/src/components/Asset/Edit/AddService.tsx +++ b/src/components/Asset/Edit/AddService.tsx @@ -26,11 +26,9 @@ import { ethers } from 'ethers' import FormAddService from './FormAddService' export default function AddService({ - asset, - onRemove + asset }: { asset: AssetExtended - onRemove: () => void }): ReactElement { const { fetchAsset, isAssetNetwork } = useAsset() const { address: accountId } = useAccount() @@ -221,7 +219,7 @@ export default function AddService({ /> ) : ( <> - + void +}): ReactElement { + return ( +
+ Add a new service +
+ ) +} diff --git a/src/components/Asset/Edit/EditServices.tsx b/src/components/Asset/Edit/EditServices.tsx index 036df7d72..9ae967f82 100644 --- a/src/components/Asset/Edit/EditServices.tsx +++ b/src/components/Asset/Edit/EditServices.tsx @@ -2,43 +2,71 @@ import { ReactElement, useState } from 'react' import EditService from './EditService' import Button from '@components/@shared/atoms/Button' import AddService from './AddService' +import ServiceCard from '../AssetContent/ServiceCard' +import AddServiceCard from './AddServiceCard' export default function EditServices({ asset }: { asset: AssetExtended }): ReactElement { - const [isAddingNewService, setIsAddingNewService] = useState(false) + const [selectedService, setSelectedService] = useState() // -1 is the new service, undefined is none return (
- {asset.services.map((service, index) => ( - - ))} - {isAddingNewService ? ( - setIsAddingNewService(false)} - /> - ) : ( -
- -
+
+ {asset.services.map((service, index) => ( + setSelectedService(index)} + /> + ))} + setSelectedService(-1)} /> +
+ + {selectedService !== undefined && ( + <> +
+ +
+

+ {selectedService === -1 + ? 'Add a new service' + : `Edit service ${asset.services[selectedService].name}`} +

+ +
+ + {selectedService === -1 ? ( + + ) : ( + + )} + )}
) diff --git a/src/components/Asset/Edit/FormAddService.tsx b/src/components/Asset/Edit/FormAddService.tsx index 43791e47e..73af2ea83 100644 --- a/src/components/Asset/Edit/FormAddService.tsx +++ b/src/components/Asset/Edit/FormAddService.tsx @@ -4,18 +4,14 @@ import Input from '@shared/FormInput' import FormActions from './FormActions' import { getFieldContent } from '@utils/form' import consumerParametersContent from '../../../../content/publish/consumerParameters.json' -import Accordion from '@components/@shared/Accordion' import { ServiceEditForm } from './_types' -import Button from '@components/@shared/atoms/Button' import IconDownload from '@images/download.svg' import IconCompute from '@images/compute.svg' export default function FormAddService({ - data, - onRemove + data }: { data: FormFieldContent[] - onRemove: () => void }): ReactElement { const { values, setFieldValue } = useFormikContext() @@ -42,95 +38,70 @@ export default function FormAddService({ return ( - - -
- } - > -
- + - + - + - + - + - + - + - + - - {values.usesConsumerParameters && ( - + + {values.usesConsumerParameters && ( + -
-
+ component={Input} + name="consumerParameters" + /> + )} + ) } diff --git a/src/components/Asset/Edit/FormEditService.tsx b/src/components/Asset/Edit/FormEditService.tsx index 87995ca36..d4ab63fbf 100644 --- a/src/components/Asset/Edit/FormEditService.tsx +++ b/src/components/Asset/Edit/FormEditService.tsx @@ -4,7 +4,6 @@ import Input from '@shared/FormInput' import FormActions from './FormActions' import { getFieldContent } from '@utils/form' import consumerParametersContent from '../../../../content/publish/consumerParameters.json' -import Accordion from '@components/@shared/Accordion' import { Service } from '@oceanprotocol/lib' import { ServiceEditForm } from './_types' import IconDownload from '@images/download.svg' @@ -49,79 +48,71 @@ export default function FormEditService({ return (
- -
- + - + - + - + - + - + - + - + - - {values.usesConsumerParameters && ( - + + {values.usesConsumerParameters && ( + -
-
+ component={Input} + name="consumerParameters" + /> + )} + ) } From d4546f13c628570c330f872e2c3d039c32379965 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Mon, 9 Sep 2024 20:46:12 +0200 Subject: [PATCH 16/31] add compute settings into service settings --- content/pages/editComputeDataset.json | 2 - src/components/Asset/Edit/AddService.tsx | 17 +- .../Asset/Edit/EditComputeDataset.tsx | 155 ------------------ src/components/Asset/Edit/EditService.tsx | 11 +- src/components/Asset/Edit/FormAddService.tsx | 16 +- ...Dataset.tsx => FormEditComputeService.tsx} | 49 +++--- src/components/Asset/Edit/FormEditService.tsx | 14 +- src/components/Asset/Edit/_constants.ts | 48 +++--- src/components/Asset/Edit/_types.ts | 8 +- src/components/Asset/Edit/index.tsx | 15 +- 10 files changed, 110 insertions(+), 225 deletions(-) delete mode 100644 src/components/Asset/Edit/EditComputeDataset.tsx rename src/components/Asset/Edit/{FormEditComputeDataset.tsx => FormEditComputeService.tsx} (73%) diff --git a/content/pages/editComputeDataset.json b/content/pages/editComputeDataset.json index 92388dc1c..d0f1201e9 100644 --- a/content/pages/editComputeDataset.json +++ b/content/pages/editComputeDataset.json @@ -2,8 +2,6 @@ "form": { "title": "Set allowed algorithms", "description": "Only the algorithms selected here will be allowed to run on your dataset. Uncheck all to remove any access to your dataset.", - "success": "🎉 Successfully updated. 🎉\n\nUpdates might not show up right away on your asset. In this case, wait some seconds and reload your asset details page in your browser.", - "error": "Updating DDO failed.", "data": [ { "name": "publisherTrustedAlgorithms", diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx index f807b9d28..b68cc88e9 100644 --- a/src/components/Asset/Edit/AddService.tsx +++ b/src/components/Asset/Edit/AddService.tsx @@ -9,7 +9,10 @@ import { DispenserParams, getHash } from '@oceanprotocol/lib' -import { getNewServiceInitialValues } from './_constants' +import { + defaultServiceComputeOptions, + getNewServiceInitialValues +} from './_constants' import { ServiceEditForm } from './_types' import Web3Feedback from '@shared/Web3Feedback' import { mapTimeoutStringToSeconds, normalizeFile } from '@utils/ddo' @@ -24,6 +27,8 @@ import { transformConsumerParameters } from '@components/Publish/_utils' import { marketFeeAddress, publisherMarketFixedSwapFee } from 'app.config' import { ethers } from 'ethers' import FormAddService from './FormAddService' +import { transformComputeFormToServiceComputeOptions } from '@utils/compute' +import { useCancelToken } from '@hooks/useCancelToken' export default function AddService({ asset @@ -35,6 +40,7 @@ export default function AddService({ const { chain } = useNetwork() const { data: signer } = useSigner() const newAbortController = useAbortController() + const newCancelToken = useCancelToken() const [success, setSuccess] = useState() const [error, setError] = useState() @@ -152,7 +158,12 @@ export default function AddService({ serviceEndpoint: values.providerUrl.url, timeout: mapTimeoutStringToSeconds(values.timeout), ...(values.access === 'compute' && { - compute: values.compute + compute: await transformComputeFormToServiceComputeOptions( + values, + defaultServiceComputeOptions, + asset.chainId, + newCancelToken() + ) }), consumerParameters: transformConsumerParameters( values.consumerParameters @@ -219,7 +230,7 @@ export default function AddService({ /> ) : ( <> - + () - const [error, setError] = useState() - const newAbortController = useAbortController() - const newCancelToken = useCancelToken() - const hasFeedback = error || success - - async function handleSubmit(values: ComputeEditForm, resetForm: () => void) { - try { - const newComputeSettings: ServiceComputeOptions = - await transformComputeFormToServiceComputeOptions( - values, - asset.services[0].compute, - asset.chainId, - newCancelToken() - ) - - LoggerInstance.log( - '[edit compute settings] newComputeSettings', - newComputeSettings - ) - - const updatedService: Service = { - ...asset.services[0], - compute: newComputeSettings - } - - LoggerInstance.log( - '[edit compute settings] updatedService', - updatedService - ) - - const updatedAsset: Asset = { - ...asset, - services: [updatedService] - } - - // TODO: revert to setMetadata function - const setMetadataTx = await setNFTMetadataAndTokenURI( - updatedAsset, - accountId, - signer, - decodeTokenURI(asset.nft.tokenURI), - newAbortController() - ) - // const setMetadataTx = await setNftMetadata( - // updatedAsset, - // accountId, - // web3, - // newAbortController() - // ) - - LoggerInstance.log('[edit] setMetadata result', setMetadataTx) - - if (!setMetadataTx) { - setError(content.form.error) - LoggerInstance.error(content.form.error) - return - } - // Edit succeeded - setSuccess(content.form.success) - resetForm() - } catch (error) { - LoggerInstance.error(error.message) - setError(error.message) - } - } - - return ( - { - // move user's focus to top of screen - window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) - // kick off editing - await handleSubmit(values, resetForm) - }} - enableReinitialize - > - {({ values, isSubmitting }) => - isSubmitting || hasFeedback ? ( - { - await fetchAsset() - }, - to: `/asset/${asset.id}` - }} - /> - ) : ( - <> - - - {debug === true && ( -
- -
- )} - - ) - } -
- ) -} diff --git a/src/components/Asset/Edit/EditService.tsx b/src/components/Asset/Edit/EditService.tsx index b1eff0303..9bc6ac82e 100644 --- a/src/components/Asset/Edit/EditService.tsx +++ b/src/components/Asset/Edit/EditService.tsx @@ -20,6 +20,8 @@ import { getEncryptedFiles } from '@utils/provider' import { useAccount, useNetwork, useSigner } from 'wagmi' import { transformConsumerParameters } from '@components/Publish/_utils' import FormEditService from './FormEditService' +import { transformComputeFormToServiceComputeOptions } from '@utils/compute' +import { useCancelToken } from '@hooks/useCancelToken' export default function EditService({ asset, @@ -35,6 +37,7 @@ export default function EditService({ const { chain } = useNetwork() const { data: signer } = useSigner() const newAbortController = useAbortController() + const newCancelToken = useCancelToken() const [success, setSuccess] = useState() const [error, setError] = useState() @@ -106,7 +109,12 @@ export default function EditService({ timeout: mapTimeoutStringToSeconds(values.timeout), files: updatedFiles, // TODO: check if this works ...(values.access === 'compute' && { - compute: values.compute + compute: await transformComputeFormToServiceComputeOptions( + values, + service.compute, + asset.chainId, + newCancelToken() + ) }) } if (values.consumerParameters) { @@ -180,6 +188,7 @@ export default function EditService({ <> diff --git a/src/components/Asset/Edit/FormAddService.tsx b/src/components/Asset/Edit/FormAddService.tsx index 73af2ea83..6b02ca9d6 100644 --- a/src/components/Asset/Edit/FormAddService.tsx +++ b/src/components/Asset/Edit/FormAddService.tsx @@ -7,11 +7,15 @@ import consumerParametersContent from '../../../../content/publish/consumerParam import { ServiceEditForm } from './_types' import IconDownload from '@images/download.svg' import IconCompute from '@images/compute.svg' +import FormEditComputeService from './FormEditComputeService' +import { defaultServiceComputeOptions } from './_constants' export default function FormAddService({ - data + data, + chainId }: { data: FormFieldContent[] + chainId: number }): ReactElement { const { values, setFieldValue } = useFormikContext() @@ -53,6 +57,14 @@ export default function FormAddService({ options={accessTypeOptions} /> + {values.access === 'compute' && ( + + )} + = useFormikContext() + const { values }: FormikContextType = useFormikContext() const newCancelToken = useCancelToken() const [allAlgorithms, setAllAlgorithms] = useState() @@ -33,40 +40,34 @@ export default function FormEditComputeDataset(): ReactElement { publisherTrustedAlgorithms: PublisherTrustedAlgorithm[] ): Promise => { const baseParams = { - chainIds: [asset.chainId], + chainIds: [chainId], sort: { sortBy: SortTermOptions.Created }, filters: [getFilterTerm('metadata.type', 'algorithm')] } as BaseQueryParams const query = generateBaseQuery(baseParams) const queryResult = await queryMetadata(query, newCancelToken()) - const datasetComputeService = getServiceByName(asset, 'compute') const algorithmSelectionList = await transformAssetToAssetSelection( - datasetComputeService?.serviceEndpoint, - queryResult?.results, + serviceEndpoint, + queryResult?.results || [], accountId, publisherTrustedAlgorithms ) return algorithmSelectionList }, - [accountId, asset, newCancelToken] + [accountId, chainId, newCancelToken, serviceEndpoint] ) useEffect(() => { - if (!asset) return - - const { publisherTrustedAlgorithms } = getServiceByName( - asset, - 'compute' - ).compute + const { publisherTrustedAlgorithms } = serviceCompute getAlgorithmList(publisherTrustedAlgorithms).then((algorithms) => { setAllAlgorithms(algorithms) }) - }, [asset, getAlgorithmList]) + }, [serviceCompute, getAlgorithmList]) return ( -
+ <>

{content.form.title}

@@ -91,8 +92,6 @@ export default function FormEditComputeDataset(): ReactElement { .options } /> - - - + ) } diff --git a/src/components/Asset/Edit/FormEditService.tsx b/src/components/Asset/Edit/FormEditService.tsx index d4ab63fbf..9ae5e4c53 100644 --- a/src/components/Asset/Edit/FormEditService.tsx +++ b/src/components/Asset/Edit/FormEditService.tsx @@ -8,13 +8,17 @@ import { Service } from '@oceanprotocol/lib' import { ServiceEditForm } from './_types' import IconDownload from '@images/download.svg' import IconCompute from '@images/compute.svg' +import FormEditComputeService from './FormEditComputeService' +import { defaultServiceComputeOptions } from './_constants' export default function FormEditService({ data, + chainId, service, accessDetails }: { data: FormFieldContent[] + chainId: number service: Service accessDetails: AccessDetails }): ReactElement { @@ -65,6 +69,14 @@ export default function FormEditService({ disabled={true} /> + {values.access === 'compute' && ( + + )} + algo.did) + + return { + allowAllPublishedAlgorithms, + publisherTrustedAlgorithms: publisherTrustedAlgorithmsForForm, + publisherTrustedAlgorithmPublishers + } +} + export const getNewServiceInitialValues = ( accountId: string, firstService: Service ): ServiceEditForm => { + const computeSettings = getComputeSettingsInitialValues( + defaultServiceComputeOptions + ) return { name: 'New Service', description: '', access: 'access', - compute: defaultServiceComputeOptions, price: 1, paymentCollector: accountId, providerUrl: { @@ -59,7 +77,8 @@ export const getNewServiceInitialValues = ( files: [{ url: '', type: 'hidden' }], timeout: '1 day', usesConsumerParameters: false, - consumerParameters: [] + consumerParameters: [], + ...computeSettings } } @@ -67,11 +86,13 @@ export const getServiceInitialValues = ( service: Service, accessDetails: AccessDetails ): ServiceEditForm => { + const computeSettings = getComputeSettingsInitialValues( + service.compute || defaultServiceComputeOptions + ) return { name: service.name, description: service.description, access: service.type as 'access' | 'compute', - compute: service.compute || defaultServiceComputeOptions, price: parseFloat(accessDetails.price), paymentCollector: accessDetails.paymentCollector, providerUrl: { @@ -82,22 +103,7 @@ export const getServiceInitialValues = ( files: [{ url: '', type: 'hidden' }], timeout: secondsToString(service.timeout), usesConsumerParameters: service.consumerParameters?.length > 0, - consumerParameters: parseConsumerParameters(service.consumerParameters) - } -} - -export function getComputeSettingsInitialValues({ - publisherTrustedAlgorithms, - publisherTrustedAlgorithmPublishers -}: ServiceComputeOptions): ComputeEditForm { - const allowAllPublishedAlgorithms = publisherTrustedAlgorithms === null - const publisherTrustedAlgorithmsForForm = allowAllPublishedAlgorithms - ? null - : publisherTrustedAlgorithms.map((algo) => algo.did) - - return { - allowAllPublishedAlgorithms, - publisherTrustedAlgorithms: publisherTrustedAlgorithmsForForm, - publisherTrustedAlgorithmPublishers + consumerParameters: parseConsumerParameters(service.consumerParameters), + ...computeSettings } } diff --git a/src/components/Asset/Edit/_types.ts b/src/components/Asset/Edit/_types.ts index e9a948d7c..ffe0709e5 100644 --- a/src/components/Asset/Edit/_types.ts +++ b/src/components/Asset/Edit/_types.ts @@ -1,5 +1,5 @@ import { FormConsumerParameter } from '@components/Publish/_types' -import { FileInfo, ServiceComputeOptions } from '@oceanprotocol/lib' +import { FileInfo } from '@oceanprotocol/lib' export interface MetadataEditForm { name: string @@ -19,7 +19,6 @@ export interface ServiceEditForm { name: string description: string access: 'access' | 'compute' - compute: ServiceComputeOptions providerUrl: { url: string; valid: boolean; custom: boolean } price: number paymentCollector: string @@ -27,8 +26,13 @@ export interface ServiceEditForm { timeout: string usesConsumerParameters: boolean consumerParameters: FormConsumerParameter[] + // compute + allowAllPublishedAlgorithms: boolean + publisherTrustedAlgorithms: string[] + publisherTrustedAlgorithmPublishers: string[] } +// TODO delete export interface ComputeEditForm { allowAllPublishedAlgorithms: boolean publisherTrustedAlgorithms: string[] diff --git a/src/components/Asset/Edit/index.tsx b/src/components/Asset/Edit/index.tsx index d804c1134..07764c80e 100644 --- a/src/components/Asset/Edit/index.tsx +++ b/src/components/Asset/Edit/index.tsx @@ -3,7 +3,6 @@ import { useAsset } from '@context/Asset' import styles from './index.module.css' import Tabs from '@shared/atoms/Tabs' import EditMetadata from './EditMetadata' -import EditComputeDataset from './EditComputeDataset' import Page from '@shared/Page' import Loader from '@shared/atoms/Loader' import Alert from '@shared/atoms/Alert' @@ -13,7 +12,6 @@ import EditServices from './EditServices' export default function Edit({ uri }: { uri: string }): ReactElement { const { asset, error, isInPurgatory, title, isOwner } = useAsset() - const [isCompute, setIsCompute] = useState(false) const [pageTitle, setPageTitle] = useState('') const [tabIndex, setTabIndex] = useState(0) @@ -27,7 +25,6 @@ export default function Edit({ uri }: { uri: string }): ReactElement { : `Edit ${title}` setPageTitle(pageTitle) - setIsCompute(asset?.services[0]?.type === 'compute') }, [asset, isInPurgatory, title, isOwner]) const tabs = [ @@ -38,16 +35,8 @@ export default function Edit({ uri }: { uri: string }): ReactElement { { title: 'Edit Services', content: - }, - ...[ - isCompute && asset?.metadata.type !== 'algorithm' - ? { - title: 'Edit Compute Settings', - content: - } - : undefined - ] - ].filter((tab) => tab !== undefined) + } + ] return ( From 443b85a9a74ecc362f4cfdde9996a268444fc746 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Mon, 9 Sep 2024 21:12:00 +0200 Subject: [PATCH 17/31] update form validation schemas --- src/components/Asset/Edit/AddService.tsx | 3 +- src/components/Asset/Edit/EditMetadata.tsx | 4 +- src/components/Asset/Edit/EditService.tsx | 3 +- src/components/Asset/Edit/_validation.ts | 66 +++++++++++----------- 4 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx index b68cc88e9..6379d4b1d 100644 --- a/src/components/Asset/Edit/AddService.tsx +++ b/src/components/Asset/Edit/AddService.tsx @@ -29,6 +29,7 @@ import { ethers } from 'ethers' import FormAddService from './FormAddService' import { transformComputeFormToServiceComputeOptions } from '@utils/compute' import { useCancelToken } from '@hooks/useCancelToken' +import { serviceValidationSchema } from './_validation' export default function AddService({ asset @@ -205,7 +206,7 @@ export default function AddService({ { // move user's focus to top of screen window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) diff --git a/src/components/Asset/Edit/EditMetadata.tsx b/src/components/Asset/Edit/EditMetadata.tsx index bb51bf55a..f46606f25 100644 --- a/src/components/Asset/Edit/EditMetadata.tsx +++ b/src/components/Asset/Edit/EditMetadata.tsx @@ -1,7 +1,7 @@ import { ReactElement, useState } from 'react' import { Formik } from 'formik' import { LoggerInstance, Asset, Nft, Metadata } from '@oceanprotocol/lib' -import { validationSchema } from './_validation' +import { metadataValidationSchema } from './_validation' import { getInitialValues } from './_constants' import { MetadataEditForm } from './_types' import { useUserPreferences } from '@context/UserPreferences' @@ -126,7 +126,7 @@ export default function Edit({ asset?.credentials, assetState )} - validationSchema={validationSchema} + validationSchema={metadataValidationSchema} onSubmit={async (values, { resetForm }) => { // move user's focus to top of screen window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) diff --git a/src/components/Asset/Edit/EditService.tsx b/src/components/Asset/Edit/EditService.tsx index 9bc6ac82e..1e61e9857 100644 --- a/src/components/Asset/Edit/EditService.tsx +++ b/src/components/Asset/Edit/EditService.tsx @@ -22,6 +22,7 @@ import { transformConsumerParameters } from '@components/Publish/_utils' import FormEditService from './FormEditService' import { transformComputeFormToServiceComputeOptions } from '@utils/compute' import { useCancelToken } from '@hooks/useCancelToken' +import { serviceValidationSchema } from './_validation' export default function EditService({ asset, @@ -161,7 +162,7 @@ export default function EditService({ { // move user's focus to top of screen window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) diff --git a/src/components/Asset/Edit/_validation.ts b/src/components/Asset/Edit/_validation.ts index 79579feb6..79333ec53 100644 --- a/src/components/Asset/Edit/_validation.ts +++ b/src/components/Asset/Edit/_validation.ts @@ -4,25 +4,11 @@ import { isAddress } from 'ethers/lib/utils' import { testLinks } from '@utils/yup' import { validationConsumerParameters } from '@shared/FormInput/InputElement/ConsumerParameters/_validation' -export const validationSchema = Yup.object().shape({ +export const metadataValidationSchema = Yup.object().shape({ name: Yup.string() .min(4, (param) => `Title must be at least ${param.min} characters`) .required('Required'), description: Yup.string().required('Required').min(10), - price: Yup.number().required('Required'), - files: Yup.array() - .of( - Yup.object().shape({ - url: testLinks(true), - valid: Yup.boolean().test((value, context) => { - const { type } = context.parent - // allow user to submit if the value type is hidden - if (type === 'hidden') return true - return value || false - }) - }) - ) - .nullable(), links: Yup.array().of( Yup.object().shape({ url: testLinks(true), @@ -35,7 +21,6 @@ export const validationSchema = Yup.object().shape({ }) }) ), - timeout: Yup.string().required('Required'), tags: Yup.array().nullable(), usesConsumerParameters: Yup.boolean(), consumerParameters: Yup.array().when('usesConsumerParameters', { @@ -49,6 +34,39 @@ export const validationSchema = Yup.object().shape({ }), allow: Yup.array().of(Yup.string()).nullable(), deny: Yup.array().of(Yup.string()).nullable(), + retireAsset: Yup.string() +}) + +export const serviceValidationSchema = Yup.object().shape({ + name: Yup.string() + .min(4, (param) => `Name must be at least ${param.min} characters`) + .required('Required'), + description: Yup.string().required('Required').min(10), + price: Yup.number().required('Required'), + files: Yup.array() + .of( + Yup.object().shape({ + url: testLinks(true), + valid: Yup.boolean().test((value, context) => { + const { type } = context.parent + // allow user to submit if the value type is hidden + if (type === 'hidden') return true + return value || false + }) + }) + ) + .nullable(), + timeout: Yup.string().required('Required'), + usesConsumerParameters: Yup.boolean(), + consumerParameters: Yup.array().when('usesConsumerParameters', { + is: true, + then: Yup.array() + .of(Yup.object().shape(validationConsumerParameters)) + .required('Required'), + otherwise: Yup.array() + .nullable() + .transform((value) => value || null) + }), paymentCollector: Yup.string().test( 'ValidAddress', 'Must be a valid Ethereum Address.', @@ -56,22 +74,6 @@ export const validationSchema = Yup.object().shape({ return isAddress(value) } ), - retireAsset: Yup.string(), - service: Yup.object().shape({ - usesConsumerParameters: Yup.boolean(), - consumerParameters: Yup.array().when('usesConsumerParameters', { - is: true, - then: Yup.array() - .of(Yup.object().shape(validationConsumerParameters)) - .required('Required'), - otherwise: Yup.array() - .nullable() - .transform((value) => value || null) - }) - }) -}) - -export const computeSettingsValidationSchema = Yup.object().shape({ allowAllPublishedAlgorithms: Yup.boolean().nullable(), publisherTrustedAlgorithms: Yup.array().nullable(), publisherTrustedAlgorithmPublishers: Yup.array().nullable() From 1b9fa570e7b7c38dbab2e42d41c6d880cec765af Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Mon, 9 Sep 2024 21:13:19 +0200 Subject: [PATCH 18/31] update description --- content/pages/edit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/pages/edit.json b/content/pages/edit.json index 0755221bf..3e1761538 100644 --- a/content/pages/edit.json +++ b/content/pages/edit.json @@ -1,3 +1,3 @@ { - "description": "Updating metadata or updating compute settings will create an on-chain transaction you have to approve in your wallet." + "description": "Updating metadata or updating services will create an on-chain transaction you have to approve in your wallet." } From 586f149b415f3b43c11923b6dab7bc7b445d9c76 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Mon, 9 Sep 2024 21:31:26 +0200 Subject: [PATCH 19/31] update services grid UI --- .../Asset/AssetContent/ServiceCard.module.css | 1 - .../Asset/AssetContent/index.module.css | 5 +++++ src/components/Asset/AssetContent/index.tsx | 18 ++++++++++-------- .../Asset/Edit/AddServiceCard.module.css | 1 - src/components/Asset/Edit/EditServices.tsx | 9 ++------- src/components/Asset/Edit/index.module.css | 18 ++++++++++++++++++ 6 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/components/Asset/AssetContent/ServiceCard.module.css b/src/components/Asset/AssetContent/ServiceCard.module.css index 61d3788ef..6afac9103 100644 --- a/src/components/Asset/AssetContent/ServiceCard.module.css +++ b/src/components/Asset/AssetContent/ServiceCard.module.css @@ -3,7 +3,6 @@ border: var(--box-template-border-size) solid var(--box-template-border-color); cursor: pointer; padding: calc(var(--spacer) * 0.5) var(--spacer); - margin: var(--spacer) 0; } .service:hover { diff --git a/src/components/Asset/AssetContent/index.module.css b/src/components/Asset/AssetContent/index.module.css index 992dd5081..76048bcdf 100644 --- a/src/components/Asset/AssetContent/index.module.css +++ b/src/components/Asset/AssetContent/index.module.css @@ -39,3 +39,8 @@ border-top: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); } + +.servicesGrid { + display: grid; + gap: calc(var(--spacer) * 0.5); +} diff --git a/src/components/Asset/AssetContent/index.tsx b/src/components/Asset/AssetContent/index.tsx index eec3958b3..4b49c1fc4 100644 --- a/src/components/Asset/AssetContent/index.tsx +++ b/src/components/Asset/AssetContent/index.tsx @@ -81,14 +81,16 @@ export default function AssetContent({ {selectedService === undefined ? ( <>

Available services:

- {asset.services.map((service, index) => ( - setSelectedService(index)} - /> - ))} +
+ {asset.services.map((service, index) => ( + setSelectedService(index)} + /> + ))} +
) : ( -
+
{asset.services.map((service, index) => ( Date: Wed, 11 Sep 2024 08:15:02 +0200 Subject: [PATCH 20/31] add debug view --- .../Asset/Edit/DebugEditCompute.tsx | 41 ---------- .../Asset/Edit/DebugEditService.tsx | 82 +++++++++++++++++++ src/components/Asset/Edit/EditService.tsx | 14 ++++ src/components/Asset/Edit/index.module.css | 2 +- 4 files changed, 97 insertions(+), 42 deletions(-) delete mode 100644 src/components/Asset/Edit/DebugEditCompute.tsx create mode 100644 src/components/Asset/Edit/DebugEditService.tsx diff --git a/src/components/Asset/Edit/DebugEditCompute.tsx b/src/components/Asset/Edit/DebugEditCompute.tsx deleted file mode 100644 index 7d3b1fa9a..000000000 --- a/src/components/Asset/Edit/DebugEditCompute.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Asset, ServiceComputeOptions } from '@oceanprotocol/lib' -import { ReactElement, useEffect, useState } from 'react' -import DebugOutput from '@shared/DebugOutput' -import { useCancelToken } from '@hooks/useCancelToken' -import { transformComputeFormToServiceComputeOptions } from '@utils/compute' -import { ComputeEditForm } from './_types' -import { previewDebugPatch } from '@utils/ddo' - -export default function DebugEditCompute({ - values, - asset -}: { - values: ComputeEditForm - asset: Asset -}): ReactElement { - const [valuePreview, setValuePreview] = useState({}) - const [formTransformed, setFormTransformed] = - useState() - const newCancelToken = useCancelToken() - - useEffect(() => { - async function transformValues() { - const privacy = await transformComputeFormToServiceComputeOptions( - values, - asset.services[0].compute, - asset.chainId, - newCancelToken() - ) - setFormTransformed(privacy) - } - transformValues() - setValuePreview(previewDebugPatch(values, asset.chainId)) - }, [values, asset]) - - return ( - <> - - - - ) -} diff --git a/src/components/Asset/Edit/DebugEditService.tsx b/src/components/Asset/Edit/DebugEditService.tsx new file mode 100644 index 000000000..9271524f2 --- /dev/null +++ b/src/components/Asset/Edit/DebugEditService.tsx @@ -0,0 +1,82 @@ +import { Asset, Service } from '@oceanprotocol/lib' +import { ReactElement, useEffect, useState } from 'react' +import DebugOutput from '@shared/DebugOutput' +import { useCancelToken } from '@hooks/useCancelToken' +import { transformComputeFormToServiceComputeOptions } from '@utils/compute' +import { ServiceEditForm } from './_types' +import { + mapTimeoutStringToSeconds, + normalizeFile, + previewDebugPatch +} from '@utils/ddo' +import { getEncryptedFiles } from '@utils/provider' +import { transformConsumerParameters } from '@components/Publish/_utils' + +export default function DebugEditService({ + values, + asset, + service +}: { + values: ServiceEditForm + asset: Asset + service: Service +}): ReactElement { + const [valuePreview, setValuePreview] = useState({}) + const [updatedService, setUpdatedService] = useState() + const newCancelToken = useCancelToken() + + useEffect(() => { + async function transformValues() { + let updatedFiles = service.files + if (values.files[0]?.url) { + const file = { + nftAddress: asset.nftAddress, + datatokenAddress: service.datatokenAddress, + files: [ + normalizeFile(values.files[0].type, values.files[0], asset.chainId) + ] + } + + const filesEncrypted = await getEncryptedFiles( + file, + asset.chainId, + service.serviceEndpoint + ) + updatedFiles = filesEncrypted + } + + const updatedService: Service = { + ...service, + name: values.name, + description: values.description, + timeout: mapTimeoutStringToSeconds(values.timeout), + files: updatedFiles, // TODO: check if this works + ...(values.access === 'compute' && { + compute: await transformComputeFormToServiceComputeOptions( + values, + service.compute, + asset.chainId, + newCancelToken() + ) + }) + } + if (values.consumerParameters) { + updatedService.consumerParameters = transformConsumerParameters( + values.consumerParameters + ) + } + + setUpdatedService(updatedService) + } + + transformValues() + setValuePreview(previewDebugPatch(values, asset.chainId)) + }, [values, asset, newCancelToken, service]) + + return ( + <> + + + + ) +} diff --git a/src/components/Asset/Edit/EditService.tsx b/src/components/Asset/Edit/EditService.tsx index 1e61e9857..402c44758 100644 --- a/src/components/Asset/Edit/EditService.tsx +++ b/src/components/Asset/Edit/EditService.tsx @@ -23,6 +23,9 @@ import FormEditService from './FormEditService' import { transformComputeFormToServiceComputeOptions } from '@utils/compute' import { useCancelToken } from '@hooks/useCancelToken' import { serviceValidationSchema } from './_validation' +import { useUserPreferences } from '@context/UserPreferences' +import DebugEditService from './DebugEditService' +import styles from './index.module.css' export default function EditService({ asset, @@ -33,6 +36,7 @@ export default function EditService({ service: Service accessDetails: AccessDetails | undefined }): ReactElement { + const { debug } = useUserPreferences() const { fetchAsset, isAssetNetwork } = useAsset() const { address: accountId } = useAccount() const { chain } = useNetwork() @@ -199,6 +203,16 @@ export default function EditService({ accountId={accountId} isAssetNetwork={isAssetNetwork} /> + + {debug === true && ( +
+ +
+ )} ) } diff --git a/src/components/Asset/Edit/index.module.css b/src/components/Asset/Edit/index.module.css index a32d2252d..2c7da69f4 100644 --- a/src/components/Asset/Edit/index.module.css +++ b/src/components/Asset/Edit/index.module.css @@ -17,7 +17,7 @@ @media (min-width: 60rem) { .grid { - grid-template-columns: 1.5fr 1fr; + grid-template-columns: 1fr 1fr; } } From 6a5bff98265ae377e42da05a393b1c5ecc2b7935 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 11 Sep 2024 08:28:52 +0200 Subject: [PATCH 21/31] add debug view to AddService --- src/components/Asset/Edit/AddService.tsx | 24 +++++++++++++++++++ .../Asset/Edit/DebugEditService.tsx | 1 + 2 files changed, 25 insertions(+) diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx index 6379d4b1d..922a89c1e 100644 --- a/src/components/Asset/Edit/AddService.tsx +++ b/src/components/Asset/Edit/AddService.tsx @@ -30,12 +30,16 @@ import FormAddService from './FormAddService' import { transformComputeFormToServiceComputeOptions } from '@utils/compute' import { useCancelToken } from '@hooks/useCancelToken' import { serviceValidationSchema } from './_validation' +import DebugEditService from './DebugEditService' +import styles from './index.module.css' +import { useUserPreferences } from '@context/UserPreferences' export default function AddService({ asset }: { asset: AssetExtended }): ReactElement { + const { debug } = useUserPreferences() const { fetchAsset, isAssetNetwork } = useAsset() const { address: accountId } = useAccount() const { chain } = useNetwork() @@ -238,6 +242,26 @@ export default function AddService({ accountId={accountId} isAssetNetwork={isAssetNetwork} /> + + {debug === true && ( +
+ +
+ )} ) } diff --git a/src/components/Asset/Edit/DebugEditService.tsx b/src/components/Asset/Edit/DebugEditService.tsx index 9271524f2..a64ed3ed5 100644 --- a/src/components/Asset/Edit/DebugEditService.tsx +++ b/src/components/Asset/Edit/DebugEditService.tsx @@ -49,6 +49,7 @@ export default function DebugEditService({ ...service, name: values.name, description: values.description, + type: values.access, timeout: mapTimeoutStringToSeconds(values.timeout), files: updatedFiles, // TODO: check if this works ...(values.access === 'compute' && { From ad356810f27723d8e9f6e90b51187bc27dac7cfa Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 11 Sep 2024 08:34:57 +0200 Subject: [PATCH 22/31] add a comment --- src/@utils/ddo.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/@utils/ddo.ts b/src/@utils/ddo.ts index 3366c8e45..21099fb4a 100644 --- a/src/@utils/ddo.ts +++ b/src/@utils/ddo.ts @@ -25,6 +25,7 @@ export function isValidDid(did: string): boolean { return regex.test(did) } +// TODO: this function doesn't make sense, since market is now supporting multiple services. We should remove it after checking all the flows where it's being used. export function getServiceByName( ddo: Asset | DDO, name: 'access' | 'compute' From 5f0abb32cc5d8b32e9940105f7f75ca6ca9b0538 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 11 Sep 2024 09:33:46 +0200 Subject: [PATCH 23/31] fix some types, remove unnecessary code --- src/@utils/ddo.ts | 19 +---- .../Asset/Edit/DebugEditMetadata.tsx | 84 ++++++++++--------- .../Asset/Edit/DebugEditService.tsx | 2 +- src/components/Asset/Edit/EditMetadata.tsx | 5 +- src/components/Asset/Edit/FormActions.tsx | 4 +- src/components/Asset/Edit/_constants.ts | 2 +- src/components/Publish/Debug/index.tsx | 4 +- 7 files changed, 55 insertions(+), 65 deletions(-) diff --git a/src/@utils/ddo.ts b/src/@utils/ddo.ts index 21099fb4a..30009c4c6 100644 --- a/src/@utils/ddo.ts +++ b/src/@utils/ddo.ts @@ -1,6 +1,6 @@ import { - ComputeEditForm, - MetadataEditForm + MetadataEditForm, + ServiceEditForm } from '@components/Asset/Edit/_types' import { FormConsumerParameter, @@ -172,25 +172,12 @@ export function normalizeFile( } export function previewDebugPatch( - values: FormPublishData | Partial | ComputeEditForm, - chainId: number + values: FormPublishData | MetadataEditForm | ServiceEditForm ) { // handle file's object property dynamically // without braking Yup and type validation const buildValuesPreview = JSON.parse(JSON.stringify(values)) - // fallback for edit mode under "edit compute settings" - if (!buildValuesPreview.services) return buildValuesPreview - - const valuesService = buildValuesPreview.services - ? buildValuesPreview.services[0] - : buildValuesPreview - valuesService.files[0] = normalizeFile( - valuesService.files[0].type, - valuesService.files[0], - chainId - ) - return buildValuesPreview } diff --git a/src/components/Asset/Edit/DebugEditMetadata.tsx b/src/components/Asset/Edit/DebugEditMetadata.tsx index 5a12b2868..de24f45af 100644 --- a/src/components/Asset/Edit/DebugEditMetadata.tsx +++ b/src/components/Asset/Edit/DebugEditMetadata.tsx @@ -13,52 +13,60 @@ export default function DebugEditMetadata({ values, asset }: { - values: Partial + values: MetadataEditForm asset: Asset }): ReactElement { const [valuePreview, setValuePreview] = useState({}) - const linksTransformed = values.links?.length && - values.links[0].valid && [sanitizeUrl(values.links[0].url)] + const [updatedAsset, setUpdatedAsset] = useState() - const newMetadata: Metadata = { - ...asset?.metadata, - name: values.name, - description: values.description, - links: linksTransformed, - author: values.author, - tags: values.tags, - license: values.license, - additionalInformation: { - ...asset?.metadata?.additionalInformation - } - } - if (asset.metadata.type === 'algorithm') { - newMetadata.algorithm.consumerParameters = !values.usesConsumerParameters - ? undefined - : transformConsumerParameters(values.consumerParameters) - } + useEffect(() => { + function transformValues() { + const linksTransformed = values.links?.length && + values.links[0].valid && [sanitizeUrl(values.links[0].url)] - const updatedCredentials: Credentials = generateCredentials( - asset?.credentials, - values?.allow, - values?.deny - ) + const newMetadata: Metadata = { + ...asset?.metadata, + name: values.name, + description: values.description, + links: linksTransformed, + author: values.author, + tags: values.tags, + license: values.license, + additionalInformation: { + ...asset?.metadata?.additionalInformation + } + } + if (asset.metadata.type === 'algorithm') { + newMetadata.algorithm.consumerParameters = + !values.usesConsumerParameters + ? undefined + : transformConsumerParameters(values.consumerParameters) + } - const updatedAsset: Asset = { - ...asset, - metadata: newMetadata, - credentials: updatedCredentials - } + const updatedCredentials: Credentials = generateCredentials( + asset?.credentials, + values?.allow, + values?.deny + ) - // delete custom helper properties injected in the market that will not be written on chain - delete (updatedAsset as AssetExtended).accessDetails - delete (updatedAsset as AssetExtended).datatokens - delete (updatedAsset as AssetExtended).stats - delete (updatedAsset as AssetExtended).offchain + const tmpAsset: Asset = { + ...asset, + metadata: newMetadata, + credentials: updatedCredentials + } - useEffect(() => { - setValuePreview(previewDebugPatch(values, asset.chainId)) - }, [asset.chainId, values]) + // delete custom helper properties injected in the market that will not be written on chain + delete (tmpAsset as AssetExtended).accessDetails + delete (tmpAsset as AssetExtended).datatokens + delete (tmpAsset as AssetExtended).stats + delete (tmpAsset as AssetExtended).offchain + + setUpdatedAsset(tmpAsset) + } + + transformValues() + setValuePreview(previewDebugPatch(values)) + }, [asset, values]) return ( <> diff --git a/src/components/Asset/Edit/DebugEditService.tsx b/src/components/Asset/Edit/DebugEditService.tsx index a64ed3ed5..87a939df6 100644 --- a/src/components/Asset/Edit/DebugEditService.tsx +++ b/src/components/Asset/Edit/DebugEditService.tsx @@ -71,7 +71,7 @@ export default function DebugEditService({ } transformValues() - setValuePreview(previewDebugPatch(values, asset.chainId)) + setValuePreview(previewDebugPatch(values)) }, [values, asset, newCancelToken, service]) return ( diff --git a/src/components/Asset/Edit/EditMetadata.tsx b/src/components/Asset/Edit/EditMetadata.tsx index f46606f25..3b0228126 100644 --- a/src/components/Asset/Edit/EditMetadata.tsx +++ b/src/components/Asset/Edit/EditMetadata.tsx @@ -37,10 +37,7 @@ export default function Edit({ const [error, setError] = useState() const hasFeedback = error || success - async function handleSubmit( - values: Partial, - resetForm: () => void - ) { + async function handleSubmit(values: MetadataEditForm, resetForm: () => void) { try { const linksTransformed = values.links?.length && values.links[0].valid && [sanitizeUrl(values.links[0].url)] diff --git a/src/components/Asset/Edit/FormActions.tsx b/src/components/Asset/Edit/FormActions.tsx index 7a4807d61..eac7c9aec 100644 --- a/src/components/Asset/Edit/FormActions.tsx +++ b/src/components/Asset/Edit/FormActions.tsx @@ -4,7 +4,7 @@ import { useAsset } from '@context/Asset' import Button from '@shared/atoms/Button' import styles from './FormActions.module.css' import Link from 'next/link' -import { ComputeEditForm, MetadataEditForm } from './_types' +import { MetadataEditForm, ServiceEditForm } from './_types' export default function FormActions({ handleClick @@ -12,7 +12,7 @@ export default function FormActions({ handleClick?: () => void }): ReactElement { const { isAssetNetwork, asset } = useAsset() - const { isValid }: FormikContextType = + const { isValid }: FormikContextType = useFormikContext() const isSubmitDisabled = !isValid || !isAssetNetwork diff --git a/src/components/Asset/Edit/_constants.ts b/src/components/Asset/Edit/_constants.ts index 5e02f7f3d..cec41858e 100644 --- a/src/components/Asset/Edit/_constants.ts +++ b/src/components/Asset/Edit/_constants.ts @@ -18,7 +18,7 @@ export function getInitialValues( metadata: Metadata, credentials: Credentials, assetState: string -): Partial { +): MetadataEditForm { return { name: metadata?.name, description: metadata?.description, diff --git a/src/components/Publish/Debug/index.tsx b/src/components/Publish/Debug/index.tsx index ad9f0d4d9..c84e058b2 100644 --- a/src/components/Publish/Debug/index.tsx +++ b/src/components/Publish/Debug/index.tsx @@ -6,18 +6,16 @@ import { transformPublishFormToDdo } from '../_utils' import styles from './index.module.css' import { DDO } from '@oceanprotocol/lib' import { previewDebugPatch } from '@utils/ddo' -import { useNetwork } from 'wagmi' export default function Debug(): ReactElement { const { values } = useFormikContext() const [valuePreview, setValuePreview] = useState({}) const [ddo, setDdo] = useState() - const { chain } = useNetwork() useEffect(() => { async function makeDdo() { const ddo = await transformPublishFormToDdo(values) - setValuePreview(previewDebugPatch(values, chain?.id)) + setValuePreview(previewDebugPatch(values)) setDdo(ddo) } makeDdo() From dfab68af8046a6a15fd086236383f286e39afa0f Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 11 Sep 2024 14:14:14 +0200 Subject: [PATCH 24/31] add asset type in edit metadata just to view --- content/pages/editMetadata.json | 8 ++++ src/components/Asset/Edit/EditMetadata.tsx | 2 +- .../Asset/Edit/FormEditMetadata.tsx | 45 ++++++++++++++----- src/components/Asset/Edit/_constants.ts | 1 + src/components/Asset/Edit/_types.ts | 1 + 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/content/pages/editMetadata.json b/content/pages/editMetadata.json index 6538fd412..6783763d1 100644 --- a/content/pages/editMetadata.json +++ b/content/pages/editMetadata.json @@ -18,6 +18,14 @@ "rows": 10, "required": true }, + { + "name": "type", + "label": "Asset Type", + "type": "boxSelection", + "options": ["Dataset", "Algorithm"], + "disabled": true, + "help": "The asset type cannot be changed once set." + }, { "name": "links", "label": "Sample file", diff --git a/src/components/Asset/Edit/EditMetadata.tsx b/src/components/Asset/Edit/EditMetadata.tsx index 3b0228126..cc3a6a0c4 100644 --- a/src/components/Asset/Edit/EditMetadata.tsx +++ b/src/components/Asset/Edit/EditMetadata.tsx @@ -148,7 +148,7 @@ export default function Edit({ /> ) : ( <> - + () + const { values, setFieldValue } = useFormikContext() + + // BoxSelection component is not a Formik component + // so we need to handle checked state manually. + const assetTypeOptions: BoxSelectionOption[] = [ + { + name: assetTypeOptionsTitles[0].toLowerCase(), + title: assetTypeOptionsTitles[0], + checked: values.type === assetTypeOptionsTitles[0].toLowerCase(), + icon: + }, + { + name: assetTypeOptionsTitles[1].toLowerCase(), + title: assetTypeOptionsTitles[1], + checked: values.type === assetTypeOptionsTitles[1].toLowerCase(), + icon: + } + ] useEffect(() => { - const providerUrl = values?.services - ? values?.services[0].providerUrl.url - : asset.services[0].serviceEndpoint + const providerUrl = asset.services[0].serviceEndpoint // if we have a sample file, we need to get the files' info before setting defaults links value asset?.metadata?.links?.[0] && @@ -51,6 +68,14 @@ export default function FormEditMetadata({ return (
+ + Date: Wed, 11 Sep 2024 14:22:48 +0200 Subject: [PATCH 25/31] update feedback message --- src/components/Asset/Edit/AddService.tsx | 2 +- src/components/Asset/Edit/EditService.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx index 922a89c1e..6bd08d121 100644 --- a/src/components/Asset/Edit/AddService.tsx +++ b/src/components/Asset/Edit/AddService.tsx @@ -221,7 +221,7 @@ export default function AddService({ {({ isSubmitting, values }) => isSubmitting || hasFeedback ? ( isSubmitting || hasFeedback ? ( Date: Wed, 11 Sep 2024 15:58:04 +0200 Subject: [PATCH 26/31] fix boxselection checked prop --- .../@shared/FormInput/InputElement/BoxSelection/index.tsx | 1 + src/components/Asset/Edit/FormEditService.tsx | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/@shared/FormInput/InputElement/BoxSelection/index.tsx b/src/components/@shared/FormInput/InputElement/BoxSelection/index.tsx index bd3476e94..93c951da6 100644 --- a/src/components/@shared/FormInput/InputElement/BoxSelection/index.tsx +++ b/src/components/@shared/FormInput/InputElement/BoxSelection/index.tsx @@ -38,6 +38,7 @@ export default function BoxSelection({ handleChange(event)} {...props} type="radio" diff --git a/src/components/Asset/Edit/FormEditService.tsx b/src/components/Asset/Edit/FormEditService.tsx index 9ae5e4c53..515888b3d 100644 --- a/src/components/Asset/Edit/FormEditService.tsx +++ b/src/components/Asset/Edit/FormEditService.tsx @@ -46,10 +46,6 @@ export default function FormEditService({ } ] - const handleAccessChange = (value: string) => { - setFieldValue('access', value) - } - return ( @@ -65,7 +61,6 @@ export default function FormEditService({ component={Input} name="access" options={accessTypeOptions} - onChange={handleAccessChange} // because BoxSelection component is not a Formik component and we have could have multiple Formiks on 1 page disabled={true} /> From a4cb12b1a6975a5de44df8d8cff841033ec5c2a6 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Wed, 11 Sep 2024 16:18:10 +0200 Subject: [PATCH 27/31] update logs --- src/components/Asset/AssetContent/ServiceCard.tsx | 2 ++ src/components/Asset/Edit/AddService.tsx | 14 +++++++------- src/components/Asset/Edit/EditService.tsx | 4 ---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/components/Asset/AssetContent/ServiceCard.tsx b/src/components/Asset/AssetContent/ServiceCard.tsx index 9d596ddb4..3fd2afbe7 100644 --- a/src/components/Asset/AssetContent/ServiceCard.tsx +++ b/src/components/Asset/AssetContent/ServiceCard.tsx @@ -11,6 +11,8 @@ export default function ServiceCard({ accessDetails: AccessDetails onClick: () => void }): ReactElement { + if (!accessDetails) return null + return (
Name: diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx index 6bd08d121..ec5531434 100644 --- a/src/components/Asset/Edit/AddService.tsx +++ b/src/components/Asset/Edit/AddService.tsx @@ -53,7 +53,6 @@ export default function AddService({ // add new service async function handleSubmit(values: ServiceEditForm, resetForm: () => void) { - console.log('values', values) try { if (!isAssetNetwork) { setError('Please switch to the correct network.') @@ -79,7 +78,7 @@ export default function AddService({ 1 ) - console.log('Datatoken created.', datatokenAddress) + LoggerInstance.log('Datatoken created.', datatokenAddress) // -------------------------------------------------- // 2. Create Pricing @@ -88,7 +87,7 @@ export default function AddService({ let pricingTransactionReceipt if (values.price > 0) { - console.log( + LoggerInstance.log( `Creating fixed rate exchange with price ${values.price} for datatoken ${datatokenAddress}` ) @@ -112,7 +111,9 @@ export default function AddService({ freParams ) } else { - console.log(`Creating dispenser for datatoken ${datatokenAddress}`) + LoggerInstance.log( + `Creating dispenser for datatoken ${datatokenAddress}` + ) const dispenserParams: DispenserParams = { maxTokens: ethers.utils.parseEther('1').toString(), @@ -128,15 +129,14 @@ export default function AddService({ ) } - const tx = await pricingTransactionReceipt.wait() - console.log('Pricing scheme created.') + await pricingTransactionReceipt.wait() + LoggerInstance.log('Pricing scheme created.') // -------------------------------------------------- // 2. Update DDO // -------------------------------------------------- let newFiles = asset.services[0].files // by default it could be the same file as in other services if (values.files[0]?.url) { - console.log('updating files') const file = { nftAddress: asset.nftAddress, datatokenAddress, diff --git a/src/components/Asset/Edit/EditService.tsx b/src/components/Asset/Edit/EditService.tsx index 47fd8dc9e..5919f6d4b 100644 --- a/src/components/Asset/Edit/EditService.tsx +++ b/src/components/Asset/Edit/EditService.tsx @@ -49,7 +49,6 @@ export default function EditService({ const hasFeedback = error || success async function updateFixedPrice(newPrice: number) { - console.log('updateFixedPrice') const config = getOceanConfig(asset.chainId) const fixedRateInstance = new FixedRateExchange( @@ -70,7 +69,6 @@ export default function EditService({ // edit 1 service async function handleSubmit(values: ServiceEditForm, resetForm: () => void) { - console.log('values', values) try { // update fixed price if changed accessDetails.type === 'fixed' && @@ -79,7 +77,6 @@ export default function EditService({ // update payment collector if changed if (values.paymentCollector !== accessDetails.paymentCollector) { - console.log('setting payment collector') const datatoken = new Datatoken(signer) await datatoken.setPaymentCollector( service.datatokenAddress, @@ -90,7 +87,6 @@ export default function EditService({ let updatedFiles = service.files if (values.files[0]?.url) { - console.log('updating files') const file = { nftAddress: asset.nftAddress, datatokenAddress: service.datatokenAddress, From 4536b36c64bbaf577ec7b9bf17b562e84e4dda57 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Mon, 16 Sep 2024 10:23:52 +0200 Subject: [PATCH 28/31] get addresses from config instead of process.env --- src/components/Asset/Edit/AddService.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx index ec5531434..8d3b4ae21 100644 --- a/src/components/Asset/Edit/AddService.tsx +++ b/src/components/Asset/Edit/AddService.tsx @@ -33,6 +33,7 @@ import { serviceValidationSchema } from './_validation' import DebugEditService from './DebugEditService' import styles from './index.module.css' import { useUserPreferences } from '@context/UserPreferences' +import { getOceanConfig } from '@utils/ocean' export default function AddService({ asset @@ -46,6 +47,7 @@ export default function AddService({ const { data: signer } = useSigner() const newAbortController = useAbortController() const newCancelToken = useCancelToken() + const config = getOceanConfig(asset?.chainId) const [success, setSuccess] = useState() const [error, setError] = useState() @@ -70,7 +72,7 @@ export default function AddService({ accountId, values.paymentCollector, marketFeeAddress, - process.env.NEXT_PUBLIC_OCEAN_TOKEN_ADDRESS, + config.oceanTokenAddress, publisherMarketFixedSwapFee, '100000000', 'DataToken', @@ -92,8 +94,8 @@ export default function AddService({ ) const freParams: FreCreationParams = { - fixedRateAddress: process.env.NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS, - baseTokenAddress: process.env.NEXT_PUBLIC_OCEAN_TOKEN_ADDRESS, + fixedRateAddress: config.fixedRateExchangeAddress, + baseTokenAddress: config.oceanTokenAddress, owner: accountId, marketFeeCollector: marketFeeAddress, baseTokenDecimals: 18, @@ -124,7 +126,7 @@ export default function AddService({ pricingTransactionReceipt = await datatoken.createDispenser( datatokenAddress, accountId, - process.env.NEXT_PUBLIC_DISPENSER_ADDRESS, + config.dispenserAddress, dispenserParams ) } From 2eae8b413ac8fced41c7c9ef265c18a960a331d1 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Mon, 16 Sep 2024 13:21:46 +0200 Subject: [PATCH 29/31] update styles --- src/components/Asset/Edit/EditServices.tsx | 10 ++-------- src/components/Asset/Edit/FormAddService.tsx | 3 ++- .../Asset/Edit/FormEditMetadata.module.css | 15 --------------- src/components/Asset/Edit/FormEditService.tsx | 3 ++- src/components/Asset/Edit/index.module.css | 11 +++++++++++ 5 files changed, 17 insertions(+), 25 deletions(-) delete mode 100644 src/components/Asset/Edit/FormEditMetadata.module.css diff --git a/src/components/Asset/Edit/EditServices.tsx b/src/components/Asset/Edit/EditServices.tsx index 0f868bf62..ab9344830 100644 --- a/src/components/Asset/Edit/EditServices.tsx +++ b/src/components/Asset/Edit/EditServices.tsx @@ -29,15 +29,9 @@ export default function EditServices({ {selectedService !== undefined && ( <> -
+
-
+

{selectedService === -1 ? 'Add a new service' diff --git a/src/components/Asset/Edit/FormAddService.tsx b/src/components/Asset/Edit/FormAddService.tsx index 6b02ca9d6..82cb9d53a 100644 --- a/src/components/Asset/Edit/FormAddService.tsx +++ b/src/components/Asset/Edit/FormAddService.tsx @@ -9,6 +9,7 @@ import IconDownload from '@images/download.svg' import IconCompute from '@images/compute.svg' import FormEditComputeService from './FormEditComputeService' import { defaultServiceComputeOptions } from './_constants' +import styles from './index.module.css' export default function FormAddService({ data, @@ -41,7 +42,7 @@ export default function FormAddService({ ] return ( - + div { - margin-bottom: calc(var(--spacer) / 2); -} diff --git a/src/components/Asset/Edit/FormEditService.tsx b/src/components/Asset/Edit/FormEditService.tsx index 515888b3d..7a4ab120a 100644 --- a/src/components/Asset/Edit/FormEditService.tsx +++ b/src/components/Asset/Edit/FormEditService.tsx @@ -10,6 +10,7 @@ import IconDownload from '@images/download.svg' import IconCompute from '@images/compute.svg' import FormEditComputeService from './FormEditComputeService' import { defaultServiceComputeOptions } from './_constants' +import styles from './index.module.css' export default function FormEditService({ data, @@ -47,7 +48,7 @@ export default function FormEditService({ ] return ( - + Date: Mon, 16 Sep 2024 13:25:59 +0200 Subject: [PATCH 30/31] add try catch --- .../Asset/Edit/DebugEditService.tsx | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/components/Asset/Edit/DebugEditService.tsx b/src/components/Asset/Edit/DebugEditService.tsx index 87a939df6..827c9dd6a 100644 --- a/src/components/Asset/Edit/DebugEditService.tsx +++ b/src/components/Asset/Edit/DebugEditService.tsx @@ -1,4 +1,4 @@ -import { Asset, Service } from '@oceanprotocol/lib' +import { Asset, LoggerInstance, Service } from '@oceanprotocol/lib' import { ReactElement, useEffect, useState } from 'react' import DebugOutput from '@shared/DebugOutput' import { useCancelToken } from '@hooks/useCancelToken' @@ -28,21 +28,29 @@ export default function DebugEditService({ useEffect(() => { async function transformValues() { let updatedFiles = service.files - if (values.files[0]?.url) { - const file = { - nftAddress: asset.nftAddress, - datatokenAddress: service.datatokenAddress, - files: [ - normalizeFile(values.files[0].type, values.files[0], asset.chainId) - ] - } + try { + if (values.files[0]?.url) { + const file = { + nftAddress: asset.nftAddress, + datatokenAddress: service.datatokenAddress, + files: [ + normalizeFile( + values.files[0].type, + values.files[0], + asset.chainId + ) + ] + } - const filesEncrypted = await getEncryptedFiles( - file, - asset.chainId, - service.serviceEndpoint - ) - updatedFiles = filesEncrypted + const filesEncrypted = await getEncryptedFiles( + file, + asset.chainId, + service.serviceEndpoint + ) + updatedFiles = filesEncrypted + } + } catch (error) { + LoggerInstance.error('Error encrypting files:', error.message) } const updatedService: Service = { From b5047b2aa9f624832dcacb801b0853dcb8c262a9 Mon Sep 17 00:00:00 2001 From: Filip Masar Date: Mon, 16 Sep 2024 15:47:30 +0200 Subject: [PATCH 31/31] add datatoken cap to app.config --- app.config.js | 2 ++ src/components/Asset/Edit/AddService.tsx | 8 ++++++-- src/components/Publish/_utils.ts | 5 +++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app.config.js b/app.config.js index c29bbcad3..aff7f569a 100644 --- a/app.config.js +++ b/app.config.js @@ -20,6 +20,8 @@ module.exports = { infuraProjectId: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID || 'xxx', + defaultDatatokenCap: + '115792089237316195423570985008687907853269984665640564039457', defaultDatatokenTemplateIndex: 2, // The ETH address the marketplace fee will be sent to. marketFeeAddress: diff --git a/src/components/Asset/Edit/AddService.tsx b/src/components/Asset/Edit/AddService.tsx index 8d3b4ae21..537f9781b 100644 --- a/src/components/Asset/Edit/AddService.tsx +++ b/src/components/Asset/Edit/AddService.tsx @@ -24,7 +24,11 @@ import { setNftMetadata } from '@utils/nft' import { getEncryptedFiles } from '@utils/provider' import { useAccount, useNetwork, useSigner } from 'wagmi' import { transformConsumerParameters } from '@components/Publish/_utils' -import { marketFeeAddress, publisherMarketFixedSwapFee } from 'app.config' +import { + defaultDatatokenCap, + marketFeeAddress, + publisherMarketFixedSwapFee +} from 'app.config' import { ethers } from 'ethers' import FormAddService from './FormAddService' import { transformComputeFormToServiceComputeOptions } from '@utils/compute' @@ -74,7 +78,7 @@ export default function AddService({ marketFeeAddress, config.oceanTokenAddress, publisherMarketFixedSwapFee, - '100000000', + defaultDatatokenCap, 'DataToken', 'DT', 1 diff --git a/src/components/Publish/_utils.ts b/src/components/Publish/_utils.ts index 8cd79dede..581305b07 100644 --- a/src/components/Publish/_utils.ts +++ b/src/components/Publish/_utils.ts @@ -32,7 +32,8 @@ import { publisherMarketFixedSwapFee, defaultDatatokenTemplateIndex, customProviderUrl, - defaultAccessTerms + defaultAccessTerms, + defaultDatatokenCap } from '../../../app.config' import { sanitizeUrl } from '@utils/url' import { getContainerChecksum } from '@utils/docker' @@ -292,7 +293,7 @@ export async function createTokensAndPricing( values.pricing.baseToken.address, feeAmount: publisherMarketOrderFee, // max number - cap: '115792089237316195423570985008687907853269984665640564039457', + cap: defaultDatatokenCap, name: values.services[0].dataTokenOptions.name, symbol: values.services[0].dataTokenOptions.symbol }