mirror of
https://github.com/oceanprotocol/market.git
synced 2024-06-30 05:41:41 +02:00
Merge pull request #967 from oceanprotocol/feature/publish
Publish flow changes
This commit is contained in:
commit
7d4d526d21
24
.eslintrc
24
.eslintrc
|
@ -2,11 +2,20 @@
|
|||
"extends": ["eslint:recommended", "prettier"],
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": { "jsx": true }
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es2020": true,
|
||||
"jest": true
|
||||
},
|
||||
"env": { "browser": true, "node": true, "es2020": true, "jest": true },
|
||||
"settings": {
|
||||
"react": { "version": "detect" }
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
@ -30,7 +39,14 @@
|
|||
"react/jsx-no-bind": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-use-before-define": "error"
|
||||
"@typescript-eslint/no-use-before-define": "error",
|
||||
"prefer-destructuring": [
|
||||
"error",
|
||||
{
|
||||
"object": true,
|
||||
"array": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -128,7 +128,7 @@
|
|||
"label": "Access Type",
|
||||
"help": "Choose how you want your files to be accessible for the specified price.",
|
||||
"type": "boxSelection",
|
||||
"options": ["Download", "Compute"],
|
||||
"options": ["Access", "Compute"],
|
||||
"required": true,
|
||||
"disclaimer": "Please do not provide downloadable personal data without the consent of the data subjects.",
|
||||
"disclaimerValues": ["Download"]
|
||||
|
|
11
package-lock.json
generated
11
package-lock.json
generated
|
@ -35,7 +35,6 @@
|
|||
"gray-matter": "^4.0.3",
|
||||
"is-url-superb": "^6.1.0",
|
||||
"js-cookie": "^3.0.1",
|
||||
"js-sha256": "^0.9.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.omit": "^4.5.0",
|
||||
|
@ -14512,11 +14511,6 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/js-sha256": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
|
||||
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
|
||||
},
|
||||
"node_modules/js-sha3": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
|
||||
|
@ -33620,11 +33614,6 @@
|
|||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
|
||||
"integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw=="
|
||||
},
|
||||
"js-sha256": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
|
||||
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
|
||||
},
|
||||
"js-sha3": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
"gray-matter": "^4.0.3",
|
||||
"is-url-superb": "^6.1.0",
|
||||
"js-cookie": "^3.0.1",
|
||||
"js-sha256": "^0.9.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.omit": "^4.5.0",
|
||||
|
|
20
src/@hooks/contracts/useNftFactory.ts
Normal file
20
src/@hooks/contracts/useNftFactory.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import { NftFactory } from '@oceanprotocol/lib'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
import { getOceanConfig } from '@utils/ocean'
|
||||
|
||||
function useNftFactory(): NftFactory {
|
||||
const { web3, chainId } = useWeb3()
|
||||
const [nftFactory, setNftFactory] = useState<NftFactory>()
|
||||
|
||||
useEffect(() => {
|
||||
if (!web3 || !chainId) return
|
||||
const config = getOceanConfig(chainId)
|
||||
const factory = new NftFactory(config.erc721FactoryAddress, web3)
|
||||
setNftFactory(factory)
|
||||
}, [web3, chainId])
|
||||
|
||||
return nftFactory
|
||||
}
|
||||
|
||||
export default useNftFactory
|
|
@ -43,36 +43,36 @@ function useConsume(): UseConsume {
|
|||
|
||||
try {
|
||||
setStep(0)
|
||||
if (!orderId) {
|
||||
// if we don't have a previous valid order, get one
|
||||
// const userOwnedTokens = await ocean.accounts.getTokenBalance(
|
||||
// dataTokenAddress,
|
||||
// account
|
||||
// )
|
||||
// if (parseFloat(userOwnedTokens) < 1) {
|
||||
// setConsumeError('Not enough datatokens')
|
||||
// return 'Not enough datatokens'
|
||||
// } else {
|
||||
// setStep(1)
|
||||
// try {
|
||||
// orderId = await ocean.assets.order(
|
||||
// did as string,
|
||||
// serviceType,
|
||||
// accountId,
|
||||
// undefined,
|
||||
// marketFeeAddress,
|
||||
// undefined,
|
||||
// null,
|
||||
// false
|
||||
// )
|
||||
// LoggerInstance.log('ordercreated', orderId)
|
||||
// setStep(2)
|
||||
// } catch (error) {
|
||||
// setConsumeError(error.message)
|
||||
// return error.message
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// if (!orderId) {
|
||||
// if we don't have a previous valid order, get one
|
||||
// const userOwnedTokens = await ocean.accounts.getTokenBalance(
|
||||
// dataTokenAddress,
|
||||
// account
|
||||
// )
|
||||
// if (parseFloat(userOwnedTokens) < 1) {
|
||||
// setConsumeError('Not enough datatokens')
|
||||
// return 'Not enough datatokens'
|
||||
// } else {
|
||||
// setStep(1)
|
||||
// try {
|
||||
// orderId = await ocean.assets.order(
|
||||
// did as string,
|
||||
// serviceType,
|
||||
// accountId,
|
||||
// undefined,
|
||||
// marketFeeAddress,
|
||||
// undefined,
|
||||
// null,
|
||||
// false
|
||||
// )
|
||||
// LoggerInstance.log('ordercreated', orderId)
|
||||
// setStep(2)
|
||||
// } catch (error) {
|
||||
// setConsumeError(error.message)
|
||||
// return error.message
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
setStep(3)
|
||||
// if (orderId)
|
||||
// await ocean.assets.download(
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import { renderStaticWaves } from './oceanWaves'
|
||||
|
||||
export interface NftOptions {
|
||||
// https://docs.opensea.io/docs/metadata-standards
|
||||
export interface NftMetadata {
|
||||
name: string
|
||||
symbol: string
|
||||
description: string
|
||||
image: string
|
||||
image?: string
|
||||
/* eslint-disable camelcase */
|
||||
external_url?: string
|
||||
image_data?: string
|
||||
background_color?: string
|
||||
/* eslint-enable camelcase */
|
||||
}
|
||||
|
||||
function encodeSvg(svgString: string): string {
|
||||
|
@ -25,20 +31,23 @@ function encodeSvg(svgString: string): string {
|
|||
.replace(/\s+/g, ' ')
|
||||
}
|
||||
|
||||
export function generateNftOptions(): NftOptions {
|
||||
export function generateNftMetadata(): NftMetadata {
|
||||
// TODO: crop image properly in the end as generated SVG waves are a super-wide image,
|
||||
// and add a filled background deciding on either black or white.
|
||||
const image = renderStaticWaves()
|
||||
// const image = new XMLSerializer().serializeToString(waves)
|
||||
// const image = `<svg><path d="M0 10.4304L16.3396 10.4304L8.88727 17.6833L10.2401 19L20 9.5L10.2401 0L8.88727 1.31491L16.3396 8.56959L0 8.56959V10.4304Z" /></svg>`
|
||||
|
||||
const newNft: NftOptions = {
|
||||
const newNft: NftMetadata = {
|
||||
name: 'Ocean Asset v4 NFT',
|
||||
symbol: 'OCEAN-V4-NFT',
|
||||
description: `This NFT represents an asset in the Ocean Protocol v4 ecosystem.`,
|
||||
// TODO: ideally this includes the final DID
|
||||
external_url: 'https://market.oceanprotocol.com',
|
||||
background_color: '141414', // dark background
|
||||
// TODO: figure out if also image URI needs base64 encoding
|
||||
// generated SVG embedded as 'data:image/svg+xml' and encoded characters
|
||||
image: `data:image/svg+xml,${encodeSvg(image)}`
|
||||
image_data: `data:image/svg+xml,${encodeSvg(image)}`
|
||||
// generated SVG embedded as 'data:image/svg+xml;base64'
|
||||
// image: `data:image/svg+xml;base64,${window?.btoa(image)}`
|
||||
// image: `data:image/svg+xml;base64,${Buffer.from(image).toString('base64')}`
|
||||
|
@ -47,15 +56,15 @@ export function generateNftOptions(): NftOptions {
|
|||
return newNft
|
||||
}
|
||||
|
||||
export function generateNftCreateData(nftOptions: NftOptions): any {
|
||||
export function generateNftCreateData(nftMetadata: NftMetadata): any {
|
||||
const nftCreateData = {
|
||||
name: nftOptions.name,
|
||||
symbol: nftOptions.symbol,
|
||||
name: nftMetadata.name,
|
||||
symbol: nftMetadata.symbol,
|
||||
templateIndex: 1,
|
||||
// TODO: figure out if Buffer.from method is working in browser in final build
|
||||
// as BTOA is deprecated.
|
||||
tokenURI: window?.btoa(JSON.stringify(nftOptions))
|
||||
// tokenURI: Buffer.from(JSON.stringify(nftOptions)).toString('base64') // should end up as data:application/json;base64
|
||||
tokenURI: window?.btoa(JSON.stringify(nftMetadata))
|
||||
// tokenURI: Buffer.from(JSON.stringify(nftMetadata)).toString('base64') // should end up as data:application/json;base64
|
||||
}
|
||||
|
||||
return nftCreateData
|
||||
|
|
|
@ -16,6 +16,10 @@ export function getOceanConfig(network: string | number): Config {
|
|||
? undefined
|
||||
: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID
|
||||
) as Config
|
||||
|
||||
// TODO: remove hack once address is fixed
|
||||
if (network === 'rinkeby' || network === 4)
|
||||
config.oceanTokenAddress = '0x8967bcf84170c91b0d24d4302c2376283b0b3a07'
|
||||
return config as Config
|
||||
}
|
||||
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
import axios, { CancelToken, AxiosResponse } from 'axios'
|
||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||
import axios, { CancelToken, AxiosResponse, Method } from 'axios'
|
||||
import {
|
||||
FileMetadata,
|
||||
LoggerInstance,
|
||||
ProviderInstance
|
||||
} from '@oceanprotocol/lib'
|
||||
|
||||
export interface FileMetadata {
|
||||
index: number
|
||||
valid: boolean
|
||||
contentType: string
|
||||
contentLength: string
|
||||
}
|
||||
|
||||
export async function getEncryptedFileUrls(
|
||||
files: string[],
|
||||
providerUrl: string,
|
||||
did: string,
|
||||
accountId: string
|
||||
export async function getEncryptedFiles(
|
||||
files: FileMetadata[],
|
||||
providerUrl: string
|
||||
): Promise<string> {
|
||||
try {
|
||||
// https://github.com/oceanprotocol/provider/blob/v4main/API.md#encrypt-endpoint
|
||||
const url = `${providerUrl}/api/v1/services/encrypt`
|
||||
const response: AxiosResponse<{ encryptedDocument: string }> =
|
||||
await axios.post(url, {
|
||||
documentId: did,
|
||||
signature: '', // TODO: add signature
|
||||
publisherAddress: accountId,
|
||||
document: files
|
||||
})
|
||||
return response?.data?.encryptedDocument
|
||||
console.log('start encr')
|
||||
const response = await ProviderInstance.encrypt(
|
||||
files,
|
||||
providerUrl,
|
||||
(httpMethod: Method, url: string, body: string, headers: any) => {
|
||||
return axios(url, {
|
||||
method: httpMethod,
|
||||
data: body,
|
||||
headers: headers
|
||||
})
|
||||
}
|
||||
)
|
||||
console.log('encr eres', response)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Error parsing json: ' + error.message)
|
||||
}
|
||||
|
@ -36,9 +36,12 @@ export async function getFileInfo(
|
|||
cancelToken: CancelToken
|
||||
): Promise<FileMetadata[]> {
|
||||
try {
|
||||
const postBody = { url }
|
||||
// TODO: what was the point of this?
|
||||
// if (url instanceof DID) postBody = { did: url.getDid() }
|
||||
// else postBody = { url }
|
||||
const postBody = { url, type: 'url' }
|
||||
const response: AxiosResponse<FileMetadata[]> = await axios.post(
|
||||
`${providerUrl}/api/v1/services/fileinfo`,
|
||||
`${providerUrl}/api/services/fileinfo`,
|
||||
postBody,
|
||||
{ cancelToken }
|
||||
)
|
||||
|
|
|
@ -4,7 +4,7 @@ import classNames from 'classnames/bind'
|
|||
import cleanupContentType from '@utils/cleanupContentType'
|
||||
import styles from './index.module.css'
|
||||
import Loader from '@shared/atoms/Loader'
|
||||
import { FileMetadata } from '@utils/provider'
|
||||
import { FileMetadata } from '@oceanprotocol/lib'
|
||||
|
||||
const cx = classNames.bind(styles)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ const cx = classNames.bind(styles)
|
|||
|
||||
export interface BoxSelectionOption {
|
||||
name: string
|
||||
value?: string
|
||||
checked: boolean
|
||||
title: JSX.Element | string
|
||||
icon?: JSX.Element
|
||||
|
@ -50,7 +51,7 @@ export default function BoxSelection({
|
|||
onChange={(event) => handleChange(event)}
|
||||
{...props}
|
||||
disabled={disabled}
|
||||
value={option.name}
|
||||
value={option.value ? option.value : option.name}
|
||||
name={name}
|
||||
/>
|
||||
<label
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
|
|||
import { prettySize } from './utils'
|
||||
import cleanupContentType from '@utils/cleanupContentType'
|
||||
import styles from './Info.module.css'
|
||||
import { FileMetadata } from '@utils/provider'
|
||||
import { FileMetadata } from '@oceanprotocol/lib'
|
||||
|
||||
export default function FileInfo({
|
||||
file,
|
||||
|
@ -13,7 +13,7 @@ export default function FileInfo({
|
|||
}): ReactElement {
|
||||
return (
|
||||
<div className={styles.info}>
|
||||
<h3 className={styles.url}>{(file as any).url}</h3>
|
||||
<h3 className={styles.url}>{file.url}</h3>
|
||||
<ul>
|
||||
<li className={styles.success}>✓ URL confirmed</li>
|
||||
{file.contentLength && <li>{prettySize(+file.contentLength)}</li>}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Button from '@shared/atoms/Button'
|
||||
import { InputProps } from '@shared/FormInput'
|
||||
import { generateNftOptions } from '@utils/nft'
|
||||
import { generateNftMetadata } from '@utils/nft'
|
||||
import { useField } from 'formik'
|
||||
import React, { ReactElement, useEffect } from 'react'
|
||||
import Refresh from '@images/refresh.svg'
|
||||
|
@ -13,14 +13,14 @@ export default function Nft(props: InputProps): ReactElement {
|
|||
useEffect(() => {
|
||||
if (field.value?.name !== '') return
|
||||
|
||||
const nftOptions = generateNftOptions()
|
||||
const nftOptions = generateNftMetadata()
|
||||
helpers.setValue({ ...nftOptions })
|
||||
}, [field.value?.name])
|
||||
|
||||
return (
|
||||
<div className={styles.nft}>
|
||||
<figure className={styles.image}>
|
||||
<img src={field?.value?.image} width="128" height="128" />
|
||||
<img src={field?.value?.image_data} width="128" height="128" />
|
||||
<Button
|
||||
style="text"
|
||||
size="small"
|
||||
|
@ -28,8 +28,8 @@ export default function Nft(props: InputProps): ReactElement {
|
|||
title="Generate new image"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
const nftOptions = generateNftOptions()
|
||||
helpers.setValue({ ...nftOptions })
|
||||
const nftMetadata = generateNftMetadata()
|
||||
helpers.setValue({ ...nftMetadata })
|
||||
}}
|
||||
>
|
||||
<Refresh />
|
||||
|
|
|
@ -5,7 +5,8 @@ import {
|
|||
ComputeOutput,
|
||||
Asset,
|
||||
DDO,
|
||||
PublisherTrustedAlgorithm
|
||||
PublisherTrustedAlgorithm,
|
||||
FileMetadata
|
||||
} from '@oceanprotocol/lib'
|
||||
import { toast } from 'react-toastify'
|
||||
import Price from '@shared/Price'
|
||||
|
@ -14,7 +15,6 @@ import Alert from '@shared/atoms/Alert'
|
|||
import { useSiteMetadata } from '@hooks/useSiteMetadata'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
import { usePricing } from '@hooks/usePricing'
|
||||
import { useAsset } from '@context/Asset'
|
||||
import {
|
||||
generateBaseQuery,
|
||||
getFilterTerm,
|
||||
|
@ -36,7 +36,6 @@ import ComputeJobs from '../../../Profile/History/ComputeJobs'
|
|||
import { useCancelToken } from '@hooks/useCancelToken'
|
||||
import { useIsMounted } from '@hooks/useIsMounted'
|
||||
import { SortTermOptions } from '../../../../@types/aquarius/SearchQuery'
|
||||
import { FileMetadata } from '@utils/provider'
|
||||
|
||||
export default function Compute({
|
||||
ddo,
|
||||
|
|
|
@ -16,8 +16,7 @@ import { secondsToString } from '@utils/ddo'
|
|||
import AlgorithmDatasetsListForCompute from './Compute/AlgorithmDatasetsListForCompute'
|
||||
import styles from './Consume.module.css'
|
||||
import { useIsMounted } from '@hooks/useIsMounted'
|
||||
import { FileMetadata } from '@utils/provider'
|
||||
import { Asset } from '@oceanprotocol/lib'
|
||||
import { Asset, FileMetadata } from '@oceanprotocol/lib'
|
||||
|
||||
const previousOrderQuery = gql`
|
||||
query PreviousOrder($id: String!, $account: String!) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { ReactElement, useState, useEffect } from 'react'
|
||||
import Compute from './Compute'
|
||||
import Consume from './Consume'
|
||||
import { Asset, LoggerInstance } from '@oceanprotocol/lib'
|
||||
import { Asset, FileMetadata, LoggerInstance } from '@oceanprotocol/lib'
|
||||
import Tabs, { TabsItem } from '@shared/atoms/Tabs'
|
||||
import { compareAsBN } from '@utils/numbers'
|
||||
import Pool from './Pool'
|
||||
|
@ -9,7 +9,7 @@ import Trade from './Trade'
|
|||
import { useAsset } from '@context/Asset'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
import Web3Feedback from '@shared/Web3Feedback'
|
||||
import { FileMetadata, getFileInfo } from '@utils/provider'
|
||||
import { getFileInfo } from '@utils/provider'
|
||||
import { getOceanConfig } from '@utils/ocean'
|
||||
import { useCancelToken } from '@hooks/useCancelToken'
|
||||
import { useIsMounted } from '@hooks/useIsMounted'
|
||||
|
|
|
@ -39,7 +39,7 @@ export default function Actions({
|
|||
<Button
|
||||
type="submit"
|
||||
style="primary"
|
||||
disabled={values.user.accountId === '' || !isValid}
|
||||
disabled={values.user.accountId === ''}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
|
|
|
@ -90,5 +90,6 @@
|
|||
|
||||
.success.current:before {
|
||||
background: var(--font-color-heading);
|
||||
color: var(--brand-alert-green);
|
||||
border-color: var(--brand-alert-green);
|
||||
}
|
||||
|
|
|
@ -19,10 +19,12 @@ export default function ServicesFields(): ReactElement {
|
|||
const { values, setFieldValue, touched, setTouched } =
|
||||
useFormikContext<FormPublishData>()
|
||||
|
||||
// name and title should be download, but option value should be access, probably the best way would be to change the component so that option is an object like {name,value}
|
||||
const accessTypeOptions = [
|
||||
{
|
||||
name: accessTypeOptionsTitles[0].toLowerCase(),
|
||||
title: accessTypeOptionsTitles[0],
|
||||
name: 'download',
|
||||
value: accessTypeOptionsTitles[0].toLowerCase(),
|
||||
title: 'Download',
|
||||
icon: <IconDownload />,
|
||||
// BoxSelection component is not a Formik component
|
||||
// so we need to handle checked state manually.
|
||||
|
@ -31,6 +33,7 @@ export default function ServicesFields(): ReactElement {
|
|||
},
|
||||
{
|
||||
name: accessTypeOptionsTitles[1].toLowerCase(),
|
||||
value: accessTypeOptionsTitles[1].toLowerCase(),
|
||||
title: accessTypeOptionsTitles[1],
|
||||
icon: <IconCompute />,
|
||||
checked:
|
||||
|
@ -55,7 +58,7 @@ export default function ServicesFields(): ReactElement {
|
|||
|
||||
setFieldValue(
|
||||
'services[0].access',
|
||||
values.services[0].algorithmPrivacy === true ? 'compute' : 'download'
|
||||
values.services[0].algorithmPrivacy === true ? 'compute' : 'access'
|
||||
)
|
||||
}, [values.services[0].algorithmPrivacy, setFieldValue])
|
||||
|
||||
|
|
|
@ -2,12 +2,17 @@ import { ReactElement, useEffect } from 'react'
|
|||
import { useFormikContext } from 'formik'
|
||||
import { wizardSteps } from './_constants'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
import { FormPublishData } from './_types'
|
||||
import { FormPublishData, PublishFeedback } from './_types'
|
||||
|
||||
export function Steps(): ReactElement {
|
||||
export function Steps({
|
||||
feedback
|
||||
}: {
|
||||
feedback: PublishFeedback
|
||||
}): ReactElement {
|
||||
const { chainId, accountId } = useWeb3()
|
||||
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
||||
|
||||
// auto-sync user chainId & account into form data values
|
||||
useEffect(() => {
|
||||
if (!chainId || !accountId) return
|
||||
|
||||
|
@ -15,6 +20,11 @@ export function Steps(): ReactElement {
|
|||
setFieldValue('user.accountId', accountId)
|
||||
}, [chainId, accountId, setFieldValue])
|
||||
|
||||
// auto-sync publish feedback into form data values
|
||||
useEffect(() => {
|
||||
setFieldValue('feedback', feedback)
|
||||
}, [feedback, setFieldValue])
|
||||
|
||||
const { component } = wizardSteps.filter(
|
||||
(stepContent) => stepContent.step === values.user.stepCurrent
|
||||
)[0]
|
||||
|
|
18
src/components/Publish/Submission/Feedback.tsx
Normal file
18
src/components/Publish/Submission/Feedback.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { ListItem } from '@shared/atoms/Lists'
|
||||
import { useFormikContext } from 'formik'
|
||||
import React, { ReactElement } from 'react'
|
||||
import { FormPublishData } from '../_types'
|
||||
|
||||
export function Feedback(): ReactElement {
|
||||
const { values } = useFormikContext<FormPublishData>()
|
||||
|
||||
const items = Object.entries(values.feedback).map(([key, value], index) => {
|
||||
return (
|
||||
<ListItem ol key={index}>
|
||||
{value.name}
|
||||
</ListItem>
|
||||
)
|
||||
})
|
||||
|
||||
return <ol>{items}</ol>
|
||||
}
|
|
@ -2,6 +2,7 @@ import React, { ReactElement } from 'react'
|
|||
import styles from './index.module.css'
|
||||
import { FormPublishData } from '../_types'
|
||||
import { useFormikContext } from 'formik'
|
||||
import { Feedback } from './Feedback'
|
||||
|
||||
export default function Submission(): ReactElement {
|
||||
const { values, handleSubmit } = useFormikContext<FormPublishData>()
|
||||
|
@ -12,6 +13,7 @@ export default function Submission(): ReactElement {
|
|||
Place to teach about what happens next, output all the steps in background
|
||||
in some list, after submission continously update this list with the
|
||||
status of the submission.
|
||||
<Feedback />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ export const initialValues: FormPublishData = {
|
|||
accountId: ''
|
||||
},
|
||||
metadata: {
|
||||
nft: { name: '', symbol: '', description: '', image: '' },
|
||||
nft: { name: '', symbol: '', description: '', image_data: '' },
|
||||
type: 'dataset',
|
||||
name: '',
|
||||
author: '',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ServiceComputeOptions } from '@oceanprotocol/lib'
|
||||
import { DataTokenOptions } from '@utils/datatokens'
|
||||
import { NftOptions } from '@utils/nft'
|
||||
import { NftMetadata } from '@utils/nft'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
interface FileMetadata {
|
||||
|
@ -28,7 +28,7 @@ export interface FormPublishData {
|
|||
chainId: number
|
||||
}
|
||||
metadata: {
|
||||
nft: NftOptions
|
||||
nft: NftMetadata
|
||||
type: 'dataset' | 'algorithm'
|
||||
name: string
|
||||
description: string
|
||||
|
@ -43,6 +43,7 @@ export interface FormPublishData {
|
|||
}
|
||||
services: FormPublishService[]
|
||||
pricing: PriceOptions
|
||||
feedback?: PublishFeedback
|
||||
}
|
||||
|
||||
export interface StepContent {
|
||||
|
@ -50,3 +51,11 @@ export interface StepContent {
|
|||
title: string
|
||||
component: ReactElement
|
||||
}
|
||||
|
||||
export interface PublishFeedback {
|
||||
[key: number]: {
|
||||
name: string
|
||||
status: 'success' | 'error' | 'pending'
|
||||
message?: string
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
import { DDO, Metadata, Service } from '@oceanprotocol/lib'
|
||||
import {
|
||||
Config,
|
||||
DDO,
|
||||
Erc20CreateParams,
|
||||
FreCreationParams,
|
||||
generateDid,
|
||||
getHash,
|
||||
LoggerInstance,
|
||||
Metadata,
|
||||
NftCreateData,
|
||||
NftFactory,
|
||||
Pool,
|
||||
PoolCreationParams,
|
||||
Service,
|
||||
ZERO_ADDRESS
|
||||
} from '@oceanprotocol/lib'
|
||||
import { mapTimeoutStringToSeconds } from '@utils/ddo'
|
||||
import { getEncryptedFileUrls } from '@utils/provider'
|
||||
import { sha256 } from 'js-sha256'
|
||||
import { getEncryptedFiles } from '@utils/provider'
|
||||
import slugify from 'slugify'
|
||||
import Web3 from 'web3'
|
||||
import {
|
||||
algorithmContainerPresets,
|
||||
MetadataAlgorithmContainer
|
||||
|
@ -66,9 +81,10 @@ export async function transformPublishFormToDdo(
|
|||
} = metadata
|
||||
const { access, files, links, providerUrl, timeout } = services[0]
|
||||
|
||||
const did = nftAddress ? `0x${sha256(`${nftAddress}${chainId}`)}` : '0x...'
|
||||
const did = nftAddress ? generateDid(nftAddress, chainId) : '0x...'
|
||||
const currentTime = dateToStringNoMS(new Date())
|
||||
|
||||
const isPreview = !datatokenAddress && !nftAddress
|
||||
console.log('did', did, isPreview)
|
||||
// Transform from files[0].url to string[] assuming only 1 file
|
||||
const filesTransformed = files?.length && files[0].valid && [files[0].url]
|
||||
const linksTransformed = links?.length && links[0].valid && [links[0].url]
|
||||
|
@ -114,21 +130,24 @@ export async function transformPublishFormToDdo(
|
|||
}
|
||||
})
|
||||
}
|
||||
console.log('new meta', newMetadata)
|
||||
|
||||
// Encrypt just created string[] of urls
|
||||
// this is the default format hardcoded
|
||||
const file = [
|
||||
{
|
||||
type: 'url',
|
||||
url: files[0].url,
|
||||
method: 'GET'
|
||||
}
|
||||
]
|
||||
const filesEncrypted =
|
||||
!isPreview &&
|
||||
files?.length &&
|
||||
files[0].valid &&
|
||||
(await getEncryptedFileUrls(
|
||||
filesTransformed,
|
||||
providerUrl.url,
|
||||
did,
|
||||
accountId
|
||||
))
|
||||
(await getEncryptedFiles(file, providerUrl.url))
|
||||
|
||||
const newService: Service = {
|
||||
// TODO: give some id
|
||||
id: '1',
|
||||
id: getHash(datatokenAddress + filesEncrypted),
|
||||
type: access,
|
||||
files: filesEncrypted || '',
|
||||
datatokenAddress,
|
||||
|
@ -151,10 +170,12 @@ export async function transformPublishFormToDdo(
|
|||
// again, we can assume if `datatokenAddress` is not passed,
|
||||
// we are on preview.
|
||||
...(!datatokenAddress && {
|
||||
dataTokenInfo: {
|
||||
name: values.services[0].dataTokenOptions.name,
|
||||
symbol: values.services[0].dataTokenOptions.symbol
|
||||
},
|
||||
datatokens: [
|
||||
{
|
||||
name: values.services[0].dataTokenOptions.name,
|
||||
symbol: values.services[0].dataTokenOptions.symbol
|
||||
}
|
||||
],
|
||||
nft: {
|
||||
owner: accountId
|
||||
}
|
||||
|
@ -163,3 +184,132 @@ export async function transformPublishFormToDdo(
|
|||
|
||||
return newDdo
|
||||
}
|
||||
|
||||
export async function createTokensAndPricing(
|
||||
values: FormPublishData,
|
||||
accountId: string,
|
||||
marketFeeAddress: string,
|
||||
config: Config,
|
||||
nftFactory: NftFactory,
|
||||
web3: Web3
|
||||
) {
|
||||
// image not included here for gas fees reasons. It is also an issue to reaserch how we add the image in the nft
|
||||
const nftCreateData: NftCreateData = {
|
||||
name: values.metadata.nft.name,
|
||||
symbol: values.metadata.nft.symbol,
|
||||
// tokenURI: values.metadata.nft.image_data,
|
||||
tokenURI: '',
|
||||
templateIndex: 1
|
||||
}
|
||||
|
||||
// TODO: cap is hardcoded for now to 1000, this needs to be discussed at some point
|
||||
// fee is default 0 for now
|
||||
// TODO: templateIndex is hardcoded for now but this is incorrect, in the future it should be something like 1 for pools, and 2 for fre and free
|
||||
const ercParams: Erc20CreateParams = {
|
||||
templateIndex: values.pricing.type === 'dynamic' ? 1 : 2,
|
||||
minter: accountId,
|
||||
feeManager: accountId,
|
||||
mpFeeAddress: marketFeeAddress,
|
||||
feeToken: config.oceanTokenAddress,
|
||||
feeAmount: `0`,
|
||||
cap: '1000',
|
||||
name: values.services[0].dataTokenOptions.name,
|
||||
symbol: values.services[0].dataTokenOptions.symbol
|
||||
}
|
||||
|
||||
let erc721Address = ''
|
||||
let datatokenAddress = ''
|
||||
|
||||
// TODO: cleaner code for this huge switch !??!?
|
||||
switch (values.pricing.type) {
|
||||
case 'dynamic': {
|
||||
// no vesting in market by default, maybe at a later time , vestingAmount and vestedBlocks are hardcoded
|
||||
// we use only ocean as basetoken
|
||||
// TODO: discuss swapFeeLiquidityProvider, swapFeeMarketPlaceRunner
|
||||
const poolParams: PoolCreationParams = {
|
||||
ssContract: config.sideStakingAddress,
|
||||
basetokenAddress: config.oceanTokenAddress,
|
||||
basetokenSender: config.erc721FactoryAddress,
|
||||
publisherAddress: accountId,
|
||||
marketFeeCollector: marketFeeAddress,
|
||||
poolTemplateAddress: config.poolTemplateAddress,
|
||||
rate: values.pricing.price.toString(),
|
||||
basetokenDecimals: 18,
|
||||
vestingAmount: '0',
|
||||
vestedBlocks: 2726000,
|
||||
initialBasetokenLiquidity: values.pricing.amountOcean.toString(),
|
||||
swapFeeLiquidityProvider: 1e15,
|
||||
swapFeeMarketRunner: 1e15
|
||||
}
|
||||
// the spender in this case is the erc721Factory because we are delegating
|
||||
const pool = new Pool(web3, LoggerInstance)
|
||||
const txApp = await pool.approve(
|
||||
accountId,
|
||||
config.oceanTokenAddress,
|
||||
config.erc721FactoryAddress,
|
||||
'200',
|
||||
false
|
||||
)
|
||||
console.log('aprove', txApp)
|
||||
const result = await nftFactory.createNftErcWithPool(
|
||||
accountId,
|
||||
nftCreateData,
|
||||
ercParams,
|
||||
poolParams
|
||||
)
|
||||
|
||||
erc721Address = result.events.NFTCreated.returnValues[0]
|
||||
datatokenAddress = result.events.TokenCreated.returnValues[0]
|
||||
break
|
||||
}
|
||||
case 'fixed': {
|
||||
const freParams: FreCreationParams = {
|
||||
fixedRateAddress: config.fixedRateExchangeAddress,
|
||||
baseTokenAddress: config.oceanTokenAddress,
|
||||
owner: accountId,
|
||||
marketFeeCollector: marketFeeAddress,
|
||||
baseTokenDecimals: 18,
|
||||
dataTokenDecimals: 18,
|
||||
fixedRate: values.pricing.price.toString(),
|
||||
marketFee: 1e15,
|
||||
withMint: true
|
||||
}
|
||||
|
||||
const result = await nftFactory.createNftErcWithFixedRate(
|
||||
accountId,
|
||||
nftCreateData,
|
||||
ercParams,
|
||||
freParams
|
||||
)
|
||||
|
||||
erc721Address = result.events.NFTCreated.returnValues[0]
|
||||
datatokenAddress = result.events.TokenCreated.returnValues[0]
|
||||
|
||||
break
|
||||
}
|
||||
case 'free': {
|
||||
// maxTokens - how many tokens cand be dispensed when someone requests . If maxTokens=2 then someone can't request 3 in one tx
|
||||
// maxBalance - how many dt the user has in it's wallet before the dispenser will not dispense dt
|
||||
// both will be just 1 for the market
|
||||
const dispenserParams = {
|
||||
dispenserAddress: config.dispenserAddress,
|
||||
maxTokens: web3.utils.toWei('1'),
|
||||
maxBalance: web3.utils.toWei('1'),
|
||||
withMint: true,
|
||||
allowedSwapper: ZERO_ADDRESS
|
||||
}
|
||||
const result = await nftFactory.createNftErcWithDispenser(
|
||||
accountId,
|
||||
nftCreateData,
|
||||
ercParams,
|
||||
dispenserParams
|
||||
)
|
||||
erc721Address = result.events.NFTCreated.returnValues[0]
|
||||
datatokenAddress = result.events.TokenCreated.returnValues[0]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return { erc721Address, datatokenAddress }
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ const validationService = {
|
|||
}),
|
||||
timeout: Yup.string().required('Required'),
|
||||
access: Yup.string()
|
||||
.matches(/compute|download/g)
|
||||
.matches(/compute|access/g)
|
||||
.required('Required'),
|
||||
providerUrl: Yup.object().shape({
|
||||
url: Yup.string().url('Must be a valid URL.').required('Required'),
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import React, { ReactElement, useState, useRef } from 'react'
|
||||
import { Form, Formik, validateYupSchema } from 'formik'
|
||||
import { Form, Formik } from 'formik'
|
||||
import { initialValues } from './_constants'
|
||||
import { validationSchema } from './_validation'
|
||||
import { useAccountPurgatory } from '@hooks/useAccountPurgatory'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
import { transformPublishFormToDdo } from './_utils'
|
||||
import { createTokensAndPricing, transformPublishFormToDdo } from './_utils'
|
||||
import PageHeader from '@shared/Page/PageHeader'
|
||||
import Title from './Title'
|
||||
import styles from './index.module.css'
|
||||
|
@ -12,10 +11,15 @@ import Actions from './Actions'
|
|||
import Debug from './Debug'
|
||||
import Navigation from './Navigation'
|
||||
import { Steps } from './Steps'
|
||||
import { FormPublishData } from './_types'
|
||||
import { sha256 } from 'js-sha256'
|
||||
import { generateNftCreateData } from '@utils/nft'
|
||||
import { FormPublishData, PublishFeedback } from './_types'
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
import useNftFactory from '@hooks/contracts/useNftFactory'
|
||||
import { Nft, getHash, ProviderInstance } from '@oceanprotocol/lib'
|
||||
import { useSiteMetadata } from '@hooks/useSiteMetadata'
|
||||
import axios, { Method } from 'axios'
|
||||
import { useCancelToken } from '@hooks/useCancelToken'
|
||||
import { getOceanConfig } from '@utils/ocean'
|
||||
import { validationSchema } from './_validation'
|
||||
|
||||
// TODO: restore FormikPersist, add back clear form action
|
||||
const formName = 'ocean-publish-form'
|
||||
|
@ -26,58 +30,211 @@ export default function PublishPage({
|
|||
content: { title: string; description: string; warning: string }
|
||||
}): ReactElement {
|
||||
const { debug } = useUserPreferences()
|
||||
const { accountId, chainId } = useWeb3()
|
||||
const { accountId, web3, chainId } = useWeb3()
|
||||
const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId)
|
||||
|
||||
// TODO: success & error states need to be handled for each step we want to display
|
||||
// most likely with a nested data structure.
|
||||
const [success, setSuccess] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
const scrollToRef = useRef()
|
||||
const { appConfig } = useSiteMetadata()
|
||||
const nftFactory = useNftFactory()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
const [feedback, setFeedback] = useState<PublishFeedback>({
|
||||
1: {
|
||||
name: 'Create Tokens & Pricing',
|
||||
status: 'pending'
|
||||
},
|
||||
2: {
|
||||
name: 'Encrypt DDO',
|
||||
status: 'pending'
|
||||
},
|
||||
3: {
|
||||
name: 'Publish DDO',
|
||||
status: 'pending'
|
||||
}
|
||||
})
|
||||
|
||||
async function handleSubmit(values: FormPublishData) {
|
||||
let _erc721Address, _datatokenAddress, _ddo, _encryptedDdo
|
||||
|
||||
// --------------------------------------------------
|
||||
// 1. Create NFT & datatokens & create pricing schema
|
||||
// --------------------------------------------------
|
||||
try {
|
||||
// --------------------------------------------------
|
||||
// 1. Mint NFT & datatokens & put in pool
|
||||
// --------------------------------------------------
|
||||
// const nftOptions = values.metadata.nft
|
||||
// const nftCreateData = generateNftCreateData(nftOptions)
|
||||
const config = getOceanConfig(chainId)
|
||||
console.log('config', config)
|
||||
|
||||
// TODO: figure out syntax of ercParams we most likely need to pass
|
||||
// to createNftWithErc() as we need to pass options for the datatoken.
|
||||
// const ercParams = {}
|
||||
// const priceOptions = {
|
||||
// // swapFee is tricky: to get 0.1% you need to send 0.001 as value
|
||||
// swapFee: `${values.pricing.swapFee / 100}`
|
||||
// }
|
||||
// const txMint = await createNftWithErc(accountId, nftCreateData)
|
||||
const { erc721Address, datatokenAddress } = await createTokensAndPricing(
|
||||
values,
|
||||
accountId,
|
||||
appConfig.marketFeeAddress,
|
||||
config,
|
||||
nftFactory,
|
||||
web3
|
||||
)
|
||||
|
||||
// TODO: figure out how to get nftAddress & datatokenAddress from tx log.
|
||||
// const { nftAddress, datatokenAddress } = txMint.logs[0].args
|
||||
// if (!nftAddress || !datatokenAddress) { throw new Error() }
|
||||
//
|
||||
// --------------------------------------------------
|
||||
// 2. Construct and publish DDO
|
||||
// --------------------------------------------------
|
||||
// const did = sha256(`${nftAddress}${chainId}`)
|
||||
// const ddo = transformPublishFormToDdo(values, datatokenAddress, nftAddress)
|
||||
// const txPublish = await publish(ddo)
|
||||
//
|
||||
// --------------------------------------------------
|
||||
// 3. Integrity check of DDO before & after publishing
|
||||
// --------------------------------------------------
|
||||
// const checksumBefore = sha256(ddo)
|
||||
// const ddoFromChain = await getDdoFromChain(ddo.id)
|
||||
// const ddoFromChainDecrypted = await decryptDdo(ddoFromChain)
|
||||
// const checksumAfter = sha256(ddoFromChainDecrypted)
|
||||
const isSuccess = erc721Address && datatokenAddress
|
||||
_erc721Address = erc721Address
|
||||
_datatokenAddress = datatokenAddress
|
||||
|
||||
// if (checksumBefore !== checksumAfter) {
|
||||
// throw new Error('DDO integrity check failed!')
|
||||
// }
|
||||
setSuccess('Your DDO was published successfully!')
|
||||
setFeedback({
|
||||
...feedback,
|
||||
1: {
|
||||
...feedback[1],
|
||||
status: isSuccess ? 'success' : 'error'
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
setError(error.message)
|
||||
console.error('error', error.message)
|
||||
setFeedback({
|
||||
...feedback,
|
||||
1: {
|
||||
...feedback[1],
|
||||
status: 'error',
|
||||
message: error.message
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 2. Construct and encypt DDO
|
||||
// --------------------------------------------------
|
||||
try {
|
||||
const ddo = await transformPublishFormToDdo(
|
||||
values,
|
||||
_datatokenAddress,
|
||||
_erc721Address
|
||||
)
|
||||
|
||||
_ddo = ddo
|
||||
|
||||
const encryptedResponse = await ProviderInstance.encrypt(
|
||||
ddo,
|
||||
values.services[0].providerUrl.url,
|
||||
(httpMethod: Method, url: string, body: string, headers: any) => {
|
||||
return axios(url, {
|
||||
method: httpMethod,
|
||||
data: body,
|
||||
headers: headers,
|
||||
cancelToken: newCancelToken()
|
||||
})
|
||||
}
|
||||
)
|
||||
const encryptedDdo = encryptedResponse.data
|
||||
|
||||
_encryptedDdo = encryptedDdo
|
||||
|
||||
console.log('ddo', JSON.stringify(ddo))
|
||||
|
||||
setFeedback({
|
||||
...feedback,
|
||||
2: {
|
||||
...feedback[2],
|
||||
status: encryptedDdo ? 'success' : 'error'
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('error', error.message)
|
||||
setFeedback({
|
||||
...feedback,
|
||||
2: {
|
||||
...feedback[2],
|
||||
status: 'error',
|
||||
message: error.message
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 3. Publish DDO
|
||||
// --------------------------------------------------
|
||||
try {
|
||||
// TODO: this whole setMetadata needs to go in a function ,too many hardcoded/calculated params
|
||||
// TODO: hash generation : this needs to be moved in a function (probably on ocean.js) after we figure out what is going on in provider, leave it here for now
|
||||
const metadataHash = getHash(JSON.stringify(_ddo))
|
||||
const nft = new Nft(web3)
|
||||
|
||||
// theoretically used by aquarius or provider, not implemented yet, will remain hardcoded
|
||||
const flags = '0x2'
|
||||
|
||||
const res = await nft.setMetadata(
|
||||
_erc721Address,
|
||||
accountId,
|
||||
0,
|
||||
values.services[0].providerUrl.url,
|
||||
'',
|
||||
flags,
|
||||
_encryptedDdo,
|
||||
'0x' + metadataHash
|
||||
)
|
||||
|
||||
console.log('result', res)
|
||||
|
||||
setFeedback({
|
||||
...feedback,
|
||||
3: {
|
||||
...feedback[3],
|
||||
status: res ? 'success' : 'error'
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('error', error.message)
|
||||
setFeedback({
|
||||
...feedback,
|
||||
3: {
|
||||
...feedback[3],
|
||||
status: 'error',
|
||||
message: error.message
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 3. Integrity check of DDO before & after publishing
|
||||
// --------------------------------------------------
|
||||
|
||||
// TODO: not sure we want to do this at this step, seems overkill
|
||||
|
||||
// if we want to do this we just need to fetch it from aquarius. If we want to fetch from chain and decrypt, we would have more metamask pop-ups (not UX friendly)
|
||||
// decrypt also validates the checksum
|
||||
|
||||
// TODO: remove the commented lines of code until `setSuccess`, didn't remove them yet because maybe i missed something
|
||||
|
||||
// --------------------------------------------------
|
||||
// 1. Mint NFT & datatokens & put in pool
|
||||
// --------------------------------------------------
|
||||
// const nftOptions = values.metadata.nft
|
||||
// const nftCreateData = generateNftCreateData(nftOptions)
|
||||
|
||||
// figure out syntax of ercParams we most likely need to pass
|
||||
// to createNftWithErc() as we need to pass options for the datatoken.
|
||||
// const ercParams = {}
|
||||
// const priceOptions = {
|
||||
// // swapFee is tricky: to get 0.1% you need to send 0.001 as value
|
||||
// swapFee: `${values.pricing.swapFee / 100}`
|
||||
// }
|
||||
// const txMint = await createNftWithErc(accountId, nftCreateData)
|
||||
|
||||
// figure out how to get nftAddress & datatokenAddress from tx log.
|
||||
// const { nftAddress, datatokenAddress } = txMint.logs[0].args
|
||||
// if (!nftAddress || !datatokenAddress) { throw new Error() }
|
||||
//
|
||||
// --------------------------------------------------
|
||||
// 2. Construct and publish DDO
|
||||
// --------------------------------------------------
|
||||
// const did = sha256(`${nftAddress}${chainId}`)
|
||||
// const ddo = transformPublishFormToDdo(values, datatokenAddress, nftAddress)
|
||||
// const txPublish = await publish(ddo)
|
||||
//
|
||||
// --------------------------------------------------
|
||||
// 3. Integrity check of DDO before & after publishing
|
||||
// --------------------------------------------------
|
||||
// const checksumBefore = sha256(ddo)
|
||||
// const ddoFromChain = await getDdoFromChain(ddo.id)
|
||||
// const ddoFromChainDecrypted = await decryptDdo(ddoFromChain)
|
||||
// const checksumAfter = sha256(ddoFromChainDecrypted)
|
||||
|
||||
// if (checksumBefore !== checksumAfter) {
|
||||
// throw new Error('DDO integrity check failed!')
|
||||
// }
|
||||
}
|
||||
|
||||
return isInPurgatory && purgatoryData ? null : (
|
||||
|
@ -97,7 +254,7 @@ export default function PublishPage({
|
|||
/>
|
||||
<Form className={styles.form} ref={scrollToRef}>
|
||||
<Navigation />
|
||||
<Steps />
|
||||
<Steps feedback={feedback} />
|
||||
<Actions scrollToRef={scrollToRef} />
|
||||
</Form>
|
||||
{debug && <Debug />}
|
||||
|
|
Loading…
Reference in New Issue
Block a user