diff --git a/content/pages/editMetadata.json b/content/pages/editMetadata.json index b70bd3737..15da8b4d6 100644 --- a/content/pages/editMetadata.json +++ b/content/pages/editMetadata.json @@ -66,6 +66,13 @@ "type": "tags", "placeholder": "e.g. logistics", "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 } ] } diff --git a/src/components/Asset/AssetContent/MetaFull.tsx b/src/components/Asset/AssetContent/MetaFull.tsx index d6dabd1d1..f01d71915 100644 --- a/src/components/Asset/AssetContent/MetaFull.tsx +++ b/src/components/Asset/AssetContent/MetaFull.tsx @@ -1,12 +1,29 @@ -import React, { ReactElement } from 'react' +import React, { ReactElement, useState, useEffect } from 'react' import MetaItem from './MetaItem' import styles from './MetaFull.module.css' import Publisher from '@shared/Publisher' import { useAsset } from '@context/Asset' -import { Asset } from '@oceanprotocol/lib' +import { useWeb3 } from '@context/Web3' +import { Asset, Datatoken, LoggerInstance } from '@oceanprotocol/lib' export default function MetaFull({ ddo }: { ddo: Asset }): ReactElement { + const [paymentCollector, setPaymentCollector] = useState() const { isInPurgatory } = useAsset() + const { web3 } = useWeb3() + + useEffect(() => { + async function getInitialPaymentCollector() { + try { + const datatoken = new Datatoken(web3) + setPaymentCollector( + await datatoken.getPaymentCollector(ddo.datatokens[0].address) + ) + } catch (error) { + LoggerInstance.error('[MetaFull: getInitialPaymentCollector]', error) + } + } + getInitialPaymentCollector() + }, [ddo, web3]) function DockerImage() { const containerInfo = ddo?.metadata?.algorithm?.container @@ -23,6 +40,12 @@ export default function MetaFull({ ddo }: { ddo: Asset }): ReactElement { title="Owner" content={} /> + {paymentCollector && paymentCollector !== ddo?.nft?.owner && ( + } + /> + )} {ddo?.metadata?.type === 'algorithm' && ddo?.metadata?.algorithm && ( } /> diff --git a/src/components/Asset/Edit/EditMetadata.tsx b/src/components/Asset/Edit/EditMetadata.tsx index fa42718de..6ba94e35f 100644 --- a/src/components/Asset/Edit/EditMetadata.tsx +++ b/src/components/Asset/Edit/EditMetadata.tsx @@ -1,11 +1,12 @@ -import React, { ReactElement, useState } from 'react' +import React, { ReactElement, useState, useEffect } from 'react' import { Formik } from 'formik' import { LoggerInstance, Metadata, FixedRateExchange, Asset, - Service + Service, + Datatoken } from '@oceanprotocol/lib' import { validationSchema } from './_validation' import { getInitialValues } from './_constants' @@ -36,10 +37,28 @@ export default function Edit({ const { accountId, web3 } = useWeb3() 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(() => { + async function getInitialPaymentCollector() { + try { + const datatoken = new Datatoken(web3) + setPaymentCollector( + await datatoken.getPaymentCollector(asset?.datatokens[0].address) + ) + } catch (error) { + LoggerInstance.error( + '[EditMetadata: getInitialPaymentCollector]', + error + ) + } + } + getInitialPaymentCollector() + }, [asset, web3]) + async function updateFixedPrice(newPrice: string) { const config = getOceanConfig(asset.chainId) @@ -81,6 +100,15 @@ export default function Edit({ values.price !== asset.accessDetails.price && (await updateFixedPrice(values.price)) + if (values.paymentCollector !== paymentCollector) { + const datatoken = new Datatoken(web3) + await datatoken.setPaymentCollector( + asset?.datatokens[0].address, + accountId, + values.paymentCollector + ) + } + if (values.files[0]?.url) { const file = { nftAddress: asset.nftAddress, @@ -147,7 +175,8 @@ export default function Edit({ initialValues={getInitialValues( asset?.metadata, asset?.services[0]?.timeout, - asset?.accessDetails?.price + asset?.accessDetails?.price, + paymentCollector )} validationSchema={validationSchema} onSubmit={async (values, { resetForm }) => { diff --git a/src/components/Asset/Edit/FormActions.test.tsx b/src/components/Asset/Edit/FormActions.test.tsx new file mode 100644 index 000000000..0f3899d7b --- /dev/null +++ b/src/components/Asset/Edit/FormActions.test.tsx @@ -0,0 +1,15 @@ +import { render, screen } from '@testing-library/react' +import React from 'react' +import { useFormikContext } from 'formik' +import FormActions from './FormActions' + +jest.mock('formik') + +describe('src/components/Asset/Edit/FormActions.tsx', () => { + it('renders fixed price', () => { + const isValid = true + ;(useFormikContext as jest.Mock).mockReturnValue([isValid]) + render() + expect(screen.getByText('Submit')).toBeInTheDocument() + }) +}) diff --git a/src/components/Asset/Edit/FormEditMetadata.tsx b/src/components/Asset/Edit/FormEditMetadata.tsx index 6bcfc8e43..4349ef9bf 100644 --- a/src/components/Asset/Edit/FormEditMetadata.tsx +++ b/src/components/Asset/Edit/FormEditMetadata.tsx @@ -1,5 +1,5 @@ import React, { ReactElement, useEffect } from 'react' -import { Field, Form, useField, useFormikContext } from 'formik' +import { Field, Form, useFormikContext } from 'formik' import Input, { InputProps } from '@shared/FormInput' import FormActions from './FormActions' import { useAsset } from '@context/Asset' diff --git a/src/components/Asset/Edit/_constants.ts b/src/components/Asset/Edit/_constants.ts index 3a1031d41..99e7b3cbd 100644 --- a/src/components/Asset/Edit/_constants.ts +++ b/src/components/Asset/Edit/_constants.ts @@ -5,7 +5,8 @@ import { ComputeEditForm, MetadataEditForm } from './_types' export function getInitialValues( metadata: Metadata, timeout: number, - price: string + price: string, + paymentCollector: string ): Partial { return { name: metadata?.name, @@ -15,7 +16,8 @@ export function getInitialValues( files: [{ url: '', type: '' }], timeout: secondsToString(timeout), author: metadata?.author, - tags: metadata?.tags + tags: metadata?.tags, + paymentCollector } } diff --git a/src/components/Asset/Edit/_types.ts b/src/components/Asset/Edit/_types.ts index f5a972cb6..923aabb5c 100644 --- a/src/components/Asset/Edit/_types.ts +++ b/src/components/Asset/Edit/_types.ts @@ -3,6 +3,7 @@ export interface MetadataEditForm { name: string description: string timeout: string + paymentCollector: string price?: string files: FileInfo[] links?: FileInfo[] diff --git a/src/components/Asset/Edit/_validation.ts b/src/components/Asset/Edit/_validation.ts index 8d81768fe..afc256153 100644 --- a/src/components/Asset/Edit/_validation.ts +++ b/src/components/Asset/Edit/_validation.ts @@ -1,5 +1,6 @@ import { FileInfo } from '@oceanprotocol/lib' import * as Yup from 'yup' +import web3 from 'web3' export const validationSchema = Yup.object().shape({ name: Yup.string() @@ -41,7 +42,14 @@ export const validationSchema = Yup.object().shape({ .nullable(), timeout: Yup.string().required('Required'), author: Yup.string().nullable(), - tags: Yup.array().nullable() + tags: Yup.array().nullable(), + paymentCollector: Yup.string().test( + 'ValidAddress', + 'Must be a valid Ethereum Address.', + (value) => { + return web3.utils.isAddress(value) + } + ) }) export const computeSettingsValidationSchema = Yup.object().shape({