Fix consume of FRE assets & improve error handling (#1940)

* wait for approve tx to finish

* handle provider errors better

* more error handling updates

* cleanups

* bump ocean lib

* fix publish try catch const assign
This commit is contained in:
Bogdan Fazakas 2023-07-17 12:01:21 +03:00 committed by GitHub
parent 4d9002da07
commit 2e69739778
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 236 additions and 143 deletions

30
package-lock.json generated
View File

@ -15,7 +15,7 @@
"@coingecko/cryptoformat": "^0.5.4",
"@loadable/component": "^5.15.2",
"@oceanprotocol/art": "^3.2.0",
"@oceanprotocol/lib": "^3.0.1",
"@oceanprotocol/lib": "^3.0.2",
"@oceanprotocol/typographies": "^0.1.0",
"@oceanprotocol/use-dark-mode": "^2.4.3",
"@orbisclub/orbis-sdk": "^0.4.40",
@ -6072,16 +6072,16 @@
"integrity": "sha512-aUQtg4m5hJlQ0u8C29O9TXJWcAenO3G9vP+vf6LNFkpTDOCMycN/F0SzHS89VNrvGUha8oTDEg7FAkfZBPv2WA=="
},
"node_modules/@oceanprotocol/contracts": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.1.8.tgz",
"integrity": "sha512-I6xoADZpP/8EyN3VWZ+dLYv24DbJj3xzmSDcYv0FolXeAUF3FluzminL5AgQtAaoyUtlHl1D3ij1B++KupWcQQ=="
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.1.14.tgz",
"integrity": "sha512-PJih7C6LHaWHHj1qgxZsSkEqKphhJrL3G7WuMOxl4N1daDrF6sooDDU+9dZkcHSVPc7cMjkFqLc5fP58NSAobw=="
},
"node_modules/@oceanprotocol/lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-3.0.1.tgz",
"integrity": "sha512-9GPwQ5SBv4tej6YfrNmZpCnQPte6kinU3U7jPCdFAAVCpRc+IuQymu0/PfzYb1yo3GrMyGsp3ht6wjZDhy37QA==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-3.0.2.tgz",
"integrity": "sha512-yp75eYcFzqqgUAzG+fkLXHrNaA/ZZvogEE25NOy2DI5EAUp1Wm6tZ0q6AIH0i05KCPOfXoIGwqTBlfQa0rh48A==",
"dependencies": {
"@oceanprotocol/contracts": "^1.1.8",
"@oceanprotocol/contracts": "^1.1.14",
"cross-fetch": "^3.1.5",
"crypto-js": "^4.1.1",
"decimal.js": "^10.4.1",
@ -58441,16 +58441,16 @@
"integrity": "sha512-aUQtg4m5hJlQ0u8C29O9TXJWcAenO3G9vP+vf6LNFkpTDOCMycN/F0SzHS89VNrvGUha8oTDEg7FAkfZBPv2WA=="
},
"@oceanprotocol/contracts": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.1.8.tgz",
"integrity": "sha512-I6xoADZpP/8EyN3VWZ+dLYv24DbJj3xzmSDcYv0FolXeAUF3FluzminL5AgQtAaoyUtlHl1D3ij1B++KupWcQQ=="
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.1.14.tgz",
"integrity": "sha512-PJih7C6LHaWHHj1qgxZsSkEqKphhJrL3G7WuMOxl4N1daDrF6sooDDU+9dZkcHSVPc7cMjkFqLc5fP58NSAobw=="
},
"@oceanprotocol/lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-3.0.1.tgz",
"integrity": "sha512-9GPwQ5SBv4tej6YfrNmZpCnQPte6kinU3U7jPCdFAAVCpRc+IuQymu0/PfzYb1yo3GrMyGsp3ht6wjZDhy37QA==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-3.0.2.tgz",
"integrity": "sha512-yp75eYcFzqqgUAzG+fkLXHrNaA/ZZvogEE25NOy2DI5EAUp1Wm6tZ0q6AIH0i05KCPOfXoIGwqTBlfQa0rh48A==",
"requires": {
"@oceanprotocol/contracts": "^1.1.8",
"@oceanprotocol/contracts": "^1.1.14",
"cross-fetch": "^3.1.5",
"crypto-js": "^4.1.1",
"decimal.js": "^10.4.1",

View File

@ -29,7 +29,7 @@
"@coingecko/cryptoformat": "^0.5.4",
"@loadable/component": "^5.15.2",
"@oceanprotocol/art": "^3.2.0",
"@oceanprotocol/lib": "^3.0.1",
"@oceanprotocol/lib": "^3.0.2",
"@oceanprotocol/typographies": "^0.1.0",
"@oceanprotocol/use-dark-mode": "^2.4.3",
"@orbisclub/orbis-sdk": "^0.4.40",

View File

@ -5,6 +5,7 @@ import {
TokenPriceQuery_token as TokenPrice
} from '../@types/subgraph/TokenPriceQuery'
import {
getErrorMessage,
LoggerInstance,
ProviderFees,
ProviderInstance
@ -16,6 +17,7 @@ import {
publisherMarketOrderFee
} from '../../app.config'
import { Signer } from 'ethers'
import { toast } from 'react-toastify'
const tokenPriceQuery = gql`
query TokenPriceQuery($datatokenId: ID!, $account: String) {
@ -82,7 +84,6 @@ function getAccessDetailsFromTokenPrice(
timeout?: number
): AccessDetails {
const accessDetails = {} as AccessDetails
// Return early when no supported pricing schema found.
if (
tokenPrice?.dispensers?.length === 0 &&
@ -176,15 +177,22 @@ export async function getOrderPriceAndFees(
} as OrderPriceAndFees
// fetch provider fee
const initializeData =
!providerFees &&
(await ProviderInstance.initialize(
asset?.id,
asset?.services[0].id,
0,
accountId,
asset?.services[0].serviceEndpoint
))
let initializeData
try {
initializeData =
!providerFees &&
(await ProviderInstance.initialize(
asset?.id,
asset?.services[0].id,
0,
accountId,
asset?.services[0].serviceEndpoint
))
} catch (error) {
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Initialize Provider] Error:', message)
toast.error(message)
}
orderPriceAndFee.providerFee = providerFees || initializeData.providerFee
// fetch price and swap fees

View File

@ -9,7 +9,8 @@ import {
Service,
ProviderInstance,
ComputeEnvironment,
ComputeJob
ComputeJob,
getErrorMessage
} from '@oceanprotocol/lib'
import { CancelToken } from 'axios'
import { gql } from 'urql'
@ -26,6 +27,7 @@ import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelecti
import { transformAssetToAssetSelection } from './assetConvertor'
import { ComputeEditForm } from '../components/Asset/Edit/_types'
import { getFileDidInfo } from './provider'
import { toast } from 'react-toastify'
const getComputeOrders = gql`
query ComputeOrders($user: String!) {
@ -146,7 +148,12 @@ export async function getComputeEnviroment(
if (!computeEnvs[asset.chainId][0]) return null
return computeEnvs[asset.chainId][0]
} catch (e) {
LoggerInstance.error('[compute] Fetch compute enviroment: ', e.message)
const message = getErrorMessage(JSON.parse(e.message))
LoggerInstance.error(
'[Compute to Data] Fetch compute environment:',
message
)
toast.error(message)
}
}
@ -222,8 +229,6 @@ async function getJobs(
assets: Asset[]
): Promise<ComputeJobMetaData[]> {
const computeJobs: ComputeJobMetaData[] = []
// commented loop since we decide how to filter jobs
// for await (const providerUrl of providerUrls) {
try {
const providerComputeJobs = (await ProviderInstance.computeStatus(
providerUrls[0],
@ -256,9 +261,10 @@ async function getJobs(
})
}
} catch (err) {
LoggerInstance.error(err.message)
const message = getErrorMessage(JSON.parse(err.message))
LoggerInstance.error('[Compute to Data] Error:', message)
toast.error(message)
}
// }
return computeJobs
}

View File

@ -6,10 +6,12 @@ import {
ProviderInstance,
DDO,
MetadataAndTokenURI,
NftCreateData
NftCreateData,
getErrorMessage
} from '@oceanprotocol/lib'
import { SvgWaves } from './SvgWaves'
import { Signer, ethers } from 'ethers'
import { toast } from 'react-toastify'
// https://docs.opensea.io/docs/metadata-standards
export interface NftMetadata {
@ -101,12 +103,19 @@ export async function setNftMetadata(
signer: Signer,
signal: AbortSignal
): Promise<ethers.providers.TransactionResponse> {
const encryptedDdo = await ProviderInstance.encrypt(
asset,
asset.chainId,
asset.services[0].serviceEndpoint,
signal
)
let encryptedDdo
try {
encryptedDdo = await ProviderInstance.encrypt(
asset,
asset.chainId,
asset.services[0].serviceEndpoint,
signal
)
} catch (err) {
const message = getErrorMessage(JSON.parse(err.message))
LoggerInstance.error('[Encrypt Data] Error:', message)
toast.error(message)
}
LoggerInstance.log('[setNftMetadata] Got encrypted DDO', encryptedDdo)
const metadataHash = getHash(JSON.stringify(asset))
@ -136,12 +145,19 @@ export async function setNFTMetadataAndTokenURI(
nftMetadata: NftMetadata,
signal: AbortSignal
): Promise<ethers.providers.TransactionResponse> {
const encryptedDdo = await ProviderInstance.encrypt(
asset,
asset.chainId,
asset.services[0].serviceEndpoint,
signal
)
let encryptedDdo
try {
encryptedDdo = await ProviderInstance.encrypt(
asset,
asset.chainId,
asset.services[0].serviceEndpoint,
signal
)
} catch (err) {
const message = getErrorMessage(JSON.parse(err.message))
LoggerInstance.error('[Encrypt Data] Error:', message)
toast.error(message)
}
LoggerInstance.log(
'[setNFTMetadataAndTokenURI] Got encrypted DDO',
encryptedDdo

View File

@ -11,7 +11,8 @@ import {
ProviderComputeInitialize,
ProviderFees,
ProviderInstance,
ProviderInitialize
ProviderInitialize,
getErrorMessage
} from '@oceanprotocol/lib'
import { Signer, ethers } from 'ethers'
import { getOceanConfig } from './ocean'
@ -38,7 +39,9 @@ async function initializeProvider(
)
return provider
} catch (error) {
LoggerInstance.log('[Initialize Provider] Error:', error)
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.log('[Initialize Provider] Error:', message)
toast.error(message)
}
}
@ -99,19 +102,16 @@ export async function order(
if (asset.accessDetails.templateId === 1) {
if (!hasDatatoken) {
// buy datatoken
const txApprove = await approve(
const tx: any = await approve(
signer,
config,
accountId,
await signer.getAddress(),
asset.accessDetails.baseToken.address,
config.fixedRateExchangeAddress,
await amountToUnits(
signer,
asset?.accessDetails?.baseToken?.address,
orderPriceAndFees.price
),
orderPriceAndFees.price,
false
)
const txApprove = typeof tx !== 'number' ? await tx.wait() : tx
if (!txApprove) {
return
}
@ -126,6 +126,7 @@ export async function order(
marketFeeAddress,
consumeMarketFixedSwapFee
)
const buyDtTx = await freTx.wait()
}
return await datatoken.startOrder(
asset.accessDetails.datatoken.address,
@ -136,19 +137,17 @@ export async function order(
)
}
if (asset.accessDetails.templateId === 2) {
const txApprove = await approve(
const tx: any = await approve(
signer,
config,
accountId,
asset.accessDetails.baseToken.address,
asset.accessDetails.datatoken.address,
await amountToUnits(
signer,
asset?.accessDetails?.baseToken?.address,
orderPriceAndFees.price
),
orderPriceAndFees.price,
false
)
const txApprove = typeof tx !== 'number' ? await tx.wait() : tx
if (!txApprove) {
return
}

View File

@ -12,11 +12,14 @@ import {
ProviderComputeInitializeResults,
ProviderInstance,
UrlFile,
AbiItem
AbiItem,
getErrorMessage
} from '@oceanprotocol/lib'
import { QueryHeader } from '@shared/FormInput/InputElement/Headers'
import { Signer } from 'ethers'
import { getValidUntilTime } from './compute'
import { toast } from 'react-toastify'
import { tr } from 'date-fns/locale'
export async function initializeProviderForCompute(
dataset: AssetExtended,
@ -51,7 +54,9 @@ export async function initializeProviderForCompute(
accountId
)
} catch (error) {
LoggerInstance.error(`Error initializing provider for the compute job!`)
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Initialize Provider] Error:', message)
toast.error(message)
return null
}
}
@ -67,7 +72,9 @@ export async function getEncryptedFiles(
const response = await ProviderInstance.encrypt(files, chainId, providerUrl)
return response
} catch (error) {
console.error('Error parsing json: ' + error.message)
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Encrypt] Error:', message)
toast.error(message)
}
}
@ -86,7 +93,9 @@ export async function getFileDidInfo(
)
return response
} catch (error) {
LoggerInstance.error(error.message)
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Initialize check file did] Error:', message)
toast.error(message)
}
}
@ -100,76 +109,99 @@ export async function getFileInfo(
chainId?: number,
method?: string
): Promise<FileInfo[]> {
try {
let response
const headersProvider = {}
if (headers?.length > 0) {
headers.map((el) => {
headersProvider[el.key] = el.value
return el
})
}
let response
const headersProvider = {}
if (headers?.length > 0) {
headers.map((el) => {
headersProvider[el.key] = el.value
return el
})
}
switch (storageType) {
case 'ipfs': {
const fileIPFS: Ipfs = {
type: storageType,
hash: file
}
switch (storageType) {
case 'ipfs': {
const fileIPFS: Ipfs = {
type: storageType,
hash: file
}
try {
response = await ProviderInstance.getFileInfo(fileIPFS, providerUrl)
break
} catch (error) {
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Get File info] Error:', message)
toast.error(message)
}
case 'arweave': {
const fileArweave: Arweave = {
type: storageType,
transactionId: file
}
break
}
case 'arweave': {
const fileArweave: Arweave = {
type: storageType,
transactionId: file
}
try {
response = await ProviderInstance.getFileInfo(fileArweave, providerUrl)
break
} catch (error) {
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Get File info] Error:', message)
toast.error(message)
}
case 'graphql': {
const fileGraphql: GraphqlQuery = {
type: storageType,
url: file,
headers: headersProvider,
query
}
break
}
case 'graphql': {
const fileGraphql: GraphqlQuery = {
type: storageType,
url: file,
headers: headersProvider,
query
}
try {
response = await ProviderInstance.getFileInfo(fileGraphql, providerUrl)
break
} catch (error) {
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Get File info] Error:', message)
toast.error(message)
}
case 'smartcontract': {
// clean obj
const fileSmartContract: Smartcontract = {
chainId,
type: storageType,
address: file,
abi: JSON.parse(abi) as AbiItem
}
break
}
case 'smartcontract': {
// clean obj
const fileSmartContract: Smartcontract = {
chainId,
type: storageType,
address: file,
abi: JSON.parse(abi) as AbiItem
}
try {
response = await ProviderInstance.getFileInfo(
fileSmartContract,
providerUrl
)
break
}
default: {
const fileUrl: UrlFile = {
type: 'url',
index: 0,
url: file,
headers: headersProvider,
method
}
response = await ProviderInstance.getFileInfo(fileUrl, providerUrl)
break
} catch (error) {
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Get File info] Error:', message)
toast.error(message)
}
break
}
default: {
const fileUrl: UrlFile = {
type: 'url',
index: 0,
url: file,
headers: headersProvider,
method
}
try {
response = await ProviderInstance.getFileInfo(fileUrl, providerUrl)
} catch (error) {
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Get File info] Error:', message)
toast.error(message)
}
break
}
return response
} catch (error) {
LoggerInstance.error(error.message)
}
return response
}
export async function downloadFile(
@ -178,14 +210,21 @@ export async function downloadFile(
accountId: string,
validOrderTx?: string
) {
const downloadUrl = await ProviderInstance.getDownloadUrl(
asset.id,
asset.services[0].id,
0,
validOrderTx || asset.accessDetails.validOrderTx,
asset.services[0].serviceEndpoint,
signer
)
let downloadUrl
try {
downloadUrl = await ProviderInstance.getDownloadUrl(
asset.id,
asset.services[0].id,
0,
validOrderTx || asset.accessDetails.validOrderTx,
asset.services[0].serviceEndpoint,
signer
)
} catch (error) {
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Get download url] Error:', message)
toast.error(message)
}
await downloadFileBrowser(downloadUrl)
}
@ -196,6 +235,8 @@ export async function checkValidProvider(
const response = await ProviderInstance.isValidProvider(providerUrl)
return response
} catch (error) {
LoggerInstance.error(error.message)
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Check] Error:', message)
toast.error(message)
}
}

View File

@ -5,12 +5,17 @@ import { InputProps } from '@shared/FormInput'
import FileInfo from '../FilesInput/Info'
import styles from './index.module.css'
import Button from '@shared/atoms/Button'
import { LoggerInstance, ProviderInstance } from '@oceanprotocol/lib'
import {
LoggerInstance,
ProviderInstance,
getErrorMessage
} from '@oceanprotocol/lib'
import { FormPublishData } from '@components/Publish/_types'
import { getOceanConfig } from '@utils/ocean'
import axios from 'axios'
import { useCancelToken } from '@hooks/useCancelToken'
import { useNetwork } from 'wagmi'
import { toast } from 'react-toastify'
export default function CustomProvider(props: InputProps): ReactElement {
const { chain } = useNetwork()
@ -56,8 +61,9 @@ export default function CustomProvider(props: InputProps): ReactElement {
// if all good, add provider to formik state
helpers.setValue({ url: field.value.url, valid: isValid, custom: true })
} catch (error) {
setFieldError(`${field.name}.url`, error.message)
LoggerInstance.error(error.message)
const message = getErrorMessage(JSON.parse(error.message))
setFieldError(`${field.name}.url`, message)
LoggerInstance.error('[Custom Provider]:', message)
} finally {
setIsLoading(false)
}

View File

@ -14,7 +14,8 @@ import {
ProviderComputeInitializeResults,
unitsToAmount,
minAbi,
ProviderFees
ProviderFees,
getErrorMessage
} from '@oceanprotocol/lib'
import { toast } from 'react-toastify'
import Price from '@shared/Price'
@ -430,9 +431,10 @@ export default function Compute({
setRefetchJobs(!refetchJobs)
initPriceAndFees()
} catch (error) {
setError(error.message)
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Compute] Error:', message)
setError(message)
setRetry(true)
LoggerInstance.error(`[compute] ${error.message} `)
} finally {
setIsOrdering(false)
}

View File

@ -1,6 +1,7 @@
import {
ComputeResultType,
downloadFileBrowser,
getErrorMessage,
LoggerInstance,
Provider
} from '@oceanprotocol/lib'
@ -13,6 +14,7 @@ import content from '../../../../../content/pages/history.json'
import { useCancelToken } from '@hooks/useCancelToken'
import { getAsset } from '@utils/aquarius'
import { useAccount, useSigner } from 'wagmi'
import { toast } from 'react-toastify'
export default function Results({
job
@ -70,7 +72,9 @@ export default function Results({
)
await downloadFileBrowser(jobResult)
} catch (error) {
LoggerInstance.error(error.message)
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Get c2d results url] Error:', message)
toast.error(message)
}
}

View File

@ -13,7 +13,12 @@ import { Steps } from './Steps'
import { FormPublishData } from './_types'
import { useUserPreferences } from '@context/UserPreferences'
import useNftFactory from '@hooks/useNftFactory'
import { ProviderInstance, LoggerInstance, DDO } from '@oceanprotocol/lib'
import {
ProviderInstance,
LoggerInstance,
DDO,
getErrorMessage
} from '@oceanprotocol/lib'
import { getOceanConfig } from '@utils/ocean'
import { validationSchema } from './_validation'
import { useAbortController } from '@hooks/useAbortController'
@ -134,12 +139,18 @@ export default function PublishPage({
setDdo(ddo)
LoggerInstance.log('[publish] Got new DDO', ddo)
const ddoEncrypted = await ProviderInstance.encrypt(
ddo,
ddo.chainId,
values.services[0].providerUrl.url,
newAbortController()
)
let ddoEncrypted: string
try {
ddoEncrypted = await ProviderInstance.encrypt(
ddo,
ddo.chainId,
values.services[0].providerUrl.url,
newAbortController()
)
} catch (error) {
const message = getErrorMessage(JSON.parse(error.message))
LoggerInstance.error('[Provider Encrypt] Error:', message)
}
if (!ddoEncrypted)
throw new Error('No encrypted DDO received. Please try again.')