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

Merge branch 'main' into fix/issue-1069-c2d-unsupported-networks

This commit is contained in:
EnzoVezzaro 2022-11-04 06:49:33 -04:00
commit 983d460614
27 changed files with 8743 additions and 1037 deletions

View File

@ -14,7 +14,7 @@ module.exports = {
chainIds: [1, 137, 56, 246, 1285], chainIds: [1, 137, 56, 246, 1285],
// List of all supported chainIds. Used to populate the Chains user preferences list. // List of all supported chainIds. Used to populate the Chains user preferences list.
chainIdsSupported: [1, 137, 56, 246, 1285, 5, 80001, 1287], chainIdsSupported: [1, 137, 56, 246, 1285, 5, 80001],
infuraProjectId: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID || 'xxx', infuraProjectId: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID || 'xxx',

9329
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -26,13 +26,13 @@
"@coingecko/cryptoformat": "^0.5.4", "@coingecko/cryptoformat": "^0.5.4",
"@loadable/component": "^5.15.2", "@loadable/component": "^5.15.2",
"@oceanprotocol/art": "^3.2.0", "@oceanprotocol/art": "^3.2.0",
"@oceanprotocol/lib": "^2.2.3", "@oceanprotocol/lib": "^2.4.0",
"@oceanprotocol/typographies": "^0.1.0", "@oceanprotocol/typographies": "^0.1.0",
"@oceanprotocol/use-dark-mode": "^2.4.3", "@oceanprotocol/use-dark-mode": "^2.4.3",
"@tippyjs/react": "^4.2.6", "@tippyjs/react": "^4.2.6",
"@urql/exchange-refocus": "^1.0.0", "@urql/exchange-refocus": "^1.0.0",
"@walletconnect/web3-provider": "^1.8.0", "@walletconnect/web3-provider": "^1.8.0",
"axios": "^0.27.2", "axios": "^1.1.3",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
"decimal.js": "^10.3.1", "decimal.js": "^10.3.1",
@ -74,9 +74,9 @@
"devDependencies": { "devDependencies": {
"@storybook/addon-essentials": "^6.5.12", "@storybook/addon-essentials": "^6.5.12",
"@storybook/builder-webpack5": "^6.5.12", "@storybook/builder-webpack5": "^6.5.12",
"@storybook/manager-webpack5": "^6.5.12", "@storybook/manager-webpack5": "^6.5.13",
"@storybook/react": "^6.5.12", "@storybook/react": "^6.5.13",
"@svgr/webpack": "^6.3.1", "@svgr/webpack": "^6.5.1",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@types/js-cookie": "^3.0.2", "@types/js-cookie": "^3.0.2",
@ -104,7 +104,7 @@
"https-browserify": "^1.0.0", "https-browserify": "^1.0.0",
"husky": "^8.0.1", "husky": "^8.0.1",
"jest": "^29.1.2", "jest": "^29.1.2",
"jest-environment-jsdom": "^29.0.3", "jest-environment-jsdom": "^29.2.2",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"pretty-quick": "^3.1.3", "pretty-quick": "^3.1.3",
"process": "^0.11.10", "process": "^0.11.10",

View File

@ -77,8 +77,8 @@ function AssetProvider({
return return
} }
if (asset.nft.state) { if ([1, 2, 3].includes(asset.nft.state)) {
// handle nft states as documented in https://docs.oceanprotocol.com/concepts/did-ddo/#state // handle nft states as documented in https://docs.oceanprotocol.com/core-concepts/did-ddo/#state
let state let state
switch (asset.nft.state) { switch (asset.nft.state) {
case 1: case 1:
@ -120,7 +120,7 @@ function AssetProvider({
// Helper: Get and set asset access details // Helper: Get and set asset access details
// ----------------------------------- // -----------------------------------
const fetchAccessDetails = useCallback(async (): Promise<void> => { const fetchAccessDetails = useCallback(async (): Promise<void> => {
if (!asset?.chainId || !asset?.services) return if (!asset?.chainId || !asset?.services?.length) return
const accessDetails = await getAccessDetails( const accessDetails = await getAccessDetails(
asset.chainId, asset.chainId,

View File

@ -29,6 +29,7 @@ interface ProfileProviderValue {
downloadsTotal: number downloadsTotal: number
isDownloadsLoading: boolean isDownloadsLoading: boolean
sales: number sales: number
ownAccount: boolean
} }
const ProfileContext = createContext({} as ProfileProviderValue) const ProfileContext = createContext({} as ProfileProviderValue)
@ -46,17 +47,18 @@ const clearedProfile: Profile = {
function ProfileProvider({ function ProfileProvider({
accountId, accountId,
accountEns, accountEns,
ownAccount,
children children
}: { }: {
accountId: string accountId: string
accountEns: string accountEns: string
ownAccount: boolean
children: ReactNode children: ReactNode
}): ReactElement { }): ReactElement {
const { chainIds } = useUserPreferences() const { chainIds } = useUserPreferences()
const { appConfig } = useMarketMetadata() const { appConfig } = useMarketMetadata()
const [isEthAddress, setIsEthAddress] = useState<boolean>() const [isEthAddress, setIsEthAddress] = useState<boolean>()
// //
// Do nothing in all following effects // Do nothing in all following effects
// when accountId is no ETH address // when accountId is no ETH address
@ -111,7 +113,8 @@ function ProfileProvider({
const result = await getPublishedAssets( const result = await getPublishedAssets(
accountId, accountId,
chainIds, chainIds,
cancelTokenSource.token cancelTokenSource.token,
ownAccount
) )
setAssets(result.results) setAssets(result.results)
setAssetsTotal(result.totalResults) setAssetsTotal(result.totalResults)
@ -134,7 +137,13 @@ function ProfileProvider({
return () => { return () => {
cancelTokenSource.cancel() cancelTokenSource.cancel()
} }
}, [accountId, appConfig.metadataCacheUri, chainIds, isEthAddress]) }, [
accountId,
appConfig.metadataCacheUri,
chainIds,
isEthAddress,
ownAccount
])
// //
// DOWNLOADS // DOWNLOADS
@ -154,11 +163,13 @@ function ProfileProvider({
for (let i = 0; i < tokenOrders?.length; i++) { for (let i = 0; i < tokenOrders?.length; i++) {
dtList.push(tokenOrders[i].datatoken.address) dtList.push(tokenOrders[i].datatoken.address)
} }
const downloads = await getDownloadAssets( const downloads = await getDownloadAssets(
dtList, dtList,
tokenOrders, tokenOrders,
chainIds, chainIds,
cancelToken cancelToken,
ownAccount
) )
setDownloads(downloads) setDownloads(downloads)
setDownloadsTotal(downloads.length) setDownloadsTotal(downloads.length)
@ -167,7 +178,7 @@ function ProfileProvider({
downloads downloads
) )
}, },
[accountId, chainIds] [accountId, chainIds, ownAccount]
) )
useEffect(() => { useEffect(() => {
@ -230,6 +241,7 @@ function ProfileProvider({
downloads, downloads,
downloadsTotal, downloadsTotal,
isDownloadsLoading, isDownloadsLoading,
ownAccount,
sales sales
}} }}
> >

View File

@ -27,4 +27,9 @@ declare global {
computeJobs: ComputeJobMetaData[] computeJobs: ComputeJobMetaData[]
isLoaded: boolean isLoaded: boolean
} }
interface totalPriceMap {
value: string
symbol: string
}
} }

View File

@ -12,4 +12,5 @@ interface BaseQueryParams {
aggs?: any aggs?: any
filters?: FilterTerm[] filters?: FilterTerm[]
ignorePurgatory?: boolean ignorePurgatory?: boolean
ignoreState?: boolean
} }

View File

@ -59,7 +59,22 @@ export function generateBaseQuery(
getFilterTerm('_index', 'aquarius'), getFilterTerm('_index', 'aquarius'),
...(baseQueryParams.ignorePurgatory ...(baseQueryParams.ignorePurgatory
? [] ? []
: [getFilterTerm('purgatory.state', false)]) : [getFilterTerm('purgatory.state', false)]),
...(baseQueryParams.ignoreState
? []
: [
{
bool: {
must_not: [
{
term: {
'nft.state': 5
}
}
]
}
}
])
] ]
} }
} }
@ -177,7 +192,7 @@ export async function getAssetsFromDidList(
cancelToken: CancelToken cancelToken: CancelToken
): Promise<PagedAssets> { ): Promise<PagedAssets> {
try { try {
if (!(didList.length > 0)) return if (!didList.length) return
const baseParams = { const baseParams = {
chainIds, chainIds,
@ -199,7 +214,7 @@ export async function getAssetsFromDtList(
cancelToken: CancelToken cancelToken: CancelToken
): Promise<Asset[]> { ): Promise<Asset[]> {
try { try {
if (!(dtList.length > 0)) return if (!dtList.length) return
const baseParams = { const baseParams = {
chainIds, chainIds,
@ -304,6 +319,7 @@ export async function getPublishedAssets(
accountId: string, accountId: string,
chainIds: number[], chainIds: number[],
cancelToken: CancelToken, cancelToken: CancelToken,
ignoreState = false,
page?: number, page?: number,
type?: string, type?: string,
accesType?: string accesType?: string
@ -332,6 +348,7 @@ export async function getPublishedAssets(
} }
}, },
ignorePurgatory: true, ignorePurgatory: true,
ignoreState,
esPaginationOptions: { esPaginationOptions: {
from: (Number(page) - 1 || 0) * 9, from: (Number(page) - 1 || 0) * 9,
size: 9 size: 9
@ -445,14 +462,17 @@ export async function getDownloadAssets(
dtList: string[], dtList: string[],
tokenOrders: OrdersData[], tokenOrders: OrdersData[],
chainIds: number[], chainIds: number[],
cancelToken: CancelToken cancelToken: CancelToken,
ignoreState = false
): Promise<DownloadedAsset[]> { ): Promise<DownloadedAsset[]> {
const baseQueryparams = { const baseQueryparams = {
chainIds, chainIds,
filters: [ filters: [
getFilterTerm('services.datatokenAddress', dtList), getFilterTerm('services.datatokenAddress', dtList),
getFilterTerm('services.type', 'access') getFilterTerm('services.type', 'access')
] ],
ignorePurgatory: true,
ignoreState
} as BaseQueryParams } as BaseQueryParams
const query = generateBaseQuery(baseQueryparams) const query = generateBaseQuery(baseQueryparams)
try { try {

View File

@ -1,18 +1,13 @@
import { LoggerInstance, Dispenser, Datatoken } from '@oceanprotocol/lib' import { LoggerInstance, Datatoken } from '@oceanprotocol/lib'
import Web3 from 'web3' import Web3 from 'web3'
import { TransactionReceipt } from 'web3-core' import { TransactionReceipt } from 'web3-core'
export async function setMinterToPublisher( export async function setMinterToPublisher(
web3: Web3, web3: Web3,
dispenserAddress: string,
datatokenAddress: string, datatokenAddress: string,
accountId: string, accountId: string,
setError: (msg: string) => void setError: (msg: string) => void
): Promise<TransactionReceipt> { ): Promise<TransactionReceipt> {
const dispenserInstance = new Dispenser(dispenserAddress, web3)
const status = await dispenserInstance.status(datatokenAddress)
if (!status?.active) return
const datatokenInstance = new Datatoken(web3) const datatokenInstance = new Datatoken(web3)
const response = await datatokenInstance.removeMinter( const response = await datatokenInstance.removeMinter(
@ -20,6 +15,7 @@ export async function setMinterToPublisher(
accountId, accountId,
accountId accountId
) )
if (!response) { if (!response) {
setError('Updating DDO failed.') setError('Updating DDO failed.')
LoggerInstance.error('Failed at cancelMinter') LoggerInstance.error('Failed at cancelMinter')

View File

@ -6,7 +6,8 @@ import {
FileInfo, FileInfo,
LoggerInstance, LoggerInstance,
ProviderComputeInitializeResults, ProviderComputeInitializeResults,
ProviderInstance ProviderInstance,
UrlFile
} from '@oceanprotocol/lib' } from '@oceanprotocol/lib'
import Web3 from 'web3' import Web3 from 'web3'
import { getValidUntilTime } from './compute' import { getValidUntilTime } from './compute'
@ -87,7 +88,13 @@ export async function getFileUrlInfo(
providerUrl: string providerUrl: string
): Promise<FileInfo[]> { ): Promise<FileInfo[]> {
try { try {
const response = await ProviderInstance.checkFileUrl(url, providerUrl) const fileUrl: UrlFile = {
type: 'url',
index: 0,
url,
method: 'get'
}
const response = await ProviderInstance.getFileInfo(fileUrl, providerUrl)
return response return response
} catch (error) { } catch (error) {
LoggerInstance.error(error.message) LoggerInstance.error(error.message)

View File

@ -58,7 +58,7 @@ export default function AssetTeaser({
{removeMarkdown(description?.substring(0, 300) || '')} {removeMarkdown(description?.substring(0, 300) || '')}
</Dotdotdot> </Dotdotdot>
</div> </div>
{isUnsupportedPricing ? ( {isUnsupportedPricing || !asset.services.length ? (
<strong>No pricing schema available</strong> <strong>No pricing schema available</strong>
) : ( ) : (
<Price accessDetails={asset.accessDetails} size="small" /> <Price accessDetails={asset.accessDetails} size="small" />

View File

@ -30,9 +30,9 @@ export default function FileIcon({
return ( return (
<ul className={styleClasses}> <ul className={styleClasses}>
{!isLoading && file ? ( {!isLoading ? (
<> <>
{file.contentType || file.contentLength ? ( {file?.contentType || file?.contentLength ? (
<> <>
<li>{cleanupContentType(file.contentType)}</li> <li>{cleanupContentType(file.contentType)}</li>
<li> <li>

View File

@ -31,6 +31,11 @@
padding-right: calc(var(--spacer) / 2); padding-right: calc(var(--spacer) / 2);
} }
.hideUrl {
filter: blur(0.2rem);
user-select: none;
}
.warning { .warning {
margin-top: calc(var(--spacer) / 3); margin-top: calc(var(--spacer) / 3);
margin-left: 0; margin-left: 0;

View File

@ -15,9 +15,13 @@ export default function FileInfo({
? cleanupContentType(file.contentType) ? cleanupContentType(file.contentType)
: null : null
const hideUrl = file.type === 'hidden' || false
return ( return (
<div className={styles.info}> <div className={`${styles.info}`}>
<h3 className={styles.url}>{file.url}</h3> <h3 className={`${styles.url} ${hideUrl ? styles.hideUrl : null}`}>
{hideUrl ? 'https://oceanprotocol/placeholder' : file.url}
</h3>
<ul> <ul>
<li className={styles.success}> URL confirmed</li> <li className={styles.success}> URL confirmed</li>
{file.contentLength && <li>{prettySize(+file.contentLength)}</li>} {file.contentLength && <li>{prettySize(+file.contentLength)}</li>}

View File

@ -16,7 +16,7 @@ export default function FilesInput(props: InputProps): ReactElement {
async function handleValidation(e: React.SyntheticEvent, url: string) { async function handleValidation(e: React.SyntheticEvent, url: string) {
// File example 'https://oceanprotocol.com/tech-whitepaper.pdf' // File example 'https://oceanprotocol.com/tech-whitepaper.pdf'
e.preventDefault() e?.preventDefault()
try { try {
const providerUrl = values?.services const providerUrl = values?.services
@ -43,13 +43,14 @@ export default function FilesInput(props: InputProps): ReactElement {
} }
function handleClose() { function handleClose() {
helpers.setValue(meta.initialValue)
helpers.setTouched(false) helpers.setTouched(false)
helpers.setValue(meta.initialValue)
} }
return ( return (
<> <>
{field?.value?.[0]?.valid === true ? ( {field?.value?.[0]?.valid === true ||
field?.value?.[0]?.type === 'hidden' ? (
<FileInfo file={field.value[0]} handleClose={handleClose} /> <FileInfo file={field.value[0]} handleClose={handleClose} />
) : ( ) : (
<UrlInput <UrlInput

View File

@ -11,11 +11,11 @@ import { useWeb3 } from '@context/Web3'
import content from '../../../../../content/pages/startComputeDataset.json' import content from '../../../../../content/pages/startComputeDataset.json'
import { Asset, ZERO_ADDRESS } from '@oceanprotocol/lib' import { Asset, ZERO_ADDRESS } from '@oceanprotocol/lib'
import { getAccessDetails } from '@utils/accessDetailsAndPricing' import { getAccessDetails } from '@utils/accessDetailsAndPricing'
import Decimal from 'decimal.js'
import { MAX_DECIMALS } from '@utils/constants'
import { useMarketMetadata } from '@context/MarketMetadata' import { useMarketMetadata } from '@context/MarketMetadata'
import Alert from '@shared/atoms/Alert' import Alert from '@shared/atoms/Alert'
import { getTokenBalanceFromSymbol } from '@utils/web3' import { getTokenBalanceFromSymbol } from '@utils/web3'
import { MAX_DECIMALS } from '@utils/constants'
import Decimal from 'decimal.js'
export default function FormStartCompute({ export default function FormStartCompute({
algorithms, algorithms,
@ -31,7 +31,8 @@ export default function FormStartCompute({
assetTimeout, assetTimeout,
hasPreviousOrderSelectedComputeAsset, hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset, hasDatatokenSelectedComputeAsset,
oceanSymbol, datasetSymbol,
algorithmSymbol,
dtSymbolSelectedComputeAsset, dtSymbolSelectedComputeAsset,
dtBalanceSelectedComputeAsset, dtBalanceSelectedComputeAsset,
selectedComputeAssetType, selectedComputeAssetType,
@ -57,7 +58,8 @@ export default function FormStartCompute({
assetTimeout: string assetTimeout: string
hasPreviousOrderSelectedComputeAsset?: boolean hasPreviousOrderSelectedComputeAsset?: boolean
hasDatatokenSelectedComputeAsset?: boolean hasDatatokenSelectedComputeAsset?: boolean
oceanSymbol?: string datasetSymbol?: string
algorithmSymbol?: string
dtSymbolSelectedComputeAsset?: string dtSymbolSelectedComputeAsset?: string
dtBalanceSelectedComputeAsset?: string dtBalanceSelectedComputeAsset?: string
selectedComputeAssetType?: string selectedComputeAssetType?: string
@ -76,14 +78,14 @@ export default function FormStartCompute({
useFormikContext() useFormikContext()
const { asset, isAssetNetwork } = useAsset() const { asset, isAssetNetwork } = useAsset()
const [totalPrice, setTotalPrice] = useState('0')
const [datasetOrderPrice, setDatasetOrderPrice] = useState( const [datasetOrderPrice, setDatasetOrderPrice] = useState(
asset?.accessDetails?.price asset?.accessDetails?.price
) )
const [algoOrderPrice, setAlgoOrderPrice] = useState( const [algoOrderPrice, setAlgoOrderPrice] = useState(
selectedAlgorithmAsset?.accessDetails?.price selectedAlgorithmAsset?.accessDetails?.price
) )
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>(false) const [totalPrices, setTotalPrices] = useState([])
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>(true)
function getAlgorithmAsset(algorithmId: string): Asset { function getAlgorithmAsset(algorithmId: string): Asset {
let assetDdo = null let assetDdo = null
@ -119,40 +121,80 @@ export default function FormStartCompute({
useEffect(() => { useEffect(() => {
if (!asset?.accessDetails || !selectedAlgorithmAsset?.accessDetails) return if (!asset?.accessDetails || !selectedAlgorithmAsset?.accessDetails) return
const priceDataset = new Decimal( setDatasetOrderPrice(
isAssetNetwork datasetOrderPriceAndFees?.price || asset.accessDetails.price
? hasPreviousOrder || hasDatatoken )
? 0 setAlgoOrderPrice(
: datasetOrderPriceAndFees?.price || asset.accessDetails.price algoOrderPriceAndFees?.price ||
: datasetOrderPriceAndFees?.price || asset.accessDetails.price selectedAlgorithmAsset?.accessDetails.price
).toDecimalPlaces(MAX_DECIMALS) )
const totalPrices: totalPriceMap[] = []
setDatasetOrderPrice(priceDataset.toString()) const priceDataset =
!datasetOrderPrice || hasPreviousOrder || hasDatatoken
const priceAlgo = new Decimal( ? new Decimal(0)
isAssetNetwork : new Decimal(datasetOrderPrice).toDecimalPlaces(MAX_DECIMALS)
? hasPreviousOrderSelectedComputeAsset || const priceAlgo =
hasDatatokenSelectedComputeAsset !algoOrderPrice ||
? 0 hasPreviousOrderSelectedComputeAsset ||
: algoOrderPriceAndFees?.price || hasDatatokenSelectedComputeAsset
selectedAlgorithmAsset.accessDetails.price ? new Decimal(0)
: algoOrderPriceAndFees?.price || : new Decimal(algoOrderPrice).toDecimalPlaces(MAX_DECIMALS)
selectedAlgorithmAsset.accessDetails.price
).toDecimalPlaces(MAX_DECIMALS)
setAlgoOrderPrice(priceAlgo.toString())
const providerFees = providerFeeAmount const providerFees = providerFeeAmount
? new Decimal(providerFeeAmount).toDecimalPlaces(MAX_DECIMALS) ? new Decimal(providerFeeAmount).toDecimalPlaces(MAX_DECIMALS)
: new Decimal(0) : new Decimal(0)
const totalPrice = priceDataset if (algorithmSymbol === 'OCEAN') {
.plus(priceAlgo) let sum = providerFees.add(priceAlgo)
.plus(providerFees) totalPrices.push({
.toDecimalPlaces(MAX_DECIMALS) value: sum.toDecimalPlaces(MAX_DECIMALS).toString(),
.toString() symbol: algorithmSymbol
})
setTotalPrice(totalPrice) if (algorithmSymbol === datasetSymbol) {
sum = sum.add(priceDataset)
totalPrices[0].value = sum.toDecimalPlaces(MAX_DECIMALS).toString()
} else {
totalPrices.push({
value: priceDataset.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: datasetSymbol
})
}
} else {
if (datasetSymbol === 'OCEAN') {
const sum = providerFees.add(priceDataset)
totalPrices.push({
value: sum.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: datasetSymbol
})
totalPrices.push({
value: priceAlgo.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: algorithmSymbol
})
} else if (datasetSymbol === algorithmSymbol) {
const sum = priceAlgo.add(priceDataset)
totalPrices.push({
value: sum.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: algorithmSymbol
})
totalPrices.push({
value: providerFees.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: 'OCEAN'
})
} else {
totalPrices.push({
value: priceDataset.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: datasetSymbol
})
totalPrices.push({
value: providerFees.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: 'OCEAN'
})
totalPrices.push({
value: priceAlgo.toDecimalPlaces(MAX_DECIMALS).toString(),
symbol: algorithmSymbol
})
}
}
setTotalPrices(totalPrices)
}, [ }, [
asset, asset,
hasPreviousOrder, hasPreviousOrder,
@ -166,18 +208,18 @@ export default function FormStartCompute({
]) ])
useEffect(() => { useEffect(() => {
const baseTokenBalance = getTokenBalanceFromSymbol( totalPrices.forEach((price) => {
balance, const baseTokenBalance = getTokenBalanceFromSymbol(balance, price.symbol)
asset?.accessDetails?.baseToken?.symbol if (!baseTokenBalance) {
) setIsBalanceSufficient(false)
return
if (!totalPrice || !baseTokenBalance || !dtBalance) return }
setIsBalanceSufficient( // if one comparison of baseTokenBalance and token price comparison is false then the state will be false
compareAsBN(baseTokenBalance, `${totalPrice}`) || Number(dtBalance) >= 1 setIsBalanceSufficient(
) isBalanceSufficient && compareAsBN(baseTokenBalance, `${price.value}`)
}, [totalPrice, balance, dtBalance, asset?.accessDetails?.baseToken?.symbol]) )
})
console.log(totalPrice, datasetOrderPrice, algoOrderPrice) }, [balance, dtBalance, datasetSymbol, algorithmSymbol])
return ( return (
<Form className={styles.form}> <Form className={styles.form}>
@ -208,12 +250,13 @@ export default function FormStartCompute({
selectedComputeAssetTimeout={selectedComputeAssetTimeout} selectedComputeAssetTimeout={selectedComputeAssetTimeout}
hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset} hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset}
algorithmConsumeDetails={selectedAlgorithmAsset?.accessDetails} algorithmConsumeDetails={selectedAlgorithmAsset?.accessDetails}
symbol={oceanSymbol} symbol={datasetSymbol}
totalPrice={totalPrice} algorithmSymbol={algorithmSymbol}
datasetOrderPrice={datasetOrderPrice} datasetOrderPrice={datasetOrderPrice}
algoOrderPrice={algoOrderPrice} algoOrderPrice={algoOrderPrice}
providerFeeAmount={providerFeeAmount} providerFeeAmount={providerFeeAmount}
validUntil={validUntil} validUntil={validUntil}
totalPrices={totalPrices}
/> />
<ButtonBuy <ButtonBuy

View File

@ -8,7 +8,6 @@ import Decimal from 'decimal.js'
import { useWeb3 } from '@context/Web3' import { useWeb3 } from '@context/Web3'
interface PriceOutputProps { interface PriceOutputProps {
totalPrice: string
hasPreviousOrder: boolean hasPreviousOrder: boolean
hasDatatoken: boolean hasDatatoken: boolean
symbol: string symbol: string
@ -16,11 +15,13 @@ interface PriceOutputProps {
hasPreviousOrderSelectedComputeAsset: boolean hasPreviousOrderSelectedComputeAsset: boolean
hasDatatokenSelectedComputeAsset: boolean hasDatatokenSelectedComputeAsset: boolean
algorithmConsumeDetails: AccessDetails algorithmConsumeDetails: AccessDetails
algorithmSymbol: string
selectedComputeAssetTimeout: string selectedComputeAssetTimeout: string
datasetOrderPrice?: string datasetOrderPrice?: string
algoOrderPrice?: string algoOrderPrice?: string
providerFeeAmount?: string providerFeeAmount?: string
validUntil?: string validUntil?: string
totalPrices?: totalPriceMap[]
} }
function Row({ function Row({
@ -71,7 +72,6 @@ function Row({
} }
export default function PriceOutput({ export default function PriceOutput({
totalPrice,
hasPreviousOrder, hasPreviousOrder,
hasDatatoken, hasDatatoken,
assetTimeout, assetTimeout,
@ -79,18 +79,30 @@ export default function PriceOutput({
hasPreviousOrderSelectedComputeAsset, hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset, hasDatatokenSelectedComputeAsset,
algorithmConsumeDetails, algorithmConsumeDetails,
algorithmSymbol,
selectedComputeAssetTimeout, selectedComputeAssetTimeout,
datasetOrderPrice, datasetOrderPrice,
algoOrderPrice, algoOrderPrice,
providerFeeAmount, providerFeeAmount,
validUntil validUntil,
totalPrices
}: PriceOutputProps): ReactElement { }: PriceOutputProps): ReactElement {
const { asset } = useAsset() const { asset } = useAsset()
return ( return (
<div className={styles.priceComponent}> <div className={styles.priceComponent}>
You will pay{' '} You will pay{' '}
<PriceUnit price={Number(totalPrice)} symbol={symbol} size="small" /> {totalPrices.map((item, index) => (
<div key={item.symbol}>
<PriceUnit
price={Number(item.value)}
symbol={
index < totalPrices.length - 1 ? `${item.symbol} & ` : item.symbol
}
size="small"
/>
</div>
))}
<Tooltip <Tooltip
content={ content={
<div className={styles.calculation}> <div className={styles.calculation}>
@ -115,18 +127,25 @@ export default function PriceOutput({
.toDecimalPlaces(MAX_DECIMALS) .toDecimalPlaces(MAX_DECIMALS)
.toString()} .toString()}
timeout={selectedComputeAssetTimeout} timeout={selectedComputeAssetTimeout}
symbol={symbol} symbol={algorithmSymbol}
sign="+" sign="+"
type="ALGORITHM" type="ALGORITHM"
/> />
<Row <Row
price={providerFeeAmount} // initializeCompute.provider fee amount price={providerFeeAmount} // initializeCompute.provider fee amount
timeout={`${validUntil} seconds`} // valid until value timeout={`${validUntil} seconds`} // valid until value
symbol={symbol} symbol={'OCEAN'} // we assume that provider fees will always be in OCEAN token
sign="+" sign="+"
type="C2D RESOURCES" type="C2D RESOURCES"
/> />
<Row price={totalPrice} symbol={symbol} sign="=" /> {totalPrices.map((item, index) => (
<Row
price={item.value}
symbol={item.symbol}
sign={index === 0 ? '=' : '&'}
key={item.symbol}
/>
))}
</div> </div>
} }
/> />

View File

@ -465,8 +465,8 @@ export default function Compute({
assetTimeout={secondsToString(asset?.services[0].timeout)} assetTimeout={secondsToString(asset?.services[0].timeout)}
hasPreviousOrderSelectedComputeAsset={!!validAlgorithmOrderTx} hasPreviousOrderSelectedComputeAsset={!!validAlgorithmOrderTx}
hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken} hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken}
oceanSymbol={ datasetSymbol={asset?.accessDetails?.baseToken?.symbol || 'OCEAN'}
asset?.accessDetails?.baseToken?.symbol || algorithmSymbol={
selectedAlgorithmAsset?.accessDetails?.baseToken?.symbol || selectedAlgorithmAsset?.accessDetails?.baseToken?.symbol ||
'OCEAN' 'OCEAN'
} }

View File

@ -45,11 +45,15 @@ export default function Download({
const [isPriceLoading, setIsPriceLoading] = useState(false) const [isPriceLoading, setIsPriceLoading] = useState(false)
const [isOwned, setIsOwned] = useState(false) const [isOwned, setIsOwned] = useState(false)
const [validOrderTx, setValidOrderTx] = useState('') const [validOrderTx, setValidOrderTx] = useState('')
const [isOrderDisabled, setIsOrderDisabled] = useState(false)
const [orderPriceAndFees, setOrderPriceAndFees] = const [orderPriceAndFees, setOrderPriceAndFees] =
useState<OrderPriceAndFees>() useState<OrderPriceAndFees>()
const isUnsupportedPricing = asset?.accessDetails?.type === 'NOT_SUPPORTED' const isUnsupportedPricing = asset?.accessDetails?.type === 'NOT_SUPPORTED'
useEffect(() => {
Number(asset?.nft.state) === 4 && setIsOrderDisabled(true)
}, [asset?.nft.state])
useEffect(() => { useEffect(() => {
if (!asset?.accessDetails || isUnsupportedPricing) return if (!asset?.accessDetails || isUnsupportedPricing) return
@ -169,7 +173,7 @@ export default function Download({
dtSymbol={asset?.datatokens[0]?.symbol} dtSymbol={asset?.datatokens[0]?.symbol}
dtBalance={dtBalance} dtBalance={dtBalance}
onClick={handleOrderOrDownload} onClick={handleOrderOrDownload}
assetTimeout={secondsToString(asset.services[0].timeout)} assetTimeout={secondsToString(asset?.services?.[0]?.timeout)}
assetType={asset?.metadata?.type} assetType={asset?.metadata?.type}
stepText={statusText} stepText={statusText}
isLoading={isLoading} isLoading={isLoading}
@ -184,26 +188,36 @@ export default function Download({
const AssetAction = ({ asset }: { asset: AssetExtended }) => { const AssetAction = ({ asset }: { asset: AssetExtended }) => {
return ( return (
<div> <div>
{isUnsupportedPricing ? ( {isOrderDisabled ? (
<Alert <Alert
className={styles.fieldWarning} className={styles.fieldWarning}
state="info" state="info"
text={`No pricing schema available for this asset.`} text={`The publisher temporarily disabled ordering for this asset`}
/> />
) : ( ) : (
<> <>
{isPriceLoading ? ( {isUnsupportedPricing || !asset.services.length ? (
<Loader message="Calculating full price (including fees)" /> <Alert
) : ( className={styles.fieldWarning}
<Price state="info"
accessDetails={asset.accessDetails} text={`No pricing schema available for this asset.`}
orderPriceAndFees={orderPriceAndFees}
conversion
size="large"
/> />
)} ) : (
<>
{isPriceLoading ? (
<Loader message="Calculating full price (including fees)" />
) : (
<Price
accessDetails={asset.accessDetails}
orderPriceAndFees={orderPriceAndFees}
conversion
size="large"
/>
)}
{!isInPurgatory && <PurchaseButton />} {!isInPurgatory && <PurchaseButton />}
</>
)}
</> </>
)} )}
</div> </div>

View File

@ -30,8 +30,8 @@ export default function MetaAsset({
networkId={asset?.chainId} networkId={asset?.chainId}
path={ path={
isBlockscoutExplorer isBlockscoutExplorer
? `tokens/${asset?.services[0].datatokenAddress}` ? `tokens/${asset?.services?.[0]?.datatokenAddress}`
: `token/${asset?.services[0].datatokenAddress}` : `token/${asset?.services?.[0]?.datatokenAddress}`
} }
> >
{`Accessed with ${dataTokenSymbol}`} {`Accessed with ${dataTokenSymbol}`}

View File

@ -41,10 +41,12 @@ export default function EditComputeDataset({
async function handleSubmit(values: ComputeEditForm, resetForm: () => void) { async function handleSubmit(values: ComputeEditForm, resetForm: () => void) {
try { try {
if (asset?.accessDetails?.type === 'free') { if (
asset?.accessDetails?.type === 'free' &&
asset?.accessDetails?.isPurchasable
) {
const tx = await setMinterToPublisher( const tx = await setMinterToPublisher(
web3, web3,
asset?.accessDetails?.addressOrId,
asset?.accessDetails?.datatoken?.address, asset?.accessDetails?.datatoken?.address,
accountId, accountId,
setError setError

View File

@ -1,8 +1,10 @@
import React, { ReactElement } from 'react' import React, { ReactElement, useEffect } from 'react'
import { Field, Form } from 'formik' import { Field, Form, useField, useFormikContext } from 'formik'
import Input, { InputProps } from '@shared/FormInput' import Input, { InputProps } from '@shared/FormInput'
import FormActions from './FormActions' import FormActions from './FormActions'
import { useAsset } from '@context/Asset' import { useAsset } from '@context/Asset'
import { FormPublishData } from 'src/components/Publish/_types'
import { getFileUrlInfo } from '@utils/provider'
export function checkIfTimeoutInPredefinedValues( export function checkIfTimeoutInPredefinedValues(
timeout: string, timeout: string,
@ -23,7 +25,8 @@ export default function FormEditMetadata({
showPrice: boolean showPrice: boolean
isComputeDataset: boolean isComputeDataset: boolean
}): ReactElement { }): ReactElement {
const { oceanConfig } = useAsset() const { oceanConfig, asset } = useAsset()
const { values, setFieldValue } = useFormikContext<FormPublishData>()
// This component is handled by Formik so it's not rendered like a "normal" react component, // This component is handled by Formik so it's not rendered like a "normal" react component,
// so handleTimeoutCustomOption is called only once. // so handleTimeoutCustomOption is called only once.
@ -41,6 +44,34 @@ export default function FormEditMetadata({
timeoutOptionsArray.push('Forever') timeoutOptionsArray.push('Forever')
} }
useEffect(() => {
// let's initiate files with empty url (we can't access the asset url) with type hidden (for UI frontend)
setFieldValue('files', [
{
url: '',
type: 'hidden'
}
])
const providerUrl = values?.services
? values?.services[0].providerUrl.url
: asset.services[0].serviceEndpoint
// if we have a sample file, we need to get the files' info before setting defaults links value
asset?.metadata?.links?.[0] &&
getFileUrlInfo(asset.metadata.links[0], providerUrl).then(
(checkedFile) => {
console.log(checkedFile)
// initiate link with values from asset metadata
setFieldValue('links', [
{
url: asset.metadata.links[0],
...checkedFile[0]
}
])
}
)
}, [])
return ( return (
<Form> <Form>
{data.map( {data.map(

View File

@ -11,7 +11,7 @@ export function getInitialValues(
name: metadata?.name, name: metadata?.name,
description: metadata?.description, description: metadata?.description,
price, price,
links: metadata?.links as any, links: [{ url: '', type: '' }],
files: [{ url: '', type: '' }], files: [{ url: '', type: '' }],
timeout: secondsToString(timeout), timeout: secondsToString(timeout),
author: metadata?.author, author: metadata?.author,

View File

@ -10,7 +10,15 @@ export const validationSchema = Yup.object().shape({
files: Yup.array<FileInfo[]>() files: Yup.array<FileInfo[]>()
.of( .of(
Yup.object().shape({ Yup.object().shape({
url: Yup.string().url('Must be a valid URL.'), url: Yup.string()
.url('Must be a valid URL.')
.test(
'GoogleNotSupported',
'Google Drive is not a supported hosting service. Please use an alternative.',
(value) => {
return !value?.toString().includes('drive.google')
}
),
valid: Yup.boolean().isTrue() valid: Yup.boolean().isTrue()
}) })
) )
@ -18,7 +26,15 @@ export const validationSchema = Yup.object().shape({
links: Yup.array<FileInfo[]>() links: Yup.array<FileInfo[]>()
.of( .of(
Yup.object().shape({ Yup.object().shape({
url: Yup.string().url('Must be a valid URL.'), url: Yup.string()
.url('Must be a valid URL.')
.test(
'GoogleNotSupported',
'Google Drive is not a supported hosting service. Please use an alternative.',
(value) => {
return !value?.toString().includes('drive.google')
}
),
valid: Yup.boolean().isTrue() valid: Yup.boolean().isTrue()
}) })
) )

View File

@ -8,6 +8,7 @@ import { useCancelToken } from '@hooks/useCancelToken'
import Filters from '../../Search/Filters' import Filters from '../../Search/Filters'
import { useMarketMetadata } from '@context/MarketMetadata' import { useMarketMetadata } from '@context/MarketMetadata'
import { CancelToken } from 'axios' import { CancelToken } from 'axios'
import { useProfile } from '@context/Profile'
export default function PublishedList({ export default function PublishedList({
accountId accountId
@ -16,7 +17,7 @@ export default function PublishedList({
}): ReactElement { }): ReactElement {
const { appConfig } = useMarketMetadata() const { appConfig } = useMarketMetadata()
const { chainIds } = useUserPreferences() const { chainIds } = useUserPreferences()
const { ownAccount } = useProfile()
const [queryResult, setQueryResult] = useState<PagedAssets>() const [queryResult, setQueryResult] = useState<PagedAssets>()
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [page, setPage] = useState<number>(1) const [page, setPage] = useState<number>(1)
@ -39,6 +40,7 @@ export default function PublishedList({
accountId.toLowerCase(), accountId.toLowerCase(),
chainIds, chainIds,
cancelToken, cancelToken,
ownAccount,
page, page,
service, service,
access access
@ -50,7 +52,7 @@ export default function PublishedList({
setIsLoading(false) setIsLoading(false)
} }
}, },
[] [ownAccount]
) )
useEffect(() => { useEffect(() => {

View File

@ -32,7 +32,17 @@ const validationService = {
files: Yup.array<FileInfo[]>() files: Yup.array<FileInfo[]>()
.of( .of(
Yup.object().shape({ Yup.object().shape({
url: Yup.string().url('Must be a valid URL.').required('Required'), url: Yup.string()
.test(
'GoogleNotSupported',
'Google Drive is not a supported hosting service. Please use an alternative.',
(value) => {
return !value?.toString().includes('drive.google')
}
)
.url('Must be a valid URL.')
.required('Required'),
valid: Yup.boolean().isTrue().required('File must be valid.') valid: Yup.boolean().isTrue().required('File must be valid.')
}) })
) )
@ -41,7 +51,15 @@ const validationService = {
links: Yup.array<FileInfo[]>() links: Yup.array<FileInfo[]>()
.of( .of(
Yup.object().shape({ Yup.object().shape({
url: Yup.string().url('Must be a valid URL.'), url: Yup.string()
.url('Must be a valid URL.')
.test(
'GoogleNotSupported',
'Google Drive is not a supported hosting service. Please use an alternative.',
(value) => {
return !value?.toString().includes('drive.google')
}
),
// TODO: require valid file only when URL is given // TODO: require valid file only when URL is given
valid: Yup.boolean() valid: Yup.boolean()
// valid: Yup.boolean().isTrue('File must be valid.') // valid: Yup.boolean().isTrue('File must be valid.')

View File

@ -13,7 +13,7 @@ export default function PageProfile(): ReactElement {
const { accountId, accountEns } = useWeb3() const { accountId, accountEns } = useWeb3()
const [finalAccountId, setFinalAccountId] = useState<string>() const [finalAccountId, setFinalAccountId] = useState<string>()
const [finalAccountEns, setFinalAccountEns] = useState<string>() const [finalAccountEns, setFinalAccountEns] = useState<string>()
const [ownAccount, setOwnAccount] = useState(false)
// Have accountId in path take over, if not present fall back to web3 // Have accountId in path take over, if not present fall back to web3
useEffect(() => { useEffect(() => {
async function init() { async function init() {
@ -23,6 +23,7 @@ export default function PageProfile(): ReactElement {
if (router.asPath === '/profile') { if (router.asPath === '/profile') {
setFinalAccountEns(accountEns) setFinalAccountEns(accountEns)
setFinalAccountId(accountId) setFinalAccountId(accountId)
setOwnAccount(true)
return return
} }
@ -30,6 +31,7 @@ export default function PageProfile(): ReactElement {
// Path has ETH address // Path has ETH address
if (web3.utils.isAddress(pathAccount)) { if (web3.utils.isAddress(pathAccount)) {
setOwnAccount(pathAccount === accountId)
const finalAccountId = pathAccount || accountId const finalAccountId = pathAccount || accountId
setFinalAccountId(finalAccountId) setFinalAccountId(finalAccountId)
@ -45,6 +47,7 @@ export default function PageProfile(): ReactElement {
resolvedAccountId === '0x0000000000000000000000000000000000000000' resolvedAccountId === '0x0000000000000000000000000000000000000000'
) )
return return
setOwnAccount(resolvedAccountId === accountId)
setFinalAccountId(resolvedAccountId) setFinalAccountId(resolvedAccountId)
} }
} }
@ -66,7 +69,11 @@ export default function PageProfile(): ReactElement {
title={accountTruncate(finalAccountId)} title={accountTruncate(finalAccountId)}
noPageHeader noPageHeader
> >
<ProfileProvider accountId={finalAccountId} accountEns={finalAccountEns}> <ProfileProvider
accountId={finalAccountId}
accountEns={finalAccountEns}
ownAccount={ownAccount}
>
<ProfilePage accountId={finalAccountId} /> <ProfilePage accountId={finalAccountId} />
</ProfileProvider> </ProfileProvider>
</Page> </Page>