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

Merge branch 'main' into orbis

This commit is contained in:
marcoelissa 2022-11-20 14:35:43 +07:00
commit 728f79cf69
24 changed files with 346 additions and 102 deletions

View File

@ -3,6 +3,7 @@ import { assetAquarius } from './assetAquarius'
export const asset: AssetExtended = {
...assetAquarius,
accessDetails: {
templateId: 1,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:

View File

@ -73,6 +73,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 1,
type: 'NOT_SUPPORTED'
} as any
},
@ -159,6 +160,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 1,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:
@ -252,6 +254,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 2,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:
@ -345,6 +348,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 1,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:
@ -444,6 +448,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 1,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:
@ -468,6 +473,7 @@ export const assets: AssetExtended[] = [
{
'@context': ['https://w3id.org/did/v1'],
accessDetails: {
templateId: 1,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:
@ -660,6 +666,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 1,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:
@ -753,6 +760,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 1,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:
@ -843,6 +851,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 1,
publisherMarketOrderFee: '0',
type: 'free',
addressOrId: '0x0a81f1c69e5428067e6124817c7affe8bc0adf9f',
@ -927,6 +936,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 1,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:
@ -1015,6 +1025,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 2,
publisherMarketOrderFee: '0',
type: 'free',
addressOrId: '0x772224c2c2bddb88a55b3905aaaf8c7188b02ce3',
@ -1099,6 +1110,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 2,
publisherMarketOrderFee: '0',
type: 'free',
addressOrId: '0x89a0170556bb80438081d69f43d8c07a90e9aa24',
@ -1181,6 +1193,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 2,
publisherMarketOrderFee: '0',
type: 'free',
addressOrId: '0xad42c7afee47140b5cd87f05d5846c418145f43a',
@ -1335,6 +1348,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 2,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:
@ -1423,6 +1437,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 2,
publisherMarketOrderFee: '0',
type: 'free',
addressOrId: '0x23c1fd10dadcaf558fb7173b79cfd0d867568a3d',
@ -1514,6 +1529,7 @@ export const assets: AssetExtended[] = [
},
version: '4.1.0',
accessDetails: {
templateId: 2,
publisherMarketOrderFee: '0',
type: 'fixed',
addressOrId:

View File

@ -18,6 +18,7 @@ module.exports = {
infuraProjectId: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID || 'xxx',
defaultDatatokenTemplateIndex: 2,
// The ETH address the marketplace fee will be sent to.
marketFeeAddress:
process.env.NEXT_PUBLIC_MARKET_FEE_ADDRESS ||

View File

@ -66,6 +66,13 @@
"type": "tags",
"placeholder": "e.g. logistics",
"required": false
},
{
"name": "paymentCollector",
"label": "Payment Collector Address",
"placeholder": "e.g. 0X123ABC...",
"help": "This address will receive the revenue from all sales. More info available in our [docs](https://docs.oceanprotocol.com/core-concepts/datanft-and-datatoken#revenue).",
"required": false
}
]
}

View File

@ -10,6 +10,7 @@ export interface AppConfig {
infuraProjectId: string
chainIds: number[]
chainIdsSupported: number[]
defaultDatatokenTemplateIndex: number
marketFeeAddress: string
publisherMarketOrderFee: string
publisherMarketFixedSwapFee: string

View File

@ -39,6 +39,7 @@ declare global {
interface AccessDetails {
type: 'fixed' | 'free' | 'NOT_SUPPORTED'
price: string
templateId: number
addressOrId: string
baseToken: TokenInfo
datatoken: TokenInfo

View File

@ -30,6 +30,7 @@ const tokensPriceQuery = gql`
publishMarketFeeAddress
publishMarketFeeToken
publishMarketFeeAmount
templateId
orders(
where: { payer: $account }
orderBy: createdTimestamp
@ -84,6 +85,7 @@ const tokenPriceQuery = gql`
id
symbol
name
templateId
publishMarketFeeAddress
publishMarketFeeToken
publishMarketFeeAmount
@ -160,7 +162,7 @@ function getAccessDetailsFromTokenPrice(
// the last valid order should be the last reuse order tx id if there is one
accessDetails.validOrderTx = reusedOrder?.tx || order?.tx
}
accessDetails.templateId = tokenPrice.templateId
// TODO: fetch order fee from sub query
accessDetails.publisherMarketOrderFee = tokenPrice?.publishMarketFeeAmount
@ -169,6 +171,7 @@ function getAccessDetailsFromTokenPrice(
const dispenser = tokenPrice.dispensers[0]
accessDetails.type = 'free'
accessDetails.addressOrId = dispenser.token.id
accessDetails.price = '0'
accessDetails.isPurchasable = dispenser.active
accessDetails.datatoken = {

View File

@ -3,6 +3,8 @@ import {
approve,
approveWei,
Datatoken,
Dispenser,
FixedRateExchange,
FreOrderParams,
LoggerInstance,
OrderParams,
@ -83,22 +85,6 @@ export async function order(
switch (asset.accessDetails?.type) {
case 'fixed': {
// this assumes all fees are in ocean
const txApprove = await approve(
web3,
config,
accountId,
asset.accessDetails.baseToken.address,
asset.accessDetails.datatoken.address,
await amountToUnits(
web3,
asset?.accessDetails?.baseToken?.address,
orderPriceAndFees.price
),
false
)
if (!txApprove) {
return
}
const freParams = {
exchangeContract: config.fixedRateExchangeAddress,
@ -109,23 +95,96 @@ export async function order(
swapMarketFee: consumeMarketFixedSwapFee,
marketFeeAddress
} as FreOrderParams
const tx = await datatoken.buyFromFreAndOrder(
asset.accessDetails.datatoken.address,
accountId,
orderParams,
freParams
)
return tx
if (asset.accessDetails.templateId === 1) {
// buy datatoken
const txApprove = await approve(
web3,
config,
accountId,
asset.accessDetails.baseToken.address,
config.fixedRateExchangeAddress,
await amountToUnits(
web3,
asset?.accessDetails?.baseToken?.address,
orderPriceAndFees.price
),
false
)
if (!txApprove) {
return
}
const fre = new FixedRateExchange(config.fixedRateExchangeAddress, web3)
const freTx = await fre.buyDatatokens(
accountId,
asset.accessDetails?.addressOrId,
'1',
orderPriceAndFees.price,
marketFeeAddress,
consumeMarketFixedSwapFee
)
return await datatoken.startOrder(
asset.accessDetails.datatoken.address,
accountId,
orderParams.consumer,
orderParams.serviceIndex,
orderParams._providerFee,
orderParams._consumeMarketFee
)
}
if (asset.accessDetails.templateId === 2) {
const txApprove = await approve(
web3,
config,
accountId,
asset.accessDetails.baseToken.address,
asset.accessDetails.datatoken.address,
await amountToUnits(
web3,
asset?.accessDetails?.baseToken?.address,
orderPriceAndFees.price
),
false
)
if (!txApprove) {
return
}
return await datatoken.buyFromFreAndOrder(
asset.accessDetails.datatoken.address,
accountId,
orderParams,
freParams
)
}
break
}
case 'free': {
const tx = await datatoken.buyFromDispenserAndOrder(
asset.services[0].datatokenAddress,
accountId,
orderParams,
config.dispenserAddress
)
return tx
if (asset.accessDetails.templateId === 1) {
const dispenser = new Dispenser(config.dispenserAddress, web3)
const dispenserTx = await dispenser.dispense(
asset.accessDetails?.datatoken.address,
accountId,
'1',
accountId
)
return await datatoken.startOrder(
asset.accessDetails.datatoken.address,
accountId,
orderParams.consumer,
orderParams.serviceIndex,
orderParams._providerFee,
orderParams._consumeMarketFee
)
}
if (asset.accessDetails.templateId === 2) {
return await datatoken.buyFromDispenserAndOrder(
asset.services[0].datatokenAddress,
accountId,
orderParams,
config.dispenserAddress
)
}
}
}
}

View File

@ -2,23 +2,9 @@ import { gql, OperationResult, TypedDocumentNode, OperationContext } from 'urql'
import { LoggerInstance } from '@oceanprotocol/lib'
import { getUrqlClientInstance } from '@context/UrqlProvider'
import { getOceanConfig } from './ocean'
import { AssetPreviousOrder } from '../@types/subgraph/AssetPreviousOrder'
import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData'
import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery'
const PreviousOrderQuery = gql`
query AssetPreviousOrder($id: String!, $account: String!) {
orders(
first: 1
where: { datatoken: $id, payer: $account }
orderBy: createdTimestamp
orderDirection: desc
) {
createdTimestamp
tx
}
}
`
import appConfig from '../../app.config'
const UserTokenOrders = gql`
query OrdersData($user: String!) {
@ -76,6 +62,11 @@ export function getSubgraphUri(chainId: number): string {
export function getQueryContext(chainId: number): OperationContext {
try {
if (!appConfig.chainIdsSupported.includes(chainId))
throw Object.assign(
new Error('network not supported, query context cancelled')
)
const queryContext: OperationContext = {
url: `${getSubgraphUri(
Number(chainId)

View File

@ -21,6 +21,14 @@ export default function FilesInput(props: InputProps): ReactElement {
? props.form?.values?.services[0].providerUrl.url
: asset.services[0].serviceEndpoint
setIsLoading(true)
// TODO: handled on provider
if (url.includes('drive.google')) {
throw Error(
'Google Drive is not a supported hosting service. Please use an alternative.'
)
}
const checkedFile = await getFileUrlInfo(url, providerUrl)
// error if something's not right from response

View File

@ -2,6 +2,8 @@ import React, { FormEvent, ReactElement } from 'react'
import Button from '../../../@shared/atoms/Button'
import styles from './index.module.css'
import Loader from '../../../@shared/atoms/Loader'
import { useWeb3 } from '@context/Web3'
import Web3 from 'web3'
export interface ButtonBuyProps {
action: 'download' | 'compute'
@ -28,12 +30,11 @@ export interface ButtonBuyProps {
priceType?: string
algorithmPriceType?: string
isAlgorithmConsumable?: boolean
isSupportedOceanNetwork?: boolean
hasProviderFee?: boolean
retry?: boolean
}
// TODO: we need to take a look at these messages
function getConsumeHelpText(
btSymbol: string,
dtBalance: string,
@ -43,12 +44,14 @@ function getConsumeHelpText(
assetType: string,
isConsumable: boolean,
isBalanceSufficient: boolean,
consumableFeedback: string
consumableFeedback: string,
isSupportedOceanNetwork: boolean,
web3: Web3
) {
const text =
isConsumable === false
? consumableFeedback
: hasPreviousOrder
: hasPreviousOrder && web3 && isSupportedOceanNetwork
? `You bought this ${assetType} already allowing you to use it without paying again.`
: hasDatatoken
? `You own ${dtBalance} ${dtSymbol} allowing you to use this dataset by spending 1 ${dtSymbol}, but without paying ${btSymbol} again.`
@ -58,6 +61,35 @@ function getConsumeHelpText(
return text
}
function getAlgoHelpText(
dtSymbolSelectedComputeAsset: string,
dtBalanceSelectedComputeAsset: string,
isConsumable: boolean,
isAlgorithmConsumable: boolean,
hasPreviousOrderSelectedComputeAsset: boolean,
selectedComputeAssetType: string,
hasDatatokenSelectedComputeAsset: boolean,
isBalanceSufficient: boolean,
isSupportedOceanNetwork: boolean,
web3: Web3
) {
const text =
(!dtSymbolSelectedComputeAsset && !dtBalanceSelectedComputeAsset) ||
isConsumable === false ||
isAlgorithmConsumable === false
? ''
: hasPreviousOrderSelectedComputeAsset && web3 && isSupportedOceanNetwork
? `You already bought the selected ${selectedComputeAssetType}, allowing you to use it without paying again.`
: hasDatatokenSelectedComputeAsset
? `You own ${dtBalanceSelectedComputeAsset} ${dtSymbolSelectedComputeAsset} allowing you to use the selected ${selectedComputeAssetType} by spending 1 ${dtSymbolSelectedComputeAsset}, but without paying OCEAN again.`
: web3 && !isSupportedOceanNetwork
? `Connect to the correct network to interact with this asset.`
: isBalanceSufficient === false
? ''
: `Additionally, you will buy 1 ${dtSymbolSelectedComputeAsset} for the ${selectedComputeAssetType} and spend it back to its publisher and pool.`
return text
}
function getComputeAssetHelpText(
hasPreviousOrder: boolean,
hasDatatoken: boolean,
@ -74,6 +106,8 @@ function getComputeAssetHelpText(
dtBalanceSelectedComputeAsset?: string,
selectedComputeAssetType?: string,
isAlgorithmConsumable?: boolean,
isSupportedOceanNetwork?: boolean,
web3?: Web3,
hasProviderFee?: boolean
) {
const computeAssetHelpText = getConsumeHelpText(
@ -85,21 +119,24 @@ function getComputeAssetHelpText(
assetType,
isConsumable,
isBalanceSufficient,
consumableFeedback
consumableFeedback,
isSupportedOceanNetwork,
web3
)
const computeAlgoHelpText = getAlgoHelpText(
dtSymbolSelectedComputeAsset,
dtBalanceSelectedComputeAsset,
isConsumable,
isAlgorithmConsumable,
hasPreviousOrderSelectedComputeAsset,
selectedComputeAssetType,
hasDatatokenSelectedComputeAsset,
isBalanceSufficient,
isSupportedOceanNetwork,
web3
)
const computeAlgoHelpText =
(!dtSymbolSelectedComputeAsset && !dtBalanceSelectedComputeAsset) ||
isConsumable === false ||
isAlgorithmConsumable === false
? ''
: hasPreviousOrderSelectedComputeAsset
? `You already bought the selected ${selectedComputeAssetType}, allowing you to use it without paying again.`
: hasDatatokenSelectedComputeAsset
? `You own ${dtBalanceSelectedComputeAsset} ${dtSymbolSelectedComputeAsset} allowing you to use the selected ${selectedComputeAssetType} by spending 1 ${dtSymbolSelectedComputeAsset}, but without paying ${btSymbol} again.`
: isBalanceSufficient === false
? ''
: `Additionally, you will buy 1 ${dtSymbolSelectedComputeAsset} for the ${selectedComputeAssetType} and spend it back to its publisher.`
const providerFeeHelpText = hasProviderFee
? 'In order to start the job you also need to pay the fees for renting the c2d resources.'
: 'C2D resources required to start the job are available, no payment required for those fees.'
@ -133,8 +170,10 @@ export default function ButtonBuy({
algorithmPriceType,
isAlgorithmConsumable,
hasProviderFee,
retry
retry,
isSupportedOceanNetwork
}: ButtonBuyProps): ReactElement {
const { web3 } = useWeb3()
const buttonText = retry
? 'Retry'
: action === 'download'
@ -177,7 +216,9 @@ export default function ButtonBuy({
assetType,
isConsumable,
isBalanceSufficient,
consumableFeedback
consumableFeedback,
isSupportedOceanNetwork,
web3
)
: getComputeAssetHelpText(
hasPreviousOrder,
@ -195,6 +236,8 @@ export default function ButtonBuy({
dtBalanceSelectedComputeAsset,
selectedComputeAssetType,
isAlgorithmConsumable,
isSupportedOceanNetwork,
web3,
hasProviderFee
)}
</div>

View File

@ -9,7 +9,7 @@ import PriceOutput from './PriceOutput'
import { useAsset } from '@context/Asset'
import { useWeb3 } from '@context/Web3'
import content from '../../../../../content/pages/startComputeDataset.json'
import { Asset } from '@oceanprotocol/lib'
import { Asset, ZERO_ADDRESS } from '@oceanprotocol/lib'
import { getAccessDetails } from '@utils/accessDetailsAndPricing'
import { useMarketMetadata } from '@context/MarketMetadata'
import Alert from '@shared/atoms/Alert'
@ -75,7 +75,7 @@ export default function FormStartCompute({
retry: boolean
}): ReactElement {
const { siteContent } = useMarketMetadata()
const { accountId, balance } = useWeb3()
const { accountId, balance, isSupportedOceanNetwork } = useWeb3()
const { isValid, values }: FormikContextType<{ algorithm: string }> =
useFormikContext()
const { asset, isAssetNetwork } = useAsset()
@ -98,7 +98,7 @@ export default function FormStartCompute({
}
useEffect(() => {
if (!values.algorithm || !accountId || !isConsumable) return
if (!values.algorithm || !isConsumable) return
async function fetchAlgorithmAssetExtended() {
const algorithmAsset = getAlgorithmAsset(values.algorithm)
@ -106,7 +106,7 @@ export default function FormStartCompute({
algorithmAsset.chainId,
algorithmAsset.services[0].datatokenAddress,
algorithmAsset.services[0].timeout,
accountId
accountId || ZERO_ADDRESS // if user is not connected, use ZERO_ADDRESS as accountId
)
const extendedAlgoAsset: AssetExtended = {
...algorithmAsset,
@ -198,15 +198,20 @@ export default function FormStartCompute({
}
setTotalPrices(totalPrices)
}, [
asset?.accessDetails,
selectedAlgorithmAsset?.accessDetails,
asset,
hasPreviousOrder,
hasDatatoken,
hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset,
datasetOrderPriceAndFees,
algoOrderPriceAndFees,
providerFeeAmount
providerFeeAmount,
isAssetNetwork,
selectedAlgorithmAsset?.accessDetails,
datasetOrderPrice,
algoOrderPrice,
algorithmSymbol,
datasetSymbol
])
useEffect(() => {
@ -216,12 +221,13 @@ export default function FormStartCompute({
setIsBalanceSufficient(false)
return
}
// if one comparison of baseTokenBalance and token price comparison is false then the state will be false
setIsBalanceSufficient(
isBalanceSufficient && compareAsBN(baseTokenBalance, `${price.value}`)
baseTokenBalance && compareAsBN(baseTokenBalance, `${price.value}`)
)
})
}, [balance, dtBalance, datasetSymbol, algorithmSymbol])
}, [balance, dtBalance, datasetSymbol, algorithmSymbol, totalPrices])
return (
<Form className={styles.form}>
@ -295,6 +301,7 @@ export default function FormStartCompute({
isAlgorithmConsumable={
selectedAlgorithmAsset?.accessDetails?.isPurchasable
}
isSupportedOceanNetwork={isSupportedOceanNetwork}
hasProviderFee={providerFeeAmount && providerFeeAmount !== '0'}
retry={retry}
/>

View File

@ -5,6 +5,7 @@ import Tooltip from '@shared/atoms/Tooltip'
import styles from './PriceOutput.module.css'
import { MAX_DECIMALS } from '@utils/constants'
import Decimal from 'decimal.js'
import { useWeb3 } from '@context/Web3'
interface PriceOutputProps {
hasPreviousOrder: boolean
@ -40,13 +41,21 @@ function Row({
sign?: string
type?: string
}) {
const { isSupportedOceanNetwork } = useWeb3()
return (
<div className={styles.priceRow}>
<div className={styles.sign}>{sign}</div>
<div className={styles.type}>{type}</div>
<div>
<PriceUnit
price={hasPreviousOrder || hasDatatoken ? 0 : Number(price)}
price={
!isSupportedOceanNetwork
? hasPreviousOrder || hasDatatoken
? 0
: Number(price)
: Number(price)
}
symbol={symbol}
size="small"
className={styles.price}

View File

@ -45,6 +45,7 @@ import { getComputeFeedback } from '@utils/feedback'
import { getDummyWeb3 } from '@utils/web3'
import { initializeProviderForCompute } from '@utils/provider'
import { useUserPreferences } from '@context/UserPreferences'
import { useAsset } from '@context/Asset'
const refreshInterval = 10000 // 10 sec.
export default function Compute({
@ -60,8 +61,10 @@ export default function Compute({
fileIsLoading?: boolean
consumableFeedback?: string
}): ReactElement {
const { accountId, web3 } = useWeb3()
const { accountId, web3, isSupportedOceanNetwork } = useWeb3()
const { chainIds } = useUserPreferences()
const { isAssetNetwork } = useAsset()
const newAbortController = useAbortController()
const newCancelToken = useCancelToken()
@ -116,7 +119,7 @@ export default function Compute({
const datatokenInstance = new Datatoken(web3)
const dtBalance = await datatokenInstance.balance(
asset?.services[0].datatokenAddress,
accountId
accountId || ZERO_ADDRESS // if the user is not connected, we use ZERO_ADDRESS as accountId
)
setAlgorithmDTBalance(new Decimal(dtBalance).toString())
const hasAlgoDt = Number(dtBalance) >= 1
@ -134,9 +137,10 @@ export default function Compute({
const initializedProvider = await initializeProviderForCompute(
asset,
selectedAlgorithmAsset,
accountId,
accountId || ZERO_ADDRESS, // if the user is not connected, we use ZERO_ADDRESS as accountId
computeEnv
)
if (
!initializedProvider ||
!initializedProvider?.datasets ||
@ -145,13 +149,17 @@ export default function Compute({
throw new Error(`Error initializing provider for the compute job!`)
setInitializedProviderResponse(initializedProvider)
setProviderFeeAmount(
await unitsToAmount(
web3,
initializedProvider?.datasets?.[0]?.providerFee?.providerFeeToken,
initializedProvider?.datasets?.[0]?.providerFee?.providerFeeAmount
)
const feeAmount = await unitsToAmount(
!isSupportedOceanNetwork || !isAssetNetwork
? await getDummyWeb3(asset?.chainId)
: web3,
initializedProvider?.datasets?.[0]?.providerFee?.providerFeeToken,
initializedProvider?.datasets?.[0]?.providerFee?.providerFeeAmount
)
setProviderFeeAmount(feeAmount)
const computeDuration = (
parseInt(initializedProvider?.datasets?.[0]?.providerFee?.validUntil) -
Math.floor(Date.now() / 1000)
@ -217,7 +225,7 @@ export default function Compute({
}, [asset?.accessDetails, accountId, isUnsupportedPricing])
useEffect(() => {
if (!selectedAlgorithmAsset?.accessDetails || !accountId) return
if (!selectedAlgorithmAsset?.accessDetails) return
setIsRequestingAlgoOrderPrice(true)
setIsConsumableAlgorithmPrice(
@ -306,6 +314,7 @@ export default function Compute({
documentId: selectedAlgorithmAsset.id,
serviceId: selectedAlgorithmAsset.services[0].id
}
const allowed = await isOrderable(
asset,
computeService.id,
@ -450,14 +459,12 @@ export default function Compute({
setSelectedAlgorithm={setSelectedAlgorithmAsset}
isLoading={isOrdering || isRequestingAlgoOrderPrice}
isComputeButtonDisabled={isComputeButtonDisabled}
hasPreviousOrder={validOrderTx !== undefined}
hasPreviousOrder={!!validOrderTx}
hasDatatoken={hasDatatoken}
dtBalance={dtBalance}
assetType={asset?.metadata.type}
assetTimeout={secondsToString(asset?.services[0].timeout)}
hasPreviousOrderSelectedComputeAsset={
validAlgorithmOrderTx !== undefined
}
hasPreviousOrderSelectedComputeAsset={!!validAlgorithmOrderTx}
hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken}
datasetSymbol={asset?.accessDetails?.baseToken?.symbol || 'OCEAN'}
algorithmSymbol={

View File

@ -33,7 +33,7 @@ export default function Download({
fileIsLoading?: boolean
consumableFeedback?: string
}): ReactElement {
const { accountId, web3 } = useWeb3()
const { accountId, web3, isSupportedOceanNetwork } = useWeb3()
const { getOpcFeeForToken } = useMarketMetadata()
const { isInPurgatory, isAssetNetwork } = useAsset()
const isMounted = useIsMounted()
@ -184,6 +184,7 @@ export default function Download({
isBalanceSufficient={isBalanceSufficient}
consumableFeedback={consumableFeedback}
retry={retry}
isSupportedOceanNetwork={isSupportedOceanNetwork}
/>
)

View File

@ -1,12 +1,29 @@
import React, { ReactElement } from 'react'
import React, { ReactElement, useState, useEffect } from 'react'
import MetaItem from './MetaItem'
import styles from './MetaFull.module.css'
import Publisher from '@shared/Publisher'
import { useAsset } from '@context/Asset'
import { Asset } from '@oceanprotocol/lib'
import { useWeb3 } from '@context/Web3'
import { Asset, Datatoken, LoggerInstance } from '@oceanprotocol/lib'
export default function MetaFull({ ddo }: { ddo: Asset }): ReactElement {
const [paymentCollector, setPaymentCollector] = useState<string>()
const { isInPurgatory } = useAsset()
const { web3 } = useWeb3()
useEffect(() => {
async function getInitialPaymentCollector() {
try {
const datatoken = new Datatoken(web3)
setPaymentCollector(
await datatoken.getPaymentCollector(ddo.datatokens[0].address)
)
} catch (error) {
LoggerInstance.error('[MetaFull: getInitialPaymentCollector]', error)
}
}
getInitialPaymentCollector()
}, [ddo, web3])
function DockerImage() {
const containerInfo = ddo?.metadata?.algorithm?.container
@ -23,6 +40,12 @@ export default function MetaFull({ ddo }: { ddo: Asset }): ReactElement {
title="Owner"
content={<Publisher account={ddo?.nft?.owner} />}
/>
{paymentCollector && paymentCollector !== ddo?.nft?.owner && (
<MetaItem
title="Revenue Sent To"
content={<Publisher account={paymentCollector} />}
/>
)}
{ddo?.metadata?.type === 'algorithm' && ddo?.metadata?.algorithm && (
<MetaItem title="Docker Image" content={<DockerImage />} />

View File

@ -1,11 +1,12 @@
import React, { ReactElement, useState } from 'react'
import React, { ReactElement, useState, useEffect } from 'react'
import { Formik } from 'formik'
import {
LoggerInstance,
Metadata,
FixedRateExchange,
Asset,
Service
Service,
Datatoken
} from '@oceanprotocol/lib'
import { validationSchema } from './_validation'
import { getInitialValues } from './_constants'
@ -36,10 +37,28 @@ export default function Edit({
const { accountId, web3 } = useWeb3()
const newAbortController = useAbortController()
const [success, setSuccess] = useState<string>()
const [paymentCollector, setPaymentCollector] = useState<string>()
const [error, setError] = useState<string>()
const isComputeType = asset?.services[0]?.type === 'compute'
const hasFeedback = error || success
useEffect(() => {
async function getInitialPaymentCollector() {
try {
const datatoken = new Datatoken(web3)
setPaymentCollector(
await datatoken.getPaymentCollector(asset?.datatokens[0].address)
)
} catch (error) {
LoggerInstance.error(
'[EditMetadata: getInitialPaymentCollector]',
error
)
}
}
getInitialPaymentCollector()
}, [asset, web3])
async function updateFixedPrice(newPrice: string) {
const config = getOceanConfig(asset.chainId)
@ -81,6 +100,15 @@ export default function Edit({
values.price !== asset.accessDetails.price &&
(await updateFixedPrice(values.price))
if (values.paymentCollector !== paymentCollector) {
const datatoken = new Datatoken(web3)
await datatoken.setPaymentCollector(
asset?.datatokens[0].address,
accountId,
values.paymentCollector
)
}
if (values.files[0]?.url) {
const file = {
nftAddress: asset.nftAddress,
@ -147,7 +175,8 @@ export default function Edit({
initialValues={getInitialValues(
asset?.metadata,
asset?.services[0]?.timeout,
asset?.accessDetails?.price
asset?.accessDetails?.price,
paymentCollector
)}
validationSchema={validationSchema}
onSubmit={async (values, { resetForm }) => {

View File

@ -0,0 +1,15 @@
import { render, screen } from '@testing-library/react'
import React from 'react'
import { useFormikContext } from 'formik'
import FormActions from './FormActions'
jest.mock('formik')
describe('src/components/Asset/Edit/FormActions.tsx', () => {
it('renders fixed price', () => {
const isValid = true
;(useFormikContext as jest.Mock).mockReturnValue([isValid])
render(<FormActions />)
expect(screen.getByText('Submit')).toBeInTheDocument()
})
})

View File

@ -1,5 +1,5 @@
import React, { ReactElement, useEffect } from 'react'
import { Field, Form, useField, useFormikContext } from 'formik'
import { Field, Form, useFormikContext } from 'formik'
import Input, { InputProps } from '@shared/FormInput'
import FormActions from './FormActions'
import { useAsset } from '@context/Asset'
@ -60,7 +60,16 @@ export default function FormEditMetadata({
asset?.metadata?.links?.[0] &&
getFileUrlInfo(asset.metadata.links[0], providerUrl).then(
(checkedFile) => {
console.log(checkedFile)
// set valid false if url is using google drive
if (asset.metadata.links[0].includes('drive.google')) {
setFieldValue('links', [
{
url: asset.metadata.links[0],
valid: false
}
])
return
}
// initiate link with values from asset metadata
setFieldValue('links', [
{

View File

@ -5,7 +5,8 @@ import { ComputeEditForm, MetadataEditForm } from './_types'
export function getInitialValues(
metadata: Metadata,
timeout: number,
price: string
price: string,
paymentCollector: string
): Partial<MetadataEditForm> {
return {
name: metadata?.name,
@ -15,7 +16,8 @@ export function getInitialValues(
files: [{ url: '', type: '' }],
timeout: secondsToString(timeout),
author: metadata?.author,
tags: metadata?.tags
tags: metadata?.tags,
paymentCollector
}
}

View File

@ -3,6 +3,7 @@ export interface MetadataEditForm {
name: string
description: string
timeout: string
paymentCollector: string
price?: string
files: FileInfo[]
links?: FileInfo[]

View File

@ -1,5 +1,6 @@
import { FileInfo } from '@oceanprotocol/lib'
import * as Yup from 'yup'
import web3 from 'web3'
export const validationSchema = Yup.object().shape({
name: Yup.string()
@ -41,7 +42,14 @@ export const validationSchema = Yup.object().shape({
.nullable(),
timeout: Yup.string().required('Required'),
author: Yup.string().nullable(),
tags: Yup.array<string[]>().nullable()
tags: Yup.array<string[]>().nullable(),
paymentCollector: Yup.string().test(
'ValidAddress',
'Must be a valid Ethereum Address.',
(value) => {
return web3.utils.isAddress(value)
}
)
})
export const computeSettingsValidationSchema = Yup.object().shape({

View File

@ -17,6 +17,7 @@ export default function Preview(): ReactElement {
asset.accessDetails = {
type: values.pricing.type,
addressOrId: ZERO_ADDRESS,
templateId: 1,
price: `${values.pricing.price}`,
baseToken: {
address: ZERO_ADDRESS,

View File

@ -23,7 +23,8 @@ import { FormPublishData, MetadataAlgorithmContainer } from './_types'
import {
marketFeeAddress,
publisherMarketOrderFee,
publisherMarketFixedSwapFee
publisherMarketFixedSwapFee,
defaultDatatokenTemplateIndex
} from '../../../app.config'
import { sanitizeUrl } from '@utils/url'
import { getContainerChecksum } from '@utils/docker'
@ -211,7 +212,7 @@ export async function createTokensAndPricing(
// TODO: cap is hardcoded for now to 1000, this needs to be discussed at some point
const ercParams: DatatokenCreateParams = {
templateIndex: 2,
templateIndex: defaultDatatokenTemplateIndex,
minter: accountId,
paymentCollector: accountId,
mpFeeAddress: marketFeeAddress,