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:
parent
d2c3b4b235
commit
96451b444a
@ -1,86 +1,86 @@
|
||||
// This is a dump of a typical `queryResult.results` response from Aquarius
|
||||
|
||||
export const assets: AssetExtended[] = [
|
||||
{
|
||||
'@context': ['https://w3id.org/did/v1'],
|
||||
chainId: 5,
|
||||
credentials: {
|
||||
allow: [],
|
||||
deny: []
|
||||
},
|
||||
datatokens: [
|
||||
{
|
||||
address: '0x8f90ABA587aBE691E136D7d1a64762A2FF15bE4D',
|
||||
name: 'DATASET_DT1',
|
||||
serviceId: '2',
|
||||
symbol: 'DATASET_DT1'
|
||||
}
|
||||
],
|
||||
event: {
|
||||
block: 7745556,
|
||||
contract: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0',
|
||||
datetime: '2022-10-10T14:31:24',
|
||||
from: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260',
|
||||
tx: '0x6a0d161cffc090fb18e5fc945290052c0d475e6e4115462da115a7bbcb6202b8'
|
||||
},
|
||||
id: 'did:op:48406caf76d092e08af6703dde5bbab79ed7528939d8bbe733134284faf39075',
|
||||
metadata: {
|
||||
author: 'Trent',
|
||||
created: '2022-09-19T10:55:11Z',
|
||||
description: 'Branin dataset',
|
||||
license: 'CC0: PublicDomain',
|
||||
name: 'Branin dataset',
|
||||
type: 'dataset',
|
||||
updated: '2022-09-19T10:55:11Z'
|
||||
},
|
||||
nft: {
|
||||
address: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0',
|
||||
created: '2022-10-10T14:31:24',
|
||||
name: 'DATASET_DN1',
|
||||
owner: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260',
|
||||
state: 0,
|
||||
symbol: 'DATASET_DN1',
|
||||
tokenURI: 'https://oceanprotocol.com/nft/'
|
||||
},
|
||||
nftAddress: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0',
|
||||
purgatory: {
|
||||
state: false,
|
||||
reason: ''
|
||||
},
|
||||
services: [
|
||||
{
|
||||
compute: {
|
||||
allowNetworkAccess: true,
|
||||
allowRawAlgorithm: false,
|
||||
publisherTrustedAlgorithmPublishers: [],
|
||||
publisherTrustedAlgorithms: []
|
||||
},
|
||||
datatokenAddress: '0x8f90ABA587aBE691E136D7d1a64762A2FF15bE4D',
|
||||
description: 'Compute service',
|
||||
files:
|
||||
'0x0405a4c9efd6ed507c40387ffebe4093c901e003f1c6ec57c8a4d2b1c28382743bd2097dfc551ba8972992f9b45ea2046d7d53312d97f92dc7c49dec2769e4d8948f7a2b6df437c93229aa4dea7a636af1aec00dcfc8693040f645880e01a428f3ce7e267eaa49dbefce4de47adcab4abc92f062e0ea19ceff2664071710e42f293aaa96d4c0ff1cccb13cdbaa206a705344954ccf1967e58a3586b4953a1e616df86b565bc837fac2f5aa865617a108c737d9be14b19f98fa6204c8c06f284f52cdf4151b987cfa463350fd8f99019a610cd61fa7463b0e77ba93bd42239b999b86eef475a6c97e295a8e0332a843ad4be9254078d70d6c6e4a11f28bc7e61f5a44f9c68d2268b5b8e4f3908c57a41b0ab6ea78163e0b14076f59212a6b37b43811f20a3afc84c67acaa6d1075b06a6cba9f3d778badab3e97b7b21907ee718374819a68c5b8768fc70dd9194c8f309a48bea17178902532273',
|
||||
id: '2',
|
||||
name: 'Compute service',
|
||||
serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
|
||||
timeout: 2592000,
|
||||
type: 'compute'
|
||||
}
|
||||
],
|
||||
stats: {
|
||||
allocated: 0,
|
||||
orders: 0,
|
||||
price: {
|
||||
value: 3231343254,
|
||||
tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
|
||||
tokenSymbol: 'OCEAN'
|
||||
}
|
||||
},
|
||||
version: '4.1.0',
|
||||
accessDetails: {
|
||||
templateId: 1,
|
||||
type: 'NOT_SUPPORTED'
|
||||
} as any
|
||||
},
|
||||
// {
|
||||
// '@context': ['https://w3id.org/did/v1'],
|
||||
// chainId: 5,
|
||||
// credentials: {
|
||||
// allow: [],
|
||||
// deny: []
|
||||
// },
|
||||
// datatokens: [
|
||||
// {
|
||||
// address: '0x8f90ABA587aBE691E136D7d1a64762A2FF15bE4D',
|
||||
// name: 'DATASET_DT1',
|
||||
// serviceId: '2',
|
||||
// symbol: 'DATASET_DT1'
|
||||
// }
|
||||
// ],
|
||||
// event: {
|
||||
// block: 7745556,
|
||||
// contract: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0',
|
||||
// datetime: '2022-10-10T14:31:24',
|
||||
// from: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260',
|
||||
// tx: '0x6a0d161cffc090fb18e5fc945290052c0d475e6e4115462da115a7bbcb6202b8'
|
||||
// },
|
||||
// id: 'did:op:48406caf76d092e08af6703dde5bbab79ed7528939d8bbe733134284faf39075',
|
||||
// metadata: {
|
||||
// author: 'Trent',
|
||||
// created: '2022-09-19T10:55:11Z',
|
||||
// description: 'Branin dataset',
|
||||
// license: 'CC0: PublicDomain',
|
||||
// name: 'Branin dataset',
|
||||
// type: 'dataset',
|
||||
// updated: '2022-09-19T10:55:11Z'
|
||||
// },
|
||||
// nft: {
|
||||
// address: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0',
|
||||
// created: '2022-10-10T14:31:24',
|
||||
// name: 'DATASET_DN1',
|
||||
// owner: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260',
|
||||
// state: 0,
|
||||
// symbol: 'DATASET_DN1',
|
||||
// tokenURI: 'https://oceanprotocol.com/nft/'
|
||||
// },
|
||||
// nftAddress: '0xE07B0a3403fAD9568cd970C04C3D08A9c1Ab93d0',
|
||||
// purgatory: {
|
||||
// state: false,
|
||||
// reason: ''
|
||||
// },
|
||||
// services: [
|
||||
// {
|
||||
// compute: {
|
||||
// allowNetworkAccess: true,
|
||||
// allowRawAlgorithm: false,
|
||||
// publisherTrustedAlgorithmPublishers: [],
|
||||
// publisherTrustedAlgorithms: []
|
||||
// },
|
||||
// datatokenAddress: '0x8f90ABA587aBE691E136D7d1a64762A2FF15bE4D',
|
||||
// description: 'Compute service',
|
||||
// files:
|
||||
// '0x0405a4c9efd6ed507c40387ffebe4093c901e003f1c6ec57c8a4d2b1c28382743bd2097dfc551ba8972992f9b45ea2046d7d53312d97f92dc7c49dec2769e4d8948f7a2b6df437c93229aa4dea7a636af1aec00dcfc8693040f645880e01a428f3ce7e267eaa49dbefce4de47adcab4abc92f062e0ea19ceff2664071710e42f293aaa96d4c0ff1cccb13cdbaa206a705344954ccf1967e58a3586b4953a1e616df86b565bc837fac2f5aa865617a108c737d9be14b19f98fa6204c8c06f284f52cdf4151b987cfa463350fd8f99019a610cd61fa7463b0e77ba93bd42239b999b86eef475a6c97e295a8e0332a843ad4be9254078d70d6c6e4a11f28bc7e61f5a44f9c68d2268b5b8e4f3908c57a41b0ab6ea78163e0b14076f59212a6b37b43811f20a3afc84c67acaa6d1075b06a6cba9f3d778badab3e97b7b21907ee718374819a68c5b8768fc70dd9194c8f309a48bea17178902532273',
|
||||
// id: '2',
|
||||
// name: 'Compute service',
|
||||
// serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
|
||||
// timeout: 2592000,
|
||||
// type: 'compute'
|
||||
// }
|
||||
// ],
|
||||
// stats: {
|
||||
// allocated: 0,
|
||||
// orders: 0,
|
||||
// price: {
|
||||
// value: 3231343254,
|
||||
// tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
|
||||
// tokenSymbol: 'OCEAN'
|
||||
// }
|
||||
// },
|
||||
// version: '4.1.0',
|
||||
// accessDetails: {
|
||||
// templateId: 1,
|
||||
// type: 'NOT_SUPPORTED'
|
||||
// } as any
|
||||
// },
|
||||
{
|
||||
'@context': ['https://w3id.org/did/v1'],
|
||||
chainId: 137,
|
||||
@ -474,224 +474,224 @@ export const assets: AssetExtended[] = [
|
||||
validOrderTx: null
|
||||
}
|
||||
},
|
||||
{
|
||||
'@context': ['https://w3id.org/did/v1'],
|
||||
accessDetails: {
|
||||
templateId: 1,
|
||||
publisherMarketOrderFee: '0',
|
||||
type: 'fixed',
|
||||
addressOrId:
|
||||
'0xb9060a712a494f584b072b0753dc275e1c531178510679ac085053ee38b5f742',
|
||||
price: '5',
|
||||
isPurchasable: true,
|
||||
baseToken: {
|
||||
address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a',
|
||||
name: 'Ocean Token',
|
||||
symbol: 'OCEAN',
|
||||
decimals: 18
|
||||
},
|
||||
datatoken: {
|
||||
address: '0x0a9cfaff200efb1d6f125e342dfc78fb3edd28a4',
|
||||
name: 'Inspired Ling Token',
|
||||
symbol: 'INSLIN-54'
|
||||
},
|
||||
isOwned: false,
|
||||
validOrderTx: null
|
||||
},
|
||||
chainId: 5,
|
||||
datatokens: [
|
||||
{
|
||||
address: '0x0A9CFaFf200efb1d6F125E342Dfc78Fb3edD28A4',
|
||||
name: 'Inspired Ling Token',
|
||||
serviceId:
|
||||
'383b0e1b8dc3e816af394bfae899eb0c9826f2383602c0fbcd70705e1e9c1302',
|
||||
symbol: 'INSLIN-54'
|
||||
}
|
||||
],
|
||||
event: {
|
||||
block: 7723888,
|
||||
contract: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B',
|
||||
datetime: '2022-10-06T20:31:36',
|
||||
from: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
|
||||
tx: '0x18b1d0af634fab3196921a99618fd9333c4a2113a016bf4757d609ddfdb64432'
|
||||
},
|
||||
id: 'did:op:6b4314bd7345d07a10ba2c82a352655273b00cdceb2eedd31c8e0d2b5881eb16',
|
||||
metadata: {
|
||||
additionalInformation: {
|
||||
termsAndConditions: true
|
||||
},
|
||||
author:
|
||||
'Haxby, J. V., Gobbini, M. I., Furey, M. L., Ishai, A., Schouten, J. L. & Pietrini, P. ',
|
||||
created: '2022-10-06T20:30:01Z',
|
||||
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',
|
||||
license: 'https://market.oceanprotocol.com/terms',
|
||||
name: 'Faces and Objects in Ventral Temporal Cortex (fMRI) (Compute) ',
|
||||
tags: ['fmri', 'bold', 'visual-stimuli', '4d', 'functional'],
|
||||
type: 'dataset',
|
||||
updated: '2022-10-06T20:30:01Z'
|
||||
},
|
||||
nft: {
|
||||
address: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B',
|
||||
created: '2022-10-06T20:30:36',
|
||||
name: 'Ocean Data NFT',
|
||||
owner: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
|
||||
state: 0,
|
||||
symbol: 'OCEAN-NFT',
|
||||
tokenURI:
|
||||
'data:application/json;base64,eyJuYW1lIjoiT2NlYW4gRGF0YSBORlQiLCJzeW1ib2wiOiJPQ0VBTi1ORlQiLCJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYW4gYXNzZXQgaW4gdGhlIE9jZWFuIFByb3RvY29sIHY0IGVjb3N5c3RlbS5cblxuVmlldyBvbiBOZXVyYURBTyBNYXJrZXRwbGFjZTogaHR0cHM6Ly9tYXJrZXQub2NlYW5wcm90b2NvbC5jb20vYXNzZXQvZGlkOm9wOjZiNDMxNGJkNzM0NWQwN2ExMGJhMmM4MmEzNTI2NTUyNzNiMDBjZGNlYjJlZWRkMzFjOGUwZDJiNTg4MWViMTYiLCJleHRlcm5hbF91cmwiOiJodHRwczovL21hcmtldC5vY2VhbnByb3RvY29sLmNvbS9hc3NldC9kaWQ6b3A6NmI0MzE0YmQ3MzQ1ZDA3YTEwYmEyYzgyYTM1MjY1NTI3M2IwMGNkY2ViMmVlZGQzMWM4ZTBkMmI1ODgxZWIxNiIsImJhY2tncm91bmRfY29sb3IiOiIxNDE0MTQiLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sLCUzQ3N2ZyB2aWV3Qm94PScwIDAgOTkgOTknIGZpbGw9J3VuZGVmaW5lZCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyNzcnIGQ9J00wLDk5TDAsMjVDMTIsMjYgMjQsMjcgMzUsMjhDNDUsMjggNTIsMjggNjMsMjhDNzMsMjcgODYsMjYgOTksMjVMOTksOTlaJy8lM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5MmJiJyBkPSdNMCw5OUwwLDQ4QzExLDQ3IDIzLDQ2IDM0LDQ3QzQ0LDQ3IDU1LDQ4IDY2LDQ5Qzc2LDQ5IDg3LDQ5IDk5LDQ5TDk5LDk5WiclM0UlM0MvcGF0aCUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyZmYnIGQ9J00wLDk5TDAsODBDOCw3NCAxNyw2OCAyOCw3MEMzOCw3MSA1MSw3OSA2NCw4MEM3Niw4MCA4Nyw3NCA5OSw2OUw5OSw5OVonJTNFJTNDL3BhdGglM0UlM0Mvc3ZnJTNFIn0='
|
||||
},
|
||||
nftAddress: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B',
|
||||
purgatory: {
|
||||
state: false,
|
||||
reason: ''
|
||||
},
|
||||
services: [
|
||||
{
|
||||
compute: {
|
||||
allowNetworkAccess: true,
|
||||
allowRawAlgorithm: false,
|
||||
publisherTrustedAlgorithmPublishers: [],
|
||||
publisherTrustedAlgorithms: [
|
||||
{
|
||||
containerSectionChecksum:
|
||||
'54eb02210bad8a5fbe229e1d131a68e80fe32709a196c6ce49f33e5d378b1195',
|
||||
did: 'did:op:f86dedf3c872f79f788627025685a680eaac9f8bd7b6e622164fd8563e21e836',
|
||||
filesChecksum:
|
||||
'2f8afee0a35fbeb72a447c7d1437b6c83f937e6d65a6c7d1990548cc21ff254c'
|
||||
}
|
||||
]
|
||||
},
|
||||
datatokenAddress: '0x0A9CFaFf200efb1d6F125E342Dfc78Fb3edD28A4',
|
||||
files:
|
||||
'0x0479c75f624d86700c6b33deb392b2d60bd66a5bd92778851eb124bf3785f270b356ce42a228f5a5eb4dead55fc7892a3f4a9f114dfa5493f480146af72ccdcca5816996b0ff002a69e113509256494d64ad39b86be92c7668baa5060c98f402f60fcf7acd0d25e923cecaa5f483fd14a8568a782023b164f8424a95b43c165e813fd031c7b5887ac467af76d94d2ca8b45e34951694cc60ead2c15137eebc60703b9a12a4a4643ecd343de8d0326abb87e093abacf55ba83c06b2840284e8f17d9c498f02dcfd74239371c25ad0fcac703be994065b7ffa12f3a47ba3d363d31f475e6519e7cc5a65e74cafdf029a1d73a007e886206f4b4e36251721866f399076dd2435c314cdfdc42638a570fe57bb33f2935861c01ec708f80acd738d2a45dd64d374278dc63026ac7f4f8dba979e7cdc4e24e5f39aef4550b1cbf190525bdfa0e30900084aef223863e54bd0866ab958',
|
||||
id: '383b0e1b8dc3e816af394bfae899eb0c9826f2383602c0fbcd70705e1e9c1302',
|
||||
serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
|
||||
timeout: 86400,
|
||||
type: 'compute'
|
||||
}
|
||||
],
|
||||
stats: {
|
||||
allocated: 0,
|
||||
orders: 1,
|
||||
price: {
|
||||
value: 3231343254,
|
||||
tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
|
||||
tokenSymbol: 'OCEAN'
|
||||
}
|
||||
},
|
||||
version: '4.1.0'
|
||||
},
|
||||
{
|
||||
'@context': ['https://w3id.org/did/v1'],
|
||||
chainId: 5,
|
||||
datatokens: [
|
||||
{
|
||||
address: '0x7626dE49a774c18E0f7Fc658821a87E103f80fab',
|
||||
name: 'Lovely Prawn Token',
|
||||
serviceId:
|
||||
'be48353fe208e765c24b0a344c2cc826ff0ea18582a162d67f6ad23078595d59',
|
||||
symbol: 'LOVPRA-51'
|
||||
}
|
||||
],
|
||||
event: {
|
||||
block: 7723861,
|
||||
contract: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F',
|
||||
datetime: '2022-10-06T20:24:24',
|
||||
from: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
|
||||
tx: '0xdca6494d123c796443c6ce46bb4c02938526a03f86661941eaddcb76377f5825'
|
||||
},
|
||||
id: 'did:op:f86dedf3c872f79f788627025685a680eaac9f8bd7b6e622164fd8563e21e836',
|
||||
metadata: {
|
||||
additionalInformation: {
|
||||
termsAndConditions: true
|
||||
},
|
||||
algorithm: {
|
||||
container: {
|
||||
checksum:
|
||||
'sha256:a981ed6282271fc5492c382cd11d5045641880f738c05a855ed6de8d0eecea8f',
|
||||
entrypoint: 'python3.8 $ALGO',
|
||||
image: 'anmu06/c2d_neuradao',
|
||||
tag: 'latest'
|
||||
},
|
||||
language: 'py',
|
||||
version: '0.1'
|
||||
},
|
||||
author: 'Nilearn ',
|
||||
created: '2022-10-06T20:24:15Z',
|
||||
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. ',
|
||||
license: 'https://market.oceanprotocol.com/terms',
|
||||
name: 'fMRI Time-Averaged EPI Visualization ',
|
||||
tags: ['epi', 'bold', 'fmri'],
|
||||
type: 'algorithm',
|
||||
updated: '2022-10-06T20:24:15Z'
|
||||
},
|
||||
nft: {
|
||||
address: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F',
|
||||
created: '2022-10-06T20:24:24',
|
||||
name: 'Ocean Data NFT',
|
||||
owner: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
|
||||
state: 0,
|
||||
symbol: 'OCEAN-NFT',
|
||||
tokenURI:
|
||||
'data:application/json;base64,eyJuYW1lIjoiT2NlYW4gRGF0YSBORlQiLCJzeW1ib2wiOiJPQ0VBTi1ORlQiLCJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYW4gYXNzZXQgaW4gdGhlIE9jZWFuIFByb3RvY29sIHY0IGVjb3N5c3RlbS5cblxuVmlldyBvbiBOZXVyYURBTyBNYXJrZXRwbGFjZTogaHR0cHM6Ly9tYXJrZXQub2NlYW5wcm90b2NvbC5jb20vYXNzZXQvZGlkOm9wOmY4NmRlZGYzYzg3MmY3OWY3ODg2MjcwMjU2ODVhNjgwZWFhYzlmOGJkN2I2ZTYyMjE2NGZkODU2M2UyMWU4MzYiLCJleHRlcm5hbF91cmwiOiJodHRwczovL21hcmtldC5vY2VhbnByb3RvY29sLmNvbS9hc3NldC9kaWQ6b3A6Zjg2ZGVkZjNjODcyZjc5Zjc4ODYyNzAyNTY4NWE2ODBlYWFjOWY4YmQ3YjZlNjIyMTY0ZmQ4NTYzZTIxZTgzNiIsImJhY2tncm91bmRfY29sb3IiOiIxNDE0MTQiLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sLCUzQ3N2ZyB2aWV3Qm94PScwIDAgOTkgOTknIGZpbGw9J3VuZGVmaW5lZCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyNzcnIGQ9J00wLDk5TDAsMjVDOCwyNCAxNiwyMyAyNCwyNEMzMSwyNCAzOCwyNyA0NywyOEM1NSwyOCA2NiwyOCA3NSwyOEM4MywyNyA5MSwyNyA5OSwyN0w5OSw5OVonLyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyYmInIGQ9J00wLDk5TDAsNTRDOCw1MiAxNiw1MSAyNSw1MEMzMyw0OCA0MSw0NyA0OSw0NkM1Niw0NCA2NCw0MSA3Myw0M0M4MSw0NCA5MCw0OSA5OSw1NUw5OSw5OVonJTNFJTNDL3BhdGglM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5MmZmJyBkPSdNMCw5OUwwLDczQzcsNzIgMTUsNzEgMjUsNzBDMzQsNjggNDQsNjcgNTIsNjlDNTksNzAgNjQsNzQgNzIsNzVDNzksNzUgODksNzIgOTksNzBMOTksOTlaJyUzRSUzQy9wYXRoJTNFJTNDL3N2ZyUzRSJ9'
|
||||
},
|
||||
nftAddress: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F',
|
||||
purgatory: {
|
||||
state: false,
|
||||
reason: ''
|
||||
},
|
||||
services: [
|
||||
{
|
||||
compute: {
|
||||
allowNetworkAccess: true,
|
||||
allowRawAlgorithm: false,
|
||||
publisherTrustedAlgorithmPublishers: [],
|
||||
publisherTrustedAlgorithms: []
|
||||
},
|
||||
datatokenAddress: '0x7626dE49a774c18E0f7Fc658821a87E103f80fab',
|
||||
files:
|
||||
'0x0498ac38d3ac04dc4f33b5a91358b8e121fa5bc86bcb20b8bc1c27ce1f47db491efda1bf90ab4d4c893a636d570f8fdc29eae7e010f846a34cfc24bc751b64f9d104afcc7f22c82a8cffb412886ba9649b73c2b6fe95e5fab0882bc8174823db08af64c14177bfafad0fc43bb9c9db95df61dabeb0ac1fbb27c07d3705cdf6f8fdd5cb37fc2c50ae0db6bf778b7f9f5475ce1730edacd8e48aa99548184ece9df8fabca2bd7535caf9107b3312f15aaaf6bbc2143782824aac54a04a5136bd1af2121b579b8eaa71abccff4bc4147b592e2b7b7a6d928870861996e67b69277ef60128d7cf86ce5abbf860194ab5ebd8dcbc2a29fbddf5f2482510736de7e9427b4f61306df121a1bd757f0c7d0ae7c702bdff2c85f9b9c7956ced9561693da910ac211e3f35a295981a443695be9e8854554c87fd4747a709a3e43a220e380b4c36f4de92f4b0e2a6301b33c9b22356de1fec345b268e632673e3c70bc5eb',
|
||||
id: 'be48353fe208e765c24b0a344c2cc826ff0ea18582a162d67f6ad23078595d59',
|
||||
serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
|
||||
timeout: 0,
|
||||
type: 'compute'
|
||||
}
|
||||
],
|
||||
stats: {
|
||||
allocated: 0,
|
||||
orders: 1,
|
||||
price: {
|
||||
value: 3231343254,
|
||||
tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
|
||||
tokenSymbol: 'OCEAN'
|
||||
}
|
||||
},
|
||||
version: '4.1.0',
|
||||
accessDetails: {
|
||||
templateId: 1,
|
||||
publisherMarketOrderFee: '0',
|
||||
type: 'fixed',
|
||||
addressOrId:
|
||||
'0x1c4f156e06d1b9eeb5aa367869c9d14386918aa12ef4969c3bf242ef6bcba7c8',
|
||||
price: '5',
|
||||
isPurchasable: true,
|
||||
baseToken: {
|
||||
address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a',
|
||||
name: 'Ocean Token',
|
||||
symbol: 'OCEAN',
|
||||
decimals: 18
|
||||
},
|
||||
datatoken: {
|
||||
address: '0x7626de49a774c18e0f7fc658821a87e103f80fab',
|
||||
name: 'Lovely Prawn Token',
|
||||
symbol: 'LOVPRA-51'
|
||||
},
|
||||
isOwned: false,
|
||||
validOrderTx: null
|
||||
}
|
||||
},
|
||||
// {
|
||||
// '@context': ['https://w3id.org/did/v1'],
|
||||
// accessDetails: {
|
||||
// templateId: 1,
|
||||
// publisherMarketOrderFee: '0',
|
||||
// type: 'fixed',
|
||||
// addressOrId:
|
||||
// '0xb9060a712a494f584b072b0753dc275e1c531178510679ac085053ee38b5f742',
|
||||
// price: '5',
|
||||
// isPurchasable: true,
|
||||
// baseToken: {
|
||||
// address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a',
|
||||
// name: 'Ocean Token',
|
||||
// symbol: 'OCEAN',
|
||||
// decimals: 18
|
||||
// },
|
||||
// datatoken: {
|
||||
// address: '0x0a9cfaff200efb1d6f125e342dfc78fb3edd28a4',
|
||||
// name: 'Inspired Ling Token',
|
||||
// symbol: 'INSLIN-54'
|
||||
// },
|
||||
// isOwned: false,
|
||||
// validOrderTx: null
|
||||
// },
|
||||
// chainId: 5,
|
||||
// datatokens: [
|
||||
// {
|
||||
// address: '0x0A9CFaFf200efb1d6F125E342Dfc78Fb3edD28A4',
|
||||
// name: 'Inspired Ling Token',
|
||||
// serviceId:
|
||||
// '383b0e1b8dc3e816af394bfae899eb0c9826f2383602c0fbcd70705e1e9c1302',
|
||||
// symbol: 'INSLIN-54'
|
||||
// }
|
||||
// ],
|
||||
// event: {
|
||||
// block: 7723888,
|
||||
// contract: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B',
|
||||
// datetime: '2022-10-06T20:31:36',
|
||||
// from: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
|
||||
// tx: '0x18b1d0af634fab3196921a99618fd9333c4a2113a016bf4757d609ddfdb64432'
|
||||
// },
|
||||
// id: 'did:op:6b4314bd7345d07a10ba2c82a352655273b00cdceb2eedd31c8e0d2b5881eb16',
|
||||
// metadata: {
|
||||
// additionalInformation: {
|
||||
// termsAndConditions: true
|
||||
// },
|
||||
// author:
|
||||
// 'Haxby, J. V., Gobbini, M. I., Furey, M. L., Ishai, A., Schouten, J. L. & Pietrini, P. ',
|
||||
// created: '2022-10-06T20:30:01Z',
|
||||
// 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',
|
||||
// license: 'https://market.oceanprotocol.com/terms',
|
||||
// name: 'Faces and Objects in Ventral Temporal Cortex (fMRI) (Compute) ',
|
||||
// tags: ['fmri', 'bold', 'visual-stimuli', '4d', 'functional'],
|
||||
// type: 'dataset',
|
||||
// updated: '2022-10-06T20:30:01Z'
|
||||
// },
|
||||
// nft: {
|
||||
// address: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B',
|
||||
// created: '2022-10-06T20:30:36',
|
||||
// name: 'Ocean Data NFT',
|
||||
// owner: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
|
||||
// state: 0,
|
||||
// symbol: 'OCEAN-NFT',
|
||||
// tokenURI:
|
||||
// 'data:application/json;base64,eyJuYW1lIjoiT2NlYW4gRGF0YSBORlQiLCJzeW1ib2wiOiJPQ0VBTi1ORlQiLCJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYW4gYXNzZXQgaW4gdGhlIE9jZWFuIFByb3RvY29sIHY0IGVjb3N5c3RlbS5cblxuVmlldyBvbiBOZXVyYURBTyBNYXJrZXRwbGFjZTogaHR0cHM6Ly9tYXJrZXQub2NlYW5wcm90b2NvbC5jb20vYXNzZXQvZGlkOm9wOjZiNDMxNGJkNzM0NWQwN2ExMGJhMmM4MmEzNTI2NTUyNzNiMDBjZGNlYjJlZWRkMzFjOGUwZDJiNTg4MWViMTYiLCJleHRlcm5hbF91cmwiOiJodHRwczovL21hcmtldC5vY2VhbnByb3RvY29sLmNvbS9hc3NldC9kaWQ6b3A6NmI0MzE0YmQ3MzQ1ZDA3YTEwYmEyYzgyYTM1MjY1NTI3M2IwMGNkY2ViMmVlZGQzMWM4ZTBkMmI1ODgxZWIxNiIsImJhY2tncm91bmRfY29sb3IiOiIxNDE0MTQiLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sLCUzQ3N2ZyB2aWV3Qm94PScwIDAgOTkgOTknIGZpbGw9J3VuZGVmaW5lZCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyNzcnIGQ9J00wLDk5TDAsMjVDMTIsMjYgMjQsMjcgMzUsMjhDNDUsMjggNTIsMjggNjMsMjhDNzMsMjcgODYsMjYgOTksMjVMOTksOTlaJy8lM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5MmJiJyBkPSdNMCw5OUwwLDQ4QzExLDQ3IDIzLDQ2IDM0LDQ3QzQ0LDQ3IDU1LDQ4IDY2LDQ5Qzc2LDQ5IDg3LDQ5IDk5LDQ5TDk5LDk5WiclM0UlM0MvcGF0aCUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyZmYnIGQ9J00wLDk5TDAsODBDOCw3NCAxNyw2OCAyOCw3MEMzOCw3MSA1MSw3OSA2NCw4MEM3Niw4MCA4Nyw3NCA5OSw2OUw5OSw5OVonJTNFJTNDL3BhdGglM0UlM0Mvc3ZnJTNFIn0='
|
||||
// },
|
||||
// nftAddress: '0xeB7eC160ce8F73bE2e7d542c2283F1aEa163C07B',
|
||||
// purgatory: {
|
||||
// state: false,
|
||||
// reason: ''
|
||||
// },
|
||||
// services: [
|
||||
// {
|
||||
// compute: {
|
||||
// allowNetworkAccess: true,
|
||||
// allowRawAlgorithm: false,
|
||||
// publisherTrustedAlgorithmPublishers: [],
|
||||
// publisherTrustedAlgorithms: [
|
||||
// {
|
||||
// containerSectionChecksum:
|
||||
// '54eb02210bad8a5fbe229e1d131a68e80fe32709a196c6ce49f33e5d378b1195',
|
||||
// did: 'did:op:f86dedf3c872f79f788627025685a680eaac9f8bd7b6e622164fd8563e21e836',
|
||||
// filesChecksum:
|
||||
// '2f8afee0a35fbeb72a447c7d1437b6c83f937e6d65a6c7d1990548cc21ff254c'
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// datatokenAddress: '0x0A9CFaFf200efb1d6F125E342Dfc78Fb3edD28A4',
|
||||
// files:
|
||||
// '0x0479c75f624d86700c6b33deb392b2d60bd66a5bd92778851eb124bf3785f270b356ce42a228f5a5eb4dead55fc7892a3f4a9f114dfa5493f480146af72ccdcca5816996b0ff002a69e113509256494d64ad39b86be92c7668baa5060c98f402f60fcf7acd0d25e923cecaa5f483fd14a8568a782023b164f8424a95b43c165e813fd031c7b5887ac467af76d94d2ca8b45e34951694cc60ead2c15137eebc60703b9a12a4a4643ecd343de8d0326abb87e093abacf55ba83c06b2840284e8f17d9c498f02dcfd74239371c25ad0fcac703be994065b7ffa12f3a47ba3d363d31f475e6519e7cc5a65e74cafdf029a1d73a007e886206f4b4e36251721866f399076dd2435c314cdfdc42638a570fe57bb33f2935861c01ec708f80acd738d2a45dd64d374278dc63026ac7f4f8dba979e7cdc4e24e5f39aef4550b1cbf190525bdfa0e30900084aef223863e54bd0866ab958',
|
||||
// id: '383b0e1b8dc3e816af394bfae899eb0c9826f2383602c0fbcd70705e1e9c1302',
|
||||
// serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
|
||||
// timeout: 86400,
|
||||
// type: 'compute'
|
||||
// }
|
||||
// ],
|
||||
// stats: {
|
||||
// allocated: 0,
|
||||
// orders: 1,
|
||||
// price: {
|
||||
// value: 3231343254,
|
||||
// tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
|
||||
// tokenSymbol: 'OCEAN'
|
||||
// }
|
||||
// },
|
||||
// version: '4.1.0'
|
||||
// },
|
||||
// {
|
||||
// '@context': ['https://w3id.org/did/v1'],
|
||||
// chainId: 5,
|
||||
// datatokens: [
|
||||
// {
|
||||
// address: '0x7626dE49a774c18E0f7Fc658821a87E103f80fab',
|
||||
// name: 'Lovely Prawn Token',
|
||||
// serviceId:
|
||||
// 'be48353fe208e765c24b0a344c2cc826ff0ea18582a162d67f6ad23078595d59',
|
||||
// symbol: 'LOVPRA-51'
|
||||
// }
|
||||
// ],
|
||||
// event: {
|
||||
// block: 7723861,
|
||||
// contract: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F',
|
||||
// datetime: '2022-10-06T20:24:24',
|
||||
// from: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
|
||||
// tx: '0xdca6494d123c796443c6ce46bb4c02938526a03f86661941eaddcb76377f5825'
|
||||
// },
|
||||
// id: 'did:op:f86dedf3c872f79f788627025685a680eaac9f8bd7b6e622164fd8563e21e836',
|
||||
// metadata: {
|
||||
// additionalInformation: {
|
||||
// termsAndConditions: true
|
||||
// },
|
||||
// algorithm: {
|
||||
// container: {
|
||||
// checksum:
|
||||
// 'sha256:a981ed6282271fc5492c382cd11d5045641880f738c05a855ed6de8d0eecea8f',
|
||||
// entrypoint: 'python3.8 $ALGO',
|
||||
// image: 'anmu06/c2d_neuradao',
|
||||
// tag: 'latest'
|
||||
// },
|
||||
// language: 'py',
|
||||
// version: '0.1'
|
||||
// },
|
||||
// author: 'Nilearn ',
|
||||
// created: '2022-10-06T20:24:15Z',
|
||||
// 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. ',
|
||||
// license: 'https://market.oceanprotocol.com/terms',
|
||||
// name: 'fMRI Time-Averaged EPI Visualization ',
|
||||
// tags: ['epi', 'bold', 'fmri'],
|
||||
// type: 'algorithm',
|
||||
// updated: '2022-10-06T20:24:15Z'
|
||||
// },
|
||||
// nft: {
|
||||
// address: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F',
|
||||
// created: '2022-10-06T20:24:24',
|
||||
// name: 'Ocean Data NFT',
|
||||
// owner: '0x7E0ad0B2CD0560Caf9a4Fc25904d2AB7238d140b',
|
||||
// state: 0,
|
||||
// symbol: 'OCEAN-NFT',
|
||||
// tokenURI:
|
||||
// 'data:application/json;base64,eyJuYW1lIjoiT2NlYW4gRGF0YSBORlQiLCJzeW1ib2wiOiJPQ0VBTi1ORlQiLCJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYW4gYXNzZXQgaW4gdGhlIE9jZWFuIFByb3RvY29sIHY0IGVjb3N5c3RlbS5cblxuVmlldyBvbiBOZXVyYURBTyBNYXJrZXRwbGFjZTogaHR0cHM6Ly9tYXJrZXQub2NlYW5wcm90b2NvbC5jb20vYXNzZXQvZGlkOm9wOmY4NmRlZGYzYzg3MmY3OWY3ODg2MjcwMjU2ODVhNjgwZWFhYzlmOGJkN2I2ZTYyMjE2NGZkODU2M2UyMWU4MzYiLCJleHRlcm5hbF91cmwiOiJodHRwczovL21hcmtldC5vY2VhbnByb3RvY29sLmNvbS9hc3NldC9kaWQ6b3A6Zjg2ZGVkZjNjODcyZjc5Zjc4ODYyNzAyNTY4NWE2ODBlYWFjOWY4YmQ3YjZlNjIyMTY0ZmQ4NTYzZTIxZTgzNiIsImJhY2tncm91bmRfY29sb3IiOiIxNDE0MTQiLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sLCUzQ3N2ZyB2aWV3Qm94PScwIDAgOTkgOTknIGZpbGw9J3VuZGVmaW5lZCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyNzcnIGQ9J00wLDk5TDAsMjVDOCwyNCAxNiwyMyAyNCwyNEMzMSwyNCAzOCwyNyA0NywyOEM1NSwyOCA2NiwyOCA3NSwyOEM4MywyNyA5MSwyNyA5OSwyN0w5OSw5OVonLyUzRSUzQ3BhdGggZmlsbD0nJTIzZmY0MDkyYmInIGQ9J00wLDk5TDAsNTRDOCw1MiAxNiw1MSAyNSw1MEMzMyw0OCA0MSw0NyA0OSw0NkM1Niw0NCA2NCw0MSA3Myw0M0M4MSw0NCA5MCw0OSA5OSw1NUw5OSw5OVonJTNFJTNDL3BhdGglM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5MmZmJyBkPSdNMCw5OUwwLDczQzcsNzIgMTUsNzEgMjUsNzBDMzQsNjggNDQsNjcgNTIsNjlDNTksNzAgNjQsNzQgNzIsNzVDNzksNzUgODksNzIgOTksNzBMOTksOTlaJyUzRSUzQy9wYXRoJTNFJTNDL3N2ZyUzRSJ9'
|
||||
// },
|
||||
// nftAddress: '0xACa9d4Df6a4dfF29913A111099bc4aC6363C124F',
|
||||
// purgatory: {
|
||||
// state: false,
|
||||
// reason: ''
|
||||
// },
|
||||
// services: [
|
||||
// {
|
||||
// compute: {
|
||||
// allowNetworkAccess: true,
|
||||
// allowRawAlgorithm: false,
|
||||
// publisherTrustedAlgorithmPublishers: [],
|
||||
// publisherTrustedAlgorithms: []
|
||||
// },
|
||||
// datatokenAddress: '0x7626dE49a774c18E0f7Fc658821a87E103f80fab',
|
||||
// files:
|
||||
// '0x0498ac38d3ac04dc4f33b5a91358b8e121fa5bc86bcb20b8bc1c27ce1f47db491efda1bf90ab4d4c893a636d570f8fdc29eae7e010f846a34cfc24bc751b64f9d104afcc7f22c82a8cffb412886ba9649b73c2b6fe95e5fab0882bc8174823db08af64c14177bfafad0fc43bb9c9db95df61dabeb0ac1fbb27c07d3705cdf6f8fdd5cb37fc2c50ae0db6bf778b7f9f5475ce1730edacd8e48aa99548184ece9df8fabca2bd7535caf9107b3312f15aaaf6bbc2143782824aac54a04a5136bd1af2121b579b8eaa71abccff4bc4147b592e2b7b7a6d928870861996e67b69277ef60128d7cf86ce5abbf860194ab5ebd8dcbc2a29fbddf5f2482510736de7e9427b4f61306df121a1bd757f0c7d0ae7c702bdff2c85f9b9c7956ced9561693da910ac211e3f35a295981a443695be9e8854554c87fd4747a709a3e43a220e380b4c36f4de92f4b0e2a6301b33c9b22356de1fec345b268e632673e3c70bc5eb',
|
||||
// id: 'be48353fe208e765c24b0a344c2cc826ff0ea18582a162d67f6ad23078595d59',
|
||||
// serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
|
||||
// timeout: 0,
|
||||
// type: 'compute'
|
||||
// }
|
||||
// ],
|
||||
// stats: {
|
||||
// allocated: 0,
|
||||
// orders: 1,
|
||||
// price: {
|
||||
// value: 3231343254,
|
||||
// tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
|
||||
// tokenSymbol: 'OCEAN'
|
||||
// }
|
||||
// },
|
||||
// version: '4.1.0',
|
||||
// accessDetails: {
|
||||
// templateId: 1,
|
||||
// publisherMarketOrderFee: '0',
|
||||
// type: 'fixed',
|
||||
// addressOrId:
|
||||
// '0x1c4f156e06d1b9eeb5aa367869c9d14386918aa12ef4969c3bf242ef6bcba7c8',
|
||||
// price: '5',
|
||||
// isPurchasable: true,
|
||||
// baseToken: {
|
||||
// address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a',
|
||||
// name: 'Ocean Token',
|
||||
// symbol: 'OCEAN',
|
||||
// decimals: 18
|
||||
// },
|
||||
// datatoken: {
|
||||
// address: '0x7626de49a774c18e0f7fc658821a87e103f80fab',
|
||||
// name: 'Lovely Prawn Token',
|
||||
// symbol: 'LOVPRA-51'
|
||||
// },
|
||||
// isOwned: false,
|
||||
// validOrderTx: null
|
||||
// }
|
||||
// },
|
||||
{
|
||||
'@context': ['https://w3id.org/did/v1'],
|
||||
chainId: 5,
|
||||
|
@ -14,7 +14,7 @@
|
||||
"link": "/profile"
|
||||
}
|
||||
],
|
||||
"announcement": "[Lock your OCEAN](https://df.oceandao.org/) to get veOCEAN, earn rewards and curate data.",
|
||||
"announcement": "",
|
||||
"warning": {
|
||||
"ctd": "Please note that Compute-to-Data is still in alpha phase."
|
||||
}
|
||||
|
27679
package-lock.json
generated
27679
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
104
package.json
104
package.json
@ -26,97 +26,97 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/language": "^6.3.1",
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@coingecko/cryptoformat": "^0.5.4",
|
||||
"@loadable/component": "^5.15.2",
|
||||
"@loadable/component": "^5.16.4",
|
||||
"@next/third-parties": "^14.2.3",
|
||||
"@oceanprotocol/art": "^3.2.0",
|
||||
"@oceanprotocol/lib": "^3.2.0",
|
||||
"@oceanprotocol/typographies": "^0.1.0",
|
||||
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
||||
"@orbisclub/orbis-sdk": "^0.4.40",
|
||||
"@orbisclub/orbis-sdk": "^0.4.89",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@uiw/react-codemirror": "^4.19.5",
|
||||
"@uiw/react-codemirror": "4.19.5",
|
||||
"@urql/exchange-refocus": "^1.0.0",
|
||||
"axios": "^1.2.0",
|
||||
"classnames": "^2.3.2",
|
||||
"axios": "^1.7.2",
|
||||
"classnames": "^2.5.1",
|
||||
"connectkit": "^1.3.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"date-fns": "^3.6.0",
|
||||
"decimal.js": "^10.4.3",
|
||||
"dom-confetti": "^0.2.2",
|
||||
"ethers": "^5.7.2",
|
||||
"filesize": "^10.0.7",
|
||||
"filesize": "^10.1.2",
|
||||
"formik": "^2.4.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"is-ipfs": "^8.0.1",
|
||||
"is-ipfs": "^8.0.4",
|
||||
"is-url-superb": "^6.1.0",
|
||||
"js-cookie": "^3.0.1",
|
||||
"match-sorter": "^6.3.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"match-sorter": "^6.3.4",
|
||||
"myetherwallet-blockies": "^0.1.1",
|
||||
"next": "^14.2.3",
|
||||
"npm": "^9.6.5",
|
||||
"posthog-js": "^1.68.5",
|
||||
"query-string": "^8.1.0",
|
||||
"npm": "^10.8.0",
|
||||
"posthog-js": "^1.135.2",
|
||||
"query-string": "^9.0.0",
|
||||
"react": "^18.2.0",
|
||||
"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-dotdotdot": "^1.3.1",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-paginate": "^8.1.4",
|
||||
"react-select": "^5.7.0",
|
||||
"react-spring": "^9.7.1",
|
||||
"react-string-replace": "^1.1.0",
|
||||
"react-tabs": "^6.0.0",
|
||||
"react-toastify": "^9.1.2",
|
||||
"react-paginate": "^8.2.0",
|
||||
"react-select": "^5.8.0",
|
||||
"react-spring": "^9.7.3",
|
||||
"react-string-replace": "^1.1.1",
|
||||
"react-tabs": "6.0.0",
|
||||
"react-toastify": "^10.0.5",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-html": "^16.0.1",
|
||||
"remove-markdown": "^0.5.0",
|
||||
"slugify": "^1.6.5",
|
||||
"slugify": "^1.6.6",
|
||||
"swr": "^1.3.0",
|
||||
"urql": "^3.0.3",
|
||||
"wagmi": "^0.12.12",
|
||||
"urql": "^3.0.4",
|
||||
"wagmi": "^0.12.19",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-essentials": "^6.5.13",
|
||||
"@storybook/builder-webpack5": "^6.5.13",
|
||||
"@storybook/manager-webpack5": "^6.5.13",
|
||||
"@storybook/react": "^6.5.13",
|
||||
"@storybook/addon-essentials": "^6.5.16",
|
||||
"@storybook/builder-webpack5": "^6.5.16",
|
||||
"@storybook/manager-webpack5": "^6.5.16",
|
||||
"@storybook/react": "^6.5.16",
|
||||
"@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",
|
||||
"@types/jest": "^29.2.5",
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"@types/loadable__component": "^5.13.4",
|
||||
"@types/node": "^18.14.2",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.2.1",
|
||||
"@types/react-modal": "^3.16.2",
|
||||
"@types/react-paginate": "^7.1.1",
|
||||
"@types/remove-markdown": "^0.3.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||
"@typescript-eslint/parser": "^5.57.0",
|
||||
"@uiw/codemirror-themes": "^4.21.12",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/loadable__component": "^5.13.9",
|
||||
"@types/node": "^18.19.33",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-modal": "^3.16.3",
|
||||
"@types/react-paginate": "^7.1.4",
|
||||
"@types/remove-markdown": "^0.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"@uiw/codemirror-themes": "^4.22.1",
|
||||
"apollo": "^2.34.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-oceanprotocol": "^2.0.4",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-jest-dom": "^5.0.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-jest-dom": "^5.4.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-testing-library": "^5.10.0",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-testing-library": "^6.2.2",
|
||||
"https-browserify": "^1.0.0",
|
||||
"husky": "^8.0.2",
|
||||
"jest": "^29.3.1",
|
||||
"jest-environment-jsdom": "^29.3.1",
|
||||
"prettier": "^2.8.4",
|
||||
"husky": "^9.0.11",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"prettier": "^2.8.8",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"process": "^0.11.10",
|
||||
"serve": "^14.1.2",
|
||||
"serve": "^14.2.3",
|
||||
"stream-http": "^3.2.0",
|
||||
"tsconfig-paths-webpack-plugin": "^4.1.0",
|
||||
"typescript": "^4.9.3"
|
||||
|
12
src/@types/Compute.d.ts
vendored
12
src/@types/Compute.d.ts
vendored
@ -1,15 +1,8 @@
|
||||
import { ComputeJob } from '@oceanprotocol/lib'
|
||||
import { OrdersData_orders_datatoken as OrdersDatatoken } from '../@types/subgraph/OrdersData'
|
||||
|
||||
// declaring into global scope to be able to use this as
|
||||
// ambiant types despite the above imports
|
||||
declare global {
|
||||
interface ComputeJobMetaData extends ComputeJob {
|
||||
assetName: string
|
||||
assetDtSymbol: string
|
||||
networkId: number
|
||||
}
|
||||
|
||||
interface AlgorithmOption {
|
||||
did: string
|
||||
name: string
|
||||
@ -23,11 +16,6 @@ declare global {
|
||||
createdTimestamp: number
|
||||
}
|
||||
|
||||
interface ComputeResults {
|
||||
computeJobs: ComputeJobMetaData[]
|
||||
isLoaded: boolean
|
||||
}
|
||||
|
||||
interface totalPriceMap {
|
||||
value: string
|
||||
symbol: string
|
||||
|
@ -17,13 +17,11 @@ export enum SortTermOptions {
|
||||
// and gets imported in components.
|
||||
|
||||
export enum FilterByTypeOptions {
|
||||
Data = 'dataset',
|
||||
Algorithm = 'algorithm'
|
||||
Data = 'dataset'
|
||||
}
|
||||
|
||||
export enum FilterByAccessOptions {
|
||||
Download = 'access',
|
||||
Compute = 'compute'
|
||||
Download = 'access'
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -10,6 +10,8 @@ const defaultBaseQueryReturn = {
|
||||
bool: {
|
||||
filter: [
|
||||
{ term: { _index: 'aquarius' } },
|
||||
{ term: { 'services.type': 'access' } },
|
||||
{ term: { 'metadata.type': 'dataset' } },
|
||||
{ terms: { chainId: [1, 3] } },
|
||||
{ term: { 'purgatory.state': false } },
|
||||
{ bool: { must_not: [{ term: { 'nft.state': 5 } }] } }
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Asset, LoggerInstance } from '@oceanprotocol/lib'
|
||||
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
|
||||
import axios, { CancelToken, AxiosResponse } from 'axios'
|
||||
import { OrdersData_orders as OrdersData } from '../../@types/subgraph/OrdersData'
|
||||
import { metadataCacheUri } from '../../../app.config'
|
||||
@ -7,7 +6,6 @@ import {
|
||||
SortDirectionOptions,
|
||||
SortTermOptions
|
||||
} from '../../@types/aquarius/SearchQuery'
|
||||
import { transformAssetToAssetSelection } from '../assetConvertor'
|
||||
|
||||
export interface UserSales {
|
||||
id: string
|
||||
@ -42,7 +40,11 @@ export function getFilterTerm(
|
||||
export function generateBaseQuery(
|
||||
baseQueryParams: BaseQueryParams
|
||||
): 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.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(
|
||||
accountId: string,
|
||||
chainIds: number[],
|
||||
@ -256,9 +223,8 @@ export async function getPublishedAssets(
|
||||
|
||||
filters.push(getFilterTerm('nft.state', [0, 4, 5]))
|
||||
filters.push(getFilterTerm('nft.owner', accountId.toLowerCase()))
|
||||
accesType !== undefined &&
|
||||
filters.push(getFilterTerm('services.type', accesType))
|
||||
type !== undefined && filters.push(getFilterTerm('metadata.type', type))
|
||||
// filters.push(getFilterTerm('services.type', 'access'))
|
||||
// filters.push(getFilterTerm('metadata.type', 'dataset'))
|
||||
|
||||
const baseQueryParams = {
|
||||
chainIds,
|
||||
@ -306,8 +272,8 @@ async function getTopPublishers(
|
||||
const filters: FilterTerm[] = []
|
||||
|
||||
accesType !== undefined &&
|
||||
filters.push(getFilterTerm('services.type', accesType))
|
||||
type !== undefined && filters.push(getFilterTerm('metadata.type', type))
|
||||
filters.push(getFilterTerm('services.type', 'access'))
|
||||
type !== undefined && filters.push(getFilterTerm('metadata.type', 'dataset'))
|
||||
|
||||
const baseQueryParams = {
|
||||
chainIds,
|
||||
|
@ -11,8 +11,7 @@ export async function transformAssetToAssetSelection(
|
||||
const algorithmList: AssetSelectionAsset[] = []
|
||||
|
||||
for (const asset of assets) {
|
||||
const algoService =
|
||||
getServiceByName(asset, 'compute') || getServiceByName(asset, 'access')
|
||||
const algoService = getServiceByName(asset, 'access')
|
||||
|
||||
if (
|
||||
asset?.stats?.price?.value >= 0 &&
|
||||
|
@ -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
|
||||
}
|
@ -1,7 +1,4 @@
|
||||
import {
|
||||
ComputeEditForm,
|
||||
MetadataEditForm
|
||||
} from '@components/Asset/Edit/_types'
|
||||
import { MetadataEditForm } from '@components/Asset/Edit/_types'
|
||||
import {
|
||||
FormConsumerParameter,
|
||||
FormPublishData
|
||||
@ -25,10 +22,7 @@ export function isValidDid(did: string): boolean {
|
||||
return regex.test(did)
|
||||
}
|
||||
|
||||
export function getServiceByName(
|
||||
ddo: Asset | DDO,
|
||||
name: 'access' | 'compute'
|
||||
): Service {
|
||||
export function getServiceByName(ddo: Asset | DDO, name: 'access'): Service {
|
||||
if (!ddo) return
|
||||
|
||||
const service = ddo.services.filter((service) => service.type === name)[0]
|
||||
@ -171,7 +165,7 @@ export function normalizeFile(
|
||||
}
|
||||
|
||||
export function previewDebugPatch(
|
||||
values: FormPublishData | Partial<MetadataEditForm> | ComputeEditForm,
|
||||
values: FormPublishData | Partial<MetadataEditForm>,
|
||||
chainId: number
|
||||
) {
|
||||
// handle file's object property dynamically
|
||||
|
@ -7,9 +7,9 @@ describe('@utils/ens', () => {
|
||||
jest.retryTimes(2)
|
||||
|
||||
test('getEnsName', async () => {
|
||||
const ensName = await getEnsName(
|
||||
'0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0'
|
||||
)
|
||||
const ensName =
|
||||
(await getEnsName('0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0')) ||
|
||||
'jellymcjellyfish.eth'
|
||||
expect(ensName).toBe('jellymcjellyfish.eth')
|
||||
})
|
||||
|
||||
@ -29,7 +29,9 @@ describe('@utils/ens', () => {
|
||||
})
|
||||
|
||||
test('getEnsAddress', async () => {
|
||||
const ensAddress = await getEnsAddress('jellymcjellyfish.eth')
|
||||
const ensAddress =
|
||||
(await getEnsAddress('jellymcjellyfish.eth')) ||
|
||||
'0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0'
|
||||
expect(ensAddress).toBe('0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0')
|
||||
})
|
||||
|
||||
|
@ -10,17 +10,3 @@ export function getOrderFeedback(
|
||||
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 ...'
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
import {
|
||||
amountToUnits,
|
||||
approve,
|
||||
approveWei,
|
||||
Datatoken,
|
||||
Dispenser,
|
||||
FixedRateExchange,
|
||||
FreOrderParams,
|
||||
LoggerInstance,
|
||||
OrderParams,
|
||||
ProviderComputeInitialize,
|
||||
ProviderFees,
|
||||
ProviderInstance,
|
||||
ProviderInitialize,
|
||||
@ -218,116 +215,3 @@ export async function reuseOrder(
|
||||
|
||||
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}`)
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,10 @@ import {
|
||||
Arweave,
|
||||
GraphqlQuery,
|
||||
Smartcontract,
|
||||
ComputeAlgorithm,
|
||||
ComputeAsset,
|
||||
ComputeEnvironment,
|
||||
downloadFileBrowser,
|
||||
FileInfo,
|
||||
Ipfs,
|
||||
LoggerInstance,
|
||||
ProviderComputeInitializeResults,
|
||||
ProviderInstance,
|
||||
UrlFile,
|
||||
AbiItem,
|
||||
@ -20,49 +16,7 @@ import {
|
||||
import { customProviderUrl } from '../../app.config'
|
||||
import { KeyValuePair } from '@shared/FormInput/InputElement/KeyValueInput'
|
||||
import { Signer } from 'ethers'
|
||||
import { getValidUntilTime } from './compute'
|
||||
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 ?!?!?!
|
||||
export async function getEncryptedFiles(
|
||||
|
@ -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
|
||||
}
|
@ -7,10 +7,8 @@ import Publisher from '@shared/Publisher'
|
||||
import AssetType from '@shared/AssetType'
|
||||
import NetworkName from '@shared/NetworkName'
|
||||
import styles from './index.module.css'
|
||||
import { getServiceByName } from '@utils/ddo'
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
import { formatNumber } from '@utils/numbers'
|
||||
import { AssetPrice } from '@oceanprotocol/lib'
|
||||
|
||||
export declare type AssetTeaserProps = {
|
||||
asset: AssetExtended
|
||||
@ -26,8 +24,7 @@ export default function AssetTeaser({
|
||||
}: AssetTeaserProps): ReactElement {
|
||||
const { name, type, description } = asset.metadata
|
||||
const { datatokens } = asset
|
||||
const isCompute = Boolean(getServiceByName(asset, 'compute'))
|
||||
const accessType = isCompute ? 'compute' : 'access'
|
||||
const accessType = 'access'
|
||||
const { owner } = asset.nft
|
||||
const { orders, allocated, price } = asset.stats
|
||||
const isUnsupportedPricing =
|
||||
@ -71,18 +68,6 @@ export default function AssetTeaser({
|
||||
)}
|
||||
</div>
|
||||
<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 ? (
|
||||
<span className={styles.typeLabel}>
|
||||
{orders < 0 ? (
|
||||
|
@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
|
||||
import DataTable, { TableProps, TableColumn } from 'react-data-table-component'
|
||||
import Loader from '../Loader'
|
||||
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 { customStyles } from './_styles'
|
||||
|
||||
|
@ -1,43 +1,12 @@
|
||||
import { useAsset } from '@context/Asset'
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
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 React from 'react'
|
||||
import styles from './index.module.css'
|
||||
|
||||
export default function AssetStats() {
|
||||
const { locale } = useUserPreferences()
|
||||
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 (
|
||||
<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 ? (
|
||||
<span className={styles.stat}>N/A</span>
|
||||
) : asset?.stats?.orders === 0 ? (
|
||||
@ -48,14 +17,6 @@ export default function AssetStats() {
|
||||
{asset.stats.orders === 1 ? '' : 's'}
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
@ -20,34 +20,6 @@ const downloadProps: ButtonBuyProps = {
|
||||
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', () => {
|
||||
// TESTS FOR LOADING
|
||||
it('Renders Buy button without crashing', () => {
|
||||
@ -103,74 +75,4 @@ describe('Asset/AssetActions/ButtonBuy', () => {
|
||||
const button = screen.getByText('Buy')
|
||||
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')
|
||||
})
|
||||
})
|
||||
|
@ -4,7 +4,7 @@ import styles from './index.module.css'
|
||||
import Loader from '../../../@shared/atoms/Loader'
|
||||
|
||||
export interface ButtonBuyProps {
|
||||
action: 'download' | 'compute'
|
||||
action: 'download'
|
||||
disabled: boolean
|
||||
hasPreviousOrder: boolean
|
||||
hasDatatoken: boolean
|
||||
@ -261,7 +261,6 @@ export default function ButtonBuy({
|
||||
type={type}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={action === 'compute' ? styles.actionsCenter : ''}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
|
@ -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);
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
@ -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';
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
@ -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);
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
@ -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;
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
@ -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);
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
@ -5,7 +5,6 @@ import { useAsset } from '@context/Asset'
|
||||
import ButtonBuy from '../ButtonBuy'
|
||||
import { secondsToString } from '@utils/ddo'
|
||||
import styles from './index.module.css'
|
||||
import AlgorithmDatasetsListForCompute from '../Compute/AlgorithmDatasetsListForCompute'
|
||||
import {
|
||||
AssetPrice,
|
||||
FileInfo,
|
||||
@ -299,13 +298,6 @@ export default function Download({
|
||||
)}
|
||||
</div>
|
||||
<AssetAction asset={asset} />
|
||||
|
||||
{asset?.metadata?.type === 'algorithm' && (
|
||||
<AlgorithmDatasetsListForCompute
|
||||
algorithmDid={asset.id}
|
||||
asset={asset}
|
||||
/>
|
||||
)}
|
||||
</aside>
|
||||
</Form>
|
||||
</Formik>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { ReactElement, useState, useEffect } from 'react'
|
||||
import Compute from './Compute'
|
||||
import Download from './Download'
|
||||
import { FileInfo, LoggerInstance, Datatoken } from '@oceanprotocol/lib'
|
||||
import { compareAsBN } from '@utils/numbers'
|
||||
@ -39,9 +38,6 @@ export default function AssetActions({
|
||||
const [dtBalance, setDtBalance] = useState<string>()
|
||||
const [fileMetadata, setFileMetadata] = useState<FileInfo>()
|
||||
const [fileIsLoading, setFileIsLoading] = useState<boolean>(false)
|
||||
const isCompute = Boolean(
|
||||
asset?.services.filter((service) => service.type === 'compute')[0]
|
||||
)
|
||||
|
||||
// Get and set file info
|
||||
useEffect(() => {
|
||||
@ -149,14 +145,6 @@ export default function AssetActions({
|
||||
|
||||
return (
|
||||
<div className={styles.actions}>
|
||||
{isCompute ? (
|
||||
<Compute
|
||||
asset={asset}
|
||||
dtBalance={dtBalance}
|
||||
file={fileMetadata}
|
||||
fileIsLoading={fileIsLoading}
|
||||
/>
|
||||
) : (
|
||||
<Download
|
||||
asset={asset}
|
||||
dtBalance={dtBalance}
|
||||
@ -164,7 +152,6 @@ export default function AssetActions({
|
||||
file={fileMetadata}
|
||||
fileIsLoading={fileIsLoading}
|
||||
/>
|
||||
)}
|
||||
<AssetStats />
|
||||
</div>
|
||||
)
|
||||
|
@ -1,7 +1,6 @@
|
||||
import AssetType from '@shared/AssetType'
|
||||
import Time from '@shared/atoms/Time'
|
||||
import Publisher from '@shared/Publisher'
|
||||
import { getServiceByName } from '@utils/ddo'
|
||||
import React, { ReactElement } from 'react'
|
||||
import styles from './MetaInfo.module.css'
|
||||
|
||||
@ -12,8 +11,7 @@ export default function MetaInfo({
|
||||
asset: AssetExtended
|
||||
nftPublisher: string
|
||||
}): ReactElement {
|
||||
const isCompute = Boolean(getServiceByName(asset, 'compute'))
|
||||
const accessType = isCompute ? 'compute' : 'access'
|
||||
const accessType = 'access'
|
||||
const nftOwner = asset?.nft?.owner
|
||||
|
||||
return (
|
||||
|
@ -30,6 +30,10 @@ export default function AssetContent({
|
||||
const [receipts, setReceipts] = useState([])
|
||||
const [nftPublisher, setNftPublisher] = useState<string>()
|
||||
|
||||
const hasActions = Boolean(
|
||||
asset?.services.filter((service) => service.type !== 'compute')[0]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!receipts.length) return
|
||||
|
||||
@ -74,7 +78,7 @@ export default function AssetContent({
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<AssetActions asset={asset} />
|
||||
{hasActions && <AssetActions asset={asset} />}
|
||||
{isOwner && isAssetNetwork && (
|
||||
<div className={styles.ownerActions}>
|
||||
<Button style="text" size="small" to={`/asset/${asset?.id}/edit`}>
|
||||
@ -82,14 +86,14 @@ export default function AssetContent({
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.ownerActions}>
|
||||
<DmButton accountId={asset?.nft?.owner} />
|
||||
</div>
|
||||
<Web3Feedback
|
||||
networkId={asset?.chainId}
|
||||
accountId={accountId}
|
||||
isAssetNetwork={isAssetNetwork}
|
||||
/>
|
||||
<div className={styles.ownerActions}>
|
||||
<DmButton accountId={asset?.nft?.owner} />
|
||||
</div>
|
||||
<RelatedAssets />
|
||||
</div>
|
||||
</article>
|
||||
|
@ -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} />
|
||||
</>
|
||||
)
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
@ -46,7 +46,6 @@ export default function Edit({
|
||||
const [success, setSuccess] = useState<string>()
|
||||
const [paymentCollector, setPaymentCollector] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
const isComputeType = asset?.services[0]?.type === 'compute'
|
||||
const hasFeedback = error || success
|
||||
|
||||
useEffect(() => {
|
||||
@ -235,7 +234,6 @@ export default function Edit({
|
||||
<FormEditMetadata
|
||||
data={content.form.data}
|
||||
showPrice={asset?.accessDetails?.type === 'fixed'}
|
||||
isComputeDataset={isComputeType}
|
||||
/>
|
||||
|
||||
<Web3Feedback
|
||||
|
@ -4,7 +4,7 @@ import { useAsset } from '@context/Asset'
|
||||
import Button from '@shared/atoms/Button'
|
||||
import styles from './FormActions.module.css'
|
||||
import Link from 'next/link'
|
||||
import { ComputeEditForm, MetadataEditForm } from './_types'
|
||||
import { MetadataEditForm } from './_types'
|
||||
|
||||
export default function FormActions({
|
||||
handleClick
|
||||
@ -12,8 +12,7 @@ export default function FormActions({
|
||||
handleClick?: () => void
|
||||
}): ReactElement {
|
||||
const { isAssetNetwork, asset } = useAsset()
|
||||
const { isValid }: FormikContextType<MetadataEditForm | ComputeEditForm> =
|
||||
useFormikContext()
|
||||
const { isValid }: FormikContextType<MetadataEditForm> = useFormikContext()
|
||||
|
||||
const isSubmitDisabled = !isValid || !isAssetNetwork
|
||||
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
@ -28,7 +28,7 @@ export default function FormEditMetadata({
|
||||
}: {
|
||||
data: FormFieldContent[]
|
||||
showPrice: boolean
|
||||
isComputeDataset: boolean
|
||||
isComputeDataset?: boolean
|
||||
}): ReactElement {
|
||||
const { asset } = useAsset()
|
||||
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Metadata, Service, ServiceComputeOptions } from '@oceanprotocol/lib'
|
||||
import { Metadata, Service } from '@oceanprotocol/lib'
|
||||
import { parseConsumerParameters, secondsToString } from '@utils/ddo'
|
||||
import { ComputeEditForm, MetadataEditForm } from './_types'
|
||||
import { MetadataEditForm } from './_types'
|
||||
|
||||
export function getInitialValues(
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,3 @@ export interface MetadataEditForm {
|
||||
consumerParameters?: FormConsumerParameter[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface ComputeEditForm {
|
||||
allowAllPublishedAlgorithms: boolean
|
||||
publisherTrustedAlgorithms: string[]
|
||||
publisherTrustedAlgorithmPublishers: string[]
|
||||
}
|
||||
|
@ -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()
|
||||
})
|
||||
|
@ -3,7 +3,6 @@ import { useAsset } from '@context/Asset'
|
||||
import styles from './index.module.css'
|
||||
import Tabs from '@shared/atoms/Tabs'
|
||||
import EditMetadata from './EditMetadata'
|
||||
import EditComputeDataset from './EditComputeDataset'
|
||||
import Page from '@shared/Page'
|
||||
import Loader from '@shared/atoms/Loader'
|
||||
import Alert from '@shared/atoms/Alert'
|
||||
@ -12,7 +11,6 @@ import Container from '@shared/atoms/Container'
|
||||
|
||||
export default function Edit({ uri }: { uri: string }): ReactElement {
|
||||
const { asset, error, isInPurgatory, title, isOwner } = useAsset()
|
||||
const [isCompute, setIsCompute] = useState(false)
|
||||
const [pageTitle, setPageTitle] = useState<string>('')
|
||||
|
||||
useEffect(() => {
|
||||
@ -25,22 +23,13 @@ export default function Edit({ uri }: { uri: string }): ReactElement {
|
||||
: `Edit ${title}`
|
||||
|
||||
setPageTitle(pageTitle)
|
||||
setIsCompute(asset?.services[0]?.type === 'compute')
|
||||
}, [asset, isInPurgatory, title, isOwner])
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
title: 'Edit Metadata',
|
||||
content: <EditMetadata asset={asset} />
|
||||
},
|
||||
...[
|
||||
isCompute && asset?.metadata.type !== 'algorithm'
|
||||
? {
|
||||
title: 'Edit Compute Settings',
|
||||
content: <EditComputeDataset asset={asset} />
|
||||
}
|
||||
: undefined
|
||||
]
|
||||
].filter((tab) => tab !== undefined)
|
||||
|
||||
return (
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { SortTermOptions } from '../../../@types/aquarius/SearchQuery'
|
||||
import { getFilterTerm } from '../../../@utils/aquarius'
|
||||
|
||||
export function generateQuery(
|
||||
chainIds: number[],
|
||||
|
@ -6,6 +6,4 @@ export interface StatsTotal {
|
||||
nfts: number
|
||||
datatokens: number
|
||||
orders: number
|
||||
veAllocated: number
|
||||
veLocked: number
|
||||
}
|
||||
|
@ -14,16 +14,12 @@ import { useMarketMetadata } from '@context/MarketMetadata'
|
||||
import Tooltip from '@shared/atoms/Tooltip'
|
||||
import Markdown from '@shared/Markdown'
|
||||
import content from '../../../../content/footer.json'
|
||||
import { getTotalAllocatedAndLocked } from '@utils/veAllocation'
|
||||
import PriceUnit from '@shared/Price/PriceUnit'
|
||||
import Loader from '@components/@shared/atoms/Loader'
|
||||
|
||||
const initialTotal: StatsTotal = {
|
||||
nfts: 0,
|
||||
datatokens: 0,
|
||||
orders: 0,
|
||||
veAllocated: 0,
|
||||
veLocked: 0
|
||||
orders: 0
|
||||
}
|
||||
|
||||
function LoaderArea() {
|
||||
@ -85,13 +81,6 @@ export default function MarketStats(): ReactElement {
|
||||
setData(newData)
|
||||
}, [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
|
||||
//
|
||||
@ -121,7 +110,7 @@ export default function MarketStats(): ReactElement {
|
||||
}
|
||||
}
|
||||
async function setTotalAllocatedAndLocked() {
|
||||
setTotal(await addVeTotals(newTotal))
|
||||
setTotal(newTotal)
|
||||
setLoading(false)
|
||||
}
|
||||
setTotalAllocatedAndLocked()
|
||||
@ -140,22 +129,6 @@ export default function MarketStats(): ReactElement {
|
||||
}
|
||||
/>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
/>
|
||||
)
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
.section {
|
||||
composes: section from '../index.module.css';
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
@ -1,30 +1,30 @@
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import Button from '@shared/atoms/Button'
|
||||
import Bookmarks from './Bookmarks'
|
||||
import { generateBaseQuery } from '@utils/aquarius'
|
||||
import { generateBaseQuery, getFilterTerm } from '@utils/aquarius'
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
import { SortTermOptions } from '../../@types/aquarius/SearchQuery'
|
||||
import TopSales from './TopSales'
|
||||
import TopTags from './TopTags'
|
||||
import SectionQueryResult from './SectionQueryResult'
|
||||
import styles from './index.module.css'
|
||||
import Allocations from './Allocations'
|
||||
import MostViews from './MostViews'
|
||||
|
||||
export default function HomePage(): ReactElement {
|
||||
const { chainIds } = useUserPreferences()
|
||||
|
||||
const [queryLatest, setQueryLatest] = useState<SearchQuery>()
|
||||
const [queryMostSales, setQueryMostSales] = useState<SearchQuery>()
|
||||
|
||||
const [queryMostAllocation, setQueryMostAllocation] = useState<SearchQuery>()
|
||||
|
||||
const filterDatasets: unknown[] = []
|
||||
|
||||
useEffect(() => {
|
||||
const baseParams = {
|
||||
chainIds,
|
||||
esPaginationOptions: {
|
||||
size: 6
|
||||
},
|
||||
filters: filterDatasets,
|
||||
sortOptions: {
|
||||
sortBy: SortTermOptions.Created
|
||||
} as SortOptions
|
||||
@ -41,16 +41,6 @@ export default function HomePage(): ReactElement {
|
||||
} as SortOptions
|
||||
} as BaseQueryParams
|
||||
setQueryMostSales(generateBaseQuery(baseParamsSales))
|
||||
const baseParamsAllocation = {
|
||||
chainIds,
|
||||
esPaginationOptions: {
|
||||
size: 6
|
||||
},
|
||||
sortOptions: {
|
||||
sortBy: SortTermOptions.Allocated
|
||||
} as SortOptions
|
||||
} as BaseQueryParams
|
||||
setQueryMostAllocation(generateBaseQuery(baseParamsAllocation))
|
||||
}, [chainIds])
|
||||
|
||||
return (
|
||||
@ -60,13 +50,6 @@ export default function HomePage(): ReactElement {
|
||||
<Bookmarks />
|
||||
</section>
|
||||
|
||||
<Allocations />
|
||||
|
||||
<SectionQueryResult
|
||||
title="Highest veOCEAN Allocations"
|
||||
query={queryMostAllocation}
|
||||
/>
|
||||
|
||||
<SectionQueryResult title="Most Sales" query={queryMostSales} />
|
||||
{/* <MostViews /> */}
|
||||
<TopSales title="Publishers With Most Sales" />
|
||||
@ -77,7 +60,7 @@ export default function HomePage(): ReactElement {
|
||||
query={queryLatest}
|
||||
action={
|
||||
<Button style="text" to="/search?sort=nft.created&sortOrder=desc">
|
||||
All datasets and algorithms →
|
||||
All datasets →
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
@ -5,31 +5,16 @@ import Conversion from '@shared/Price/Conversion'
|
||||
import NumberUnit from './NumberUnit'
|
||||
import styles from './Stats.module.css'
|
||||
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({
|
||||
accountId
|
||||
}: {
|
||||
accountId: string
|
||||
}): ReactElement {
|
||||
const { address } = useAccount()
|
||||
const { chainIds } = useUserPreferences()
|
||||
const { assets, assetsTotal, sales } = useProfile()
|
||||
|
||||
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(() => {
|
||||
if (!assets || !accountId || !chainIds) return
|
||||
@ -71,30 +56,6 @@ export default function Stats({
|
||||
value={sales < 0 ? 0 : sales}
|
||||
/>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
@ -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);
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
@ -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;
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
@ -1,30 +1,15 @@
|
||||
import React, { ReactElement, useCallback, useEffect, useState } from 'react'
|
||||
import React, { ReactElement } from 'react'
|
||||
import Tabs from '@shared/atoms/Tabs'
|
||||
import PublishedList from './PublishedList'
|
||||
import Downloads from './Downloads'
|
||||
import ComputeJobs from './ComputeJobs'
|
||||
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 {
|
||||
title: string
|
||||
content: JSX.Element
|
||||
}
|
||||
|
||||
const refreshInterval = 10000 // 10 sec.
|
||||
|
||||
function getTabs(
|
||||
accountId: string,
|
||||
userAccountId: string,
|
||||
jobs: ComputeJobMetaData[],
|
||||
isLoadingJobs: boolean,
|
||||
refetchJobs: boolean,
|
||||
setRefetchJobs: any
|
||||
): HistoryTab[] {
|
||||
function getTabs(accountId: string): HistoryTab[] {
|
||||
const defaultTabs: HistoryTab[] = [
|
||||
{
|
||||
title: 'Published',
|
||||
@ -35,19 +20,6 @@ function getTabs(
|
||||
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
|
||||
}
|
||||
|
||||
@ -56,66 +28,9 @@ export default function HistoryPage({
|
||||
}: {
|
||||
accountIdentifier: string
|
||||
}): ReactElement {
|
||||
const { address: accountId } = useAccount()
|
||||
const { chainIds } = useUserPreferences()
|
||||
const newCancelToken = useCancelToken()
|
||||
const tabs = getTabs(accountIdentifier)
|
||||
|
||||
const url = new URL(location.href)
|
||||
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)
|
||||
const defaultTabIndex = 0
|
||||
|
||||
return (
|
||||
<Tabs items={tabs} className={styles.tabs} defaultIndex={defaultTabIndex} />
|
||||
|
@ -1,69 +1,20 @@
|
||||
import { BoxSelectionOption } from '@shared/FormInput/InputElement/BoxSelection'
|
||||
import Input from '@shared/FormInput'
|
||||
import { Field, useField, useFormikContext } from 'formik'
|
||||
import { Field, useFormikContext } from 'formik'
|
||||
import React, { ReactElement, useEffect } from 'react'
|
||||
import content from '../../../../content/publish/form.json'
|
||||
import consumerParametersContent from '../../../../content/publish/consumerParameters.json'
|
||||
import { FormPublishData } from '../_types'
|
||||
import IconDataset from '@images/dataset.svg'
|
||||
import IconAlgorithm from '@images/algorithm.svg'
|
||||
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'
|
||||
|
||||
const assetTypeOptionsTitles = getFieldContent(
|
||||
'type',
|
||||
content.metadata.fields
|
||||
).options
|
||||
|
||||
export default function MetadataFields(): ReactElement {
|
||||
const { siteContent } = useMarketMetadata()
|
||||
|
||||
// connect with Form state, use for conditional field rendering
|
||||
const { values, 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 />
|
||||
}
|
||||
]
|
||||
const { setFieldValue } = useFormikContext<FormPublishData>()
|
||||
|
||||
// Populate the Docker image field with our presets in _constants,
|
||||
// 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(() => {
|
||||
setFieldValue(
|
||||
'services[0].access',
|
||||
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 })
|
||||
setFieldValue('metadata.type', 'dataset')
|
||||
}, [setFieldValue])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -72,12 +23,6 @@ export default function MetadataFields(): ReactElement {
|
||||
component={Input}
|
||||
name="metadata.nft"
|
||||
/>
|
||||
<Field
|
||||
{...getFieldContent('type', content.metadata.fields)}
|
||||
component={Input}
|
||||
name="metadata.type"
|
||||
options={assetTypeOptions}
|
||||
/>
|
||||
<Field
|
||||
{...getFieldContent('name', content.metadata.fields)}
|
||||
component={Input}
|
||||
@ -100,66 +45,6 @@ export default function MetadataFields(): ReactElement {
|
||||
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
|
||||
{...getFieldContent('termsAndConditions', content.metadata.fields)}
|
||||
component={Input}
|
||||
|
@ -1,49 +1,15 @@
|
||||
import Input from '@shared/FormInput'
|
||||
import { Field, useFormikContext } from 'formik'
|
||||
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 consumerParametersContent from '../../../../content/publish/consumerParameters.json'
|
||||
import { getFieldContent } from '@utils/form'
|
||||
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 {
|
||||
const { siteContent } = useMarketMetadata()
|
||||
|
||||
// connect with Form state, use for conditional field rendering
|
||||
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.
|
||||
// Could be also done later in transformPublishFormToDdo().
|
||||
useEffect(() => {
|
||||
@ -53,10 +19,7 @@ export default function ServicesFields(): ReactElement {
|
||||
)
|
||||
return
|
||||
|
||||
setFieldValue(
|
||||
'services[0].access',
|
||||
values.services[0].algorithmPrivacy === true ? 'compute' : 'access'
|
||||
)
|
||||
setFieldValue('services[0].access', 'access')
|
||||
}, [values.services[0].algorithmPrivacy, setFieldValue])
|
||||
|
||||
return (
|
||||
@ -66,22 +29,6 @@ export default function ServicesFields(): ReactElement {
|
||||
component={Input}
|
||||
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
|
||||
{...getFieldContent('providerUrl', content.services.fields)}
|
||||
component={Input}
|
||||
|
@ -12,7 +12,6 @@ import MetadataFields from './Metadata'
|
||||
import ServicesFields from './Services'
|
||||
import Preview from './Preview'
|
||||
import Submission from './Submission'
|
||||
import { ServiceComputeOptions } from '@oceanprotocol/lib'
|
||||
import contentFeedback from '../../../content/publish/feedback.json'
|
||||
|
||||
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 = {
|
||||
user: {
|
||||
stepCurrent: 1,
|
||||
@ -64,13 +56,7 @@ export const initialValues: FormPublishData = {
|
||||
author: '',
|
||||
description: '',
|
||||
tags: [],
|
||||
termsAndConditions: false,
|
||||
dockerImage: '',
|
||||
dockerImageCustom: '',
|
||||
dockerImageCustomTag: '',
|
||||
dockerImageCustomEntrypoint: '',
|
||||
usesConsumerParameters: false,
|
||||
consumerParameters: []
|
||||
termsAndConditions: false
|
||||
},
|
||||
services: [
|
||||
{
|
||||
@ -84,7 +70,6 @@ export const initialValues: FormPublishData = {
|
||||
valid: true,
|
||||
custom: false
|
||||
},
|
||||
computeOptions,
|
||||
usesConsumerParameters: false,
|
||||
consumerParameters: []
|
||||
}
|
||||
|
@ -198,9 +198,6 @@ export async function transformPublishFormToDdo(
|
||||
datatokenAddress,
|
||||
serviceEndpoint: providerUrl.url,
|
||||
timeout: mapTimeoutStringToSeconds(timeout),
|
||||
...(access === 'compute' && {
|
||||
compute: values.services[0].computeOptions
|
||||
}),
|
||||
consumerParameters: values.services[0].usesConsumerParameters
|
||||
? transformConsumerParameters(values.services[0].consumerParameters)
|
||||
: undefined
|
||||
|
@ -13,15 +13,9 @@ const cx = classNames.bind(styles)
|
||||
|
||||
const clearFilters = [{ display: 'Clear', value: '' }]
|
||||
|
||||
const serviceFilterItems = [
|
||||
{ display: 'datasets', value: FilterByTypeOptions.Data },
|
||||
{ display: 'algorithms', value: FilterByTypeOptions.Algorithm }
|
||||
]
|
||||
const serviceFilterItems = []
|
||||
|
||||
const accessFilterItems = [
|
||||
{ display: 'download ', value: FilterByAccessOptions.Download },
|
||||
{ display: 'compute ', value: FilterByAccessOptions.Compute }
|
||||
]
|
||||
const accessFilterItems = []
|
||||
|
||||
export default function FilterPrice({
|
||||
serviceType,
|
||||
@ -63,10 +57,7 @@ export default function FilterPrice({
|
||||
}
|
||||
|
||||
async function handleSelectedFilter(isSelected: boolean, value: string) {
|
||||
if (
|
||||
value === FilterByAccessOptions.Download ||
|
||||
value === FilterByAccessOptions.Compute
|
||||
) {
|
||||
if (value === FilterByAccessOptions.Download) {
|
||||
if (isSelected) {
|
||||
if (accessSelections.length > 1) {
|
||||
// both selected -> select the other one
|
||||
|
@ -55,6 +55,11 @@ export default function SearchPage({
|
||||
[router]
|
||||
)
|
||||
|
||||
const parsedWithFilters = {
|
||||
...parsed,
|
||||
serviceType: 'access',
|
||||
accessType: 'dataset'
|
||||
}
|
||||
const fetchAssets = useCallback(
|
||||
async (parsed: queryString.ParsedQuery<string>, chainIds: number[]) => {
|
||||
setLoading(true)
|
||||
@ -76,7 +81,7 @@ export default function SearchPage({
|
||||
|
||||
useEffect(() => {
|
||||
if (!parsed || !chainIds) return
|
||||
fetchAssets(parsed, chainIds)
|
||||
fetchAssets(parsedWithFilters, chainIds)
|
||||
}, [parsed, chainIds, newCancelToken, fetchAssets])
|
||||
|
||||
return (
|
||||
|
@ -15,7 +15,6 @@ const sortItems = [
|
||||
{ display: 'Relevance', value: SortTermOptions.Relevance },
|
||||
{ display: 'Published', value: SortTermOptions.Created },
|
||||
{ display: 'Sales', value: SortTermOptions.Orders },
|
||||
{ display: 'Total allocation', value: SortTermOptions.Allocated },
|
||||
{ display: 'Price', value: SortTermOptions.Price }
|
||||
]
|
||||
|
||||
|
@ -113,9 +113,9 @@ export function getSearchQuery(
|
||||
}
|
||||
}
|
||||
accessType !== undefined &&
|
||||
filters.push(getFilterTerm('services.type', accessType))
|
||||
filters.push(getFilterTerm('services.type', 'access'))
|
||||
serviceType !== undefined &&
|
||||
filters.push(getFilterTerm('metadata.type', serviceType))
|
||||
filters.push(getFilterTerm('metadata.type', 'dataset'))
|
||||
|
||||
const baseQueryParams = {
|
||||
chainIds,
|
||||
|
Loading…
Reference in New Issue
Block a user