import React, { ReactElement, useState, useRef } from 'react' import { Form, Formik } from 'formik' import { initialPublishFeedback, initialValues } from './_constants' import { useAccountPurgatory } from '@hooks/useAccountPurgatory' import { useWeb3 } from '@context/Web3' import { createTokensAndPricing, transformPublishFormToDdo } from './_utils' import PageHeader from '@shared/Page/PageHeader' import Title from './Title' import styles from './index.module.css' import Actions from './Actions' import Debug from './Debug' import Navigation from './Navigation' import { Steps } from './Steps' import { FormPublishData, PublishFeedback } from './_types' import { useUserPreferences } from '@context/UserPreferences' import useNftFactory from '@hooks/contracts/useNftFactory' import { Nft, getHash, ProviderInstance, LoggerInstance, DDO } 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' export default function PublishPage({ content }: { content: { title: string; description: string; warning: string } }): ReactElement { const { debug } = useUserPreferences() const { accountId, web3, chainId } = useWeb3() const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId) const scrollToRef = useRef() const { appConfig } = useSiteMetadata() const nftFactory = useNftFactory() const newCancelToken = useCancelToken() const [feedback, setFeedback] = useState( initialPublishFeedback ) async function handleSubmit(values: FormPublishData) { let _erc721Address: string, _datatokenAddress: string, _ddo: DDO, _encryptedDdo: string // reset all feedback state setFeedback(initialPublishFeedback) // -------------------------------------------------- // 1. Create NFT & datatokens & create pricing schema // -------------------------------------------------- try { setFeedback((prevState) => ({ ...prevState, 1: { ...prevState[1], status: 'active', txCount: values.pricing.type === 'dynamic' ? 2 : 1 } })) const config = getOceanConfig(chainId) LoggerInstance.log('[publish] using config: ', config) const { erc721Address, datatokenAddress, txHash } = await createTokensAndPricing( values, accountId, appConfig.marketFeeAddress, config, nftFactory, web3 ) const isSuccess = Boolean(erc721Address && datatokenAddress && txHash) _erc721Address = erc721Address _datatokenAddress = datatokenAddress LoggerInstance.log('[publish] createTokensAndPricing tx', txHash) LoggerInstance.log('[publish] erc721Address', erc721Address) LoggerInstance.log('[publish] datatokenAddress', datatokenAddress) setFeedback((prevState) => ({ ...prevState, 1: { ...prevState[1], status: isSuccess ? 'success' : 'error', txHash } })) } catch (error) { LoggerInstance.error('[publish] error', error.message) setFeedback((prevState) => ({ ...prevState, 1: { ...prevState[1], status: 'error', message: error.message } })) } // -------------------------------------------------- // 2. Construct and encrypt DDO // -------------------------------------------------- try { setFeedback((prevState) => ({ ...prevState, 2: { ...prevState[2], status: _datatokenAddress && _erc721Address ? 'active' : 'error' } })) if (!_datatokenAddress || !_erc721Address) throw new Error('No NFT or Datatoken received.') const ddo = await transformPublishFormToDdo( values, _datatokenAddress, _erc721Address ) _ddo = ddo LoggerInstance.log('[publish] Got new DDO', JSON.stringify(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 LoggerInstance.log('[publish] Got encrypted DDO', encryptedDdo) setFeedback((prevState) => ({ ...prevState, 2: { ...prevState[2], status: encryptedDdo ? 'success' : 'error' } })) } catch (error) { LoggerInstance.error('[publish] error', error.message) setFeedback((prevState) => ({ ...prevState, 2: { ...prevState[2], status: 'error', message: error.message } })) } // -------------------------------------------------- // 3. Write DDO into NFT metadata // -------------------------------------------------- try { setFeedback((prevState) => ({ ...prevState, 3: { ...prevState[3], status: _ddo && _encryptedDdo ? 'active' : 'error' } })) if (!_ddo || !_encryptedDdo) throw new Error('No DDO received.') // 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 ) LoggerInstance.log('[publish] setMetadata result', res) const txHash = res.transactionHash setFeedback((prevState) => ({ ...prevState, 3: { ...prevState[3], status: res ? 'success' : 'error', txHash } })) } catch (error) { LoggerInstance.error('[publish] error', error.message) setFeedback((prevState) => ({ ...prevState, 3: { ...prevState[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 : ( { // kick off publishing await handleSubmit(values) }} > {({ values }) => ( <> } description={content.description} />
{debug && } )}
) }