1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

Move auto-changing providerUrl (#1256)

* move auto-changing providerUrl

* remove comments

* new custom provider → change network interaction

* more interaction tweaks for custom provider → change network case

* fix adding custom provider

* add provider chainId check

* initialValues from form context

* user chainId fallback
This commit is contained in:
Matthias Kretschmann 2022-04-15 14:28:45 +01:00 committed by GitHub
parent cc0da26740
commit 967b869b02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 68 additions and 82 deletions

View File

@ -5,23 +5,29 @@ import { InputProps } from '@shared/FormInput'
import FileInfo from '../FilesInput/Info' import FileInfo from '../FilesInput/Info'
import styles from './index.module.css' import styles from './index.module.css'
import Button from '@shared/atoms/Button' import Button from '@shared/atoms/Button'
import { initialValues } from 'src/components/Publish/_constants'
import { LoggerInstance, ProviderInstance } from '@oceanprotocol/lib' import { LoggerInstance, ProviderInstance } from '@oceanprotocol/lib'
import { FormPublishData } from 'src/components/Publish/_types' import { FormPublishData } from 'src/components/Publish/_types'
import { getOceanConfig } from '@utils/ocean'
import { useWeb3 } from '@context/Web3'
import axios from 'axios'
import { useCancelToken } from '@hooks/useCancelToken'
export default function CustomProvider(props: InputProps): ReactElement { export default function CustomProvider(props: InputProps): ReactElement {
const { chainId } = useWeb3()
const newCancelToken = useCancelToken()
const { initialValues, setFieldError } = useFormikContext<FormPublishData>()
const [field, meta, helpers] = useField(props.name) const [field, meta, helpers] = useField(props.name)
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const { setFieldError } = useFormikContext<FormPublishData>()
async function handleValidation(e: React.SyntheticEvent) { async function handleValidation(e: React.SyntheticEvent) {
e.preventDefault() e.preventDefault()
try { try {
setIsLoading(true) setIsLoading(true)
// Check if provider is a valid provider
const isValid = await ProviderInstance.isValidProvider(field.value.url) const isValid = await ProviderInstance.isValidProvider(field.value.url)
// error if something's not right from response
// No way to detect a failed request with ProviderInstance.isValidProvider, // No way to detect a failed request with ProviderInstance.isValidProvider,
// making this error show up for multiple cases it shouldn't, like network // making this error show up for multiple cases it shouldn't, like network
// down. // down.
@ -30,8 +36,20 @@ export default function CustomProvider(props: InputProps): ReactElement {
'✗ No valid provider detected. Check your network, your URL and try again.' '✗ No valid provider detected. Check your network, your URL and try again.'
) )
// Check if valid provider is for same chain user is on
const providerResponse = await axios.get(field.value.url, {
cancelToken: newCancelToken()
})
const providerChainId = providerResponse?.data?.chainId
const userChainId = chainId || 1
if (providerChainId !== userChainId)
throw Error(
'✗ This provider is incompatible with the network your wallet is connected to.'
)
// if all good, add provider to formik state // if all good, add provider to formik state
helpers.setValue({ url: field.value.url, valid: isValid }) helpers.setValue({ url: field.value.url, valid: isValid, custom: true })
} catch (error) { } catch (error) {
setFieldError(`${field.name}.url`, error.message) setFieldError(`${field.name}.url`, error.message)
LoggerInstance.error(error.message) LoggerInstance.error(error.message)
@ -41,13 +59,18 @@ export default function CustomProvider(props: InputProps): ReactElement {
} }
function handleFileInfoClose() { function handleFileInfoClose() {
helpers.setValue({ url: '', valid: false }) helpers.setValue({ url: '', valid: false, custom: true })
helpers.setTouched(false) helpers.setTouched(false)
} }
function handleDefault(e: React.SyntheticEvent) { function handleDefault(e: React.SyntheticEvent) {
e.preventDefault() e.preventDefault()
helpers.setValue(initialValues.services[0].providerUrl)
const oceanConfig = getOceanConfig(chainId)
const providerUrl =
oceanConfig?.providerUri || initialValues.services[0].providerUrl.url
helpers.setValue({ url: providerUrl, valid: true, custom: false })
} }
return field?.value?.valid === true ? ( return field?.value?.valid === true ? (

View File

@ -6,7 +6,6 @@ import IconCompute from '@images/compute.svg'
import content from '../../../../content/publish/form.json' import content from '../../../../content/publish/form.json'
import { getFieldContent } from '../_utils' import { getFieldContent } from '../_utils'
import { FormPublishData } from '../_types' import { FormPublishData } from '../_types'
import { getOceanConfig } from '@utils/ocean'
const accessTypeOptionsTitles = getFieldContent( const accessTypeOptionsTitles = getFieldContent(
'access', 'access',
@ -15,8 +14,7 @@ const accessTypeOptionsTitles = getFieldContent(
export default function ServicesFields(): ReactElement { export default function ServicesFields(): ReactElement {
// connect with Form state, use for conditional field rendering // connect with Form state, use for conditional field rendering
const { values, setFieldValue, touched, setTouched } = const { values, setFieldValue } = useFormikContext<FormPublishData>()
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} // 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 = [ const accessTypeOptions = [
@ -55,15 +53,6 @@ export default function ServicesFields(): ReactElement {
) )
}, [values.services[0].algorithmPrivacy, setFieldValue]) }, [values.services[0].algorithmPrivacy, setFieldValue])
// Auto-change default providerUrl on user network change
useEffect(() => {
if (!values?.user?.chainId) return
const config = getOceanConfig(values.user.chainId)
config && setFieldValue('services[0].providerUrl.url', config.providerUri)
setTouched({ ...touched, services: [{ providerUrl: { url: true } }] })
}, [values.user.chainId, setFieldValue, setTouched])
return ( return (
<> <>
<Field <Field

View File

@ -3,6 +3,7 @@ import { useFormikContext } from 'formik'
import { wizardSteps, initialPublishFeedback } from './_constants' import { wizardSteps, initialPublishFeedback } from './_constants'
import { useWeb3 } from '@context/Web3' import { useWeb3 } from '@context/Web3'
import { FormPublishData, PublishFeedback } from './_types' import { FormPublishData, PublishFeedback } from './_types'
import { getOceanConfig } from '@utils/ocean'
export function Steps({ export function Steps({
feedback feedback
@ -10,7 +11,8 @@ export function Steps({
feedback: PublishFeedback feedback: PublishFeedback
}): ReactElement { }): ReactElement {
const { chainId, accountId } = useWeb3() const { chainId, accountId } = useWeb3()
const { values, setFieldValue } = useFormikContext<FormPublishData>() const { values, setFieldValue, touched, setTouched } =
useFormikContext<FormPublishData>()
// auto-sync user chainId & account into form data values // auto-sync user chainId & account into form data values
useEffect(() => { useEffect(() => {
@ -41,7 +43,32 @@ export function Steps({
: initialPublishFeedback['1'].description : initialPublishFeedback['1'].description
} }
}) })
}, [values.pricing.type, setFieldValue]) }, [values.pricing.type, feedback, setFieldValue])
// Auto-change default providerUrl on user network change
useEffect(() => {
if (
!values?.user?.chainId ||
values?.services[0]?.providerUrl.custom === true
)
return
const config = getOceanConfig(values.user.chainId)
if (config) {
setFieldValue('services[0].providerUrl', {
url: config.providerUri,
valid: true,
custom: false
})
}
setTouched({ ...touched, services: [{ providerUrl: { url: true } }] })
}, [
values?.user?.chainId,
values?.services[0]?.providerUrl.custom,
setFieldValue,
setTouched
])
const { component } = wizardSteps.filter( const { component } = wizardSteps.filter(
(stepContent) => stepContent.step === values.user.stepCurrent (stepContent) => stepContent.step === values.user.stepCurrent

View File

@ -74,7 +74,8 @@ export const initialValues: FormPublishData = {
access: 'access', access: 'access',
providerUrl: { providerUrl: {
url: 'https://provider.mainnet.oceanprotocol.com', url: 'https://provider.mainnet.oceanprotocol.com',
valid: true valid: true,
custom: false
}, },
computeOptions computeOptions
} }

View File

@ -16,7 +16,7 @@ export interface FormPublishService {
timeout: string timeout: string
dataTokenOptions: { name: string; symbol: string } dataTokenOptions: { name: string; symbol: string }
access: 'Download' | 'Compute' | string access: 'Download' | 'Compute' | string
providerUrl?: { url: string; valid: boolean } providerUrl: { url: string; valid: boolean; custom: boolean }
algorithmPrivacy?: boolean algorithmPrivacy?: boolean
computeOptions?: ServiceComputeOptions computeOptions?: ServiceComputeOptions
} }

View File

@ -58,7 +58,8 @@ const validationService = {
.required('Required'), .required('Required'),
providerUrl: Yup.object().shape({ providerUrl: Yup.object().shape({
url: Yup.string().url('Must be a valid URL.').required('Required'), url: Yup.string().url('Must be a valid URL.').required('Required'),
valid: Yup.boolean().isTrue().required('Valid Provider is required.') valid: Yup.boolean().isTrue().required('Valid Provider is required.'),
custom: Yup.boolean()
}) })
} }

View File

@ -1,4 +1,4 @@
import React, { ReactElement, useState, useRef, useEffect } from 'react' import React, { ReactElement, useState, useRef } from 'react'
import { Form, Formik } from 'formik' import { Form, Formik } from 'formik'
import { initialPublishFeedback, initialValues } from './_constants' import { initialPublishFeedback, initialValues } from './_constants'
import { useAccountPurgatory } from '@hooks/useAccountPurgatory' import { useAccountPurgatory } from '@hooks/useAccountPurgatory'
@ -14,20 +14,14 @@ import { Steps } from './Steps'
import { FormPublishData, PublishFeedback } from './_types' import { FormPublishData, PublishFeedback } from './_types'
import { useUserPreferences } from '@context/UserPreferences' import { useUserPreferences } from '@context/UserPreferences'
import useNftFactory from '@hooks/contracts/useNftFactory' import useNftFactory from '@hooks/contracts/useNftFactory'
import { import { ProviderInstance, LoggerInstance, DDO } from '@oceanprotocol/lib'
Nft,
getHash,
ProviderInstance,
LoggerInstance,
DDO
} from '@oceanprotocol/lib'
import { getOceanConfig } from '@utils/ocean' import { getOceanConfig } from '@utils/ocean'
import { validationSchema } from './_validation' import { validationSchema } from './_validation'
import { useAbortController } from '@hooks/useAbortController' import { useAbortController } from '@hooks/useAbortController'
import { setNFTMetadataAndTokenURI } from '@utils/nft' import { setNFTMetadataAndTokenURI } from '@utils/nft'
// TODO: restore FormikPersist, add back clear form action // TODO: restore FormikPersist, add back clear form action
const formName = 'ocean-publish-form' // const formName = 'ocean-publish-form'
export default function PublishPage({ export default function PublishPage({
content content
@ -225,62 +219,13 @@ export default function PublishPage({
} }
})) }))
} }
// --------------------------------------------------
// 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 : ( return isInPurgatory && purgatoryData ? null : (
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={async (values, { resetForm }) => { onSubmit={async (values) => {
// kick off publishing // kick off publishing
await handleSubmit(values) await handleSubmit(values)
}} }}