mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Start compute job (#439)
* Wip start compute job * Wip select algorithm design * Asset selection form component, for start compute job (#442) * prototype AssetSelection * assetselection styling * typing "fix" * put back file info icon * AssetSelection styling in context * update start job method, fixed algo select, and fixed option typing * compute logic update * add has previous orders for algo asset * fixed search algorithm assets in start compute form * fixed lint errors * updated previous order for algo logic and compute flow * update use price hook and added buy DT for algo * display only alg of type exchange and sort by value * display only trusted algo for asset if field is set * added logic for allow all published algorithms or no algorithms allowed * asset selection style & spacing tweaks * refactor get algorithms for compute and edit compute * fixed form options and more refactoring * new ButtonBuy component * shared component between consume/compute * dealing with various states: loading, previous orders, help text output * effect dependencies * move error output into toast * formik data flow refactor * ditch custom field change handler * fix initialValues * typed form data & validation * fixes multiple form validation issues along the way * isInitialValid → validateOnMount * metadata display tweaks * error feedback tweaks * oler assets checks, confeti on succes job, market fee on order, removed algo compute logic * more startJob logging * feedback & messaging changes * metadata display * return all algos, fixed & dynamic priced ones * fix DOM nesting * messaging updates * copy tweaks * check algorithm previous history for both acces and compute sercive types * handle start compute error * extra checks on start compute response * styling tweaks, fix toast UI errors * AssetSelection: empty screen, tweak min/max height * fix FRE issues on start compute * check is ordarable before start compute job * logging tweaks * disable eslint no-unused-vars rule for some Apollo code blocks * fix metadata editing for compute assets * consider dataset timeout for compute too Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
6d14181d17
commit
18f2c99e78
@ -19,7 +19,7 @@
|
|||||||
"name": "files",
|
"name": "files",
|
||||||
"label": "File",
|
"label": "File",
|
||||||
"placeholder": "e.g. https://file.com/file.json",
|
"placeholder": "e.g. https://file.com/file.json",
|
||||||
"help": "Please provide a URL to your algorith file. This URL will be stored encrypted after publishing.",
|
"help": "Please provide a URL to your algorithm file. This URL will be stored encrypted after publishing.",
|
||||||
"type": "files",
|
"type": "files",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
@ -27,23 +27,23 @@
|
|||||||
"name": "dockerImage",
|
"name": "dockerImage",
|
||||||
"label": "Docker Image",
|
"label": "Docker Image",
|
||||||
"placeholder": "e.g. python3.7",
|
"placeholder": "e.g. python3.7",
|
||||||
"help": "Please select a image to run your algorithm.",
|
"help": "Please select an image to run your algorithm.",
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"options": ["node:pre-defined", "python:pre-defined", "custom image"],
|
"options": ["node:latest", "python:latest", "custom image"],
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "image",
|
"name": "image",
|
||||||
"label": "Image URL",
|
"label": "Image URL",
|
||||||
"placeholder": "e.g. oceanprotocol/algo_dockers or https://example.com/image_path",
|
"placeholder": "e.g. oceanprotocol/algo_dockers or https://example.com/image_path",
|
||||||
"help": "Provide the name of a public docker image or the full url if you have it hosted in a 3rd party repo",
|
"help": "Provide the name of a public Docker image or the full url if you have it hosted in a 3rd party repo",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "containerTag",
|
"name": "containerTag",
|
||||||
"label": "Docker Image Tag",
|
"label": "Docker Image Tag",
|
||||||
"placeholder": "e.g. latest",
|
"placeholder": "e.g. latest",
|
||||||
"help": "Provide the tag for your docker image.",
|
"help": "Provide the tag for your Docker image.",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -58,13 +58,14 @@
|
|||||||
"label": "Algorithm Privacy",
|
"label": "Algorithm Privacy",
|
||||||
"type": "checkbox",
|
"type": "checkbox",
|
||||||
"options": ["Keep my algorithm private"],
|
"options": ["Keep my algorithm private"],
|
||||||
|
"help": "By default, your algorithm can be downloaded for a fixed or dynamic price in addition to running in compute jobs. Enabling this option will prevent downloading, so your algorithm can only be run as part of a compute job on a data set.",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "author",
|
"name": "author",
|
||||||
"label": "Author",
|
"label": "Author",
|
||||||
"placeholder": "e.g. Jelly McJellyfish",
|
"placeholder": "e.g. Jelly McJellyfish",
|
||||||
"help": "Give proper attribution for your algorith.",
|
"help": "Give proper attribution for your algorithm.",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
"label": "Access Type",
|
"label": "Access Type",
|
||||||
"help": "Choose how you want your files to be accessible for the specified price.",
|
"help": "Choose how you want your files to be accessible for the specified price.",
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"options": ["Download"],
|
"options": ["Download", "Compute"],
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
16
content/pages/startComputeDataset.json
Normal file
16
content/pages/startComputeDataset.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"form": {
|
||||||
|
"success": "🎉 Your Compute job started. 🎉",
|
||||||
|
"error": "Compute job could not be started.",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"name": "algorithm",
|
||||||
|
"label": "Select an algorithm to start a compute job",
|
||||||
|
"type": "assetSelection",
|
||||||
|
"value": false,
|
||||||
|
"options": [],
|
||||||
|
"sortOptions": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
14
src/components/atoms/ButtonBuy.module.css
Normal file
14
src/components/atoms/ButtonBuy.module.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.actions {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.help {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
margin-top: calc(var(--spacer) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.help:not(:empty) {
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
}
|
90
src/components/atoms/ButtonBuy.tsx
Normal file
90
src/components/atoms/ButtonBuy.tsx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import React, { FormEvent, ReactElement } from 'react'
|
||||||
|
import Button from './Button'
|
||||||
|
import styles from './ButtonBuy.module.css'
|
||||||
|
import Loader from './Loader'
|
||||||
|
|
||||||
|
interface ButtonBuyProps {
|
||||||
|
action: 'download' | 'compute'
|
||||||
|
disabled: boolean
|
||||||
|
hasPreviousOrder: boolean
|
||||||
|
hasDatatoken: boolean
|
||||||
|
dtSymbol: string
|
||||||
|
dtBalance: string
|
||||||
|
isLoading: boolean
|
||||||
|
assetTimeout: string
|
||||||
|
onClick?: (e: FormEvent<HTMLButtonElement>) => void
|
||||||
|
stepText?: string
|
||||||
|
type?: 'submit'
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHelpText(
|
||||||
|
token: {
|
||||||
|
dtBalance: string
|
||||||
|
dtSymbol: string
|
||||||
|
},
|
||||||
|
hasDatatoken: boolean,
|
||||||
|
hasPreviousOrder: boolean,
|
||||||
|
timeout: string
|
||||||
|
) {
|
||||||
|
const { dtBalance, dtSymbol } = token
|
||||||
|
const assetTimeout = timeout === 'Forever' ? '' : ` for ${timeout}`
|
||||||
|
const text = hasPreviousOrder
|
||||||
|
? `You bought this data set already allowing you to use it without paying again${assetTimeout}.`
|
||||||
|
: hasDatatoken
|
||||||
|
? `You own ${dtBalance} ${dtSymbol} allowing you to use this data set by spending 1 ${dtSymbol}, but without paying OCEAN again.`
|
||||||
|
: `For using this data set, you will buy 1 ${dtSymbol} and immediately spend it back to the publisher and pool.`
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ButtonBuy({
|
||||||
|
action,
|
||||||
|
disabled,
|
||||||
|
hasPreviousOrder,
|
||||||
|
hasDatatoken,
|
||||||
|
dtSymbol,
|
||||||
|
dtBalance,
|
||||||
|
onClick,
|
||||||
|
assetTimeout,
|
||||||
|
stepText,
|
||||||
|
isLoading,
|
||||||
|
type
|
||||||
|
}: ButtonBuyProps): ReactElement {
|
||||||
|
const buttonText =
|
||||||
|
action === 'download'
|
||||||
|
? hasPreviousOrder
|
||||||
|
? 'Download'
|
||||||
|
: `Buy ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}`
|
||||||
|
: hasPreviousOrder
|
||||||
|
? 'Start Compute Job'
|
||||||
|
: `Buy Compute Job ${
|
||||||
|
assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`
|
||||||
|
}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.actions}>
|
||||||
|
{isLoading ? (
|
||||||
|
<Loader message={stepText} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
style="primary"
|
||||||
|
type={type}
|
||||||
|
onClick={onClick}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{buttonText}
|
||||||
|
</Button>
|
||||||
|
<div className={styles.help}>
|
||||||
|
{getHelpText(
|
||||||
|
{ dtBalance, dtSymbol },
|
||||||
|
hasDatatoken,
|
||||||
|
hasPreviousOrder,
|
||||||
|
assetTimeout
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
.file.small {
|
.file.small {
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
height: 5.75rem;
|
height: 5.5rem;
|
||||||
width: 4.5rem;
|
width: 4.5rem;
|
||||||
|
padding: calc(var(--spacer) / 2) calc(var(--spacer) / 4);
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,12 @@
|
|||||||
padding-left: 0.5rem;
|
padding-left: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.algorithmLabel {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--spacer);
|
||||||
|
grid-template-columns: 2fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.radio,
|
.radio,
|
||||||
.checkbox {
|
.checkbox {
|
||||||
composes: input;
|
composes: input;
|
||||||
@ -140,16 +146,17 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio {
|
.radio,
|
||||||
|
.radio::after {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio::after {
|
.radio::after {
|
||||||
width: 16px;
|
width: 8px;
|
||||||
height: 16px;
|
height: 8px;
|
||||||
border-radius: 50%;
|
top: 4px;
|
||||||
|
left: 4px;
|
||||||
background: var(--brand-white);
|
background: var(--brand-white);
|
||||||
transform: scale(0.7);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox::after {
|
.checkbox::after {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
.action {
|
.action {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: block;
|
display: block;
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
margin-bottom: calc(var(--spacer) / 2);
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled {
|
.disabled {
|
||||||
@ -18,8 +19,8 @@ div [class*='loaderWrap'] {
|
|||||||
.scroll {
|
.scroll {
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
margin-top: calc(var(--spacer) / 4);
|
margin-top: calc(var(--spacer) / 4);
|
||||||
min-height: 200px;
|
min-height: fit-content;
|
||||||
max-height: 300px;
|
max-height: 50vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
/* smooth overflow scrolling for pre-iOS 13 */
|
/* smooth overflow scrolling for pre-iOS 13 */
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@ -30,7 +31,11 @@ div [class*='loaderWrap'] {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 4);
|
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row:last-child {
|
||||||
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@ -46,11 +51,10 @@ div [class*='loaderWrap'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
align-self: flex-start;
|
|
||||||
min-width: 1.2rem;
|
min-width: 1.2rem;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: calc(var(--spacer) / 4);
|
margin-right: calc(var(--spacer) / 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio {
|
.radio {
|
||||||
@ -64,7 +68,7 @@ div [class*='loaderWrap'] {
|
|||||||
.title {
|
.title {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
margin-top: calc(var(--spacer) / 12);
|
margin-top: calc(var(--spacer) / 12);
|
||||||
margin-bottom: calc(var(--spacer) / 12);
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
@ -89,11 +93,12 @@ div [class*='loaderWrap'] {
|
|||||||
.price {
|
.price {
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
font-size: var(--font-size-small) !important;
|
font-size: var(--font-size-small) !important;
|
||||||
|
padding-left: calc(var(--spacer) / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
margin: calc(var(--spacer) / 4);
|
margin: calc(var(--spacer) / 4) calc(var(--spacer) / 2);
|
||||||
width: calc(100% - calc(var(--spacer) / 2));
|
width: calc(100% - var(--spacer));
|
||||||
}
|
}
|
||||||
|
|
||||||
.did {
|
.did {
|
||||||
@ -102,4 +107,14 @@ div [class*='loaderWrap'] {
|
|||||||
display: block;
|
display: block;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
|
/* makes sure DotDotDot will kick in */
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
padding: var(--spacer) calc(var(--spacer) / 2);
|
||||||
|
text-align: center;
|
||||||
|
color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,10 @@ export interface AssetSelectionAsset {
|
|||||||
checked: boolean
|
checked: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Empty() {
|
||||||
|
return <div className={styles.empty}>No assets found.</div>
|
||||||
|
}
|
||||||
|
|
||||||
export default function AssetSelection({
|
export default function AssetSelection({
|
||||||
assets,
|
assets,
|
||||||
multiple,
|
multiple,
|
||||||
@ -52,7 +56,11 @@ export default function AssetSelection({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<div className={styles.scroll}>
|
<div className={styles.scroll}>
|
||||||
{assets ? (
|
{!assets ? (
|
||||||
|
<Loader />
|
||||||
|
) : assets && !assets.length ? (
|
||||||
|
<Empty />
|
||||||
|
) : (
|
||||||
assets
|
assets
|
||||||
.filter((asset: AssetSelectionAsset) =>
|
.filter((asset: AssetSelectionAsset) =>
|
||||||
searchValue !== ''
|
searchValue !== ''
|
||||||
@ -98,8 +106,6 @@ export default function AssetSelection({
|
|||||||
<PriceUnit price={asset.price} small className={styles.price} />
|
<PriceUnit price={asset.price} small className={styles.price} />
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
) : (
|
|
||||||
<Loader />
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,11 +76,11 @@
|
|||||||
min-width: 6rem;
|
min-width: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.walletInfo{
|
.walletInfo {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.walletInfo button{
|
.walletInfo button {
|
||||||
margin-top: calc(var(--spacer) / 5) !important;
|
margin-top: calc(var(--spacer) / 5) !important;
|
||||||
}
|
}
|
||||||
|
@ -1,178 +0,0 @@
|
|||||||
import React, { useState, ReactElement, ChangeEvent, useEffect } from 'react'
|
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
|
||||||
import Loader from '../../atoms/Loader'
|
|
||||||
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
|
||||||
import Price from '../../atoms/Price'
|
|
||||||
import File from '../../atoms/File'
|
|
||||||
import { computeOptions, useCompute } from '../../../hooks/useCompute'
|
|
||||||
import styles from './Compute.module.css'
|
|
||||||
import Input from '../../atoms/Input'
|
|
||||||
import Alert from '../../atoms/Alert'
|
|
||||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
|
||||||
import checkPreviousOrder from '../../../utils/checkPreviousOrder'
|
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
|
||||||
import { usePricing } from '../../../hooks/usePricing'
|
|
||||||
import { useAsset } from '../../../providers/Asset'
|
|
||||||
|
|
||||||
export default function Compute({
|
|
||||||
ddo,
|
|
||||||
isBalanceSufficient,
|
|
||||||
dtBalance
|
|
||||||
}: {
|
|
||||||
ddo: DDO
|
|
||||||
isBalanceSufficient: boolean
|
|
||||||
dtBalance: string
|
|
||||||
}): ReactElement {
|
|
||||||
const { marketFeeAddress } = useSiteMetadata()
|
|
||||||
const { accountId } = useWeb3()
|
|
||||||
const { ocean } = useOcean()
|
|
||||||
const { type } = useAsset()
|
|
||||||
const { compute, isLoading, computeStepText, computeError } = useCompute()
|
|
||||||
const { buyDT, dtSymbol } = usePricing(ddo)
|
|
||||||
const { price } = useAsset()
|
|
||||||
const computeService = ddo.findServiceByType('compute')
|
|
||||||
const metadataService = ddo.findServiceByType('metadata')
|
|
||||||
|
|
||||||
const [isJobStarting, setIsJobStarting] = useState(false)
|
|
||||||
const [, setError] = useState('')
|
|
||||||
const [computeType, setComputeType] = useState('nodejs')
|
|
||||||
const [computeContainer, setComputeContainer] = useState(
|
|
||||||
computeOptions[0].value
|
|
||||||
)
|
|
||||||
const [algorithmRawCode, setAlgorithmRawCode] = useState('')
|
|
||||||
const [isPublished, setIsPublished] = useState(false)
|
|
||||||
const [file, setFile] = useState(null)
|
|
||||||
const [hasPreviousOrder, setHasPreviousOrder] = useState(false)
|
|
||||||
const [previousOrderId, setPreviousOrderId] = useState<string>()
|
|
||||||
const isComputeButtonDisabled =
|
|
||||||
isJobStarting === true ||
|
|
||||||
file === null ||
|
|
||||||
computeType === '' ||
|
|
||||||
!ocean ||
|
|
||||||
!isBalanceSufficient
|
|
||||||
const hasDatatoken = Number(dtBalance) >= 1
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!ocean || !accountId) return
|
|
||||||
|
|
||||||
async function checkPreviousOrders() {
|
|
||||||
const orderId = await checkPreviousOrder(ocean, accountId, ddo, 'compute')
|
|
||||||
setPreviousOrderId(orderId)
|
|
||||||
setHasPreviousOrder(!!orderId)
|
|
||||||
}
|
|
||||||
checkPreviousOrders()
|
|
||||||
}, [ocean, ddo, accountId])
|
|
||||||
|
|
||||||
const handleSelectChange = (event: ChangeEvent<HTMLSelectElement>) => {
|
|
||||||
const comType = event.target.value
|
|
||||||
setComputeType(comType)
|
|
||||||
|
|
||||||
const selectedComputeOption = computeOptions.find((x) => x.name === comType)
|
|
||||||
if (selectedComputeOption !== undefined)
|
|
||||||
setComputeContainer(selectedComputeOption.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// const startJob = async () => {
|
|
||||||
// try {
|
|
||||||
// if (!ocean) return
|
|
||||||
|
|
||||||
// setIsJobStarting(true)
|
|
||||||
// setIsPublished(false)
|
|
||||||
// setError('')
|
|
||||||
|
|
||||||
// !hasPreviousOrder && !hasDatatoken && (await buyDT('1'))
|
|
||||||
|
|
||||||
// await compute(
|
|
||||||
// ddo.id,
|
|
||||||
// computeService,
|
|
||||||
// ddo.dataToken,
|
|
||||||
// algorithmRawCode,
|
|
||||||
// computeContainer,
|
|
||||||
// marketFeeAddress,
|
|
||||||
// previousOrderId
|
|
||||||
// )
|
|
||||||
|
|
||||||
// setHasPreviousOrder(true)
|
|
||||||
// setIsPublished(true)
|
|
||||||
// setFile(null)
|
|
||||||
// } catch (error) {
|
|
||||||
// setError('Failed to start job!')
|
|
||||||
// Logger.error(error.message)
|
|
||||||
// } finally {
|
|
||||||
// setIsJobStarting(false)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={styles.info}>
|
|
||||||
<div className={styles.filewrapper}>
|
|
||||||
<File file={metadataService.attributes.main.files[0]} small />
|
|
||||||
</div>
|
|
||||||
<div className={styles.pricewrapper}>
|
|
||||||
<Price price={price} conversion />
|
|
||||||
{hasDatatoken && (
|
|
||||||
<div className={styles.hasTokens}>
|
|
||||||
You own {dtBalance} {dtSymbol} allowing you to use this data set
|
|
||||||
without paying again.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{type === 'algorithm' ? (
|
|
||||||
<Input
|
|
||||||
type="select"
|
|
||||||
name="data"
|
|
||||||
label="Select dataset for the algorithm"
|
|
||||||
placeholder=""
|
|
||||||
size="small"
|
|
||||||
value="dataset-1"
|
|
||||||
options={['dataset-1', 'dataset-2', 'dataset-3'].map((x) => x)}
|
|
||||||
onChange={handleSelectChange}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Input
|
|
||||||
type="select"
|
|
||||||
name="algorithm"
|
|
||||||
label="Select image to run the algorithm"
|
|
||||||
placeholder=""
|
|
||||||
size="small"
|
|
||||||
value={computeType}
|
|
||||||
options={computeOptions.map((x) => x.name)}
|
|
||||||
onChange={handleSelectChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={styles.actions}>
|
|
||||||
{isLoading ? (
|
|
||||||
<Loader message={computeStepText} />
|
|
||||||
) : (
|
|
||||||
<Alert text="Compute is coming back at a later stage." state="info" />
|
|
||||||
// <Button
|
|
||||||
// style="primary"
|
|
||||||
// onClick={() => startJob()}
|
|
||||||
// disabled={isComputeButtonDisabled}
|
|
||||||
// >
|
|
||||||
// {hasDatatoken || hasPreviousOrder ? 'Start job' : 'Buy'}
|
|
||||||
// </Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer className={styles.feedback}>
|
|
||||||
{computeError !== undefined && (
|
|
||||||
<Alert text={computeError} state="error" />
|
|
||||||
)}
|
|
||||||
{isPublished && (
|
|
||||||
<Alert
|
|
||||||
title="Your job started!"
|
|
||||||
text="Watch the progress in the history page."
|
|
||||||
state="success"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Web3Feedback isBalanceSufficient={isBalanceSufficient} />
|
|
||||||
</footer>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -0,0 +1,37 @@
|
|||||||
|
.form {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
margin-top: -0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form > div > label,
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions a,
|
||||||
|
.actions button {
|
||||||
|
margin-left: calc(var(--spacer) / 2);
|
||||||
|
margin-right: calc(var(--spacer) / 2);
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
|
import styles from './FormComputeDataset.module.css'
|
||||||
|
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
|
||||||
|
import Input from '../../../atoms/Input'
|
||||||
|
import { FormFieldProps } from '../../../../@types/Form'
|
||||||
|
import { useStaticQuery, graphql } from 'gatsby'
|
||||||
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
||||||
|
import ButtonBuy from '../../../atoms/ButtonBuy'
|
||||||
|
|
||||||
|
const contentQuery = graphql`
|
||||||
|
query StartComputeDatasetQuery {
|
||||||
|
content: allFile(
|
||||||
|
filter: { relativePath: { eq: "pages/startComputeDataset.json" } }
|
||||||
|
) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
childPagesJson {
|
||||||
|
description
|
||||||
|
form {
|
||||||
|
success
|
||||||
|
successAction
|
||||||
|
error
|
||||||
|
data {
|
||||||
|
name
|
||||||
|
label
|
||||||
|
help
|
||||||
|
type
|
||||||
|
required
|
||||||
|
sortOptions
|
||||||
|
options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function FormStartCompute({
|
||||||
|
algorithms,
|
||||||
|
ddoListAlgorithms,
|
||||||
|
setSelectedAlgorithm,
|
||||||
|
isLoading,
|
||||||
|
isComputeButtonDisabled,
|
||||||
|
hasPreviousOrder,
|
||||||
|
hasDatatoken,
|
||||||
|
dtSymbol,
|
||||||
|
dtBalance,
|
||||||
|
stepText,
|
||||||
|
datasetTimeout
|
||||||
|
}: {
|
||||||
|
algorithms: AssetSelectionAsset[]
|
||||||
|
ddoListAlgorithms: DDO[]
|
||||||
|
setSelectedAlgorithm: React.Dispatch<React.SetStateAction<DDO>>
|
||||||
|
isLoading: boolean
|
||||||
|
isComputeButtonDisabled: boolean
|
||||||
|
hasPreviousOrder: boolean
|
||||||
|
hasDatatoken: boolean
|
||||||
|
dtSymbol: string
|
||||||
|
dtBalance: string
|
||||||
|
stepText: string
|
||||||
|
datasetTimeout: string
|
||||||
|
}): ReactElement {
|
||||||
|
const data = useStaticQuery(contentQuery)
|
||||||
|
const content = data.content.edges[0].node.childPagesJson
|
||||||
|
|
||||||
|
const {
|
||||||
|
isValid,
|
||||||
|
values
|
||||||
|
}: FormikContextType<{ algorithm: string }> = useFormikContext()
|
||||||
|
|
||||||
|
function getAlgorithmAsset(algorithmId: string): DDO {
|
||||||
|
let assetDdo = null
|
||||||
|
ddoListAlgorithms.forEach((ddo: DDO) => {
|
||||||
|
if (ddo.id === algorithmId) assetDdo = ddo
|
||||||
|
})
|
||||||
|
return assetDdo
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!values.algorithm) return
|
||||||
|
setSelectedAlgorithm(getAlgorithmAsset(values.algorithm))
|
||||||
|
}, [values.algorithm])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form className={styles.form}>
|
||||||
|
{content.form.data.map((field: FormFieldProps) => (
|
||||||
|
<Field
|
||||||
|
key={field.name}
|
||||||
|
{...field}
|
||||||
|
options={algorithms}
|
||||||
|
component={Input}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<ButtonBuy
|
||||||
|
action="compute"
|
||||||
|
disabled={isComputeButtonDisabled || !isValid}
|
||||||
|
hasPreviousOrder={hasPreviousOrder}
|
||||||
|
hasDatatoken={hasDatatoken}
|
||||||
|
dtSymbol={dtSymbol}
|
||||||
|
dtBalance={dtBalance}
|
||||||
|
stepText={stepText}
|
||||||
|
isLoading={isLoading}
|
||||||
|
type="submit"
|
||||||
|
assetTimeout={datasetTimeout}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
@ -21,8 +21,9 @@
|
|||||||
|
|
||||||
.feedback {
|
.feedback {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.help {
|
.help {
|
||||||
composes: help from './index.module.css';
|
composes: help from '../index.module.css';
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Compute from './Compute'
|
import Compute from '.'
|
||||||
import ddo from '../../../../tests/unit/__fixtures__/ddo'
|
import ddo from '../../../../../tests/unit/__fixtures__/ddo'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -13,5 +13,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Default = (): ReactElement => (
|
export const Default = (): ReactElement => (
|
||||||
<Compute ddo={ddo as DDO} dtBalance="1" isBalanceSufficient />
|
<Compute
|
||||||
|
ddo={ddo as DDO}
|
||||||
|
dtBalance="1"
|
||||||
|
isBalanceSufficient
|
||||||
|
file={ddo.service[0].attributes.main.files[0]}
|
||||||
|
/>
|
||||||
)
|
)
|
462
src/components/organisms/AssetActions/Compute/index.tsx
Normal file
462
src/components/organisms/AssetActions/Compute/index.tsx
Normal file
@ -0,0 +1,462 @@
|
|||||||
|
import React, { useState, ReactElement, useEffect, useCallback } from 'react'
|
||||||
|
import {
|
||||||
|
DDO,
|
||||||
|
File as FileMetadata,
|
||||||
|
Logger,
|
||||||
|
ServiceType,
|
||||||
|
publisherTrustedAlgorithm,
|
||||||
|
BestPrice
|
||||||
|
} from '@oceanprotocol/lib'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import Price from '../../../atoms/Price'
|
||||||
|
import File from '../../../atoms/File'
|
||||||
|
import Alert from '../../../atoms/Alert'
|
||||||
|
import Web3Feedback from '../../../molecules/Wallet/Feedback'
|
||||||
|
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||||
|
import checkPreviousOrder from '../../../../utils/checkPreviousOrder'
|
||||||
|
import { useOcean } from '../../../../providers/Ocean'
|
||||||
|
import { useWeb3 } from '../../../../providers/Web3'
|
||||||
|
import { usePricing } from '../../../../hooks/usePricing'
|
||||||
|
import { useAsset } from '../../../../providers/Asset'
|
||||||
|
import {
|
||||||
|
queryMetadata,
|
||||||
|
transformDDOToAssetSelection
|
||||||
|
} from '../../../../utils/aquarius'
|
||||||
|
import { Formik } from 'formik'
|
||||||
|
import {
|
||||||
|
getInitialValues,
|
||||||
|
validationSchema
|
||||||
|
} from '../../../../models/FormStartComputeDataset'
|
||||||
|
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
||||||
|
import { SearchQuery } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||||
|
import axios from 'axios'
|
||||||
|
import FormStartComputeDataset from './FormComputeDataset'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import SuccessConfetti from '../../../atoms/SuccessConfetti'
|
||||||
|
import Button from '../../../atoms/Button'
|
||||||
|
import { gql, useQuery } from '@apollo/client'
|
||||||
|
import { FrePrice } from '../../../../@types/apollo/FrePrice'
|
||||||
|
import { PoolPrice } from '../../../../@types/apollo/PoolPrice'
|
||||||
|
import { secondsToString } from '../../../../utils/metadata'
|
||||||
|
|
||||||
|
const SuccessAction = () => (
|
||||||
|
<Button style="text" to="/history" size="small">
|
||||||
|
Go to history →
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
|
||||||
|
const freQuery = gql`
|
||||||
|
query AlgorithmFrePrice($datatoken: String) {
|
||||||
|
fixedRateExchanges(orderBy: id, where: { datatoken: $datatoken }) {
|
||||||
|
rate
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const poolQuery = gql`
|
||||||
|
query AlgorithmPoolPrice($datatoken: String) {
|
||||||
|
pools(where: { datatokenAddress: $datatoken }) {
|
||||||
|
spotPrice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function Compute({
|
||||||
|
ddo,
|
||||||
|
isBalanceSufficient,
|
||||||
|
dtBalance,
|
||||||
|
file
|
||||||
|
}: {
|
||||||
|
ddo: DDO
|
||||||
|
isBalanceSufficient: boolean
|
||||||
|
dtBalance: string
|
||||||
|
file: FileMetadata
|
||||||
|
}): ReactElement {
|
||||||
|
const { marketFeeAddress } = useSiteMetadata()
|
||||||
|
const { accountId } = useWeb3()
|
||||||
|
const { ocean, account, config } = useOcean()
|
||||||
|
const { price, type } = useAsset()
|
||||||
|
const { buyDT, pricingError, pricingStepText } = usePricing()
|
||||||
|
const [isJobStarting, setIsJobStarting] = useState(false)
|
||||||
|
const [error, setError] = useState<string>()
|
||||||
|
|
||||||
|
const [algorithmList, setAlgorithmList] = useState<AssetSelectionAsset[]>()
|
||||||
|
const [ddoAlgorithmList, setDdoAlgorithmList] = useState<DDO[]>()
|
||||||
|
const [selectedAlgorithmAsset, setSelectedAlgorithmAsset] = useState<DDO>()
|
||||||
|
const [hasAlgoAssetDatatoken, setHasAlgoAssetDatatoken] = useState<boolean>()
|
||||||
|
const [isPublished, setIsPublished] = useState(false)
|
||||||
|
const [hasPreviousDatasetOrder, setHasPreviousDatasetOrder] = useState(false)
|
||||||
|
const [previousDatasetOrderId, setPreviousDatasetOrderId] = useState<string>()
|
||||||
|
const [hasPreviousAlgorithmOrder, setHasPreviousAlgorithmOrder] = useState(
|
||||||
|
false
|
||||||
|
)
|
||||||
|
const [algorithmPrice, setAlgorithmPrice] = useState<BestPrice>()
|
||||||
|
const [variables, setVariables] = useState({})
|
||||||
|
const [
|
||||||
|
previousAlgorithmOrderId,
|
||||||
|
setPreviousAlgorithmOrderId
|
||||||
|
] = useState<string>()
|
||||||
|
const [datasetTimeout, setDatasetTimeout] = useState<string>()
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
const {
|
||||||
|
refetch: refetchFre,
|
||||||
|
startPolling: startPollingFre,
|
||||||
|
data: frePrice
|
||||||
|
} = useQuery<FrePrice>(freQuery, {
|
||||||
|
variables,
|
||||||
|
skip: false
|
||||||
|
})
|
||||||
|
const {
|
||||||
|
refetch: refetchPool,
|
||||||
|
startPolling: startPollingPool,
|
||||||
|
data: poolPrice
|
||||||
|
} = useQuery<PoolPrice>(poolQuery, {
|
||||||
|
variables,
|
||||||
|
skip: false
|
||||||
|
})
|
||||||
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||||
|
|
||||||
|
const isComputeButtonDisabled =
|
||||||
|
isJobStarting === true || file === null || !ocean || !isBalanceSufficient
|
||||||
|
const hasDatatoken = Number(dtBalance) >= 1
|
||||||
|
|
||||||
|
async function checkPreviousOrders(ddo: DDO, serviceType: ServiceType) {
|
||||||
|
const orderId = await checkPreviousOrder(ocean, accountId, ddo, serviceType)
|
||||||
|
const assetType = ddo.findServiceByType('metadata').attributes.main.type
|
||||||
|
if (assetType === 'algorithm') {
|
||||||
|
setPreviousAlgorithmOrderId(orderId)
|
||||||
|
setHasPreviousAlgorithmOrder(!!orderId)
|
||||||
|
} else {
|
||||||
|
setPreviousDatasetOrderId(orderId)
|
||||||
|
setHasPreviousDatasetOrder(!!orderId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkAssetDTBalance(asset: DDO) {
|
||||||
|
const AssetDtBalance = await ocean.datatokens.balance(
|
||||||
|
asset.dataToken,
|
||||||
|
accountId
|
||||||
|
)
|
||||||
|
setHasAlgoAssetDatatoken(Number(AssetDtBalance) >= 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQuerryString(
|
||||||
|
trustedAlgorithmList: publisherTrustedAlgorithm[]
|
||||||
|
): SearchQuery {
|
||||||
|
let algoQuerry = ''
|
||||||
|
trustedAlgorithmList.forEach((trusteAlgo) => {
|
||||||
|
algoQuerry += `id:"${trusteAlgo.did}" OR `
|
||||||
|
})
|
||||||
|
if (trustedAlgorithmList.length > 1) {
|
||||||
|
algoQuerry = algoQuerry.substring(0, algoQuerry.length - 3)
|
||||||
|
}
|
||||||
|
const algorithmQuery =
|
||||||
|
trustedAlgorithmList.length > 0 ? `(${algoQuerry}) AND` : ``
|
||||||
|
const query = {
|
||||||
|
page: 1,
|
||||||
|
query: {
|
||||||
|
query_string: {
|
||||||
|
query: `${algorithmQuery} service.attributes.main.type:algorithm -isInPurgatory:true`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort: { created: -1 }
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAlgorithmList(): Promise<AssetSelectionAsset[]> {
|
||||||
|
const source = axios.CancelToken.source()
|
||||||
|
const computeService = ddo.findServiceByType('compute')
|
||||||
|
let algorithmSelectionList: AssetSelectionAsset[]
|
||||||
|
if (
|
||||||
|
!computeService.attributes.main.privacy ||
|
||||||
|
!computeService.attributes.main.privacy.publisherTrustedAlgorithms ||
|
||||||
|
(computeService.attributes.main.privacy.publisherTrustedAlgorithms
|
||||||
|
.length === 0 &&
|
||||||
|
!computeService.attributes.main.privacy.allowAllPublishedAlgorithms)
|
||||||
|
) {
|
||||||
|
algorithmSelectionList = []
|
||||||
|
} else {
|
||||||
|
const gueryResults = await queryMetadata(
|
||||||
|
getQuerryString(
|
||||||
|
computeService.attributes.main.privacy.publisherTrustedAlgorithms
|
||||||
|
),
|
||||||
|
config.metadataCacheUri,
|
||||||
|
source.token
|
||||||
|
)
|
||||||
|
setDdoAlgorithmList(gueryResults.results)
|
||||||
|
algorithmSelectionList = await transformDDOToAssetSelection(
|
||||||
|
gueryResults.results,
|
||||||
|
config.metadataCacheUri,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return algorithmSelectionList
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { timeout } = (
|
||||||
|
ddo.findServiceByType('access') || ddo.findServiceByType('compute')
|
||||||
|
).attributes.main
|
||||||
|
setDatasetTimeout(secondsToString(timeout))
|
||||||
|
}, [ddo])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!frePrice ||
|
||||||
|
frePrice.fixedRateExchanges.length === 0 ||
|
||||||
|
algorithmPrice.type !== 'exchange'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
setAlgorithmPrice((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
value: frePrice.fixedRateExchanges[0].rate,
|
||||||
|
address: frePrice.fixedRateExchanges[0].id
|
||||||
|
}))
|
||||||
|
}, [frePrice])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!poolPrice ||
|
||||||
|
poolPrice.pools.length === 0 ||
|
||||||
|
algorithmPrice.type !== 'pool'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
setAlgorithmPrice((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
value: poolPrice.pools[0].spotPrice
|
||||||
|
}))
|
||||||
|
}, [poolPrice])
|
||||||
|
|
||||||
|
const initMetadata = useCallback(async (ddo: DDO): Promise<void> => {
|
||||||
|
if (!ddo) return
|
||||||
|
setAlgorithmPrice(ddo.price)
|
||||||
|
setVariables({ datatoken: ddo?.dataToken.toLowerCase() })
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ddo) return
|
||||||
|
getAlgorithmList().then((algorithms) => {
|
||||||
|
setAlgorithmList(algorithms)
|
||||||
|
})
|
||||||
|
}, [ddo])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ocean || !accountId) return
|
||||||
|
checkPreviousOrders(ddo, 'compute')
|
||||||
|
}, [ocean, ddo, accountId])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ocean || !accountId || !selectedAlgorithmAsset) return
|
||||||
|
|
||||||
|
if (selectedAlgorithmAsset.findServiceByType('access')) {
|
||||||
|
checkPreviousOrders(selectedAlgorithmAsset, 'access').then(() => {
|
||||||
|
if (
|
||||||
|
!hasPreviousAlgorithmOrder &&
|
||||||
|
selectedAlgorithmAsset.findServiceByType('compute')
|
||||||
|
) {
|
||||||
|
checkPreviousOrders(selectedAlgorithmAsset, 'compute')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (selectedAlgorithmAsset.findServiceByType('compute')) {
|
||||||
|
checkPreviousOrders(selectedAlgorithmAsset, 'compute')
|
||||||
|
}
|
||||||
|
checkAssetDTBalance(selectedAlgorithmAsset)
|
||||||
|
initMetadata(selectedAlgorithmAsset)
|
||||||
|
}, [selectedAlgorithmAsset, ocean, accountId, hasPreviousAlgorithmOrder])
|
||||||
|
|
||||||
|
// Output errors in toast UI
|
||||||
|
useEffect(() => {
|
||||||
|
const newError = error || pricingError
|
||||||
|
if (!newError) return
|
||||||
|
toast.error(newError)
|
||||||
|
}, [error, pricingError])
|
||||||
|
|
||||||
|
async function startJob(algorithmId: string) {
|
||||||
|
try {
|
||||||
|
if (!ocean) return
|
||||||
|
|
||||||
|
setIsJobStarting(true)
|
||||||
|
setIsPublished(false)
|
||||||
|
setError(undefined)
|
||||||
|
|
||||||
|
const computeService = ddo.findServiceByType('compute')
|
||||||
|
const serviceAlgo = selectedAlgorithmAsset.findServiceByType('access')
|
||||||
|
? selectedAlgorithmAsset.findServiceByType('access')
|
||||||
|
: selectedAlgorithmAsset.findServiceByType('compute')
|
||||||
|
|
||||||
|
const allowed = await ocean.compute.isOrderable(
|
||||||
|
ddo.id,
|
||||||
|
computeService.index,
|
||||||
|
selectedAlgorithmAsset.id
|
||||||
|
)
|
||||||
|
Logger.log('[compute] Is data set orderable?', allowed)
|
||||||
|
|
||||||
|
if (!allowed) {
|
||||||
|
setError(
|
||||||
|
'Data set is not orderable in combination with selected algorithm.'
|
||||||
|
)
|
||||||
|
Logger.error(
|
||||||
|
'[compute] Error starting compute job. Dataset is not orderable in combination with selected algorithm.'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPreviousDatasetOrder && !hasDatatoken) {
|
||||||
|
const tx = await buyDT('1', price, ddo)
|
||||||
|
if (!tx) {
|
||||||
|
setError('Error buying datatoken.')
|
||||||
|
Logger.error('[compute] Error buying datatoken for data set ', ddo.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPreviousAlgorithmOrder && !hasAlgoAssetDatatoken) {
|
||||||
|
const tx = await buyDT('1', algorithmPrice, selectedAlgorithmAsset)
|
||||||
|
if (!tx) {
|
||||||
|
setError('Error buying datatoken.')
|
||||||
|
Logger.error(
|
||||||
|
'[compute] Error buying datatoken for algorithm ',
|
||||||
|
selectedAlgorithmAsset.id
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: pricingError is always undefined even upon errors during buyDT for whatever reason.
|
||||||
|
// So manually drop out above, but ideally could be replaced with this alone.
|
||||||
|
if (pricingError) {
|
||||||
|
setError(pricingError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const assetOrderId = hasPreviousDatasetOrder
|
||||||
|
? previousDatasetOrderId
|
||||||
|
: await ocean.compute.orderAsset(
|
||||||
|
accountId,
|
||||||
|
ddo.id,
|
||||||
|
computeService.index,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
marketFeeAddress
|
||||||
|
)
|
||||||
|
|
||||||
|
assetOrderId &&
|
||||||
|
Logger.log(
|
||||||
|
`[compute] Got ${
|
||||||
|
hasPreviousDatasetOrder ? 'existing' : 'new'
|
||||||
|
} order ID for dataset: `,
|
||||||
|
assetOrderId
|
||||||
|
)
|
||||||
|
|
||||||
|
const algorithmAssetOrderId = hasPreviousAlgorithmOrder
|
||||||
|
? previousAlgorithmOrderId
|
||||||
|
: await ocean.compute.orderAlgorithm(
|
||||||
|
algorithmId,
|
||||||
|
serviceAlgo.type,
|
||||||
|
accountId,
|
||||||
|
serviceAlgo.index,
|
||||||
|
marketFeeAddress
|
||||||
|
)
|
||||||
|
|
||||||
|
algorithmAssetOrderId &&
|
||||||
|
Logger.log(
|
||||||
|
`[compute] Got ${
|
||||||
|
hasPreviousAlgorithmOrder ? 'existing' : 'new'
|
||||||
|
} order ID for algorithm: `,
|
||||||
|
algorithmAssetOrderId
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!assetOrderId || !algorithmAssetOrderId) {
|
||||||
|
setError('Error ordering assets.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.log('[compute] Starting compute job.')
|
||||||
|
|
||||||
|
const output = {}
|
||||||
|
const response = await ocean.compute.start(
|
||||||
|
ddo.id,
|
||||||
|
assetOrderId,
|
||||||
|
ddo.dataToken,
|
||||||
|
account,
|
||||||
|
algorithmId,
|
||||||
|
undefined,
|
||||||
|
output,
|
||||||
|
`${computeService.index}`,
|
||||||
|
computeService.type,
|
||||||
|
algorithmAssetOrderId,
|
||||||
|
selectedAlgorithmAsset.dataToken
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
!response ||
|
||||||
|
response.status !== 10 ||
|
||||||
|
response.statusText !== 'Job started'
|
||||||
|
) {
|
||||||
|
setError('Error starting compute job.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.log('[compute] Starting compute job response: ', response)
|
||||||
|
|
||||||
|
setHasPreviousDatasetOrder(true)
|
||||||
|
setIsPublished(true)
|
||||||
|
} catch (error) {
|
||||||
|
setError('Failed to start job!')
|
||||||
|
Logger.error('[compute] Failed to start job: ', error.message)
|
||||||
|
} finally {
|
||||||
|
setIsJobStarting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={styles.info}>
|
||||||
|
<File file={file} small />
|
||||||
|
<Price price={(ddo as DDO).price} conversion />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{type === 'algorithm' ? (
|
||||||
|
<Alert
|
||||||
|
text="This algorithm has been set to private by the publisher and can't be downloaded. You can run it against any allowed data sets though!"
|
||||||
|
state="info"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Formik
|
||||||
|
initialValues={getInitialValues()}
|
||||||
|
validateOnMount
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={async (values) => await startJob(values.algorithm)}
|
||||||
|
>
|
||||||
|
<FormStartComputeDataset
|
||||||
|
algorithms={algorithmList}
|
||||||
|
ddoListAlgorithms={ddoAlgorithmList}
|
||||||
|
setSelectedAlgorithm={setSelectedAlgorithmAsset}
|
||||||
|
isLoading={isJobStarting}
|
||||||
|
isComputeButtonDisabled={isComputeButtonDisabled}
|
||||||
|
hasPreviousOrder={
|
||||||
|
hasPreviousDatasetOrder || hasPreviousAlgorithmOrder
|
||||||
|
}
|
||||||
|
hasDatatoken={hasDatatoken}
|
||||||
|
dtSymbol={ddo.dataTokenInfo?.symbol}
|
||||||
|
dtBalance={dtBalance}
|
||||||
|
stepText={pricingStepText || 'Starting Compute Job...'}
|
||||||
|
datasetTimeout={datasetTimeout}
|
||||||
|
/>
|
||||||
|
</Formik>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<footer className={styles.feedback}>
|
||||||
|
{isPublished && (
|
||||||
|
<SuccessConfetti
|
||||||
|
success="Your job started successfully! Watch the progress on the history page."
|
||||||
|
action={<SuccessAction />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Web3Feedback isBalanceSufficient={isBalanceSufficient} />
|
||||||
|
</footer>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -12,19 +12,6 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: calc(var(--spacer) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.help {
|
|
||||||
composes: help from './index.module.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.help:not(:empty) {
|
|
||||||
margin-top: calc(var(--spacer) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.feedback {
|
.feedback {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { File as FileMetadata, DDO } from '@oceanprotocol/lib'
|
import { File as FileMetadata, DDO } from '@oceanprotocol/lib'
|
||||||
import Button from '../../atoms/Button'
|
|
||||||
import File from '../../atoms/File'
|
import File from '../../atoms/File'
|
||||||
import Price from '../../atoms/Price'
|
import Price from '../../atoms/Price'
|
||||||
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
||||||
import styles from './Consume.module.css'
|
import styles from './Consume.module.css'
|
||||||
import Loader from '../../atoms/Loader'
|
|
||||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
||||||
import { useAsset } from '../../../providers/Asset'
|
import { useAsset } from '../../../providers/Asset'
|
||||||
import { secondsToString } from '../../../utils/metadata'
|
import { secondsToString } from '../../../utils/metadata'
|
||||||
@ -17,6 +15,7 @@ import { useOcean } from '../../../providers/Ocean'
|
|||||||
import { useWeb3 } from '../../../providers/Web3'
|
import { useWeb3 } from '../../../providers/Web3'
|
||||||
import { usePricing } from '../../../hooks/usePricing'
|
import { usePricing } from '../../../hooks/usePricing'
|
||||||
import { useConsume } from '../../../hooks/useConsume'
|
import { useConsume } from '../../../hooks/useConsume'
|
||||||
|
import ButtonBuy from '../../atoms/ButtonBuy'
|
||||||
|
|
||||||
const previousOrderQuery = gql`
|
const previousOrderQuery = gql`
|
||||||
query PreviousOrder($id: String!, $account: String!) {
|
query PreviousOrder($id: String!, $account: String!) {
|
||||||
@ -32,26 +31,6 @@ const previousOrderQuery = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
function getHelpText(
|
|
||||||
token: {
|
|
||||||
dtBalance: string
|
|
||||||
dtSymbol: string
|
|
||||||
},
|
|
||||||
hasDatatoken: boolean,
|
|
||||||
hasPreviousOrder: boolean,
|
|
||||||
timeout: string
|
|
||||||
) {
|
|
||||||
const { dtBalance, dtSymbol } = token
|
|
||||||
const assetTimeout = timeout === 'Forever' ? '' : ` for ${timeout}`
|
|
||||||
const text = hasPreviousOrder
|
|
||||||
? `You bought this data set already allowing you to download it without paying again${assetTimeout}.`
|
|
||||||
: hasDatatoken
|
|
||||||
? `You own ${dtBalance} ${dtSymbol} allowing you to use this data set by spending 1 ${dtSymbol}, but without paying OCEAN again.`
|
|
||||||
: `For using this data set, you will buy 1 ${dtSymbol} and immediately spend it back to the publisher and pool.`
|
|
||||||
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Consume({
|
export default function Consume({
|
||||||
ddo,
|
ddo,
|
||||||
file,
|
file,
|
||||||
@ -68,10 +47,13 @@ export default function Consume({
|
|||||||
const { marketFeeAddress } = useSiteMetadata()
|
const { marketFeeAddress } = useSiteMetadata()
|
||||||
const [hasPreviousOrder, setHasPreviousOrder] = useState(false)
|
const [hasPreviousOrder, setHasPreviousOrder] = useState(false)
|
||||||
const [previousOrderId, setPreviousOrderId] = useState<string>()
|
const [previousOrderId, setPreviousOrderId] = useState<string>()
|
||||||
const { isInPurgatory, price, type } = useAsset()
|
const { isInPurgatory, price } = useAsset()
|
||||||
const { buyDT, pricingStepText, pricingError, pricingIsLoading } = usePricing(
|
const {
|
||||||
ddo
|
buyDT,
|
||||||
)
|
pricingStepText,
|
||||||
|
pricingError,
|
||||||
|
pricingIsLoading
|
||||||
|
} = usePricing()
|
||||||
const { consumeStepText, consume, consumeError } = useConsume()
|
const { consumeStepText, consume, consumeError } = useConsume()
|
||||||
const [isDisabled, setIsDisabled] = useState(true)
|
const [isDisabled, setIsDisabled] = useState(true)
|
||||||
const [hasDatatoken, setHasDatatoken] = useState(false)
|
const [hasDatatoken, setHasDatatoken] = useState(false)
|
||||||
@ -144,7 +126,7 @@ export default function Consume({
|
|||||||
])
|
])
|
||||||
|
|
||||||
async function handleConsume() {
|
async function handleConsume() {
|
||||||
!hasPreviousOrder && !hasDatatoken && (await buyDT('1', price))
|
!hasPreviousOrder && !hasDatatoken && (await buyDT('1', price, ddo))
|
||||||
await consume(
|
await consume(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
ddo.dataToken,
|
ddo.dataToken,
|
||||||
@ -162,29 +144,18 @@ export default function Consume({
|
|||||||
}, [consumeError, pricingError])
|
}, [consumeError, pricingError])
|
||||||
|
|
||||||
const PurchaseButton = () => (
|
const PurchaseButton = () => (
|
||||||
<div className={styles.actions}>
|
<ButtonBuy
|
||||||
{consumeStepText || pricingIsLoading ? (
|
action="download"
|
||||||
<Loader message={consumeStepText || pricingStepText} />
|
disabled={isDisabled}
|
||||||
) : (
|
hasPreviousOrder={hasPreviousOrder}
|
||||||
<>
|
hasDatatoken={hasDatatoken}
|
||||||
<Button style="primary" onClick={handleConsume} disabled={isDisabled}>
|
dtSymbol={ddo.dataTokenInfo?.symbol}
|
||||||
{hasPreviousOrder
|
dtBalance={dtBalance}
|
||||||
? 'Download'
|
onClick={handleConsume}
|
||||||
: `Buy ${
|
assetTimeout={assetTimeout}
|
||||||
assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`
|
stepText={consumeStepText || pricingStepText}
|
||||||
}`}
|
isLoading={pricingIsLoading}
|
||||||
</Button>
|
/>
|
||||||
<div className={styles.help}>
|
|
||||||
{getHelpText(
|
|
||||||
{ dtBalance, dtSymbol: ddo.dataTokenInfo.symbol },
|
|
||||||
hasDatatoken,
|
|
||||||
hasPreviousOrder,
|
|
||||||
assetTimeout
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -8,9 +8,14 @@ import { FormFieldProps } from '../../../../@types/Form'
|
|||||||
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
||||||
import stylesIndex from './index.module.css'
|
import stylesIndex from './index.module.css'
|
||||||
import styles from './FormEditMetadata.module.css'
|
import styles from './FormEditMetadata.module.css'
|
||||||
import { getAlgorithmsForAssetSelection } from '../../../../utils/aquarius'
|
import {
|
||||||
|
queryMetadata,
|
||||||
|
transformDDOToAssetSelection
|
||||||
|
} from '../../../../utils/aquarius'
|
||||||
import { useAsset } from '../../../../providers/Asset'
|
import { useAsset } from '../../../../providers/Asset'
|
||||||
import { ComputePrivacyForm } from '../../../../models/FormEditComputeDataset'
|
import { ComputePrivacyForm } from '../../../../models/FormEditComputeDataset'
|
||||||
|
import { publisherTrustedAlgorithm as PublisherTrustedAlgorithm } from '@oceanprotocol/lib'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
export default function FormEditComputeDataset({
|
export default function FormEditComputeDataset({
|
||||||
data,
|
data,
|
||||||
@ -34,11 +39,34 @@ export default function FormEditComputeDataset({
|
|||||||
'compute'
|
'compute'
|
||||||
).attributes.main.privacy
|
).attributes.main.privacy
|
||||||
|
|
||||||
useEffect(() => {
|
async function getAlgorithmList(
|
||||||
getAlgorithmsForAssetSelection(
|
publisherTrustedAlgorithms: PublisherTrustedAlgorithm[]
|
||||||
|
): Promise<AssetSelectionAsset[]> {
|
||||||
|
const source = axios.CancelToken.source()
|
||||||
|
const query = {
|
||||||
|
page: 1,
|
||||||
|
query: {
|
||||||
|
query_string: {
|
||||||
|
query: `service.attributes.main.type:algorithm -isInPurgatory:true`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort: { created: -1 }
|
||||||
|
}
|
||||||
|
const querryResult = await queryMetadata(
|
||||||
|
query,
|
||||||
|
config.metadataCacheUri,
|
||||||
|
source.token
|
||||||
|
)
|
||||||
|
const algorithmSelectionList = await transformDDOToAssetSelection(
|
||||||
|
querryResult.results,
|
||||||
config.metadataCacheUri,
|
config.metadataCacheUri,
|
||||||
publisherTrustedAlgorithms
|
publisherTrustedAlgorithms
|
||||||
).then((algorithms) => {
|
)
|
||||||
|
return algorithmSelectionList
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getAlgorithmList(publisherTrustedAlgorithms).then((algorithms) => {
|
||||||
setAllAlgorithms(algorithms)
|
setAllAlgorithms(algorithms)
|
||||||
})
|
})
|
||||||
}, [config.metadataCacheUri, publisherTrustedAlgorithms])
|
}, [config.metadataCacheUri, publisherTrustedAlgorithms])
|
||||||
|
@ -89,7 +89,9 @@ export default function Edit({
|
|||||||
}
|
}
|
||||||
let ddoEditedTimeout = ddoEditedMetdata
|
let ddoEditedTimeout = ddoEditedMetdata
|
||||||
if (timeoutStringValue !== values.timeout) {
|
if (timeoutStringValue !== values.timeout) {
|
||||||
const service = ddoEditedMetdata.findServiceByType('access')
|
const service =
|
||||||
|
ddoEditedMetdata.findServiceByType('access') ||
|
||||||
|
ddoEditedMetdata.findServiceByType('compute')
|
||||||
const timeout = mapTimeoutStringToSeconds(values.timeout)
|
const timeout = mapTimeoutStringToSeconds(values.timeout)
|
||||||
ddoEditedTimeout = await ocean.assets.editServiceTimeout(
|
ddoEditedTimeout = await ocean.assets.editServiceTimeout(
|
||||||
ddoEditedMetdata,
|
ddoEditedMetdata,
|
||||||
|
@ -4,9 +4,3 @@
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.help {
|
|
||||||
font-size: var(--font-size-mini);
|
|
||||||
color: var(--color-secondary);
|
|
||||||
margin-top: calc(var(--spacer) / 3);
|
|
||||||
}
|
|
||||||
|
@ -57,6 +57,7 @@ export default function AssetActions(): ReactElement {
|
|||||||
ddo={ddo}
|
ddo={ddo}
|
||||||
dtBalance={dtBalance}
|
dtBalance={dtBalance}
|
||||||
isBalanceSufficient={isBalanceSufficient}
|
isBalanceSufficient={isBalanceSufficient}
|
||||||
|
file={metadata?.main.files[0]}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Consume
|
<Consume
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacer);
|
gap: var(--spacer);
|
||||||
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
.metaFull code {
|
.metaFull code {
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeAndDate {
|
.typeAndDate {
|
||||||
margin-top: calc(var(--spacer) / 2);
|
margin-bottom: calc(var(--spacer) / 12);
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,5 +21,5 @@
|
|||||||
padding-right: calc(var(--spacer) / 3.5);
|
padding-right: calc(var(--spacer) / 3.5);
|
||||||
margin-right: calc(var(--spacer) / 4);
|
margin-right: calc(var(--spacer) / 4);
|
||||||
width: auto;
|
width: auto;
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
|
@ -15,21 +15,6 @@ export default function MetaMain(): ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className={styles.meta}>
|
<aside className={styles.meta}>
|
||||||
<p>
|
|
||||||
<ExplorerLink
|
|
||||||
networkId={networkId}
|
|
||||||
path={
|
|
||||||
networkId === 137
|
|
||||||
? `tokens/${ddo?.dataToken}`
|
|
||||||
: `token/${ddo?.dataToken}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{`${ddo?.dataTokenInfo.name} — ${ddo?.dataTokenInfo.symbol}`}
|
|
||||||
</ExplorerLink>
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
Published By <Publisher account={owner} />
|
|
||||||
</div>
|
|
||||||
<div className={styles.typeAndDate}>
|
<div className={styles.typeAndDate}>
|
||||||
<AssetType
|
<AssetType
|
||||||
type={type}
|
type={type}
|
||||||
@ -46,6 +31,19 @@ export default function MetaMain(): ReactElement {
|
|||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<p>
|
||||||
|
<ExplorerLink
|
||||||
|
networkId={networkId}
|
||||||
|
path={
|
||||||
|
networkId === 137
|
||||||
|
? `tokens/${ddo?.dataToken}`
|
||||||
|
: `token/${ddo?.dataToken}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{`${ddo?.dataTokenInfo.name} — ${ddo?.dataTokenInfo.symbol}`}
|
||||||
|
</ExplorerLink>
|
||||||
|
</p>
|
||||||
|
Published By <Publisher account={owner} />
|
||||||
</aside>
|
</aside>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Conversion from '../../../../atoms/Price/Conversion'
|
import Conversion from '../../../../atoms/Price/Conversion'
|
||||||
import { useField } from 'formik'
|
import { useField } from 'formik'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement, useState, useEffect } from 'react'
|
||||||
import Input from '../../../../atoms/Input'
|
import Input from '../../../../atoms/Input'
|
||||||
import styles from './Price.module.css'
|
import styles from './Price.module.css'
|
||||||
import Error from './Error'
|
import Error from './Error'
|
||||||
@ -16,7 +16,23 @@ export default function Price({
|
|||||||
firstPrice?: string
|
firstPrice?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [field, meta] = useField('price')
|
const [field, meta] = useField('price')
|
||||||
const { dtName, dtSymbol } = usePricing(ddo)
|
const { getDTName, getDTSymbol } = usePricing()
|
||||||
|
const [dtSymbol, setDtSymbol] = useState<string>()
|
||||||
|
const [dtName, setDtName] = useState<string>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ddo) return
|
||||||
|
async function setDatatokenSymbol(ddo: DDO) {
|
||||||
|
const dtSymbol = await getDTSymbol(ddo)
|
||||||
|
setDtSymbol(dtSymbol)
|
||||||
|
}
|
||||||
|
async function setDatatokenName(ddo: DDO) {
|
||||||
|
const dtName = await getDTName(ddo)
|
||||||
|
setDtName(dtName)
|
||||||
|
}
|
||||||
|
setDatatokenSymbol(ddo)
|
||||||
|
setDatatokenName(ddo)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.price}>
|
<div className={styles.price}>
|
||||||
|
@ -62,7 +62,7 @@ export default function Pricing({ ddo }: { ddo: DDO }): ReactElement {
|
|||||||
pricingIsLoading,
|
pricingIsLoading,
|
||||||
pricingError,
|
pricingError,
|
||||||
pricingStepText
|
pricingStepText
|
||||||
} = usePricing(ddo)
|
} = usePricing()
|
||||||
|
|
||||||
const hasFeedback = pricingIsLoading || typeof success !== 'undefined'
|
const hasFeedback = pricingIsLoading || typeof success !== 'undefined'
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ export default function Pricing({ ddo }: { ddo: DDO }): ReactElement {
|
|||||||
swapFee: `${values.swapFee / 100}`
|
swapFee: `${values.swapFee / 100}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const tx = await createPricing(priceOptions)
|
const tx = await createPricing(priceOptions, ddo)
|
||||||
|
|
||||||
// Pricing failed
|
// Pricing failed
|
||||||
if (!tx || pricingError) {
|
if (!tx || pricingError) {
|
||||||
|
@ -72,13 +72,13 @@ export default function FormPublish(): ReactElement {
|
|||||||
|
|
||||||
function handleImageSelectChange(imageSelected: string) {
|
function handleImageSelectChange(imageSelected: string) {
|
||||||
switch (imageSelected) {
|
switch (imageSelected) {
|
||||||
case 'node:pre-defined': {
|
case 'node:latest': {
|
||||||
setFieldValue('image', 'node')
|
setFieldValue('image', 'node')
|
||||||
setFieldValue('containerTag', '10')
|
setFieldValue('containerTag', 'latest')
|
||||||
setFieldValue('entrypoint', 'node $ALGO')
|
setFieldValue('entrypoint', 'node $ALGO')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'python:pre-defined': {
|
case 'python:latest': {
|
||||||
setFieldValue('image', 'oceanprotocol/algo_dockers')
|
setFieldValue('image', 'oceanprotocol/algo_dockers')
|
||||||
setFieldValue('containerTag', 'python-panda')
|
setFieldValue('containerTag', 'python-panda')
|
||||||
setFieldValue('entrypoint', 'python $ALGO')
|
setFieldValue('entrypoint', 'python $ALGO')
|
||||||
|
@ -4,7 +4,6 @@ import {
|
|||||||
} from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
} from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||||
import { MetadataCache, Logger } from '@oceanprotocol/lib'
|
import { MetadataCache, Logger } from '@oceanprotocol/lib'
|
||||||
import queryString from 'query-string'
|
import queryString from 'query-string'
|
||||||
import { TypeOf } from 'yup'
|
|
||||||
|
|
||||||
export const SortTermOptions = {
|
export const SortTermOptions = {
|
||||||
Liquidity: 'liquidity',
|
Liquidity: 'liquidity',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { DDO, Logger, BestPrice } from '@oceanprotocol/lib'
|
import { DDO, Logger, BestPrice } from '@oceanprotocol/lib'
|
||||||
import { useEffect, useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { TransactionReceipt } from 'web3-core'
|
import { TransactionReceipt } from 'web3-core'
|
||||||
import { Decimal } from 'decimal.js'
|
import { Decimal } from 'decimal.js'
|
||||||
import { getFirstPoolPrice } from '../utils/dtUtils'
|
import { getFirstPoolPrice } from '../utils/dtUtils'
|
||||||
@ -24,16 +24,21 @@ interface PriceOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface UsePricing {
|
interface UsePricing {
|
||||||
dtSymbol?: string
|
getDTSymbol: (ddo: DDO) => Promise<string>
|
||||||
dtName?: string
|
getDTName: (ddo: DDO) => Promise<string>
|
||||||
createPricing: (
|
createPricing: (
|
||||||
priceOptions: PriceOptions
|
priceOptions: PriceOptions,
|
||||||
|
ddo: DDO
|
||||||
) => Promise<TransactionReceipt | string | void>
|
) => Promise<TransactionReceipt | string | void>
|
||||||
sellDT: (dtAmount: number | string) => Promise<TransactionReceipt | void>
|
sellDT: (
|
||||||
mint: (tokensToMint: string) => Promise<TransactionReceipt | void>
|
dtAmount: number | string,
|
||||||
|
ddo: DDO
|
||||||
|
) => Promise<TransactionReceipt | void>
|
||||||
|
mint: (tokensToMint: string, ddo: DDO) => Promise<TransactionReceipt | void>
|
||||||
buyDT: (
|
buyDT: (
|
||||||
dtAmount: number | string,
|
dtAmount: number | string,
|
||||||
price: BestPrice
|
price: BestPrice,
|
||||||
|
ddo: DDO
|
||||||
) => Promise<TransactionReceipt | void>
|
) => Promise<TransactionReceipt | void>
|
||||||
pricingStep?: number
|
pricingStep?: number
|
||||||
pricingStepText?: string
|
pricingStepText?: string
|
||||||
@ -41,38 +46,37 @@ interface UsePricing {
|
|||||||
pricingIsLoading: boolean
|
pricingIsLoading: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function usePricing(ddo: DDO): UsePricing {
|
function usePricing(): UsePricing {
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { ocean, config } = useOcean()
|
const { ocean, config } = useOcean()
|
||||||
const [pricingIsLoading, setPricingIsLoading] = useState(false)
|
const [pricingIsLoading, setPricingIsLoading] = useState(false)
|
||||||
const [pricingStep, setPricingStep] = useState<number>()
|
const [pricingStep, setPricingStep] = useState<number>()
|
||||||
const [pricingStepText, setPricingStepText] = useState<string>()
|
const [pricingStepText, setPricingStepText] = useState<string>()
|
||||||
const [pricingError, setPricingError] = useState<string>()
|
const [pricingError, setPricingError] = useState<string>()
|
||||||
const [dtSymbol, setDtSymbol] = useState<string>()
|
|
||||||
const [dtName, setDtName] = useState<string>()
|
|
||||||
|
|
||||||
const { dataToken, dataTokenInfo } = ddo
|
async function getDTSymbol(ddo: DDO): Promise<string> {
|
||||||
|
if (!ocean || !accountId) return
|
||||||
|
const { dataToken, dataTokenInfo } = ddo
|
||||||
|
return dataTokenInfo
|
||||||
|
? dataTokenInfo.symbol
|
||||||
|
: await ocean?.datatokens.getSymbol(dataToken)
|
||||||
|
}
|
||||||
|
|
||||||
// Get Datatoken info, from DDO first, then from chain
|
async function getDTName(ddo: DDO): Promise<string> {
|
||||||
useEffect(() => {
|
if (!ocean || !accountId) return
|
||||||
if (!dataToken) return
|
const { dataToken, dataTokenInfo } = ddo
|
||||||
|
return dataTokenInfo
|
||||||
async function init() {
|
? dataTokenInfo.name
|
||||||
const dtSymbol = dataTokenInfo
|
: await ocean?.datatokens.getName(dataToken)
|
||||||
? dataTokenInfo.symbol
|
}
|
||||||
: await ocean?.datatokens.getSymbol(dataToken)
|
|
||||||
setDtSymbol(dtSymbol)
|
|
||||||
|
|
||||||
const dtName = dataTokenInfo
|
|
||||||
? dataTokenInfo.name
|
|
||||||
: await ocean?.datatokens.getName(dataToken)
|
|
||||||
setDtName(dtName)
|
|
||||||
}
|
|
||||||
init()
|
|
||||||
}, [ocean, dataToken, dataTokenInfo])
|
|
||||||
|
|
||||||
// Helper for setting steps & feedback for all flows
|
// Helper for setting steps & feedback for all flows
|
||||||
function setStep(index: number, type: 'pool' | 'exchange' | 'buy' | 'sell') {
|
async function setStep(
|
||||||
|
index: number,
|
||||||
|
type: 'pool' | 'exchange' | 'buy' | 'sell',
|
||||||
|
ddo: DDO
|
||||||
|
) {
|
||||||
|
const dtSymbol = await getDTSymbol(ddo)
|
||||||
setPricingStep(index)
|
setPricingStep(index)
|
||||||
if (!dtSymbol) return
|
if (!dtSymbol) return
|
||||||
|
|
||||||
@ -97,8 +101,10 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function mint(
|
async function mint(
|
||||||
tokensToMint: string
|
tokensToMint: string,
|
||||||
|
ddo: DDO
|
||||||
): Promise<TransactionReceipt | void> {
|
): Promise<TransactionReceipt | void> {
|
||||||
|
const { dataToken } = ddo
|
||||||
Logger.log('mint function', dataToken, accountId)
|
Logger.log('mint function', dataToken, accountId)
|
||||||
const balance = new Decimal(
|
const balance = new Decimal(
|
||||||
await ocean.datatokens.balance(dataToken, accountId)
|
await ocean.datatokens.balance(dataToken, accountId)
|
||||||
@ -117,7 +123,8 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
|
|
||||||
async function buyDT(
|
async function buyDT(
|
||||||
dtAmount: number | string,
|
dtAmount: number | string,
|
||||||
price: BestPrice
|
price: BestPrice,
|
||||||
|
ddo: DDO
|
||||||
): Promise<TransactionReceipt | void> {
|
): Promise<TransactionReceipt | void> {
|
||||||
if (!ocean || !accountId) return
|
if (!ocean || !accountId) return
|
||||||
|
|
||||||
@ -126,14 +133,14 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
try {
|
try {
|
||||||
setPricingIsLoading(true)
|
setPricingIsLoading(true)
|
||||||
setPricingError(undefined)
|
setPricingError(undefined)
|
||||||
setStep(1, 'buy')
|
setStep(1, 'buy', ddo)
|
||||||
|
|
||||||
Logger.log('Price found for buying', price)
|
Logger.log('Price found for buying', price)
|
||||||
switch (price?.type) {
|
switch (price?.type) {
|
||||||
case 'pool': {
|
case 'pool': {
|
||||||
const oceanAmmount = new Decimal(price.value).times(1.05).toString()
|
const oceanAmmount = new Decimal(price.value).times(1.05).toString()
|
||||||
const maxPrice = new Decimal(price.value).times(2).toString()
|
const maxPrice = new Decimal(price.value).times(2).toString()
|
||||||
setStep(2, 'buy')
|
setStep(2, 'buy', ddo)
|
||||||
Logger.log('Buying token from pool', price, accountId, price)
|
Logger.log('Buying token from pool', price, accountId, price)
|
||||||
tx = await ocean.pool.buyDT(
|
tx = await ocean.pool.buyDT(
|
||||||
accountId,
|
accountId,
|
||||||
@ -142,7 +149,7 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
oceanAmmount,
|
oceanAmmount,
|
||||||
maxPrice
|
maxPrice
|
||||||
)
|
)
|
||||||
setStep(3, 'buy')
|
setStep(3, 'buy', ddo)
|
||||||
Logger.log('DT buy response', tx)
|
Logger.log('DT buy response', tx)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -162,13 +169,13 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
`${price.value}`,
|
`${price.value}`,
|
||||||
accountId
|
accountId
|
||||||
)
|
)
|
||||||
setStep(2, 'buy')
|
setStep(2, 'buy', ddo)
|
||||||
tx = await ocean.fixedRateExchange.buyDT(
|
tx = await ocean.fixedRateExchange.buyDT(
|
||||||
price.address,
|
price.address,
|
||||||
`${dtAmount}`,
|
`${dtAmount}`,
|
||||||
accountId
|
accountId
|
||||||
)
|
)
|
||||||
setStep(3, 'buy')
|
setStep(3, 'buy', ddo)
|
||||||
Logger.log('DT exchange buy response', tx)
|
Logger.log('DT exchange buy response', tx)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -177,7 +184,7 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
setPricingError(error.message)
|
setPricingError(error.message)
|
||||||
Logger.error(error)
|
Logger.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
setStep(0, 'buy')
|
setStep(0, 'buy', ddo)
|
||||||
setPricingStepText(undefined)
|
setPricingStepText(undefined)
|
||||||
setPricingIsLoading(false)
|
setPricingIsLoading(false)
|
||||||
}
|
}
|
||||||
@ -186,7 +193,8 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function sellDT(
|
async function sellDT(
|
||||||
dtAmount: number | string
|
dtAmount: number | string,
|
||||||
|
ddo: DDO
|
||||||
): Promise<TransactionReceipt | void> {
|
): Promise<TransactionReceipt | void> {
|
||||||
if (!ocean || !accountId) return
|
if (!ocean || !accountId) return
|
||||||
|
|
||||||
@ -196,13 +204,14 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const { dataToken } = ddo
|
||||||
setPricingIsLoading(true)
|
setPricingIsLoading(true)
|
||||||
setPricingError(undefined)
|
setPricingError(undefined)
|
||||||
setStep(1, 'sell')
|
setStep(1, 'sell', ddo)
|
||||||
const pool = await getFirstPoolPrice(ocean, dataToken)
|
const pool = await getFirstPoolPrice(ocean, dataToken)
|
||||||
if (!pool || pool.value === 0) return
|
if (!pool || pool.value === 0) return
|
||||||
const price = new Decimal(pool.value).times(0.95).toString()
|
const price = new Decimal(pool.value).times(0.95).toString()
|
||||||
setStep(2, 'sell')
|
setStep(2, 'sell', ddo)
|
||||||
Logger.log('Selling token to pool', pool, accountId, price)
|
Logger.log('Selling token to pool', pool, accountId, price)
|
||||||
const tx = await ocean.pool.sellDT(
|
const tx = await ocean.pool.sellDT(
|
||||||
accountId,
|
accountId,
|
||||||
@ -210,22 +219,26 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
`${dtAmount}`,
|
`${dtAmount}`,
|
||||||
price
|
price
|
||||||
)
|
)
|
||||||
setStep(3, 'sell')
|
setStep(3, 'sell', ddo)
|
||||||
Logger.log('DT sell response', tx)
|
Logger.log('DT sell response', tx)
|
||||||
return tx
|
return tx
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setPricingError(error.message)
|
setPricingError(error.message)
|
||||||
Logger.error(error)
|
Logger.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
setStep(0, 'sell')
|
setStep(0, 'sell', ddo)
|
||||||
setPricingStepText(undefined)
|
setPricingStepText(undefined)
|
||||||
setPricingIsLoading(false)
|
setPricingIsLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPricing(
|
async function createPricing(
|
||||||
priceOptions: PriceOptions
|
priceOptions: PriceOptions,
|
||||||
|
ddo: DDO
|
||||||
): Promise<TransactionReceipt | void> {
|
): Promise<TransactionReceipt | void> {
|
||||||
|
const { dataToken } = ddo
|
||||||
|
const dtSymbol = await getDTSymbol(ddo)
|
||||||
|
|
||||||
if (!ocean || !accountId || !dtSymbol) return
|
if (!ocean || !accountId || !dtSymbol) return
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -247,12 +260,12 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
setPricingIsLoading(true)
|
setPricingIsLoading(true)
|
||||||
setPricingError(undefined)
|
setPricingError(undefined)
|
||||||
|
|
||||||
setStep(99, 'pool')
|
setStep(99, 'pool', ddo)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// if fixedPrice set dt to max amount
|
// if fixedPrice set dt to max amount
|
||||||
if (!isPool) dtAmount = 1000
|
if (!isPool) dtAmount = 1000
|
||||||
await mint(`${dtAmount}`)
|
await mint(`${dtAmount}`, ddo)
|
||||||
|
|
||||||
// dtAmount for fixed price is set to max
|
// dtAmount for fixed price is set to max
|
||||||
const tx = isPool
|
const tx = isPool
|
||||||
@ -265,10 +278,10 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
`${oceanAmount}`,
|
`${oceanAmount}`,
|
||||||
swapFee
|
swapFee
|
||||||
)
|
)
|
||||||
.next((step: number) => setStep(step, 'pool'))
|
.next((step: number) => setStep(step, 'pool', ddo))
|
||||||
: await ocean.fixedRateExchange
|
: await ocean.fixedRateExchange
|
||||||
.create(dataToken, `${price}`, accountId, `${dtAmount}`)
|
.create(dataToken, `${price}`, accountId, `${dtAmount}`)
|
||||||
.next((step: number) => setStep(step, 'exchange'))
|
.next((step: number) => setStep(step, 'exchange', ddo))
|
||||||
await sleep(20000)
|
await sleep(20000)
|
||||||
return tx
|
return tx
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -282,8 +295,8 @@ function usePricing(ddo: DDO): UsePricing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dtSymbol,
|
getDTSymbol,
|
||||||
dtName,
|
getDTName,
|
||||||
createPricing,
|
createPricing,
|
||||||
buyDT,
|
buyDT,
|
||||||
sellDT,
|
sellDT,
|
||||||
|
@ -11,7 +11,7 @@ export const validationSchema: Yup.SchemaOf<MetadataPublishFormAlgorithm> = Yup.
|
|||||||
description: Yup.string().min(10).required('Required'),
|
description: Yup.string().min(10).required('Required'),
|
||||||
files: Yup.array<FileMetadata>().required('Required').nullable(),
|
files: Yup.array<FileMetadata>().required('Required').nullable(),
|
||||||
dockerImage: Yup.string()
|
dockerImage: Yup.string()
|
||||||
.matches(/node:pre-defined|python:pre-defined|custom image/g, {
|
.matches(/node:latest|python:latest|custom image/g, {
|
||||||
excludeEmptyString: true
|
excludeEmptyString: true
|
||||||
})
|
})
|
||||||
.required('Required'),
|
.required('Required'),
|
||||||
@ -30,9 +30,9 @@ export const validationSchema: Yup.SchemaOf<MetadataPublishFormAlgorithm> = Yup.
|
|||||||
export const initialValues: Partial<MetadataPublishFormAlgorithm> = {
|
export const initialValues: Partial<MetadataPublishFormAlgorithm> = {
|
||||||
name: '',
|
name: '',
|
||||||
author: '',
|
author: '',
|
||||||
dockerImage: 'node:pre-defined',
|
dockerImage: 'node:latest',
|
||||||
image: 'node',
|
image: 'node',
|
||||||
containerTag: '10',
|
containerTag: 'latest',
|
||||||
entrypoint: 'node $ALGO',
|
entrypoint: 'node $ALGO',
|
||||||
files: '',
|
files: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
13
src/models/FormStartComputeDataset.ts
Normal file
13
src/models/FormStartComputeDataset.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
export const validationSchema: Yup.SchemaOf<{
|
||||||
|
algorithm: string
|
||||||
|
}> = Yup.object().shape({
|
||||||
|
algorithm: Yup.string().required('Required')
|
||||||
|
})
|
||||||
|
|
||||||
|
export function getInitialValues(): { algorithm: string } {
|
||||||
|
return {
|
||||||
|
algorithm: undefined
|
||||||
|
}
|
||||||
|
}
|
@ -74,6 +74,7 @@ function AssetProvider({
|
|||||||
const [type, setType] = useState<MetadataMain['type']>()
|
const [type, setType] = useState<MetadataMain['type']>()
|
||||||
const [variables, setVariables] = useState({})
|
const [variables, setVariables] = useState({})
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
const {
|
const {
|
||||||
refetch: refetchFre,
|
refetch: refetchFre,
|
||||||
startPolling: startPollingFre,
|
startPolling: startPollingFre,
|
||||||
@ -90,6 +91,7 @@ function AssetProvider({
|
|||||||
variables,
|
variables,
|
||||||
skip: false
|
skip: false
|
||||||
})
|
})
|
||||||
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||||
|
|
||||||
// this is not working as expected, thus we need to fetch both pool and fre
|
// this is not working as expected, thus we need to fetch both pool and fre
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
Config,
|
|
||||||
DDO,
|
DDO,
|
||||||
DID,
|
DID,
|
||||||
Logger,
|
Logger,
|
||||||
publisherTrustedAlgorithm as PublisherTrustedAlgorithm
|
publisherTrustedAlgorithm as PublisherTrustedAlgorithm
|
||||||
} from '@oceanprotocol/lib/'
|
} from '@oceanprotocol/lib/'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
QueryResult,
|
QueryResult,
|
||||||
SearchQuery
|
SearchQuery
|
||||||
@ -12,7 +12,6 @@ import {
|
|||||||
import { AssetSelectionAsset } from '../components/molecules/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '../components/molecules/FormFields/AssetSelection'
|
||||||
import axios, { CancelToken, AxiosResponse } from 'axios'
|
import axios, { CancelToken, AxiosResponse } from 'axios'
|
||||||
import web3 from 'web3'
|
import web3 from 'web3'
|
||||||
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
|
|
||||||
|
|
||||||
// TODO: import directly from ocean.js somehow.
|
// TODO: import directly from ocean.js somehow.
|
||||||
// Transforming Aquarius' direct response is needed for getting actual DDOs
|
// Transforming Aquarius' direct response is needed for getting actual DDOs
|
||||||
@ -107,33 +106,17 @@ export async function getAssetsNames(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAlgorithmsForAssetSelection(
|
export async function transformDDOToAssetSelection(
|
||||||
|
ddoList: DDO[],
|
||||||
metadataCacheUri: string,
|
metadataCacheUri: string,
|
||||||
selectedAlgorithms?: PublisherTrustedAlgorithm[]
|
selectedAlgorithms?: PublisherTrustedAlgorithm[]
|
||||||
): Promise<AssetSelectionAsset[]> {
|
): Promise<AssetSelectionAsset[]> {
|
||||||
const query = {
|
|
||||||
page: 1,
|
|
||||||
query: {
|
|
||||||
query_string: {
|
|
||||||
query: `(service.attributes.main.type:algorithm) -isInPurgatory:true`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sort: { created: -1 }
|
|
||||||
}
|
|
||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
const didList: string[] = []
|
const didList: string[] = []
|
||||||
const priceList: any = {}
|
const priceList: any = {}
|
||||||
const result = await queryMetadata(
|
ddoList.forEach((ddo: DDO) => {
|
||||||
query as any,
|
didList.push(ddo.id)
|
||||||
metadataCacheUri,
|
priceList[ddo.id] = ddo.price.value
|
||||||
source.token
|
|
||||||
)
|
|
||||||
result?.results?.forEach((ddo: DDO) => {
|
|
||||||
const did: string = web3.utils
|
|
||||||
.toChecksumAddress(ddo.dataToken)
|
|
||||||
.replace('0x', 'did:op:')
|
|
||||||
didList.push(did)
|
|
||||||
priceList[did] = ddo.price.value
|
|
||||||
})
|
})
|
||||||
const ddoNames = await getAssetsNames(didList, metadataCacheUri, source.token)
|
const ddoNames = await getAssetsNames(didList, metadataCacheUri, source.token)
|
||||||
const algorithmList: AssetSelectionAsset[] = []
|
const algorithmList: AssetSelectionAsset[] = []
|
||||||
|
Loading…
Reference in New Issue
Block a user