From e65abc561e813662902820494ad9dd0b03a9d99d Mon Sep 17 00:00:00 2001 From: Trent McConaghy <5305452+trentmc@users.noreply.github.com> Date: Thu, 24 Feb 2022 10:17:36 +0100 Subject: [PATCH 1/5] 2021 -> 2022 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eba0edf33..163863524 100644 --- a/README.md +++ b/README.md @@ -406,7 +406,7 @@ The style can be changed by altering the `style` prop in the `PrivacyPreferenceC ## 🏛 License ```text -Copyright 2021 Ocean Protocol Foundation Ltd. +Copyright 2022 Ocean Protocol Foundation Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 8599835be44ad5e1e4fc9d06c32cd984d6401c5e Mon Sep 17 00:00:00 2001 From: EnzoVezzaro Date: Thu, 24 Feb 2022 16:17:54 +0100 Subject: [PATCH 2/5] remove unnecessary appearance (#1142) --- src/components/@shared/FormInput/InputElement.module.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/@shared/FormInput/InputElement.module.css b/src/components/@shared/FormInput/InputElement.module.css index 233643a59..6f541dccd 100644 --- a/src/components/@shared/FormInput/InputElement.module.css +++ b/src/components/@shared/FormInput/InputElement.module.css @@ -38,8 +38,6 @@ color: var(--brand-grey-light); cursor: not-allowed; pointer-events: none; - /* for hiding spin buttons in Firefox */ - -moz-appearance: textfield; } .input[readonly]::-webkit-inner-spin-button, From ed9b1ce2d2419d507e84a59cbcca27fedc4de064 Mon Sep 17 00:00:00 2001 From: Norbi <37236152+KatunaNorbert@users.noreply.github.com> Date: Thu, 24 Feb 2022 17:21:45 +0200 Subject: [PATCH 3/5] Update fee display inside publish form (#1128) * use fixed and pool swap fees from app config inside publish form price * get opc fees from subgraph * fixed undefined opcFees * fixed get opc fees query * removed logs, unused imports and added types * remove unused import * fetch opc fees from wallet network * use fallback chainId and add chainId to dependencies array * get app config from site metadata * fixed getOpcFees typo * changed community fee field value --- src/@utils/subgraph.ts | 32 +++++++++++++++++++++++++ src/components/Publish/Pricing/Fees.tsx | 27 ++++++++++++++++++--- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/@utils/subgraph.ts b/src/@utils/subgraph.ts index 71095e067..ec6048ba0 100644 --- a/src/@utils/subgraph.ts +++ b/src/@utils/subgraph.ts @@ -17,6 +17,7 @@ import { OrdersData_orders_datatoken as OrdersDatatoken } from '../@types/subgraph/OrdersData' import { UserSalesQuery as UsersSalesList } from '../@types/subgraph/UserSalesQuery' +import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery' export interface UserLiquidity { price: string @@ -188,6 +189,17 @@ const TopSalesQuery = gql` } ` +const OpcFeesQuery = gql` + query OpcFeesQuery($id: ID!) { + opc(id: $id) { + swapOceanFee + swapNonOceanFee + consumeFee + providerFee + } + } +` + export function getSubgraphUri(chainId: number): string { const config = getOceanConfig(chainId) return config.subgraphUri @@ -242,6 +254,26 @@ export async function fetchDataForMultipleChains( return datas } +export async function getOpcFees(chainId: number) { + let opcFees + const variables = { + id: 1 + } + const context = getQueryContext(chainId) + try { + const response: OperationResult = await fetchData( + OpcFeesQuery, + variables, + context + ) + opcFees = response?.data?.opc + } catch (error) { + console.error('Error fetchData: ', error.message) + throw Error(error.message) + } + return opcFees +} + export async function getPreviousOrders( id: string, account: string, diff --git a/src/components/Publish/Pricing/Fees.tsx b/src/components/Publish/Pricing/Fees.tsx index 88a7bc06c..3936dbcbf 100644 --- a/src/components/Publish/Pricing/Fees.tsx +++ b/src/components/Publish/Pricing/Fees.tsx @@ -1,18 +1,24 @@ -import React, { ReactElement } from 'react' +import React, { ReactElement, useEffect, useState } from 'react' import Tooltip from '@shared/atoms/Tooltip' import styles from './Fees.module.css' import { useField } from 'formik' import Input from '@shared/FormInput' import Error from './Error' +import { getOpcFees } from '../../../@utils/subgraph' +import { OpcFeesQuery_opc as OpcFeesData } from '../../../@types/subgraph/OpcFeesQuery' +import { useWeb3 } from '@context/Web3' +import { useSiteMetadata } from '@hooks/useSiteMetadata' const Default = ({ title, name, - tooltip + tooltip, + value }: { title: string name: string tooltip: string + value: string }) => ( } - value="0.1" + value={value} name={name} postfix="%" readOnly @@ -37,6 +43,15 @@ export default function Fees({ pricingType: 'dynamic' | 'fixed' }): ReactElement { const [field, meta] = useField('pricing.swapFee') + const [opcFees, setOpcFees] = useState(undefined) + const { chainId } = useWeb3() + const { appConfig } = useSiteMetadata() + + useEffect(() => { + getOpcFees(chainId || 1).then((response: OpcFeesData) => { + setOpcFees(response) + }) + }, [chainId]) return ( <> @@ -64,12 +79,18 @@ export default function Fees({ title="Community Fee" name="communityFee" tooltip={tooltips.communityFee} + value={opcFees?.swapOceanFee || '0'} /> From 73f3080daf10e36ab83da2f15895f372fc628925 Mon Sep 17 00:00:00 2001 From: Bogdan Fazakas Date: Fri, 25 Feb 2022 12:33:07 +0200 Subject: [PATCH 4/5] Restore edit functionality (#1107) * add content on edit pages * display edit for user only * add form actions * add restore part of edit metadata logic * adjust edit metadata * wip edit compute settings * added console logs * wip edit compute * updated edit compute flow * updated style * fix pricing various fixes * fix edit acctions * add debug on edit compute tab * add debug on edit metadata tab * more fixes * lint fixes * add pricing to edit metada * restore timout edit * protect edit route * small fixes * fixes and add edit feetback, loading, error, succes on submit * timeout init values fix * added setNftMetadata helper * moved transfor asset to assetSelection from aquarius class * fixed links, removed dispenser hacks * fixed sample Co-authored-by: mihaisc --- package-lock.json | 206 ++++++----------- src/@utils/aquarius.ts | 68 +----- src/@utils/assetConvertor.ts | 43 ++++ src/@utils/compute.ts | 87 ++++--- src/@utils/dispenser.ts | 48 ++++ src/@utils/nft.ts | 57 +++++ .../@shared/FormFields/FilesInput/index.tsx | 51 +++-- .../Asset/AssetActions/Compute/index.tsx | 9 +- src/components/Asset/AssetContent/index.tsx | 47 ++-- .../Asset/Edit/DebugEditCompute.tsx | 24 +- .../Asset/Edit/DebugEditMetadata.tsx | 39 ++++ .../Asset/Edit/EditComputeDataset.tsx | 189 ++++++++++------ .../Asset/Edit/EditFeedback.module.css | 43 ++++ src/components/Asset/Edit/EditFeedback.tsx | 94 ++++++++ src/components/Asset/Edit/EditMetadata.tsx | 178 +++++++++++++++ ...etadata.module.css => FormEdit.module.css} | 0 .../Asset/Edit/FormEditComputeDataset.tsx | 25 +-- .../Asset/Edit/FormEditMetadata.tsx | 107 +++++---- src/components/Asset/Edit/_constants.ts | 55 ++--- src/components/Asset/Edit/_types.ts | 4 +- src/components/Asset/Edit/index.module.css | 33 ++- src/components/Asset/Edit/index.tsx | 212 +++++------------- src/components/Publish/index.tsx | 21 +- src/pages/asset/[did]/edit.tsx | 14 ++ .../asset/{[did].tsx => [did]/index.tsx} | 2 +- 25 files changed, 1034 insertions(+), 622 deletions(-) create mode 100644 src/@utils/assetConvertor.ts create mode 100644 src/@utils/dispenser.ts create mode 100644 src/components/Asset/Edit/DebugEditMetadata.tsx create mode 100644 src/components/Asset/Edit/EditFeedback.module.css create mode 100644 src/components/Asset/Edit/EditFeedback.tsx create mode 100644 src/components/Asset/Edit/EditMetadata.tsx rename src/components/Asset/Edit/{FormEditMetadata.module.css => FormEdit.module.css} (100%) create mode 100644 src/pages/asset/[did]/edit.tsx rename src/pages/asset/{[did].tsx => [did]/index.tsx} (85%) diff --git a/package-lock.json b/package-lock.json index f174e2fc3..0015789b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5003,9 +5003,9 @@ } }, "node_modules/@truffle/abi-utils": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@truffle/abi-utils/-/abi-utils-0.2.8.tgz", - "integrity": "sha512-n/9ojMthWuP1hg9JveuxctxWoRoKTh+/17RTSwlTlXTyP/kfoNxr1JcLecybXUDOIsLJ4uskqxVUZQDvj1B0PQ==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@truffle/abi-utils/-/abi-utils-0.2.9.tgz", + "integrity": "sha512-Nv4MGsA2vdI7G34nI0DfR/eSd5pbAUu+5EafYNqzgrS46y0LWhbIrSZ1NcM7cbhIrkpUn6OfNk49AjNM67TkSg==", "dependencies": { "change-case": "3.0.2", "faker": "^5.3.1", @@ -5018,12 +5018,12 @@ "integrity": "sha512-9mzYXPQkjOc23rHQM1i630i3ackITWP1cxf3PvBObaAnGqwPCQuqtmZtNDPdvN+YpOLpBGpZIdYolI91xLdJNQ==" }, "node_modules/@truffle/codec": { - "version": "0.11.26", - "resolved": "https://registry.npmjs.org/@truffle/codec/-/codec-0.11.26.tgz", - "integrity": "sha512-wNVw66vSL5RSzgNMkiQVIExz7ZF1ZDNdOSPTnFlTiZCvkpJn8QJ9OpDiow9w8HmXIftt+YjueBsKu4ByyBuAPA==", + "version": "0.11.27", + "resolved": "https://registry.npmjs.org/@truffle/codec/-/codec-0.11.27.tgz", + "integrity": "sha512-zPlbrGSZ975jscoJ4NhQpaJGwJXkasnpSoUAEjzppr6FCLKtutxssy6yfz4EUHaQDTg1SqxlVBfBhqYcrCyjvw==", "dependencies": { - "@truffle/abi-utils": "^0.2.8", - "@truffle/compile-common": "^0.7.27", + "@truffle/abi-utils": "^0.2.9", + "@truffle/compile-common": "^0.7.28", "big.js": "^5.2.2", "bn.js": "^5.1.3", "cbor": "^5.1.0", @@ -5094,23 +5094,23 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/@truffle/compile-common": { - "version": "0.7.27", - "resolved": "https://registry.npmjs.org/@truffle/compile-common/-/compile-common-0.7.27.tgz", - "integrity": "sha512-gs266BQTRnVoskoWj7wNrNGACMOhU6Yt+8YhaSp6k5e4/a2NBEluxXWxKIi32yWz4q7Ip306do1wcQR2fFOf8w==", + "version": "0.7.28", + "resolved": "https://registry.npmjs.org/@truffle/compile-common/-/compile-common-0.7.28.tgz", + "integrity": "sha512-mZCEQ6fkOqbKYCJDT82q0vZCxOEsKRQ0zrPfKuSJEb0gF9DXIQcnMkyJpBSWzmyvien9/A7/jPiGQoC7PmNEUg==", "dependencies": { "@truffle/error": "^0.1.0", "colors": "1.4.0" } }, "node_modules/@truffle/contract": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/@truffle/contract/-/contract-4.4.8.tgz", - "integrity": "sha512-zZVgIJ4zbdKX1dWubX5m9w7StMpcdEXuQorSpPQ23SUD1RwzSgnRi9Iw2rmSLNbHcO+w6L6+JFeVzFrfmI/VKA==", + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@truffle/contract/-/contract-4.4.9.tgz", + "integrity": "sha512-LSIxnpFDr824wUp4tiw2UHmDPr8io5UnzjlZX/QKuNIq+BB+JPRmkzItjwtvWzzIG3QOAGiqjtIgp1U6Mab/bw==", "dependencies": { "@ensdomains/ensjs": "^2.0.1", "@truffle/blockchain-utils": "^0.1.0", - "@truffle/contract-schema": "^3.4.4", - "@truffle/debug-utils": "^6.0.8", + "@truffle/contract-schema": "^3.4.5", + "@truffle/debug-utils": "^6.0.9", "@truffle/error": "^0.1.0", "@truffle/interface-adapter": "^0.5.11", "bignumber.js": "^7.2.1", @@ -5124,9 +5124,9 @@ } }, "node_modules/@truffle/contract-schema": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.4.4.tgz", - "integrity": "sha512-xWgrm6WRM2jmT04w7dP7aVbS2qyP9XPmH/mybQtFXMjJ/8BZlp0yltC8QOs8sGl6q8Ws7acp19YtRkLdK6SsmQ==", + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.4.5.tgz", + "integrity": "sha512-heaGV9QWqef259HaF+0is/tsmhlZIbUSWhqvj0iwKmxoN92fghKijWwdVYhPIbsmGlrQuwPTZHSCnaOlO+gsFg==", "dependencies": { "ajv": "^6.10.0", "debug": "^4.3.1" @@ -5455,16 +5455,16 @@ } }, "node_modules/@truffle/debug-utils": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/@truffle/debug-utils/-/debug-utils-6.0.8.tgz", - "integrity": "sha512-bkrHQZj6OVw5FCs0li69e0f6gdW+6FuOR8dgedfy8qpsVVtGQB+8cN7hnoFUwMs60vo7vW90grZu1c4855zVQw==", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@truffle/debug-utils/-/debug-utils-6.0.9.tgz", + "integrity": "sha512-CVKVsbEWE0TzmTdGcxEeQvNhTKxkojIRox3LWh2Z8ZARtPiWwYl/lmPqfugh1665xmc2NVLI9qyP4ygVFPkvIQ==", "dependencies": { - "@truffle/codec": "^0.11.26", - "@trufflesuite/chromafi": "^2.2.2", + "@truffle/codec": "^0.11.27", + "@trufflesuite/chromafi": "^3.0.0", "bn.js": "^5.1.3", "chalk": "^2.4.2", "debug": "^4.3.1", - "highlightjs-solidity": "^2.0.3" + "highlightjs-solidity": "^2.0.4" } }, "node_modules/@truffle/debug-utils/node_modules/bn.js": { @@ -5959,24 +5959,18 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/@trufflesuite/chromafi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz", - "integrity": "sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@trufflesuite/chromafi/-/chromafi-3.0.0.tgz", + "integrity": "sha512-oqWcOqn8nT1bwlPPfidfzS55vqcIDdpfzo3HbU9EnUmcSTX+I8z0UyUFI3tZQjByVJulbzxHxUGS3ZJPwK/GPQ==", "dependencies": { - "ansi-mark": "^1.0.0", - "ansi-regex": "^3.0.0", - "array-uniq": "^1.0.3", "camelcase": "^4.1.0", "chalk": "^2.3.2", "cheerio": "^1.0.0-rc.2", "detect-indent": "^5.0.0", - "he": "^1.1.1", "highlight.js": "^10.4.1", "lodash.merge": "^4.6.2", - "min-indent": "^1.0.0", "strip-ansi": "^4.0.0", - "strip-indent": "^2.0.0", - "super-split": "^1.1.0" + "strip-indent": "^2.0.0" } }, "node_modules/@trufflesuite/chromafi/node_modules/camelcase": { @@ -7083,18 +7077,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-mark": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/ansi-mark/-/ansi-mark-1.0.4.tgz", - "integrity": "sha1-HNS6jVfxXxCdaq9uycqXhsik7mw=", - "dependencies": { - "ansi-regex": "^3.0.0", - "array-uniq": "^1.0.3", - "chalk": "^2.3.2", - "strip-ansi": "^4.0.0", - "super-split": "^1.1.0" - } - }, "node_modules/ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -8355,14 +8337,6 @@ "node": ">=8" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -14723,6 +14697,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "peer": true, "bin": { "he": "bin/he" } @@ -17365,14 +17340,6 @@ "dom-walk": "^0.1.0" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "engines": { - "node": ">=4" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -20612,9 +20579,9 @@ ] }, "node_modules/simple-get": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", - "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", "dependencies": { "decompress-response": "^3.3.0", "once": "^1.3.1", @@ -21258,11 +21225,6 @@ } } }, - "node_modules/super-split": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/super-split/-/super-split-1.1.0.tgz", - "integrity": "sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ==" - }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -26299,7 +26261,6 @@ "cross-fetch": "^3.1.5", "crypto-js": "^4.1.1", "decimal.js": "^10.3.1", - "web3": "^1.7.0", "web3-core": "^1.7.0", "web3-eth-contract": "^1.7.0" } @@ -26402,7 +26363,6 @@ "integrity": "sha512-5vwpq6kbvwkQwKqAoOU3L72GZ3Ta8RRrewKj9OJRolx28KLJJ8Dg9Rf7obRwt5jQA9bkYd8gqzMTrI7H3xLfaw==", "dev": true, "requires": { - "@oclif/config": "^1.15.1", "@oclif/errors": "^1.3.3", "@oclif/parser": "^3.8.3", "@oclif/plugin-help": "^3", @@ -27509,9 +27469,9 @@ } }, "@truffle/abi-utils": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@truffle/abi-utils/-/abi-utils-0.2.8.tgz", - "integrity": "sha512-n/9ojMthWuP1hg9JveuxctxWoRoKTh+/17RTSwlTlXTyP/kfoNxr1JcLecybXUDOIsLJ4uskqxVUZQDvj1B0PQ==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@truffle/abi-utils/-/abi-utils-0.2.9.tgz", + "integrity": "sha512-Nv4MGsA2vdI7G34nI0DfR/eSd5pbAUu+5EafYNqzgrS46y0LWhbIrSZ1NcM7cbhIrkpUn6OfNk49AjNM67TkSg==", "requires": { "change-case": "3.0.2", "faker": "^5.3.1", @@ -27524,12 +27484,12 @@ "integrity": "sha512-9mzYXPQkjOc23rHQM1i630i3ackITWP1cxf3PvBObaAnGqwPCQuqtmZtNDPdvN+YpOLpBGpZIdYolI91xLdJNQ==" }, "@truffle/codec": { - "version": "0.11.26", - "resolved": "https://registry.npmjs.org/@truffle/codec/-/codec-0.11.26.tgz", - "integrity": "sha512-wNVw66vSL5RSzgNMkiQVIExz7ZF1ZDNdOSPTnFlTiZCvkpJn8QJ9OpDiow9w8HmXIftt+YjueBsKu4ByyBuAPA==", + "version": "0.11.27", + "resolved": "https://registry.npmjs.org/@truffle/codec/-/codec-0.11.27.tgz", + "integrity": "sha512-zPlbrGSZ975jscoJ4NhQpaJGwJXkasnpSoUAEjzppr6FCLKtutxssy6yfz4EUHaQDTg1SqxlVBfBhqYcrCyjvw==", "requires": { - "@truffle/abi-utils": "^0.2.8", - "@truffle/compile-common": "^0.7.27", + "@truffle/abi-utils": "^0.2.9", + "@truffle/compile-common": "^0.7.28", "big.js": "^5.2.2", "bn.js": "^5.1.3", "cbor": "^5.1.0", @@ -27597,23 +27557,23 @@ } }, "@truffle/compile-common": { - "version": "0.7.27", - "resolved": "https://registry.npmjs.org/@truffle/compile-common/-/compile-common-0.7.27.tgz", - "integrity": "sha512-gs266BQTRnVoskoWj7wNrNGACMOhU6Yt+8YhaSp6k5e4/a2NBEluxXWxKIi32yWz4q7Ip306do1wcQR2fFOf8w==", + "version": "0.7.28", + "resolved": "https://registry.npmjs.org/@truffle/compile-common/-/compile-common-0.7.28.tgz", + "integrity": "sha512-mZCEQ6fkOqbKYCJDT82q0vZCxOEsKRQ0zrPfKuSJEb0gF9DXIQcnMkyJpBSWzmyvien9/A7/jPiGQoC7PmNEUg==", "requires": { "@truffle/error": "^0.1.0", "colors": "1.4.0" } }, "@truffle/contract": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/@truffle/contract/-/contract-4.4.8.tgz", - "integrity": "sha512-zZVgIJ4zbdKX1dWubX5m9w7StMpcdEXuQorSpPQ23SUD1RwzSgnRi9Iw2rmSLNbHcO+w6L6+JFeVzFrfmI/VKA==", + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@truffle/contract/-/contract-4.4.9.tgz", + "integrity": "sha512-LSIxnpFDr824wUp4tiw2UHmDPr8io5UnzjlZX/QKuNIq+BB+JPRmkzItjwtvWzzIG3QOAGiqjtIgp1U6Mab/bw==", "requires": { "@ensdomains/ensjs": "^2.0.1", "@truffle/blockchain-utils": "^0.1.0", - "@truffle/contract-schema": "^3.4.4", - "@truffle/debug-utils": "^6.0.8", + "@truffle/contract-schema": "^3.4.5", + "@truffle/debug-utils": "^6.0.9", "@truffle/error": "^0.1.0", "@truffle/interface-adapter": "^0.5.11", "bignumber.js": "^7.2.1", @@ -27889,25 +27849,25 @@ } }, "@truffle/contract-schema": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.4.4.tgz", - "integrity": "sha512-xWgrm6WRM2jmT04w7dP7aVbS2qyP9XPmH/mybQtFXMjJ/8BZlp0yltC8QOs8sGl6q8Ws7acp19YtRkLdK6SsmQ==", + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.4.5.tgz", + "integrity": "sha512-heaGV9QWqef259HaF+0is/tsmhlZIbUSWhqvj0iwKmxoN92fghKijWwdVYhPIbsmGlrQuwPTZHSCnaOlO+gsFg==", "requires": { "ajv": "^6.10.0", "debug": "^4.3.1" } }, "@truffle/debug-utils": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/@truffle/debug-utils/-/debug-utils-6.0.8.tgz", - "integrity": "sha512-bkrHQZj6OVw5FCs0li69e0f6gdW+6FuOR8dgedfy8qpsVVtGQB+8cN7hnoFUwMs60vo7vW90grZu1c4855zVQw==", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@truffle/debug-utils/-/debug-utils-6.0.9.tgz", + "integrity": "sha512-CVKVsbEWE0TzmTdGcxEeQvNhTKxkojIRox3LWh2Z8ZARtPiWwYl/lmPqfugh1665xmc2NVLI9qyP4ygVFPkvIQ==", "requires": { - "@truffle/codec": "^0.11.26", - "@trufflesuite/chromafi": "^2.2.2", + "@truffle/codec": "^0.11.27", + "@trufflesuite/chromafi": "^3.0.0", "bn.js": "^5.1.3", "chalk": "^2.4.2", "debug": "^4.3.1", - "highlightjs-solidity": "^2.0.3" + "highlightjs-solidity": "^2.0.4" }, "dependencies": { "bn.js": { @@ -28349,24 +28309,18 @@ } }, "@trufflesuite/chromafi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz", - "integrity": "sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@trufflesuite/chromafi/-/chromafi-3.0.0.tgz", + "integrity": "sha512-oqWcOqn8nT1bwlPPfidfzS55vqcIDdpfzo3HbU9EnUmcSTX+I8z0UyUFI3tZQjByVJulbzxHxUGS3ZJPwK/GPQ==", "requires": { - "ansi-mark": "^1.0.0", - "ansi-regex": "^3.0.0", - "array-uniq": "^1.0.3", "camelcase": "^4.1.0", "chalk": "^2.3.2", "cheerio": "^1.0.0-rc.2", "detect-indent": "^5.0.0", - "he": "^1.1.1", "highlight.js": "^10.4.1", "lodash.merge": "^4.6.2", - "min-indent": "^1.0.0", "strip-ansi": "^4.0.0", - "strip-indent": "^2.0.0", - "super-split": "^1.1.0" + "strip-indent": "^2.0.0" }, "dependencies": { "camelcase": { @@ -29328,18 +29282,6 @@ "type-fest": "^0.21.3" } }, - "ansi-mark": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/ansi-mark/-/ansi-mark-1.0.4.tgz", - "integrity": "sha1-HNS6jVfxXxCdaq9uycqXhsik7mw=", - "requires": { - "ansi-regex": "^3.0.0", - "array-uniq": "^1.0.3", - "chalk": "^2.3.2", - "strip-ansi": "^4.0.0", - "super-split": "^1.1.0" - } - }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -30483,11 +30425,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, "array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -35547,7 +35484,8 @@ "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "peer": true }, "header-case": { "version": "1.0.1", @@ -37589,11 +37527,6 @@ "dom-walk": "^0.1.0" } }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" - }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -40054,9 +39987,9 @@ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" }, "simple-get": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", - "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", "requires": { "decompress-response": "^3.3.0", "once": "^1.3.1", @@ -40568,11 +40501,6 @@ "integrity": "sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA==", "requires": {} }, - "super-split": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/super-split/-/super-split-1.1.0.tgz", - "integrity": "sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ==" - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/src/@utils/aquarius.ts b/src/@utils/aquarius.ts index 3356c7e95..b64b26625 100644 --- a/src/@utils/aquarius.ts +++ b/src/@utils/aquarius.ts @@ -1,10 +1,5 @@ -import { - Asset, - LoggerInstance, - PublisherTrustedAlgorithm -} from '@oceanprotocol/lib' +import { Asset, LoggerInstance } from '@oceanprotocol/lib' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' -import { PriceList } from './subgraph' import axios, { CancelToken, AxiosResponse } from 'axios' import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData' import { metadataCacheUri } from '../../app.config' @@ -12,7 +7,7 @@ import { SortDirectionOptions, SortTermOptions } from '../@types/aquarius/SearchQuery' -import { getServiceByName } from './ddo' +import { transformAssetToAssetSelection } from './assetConvertor' export const MAXIMUM_NUMBER_OF_PAGES_WITH_RESULTS = 476 @@ -202,58 +197,6 @@ export async function retrieveDDOListByDIDs( } } -export async function transformDDOToAssetSelection( - datasetProviderEndpoint: string, - ddoList: Asset[], - selectedAlgorithms?: PublisherTrustedAlgorithm[], - cancelToken?: CancelToken -): Promise { - const didList: string[] = [] - // const priceList: PriceList = await getAssetsPriceList(ddoList) - const priceList: PriceList = null - const symbolList: any = {} - const didProviderEndpointMap: any = {} - for (const ddo of ddoList) { - didList.push(ddo.id) - symbolList[ddo.id] = ddo.datatokens[0].symbol - const algoComputeService = getServiceByName(ddo, 'compute') - algoComputeService?.serviceEndpoint && - (didProviderEndpointMap[ddo.id] = algoComputeService?.serviceEndpoint) - } - const ddoNames = await getAssetsNames(didList, cancelToken) - const algorithmList: AssetSelectionAsset[] = [] - didList?.forEach((did: string) => { - if ( - priceList[did] && - (!didProviderEndpointMap[did] || - didProviderEndpointMap[did] === datasetProviderEndpoint) - ) { - let selected = false - selectedAlgorithms?.forEach((algorithm: PublisherTrustedAlgorithm) => { - if (algorithm.did === did) { - selected = true - } - }) - selected - ? algorithmList.unshift({ - did: did, - name: ddoNames[did], - price: priceList[did], - checked: selected, - symbol: symbolList[did] - }) - : algorithmList.push({ - did: did, - name: ddoNames[did], - price: priceList[did], - checked: selected, - symbol: symbolList[did] - }) - } - }) - return algorithmList -} - export async function getAlgorithmDatasetsForCompute( algorithmId: string, datasetProviderUri: string, @@ -264,7 +207,7 @@ export async function getAlgorithmDatasetsForCompute( chainIds: [datasetChainId], filters: [ getFilterTerm( - 'service.attributes.main.privacy.publisherTrustedAlgorithms.did', + 'service.compite.publisherTrustedAlgorithms.did', algorithmId ) ], @@ -279,11 +222,10 @@ export async function getAlgorithmDatasetsForCompute( if (computeDatasets.totalResults === 0) return [] - const datasets = await transformDDOToAssetSelection( + const datasets = await transformAssetToAssetSelection( datasetProviderUri, computeDatasets.results, - [], - cancelToken + [] ) return datasets } diff --git a/src/@utils/assetConvertor.ts b/src/@utils/assetConvertor.ts new file mode 100644 index 000000000..0528bcdcf --- /dev/null +++ b/src/@utils/assetConvertor.ts @@ -0,0 +1,43 @@ +import { getAccessDetailsForAssets } from './accessDetailsAndPricing' +import { AssetExtended } from 'src/@types/AssetExtended' +import { PublisherTrustedAlgorithm, Asset } from '@oceanprotocol/lib' +import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' +import { getServiceByName } from './ddo' + +export async function transformAssetToAssetSelection( + datasetProviderEndpoint: string, + assets: Asset[], + selectedAlgorithms?: PublisherTrustedAlgorithm[] +): Promise { + const extendedAssets: AssetExtended[] = await getAccessDetailsForAssets( + assets + ) + const algorithmList: AssetSelectionAsset[] = [] + + for (const asset of extendedAssets) { + const algoComputeService = getServiceByName(asset, 'compute') + + if ( + asset?.accessDetails.price && + algoComputeService?.serviceEndpoint === datasetProviderEndpoint + ) { + let selected = false + selectedAlgorithms?.forEach((algorithm: PublisherTrustedAlgorithm) => { + if (algorithm.did === asset.id) { + selected = true + } + }) + const algorithmAsset: AssetSelectionAsset = { + did: asset.id, + name: asset.datatokens[0].name, + price: asset.accessDetails.price, + checked: selected, + symbol: asset.datatokens[0].symbol + } + selected + ? algorithmList.unshift(algorithmAsset) + : algorithmList.push(algorithmAsset) + } + } + return algorithmList +} diff --git a/src/@utils/compute.ts b/src/@utils/compute.ts index bedd27eb8..6ce0f9a1b 100644 --- a/src/@utils/compute.ts +++ b/src/@utils/compute.ts @@ -9,10 +9,20 @@ // Account // } from '@oceanprotocol/lib' // import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute' -import { Asset } from '@oceanprotocol/lib' +import { + Asset, + ServiceComputeOptions, + PublisherTrustedAlgorithm, + getHash +} from '@oceanprotocol/lib' import { CancelToken } from 'axios' import { gql } from 'urql' -import { queryMetadata, getFilterTerm, generateBaseQuery } from './aquarius' +import { + queryMetadata, + getFilterTerm, + generateBaseQuery, + retrieveDDOListByDIDs +} from './aquarius' import { fetchDataForMultipleChains } from './subgraph' const getComputeOrders = gql` @@ -258,37 +268,50 @@ function getServiceEndpoints(data: TokenOrder[], assets: Asset[]): string[] { // return computeResult // } -// export async function createTrustedAlgorithmList( -// selectedAlgorithms: string[], // list of DIDs -// ocean: Ocean -// ): Promise { -// const trustedAlgorithms = [] +export async function createTrustedAlgorithmList( + selectedAlgorithms: string[], // list of DIDs, + assetChainId: number, + cancelToken: CancelToken +): Promise { + const trustedAlgorithms: PublisherTrustedAlgorithm[] = [] -// for (const selectedAlgorithm of selectedAlgorithms) { -// const trustedAlgorithm = -// await ocean.compute.createPublisherTrustedAlgorithmfromDID( -// selectedAlgorithm -// ) -// trustedAlgorithms.push(trustedAlgorithm) -// } -// return trustedAlgorithms -// } + const selectedAssets = await retrieveDDOListByDIDs( + selectedAlgorithms, + [assetChainId], + cancelToken + ) -// export async function transformComputeFormToServiceComputePrivacy( -// values: ComputePrivacyForm, -// ocean: Ocean -// ): Promise { -// const { allowAllPublishedAlgorithms } = values -// const publisherTrustedAlgorithms = values.allowAllPublishedAlgorithms -// ? [] -// : await createTrustedAlgorithmList(values.publisherTrustedAlgorithms, ocean) + for (const selectedAlgorithm of selectedAssets) { + const trustedAlgorithm = { + did: selectedAlgorithm.id, + containerSectionChecksum: getHash( + JSON.stringify(selectedAlgorithm.metadata.algorithm.container) + ), + filesChecksum: getHash(selectedAlgorithm.services[0].files) + } + trustedAlgorithms.push(trustedAlgorithm) + } + return trustedAlgorithms +} -// const privacy: ServiceComputePrivacy = { -// allowNetworkAccess: false, -// allowRawAlgorithm: false, -// allowAllPublishedAlgorithms, -// publisherTrustedAlgorithms -// } +export async function transformComputeFormToServiceComputeOptions( + values: ComputePrivacyForm, + currentOptions: ServiceComputeOptions, + assetChainId: number, + cancelToken: CancelToken +): Promise { + const publisherTrustedAlgorithms = values.allowAllPublishedAlgorithms + ? [] + : await createTrustedAlgorithmList( + values.publisherTrustedAlgorithms, + assetChainId, + cancelToken + ) -// return privacy -// } + const privacy: ServiceComputeOptions = { + ...currentOptions, + publisherTrustedAlgorithms + } + + return privacy +} diff --git a/src/@utils/dispenser.ts b/src/@utils/dispenser.ts new file mode 100644 index 000000000..cf128adbe --- /dev/null +++ b/src/@utils/dispenser.ts @@ -0,0 +1,48 @@ +import { LoggerInstance, Dispenser, Datatoken } from '@oceanprotocol/lib' +import Web3 from 'web3' +import { TransactionReceipt } from 'web3-core' + +export async function setMinterToPublisher( + web3: Web3, + dispenserAddress: string, + datatokenAddress: string, + accountId: string, + setError: (msg: string) => void +): Promise { + const dispenserInstance = new Dispenser(web3, dispenserAddress) + const status = await dispenserInstance.status(datatokenAddress) + if (!status?.active) return + + const datatokenInstance = new Datatoken(web3) + + const response = await datatokenInstance.removeMinter( + datatokenAddress, + accountId, + accountId + ) + if (!response) { + setError('Updating DDO failed.') + LoggerInstance.error('Failed at cancelMinter') + } + return response +} + +export async function setMinterToDispenser( + web3: Web3, + datatokenAddress: string, + accountId: string, + setError: (msg: string) => void +): Promise { + const datatokenInstance = new Datatoken(web3) + + const response = await datatokenInstance.addMinter( + datatokenAddress, + accountId, + accountId + ) + if (!response) { + setError('Updating DDO failed.') + LoggerInstance.error('Failed at makeMinter') + } + return response +} diff --git a/src/@utils/nft.ts b/src/@utils/nft.ts index 62ae06711..15f20c036 100644 --- a/src/@utils/nft.ts +++ b/src/@utils/nft.ts @@ -1,4 +1,14 @@ import { SvgWaves } from './SvgWaves' +import { + Asset, + LoggerInstance, + getHash, + Nft, + ProviderInstance, + DDO +} from '@oceanprotocol/lib' +import Web3 from 'web3' +import { TransactionReceipt } from 'web3-core' // https://docs.opensea.io/docs/metadata-standards export interface NftMetadata { @@ -71,3 +81,50 @@ export function generateNftCreateData(nftMetadata: NftMetadata): any { return nftCreateData } + +export async function setNftMetadata( + asset: Asset | DDO, + accountId: string, + web3: Web3, + signal: AbortSignal +): Promise { + const encryptedDdo = await ProviderInstance.encrypt( + asset, + asset.services[0].serviceEndpoint, + signal + ) + LoggerInstance.log('[setNftMetadata] Got encrypted DDO', encryptedDdo) + + const metadataHash = getHash(JSON.stringify(asset)) + const nft = new Nft(web3) + + // theoretically used by aquarius or provider, not implemented yet, will remain hardcoded + const flags = '0x2' + + const estGasSetMetadata = await nft.estGasSetMetadata( + asset.nftAddress, + accountId, + 0, + asset.services[0].serviceEndpoint, + '', + flags, + encryptedDdo, + '0x' + metadataHash, + [] + ) + + console.log('[setNftMetadata] est Gas set metadata --', estGasSetMetadata) + + const setMetadataTx = await nft.setMetadata( + asset.nftAddress, + accountId, + 0, + asset.services[0].serviceEndpoint, + '', + flags, + encryptedDdo, + '0x' + metadataHash + ) + + return setMetadataTx +} diff --git a/src/components/@shared/FormFields/FilesInput/index.tsx b/src/components/@shared/FormFields/FilesInput/index.tsx index 8336222a2..a852f56e4 100644 --- a/src/components/@shared/FormFields/FilesInput/index.tsx +++ b/src/components/@shared/FormFields/FilesInput/index.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, useState } from 'react' +import React, { ReactElement, useCallback, useEffect, useState } from 'react' import { useField, useFormikContext } from 'formik' import { toast } from 'react-toastify' import FileInfo from './Info' @@ -13,24 +13,43 @@ export default function FilesInput(props: InputProps): ReactElement { const [isLoading, setIsLoading] = useState(false) const { values } = useFormikContext() - function loadFileInfo(url: string) { - const providerUri = values.services[0].providerUrl.url + const loadFileInfo = useCallback( + (url: string) => { + const providerUri = + (values.services && values.services[0].providerUrl.url) || + 'https://provider.mainnet.oceanprotocol.com' - async function validateUrl() { - try { - setIsLoading(true) - const checkedFile = await getFileUrlInfo(url, providerUri) - checkedFile && helpers.setValue([{ url, ...checkedFile[0] }]) - } catch (error) { - toast.error('Could not fetch file info. Please check URL and try again') - console.error(error.message) - } finally { - setIsLoading(false) + async function validateUrl() { + try { + setIsLoading(true) + const checkedFile = await getFileUrlInfo(url, providerUri) + checkedFile && helpers.setValue([{ url, ...checkedFile[0] }]) + } catch (error) { + toast.error( + 'Could not fetch file info. Please check URL and try again' + ) + console.error(error.message) + } finally { + setIsLoading(false) + } } - } - validateUrl() - } + validateUrl() + }, + [helpers, values.services] + ) + + useEffect(() => { + // try load from initial values, kinda hacky but it works + if ( + props.value && + props.value.length > 0 && + typeof props.value[0] === 'string' + ) { + console.log('loadFileInfo eff') + loadFileInfo(props.value[0].toString()) + } + }, [loadFileInfo, props]) async function handleButtonClick(e: React.SyntheticEvent, url: string) { // File example 'https://oceanprotocol.com/tech-whitepaper.pdf' diff --git a/src/components/Asset/AssetActions/Compute/index.tsx b/src/components/Asset/AssetActions/Compute/index.tsx index 0815f0d9f..ccf76af36 100644 --- a/src/components/Asset/AssetActions/Compute/index.tsx +++ b/src/components/Asset/AssetActions/Compute/index.tsx @@ -17,8 +17,7 @@ import { useWeb3 } from '@context/Web3' import { generateBaseQuery, getFilterTerm, - queryMetadata, - transformDDOToAssetSelection + queryMetadata } from '@utils/aquarius' import { Formik } from 'formik' import { getInitialValues, validationSchema } from './_constants' @@ -37,6 +36,7 @@ import { useIsMounted } from '@hooks/useIsMounted' import { SortTermOptions } from '../../../../@types/aquarius/SearchQuery' import { getAccessDetails } from '@utils/accessDetailsAndPricing' import { AccessDetails } from 'src/@types/Price' +import { transformAssetToAssetSelection } from '@utils/assetConvertor' export default function Compute({ ddo, @@ -158,11 +158,10 @@ export default function Compute({ ) setDdoAlgorithmList(gueryResults.results) - algorithmSelectionList = await transformDDOToAssetSelection( + algorithmSelectionList = await transformAssetToAssetSelection( computeService?.serviceEndpoint, gueryResults.results, - [], - newCancelToken() + [] ) } return algorithmSelectionList diff --git a/src/components/Asset/AssetContent/index.tsx b/src/components/Asset/AssetContent/index.tsx index 5eb5246ba..f91a16a40 100644 --- a/src/components/Asset/AssetContent/index.tsx +++ b/src/components/Asset/AssetContent/index.tsx @@ -1,4 +1,5 @@ -import React, { ReactElement } from 'react' +import React, { ReactElement, useState, useEffect } from 'react' +import Link from 'next/link' import Markdown from '@shared/Markdown' import MetaFull from './MetaFull' import MetaSecondary from './MetaSecondary' @@ -14,6 +15,7 @@ import styles from './index.module.css' import NetworkName from '@shared/NetworkName' import content from '../../../../content/purgatory.json' import { AssetExtended } from 'src/@types/AssetExtended' +import { useWeb3 } from '@context/Web3' export default function AssetContent({ asset @@ -21,7 +23,16 @@ export default function AssetContent({ asset: AssetExtended }): ReactElement { const { debug } = useUserPreferences() - const { isInPurgatory, purgatoryData } = useAsset() + const [isOwner, setIsOwner] = useState(false) + const { accountId } = useWeb3() + const { isInPurgatory, purgatoryData, owner, isAssetNetwork } = useAsset() + + useEffect(() => { + if (!accountId || !owner) return + + const isOwner = accountId.toLowerCase() === owner.toLowerCase() + setIsOwner(isOwner) + }, [accountId, owner, asset]) return ( <> @@ -62,35 +73,13 @@ export default function AssetContent({
- - {/* - TODO: restore edit actions, ideally put edit screens on new page - with own URL instead of switching out AssetContent in place. - Simple way would be modal usage - */} - {/* {isOwner && isAssetNetwork && ( + {isOwner && isAssetNetwork && (
- - {serviceCompute && ddo?.metadata.type === 'dataset' && ( - <> - | - - - )} + + Edit +
- )} */} + )}
diff --git a/src/components/Asset/Edit/DebugEditCompute.tsx b/src/components/Asset/Edit/DebugEditCompute.tsx index dc4d0e8c5..20aa121dd 100644 --- a/src/components/Asset/Edit/DebugEditCompute.tsx +++ b/src/components/Asset/Edit/DebugEditCompute.tsx @@ -2,24 +2,32 @@ import { Asset, ServiceComputeOptions } from '@oceanprotocol/lib' import React, { ReactElement, useEffect, useState } from 'react' // import { transformComputeFormToServiceComputePrivacy } from '@utils/compute' import DebugOutput from '@shared/DebugOutput' +import { useCancelToken } from '@hooks/useCancelToken' +import { transformComputeFormToServiceComputeOptions } from '@utils/compute' export default function DebugEditCompute({ values, - ddo + asset }: { values: ComputePrivacyForm - ddo: Asset + asset: Asset }): ReactElement { const [formTransformed, setFormTransformed] = useState() + const newCancelToken = useCancelToken() useEffect(() => { - // async function transformValues() { - // const privacy = await transformComputeFormToServiceComputePrivacy(values) - // setFormTransformed(privacy) - // } - // transformValues() - }, [values, ddo]) + async function transformValues() { + const privacy = await transformComputeFormToServiceComputeOptions( + values, + asset.services[0].compute, + asset.chainId, + newCancelToken() + ) + setFormTransformed(privacy) + } + transformValues() + }, [values, asset]) return ( <> diff --git a/src/components/Asset/Edit/DebugEditMetadata.tsx b/src/components/Asset/Edit/DebugEditMetadata.tsx new file mode 100644 index 000000000..a58db028e --- /dev/null +++ b/src/components/Asset/Edit/DebugEditMetadata.tsx @@ -0,0 +1,39 @@ +import { Asset, Metadata, Service } from '@oceanprotocol/lib' +import React, { ReactElement } from 'react' +import DebugOutput from '@shared/DebugOutput' +import { MetadataEditForm } from './_types' +import { mapTimeoutStringToSeconds } from '@utils/ddo' + +export default function DebugEditMetadata({ + values, + asset +}: { + values: Partial + asset: Asset +}): ReactElement { + const linksTransformed = values.links?.length && + values.links[0].valid && [values.links[0].url.replace('javascript:', '')] + const newMetadata: Metadata = { + ...asset.metadata, + name: values.name, + description: values.description, + links: linksTransformed, + author: values.author + } + const updatedService: Service = { + ...asset.services[0], + timeout: mapTimeoutStringToSeconds(values.timeout) + } + const updatedAsset: Asset = { + ...asset, + metadata: newMetadata, + services: [updatedService] + } + + return ( + <> + + + + ) +} diff --git a/src/components/Asset/Edit/EditComputeDataset.tsx b/src/components/Asset/Edit/EditComputeDataset.tsx index a7c5b4684..02e8aad07 100644 --- a/src/components/Asset/Edit/EditComputeDataset.tsx +++ b/src/components/Asset/Edit/EditComputeDataset.tsx @@ -1,95 +1,131 @@ import { useWeb3 } from '@context/Web3' import { Formik } from 'formik' import React, { ReactElement, useState } from 'react' -import { useAsset } from '@context/Asset' import FormEditComputeDataset from './FormEditComputeDataset' -import { LoggerInstance, ServiceComputeOptions } from '@oceanprotocol/lib' +import { + LoggerInstance, + ServiceComputeOptions, + Service, + ProviderInstance, + getHash, + Nft, + Asset +} from '@oceanprotocol/lib' import { useUserPreferences } from '@context/UserPreferences' -import DebugEditCompute from './DebugEditCompute' import styles from './index.module.css' -// import { transformComputeFormToServiceComputePrivacy } from '@utils/compute' import Web3Feedback from '@shared/Web3Feedback' -import { getInitialValues, validationSchema } from './_constants' +import { useCancelToken } from '@hooks/useCancelToken' +import { + getComputeSettingsInitialValues, + computeSettingsValidationSchema +} from './_constants' import content from '../../../../content/pages/editComputeDataset.json' +import { AssetExtended } from 'src/@types/AssetExtended' +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' +import { useAsset } from '@context/Asset' +import EditFeedback from './EditFeedback' +import { setNftMetadata } from '@utils/nft' export default function EditComputeDataset({ - setShowEdit + asset }: { - setShowEdit: (show: boolean) => void + asset: AssetExtended }): ReactElement { const { debug } = useUserPreferences() - const { accountId } = useWeb3() - const { asset, isAssetNetwork, fetchAsset } = useAsset() + const { accountId, web3 } = useWeb3() + const { fetchAsset, isAssetNetwork } = useAsset() const [success, setSuccess] = useState() const [error, setError] = useState() - + const newAbortController = useAbortController() + const newCancelToken = useCancelToken() const hasFeedback = error || success async function handleSubmit( values: ComputePrivacyForm, resetForm: () => void ) { - // try { - // if (price.type === 'free') { - // const tx = await setMinterToPublisher( - // ocean, - // ddo.services[0].datatokenAddress, - // accountId, - // setError - // ) - // if (!tx) return - // } - // const privacy = await transformComputeFormToServiceComputePrivacy( - // values, - // ocean - // ) - // const ddoEditedComputePrivacy = await ocean.compute.editComputePrivacy( - // ddo, - // 1, - // privacy as ServiceComputePrivacy - // ) - // if (!ddoEditedComputePrivacy) { - // setError(content.form.error) - // LoggerInstance.error(content.form.error) - // return - // } - // const storedddo = await ocean.assets.updateMetadata( - // ddoEditedComputePrivacy, - // accountId - // ) - // if (!storedddo) { - // setError(content.form.error) - // LoggerInstance.error(content.form.error) - // return - // } else { - // if (price.type === 'free') { - // const tx = await setMinterToDispenser( - // ocean, - // ddo.services[0].datatokenAddress, - // accountId, - // setError - // ) - // if (!tx) return - // } - // // Edit succeeded - // setSuccess(content.form.success) - // resetForm() - // } - // } catch (error) { - // LoggerInstance.error(error.message) - // setError(error.message) - // } + try { + if (asset?.accessDetails?.type === 'free') { + const tx = await setMinterToPublisher( + web3, + asset?.accessDetails?.addressOrId, + asset?.accessDetails?.datatoken?.address, + accountId, + setError + ) + if (!tx) return + } + 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] + } + + 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 + } else { + if (asset.accessDetails.type === 'free') { + const tx = await setMinterToDispenser( + web3, + asset?.accessDetails?.datatoken?.address, + accountId, + setError + ) + if (!tx) 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' }) @@ -99,15 +135,26 @@ export default function EditComputeDataset({ > {({ values, isSubmitting }) => isSubmitting || hasFeedback ? ( -
+ { + await fetchAsset() + }, + to: `/asset/${asset.id}` + }} + /> ) : ( <>

{content.description}

-
+
{debug === true && (
- {/* */} +
)} diff --git a/src/components/Asset/Edit/EditFeedback.module.css b/src/components/Asset/Edit/EditFeedback.module.css new file mode 100644 index 000000000..8058272bc --- /dev/null +++ b/src/components/Asset/Edit/EditFeedback.module.css @@ -0,0 +1,43 @@ +.feedback { + width: 100%; + min-height: 40vh; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + text-align: center; +} + +.box { + composes: box from '@shared/atoms/Box.module.css'; + width: 100%; +} + +.feedback h3 { + font-size: var(--font-size-large); + text-align: center; + margin-bottom: calc(var(--spacer) / 2); +} + +.feedback h3 + div { + text-align: left; +} + +@media (min-width: 40rem) { + .feedback { + max-width: 30rem; + margin: 0 auto; + } +} + +.feedback [class*='loaderWrap'] { + justify-content: center; + text-align: center; +} + +.action { + margin-top: calc(var(--spacer) / 1.5); +} +.moreInfo { + padding: calc(var(--spacer) / 4) calc(var(--spacer) / 2) !important; +} diff --git a/src/components/Asset/Edit/EditFeedback.tsx b/src/components/Asset/Edit/EditFeedback.tsx new file mode 100644 index 000000000..fb446f278 --- /dev/null +++ b/src/components/Asset/Edit/EditFeedback.tsx @@ -0,0 +1,94 @@ +import Alert from '@shared/atoms/Alert' +import Button from '@shared/atoms/Button' +import Loader from '@shared/atoms/Loader' +import SuccessConfetti from '@shared/SuccessConfetti' +import React, { ReactElement, useState, FormEvent } from 'react' +import styles from './EditFeedback.module.css' + +interface Action { + name: string + onClick?: () => void + to?: string +} + +function ActionSuccess({ action }: { action: Action }) { + const { name, onClick, to } = action + + return ( + + ) +} + +function ActionError({ setError }: { setError: (error: string) => void }) { + return ( + + ) +} + +export default function EditFeedback({ + title, + error, + success, + loading, + successAction, + setError +}: { + title: string + error: string + success: string + loading?: string + successAction: Action + setError: (error: string) => void +}): ReactElement { + const [moreInfo, setMoreInfo] = useState(false) + + function toggleMoreInfo(e: FormEvent) { + e.preventDefault() + moreInfo === true ? setMoreInfo(false) : setMoreInfo(true) + } + + return ( +
+
+

{title}

+ {error ? ( + <> +

Sorry, something went wrong. Please try again.

+ {moreInfo && } + + + + ) : success ? ( + } + /> + ) : ( + + )} +
+
+ ) +} diff --git a/src/components/Asset/Edit/EditMetadata.tsx b/src/components/Asset/Edit/EditMetadata.tsx new file mode 100644 index 000000000..e1d1fba6c --- /dev/null +++ b/src/components/Asset/Edit/EditMetadata.tsx @@ -0,0 +1,178 @@ +import React, { ReactElement, useState } from 'react' +import { Formik } from 'formik' +import { + LoggerInstance, + Metadata, + FixedRateExchange, + Asset, + Service +} from '@oceanprotocol/lib' +import { validationSchema, getInitialValues } from './_constants' +import { MetadataEditForm } from './_types' +import { useWeb3 } from '@context/Web3' +import { useUserPreferences } from '@context/UserPreferences' +import Web3Feedback from '@shared/Web3Feedback' +import FormEditMetadata from './FormEditMetadata' +import { mapTimeoutStringToSeconds } from '@utils/ddo' +import styles from './index.module.css' +import content from '../../../../content/pages/edit.json' +import { AssetExtended } from 'src/@types/AssetExtended' +import { setMinterToPublisher, setMinterToDispenser } from '@utils/dispenser' +import { useAbortController } from '@hooks/useAbortController' +import DebugEditMetadata from './DebugEditMetadata' +import { getOceanConfig } from '@utils/ocean' +import EditFeedback from './EditFeedback' +import { useAsset } from '@context/Asset' +import { setNftMetadata } from '@utils/nft' + +export default function Edit({ + asset +}: { + asset: AssetExtended +}): ReactElement { + const { debug } = useUserPreferences() + const { fetchAsset, isAssetNetwork } = useAsset() + const { accountId, web3 } = useWeb3() + const newAbortController = useAbortController() + const [success, setSuccess] = useState() + const [error, setError] = useState() + const [timeoutStringValue, setTimeoutStringValue] = useState() + const isComputeType = asset?.services[0]?.type === 'compute' + const hasFeedback = error || success + + async function updateFixedPrice(newPrice: string) { + const config = getOceanConfig(asset.chainId) + + const fixedRateInstance = new FixedRateExchange( + web3, + config.fixedRateExchangeAddress + ) + + const setPriceResp = await fixedRateInstance.setRate( + accountId, + asset.accessDetails.addressOrId, + newPrice + ) + 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 { + const linksTransformed = values.links?.length && + values.links[0].valid && [ + values.links[0].url.replace('javascript:', '') + ] + const updatedMetadata: Metadata = { + ...asset.metadata, + name: values.name, + description: values.description, + links: linksTransformed, + author: values.author + } + + asset?.accessDetails?.type === 'fixed' && + values.price !== asset.accessDetails.price && + (await updateFixedPrice(values.price)) + + const updatedService: Service = { + ...asset.services[0], + timeout: mapTimeoutStringToSeconds(values.timeout) + } + + const updatedAsset: Asset = { + ...asset, + metadata: updatedMetadata, + services: [updatedService] + } + + 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) + }} + > + {({ isSubmitting, values, initialValues }) => + isSubmitting || hasFeedback ? ( + { + await fetchAsset() + }, + to: `/asset/${asset.id}` + }} + /> + ) : ( + <> +

{content.description}

+
+ + + + + {debug === true && ( +
+ +
+ )} +
+ + ) + } +
+ ) +} diff --git a/src/components/Asset/Edit/FormEditMetadata.module.css b/src/components/Asset/Edit/FormEdit.module.css similarity index 100% rename from src/components/Asset/Edit/FormEditMetadata.module.css rename to src/components/Asset/Edit/FormEdit.module.css diff --git a/src/components/Asset/Edit/FormEditComputeDataset.tsx b/src/components/Asset/Edit/FormEditComputeDataset.tsx index 5f9a9ccf6..55eea4ca6 100644 --- a/src/components/Asset/Edit/FormEditComputeDataset.tsx +++ b/src/components/Asset/Edit/FormEditComputeDataset.tsx @@ -3,12 +3,11 @@ import { Field, Form, FormikContextType, useFormikContext } from 'formik' import Input, { InputProps } from '@shared/FormInput' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' import stylesIndex from './index.module.css' -import styles from './FormEditMetadata.module.css' +import styles from './FormEdit.module.css' import { generateBaseQuery, getFilterTerm, - queryMetadata, - transformDDOToAssetSelection + queryMetadata } from '@utils/aquarius' import { useAsset } from '@context/Asset' import { PublisherTrustedAlgorithm } from '@oceanprotocol/lib' @@ -17,25 +16,25 @@ import FormActions from './FormActions' import { useCancelToken } from '@hooks/useCancelToken' import { SortTermOptions } from '../../../@types/aquarius/SearchQuery' import { getServiceByName } from '@utils/ddo' +import { transformAssetToAssetSelection } from '@utils/assetConvertor' export default function FormEditComputeDataset({ data, - title, - setShowEdit + title }: { data: InputProps[] title: string - setShowEdit: (show: boolean) => void }): ReactElement { const { appConfig } = useSiteMetadata() const { asset } = useAsset() + const [showEditCompute, setShowEditCompute] = useState() const { values }: FormikContextType = useFormikContext() const [allAlgorithms, setAllAlgorithms] = useState() const newCancelToken = useCancelToken() const { publisherTrustedAlgorithms } = getServiceByName( asset, 'compute' - ).compute + )?.compute async function getAlgorithmList( publisherTrustedAlgorithms: PublisherTrustedAlgorithm[] @@ -43,17 +42,16 @@ export default function FormEditComputeDataset({ const baseParams = { chainIds: [asset.chainId], sort: { sortBy: SortTermOptions.Created }, - filters: [getFilterTerm('service.attributes.main.type', 'algorithm')] + filters: [getFilterTerm('metadata.type', 'algorithm')] } as BaseQueryParams const query = generateBaseQuery(baseParams) const querryResult = await queryMetadata(query, newCancelToken()) const datasetComputeService = getServiceByName(asset, 'compute') - const algorithmSelectionList = await transformDDOToAssetSelection( + const algorithmSelectionList = await transformAssetToAssetSelection( datasetComputeService?.serviceEndpoint, - querryResult.results, - publisherTrustedAlgorithms, - newCancelToken() + querryResult?.results, + publisherTrustedAlgorithms ) return algorithmSelectionList } @@ -85,7 +83,8 @@ export default function FormEditComputeDataset({ /> ))} - + {/* */} + ) } diff --git a/src/components/Asset/Edit/FormEditMetadata.tsx b/src/components/Asset/Edit/FormEditMetadata.tsx index 441510e4a..5887199ed 100644 --- a/src/components/Asset/Edit/FormEditMetadata.tsx +++ b/src/components/Asset/Edit/FormEditMetadata.tsx @@ -1,64 +1,75 @@ -import React, { ChangeEvent, ReactElement } from 'react' +import React, { ChangeEvent, ReactElement, useState } from 'react' import { Field, Form, FormikContextType, useFormikContext } from 'formik' import Input, { InputProps } from '@shared/FormInput' import FormActions from './FormActions' -import styles from './FormEditMetadata.module.css' +import styles from './FormEdit.module.css' import { FormPublishData } from '../../Publish/_types' import { useAsset } from '@context/Asset' +import { MetadataEditForm } from './_types' + +export function checkIfTimeoutInPredefinedValues( + timeout: string, + timeoutOptions: string[] +): boolean { + if (timeoutOptions.indexOf(timeout) > -1) { + return true + } + return false +} + +function handleTimeoutCustomOption( + data: FormFieldContent[], + values: Partial +) { + const timeoutFieldContent = data.filter( + (field) => field.name === 'timeout' + )[0] + const timeoutInputIndex = data.findIndex( + (element) => element.name === 'timeout' + ) + if ( + data[timeoutInputIndex].options.length < 6 && + !checkIfTimeoutInPredefinedValues( + values?.services[0]?.timeout, + timeoutFieldContent.options + ) + ) { + data[timeoutInputIndex].options.push(values?.services[0]?.timeout) + } else if ( + data[timeoutInputIndex].options.length === 6 && + checkIfTimeoutInPredefinedValues( + values?.services[0]?.timeout, + timeoutFieldContent.options + ) + ) { + data[timeoutInputIndex].options.pop() + } else if ( + data[timeoutInputIndex].options.length === 6 && + data[timeoutInputIndex].options[5] !== values?.services[0]?.timeout + ) { + data[timeoutInputIndex].options[5] = values?.services[0]?.timeout + } +} -// function handleTimeoutCustomOption( -// data: FormFieldContent[], -// values: Partial -// ) { -// const timeoutFieldContent = data.filter( -// (field) => field.name === 'timeout' -// )[0] -// const timeoutInputIndex = data.findIndex( -// (element) => element.name === 'timeout' -// ) -// if ( -// data[timeoutInputIndex].options.length < 6 && -// !checkIfTimeoutInPredefinedValues( -// values.timeout, -// timeoutFieldContent.options -// ) -// ) { -// data[timeoutInputIndex].options.push(values.timeout) -// } else if ( -// data[timeoutInputIndex].options.length === 6 && -// checkIfTimeoutInPredefinedValues( -// values.timeout, -// timeoutFieldContent.options -// ) -// ) { -// data[timeoutInputIndex].options.pop() -// } else if ( -// data[timeoutInputIndex].options.length === 6 && -// data[timeoutInputIndex].options[5] !== values.timeout -// ) { -// data[timeoutInputIndex].options[5] = values.timeout -// } -// } export default function FormEditMetadata({ data, - setShowEdit, setTimeoutStringValue, values, showPrice, isComputeDataset }: { data: InputProps[] - setShowEdit: (show: boolean) => void setTimeoutStringValue: (value: string) => void - values: Partial + values: Partial showPrice: boolean isComputeDataset: boolean }): ReactElement { const { oceanConfig } = useAsset() + const [showEdit, setShowEdit] = useState() const { validateField, setFieldValue - }: FormikContextType> = useFormikContext() + }: FormikContextType> = useFormikContext() // Manually handle change events instead of using `handleChange` from Formik. // Workaround for default `validateOnChange` not kicking in @@ -78,12 +89,12 @@ export default function FormEditMetadata({ (field) => field.name === 'timeout' )[0].options - // if (isComputeDataset && timeoutOptionsArray.includes('Forever')) { - // const foreverOptionIndex = timeoutOptionsArray.indexOf('Forever') - // timeoutOptionsArray.splice(foreverOptionIndex, 1) - // } else if (!isComputeDataset && !timeoutOptionsArray.includes('Forever')) { - // timeoutOptionsArray.push('Forever') - // } + if (isComputeDataset && timeoutOptionsArray.includes('Forever')) { + const foreverOptionIndex = timeoutOptionsArray.indexOf('Forever') + timeoutOptionsArray.splice(foreverOptionIndex, 1) + } else if (!isComputeDataset && !timeoutOptionsArray.includes('Forever')) { + timeoutOptionsArray.push('Forever') + } return (
@@ -107,10 +118,10 @@ export default function FormEditMetadata({ ) )} - {/* setTimeoutStringValue(values.timeout)} - /> */} + // handleClick={() => setTimeoutStringValue(values?.services[0]?.timeout)} + /> ) } diff --git a/src/components/Asset/Edit/_constants.ts b/src/components/Asset/Edit/_constants.ts index c2c24217c..39d912d80 100644 --- a/src/components/Asset/Edit/_constants.ts +++ b/src/components/Asset/Edit/_constants.ts @@ -1,6 +1,5 @@ -import { Metadata } from '@oceanprotocol/lib' +import { Metadata, ServiceComputeOptions } from '@oceanprotocol/lib' import { mapTimeoutStringToSeconds, secondsToString } from '@utils/ddo' -// import { EditableMetadataLinks } from '@oceanprotocol/lib' import * as Yup from 'yup' import { MetadataEditForm } from './_types' @@ -18,35 +17,41 @@ export const validationSchema = Yup.object().shape({ export function getInitialValues( metadata: Metadata, timeout: number, - price: number + price: string ): Partial { return { - name: metadata.name, - description: metadata.description, + name: metadata?.name, + description: metadata?.description, price, - timeout, - author: metadata.author + links: metadata?.links, + timeout: secondsToString(timeout), + author: metadata?.author } } -// export const validationSchema: Yup.SchemaOf = -// Yup.object().shape({ -// allowAllPublishedAlgorithms: Yup.boolean().nullable(), -// publisherTrustedAlgorithms: Yup.array().nullable() -// }) +export const computeSettingsValidationSchema = Yup.object().shape({ + allowAllPublishedAlgorithms: Yup.boolean().nullable(), + publisherTrustedAlgorithms: Yup.array().nullable() +}) -// export function getInitialValues( -// compute: ServiceComputePrivacy -// ): ComputePrivacyForm { -// // TODO: ocean.js needs allowAllAlgoritms setting -// const { allowAllPublishedAlgorithms, publisherTrustedAlgorithms } = compute +export function getComputeSettingsInitialValues( + compute: ServiceComputeOptions +): ComputePrivacyForm { + const { publisherTrustedAlgorithmPublishers, publisherTrustedAlgorithms } = + compute + const allowAllPublishedAlgorithms = !( + publisherTrustedAlgorithms?.length > 0 || + publisherTrustedAlgorithmPublishers?.length > 0 + ) -// const publisherTrustedAlgorithmsForForm = ( -// publisherTrustedAlgorithms || [] -// ).map((algo) => algo.did) + const publisherTrustedAlgorithmsForForm = ( + publisherTrustedAlgorithms || [] + ).map((algo) => algo.did) -// return { -// allowAllPublishedAlgorithms, -// publisherTrustedAlgorithms: publisherTrustedAlgorithmsForForm -// } -// } + // TODO: should we add publisherTrustedAlgorithmPublishers to the form? + + return { + allowAllPublishedAlgorithms, + publisherTrustedAlgorithms: publisherTrustedAlgorithmsForForm + } +} diff --git a/src/components/Asset/Edit/_types.ts b/src/components/Asset/Edit/_types.ts index 39d5d032b..b510246c3 100644 --- a/src/components/Asset/Edit/_types.ts +++ b/src/components/Asset/Edit/_types.ts @@ -3,8 +3,8 @@ export interface MetadataEditForm { name: string description: string - timeout: number - price?: number + timeout: string + price?: string links?: string | any[] author?: string } diff --git a/src/components/Asset/Edit/index.module.css b/src/components/Asset/Edit/index.module.css index 740b8e04b..c0a991646 100644 --- a/src/components/Asset/Edit/index.module.css +++ b/src/components/Asset/Edit/index.module.css @@ -1,12 +1,37 @@ +.edit ul > li[class*='Tabs_tab'] { + padding: calc(var(--spacer) / 4) var(--spacer); +} + +.edit [class*='Tabs_tabContent'] { + padding-left: 0; + padding-right: 0; + padding-top: 0; +} + .grid { - composes: grid from '../../AssetContent/index.module.css'; - margin-top: var(--spacer); + display: grid; + gap: calc(var(--spacer) * 1.5); + position: relative; + margin-top: -1rem; +} + +.grid > div { + overflow: hidden; +} + +.contianer { + composes: box from '@shared/atoms/Box.module.css'; + position: relative; +} + +@media (min-width: 60rem) { + .grid { + grid-template-columns: 1.5fr 1fr; + } } .description { font-size: var(--font-size-large); - margin-top: -1.5rem; - max-width: 50rem; } .title { diff --git a/src/components/Asset/Edit/index.tsx b/src/components/Asset/Edit/index.tsx index 7172bb372..1c5457700 100644 --- a/src/components/Asset/Edit/index.tsx +++ b/src/components/Asset/Edit/index.tsx @@ -1,161 +1,73 @@ -import { Formik } from 'formik' -import React, { ReactElement, useState } from 'react' -import { validationSchema, getInitialValues } from './_constants' -import { useAsset } from '@context/Asset' -import { useUserPreferences } from '@context/UserPreferences' -// import Debug from './DebugEditMetadata' -import Web3Feedback from '@shared/Web3Feedback' -import FormEditMetadata from './FormEditMetadata' -import { getServiceByName, mapTimeoutStringToSeconds } from '@utils/ddo' -import styles from './index.module.css' +import React, { ReactElement, useState, useEffect } from 'react' import { LoggerInstance } from '@oceanprotocol/lib' +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 { useWeb3 } from '@context/Web3' -import content from '../../../../content/pages/edit.json' -import { MetadataEditForm } from './_types' +import Alert from '@shared/atoms/Alert' -export default function Edit({ - setShowEdit, - isComputeType -}: { - setShowEdit: (show: boolean) => void - isComputeType?: boolean -}): ReactElement { - const { debug } = useUserPreferences() +export default function Edit({ uri }: { uri: string }): ReactElement { + const { asset, error, loading, owner } = useAsset() + const [isCompute, setIsCompute] = useState(false) + const [isOnwer, setIsOwner] = useState(false) const { accountId } = useWeb3() - const { asset, fetchAsset } = useAsset() - const [success, setSuccess] = useState() - const [error, setError] = useState() - const [timeoutStringValue, setTimeoutStringValue] = useState() - const { timeout } = asset.services[0] - const hasFeedback = error || success - - async function updateFixedPrice(newPrice: number) { - // const setPriceResp = await ocean.fixedRateExchange.setRate( - // price.address, - // newPrice, - // accountId - // ) - // if (!setPriceResp) { - // setError(content.form.error) - // LoggerInstance.error(content.form.error) - // } + // Switch type value upon tab change + function handleTabChange(tabName: string) { + LoggerInstance.log('switched to tab', tabName) + // add store restore from } - async function handleSubmit( - values: Partial, - resetForm: () => void - ) { - // try { - // if (price.type === 'free') { - // const tx = await setMinterToPublisher( - // ocean, - // ddo.services[0].datatokenAddress, - // accountId, - // setError - // ) - // if (!tx) return - // } - // // Construct new DDO with new values - // const ddoEditedMetdata = await ocean.assets.editMetadata(ddo as any, { - // title: values.name, - // description: values.description, - // links: typeof values.links !== 'string' ? values.links : [], - // author: values.author === '' ? ' ' : values.author - // }) - // price.type === 'exchange' && - // values.price !== price.value && - // (await updateFixedPrice(values.price)) - // if (!ddoEditedMetdata) { - // setError(content.form.error) - // LoggerInstance.error(content.form.error) - // return - // } - // let ddoEditedTimeout = ddoEditedMetdata - // if (timeoutStringValue !== values.timeout) { - // const service = - // getServiceByName(ddoEditedMetdata, 'access') || - // getServiceByName(ddoEditedMetdata, 'compute') - // const timeout = mapTimeoutStringToSeconds(values.timeout) - // ddoEditedTimeout = await ocean.assets.editServiceTimeout( - // ddoEditedMetdata, - // service.index, - // timeout - // ) - // } - // if (!ddoEditedTimeout) { - // setError(content.form.error) - // LoggerInstance.error(content.form.error) - // return - // } - // const storedddo = await ocean.assets.updateMetadata( - // ddoEditedTimeout, - // accountId - // ) - // if (!storedddo) { - // setError(content.form.error) - // LoggerInstance.error(content.form.error) - // return - // } else { - // if (price.type === 'free') { - // const tx = await setMinterToDispenser( - // ocean, - // ddo.services[0].datatokenAddress, - // accountId, - // setError - // ) - // if (!tx) return - // } - // // Edit succeeded - // setSuccess(content.form.success) - // resetForm() - // } - // } catch (error) { - // LoggerInstance.error(error.message) - // setError(error.message) - // } - } + useEffect(() => { + if (!asset || error) { + return + } + setIsCompute(asset?.services[0]?.type === 'compute') + setIsOwner(owner === accountId) + }, [asset, error]) - return ( - { - // move user's focus to top of screen - window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) - // kick off editing - await handleSubmit(values, resetForm) - }} + const tabs = [ + { + title: 'Edit Metadata', + content: + }, + { + title: 'Edit Compute Settings', + content: , + disabled: !isCompute + } + ].filter((tab) => tab !== undefined) + + return asset && !loading && isOnwer ? ( + +
+ +
+
+ ) : !isOnwer ? ( + - {({ isSubmitting, values, initialValues }) => - isSubmitting || hasFeedback ? ( -
- ) : ( - <> -

{content.description}

-
- {/* */} - - - - {/* {debug === true && } */} -
- - ) - } - + + + ) : ( + + + ) } diff --git a/src/components/Publish/index.tsx b/src/components/Publish/index.tsx index 368627f5f..fa8776381 100644 --- a/src/components/Publish/index.tsx +++ b/src/components/Publish/index.tsx @@ -24,6 +24,7 @@ import { import { getOceanConfig } from '@utils/ocean' import { validationSchema } from './_validation' import { useAbortController } from '@hooks/useAbortController' +import { setNftMetadata } from '@utils/nft' // TODO: restore FormikPersist, add back clear form action const formName = 'ocean-publish-form' @@ -172,23 +173,11 @@ export default function PublishPage({ if (!_ddo || !_encryptedDdo) throw new Error('No DDO received.') - // TODO: this whole setMetadata needs to go in a function ,too many hardcoded/calculated params - // TODO: hash generation : this needs to be moved in a function (probably on ocean.js) after we figure out what is going on in provider, leave it here for now - const metadataHash = getHash(JSON.stringify(_ddo)) - const nft = new Nft(web3) - - // theoretically used by aquarius or provider, not implemented yet, will remain hardcoded - const flags = '0x2' - - const res = await nft.setMetadata( - _erc721Address, + const res = await setNftMetadata( + _ddo, accountId, - 0, - values.services[0].providerUrl.url, - '', - flags, - _encryptedDdo, - '0x' + metadataHash + web3, + newAbortController() ) LoggerInstance.log('[publish] setMetadata result', res) diff --git a/src/pages/asset/[did]/edit.tsx b/src/pages/asset/[did]/edit.tsx new file mode 100644 index 000000000..3f7ad1e57 --- /dev/null +++ b/src/pages/asset/[did]/edit.tsx @@ -0,0 +1,14 @@ +import React, { ReactElement } from 'react' +import { useRouter } from 'next/router' +import EditPage from '../../../components/Asset/Edit' +import AssetProvider from '@context/Asset' + +export default function PageEditAsset(): ReactElement { + const router = useRouter() + const { did } = router.query + return ( + + + + ) +} diff --git a/src/pages/asset/[did].tsx b/src/pages/asset/[did]/index.tsx similarity index 85% rename from src/pages/asset/[did].tsx rename to src/pages/asset/[did]/index.tsx index f24d63a49..50995e81a 100644 --- a/src/pages/asset/[did].tsx +++ b/src/pages/asset/[did]/index.tsx @@ -1,6 +1,6 @@ import React, { ReactElement } from 'react' import { useRouter } from 'next/router' -import PageTemplateAssetDetails from '../../components/Asset' +import PageTemplateAssetDetails from '../../../components/Asset' import AssetProvider from '@context/Asset' export default function PageAssetDetails(): ReactElement { From b63c64496289cfc1514380b1cda5149df1883dac Mon Sep 17 00:00:00 2001 From: claudiaHash <49017601+claudiaHash@users.noreply.github.com> Date: Fri, 25 Feb 2022 12:36:32 +0200 Subject: [PATCH 5/5] Collect tokens by publisher (#1120) * get tokens balance wip * use ButtonBuy component * collect and sum base tokens * delete logs, get tx * center button * feedback and loader added * query by exchangeId * fixes * correct token symbol, set balance to 0 after collect Co-authored-by: ClaudiaHolhos --- src/@utils/feedback.ts | 7 ++ src/components/@shared/ButtonBuy/index.tsx | 4 +- .../Asset/AssetActions/Download.module.css | 7 ++ .../Asset/AssetActions/Download.tsx | 107 +++++++++++++++++- 4 files changed, 121 insertions(+), 4 deletions(-) diff --git a/src/@utils/feedback.ts b/src/@utils/feedback.ts index 2df15688b..c335bddd1 100644 --- a/src/@utils/feedback.ts +++ b/src/@utils/feedback.ts @@ -11,6 +11,13 @@ export function getOrderFeedback( } } +export function getCollectTokensFeedback( + baseTokenSymbol: string, + baseTokenBalance: string +) { + return `Collecting ${baseTokenBalance} ${baseTokenSymbol} from asset ` +} + // TODO: customize for compute export const computeFeedback: { [key in number]: string } = { 0: 'Ordering asset...', diff --git a/src/components/@shared/ButtonBuy/index.tsx b/src/components/@shared/ButtonBuy/index.tsx index e2db0e640..29d3d83a3 100644 --- a/src/components/@shared/ButtonBuy/index.tsx +++ b/src/components/@shared/ButtonBuy/index.tsx @@ -4,7 +4,7 @@ import styles from './index.module.css' import Loader from '../atoms/Loader' interface ButtonBuyProps { - action: 'download' | 'compute' + action: 'download' | 'compute' | 'collect' disabled: boolean hasPreviousOrder: boolean hasDatatoken: boolean @@ -153,6 +153,8 @@ export default function ButtonBuy({ ? 'Start Compute Job' : priceType === 'free' && algorithmPriceType === 'free' ? 'Order Compute Job' + : action === 'collect' + ? `Collect ${dtBalance} ${dtSymbol}` : `Buy Compute Job` return ( diff --git a/src/components/Asset/AssetActions/Download.module.css b/src/components/Asset/AssetActions/Download.module.css index d6dc45629..4efc7f192 100644 --- a/src/components/Asset/AssetActions/Download.module.css +++ b/src/components/Asset/AssetActions/Download.module.css @@ -16,3 +16,10 @@ width: 100%; margin-top: calc(var(--spacer)); } + +.collect { + width: 100%; + justify-content: center; + text-align: center; + margin-top: calc(var(--spacer) / 2); +} diff --git a/src/components/Asset/AssetActions/Download.tsx b/src/components/Asset/AssetActions/Download.tsx index 3f43f48f9..fd023aaa6 100644 --- a/src/components/Asset/AssetActions/Download.tsx +++ b/src/components/Asset/AssetActions/Download.tsx @@ -7,16 +7,37 @@ import ButtonBuy from '@shared/ButtonBuy' import { secondsToString } from '@utils/ddo' import AlgorithmDatasetsListForCompute from './Compute/AlgorithmDatasetsListForCompute' import styles from './Download.module.css' -import { FileMetadata, LoggerInstance, ZERO_ADDRESS } from '@oceanprotocol/lib' +import { + FileMetadata, + LoggerInstance, + ZERO_ADDRESS, + FixedRateExchange +} from '@oceanprotocol/lib' import { order } from '@utils/order' import { AssetExtended } from 'src/@types/AssetExtended' -import { buyDtFromPool, calculateBuyPrice } from '@utils/pool' +import { buyDtFromPool } from '@utils/pool' import { downloadFile } from '@utils/provider' -import { getOrderFeedback } from '@utils/feedback' +import { getCollectTokensFeedback, getOrderFeedback } from '@utils/feedback' import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing' import { OrderPriceAndFees } from 'src/@types/Price' import { toast } from 'react-toastify' +import { gql, OperationResult } from 'urql' +import { fetchData, getQueryContext } from '@utils/subgraph' +import { getOceanConfig } from '@utils/ocean' +import { FixedRateExchanges } from 'src/@types/subgraph/FixedRateExchanges' +const FixedRateExchangesQuery = gql` + query FixedRateExchanges($user: String, $exchangeId: String) { + fixedRateExchanges(where: { owner: $user, exchangeId: $exchangeId }) { + id + owner { + id + } + exchangeId + baseTokenBalance + } + } +` export default function Download({ asset, file, @@ -40,6 +61,9 @@ export default function Download({ const [isLoading, setIsLoading] = useState(false) const [isOwned, setIsOwned] = useState(false) const [validOrderTx, setValidOrderTx] = useState('') + const [isCollectLoading, setIsCollectLoading] = useState(false) + const [baseTokenBalance, setBaseTokenBalance] = useState(0) + const [collectStatusText, setCollectStatusText] = useState('') const [orderPriceAndFees, setOrderPriceAndFees] = useState() useEffect(() => { @@ -80,6 +104,29 @@ export default function Download({ isOwned ]) + useEffect(() => { + if (!accountId || asset.nft.owner !== accountId) return + const queryContext = getQueryContext(Number(asset.chainId)) + + async function getBaseTokenBalance() { + const variables = { + user: accountId.toLowerCase(), + exchangeId: asset?.accessDetails?.addressOrId + } + const result: OperationResult = await fetchData( + FixedRateExchangesQuery, + variables, + queryContext + ) + result?.data?.fixedRateExchanges[0]?.baseTokenBalance + ? setBaseTokenBalance( + parseInt(result?.data?.fixedRateExchanges[0]?.baseTokenBalance) + ) + : setBaseTokenBalance(0) + } + getBaseTokenBalance() + }, [accountId, asset?.accessDetails?.addressOrId, asset.chainId, asset.nft]) + async function handleOrderOrDownload() { setIsLoading(true) if (isOwned) { @@ -127,6 +174,37 @@ export default function Download({ setIsLoading(false) } + async function handleCollectTokens() { + setIsCollectLoading(true) + const config = getOceanConfig(asset?.chainId) + const fixed = new FixedRateExchange(web3, config.fixedRateExchangeAddress) + try { + setCollectStatusText( + getCollectTokensFeedback( + asset.accessDetails.baseToken?.symbol, + baseTokenBalance.toString() + ) + ) + + const tx = await fixed.collectBT( + accountId, + asset?.accessDetails?.addressOrId + ) + + if (!tx) { + setIsCollectLoading(false) + return + } + setBaseTokenBalance(0) + return tx + } catch (error) { + LoggerInstance.log(error.message) + setIsCollectLoading(false) + } finally { + setIsCollectLoading(false) + } + } + const PurchaseButton = () => ( ) + const CollectTokensButton = () => ( + + ) + return (
+
+ {asset.nft.owner === accountId && } +
{asset?.metadata?.type === 'algorithm' && (