Merge branch 'main' into fix/issue497-fetch-price-subgraph, fixed merge conflicts

This commit is contained in:
Bogdan Fazakas 2021-05-10 16:27:32 +03:00
commit ff1d987b5d
41 changed files with 308 additions and 320 deletions

View File

@ -1,5 +1,5 @@
# Default network, possible values:
# "development", "ropsten", "rinkeby", "mainnet", "polygon"
# "development", "ropsten", "rinkeby", "mainnet", "polygon", "moonbeamalpha"
GATSBY_NETWORK="rinkeby"
#GATSBY_INFURA_PROJECT_ID="xxx"

View File

@ -20,6 +20,15 @@
"rows": 10,
"required": true
},
{
"name": "price",
"label": "New Price",
"type": "number",
"min": "1",
"placeholder": "0",
"help": "Enter a new price.",
"required": true
},
{
"name": "links",
"label": "Sample file",

View File

@ -6,13 +6,6 @@
"successAction": "Close",
"error": "Updating DDO failed.",
"data": [
{
"name": "allowAllPublishedAlgorithms",
"label": "All Algorithms",
"help": "Allow any published algorithm to run on this data set.",
"type": "checkbox",
"options": ["Allow any published algorithm"]
},
{
"name": "publisherTrustedAlgorithms",
"label": "Selected Algorithms",
@ -21,6 +14,13 @@
"multiple": true,
"options": [],
"sortOptions": false
},
{
"name": "allowAllPublishedAlgorithms",
"label": "All Algorithms",
"help": "Allow any published algorithm to run on this data set.",
"type": "checkbox",
"options": ["Allow any published algorithm"]
}
]
}

View File

@ -1,4 +1,7 @@
{
"title": "History",
"description": "Find the data sets and jobs that you previously accessed."
"description": "Find the data sets and jobs that you previously accessed.",
"compute": {
"storage": "Results are stored for 30 days."
}
}

View File

@ -19,7 +19,7 @@
"name": "files",
"label": "File",
"placeholder": "e.g. https://file.com/file.json",
"help": "Please enter the URL to your algorithm file and click \"ADD FILE\" to validate the data. This URL will be stored encrypted after publishing.",
"help": "Please enter the URL to your algorithm file and click \"ADD FILE\" to validate the data. This URL will be stored encrypted after publishing. Some restrictions apply:\n\n- max. running time: 1 min.\n- [Writing Algorithms for Compute to Data](https://docs.oceanprotocol.com/tutorials/compute-to-data-algorithms/)",
"type": "files",
"required": true
},
@ -56,6 +56,13 @@
"sortOptions": false,
"required": true
},
{
"name": "dataTokenOptions",
"label": "Datatoken Name & Symbol",
"type": "datatoken",
"help": "The datatoken for this algorithm will be created with this name & symbol.",
"required": true
},
{
"name": "entrypoint",
"label": "Entrypoint",

View File

@ -19,7 +19,7 @@
"name": "files",
"label": "File",
"placeholder": "e.g. https://file.com/file.json",
"help": "Please enter the URL to your data set file and click \"ADD FILE\" to validate the data. This URL will be stored encrypted after publishing.",
"help": "Please enter the URL to your data set file and click \"ADD FILE\" to validate the data. This URL will be stored encrypted after publishing. For a compute data set, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size.",
"type": "files",
"required": true
},

36
package-lock.json generated
View File

@ -1573,16 +1573,16 @@
}
},
"@ethereum-navigator/atlas": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@ethereum-navigator/atlas/-/atlas-0.7.1.tgz",
"integrity": "sha512-YV7tMVwpRcJbc+Kj/Rr0RzNV/2hHBEEM1/tMWDVLB15dGJfoQuRfPJpFt6uq+Ji6s3EkldIt9kZylEeG5ALKAA=="
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@ethereum-navigator/atlas/-/atlas-0.7.3.tgz",
"integrity": "sha512-kCyV/8wOqSU/gn+H7uSaR/Xc+ZogXrW2QmF5MfZL2+NUS4+y0emlfBphTGwP5bOB0Cg2goJTcI7Y6+0LNcJYzg=="
},
"@ethereum-navigator/navigator": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@ethereum-navigator/navigator/-/navigator-0.5.2.tgz",
"integrity": "sha512-agSE2xzLxOKKid8QiS4v8jPhnFXW5uSXsICZ4JmS437aCZ8L3SUAy3cDQKikHb2PPZ3AazJO05k8m8i6u77peQ==",
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/@ethereum-navigator/navigator/-/navigator-0.5.3.tgz",
"integrity": "sha512-AOhS1EXPrVeWbLvW3fVbw5AZ2mPYXDKOqMojgVz602U/tEjRXAsw/Gwa+oSOssAyU37SNKBaIorEhBiEY+RYdA==",
"requires": {
"@ethereum-navigator/atlas": "^0.7.1",
"@ethereum-navigator/atlas": "^0.7.2",
"web3": "^1.2.7"
}
},
@ -3622,24 +3622,24 @@
"integrity": "sha512-j4PEZSVtKSqxDYMVh/hd5vk088Bg6a6QkrUMTXN9Q6OIFAMfHM235f1AxaakNrEyK0FKMD908KuJEdfFLRn9Hw=="
},
"@oceanprotocol/contracts": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-0.5.15.tgz",
"integrity": "sha512-16edzNeO2v5WLW9ClsZ9VvMH1w24fZZutCmDhZWPirCAIdojGWRHraompfICjY1fovDljnZ5MpJpffuQ6kgxOA=="
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-0.5.16.tgz",
"integrity": "sha512-p7aFIUT8RVoMzdPP7ML8G08BnQ09syywKjOT16hqJm0GmofunEuVffUXbryG4EkQ+qRbf/zeoxSmesi79kQXlA=="
},
"@oceanprotocol/lib": {
"version": "0.14.4",
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-0.14.4.tgz",
"integrity": "sha512-f6Wj6FLpYmuFSGtnDw0lex0Vru1tGgvZqtwrdBYtMD+hW6Bn3B51+F/9ACVYAiSRDADrqPTDZiZetT2Ji3QY7Q==",
"version": "0.14.8",
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-0.14.8.tgz",
"integrity": "sha512-eqab5iEgowyIM/LcDDs6xhZo/KToOmVw0betjXLG0+g70zS8R6XL2RHzCpFyutSdf/cH0w/ltPUfR8ZBElIyhQ==",
"requires": {
"@ethereum-navigator/navigator": "^0.5.2",
"@oceanprotocol/contracts": "^0.5.10",
"@oceanprotocol/contracts": "0.5.16",
"@types/crypto-js": "^4.0.1",
"cross-fetch": "^3.1.2",
"crypto-js": "^4.0.0",
"decimal.js": "^10.2.1",
"fs": "0.0.1-security",
"lzma": "^2.3.2",
"node-abort-controller": "^1.2.0",
"node-abort-controller": "^2.0.0",
"save-file": "^2.3.1",
"uuid": "^8.3.2",
"web3": "^1.3.5",
@ -28882,9 +28882,9 @@
}
},
"node-abort-controller": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-1.2.1.tgz",
"integrity": "sha512-79PYeJuj6S9+yOHirR0JBLFOgjB6sQCir10uN6xRx25iD+ZD4ULqgRn3MwWBRaQGB0vEgReJzWwJo42T1R6YbQ=="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-2.0.0.tgz",
"integrity": "sha512-L8RfEgjBTHAISTuagw51PprVAqNZoG6KSB6LQ6H1bskMVkFs5E71IyjauLBv3XbuomJlguWF/VnRHdJ1gqiAqA=="
},
"node-addon-api": {
"version": "2.0.2",

View File

@ -27,7 +27,7 @@
"@coingecko/cryptoformat": "^0.4.2",
"@loadable/component": "^5.14.1",
"@oceanprotocol/art": "^3.0.0",
"@oceanprotocol/lib": "^0.14.4",
"@oceanprotocol/lib": "^0.14.8",
"@oceanprotocol/typographies": "^0.1.0",
"@portis/web3": "^3.0.3",
"@sindresorhus/slugify": "^1.0.0",

View File

@ -49,6 +49,7 @@ export interface MetadataPublishFormAlgorithm {
dockerImage: string
algorithmPrivacy: boolean
timeout: string
dataTokenOptions: DataTokenOptions
termsAndConditions: boolean
// ---- optional fields ----
image: string
@ -61,6 +62,7 @@ export interface MetadataEditForm {
name: string
description: string
timeout: string
price?: number
links?: string | EditableMetadataLinks[]
}

View File

@ -91,6 +91,7 @@ export default function InputElement({
id={slugify(option)}
type={type}
name={name}
checked={props.defaultChecked}
{...props}
/>
<label className={styles.radioLabel} htmlFor={slugify(option)}>

View File

@ -107,7 +107,9 @@
.did {
padding: 0;
font-size: var(--font-size-mini);
/* font-size: var(--font-size-mini); */
/* hack to make DotDotDot clamp work in Safari*/
font-size: 0.63rem;
display: block;
text-align: left;
color: var(--color-secondary);

View File

@ -27,7 +27,7 @@ export default function FileInput({
<Button
style="primary"
size="small"
onClick={(e: React.SyntheticEvent) => handleButtonClick(e, field.value)}
onClick={(e: React.SyntheticEvent) => e.preventDefault()}
disabled={!field.value}
>
{isLoading ? <Loader /> : 'Add File'}

View File

@ -14,7 +14,7 @@ export default function FilesInput(props: InputProps): ReactElement {
const [fileUrl, setFileUrl] = useState<string>()
const { config } = useOcean()
useEffect(() => {
function loadFileInfo() {
const source = axios.CancelToken.source()
async function validateUrl() {
@ -33,11 +33,16 @@ export default function FilesInput(props: InputProps): ReactElement {
setIsLoading(false)
}
}
fileUrl && validateUrl()
return () => {
source.cancel()
}
}
useEffect(() => {
loadFileInfo()
}, [fileUrl, config.providerUri])
async function handleButtonClick(e: React.SyntheticEvent, url: string) {
@ -48,6 +53,11 @@ export default function FilesInput(props: InputProps): ReactElement {
// File example 'https://oceanprotocol.com/tech-whitepaper.pdf'
e.preventDefault()
// In the case when the user re-add the same URL after it was removed (by accident or intentionally)
if (fileUrl === url) {
loadFileInfo()
}
setFileUrl(url)
}

View File

@ -14,6 +14,10 @@ const query = graphql`
export default function Terms(props: InputProps): ReactElement {
const data = useStaticQuery(query)
const termsProps: InputProps = {
...props,
defaultChecked: props.value.toString() === 'true'
}
return (
<>
@ -21,7 +25,7 @@ export default function Terms(props: InputProps): ReactElement {
className={styles.terms}
dangerouslySetInnerHTML={{ __html: data.terms.html }}
/>
<InputElement {...props} type="checkbox" />
<InputElement {...termsProps} type="checkbox" />
</>
)
}

View File

@ -135,6 +135,11 @@ export function MetadataAlgorithmPreview({
<h2 className={styles.previewTitle}>Preview</h2>
<header>
{values.name && <h3 className={styles.title}>{values.name}</h3>}
{values.dataTokenOptions?.name && (
<p
className={styles.datatoken}
>{`${values.dataTokenOptions.name}${values.dataTokenOptions.symbol}`}</p>
)}
{values.description && <Description description={values.description} />}
<div className={styles.asset}>

View File

@ -17,20 +17,30 @@ export default function SearchBar({
filters?: boolean
size?: 'small' | 'large'
}): ReactElement {
const [value, setValue] = useState(initialValue || '')
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setValue(e.target.value)
}
let [value, setValue] = useState(initialValue || '')
async function startSearch(e: FormEvent<HTMLButtonElement>) {
e.preventDefault()
if (value === '') return
if (value === '') value = ' '
const urlEncodedValue = encodeURIComponent(value)
const url = await addExistingParamsToUrl(location, 'text')
navigate(`${url}&text=${urlEncodedValue}`)
}
async function emptySearch() {
const searchParams = new URLSearchParams(window.location.href)
const text = searchParams.get('text')
if (text !== ('' || undefined || null)) {
const url = await addExistingParamsToUrl(location, 'text')
navigate(`${url}&text=%20`)
}
}
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setValue(e.target.value)
e.target.value === '' && emptySearch()
}
return (
<form className={styles.form}>
<InputGroup>

View File

@ -1,14 +1,13 @@
.buttons {
display: flex;
justify-content: space-between;
display: grid;
gap: calc(var(--spacer) / 4);
grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr));
padding-bottom: calc(var(--spacer) / 8);
}
.button {
display: block;
flex: 0 0 48%;
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 2)
calc(var(--spacer) / 4) calc(var(--spacer) / 2) !important;
width: auto;
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 4) !important;
border-radius: var(--border-radius);
text-transform: none;
}

View File

@ -18,8 +18,9 @@ export default function Chain(): ReactElement {
}
const chains = [
{ name: 'ETH', oceanConfig: 'mainnet' },
{ name: 'Polygon/Matic', oceanConfig: 'polygon' }
{ name: 'ETH', oceanConfig: 'mainnet', label: 'Mainnet' },
{ name: 'Polygon/Matic', oceanConfig: 'polygon', label: 'Mainnet' },
{ name: 'Moonbase Alpha', oceanConfig: 'moonbeamalpha', label: 'Testnet' }
]
// TODO: to fully solve https://github.com/oceanprotocol/market/issues/432
@ -42,7 +43,7 @@ export default function Chain(): ReactElement {
onClick={() => connectOcean(button.oceanConfig)}
>
{button.name}
<span>Mainnet</span>
<span>{button.label}</span>
</Button>
)
})}

View File

@ -25,7 +25,10 @@ import {
getInitialValues,
validationSchema
} from '../../../../models/FormStartComputeDataset'
import { ComputeAlgorithm } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
import {
ComputeAlgorithm,
ComputeOutput
} from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
import { SearchQuery } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
import axios from 'axios'
@ -190,26 +193,30 @@ export default function Compute({
}, [ocean, ddo, accountId])
useEffect(() => {
if (!ocean || !accountId || !selectedAlgorithmAsset) return
if (!selectedAlgorithmAsset) return
if (selectedAlgorithmAsset.findServiceByType('access')) {
checkPreviousOrders(selectedAlgorithmAsset).then(() => {
if (
!hasPreviousAlgorithmOrder &&
selectedAlgorithmAsset.findServiceByType('compute')
) {
checkPreviousOrders(selectedAlgorithmAsset)
}
})
} else if (selectedAlgorithmAsset.findServiceByType('compute')) {
checkPreviousOrders(selectedAlgorithmAsset)
}
checkAssetDTBalance(selectedAlgorithmAsset)
initMetadata(selectedAlgorithmAsset)
const { timeout } = (
ddo.findServiceByType('access') || ddo.findServiceByType('compute')
).attributes.main
setAlgorithmTimeout(secondsToString(timeout))
if (accountId) {
if (selectedAlgorithmAsset.findServiceByType('access')) {
checkPreviousOrders(selectedAlgorithmAsset).then(() => {
if (
!hasPreviousAlgorithmOrder &&
selectedAlgorithmAsset.findServiceByType('compute')
) {
checkPreviousOrders(selectedAlgorithmAsset)
}
})
} else if (selectedAlgorithmAsset.findServiceByType('compute')) {
checkPreviousOrders(selectedAlgorithmAsset)
}
}
ocean && checkAssetDTBalance(selectedAlgorithmAsset)
}, [selectedAlgorithmAsset, ocean, accountId, hasPreviousAlgorithmOrder])
// Output errors in toast UI
@ -330,7 +337,10 @@ export default function Compute({
computeAlgorithm.transferTxId = algorithmAssetOrderId
Logger.log('[compute] Starting compute job.')
const output = {}
const output: ComputeOutput = {
publishAlgorithmLog: true,
publishOutput: true
}
const response = await ocean.compute.start(
ddo.id,
assetOrderId,
@ -410,7 +420,9 @@ export default function Compute({
action={<SuccessAction />}
/>
)}
<Web3Feedback isBalanceSufficient={isBalanceSufficient} />
{type !== 'algorithm' && (
<Web3Feedback isBalanceSufficient={isBalanceSufficient} />
)}
</footer>
</>
)

View File

@ -47,15 +47,17 @@ export default function FormEditMetadata({
data,
setShowEdit,
setTimeoutStringValue,
values
values,
showPrice
}: {
data: FormFieldProps[]
setShowEdit: (show: boolean) => void
setTimeoutStringValue: (value: string) => void
values: Partial<MetadataPublishFormDataset>
showPrice: boolean
}): ReactElement {
const { accountId } = useWeb3()
const { ocean } = useOcean()
const { ocean, config } = useOcean()
const {
isValid,
validateField,
@ -79,16 +81,20 @@ export default function FormEditMetadata({
return (
<Form className={styles.form}>
{data.map((field: FormFieldProps) => (
<Field
key={field.name}
{...field}
component={Input}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
handleFieldChange(e, field)
}
/>
))}
{data.map(
(field: FormFieldProps) =>
(!showPrice && field.name === 'price') || (
<Field
key={field.name}
{...field}
component={Input}
prefix={field.name === 'price' && config.oceanTokenSymbol}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
handleFieldChange(e, field)
}
/>
)
)}
<footer className={styles.actions}>
<Button

View File

@ -36,6 +36,7 @@ const contentQuery = graphql`
label
help
type
min
required
sortOptions
options
@ -60,7 +61,7 @@ export default function Edit({
const { debug } = useUserPreferences()
const { accountId } = useWeb3()
const { ocean } = useOcean()
const { metadata, ddo, refreshDdo } = useAsset()
const { metadata, ddo, refreshDdo, price } = useAsset()
const [success, setSuccess] = useState<string>()
const [error, setError] = useState<string>()
const [timeoutStringValue, setTimeoutStringValue] = useState<string>()
@ -70,6 +71,18 @@ export default function Edit({
const hasFeedback = error || success
async function updateFixedPrice(newPrice: number) {
const setPriceResp = await ocean.fixedRateExchange.setRate(
price.address,
newPrice,
accountId
)
if (!setPriceResp) {
setError(content.form.error)
Logger.error(content.form.error)
}
}
async function handleSubmit(
values: Partial<MetadataEditForm>,
resetForm: () => void
@ -82,6 +95,10 @@ export default function Edit({
links: typeof values.links !== 'string' ? values.links : []
})
price.type === 'exchange' &&
values.price !== price.value &&
(await updateFixedPrice(values.price))
if (!ddoEditedMetdata) {
setError(content.form.error)
Logger.error(content.form.error)
@ -127,7 +144,7 @@ export default function Edit({
return (
<Formik
initialValues={getInitialValues(metadata, timeout)}
initialValues={getInitialValues(metadata, timeout, price.value)}
validationSchema={validationSchema}
onSubmit={async (values, { resetForm }) => {
// move user's focus to top of screen
@ -160,6 +177,7 @@ export default function Edit({
setShowEdit={setShowEdit}
setTimeoutStringValue={setTimeoutStringValue}
values={initialValues}
showPrice={price.type === 'exchange'}
/>
<aside>

View File

@ -174,7 +174,7 @@ export default function Add({
) : (
<Alert
className={styles.warning}
text={content.warning.main}
text={content.warning}
state="info"
action={{
name: 'I understand',

View File

@ -21,6 +21,7 @@ import UserLiquidity from '../../../atoms/UserLiquidity'
import InputElement from '../../../atoms/Input/InputElement'
import { useOcean } from '../../../../providers/Ocean'
import { useWeb3 } from '../../../../providers/Web3'
import Decimal from 'decimal.js'
const contentQuery = graphql`
query PoolRemoveQuery {
@ -155,14 +156,6 @@ export default function Remove({
totalPoolTokens
])
async function calculateAmountOfOceansRemoved(amountPoolShares: string) {
const oceanAmount = await ocean.pool.getOceanRemovedforPoolShares(
poolAddress,
amountPoolShares
)
setAmountOcean(oceanAmount)
}
useEffect(() => {
const minOceanAmount =
(Number(amountOcean) * (100 - Number(slippage))) / 100
@ -177,19 +170,24 @@ export default function Remove({
setAmountPercent(e.target.value)
if (!poolTokens) return
const amountPoolShares = (Number(e.target.value) / 100) * Number(poolTokens)
const amountPoolShares = new Decimal(e.target.value)
.dividedBy(100)
.mul(new Decimal(poolTokens))
.toPrecision(18) // in some cases the returned value contain more than 18 digits which break conversion to wei
setAmountPoolShares(`${amountPoolShares}`)
calculateAmountOfOceansRemoved(`${amountPoolShares}`)
}
function handleMaxButton(e: ChangeEvent<HTMLInputElement>) {
e.preventDefault()
setAmountPercent(amountMaxPercent)
const amountPoolShares =
(Number(amountMaxPercent) / 100) * Number(poolTokens)
const amountPoolShares = new Decimal(amountMaxPercent)
.dividedBy(100)
.mul(new Decimal(poolTokens))
.toPrecision(18)
setAmountPoolShares(`${amountPoolShares}`)
calculateAmountOfOceansRemoved(`${amountPoolShares}`)
}
function handleAdvancedButton(e: FormEvent<HTMLButtonElement>) {

View File

@ -44,6 +44,7 @@ const poolLiquidityQuery = gql`
id
totalShares
swapFee
spotPrice
tokens {
tokenAddress
balance
@ -141,7 +142,8 @@ export default function Pool(): ReactElement {
setCreatorLiquidity(creatorLiquidity)
const totalCreatorLiquidityInOcean =
creatorLiquidity?.ocean + creatorLiquidity?.datatoken * price?.value
creatorLiquidity?.ocean +
creatorLiquidity?.datatoken * dataLiquidity.pool.spotPrice
setCreatorTotalLiquidityInOcean(totalCreatorLiquidityInOcean)
const creatorPoolShare =
price?.ocean &&
@ -250,7 +252,7 @@ export default function Pool(): ReactElement {
<ExplorerLink
networkId={networkId}
path={
networkId === 137
networkId === 137 || networkId === 1287
? `tokens/${ddo.dataToken}`
: `token/${ddo.dataToken}`
}

View File

@ -1,7 +1,7 @@
.bookmark {
position: absolute;
top: -10px;
right: calc(var(--spacer) / 4);
right: calc(var(--spacer) / 8);
appearance: none;
background: none;
border: none;

View File

@ -4,12 +4,30 @@
font-size: var(--font-size-small);
}
.meta p {
margin-bottom: 0;
.asset {
margin-left: -2rem;
margin-right: -2rem;
padding-left: 2rem;
padding-right: 3rem;
border-bottom: 1px solid var(--border-color);
margin-bottom: calc(var(--spacer) / 1.5);
padding-bottom: calc(var(--spacer) / 1.75);
}
.published {
margin-top: calc(var(--spacer) / 2);
@media (min-width: 40rem) {
.asset {
margin-top: -0.65rem;
}
}
.assetType {
display: inline-block;
border-right: 1px solid var(--border-color);
padding-right: calc(var(--spacer) / 3.5);
margin-right: calc(var(--spacer) / 4);
}
.byline {
font-size: var(--font-size-small);
}

View File

@ -15,21 +15,25 @@ export default function MetaMain(): ReactElement {
return (
<aside className={styles.meta}>
<AssetType type={type} accessType={accessType} />
<p>
<header className={styles.asset}>
<AssetType
type={type}
accessType={accessType}
className={styles.assetType}
/>
<ExplorerLink
networkId={networkId}
path={
networkId === 137
networkId === 137 || networkId === 1287
? `tokens/${ddo?.dataToken}`
: `token/${ddo?.dataToken}`
}
>
{`${ddo?.dataTokenInfo.name}${ddo?.dataTokenInfo.symbol}`}
</ExplorerLink>
</p>
</header>
<div className={styles.published}>
<div className={styles.byline}>
Published By <Publisher account={owner} />
<p>
<Time date={ddo?.created} relative />

View File

@ -48,14 +48,16 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
const [showPricing, setShowPricing] = useState(false)
const [showEdit, setShowEdit] = useState<boolean>()
const [showEditCompute, setShowEditCompute] = useState<boolean>()
const { ddo, price, metadata } = useAsset()
const isOwner = accountId === owner
const [isOwner, setIsOwner] = useState(false)
const { ddo, price, metadata, type } = useAsset()
useEffect(() => {
if (!price) return
if (!accountId || !owner) return
const isOwner = accountId.toLowerCase() === owner.toLowerCase()
setIsOwner(isOwner)
setShowPricing(isOwner && price.type === '')
}, [isOwner, price])
}, [accountId, price, owner])
function handleEditButton() {
// move user's focus to top of screen
@ -101,7 +103,7 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
<Button style="text" size="small" onClick={handleEditButton}>
Edit Metadata
</Button>
{ddo.findServiceByType('compute') && (
{ddo.findServiceByType('compute') && type === 'dataset' && (
<>
<span className={styles.separator}>|</span>
<Button

View File

@ -2,3 +2,7 @@
composes: asset from './Details.module.css';
border-bottom-left-radius: var(--border-radius) !important;
}
.help {
margin-top: calc(var(--spacer) / 3);
}

View File

@ -6,12 +6,33 @@ import { ListItem } from '../../../atoms/Lists'
import Button from '../../../atoms/Button'
import { useOcean } from '../../../../providers/Ocean'
import styles from './Results.module.css'
import FormHelp from '../../../atoms/Input/Help'
import { graphql, useStaticQuery } from 'gatsby'
export const contentQuery = graphql`
query HistoryPageComputeResultsQuery {
content: allFile(filter: { relativePath: { eq: "pages/history.json" } }) {
edges {
node {
childPagesJson {
compute {
storage
}
}
}
}
}
}
`
export default function Results({
job
}: {
job: ComputeJobMetaData
}): ReactElement {
const data = useStaticQuery(contentQuery)
const content = data.content.edges[0].node.childPagesJson
const { ocean, account } = useOcean()
const [isLoading, setIsLoading] = useState(false)
const [hasFetched, setHasFetched] = useState(false)
@ -85,6 +106,7 @@ export default function Results({
)}
</Button>
)}
<FormHelp className={styles.help}>{content.compute.storage}</FormHelp>
</div>
)
}

View File

@ -62,12 +62,11 @@ export default function FormPublish(): ReactElement {
const [selectedDockerImage, setSelectedDockerImage] = useState<string>(
initialValues.dockerImage
)
// reset form validation on every mount
useEffect(() => {
setErrors({})
setTouched({})
// setSubmitting(false)
}, [setErrors, setTouched])
function handleImageSelectChange(imageSelected: string) {

View File

@ -67,8 +67,11 @@ export default function FormPublish(): ReactElement {
e: ChangeEvent<HTMLInputElement>,
field: FormFieldProps
) {
const value =
field.type === 'terms' ? !JSON.parse(e.target.value) : e.target.value
validateField(field.name)
setFieldValue(field.name, e.target.value)
setFieldValue(field.name, value)
}
const resetFormAndClearStorage = (e: FormEvent<Element>) => {

View File

@ -95,9 +95,22 @@ export default function PublishPage({
const [publishType, setPublishType] = useState<MetadataMain['type']>(
'dataset'
)
const hasFeedback = isLoading || error || success
const emptyAlgoDT = Object.values(algoInitialValues.dataTokenOptions).every(
(value) => value === ''
)
const emptyDatasetDT = Object.values(
datasetInitialValues.dataTokenOptions
).every((value) => value === '')
if (emptyAlgoDT) {
algoInitialValues.dataTokenOptions = datasetInitialValues.dataTokenOptions
} else {
if (emptyDatasetDT)
datasetInitialValues.dataTokenOptions = algoInitialValues.dataTokenOptions
}
useEffect(() => {
publishType === 'dataset'
? setTitle('Publishing Data Set')
@ -160,18 +173,22 @@ export default function PublishPage({
): Promise<void> {
const metadata = transformPublishAlgorithmFormToMetadata(values)
const timeout = mapTimeoutStringToSeconds(values.timeout)
const validDockerImage =
values.dockerImage === 'custom image'
? await validateDockerImage(values.image, values.containerTag)
: true
// TODO: put back check once #572 is resolved
// https://github.com/oceanprotocol/market/issues/572
const validDockerImage = true
// const validDockerImage =
// values.dockerImage === 'custom image'
// ? await validateDockerImage(values.image, values.containerTag)
// : true
try {
if (validDockerImage) {
Logger.log('Publish Algorithm with ', metadata)
Logger.log('Publish algorithm with ', metadata, values.dataTokenOptions)
const ddo = await publish(
(metadata as unknown) as Metadata,
values.algorithmPrivacy === true ? 'compute' : 'access',
undefined,
values.dataTokenOptions,
timeout
)
@ -248,7 +265,9 @@ export default function PublishPage({
loading={publishStepText}
setError={setError}
successAction={{
name: 'Go to data set →',
name: `Go to ${
publishType === 'dataset' ? 'data set' : 'algorithm'
} `,
to: `/asset/${did}`
}}
/>

View File

@ -1,160 +0,0 @@
import { useState } from 'react'
import { Logger, ServiceCompute } from '@oceanprotocol/lib'
import { MetadataAlgorithm } from '@oceanprotocol/lib/dist/node/ddo/interfaces/MetadataAlgorithm'
import {
ComputeJob,
ComputeAlgorithm
} from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
import { computeFeedback } from '../utils/feedback'
import { useOcean } from '../providers/Ocean'
import { useWeb3 } from '../providers/Web3'
interface ComputeValue {
entrypoint: string
image: string
tag: string
}
interface ComputeOption {
name: string
value: ComputeValue
}
const computeOptions: ComputeOption[] = [
{
name: 'nodejs',
value: {
entrypoint: 'node $ALGO',
image: 'node',
tag: '10'
}
},
{
name: 'python3.7',
value: {
entrypoint: 'python $ALGO',
image: 'oceanprotocol/algo_dockers',
tag: 'python-panda'
}
}
]
interface UseCompute {
compute: (
did: string,
computeService: ServiceCompute,
dataTokenAddress: string,
algorithmRawCode: string,
computeContainer: ComputeValue,
marketFeeAddress?: string,
orderId?: string
) => Promise<ComputeJob | void>
computeStep?: number
computeStepText?: string
computeError?: string
isLoading: boolean
}
const rawAlgorithmMeta: MetadataAlgorithm = {
rawcode: `console.log('Hello world'!)`,
format: 'docker-image',
version: '0.1',
container: {
entrypoint: '',
image: '',
tag: ''
}
}
function useCompute(): UseCompute {
const { accountId } = useWeb3()
const { ocean, account } = useOcean()
const [computeStep, setComputeStep] = useState<number | undefined>()
const [computeStepText, setComputeStepText] = useState<string | undefined>()
const [computeError, setComputeError] = useState<string | undefined>()
const [isLoading, setIsLoading] = useState(false)
function setStep(index?: number) {
if (!index) {
setComputeStep(undefined)
setComputeStepText(undefined)
return
}
setComputeStep(index)
setComputeStepText(computeFeedback[index])
}
async function compute(
did: string,
computeService: ServiceCompute,
dataTokenAddress: string,
algorithmRawCode: string,
computeContainer: ComputeValue,
marketFeeAddress?: string,
orderId?: string
): Promise<ComputeJob | void> {
if (!ocean || !account) return
setComputeError(undefined)
try {
setIsLoading(true)
setStep(0)
rawAlgorithmMeta.container = computeContainer
rawAlgorithmMeta.rawcode = algorithmRawCode
const computeAlgorithm: ComputeAlgorithm = {
meta: rawAlgorithmMeta
}
const output = {}
if (!orderId) {
const userOwnedTokens = await ocean.accounts.getTokenBalance(
dataTokenAddress,
account
)
if (parseFloat(userOwnedTokens) < 1) {
setComputeError('Not enough datatokens')
} else {
Logger.log(
'compute order',
accountId,
did,
computeService,
rawAlgorithmMeta,
marketFeeAddress
)
orderId = await ocean.compute.orderAsset(
accountId,
did,
computeService.index,
computeAlgorithm,
marketFeeAddress
)
setStep(1)
}
}
setStep(2)
if (orderId) {
const response = await ocean.compute.start(
did,
orderId,
dataTokenAddress,
account,
computeAlgorithm,
output,
`${computeService.index}`,
computeService.type
)
return response
}
} catch (error) {
Logger.error(error)
setComputeError(error.message)
} finally {
setStep(undefined)
setIsLoading(false)
}
}
return { compute, computeStep, computeStepText, computeError, isLoading }
}
export { useCompute, UseCompute, ComputeValue, ComputeOption, computeOptions }
export default UseCompute

View File

@ -77,6 +77,7 @@ export function useGraphSyncStatus(): UseGraphSyncStatus {
// Get and set subgraph block
useEffect(() => {
if (!config || !config.subgraphUri) return
async function initBlockSubgraph() {
setSubgraphLoading(true)
const blockGraph = await getBlockSubgraph(config.subgraphUri)
@ -98,7 +99,7 @@ export function useGraphSyncStatus(): UseGraphSyncStatus {
return
}
setIsGraphSynced(true)
}, [blockGraph, blockHead])
}, [blockGraph, blockHead, web3Loading, subgraphLoading])
return { blockHead, blockGraph, isGraphSynced }
}

View File

@ -82,36 +82,7 @@ function usePublish(): UsePublish {
}
case 'compute': {
if (!timeout) timeout = 3600
const cluster = ocean.compute.createClusterAttributes(
'Kubernetes',
'http://10.0.0.17/xxx'
)
const servers = [
ocean.compute.createServerAttributes(
'1',
'xlsize',
'50',
'16',
'0',
'128gb',
'160gb',
timeout
)
]
const containers = [
ocean.compute.createContainerAttributes(
'tensorflow/tensorflow',
'latest',
'sha256:cb57ecfa6ebbefd8ffc7f75c0f00e57a7fa739578a429b6f72a0df19315deadc'
)
]
const provider = ocean.compute.createProviderAttributes(
'Azure',
'Compute service with 16gb ram for each node.',
cluster,
containers,
servers
)
const provider = {}
const origComputePrivacy: ServiceComputePrivacy = {
allowRawAlgorithm: false,
allowNetworkAccess: false,

View File

@ -11,6 +11,12 @@ export const validationSchema: Yup.SchemaOf<MetadataPublishFormAlgorithm> = Yup.
description: Yup.string().min(10).required('Required'),
files: Yup.array<FileMetadata>().required('Required').nullable(),
timeout: Yup.string().required('Required'),
dataTokenOptions: Yup.object()
.shape({
name: Yup.string(),
symbol: Yup.string()
})
.required('Required'),
dockerImage: Yup.string()
.matches(/node:latest|python:latest|custom image/g, {
excludeEmptyString: true
@ -31,6 +37,10 @@ export const validationSchema: Yup.SchemaOf<MetadataPublishFormAlgorithm> = Yup.
export const initialValues: Partial<MetadataPublishFormAlgorithm> = {
name: '',
author: '',
dataTokenOptions: {
name: '',
symbol: ''
},
dockerImage: 'node:latest',
image: 'node',
containerTag: 'latest',

View File

@ -1,4 +1,4 @@
import { MetadataMarket, MetadataPublishFormDataset } from '../@types/MetaData'
import { MetadataMarket, MetadataEditForm } from '../@types/MetaData'
import { secondsToString } from '../utils/metadata'
import { EditableMetadataLinks } from '@oceanprotocol/lib'
import * as Yup from 'yup'
@ -8,17 +8,20 @@ export const validationSchema = Yup.object().shape({
.min(4, (param) => `Title must be at least ${param.min} characters`)
.required('Required'),
description: Yup.string().required('Required').min(10),
price: Yup.number().required('Required'),
links: Yup.array<EditableMetadataLinks[]>().nullable(),
timeout: Yup.string().required('Required')
})
export function getInitialValues(
metadata: MetadataMarket,
timeout: number
): Partial<MetadataPublishFormDataset> {
timeout: number,
price: number
): Partial<MetadataEditForm> {
return {
name: metadata.main.name,
description: metadata.additionalInformation.description,
price,
links: metadata.additionalInformation.links,
timeout: secondsToString(timeout)
}

View File

@ -20,7 +20,7 @@ export default function PageGatsbySearch(props: PageProps): ReactElement {
? `Published by ${accountTruncate(owner as string)}`
: `${
totalResults !== undefined
? searchValue
? searchValue && searchValue !== ' '
? totalResults === 0
? 'No results'
: totalResults +

View File

@ -16,7 +16,10 @@ export function getOceanConfig(
): ConfigHelperConfig {
return new ConfigHelper().getConfig(
network,
network === 'polygon' || network === 137
network === 'polygon' ||
network === 137 ||
network === 'moonbeamalpha' ||
network === 1287
? undefined
: process.env.GATSBY_INFURA_PROJECT_ID
) as ConfigHelperConfig

View File

@ -35,7 +35,7 @@ export async function fileinfo(
return
} else {
toast.dismiss() // Remove any existing error message
toast.success('Great! That dataset looks good 🐳', {
toast.success('Great! That file looks good. 🐳', {
position: 'bottom-right',
autoClose: 5000,
hideProgressBar: false,