Refactor pricing and various components that are involved (#1046)

* update

* merge pr #1012

* fix header

* fix

* abort controller

* up next.8

* build fix

* update lock

* fix

* another fix

* ssh fix

* another ssh fix

* remove optional

* order mock

* small cleanup

* fix package

* price updates

* finish getPrice

* fix consume

* fix consume

* getConsumeDetails  comments

* restore functionality after consumeDetails

* fix some compute typings

* more price fetching updates

* 'minor' refactors and fixed build

* package-lock fix

* minor comments

* minor naming changes

* fix

* fix pool badge

* remove console.log
This commit is contained in:
mihaisc 2022-02-03 13:29:39 +02:00 committed by GitHub
parent 77c4ed42cc
commit 57be62a6b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 4998 additions and 5149 deletions

8596
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,6 @@
"@oceanprotocol/typographies": "^0.1.0",
"@portis/web3": "^4.0.6",
"@tippyjs/react": "^4.2.6",
"@urql/introspection": "^0.3.1",
"@walletconnect/web3-provider": "^1.7.1",
"axios": "^0.25.0",
"bignumber.js": "^9.0.2",
@ -36,7 +35,6 @@
"decimal.js": "^10.3.1",
"dom-confetti": "^0.2.2",
"dotenv": "^15.0.0",
"ethereum-address": "0.0.4",
"ethereum-blockies": "github:MyEtherWallet/blockies",
"filesize": "^8.0.6",
"formik": "^2.2.9",
@ -48,7 +46,6 @@
"lodash.omit": "^4.5.0",
"next": "^12.0.9",
"query-string": "^7.1.0",
"querystring": "^0.2.1",
"react": "^17.0.2",
"react-chartjs-2": "^4.0.1",
"react-clipboard.js": "^2.0.16",

View File

@ -9,26 +9,27 @@ import React, {
} from 'react'
import { Asset, Config, LoggerInstance, Purgatory } from '@oceanprotocol/lib'
import { CancelToken } from 'axios'
import { retrieveDDO } from '@utils/aquarius'
import { getPrice } from '@utils/subgraph'
import { retrieveAsset } from '@utils/aquarius'
import { useWeb3 } from './Web3'
import { useSiteMetadata } from '@hooks/useSiteMetadata'
import { useCancelToken } from '@hooks/useCancelToken'
import { getOceanConfig, getDevelopmentConfig } from '@utils/ocean'
import { AssetExtended } from 'src/@types/AssetExtended'
import { getAccessDetails } from '@utils/accessDetailsAndPricing'
interface AssetProviderValue {
isInPurgatory: boolean
purgatoryData: Purgatory
ddo: Asset
assetExtended: AssetExtended
title: string
owner: string
price: BestPrice
accessDetails: AccessDetails
error?: string
refreshInterval: number
isAssetNetwork: boolean
oceanConfig: Config
loading: boolean
refreshDdo: (token?: CancelToken) => Promise<void>
refreshAsset: (token?: CancelToken) => Promise<void>
}
const AssetContext = createContext({} as AssetProviderValue)
@ -44,12 +45,12 @@ function AssetProvider({
}): ReactElement {
const { appConfig } = useSiteMetadata()
const { networkId } = useWeb3()
const { networkId, accountId } = useWeb3()
const [isInPurgatory, setIsInPurgatory] = useState(false)
const [purgatoryData, setPurgatoryData] = useState<Purgatory>()
const [ddo, setDDO] = useState<Asset>()
const [assetExtended, setAssetExtended] = useState<Asset>()
const [title, setTitle] = useState<string>()
const [price, setPrice] = useState<BestPrice>()
const [accessDetails, setConsumeDetails] = useState<AccessDetails>()
const [owner, setOwner] = useState<string>()
const [error, setError] = useState<string>()
const [loading, setLoading] = useState(false)
@ -58,32 +59,35 @@ function AssetProvider({
const newCancelToken = useCancelToken()
const fetchDdo = async (token?: CancelToken) => {
LoggerInstance.log('[asset] Init asset, get DDO')
setLoading(true)
const ddo = await retrieveDDO(did, token)
const fetchAsset = useCallback(
async (token?: CancelToken) => {
LoggerInstance.log('[asset] Init asset, get assetExtended')
setLoading(true)
const assetExtended = await retrieveAsset(did, token)
if (!ddo) {
setError(
`[asset] The DDO for ${did} was not found in MetadataCache. If you just published a new data set, wait some seconds and refresh this page.`
)
} else {
setError(undefined)
}
setLoading(false)
return ddo
}
if (!assetExtended) {
setError(
`[asset] The assetExtended for ${did} was not found in MetadataCache. If you just published a new data set, wait some seconds and refresh this page.`
)
} else {
setError(undefined)
}
setLoading(false)
return assetExtended
},
[did]
)
const refreshDdo = async (token?: CancelToken) => {
const refreshAsset = async (token?: CancelToken) => {
setLoading(true)
const ddo = await fetchDdo(token)
LoggerInstance.debug('[asset] Got DDO', ddo)
setDDO(ddo)
const assetExtended = await fetchAsset(token)
LoggerInstance.debug('[asset] Got assetExtended', assetExtended)
setAssetExtended(assetExtended)
setLoading(false)
}
// -----------------------------------
// Get and set DDO based on passed DID
// Get and set assetExtended based on passed DID
// -----------------------------------
useEffect(() => {
if (!did || !appConfig.metadataCacheUri) return
@ -91,77 +95,89 @@ function AssetProvider({
let isMounted = true
async function init() {
const ddo = await fetchDdo(newCancelToken())
if (!isMounted || !ddo) return
LoggerInstance.debug('[asset] Got DDO', ddo)
setDDO(ddo)
setTitle(ddo.metadata.name)
setOwner(ddo.nft.owner)
setIsInPurgatory((ddo.purgatory?.state as unknown as string) === 'true')
setPurgatoryData(ddo.purgatory)
const assetExtended = await fetchAsset(newCancelToken())
if (!isMounted || !assetExtended) return
LoggerInstance.debug('[asset] Got assetExtended', assetExtended)
setAssetExtended(assetExtended)
setTitle(assetExtended.metadata.name)
setOwner(assetExtended.nft.owner)
setIsInPurgatory(
(assetExtended.purgatory?.state as unknown as string) === 'true'
)
setPurgatoryData(assetExtended.purgatory)
}
init()
return () => {
isMounted = false
}
}, [did, appConfig.metadataCacheUri])
}, [did, appConfig.metadataCacheUri, fetchAsset, newCancelToken])
// -----------------------------------
// Attach price to asset
// -----------------------------------
const initPrice = useCallback(async (ddo: Asset): Promise<void> => {
if (!ddo) return
const returnedPrice = await getPrice(ddo)
setPrice({ ...returnedPrice })
}, [])
const initPrice = useCallback(
async (assetExtended: AssetExtended, accountId: string): Promise<void> => {
if (!assetExtended) return
const accessDetails = await getAccessDetails(
assetExtended.chainId,
assetExtended.services[0].datatokenAddress,
assetExtended.services[0].timeout,
accountId
)
assetExtended.accessDetails = accessDetails
setConsumeDetails({ ...accessDetails })
setAssetExtended(assetExtended)
},
[]
)
useEffect(() => {
if (!ddo) return
initPrice(ddo)
}, [ddo, initPrice])
if (!assetExtended) return
initPrice(assetExtended, accountId)
}, [accountId, assetExtended, initPrice])
// -----------------------------------
// Check user network against asset network
// -----------------------------------
useEffect(() => {
if (!networkId || !ddo) return
if (!networkId || !assetExtended) return
const isAssetNetwork = networkId === ddo?.chainId
const isAssetNetwork = networkId === assetExtended?.chainId
setIsAssetNetwork(isAssetNetwork)
}, [networkId, ddo])
}, [networkId, assetExtended])
// -----------------------------------
// Load ocean config based on asset network
// -----------------------------------
useEffect(() => {
if (!ddo?.chainId) return
if (!assetExtended?.chainId) return
const oceanConfig = {
...getOceanConfig(ddo?.chainId),
...getOceanConfig(assetExtended?.chainId),
// add local dev values
...(ddo?.chainId === 8996 && {
...(assetExtended?.chainId === 8996 && {
...getDevelopmentConfig()
})
}
setOceanConfig(oceanConfig)
}, [ddo])
}, [assetExtended])
return (
<AssetContext.Provider
value={
{
ddo,
assetExtended,
did,
title,
owner,
price,
accessDetails,
error,
isInPurgatory,
purgatoryData,
refreshInterval,
loading,
refreshDdo,
refreshAsset,
isAssetNetwork,
oceanConfig
} as AssetProviderValue

View File

@ -19,7 +19,6 @@ import { getDownloadAssets, getPublishedAssets } from '@utils/aquarius'
import { useSiteMetadata } from '@hooks/useSiteMetadata'
import { accountTruncate } from '@utils/web3'
import axios, { CancelToken } from 'axios'
import ethereumAddress from 'ethereum-address'
import get3BoxProfile from '@utils/profile'
import web3 from 'web3'
@ -59,7 +58,7 @@ function ProfileProvider({
// when accountId is no ETH address
//
useEffect(() => {
const isEthAddress = ethereumAddress.isAddress(accountId)
const isEthAddress = web3.utils.isAddress(accountId)
setIsEthAddress(isEthAddress)
}, [accountId])

View File

@ -1,7 +1,19 @@
import { useState } from 'react'
import { consumeFeedback } from '@utils/feedback'
import { LoggerInstance } from '@oceanprotocol/lib'
import {
approve,
Datatoken,
FreOrderParams,
LoggerInstance,
OrderParams,
Pool,
ProviderFees,
ProviderInstance,
signHash,
ZERO_ADDRESS
} from '@oceanprotocol/lib'
import { useWeb3 } from '@context/Web3'
import { getOceanConfig } from '@utils/ocean'
interface UseConsume {
consume: (
@ -18,7 +30,7 @@ interface UseConsume {
}
function useConsume(): UseConsume {
const { accountId } = useWeb3()
const { accountId, web3, chainId } = useWeb3()
const [isLoading, setIsLoading] = useState(false)
const [consumeStep, setConsumeStep] = useState<number | undefined>()
const [consumeStepText, setConsumeStepText] = useState<string | undefined>()
@ -29,9 +41,10 @@ function useConsume(): UseConsume {
setConsumeStepText(consumeFeedback[index])
}
// TODO: this will be done in another PR
async function consume(
did: string,
dataTokenAddress: string,
datatokenAddress: string,
serviceType = 'access',
marketFeeAddress: string,
orderId?: string
@ -43,46 +56,90 @@ function useConsume(): UseConsume {
try {
setStep(0)
// if (!orderId) {
// if we don't have a previous valid order, get one
// const userOwnedTokens = await ocean.accounts.getTokenBalance(
// dataTokenAddress,
// account
// )
// if (parseFloat(userOwnedTokens) < 1) {
// setConsumeError('Not enough datatokens')
// return 'Not enough datatokens'
// } else {
// setStep(1)
// try {
// orderId = await ocean.assets.order(
// did as string,
// serviceType,
// accountId,
// undefined,
// marketFeeAddress,
// undefined,
// null,
// false
// )
// LoggerInstance.log('ordercreated', orderId)
// setStep(2)
// } catch (error) {
// setConsumeError(error.message)
// return error.message
// }
// }
// }
if (!orderId) {
const datatoken = new Datatoken(web3)
// if we don't have a previous valid order, get one
const userOwnedTokens = await datatoken.balance(
datatokenAddress,
accountId
)
setStep(1)
try {
const config = getOceanConfig(chainId)
// const txApprove = await approve(
// web3,
// accountId,
// config.oceanTokenAddress,
// accountId,
// '1',
// false
// )
// console.log('approve tx', txApprove)
// const txApprove1 = await approve(
// web3,
// accountId,
// config.oceanTokenAddress,
// datatokenAddress,
// '1',
// false
// )
// console.log('approve tx', txApprove1)
// diference between timeout and validUntil?
const initializeData = await ProviderInstance.initialize(
did,
'fca052c239a62523be30ab8ee70c4046867f6cd89f228185fe2996ded3d23c3c',
0,
accountId,
'https://providerv4.rinkeby.oceanprotocol.com'
)
const orderParams = {
consumer: accountId,
serviceIndex: 1,
_providerFees: initializeData.providerFee
} as OrderParams
const freParams = {
exchangeContract: config.fixedRateExchangeAddress,
exchangeId:
'0x7ac824fef114255e5e3521a161ef692ec32003916fb6f3fe985cb74790d053ca',
maxBaseTokenAmount: web3.utils.toWei('2'),
swapMarketFee: web3.utils.toWei('0'),
marketFeeAddress: ZERO_ADDRESS
} as FreOrderParams
const esttx = await datatoken.estGasBuyFromFreAndOrder(
datatokenAddress,
accountId,
orderParams,
freParams
)
const tx = await datatoken.buyFromFreAndOrder(
datatokenAddress,
accountId,
orderParams,
freParams
)
LoggerInstance.log('ordercreated', orderId)
setStep(2)
} catch (error) {
setConsumeError(error.message)
return error.message
}
}
setStep(3)
// if (orderId)
// await ocean.assets.download(
// did as string,
// orderId,
// dataTokenAddress,
// account,
// ''
// )
setStep(4)
if (orderId)
// await ocean.assets.download(
// did as string,
// orderId,
// dataTokenAddress,
// account,
// ''
// )
setStep(4)
} catch (error) {
setConsumeError(error.message)
LoggerInstance.error(error)

View File

@ -39,6 +39,7 @@ async function getBlockHead(config: Config) {
}
// for everything else, create new web3 instance with infura
// TODO: this fails randomly , WHY!?!?!?!?!
const web3Instance = new Web3(config.nodeUri)
const blockHead = await web3Instance.eth.getBlockNumber()
return blockHead

View File

@ -9,21 +9,16 @@ import {
getCreateFreePricingFeedback,
getDispenseFeedback
} from '@utils/feedback'
import { sleep } from '@utils/index'
import { useWeb3 } from '@context/Web3'
import { getOceanConfig } from '@utils/ocean'
interface UsePricing {
getDTSymbol: (ddo: Asset) => Promise<string>
getDTName: (ddo: Asset) => Promise<string>
createPricing: (
priceOptions: PriceOptions,
ddo: Asset
) => Promise<TransactionReceipt | string | void>
mint: (tokensToMint: string, ddo: Asset) => Promise<TransactionReceipt | void>
buyDT: (
amountDataToken: number | string,
price: BestPrice,
accessDetails: AccessDetails,
ddo: Asset
) => Promise<TransactionReceipt | void>
pricingStep?: number
@ -123,7 +118,7 @@ function usePricing(): UsePricing {
async function buyDT(
amountDataToken: number | string,
price: BestPrice,
accessDetails: AccessDetails,
ddo: Asset
): Promise<TransactionReceipt | void> {
if (!accountId) return
@ -135,18 +130,20 @@ function usePricing(): UsePricing {
setPricingError(undefined)
setStep(1, 'buy', ddo)
LoggerInstance.log('Price found for buying', price)
LoggerInstance.log('Price found for buying', accessDetails)
Decimal.set({ precision: 18 })
switch (price?.type) {
switch (accessDetails?.type) {
case 'dynamic': {
const oceanAmmount = new Decimal(price.value).times(1.05).toString()
const maxPrice = new Decimal(price.value).times(2).toString()
const oceanAmmount = new Decimal(accessDetails.price)
.times(1.05)
.toString()
const maxPrice = new Decimal(accessDetails.price).times(2).toString()
setStep(2, 'buy', ddo)
LoggerInstance.log(
'Buying token from pool',
price,
accessDetails,
accountId,
oceanAmmount,
maxPrice
@ -173,7 +170,11 @@ function usePricing(): UsePricing {
)
return
}
LoggerInstance.log('Buying token from exchange', price, accountId)
LoggerInstance.log(
'Buying token from exchange',
accessDetails,
accountId
)
// await ocean.datatokens.approve(
// oceanConfig.oceanTokenAddress,
// oceanConfig.fixedRateExchangeAddress,
@ -227,77 +228,9 @@ function usePricing(): UsePricing {
return tx
}
async function createPricing(
priceOptions: PriceOptions,
ddo: Asset
): Promise<TransactionReceipt | void> {
const dataToken = ddo?.services[0].datatokenAddress
const dtSymbol = await getDTSymbol(ddo)
if (!accountId || !dtSymbol) return
const { type, amountOcean, price, weightOnDataToken, swapFee } =
priceOptions
let { amountDataToken } = priceOptions
const isPool = type === 'dynamic'
if (!isPool && !oceanConfig.fixedRateExchangeAddress) {
LoggerInstance.error(`'fixedRateExchangeAddress' not set in oceanConfig.`)
return
}
setPricingIsLoading(true)
setPricingError(undefined)
setStep(99, 'pool', ddo)
try {
if (type === 'free') {
setStep(99, 'free', ddo)
// await ocean.OceanDispenser.activate(dataToken, '1', '1', accountId)
} else {
// if fixedPrice set dt to max amount
if (!isPool) amountDataToken = 1000
await mint(`${amountDataToken}`, ddo)
}
// amountDataToken for fixed price is set to max
// const tx = isPool
// ? await ocean.pool
// .create(
// accountId,
// dataToken,
// `${amountDataToken}`,
// weightOnDataToken,
// `${amountOcean}`,
// `${swapFee}`
// )
// .next((step: number) => setStep(step, 'pool', ddo))
// : type === 'fixed'
// ? await ocean.fixedRateExchange
// .create(dataToken, `${price}`, accountId, `${amountDataToken}`)
// .next((step: number) => setStep(step, 'exchange', ddo))
// : await ocean.OceanDispenser.makeMinter(dataToken, accountId).next(
// (step: number) => setStep(step, 'free', ddo)
// )
// we should remove this sleep , why do we have sleep for 20 seconds !?!?!?!?!?!?!!?
// await sleep(20000)
// return tx
} catch (error) {
setPricingError(error.message)
LoggerInstance.error(error)
} finally {
setPricingStep(0)
setPricingStepText(undefined)
setPricingIsLoading(false)
}
}
return {
getDTSymbol,
getDTName,
createPricing,
buyDT,
mint,
pricingStep,

5
src/@types/AssetExtended.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import { Asset } from '@oceanprotocol/lib'
interface AssetExtended extends Asset {
accessDetails?: AccessDetails
}

20
src/@types/Price.d.ts vendored
View File

@ -1,14 +1,14 @@
interface BestPrice {
interface AccessDetails {
type: 'dynamic' | 'fixed' | 'free' | ''
address: string
value: number
isConsumable?: 'true' | 'false' | ''
ocean?: number
oceanSymbol?: string
datatoken?: number
datatokenSymbol?: string
exchangeId?: string
pools: string[]
price: number
// if type is dynamic this is the pool address, for fixed/free this is an id.
addressOrId: string
baseToken: TokenInfo
datatoken: TokenInfo
isConsumable?: boolean
// if there are valid orders for this
owned: bool
validOrderTx: string
}
interface PriceOptions {

6
src/@types/TokenInfo.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
interface TokenInfo {
address: string
name: string
symbol: string
decimals?: number
}

View File

@ -2,5 +2,5 @@ interface DownloadedAsset {
dtSymbol: string
timestamp: number
networkId: number
ddo: Asset
asset: Asset
}

View File

@ -1,7 +1,3 @@
declare module 'ethereum-blockies' {
export function toDataUrl(address: string): string
}
declare module 'ethereum-address' {
export function isAddress(address: string): boolean
}

View File

@ -0,0 +1,282 @@
import { gql, OperationResult } from 'urql'
import { fetchData, getQueryContext } from './subgraph'
import {
TokenPriceQuery,
TokenPriceQuery_token as TokenPrice
} from '../@types/subgraph/TokenPriceQuery'
import {
TokensPriceQuery,
TokensPriceQuery_tokens as TokensPrice
} from '../@types/subgraph/TokensPriceQuery'
import { Asset } from '@oceanprotocol/lib'
import { AssetExtended } from 'src/@types/AssetExtended'
const TokensPriceQuery = gql`
query TokensPriceQuery($datatokenIds: [ID!], $account: String) {
tokens(where: { id_in: $datatokenIds }) {
id
symbol
name
orders(
where: { consumer: $account }
orderBy: createdTimestamp
orderDirection: desc
) {
tx
serviceIndex
createdTimestamp
}
dispensers {
id
active
isMinter
maxBalance
token {
id
name
symbol
}
}
fixedRateExchanges {
id
price
baseToken {
symbol
name
address
}
datatoken {
symbol
name
address
}
active
}
pools {
id
spotPrice
isFinalized
datatokenLiquidity
baseToken {
symbol
name
address
}
datatoken {
symbol
name
address
}
}
}
}
`
const TokenPriceQuery = gql`
query TokenPriceQuery($datatokenId: ID!, $account: String) {
token(id: $datatokenId) {
id
symbol
name
orders(
where: { consumer: $account }
orderBy: createdTimestamp
orderDirection: desc
) {
tx
serviceIndex
createdTimestamp
}
dispensers {
id
active
isMinter
maxBalance
token {
id
name
symbol
}
}
fixedRateExchanges {
id
price
baseToken {
symbol
name
address
}
datatoken {
symbol
name
address
}
active
}
pools {
id
spotPrice
isFinalized
datatokenLiquidity
baseToken {
symbol
name
address
}
datatoken {
symbol
name
address
}
}
}
}
`
function getAccessDetailsFromTokenPrice(
tokenPrice: TokenPrice | TokensPrice,
timeout?: number
): AccessDetails {
const accessDetails = {} as AccessDetails
if (!timeout && !tokenPrice.orders && tokenPrice.orders.length > 0) {
const order = tokenPrice.orders[0]
accessDetails.owned = Date.now() / 1000 - order.createdTimestamp < timeout
accessDetails.validOrderTx = order.tx
}
// free is always the best price
if (tokenPrice.dispensers && tokenPrice.dispensers.length > 0) {
const dispenser = tokenPrice.dispensers[0]
accessDetails.type = 'free'
accessDetails.addressOrId = dispenser.id
accessDetails.price = 0
accessDetails.isConsumable = dispenser.active
accessDetails.datatoken = {
address: dispenser.token.id,
name: dispenser.token.name,
symbol: dispenser.token.symbol
}
return accessDetails
}
// checking for fixed price
if (
tokenPrice.fixedRateExchanges &&
tokenPrice.fixedRateExchanges.length > 0
) {
const fre = tokenPrice.fixedRateExchanges[0]
accessDetails.type = 'fixed'
accessDetails.addressOrId = fre.id
accessDetails.price = fre.price
// in theory we should check dt balance here, we can skip this because in the market we always create fre with minting capabilities.
accessDetails.isConsumable = fre.active
accessDetails.baseToken = {
address: fre.baseToken.address,
name: fre.baseToken.name,
symbol: fre.baseToken.symbol
}
accessDetails.datatoken = {
address: fre.datatoken.address,
name: fre.datatoken.name,
symbol: fre.datatoken.symbol
}
return accessDetails
}
// checking for pools
if (tokenPrice.pools && tokenPrice.pools.length > 0) {
const pool = tokenPrice.pools[0]
accessDetails.type = 'dynamic'
accessDetails.addressOrId = pool.id
// TODO: this needs to be consumePrice
accessDetails.price = pool.spotPrice
// TODO: pool.datatokenLiquidity > 3 is kinda random here, we shouldn't run into this anymore now , needs more thinking/testing
accessDetails.isConsumable = pool.isFinalized && pool.datatokenLiquidity > 3
accessDetails.baseToken = {
address: pool.baseToken.address,
name: pool.baseToken.name,
symbol: pool.baseToken.symbol
}
accessDetails.datatoken = {
address: pool.datatoken.address,
name: pool.datatoken.name,
symbol: pool.datatoken.symbol
}
return accessDetails
}
return accessDetails
}
/**
* returns various consume details for the desired datatoken
* @param chain chain on witch the dt is preset
* @param datatokenAddress address of the datatoken
* @param timeout timeout of the service , only needed if you want order details like owned and validOrderId
* @param account account that wants to consume, only needed if you want order details like owned and validOrderId
* @returns AccessDetails
*/
export async function getAccessDetails(
chain: number,
datatokenAddress: string,
timeout?: number,
account = ''
): Promise<AccessDetails> {
let accessDetails = {} as AccessDetails
const queryContext = getQueryContext(Number(chain))
const tokenQueryResult: OperationResult<TokenPriceQuery, any> =
await fetchData(
TokenPriceQuery,
{
datatokenId: datatokenAddress.toLowerCase(),
account: account.toLowerCase()
},
queryContext
)
const tokenPrice: TokenPrice = tokenQueryResult.data.token
accessDetails = getAccessDetailsFromTokenPrice(tokenPrice, timeout)
return accessDetails
}
export async function getAccessDetailsForAssets(
assets: Asset[],
account = ''
): Promise<AssetExtended[]> {
const assetsExtended: AssetExtended[] = assets
const chainAssetLists: any = {}
for (const asset of assets) {
// harcoded until we have chainId on assets
if (chainAssetLists[asset.chainId]) {
chainAssetLists[asset.chainId].push(
asset?.services[0].datatokenAddress.toLowerCase()
)
} else {
chainAssetLists[asset.chainId] = []
chainAssetLists[asset.chainId].push(
asset?.services[0].datatokenAddress.toLowerCase()
)
}
}
for (const chainKey in chainAssetLists) {
const queryContext = getQueryContext(Number(chainKey))
const tokenQueryResult: OperationResult<TokensPriceQuery, any> =
await fetchData(
TokensPriceQuery,
{
datatokenIds: chainAssetLists[chainKey],
account: account.toLowerCase()
},
queryContext
)
tokenQueryResult.data?.tokens.forEach((token) => {
const accessDetails = getAccessDetailsFromTokenPrice(token)
const currentAsset = assetsExtended.find(
(asset) => asset.services[0].datatokenAddress.toLowerCase() === token.id
)
currentAsset.accessDetails = accessDetails
})
}
return assetsExtended
}

View File

@ -4,7 +4,7 @@ import {
PublisherTrustedAlgorithm
} from '@oceanprotocol/lib'
import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
import { PriceList, getAssetsPriceList } from './subgraph'
import { PriceList } from './subgraph'
import axios, { CancelToken, AxiosResponse } from 'axios'
import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData'
import { metadataCacheUri } from '../../app.config'
@ -110,7 +110,7 @@ export async function queryMetadata(
}
}
export async function retrieveDDO(
export async function retrieveAsset(
did: string,
cancelToken: CancelToken
): Promise<Asset> {
@ -207,7 +207,8 @@ export async function transformDDOToAssetSelection(
cancelToken?: CancelToken
): Promise<AssetSelectionAsset[]> {
const didList: string[] = []
const priceList: PriceList = await getAssetsPriceList(ddoList)
// const priceList: PriceList = await getAssetsPriceList(ddoList)
const priceList: PriceList = null
const symbolList: any = {}
const didProviderEndpointMap: any = {}
for (const ddo of ddoList) {
@ -346,16 +347,16 @@ export async function getDownloadAssets(
const result = await queryMetadata(query, cancelToken)
const downloadedAssets: DownloadedAsset[] = result.results
.map((ddo) => {
.map((asset) => {
const order = tokenOrders.find(
({ datatoken }) =>
datatoken?.address.toLowerCase() ===
ddo.services[0].datatokenAddress.toLowerCase()
asset.services[0].datatokenAddress.toLowerCase()
)
return {
ddo,
networkId: ddo.chainId,
asset,
networkId: asset.chainId,
dtSymbol: order?.datatoken?.symbol,
timestamp: order?.createdTimestamp
}

View File

@ -1,41 +0,0 @@
import { LoggerInstance } from '@oceanprotocol/lib'
import { TransactionReceipt } from 'web3-core'
export async function setMinterToPublisher(
dataTokenAddress: string,
accountId: string,
setError: (msg: string) => void
): Promise<TransactionReceipt> {
// free pricing v3 workaround part1
// const status = await ocean.OceanDispenser.status(dataTokenAddress)
// if (!status?.minterApproved) return
// const response = await ocean.OceanDispenser.cancelMinter(
// dataTokenAddress,
// accountId
// )
// if (!response) {
// setError('Updating DDO failed.')
// LoggerInstance.error('Failed at cancelMinter')
// }
// return response
return null
}
export async function setMinterToDispenser(
dataTokenAddress: string,
accountId: string,
setError: (msg: string) => void
): Promise<TransactionReceipt> {
// free pricing v3 workaround part2
// const response = await ocean.OceanDispenser.makeMinter(
// dataTokenAddress,
// accountId
// )
// if (!response) {
// setError('Updating DDO failed.')
// LoggerInstance.error('Failed at makeMinter')
// }
// return response
return null
}

View File

@ -62,7 +62,7 @@ export function generateNftCreateData(nftMetadata: NftMetadata): any {
symbol: nftMetadata.symbol,
templateIndex: 1,
// Gas estimation fails if we add our huge tokenURI
tokenURI: ''
tokenURI: '{url:"https://coolImage.com, name: "just for test"}'
// TODO: figure out if Buffer.from method is working in browser in final build
// as BTOA is deprecated.
// tokenURI: window?.btoa(JSON.stringify(nftMetadata))

View File

@ -16,7 +16,6 @@ export function getOceanConfig(network: string | number): Config {
? undefined
: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID
) as Config
// TODO: remove hack once address is fixed
if (network === 'rinkeby' || network === 4)
config.oceanTokenAddress = '0x8967bcf84170c91b0d24d4302c2376283b0b3a07'

View File

@ -36,11 +36,6 @@ export interface PriceList {
[key: string]: string
}
export interface AssetListPrices {
ddo: Asset
price: BestPrice
}
interface DidAndDatatokenMap {
[name: string]: string
}
@ -410,195 +405,6 @@ export async function getPreviousOrders(
}
}
function transformPriceToBestPrice(
frePrice: AssetsFrePriceFixedRateExchange[],
poolPrice: AssetsPoolPricePool[],
freePrice: AssetFreePriceDispenser[]
) {
if (poolPrice?.length > 0) {
const price: BestPrice = {
type: 'dynamic',
address: poolPrice[0]?.id,
value: poolPrice[0]?.spotPrice,
ocean: poolPrice[0]?.baseTokenLiquidity,
oceanSymbol: poolPrice[0]?.baseToken.symbol,
datatoken: poolPrice[0]?.datatokenLiquidity,
pools: [poolPrice[0]?.id],
isConsumable: poolPrice[0]?.spotPrice === '-1' ? 'false' : 'true'
}
return price
} else if (frePrice?.length > 0) {
// TODO Hacky hack, temporary™: set isConsumable to true for fre assets.
// isConsumable: 'true'
const price: BestPrice = {
type: 'fixed',
value: frePrice[0]?.price,
address: frePrice[0]?.id,
exchangeId: frePrice[0]?.id,
oceanSymbol: frePrice[0]?.baseToken.symbol,
ocean: 0,
datatoken: 0,
pools: [],
isConsumable: 'true'
}
return price
} else if (freePrice?.length > 0) {
const price: BestPrice = {
type: 'free',
value: 0,
address: freePrice[0]?.token.id,
exchangeId: '',
ocean: 0,
datatoken: 0,
pools: [],
isConsumable: 'true'
}
return price
} else {
const price: BestPrice = {
type: '',
value: 0,
address: '',
exchangeId: '',
ocean: 0,
datatoken: 0,
pools: [],
isConsumable: 'false'
}
return price
}
}
async function getAssetsPoolsExchangesAndDatatokenMap(
assets: Asset[]
): Promise<
[
AssetsPoolPricePool[],
AssetsFrePriceFixedRateExchange[],
AssetFreePriceDispenser[],
DidAndDatatokenMap
]
> {
const didDTMap: DidAndDatatokenMap = {}
const chainAssetLists: any = {}
for (const ddo of assets) {
didDTMap[ddo?.services[0].datatokenAddress.toLowerCase()] = ddo.id
// harcoded until we have chainId on assets
if (chainAssetLists[ddo.chainId]) {
chainAssetLists[ddo.chainId].push(
ddo?.services[0].datatokenAddress.toLowerCase()
)
} else {
chainAssetLists[ddo.chainId] = []
chainAssetLists[ddo.chainId].push(
ddo?.services[0].datatokenAddress.toLowerCase()
)
}
}
let poolPriceResponse: AssetsPoolPricePool[] = []
let frePriceResponse: AssetsFrePriceFixedRateExchange[] = []
let freePriceResponse: AssetFreePriceDispenser[] = []
for (const chainKey in chainAssetLists) {
const freVariables = {
datatoken_in: chainAssetLists[chainKey]
}
const poolVariables = {
datatokenAddress_in: chainAssetLists[chainKey]
}
const freeVariables = {
datatoken_in: chainAssetLists[chainKey]
}
const queryContext = getQueryContext(Number(chainKey))
const chainPoolPriceResponse: OperationResult<AssetsPoolPrice> =
await fetchData(PoolQuery, poolVariables, queryContext)
poolPriceResponse = poolPriceResponse.concat(
chainPoolPriceResponse.data.pools
)
const chainFrePriceResponse: OperationResult<AssetsFrePrice> =
await fetchData(FreQuery, freVariables, queryContext)
frePriceResponse = frePriceResponse.concat(
chainFrePriceResponse.data.fixedRateExchanges
)
const chainFreePriceResponse: OperationResult<AssetsFreePrice> =
await fetchData(FreeQuery, freeVariables, queryContext)
freePriceResponse = freePriceResponse.concat(
chainFreePriceResponse.data.dispensers
)
}
return [poolPriceResponse, frePriceResponse, freePriceResponse, didDTMap]
}
export async function getAssetsPriceList(assets: Asset[]): Promise<PriceList> {
const priceList: PriceList = {}
const values: [
AssetsPoolPricePool[],
AssetsFrePriceFixedRateExchange[],
AssetFreePriceDispenser[],
DidAndDatatokenMap
] = await getAssetsPoolsExchangesAndDatatokenMap(assets)
const poolPriceResponse = values[0]
const frePriceResponse = values[1]
const freePriceResponse = values[2]
const didDTMap: DidAndDatatokenMap = values[3]
for (const poolPrice of poolPriceResponse) {
priceList[didDTMap[poolPrice.datatoken.address]] = poolPrice.spotPrice
}
for (const frePrice of frePriceResponse) {
priceList[didDTMap[frePrice.datatoken?.address]] = frePrice.price
}
for (const freePrice of freePriceResponse) {
priceList[didDTMap[freePrice.token?.address]] = '0'
}
return priceList
}
export async function getPrice(asset: Asset): Promise<BestPrice> {
const freVariables = {
datatoken: asset?.services[0].datatokenAddress.toLowerCase()
}
const freeVariables = {
datatoken: asset?.services[0].datatokenAddress.toLowerCase()
}
const poolVariables = {
datatokenAddress: asset?.services[0].datatokenAddress.toLowerCase()
}
const queryContext = getQueryContext(Number(asset.chainId))
const poolPriceResponse: OperationResult<AssetsPoolPrice> = await fetchData(
AssetPoolPriceQuery,
poolVariables,
queryContext
)
const frePriceResponse: OperationResult<AssetsFrePrice> = await fetchData(
AssetFreQuery,
freVariables,
queryContext
)
const freePriceResponse: OperationResult<AssetsFreePrice> = await fetchData(
AssetFreeQuery,
freeVariables,
queryContext
)
const bestPrice: BestPrice = transformPriceToBestPrice(
frePriceResponse.data.fixedRateExchanges,
poolPriceResponse.data.pools,
freePriceResponse.data.dispensers
)
return bestPrice
}
export async function getSpotPrice(asset: Asset): Promise<number> {
const poolVariables = {
datatokenAddress: asset?.services[0].datatokenAddress.toLowerCase()
@ -614,49 +420,6 @@ export async function getSpotPrice(asset: Asset): Promise<number> {
return poolPriceResponse.data.pools[0].spotPrice
}
export async function getAssetsBestPrices(
assets: Asset[]
): Promise<AssetListPrices[]> {
const assetsWithPrice: AssetListPrices[] = []
const values: [
AssetsPoolPricePool[],
AssetsFrePriceFixedRateExchange[],
AssetFreePriceDispenser[],
DidAndDatatokenMap
] = await getAssetsPoolsExchangesAndDatatokenMap(assets)
const poolPriceResponse = values[0]
const frePriceResponse = values[1]
const freePriceResponse = values[2]
for (const ddo of assets) {
const dataToken = ddo.services[0].datatokenAddress.toLowerCase()
const poolPrice: AssetsPoolPricePool[] = []
const frePrice: AssetsFrePriceFixedRateExchange[] = []
const freePrice: AssetFreePriceDispenser[] = []
const pool = poolPriceResponse.find(
(pool: AssetsPoolPricePool) => pool.datatoken.address === dataToken
)
pool && poolPrice.push(pool)
const fre = frePriceResponse.find(
(fre: AssetsFrePriceFixedRateExchange) =>
fre.datatoken.address === dataToken
)
fre && frePrice.push(fre)
const free = freePriceResponse.find(
(free: AssetFreePriceDispenser) => free.token.address === dataToken
)
free && freePrice.push(free)
const bestPrice = transformPriceToBestPrice(frePrice, poolPrice, freePrice)
assetsWithPrice.push({
ddo: ddo,
price: bestPrice
})
}
return assetsWithPrice
}
export async function getHighestLiquidityDatatokens(
chainIds: number[]
): Promise<string[]> {

View File

@ -7,11 +7,11 @@ import { useSiteMetadata } from '@hooks/useSiteMetadata'
import { Asset } from '@oceanprotocol/lib'
export default function AssetListTitle({
ddo,
asset,
did,
title
}: {
ddo?: Asset
asset?: Asset
did?: string
title?: string
}): ReactElement {
@ -20,8 +20,8 @@ export default function AssetListTitle({
useEffect(() => {
if (title || !appConfig.metadataCacheUri) return
if (ddo) {
setAssetTitle(ddo.metadata.name)
if (asset) {
setAssetTitle(asset.metadata.name)
return
}
@ -32,16 +32,16 @@ export default function AssetListTitle({
setAssetTitle(title[did])
}
!ddo && did && getAssetName()
!asset && did && getAssetName()
return () => {
source.cancel()
}
}, [assetTitle, appConfig.metadataCacheUri, ddo, did, title])
}, [assetTitle, appConfig.metadataCacheUri, asset, did, title])
return (
<h3 className={styles.title}>
<Link href={`/asset/${did || ddo?.id}`}>
<Link href={`/asset/${did || asset?.id}`}>
<a>{assetTitle}</a>
</Link>
</h3>

View File

@ -3,11 +3,13 @@ import React, { ReactElement, useEffect, useState } from 'react'
import Pagination from '@shared/Pagination'
import styles from './index.module.css'
import classNames from 'classnames/bind'
import { getAssetsBestPrices, AssetListPrices } from '@utils/subgraph'
import Loader from '@shared/atoms/Loader'
import { useUserPreferences } from '@context/UserPreferences'
import { useIsMounted } from '@hooks/useIsMounted'
// not sure why this import is required
import { AssetExtended } from 'src/@types/AssetExtended'
import { Asset } from '@oceanprotocol/lib'
import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing'
const cx = classNames.bind(styles)
@ -41,24 +43,19 @@ export default function AssetList({
noPublisher
}: AssetListProps): ReactElement {
const { chainIds } = useUserPreferences()
const [assetsWithPrices, setAssetsWithPrices] = useState<AssetListPrices[]>()
const [assetsWithPrices, setAssetsWithPrices] = useState<AssetExtended[]>()
const [loading, setLoading] = useState<boolean>(isLoading)
const isMounted = useIsMounted()
useEffect(() => {
if (!assets) return
const initialAssets: AssetListPrices[] = assets.map((asset) => ({
ddo: asset,
price: null
}))
setAssetsWithPrices(initialAssets)
setAssetsWithPrices(assets as AssetExtended[])
setLoading(false)
async function fetchPrices() {
const assetsWithPrices = await getAssetsBestPrices(assets)
const assetsWithPrices = await getAccessDetailsForAssets(assets)
if (!isMounted()) return
setAssetsWithPrices(assetsWithPrices)
setAssetsWithPrices([...assetsWithPrices])
}
fetchPrices()
}, [assets, isMounted])
@ -83,9 +80,8 @@ export default function AssetList({
{assetsWithPrices.length > 0 ? (
assetsWithPrices.map((assetWithPrice) => (
<AssetTeaser
ddo={assetWithPrice.ddo}
price={assetWithPrice.price}
key={assetWithPrice.ddo.id}
assetExtended={assetWithPrice}
key={assetWithPrice.id}
noPublisher={noPublisher}
/>
))

View File

@ -8,28 +8,25 @@ import AssetType from '@shared/AssetType'
import NetworkName from '@shared/NetworkName'
import styles from './AssetTeaser.module.css'
import { getServiceByName } from '@utils/ddo'
import { Asset } from '@oceanprotocol/lib'
import { AssetExtended } from 'src/@types/AssetExtended'
declare type AssetTeaserProps = {
ddo: Asset
price: BestPrice
assetExtended: AssetExtended
noPublisher?: boolean
}
export default function AssetTeaser({
ddo,
price,
assetExtended,
noPublisher
}: AssetTeaserProps): ReactElement {
const { name, type, description } = ddo.metadata
const { datatokens } = ddo
const isCompute = Boolean(getServiceByName(ddo, 'compute'))
const { name, type, description } = assetExtended.metadata
const { datatokens } = assetExtended
const isCompute = Boolean(getServiceByName(assetExtended, 'compute'))
const accessType = isCompute ? 'compute' : 'access'
const { owner } = ddo.nft
const { owner } = assetExtended.nft
return (
<article className={`${styles.teaser} ${styles[type]}`}>
<Link href={`/asset/${ddo.id}`}>
<Link href={`/asset/${assetExtended.id}`}>
<a className={styles.link}>
<header className={styles.header}>
<div className={styles.symbol}>{datatokens[0]?.symbol}</div>
@ -54,8 +51,11 @@ export default function AssetTeaser({
</div>
<footer className={styles.foot}>
<Price price={price} small />
<NetworkName networkId={ddo.chainId} className={styles.network} />
<Price accessDetails={assetExtended.accessDetails} small />
<NetworkName
networkId={assetExtended.chainId}
className={styles.network}
/>
</footer>
</a>
</Link>

View File

@ -75,7 +75,7 @@ const txHistoryQuery = gql`
export interface PoolTransaction extends TransactionHistoryPoolTransactions {
networkId: number
ddo: Asset
asset: Asset
}
const columns = [
@ -88,7 +88,7 @@ const columns = [
{
name: 'Data Set',
selector: function getAssetRow(row: PoolTransaction) {
return <AssetTitle ddo={row.ddo} />
return <AssetTitle asset={row.asset} />
}
},
{
@ -187,7 +187,7 @@ export default function PoolTransactions({
poolTransactions.push({
...data[i],
networkId: ddoList[i]?.chainId,
ddo: ddoList[i]
asset: ddoList[i]
})
}
const sortedTransactions = poolTransactions.sort(

View File

@ -49,7 +49,7 @@ export default function PriceUnit({
<div>
{Number.isNaN(Number(price)) ? '-' : formatPrice(price, locale)}{' '}
<span className={styles.symbol}>{symbol}</span>
{type && type === 'pool' && (
{type && type === 'dynamic' && (
<Badge label="pool" className={styles.badge} />
)}
</div>

View File

@ -5,26 +5,26 @@ import Tooltip from '../atoms/Tooltip'
import PriceUnit from './PriceUnit'
export default function Price({
price,
accessDetails,
className,
small,
conversion
}: {
price: BestPrice
accessDetails: AccessDetails
className?: string
small?: boolean
conversion?: boolean
}): ReactElement {
return price?.value || price?.type === 'free' ? (
return accessDetails?.price || accessDetails?.type === 'free' ? (
<PriceUnit
price={`${price.value}`}
symbol={price.oceanSymbol}
price={`${accessDetails.price}`}
symbol={accessDetails.baseToken?.symbol}
className={className}
small={small}
conversion={conversion}
type={price.type}
type={accessDetails.type}
/>
) : !price || price?.type === '' ? (
) : !accessDetails || accessDetails?.type === '' ? (
<div className={styles.empty}>
No price set{' '}
<Tooltip content="No pricing mechanism has been set on this asset yet." />

View File

@ -18,12 +18,12 @@ export default function TokenApproval({
tokenAddress: string
tokenSymbol: string
}): ReactElement {
const { price, isAssetNetwork } = useAsset()
const { accessDetails, isAssetNetwork } = useAsset()
const [tokenApproved, setTokenApproved] = useState(false)
const [loading, setLoading] = useState(false)
const { web3, accountId } = useWeb3()
const spender = price?.address
const spender = accessDetails?.addressOrId
const checkTokenApproval = useCallback(async () => {
if (!web3 || !tokenAddress || !spender || !isAssetNetwork || !amount) return

View File

@ -11,13 +11,15 @@ import { useAsset } from '@context/Asset'
export default function WalletNetworkSwitcher(): ReactElement {
const { networkId, web3Provider } = useWeb3()
const { ddo } = useAsset()
const { assetExtended } = useAsset()
const { networksList } = useNetworkMetadata()
const ddoNetworkData = getNetworkDataById(networksList, ddo.chainId)
const ddoNetworkData = getNetworkDataById(networksList, assetExtended.chainId)
const walletNetworkData = getNetworkDataById(networksList, networkId)
const ddoNetworkName = (
<strong>{getNetworkDisplayName(ddoNetworkData, ddo.chainId)}</strong>
<strong>
{getNetworkDisplayName(ddoNetworkData, assetExtended.chainId)}
</strong>
)
const walletNetworkName = (
<strong>{getNetworkDisplayName(walletNetworkData, networkId)}</strong>
@ -25,7 +27,7 @@ export default function WalletNetworkSwitcher(): ReactElement {
async function switchWalletNetwork() {
const networkNode = await networksList.find(
(data) => data.chainId === ddo.chainId
(data) => data.chainId === assetExtended.chainId
)
addCustomNetwork(web3Provider, networkNode)
}

View File

@ -32,7 +32,7 @@ export default function FormStartCompute({
selectedComputeAssetType,
selectedComputeAssetTimeout,
stepText,
algorithmPrice,
algorithmConsumeDetails,
isConsumable,
consumableFeedback
}: {
@ -56,14 +56,14 @@ export default function FormStartCompute({
selectedComputeAssetType?: string
selectedComputeAssetTimeout?: string
stepText: string
algorithmPrice: BestPrice
algorithmConsumeDetails: AccessDetails
isConsumable: boolean
consumableFeedback: string
}): ReactElement {
const { isValid, values }: FormikContextType<{ algorithm: string }> =
useFormikContext()
const { price, ddo, isAssetNetwork } = useAsset()
const [totalPrice, setTotalPrice] = useState(price?.value)
const { accessDetails, assetExtended, isAssetNetwork } = useAsset()
const [totalPrice, setTotalPrice] = useState(accessDetails?.price)
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>(false)
const { accountId, balance } = useWeb3()
const [algorithmConsumableStatus, setAlgorithmConsumableStatus] =
@ -97,19 +97,19 @@ export default function FormStartCompute({
// Set price for calculation output
//
useEffect(() => {
if (!price || !algorithmPrice) return
if (!accessDetails || !algorithmConsumeDetails) return
const priceDataset =
hasPreviousOrder || hasDatatoken ? 0 : Number(price.value)
hasPreviousOrder || hasDatatoken ? 0 : Number(accessDetails.price)
const priceAlgo =
hasPreviousOrderSelectedComputeAsset || hasDatatokenSelectedComputeAsset
? 0
: Number(algorithmPrice.value)
: Number(algorithmConsumeDetails.price)
setTotalPrice(priceDataset + priceAlgo)
}, [
price,
algorithmPrice,
accessDetails,
algorithmConsumeDetails,
hasPreviousOrder,
hasDatatoken,
hasPreviousOrderSelectedComputeAsset,
@ -143,7 +143,7 @@ export default function FormStartCompute({
hasDatatoken={hasDatatoken}
selectedComputeAssetTimeout={selectedComputeAssetTimeout}
hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset}
algorithmPrice={algorithmPrice}
algorithmConsumeDetails={algorithmConsumeDetails}
symbol={oceanSymbol}
totalPrice={totalPrice}
/>
@ -159,7 +159,7 @@ export default function FormStartCompute({
}
hasPreviousOrder={hasPreviousOrder}
hasDatatoken={hasDatatoken}
dtSymbol={ddo?.datatokens[0]?.symbol}
dtSymbol={assetExtended?.datatokens[0]?.symbol}
dtBalance={dtBalance}
datasetLowPoolLiquidity={datasetLowPoolLiquidity}
assetTimeout={assetTimeout}
@ -177,8 +177,8 @@ export default function FormStartCompute({
stepText={stepText}
isLoading={isLoading}
type="submit"
priceType={price?.type}
algorithmPriceType={algorithmPrice?.type}
priceType={accessDetails?.type}
algorithmPriceType={algorithmConsumeDetails?.type}
isBalanceSufficient={isBalanceSufficient}
isConsumable={isConsumable}
consumableFeedback={consumableFeedback}

View File

@ -12,7 +12,7 @@ interface PriceOutputProps {
assetTimeout: string
hasPreviousOrderSelectedComputeAsset: boolean
hasDatatokenSelectedComputeAsset: boolean
algorithmPrice: BestPrice
algorithmConsumeDetails: AccessDetails
selectedComputeAssetTimeout: string
}
@ -60,10 +60,10 @@ export default function PriceOutput({
symbol,
hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset,
algorithmPrice,
algorithmConsumeDetails,
selectedComputeAssetTimeout
}: PriceOutputProps): ReactElement {
const { price } = useAsset()
const { accessDetails } = useAsset()
return (
<div className={styles.priceComponent}>
@ -74,14 +74,14 @@ export default function PriceOutput({
<Row
hasPreviousOrder={hasPreviousOrder}
hasDatatoken={hasDatatoken}
price={price?.value}
price={accessDetails?.price}
timeout={assetTimeout}
symbol={symbol}
/>
<Row
hasPreviousOrder={hasPreviousOrderSelectedComputeAsset}
hasDatatoken={hasDatatokenSelectedComputeAsset}
price={algorithmPrice?.value}
price={algorithmConsumeDetails?.price}
timeout={selectedComputeAssetTimeout}
symbol={symbol}
sign="+"

View File

@ -30,16 +30,17 @@ import SuccessConfetti from '@shared/SuccessConfetti'
import { getServiceByName, secondsToString } from '@utils/ddo'
import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute'
import { getPreviousOrders, getPrice } from '@utils/subgraph'
import { getPreviousOrders } from '@utils/subgraph'
import AssetActionHistoryTable from '../AssetActionHistoryTable'
import ComputeJobs from '../../../Profile/History/ComputeJobs'
import { useCancelToken } from '@hooks/useCancelToken'
import { useIsMounted } from '@hooks/useIsMounted'
import { SortTermOptions } from '../../../../@types/aquarius/SearchQuery'
import { getAccessDetails } from '@utils/accessDetailsAndPricing'
export default function Compute({
ddo,
price,
accessDetails,
dtBalance,
file,
fileIsLoading,
@ -47,7 +48,7 @@ export default function Compute({
consumableFeedback
}: {
ddo: Asset
price: BestPrice
accessDetails: AccessDetails
dtBalance: string
file: FileMetadata
fileIsLoading?: boolean
@ -70,7 +71,8 @@ export default function Compute({
const [hasPreviousAlgorithmOrder, setHasPreviousAlgorithmOrder] =
useState(false)
const [algorithmDTBalance, setalgorithmDTBalance] = useState<string>()
const [algorithmPrice, setAlgorithmPrice] = useState<BestPrice>()
const [algorithmConsumeDetails, setAlgorithmConsumeDetails] =
useState<AccessDetails>()
const [previousAlgorithmOrderId, setPreviousAlgorithmOrderId] =
useState<string>()
const [datasetTimeout, setDatasetTimeout] = useState<string>()
@ -169,27 +171,24 @@ export default function Compute({
const initMetadata = useCallback(async (ddo: Asset): Promise<void> => {
if (!ddo) return
const price = await getPrice(ddo)
setAlgorithmPrice(price)
const accessDetails = await getAccessDetails(
ddo.chainId,
ddo.services[0].datatokenAddress
)
setAlgorithmConsumeDetails(accessDetails)
}, [])
useEffect(() => {
if (!algorithmPrice) return
if (!algorithmConsumeDetails) return
setIsAlgoConsumablePrice(
algorithmPrice.isConsumable !== undefined
? algorithmPrice.isConsumable === 'true'
: true
)
}, [algorithmPrice])
setIsAlgoConsumablePrice(algorithmConsumeDetails.isConsumable)
}, [algorithmConsumeDetails])
useEffect(() => {
if (!price) return
if (!accessDetails) return
setIsConsumablePrice(
price.isConsumable !== undefined ? price.isConsumable === 'true' : true
)
}, [price])
setIsConsumablePrice(accessDetails.isConsumable)
}, [accessDetails])
// useEffect(() => {
// setDatasetTimeout(secondsToString(timeout))
@ -392,7 +391,7 @@ export default function Compute({
<>
<div className={styles.info}>
<FileIcon file={file} isLoading={fileIsLoading} small />
<Price price={price} conversion />
<Price accessDetails={accessDetails} conversion />
</div>
{ddo.metadata.type === 'algorithm' ? (
@ -426,7 +425,7 @@ export default function Compute({
assetTimeout={datasetTimeout}
hasPreviousOrderSelectedComputeAsset={hasPreviousAlgorithmOrder}
hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken}
oceanSymbol={price ? price.oceanSymbol : ''}
oceanSymbol={accessDetails ? accessDetails.baseToken.symbol : ''}
dtSymbolSelectedComputeAsset={
selectedAlgorithmAsset?.datatokens[0]?.symbol
}
@ -435,7 +434,7 @@ export default function Compute({
selectedComputeAssetType="algorithm"
selectedComputeAssetTimeout={algorithmTimeout}
stepText={pricingStepText || 'Starting Compute Job...'}
algorithmPrice={algorithmPrice}
algorithmConsumeDetails={algorithmConsumeDetails}
isConsumable={isConsumable}
consumableFeedback={consumableFeedback}
/>
@ -447,7 +446,7 @@ export default function Compute({
<SuccessConfetti success="Your job started successfully! Watch the progress below or on your profile." />
)}
</footer>
{accountId && price?.datatoken && (
{accountId && accessDetails?.datatoken && (
<AssetActionHistoryTable title="Your Compute Jobs">
<ComputeJobs minimal />
</AssetActionHistoryTable>

View File

@ -4,10 +4,6 @@ import FileIcon from '@shared/FileIcon'
import Price from '@shared/Price'
import { useSiteMetadata } from '@hooks/useSiteMetadata'
import { useAsset } from '@context/Asset'
import { gql } from 'urql'
import { fetchData, getQueryContext } from '@utils/subgraph'
import { OrdersData } from '../../../@types/subgraph/OrdersData'
import BigNumber from 'bignumber.js'
import { useWeb3 } from '@context/Web3'
import { usePricing } from '@hooks/usePricing'
import { useConsume } from '@hooks/useConsume'
@ -18,23 +14,9 @@ import styles from './Consume.module.css'
import { useIsMounted } from '@hooks/useIsMounted'
import { Asset, FileMetadata } from '@oceanprotocol/lib'
const previousOrderQuery = gql`
query PreviousOrder($id: String!, $account: String!) {
orders(
first: 1
where: { datatoken: $id, payer: $account }
orderBy: createdTimestamp
orderDirection: desc
) {
createdTimestamp
tx
}
}
`
export default function Consume({
ddo,
price,
accessDetails,
file,
isBalanceSufficient,
dtBalance,
@ -43,7 +25,7 @@ export default function Consume({
consumableFeedback
}: {
ddo: Asset
price: BestPrice
accessDetails: AccessDetails
file: FileMetadata
isBalanceSufficient: boolean
dtBalance: string
@ -63,50 +45,8 @@ export default function Consume({
const [hasDatatoken, setHasDatatoken] = useState(false)
const [isConsumablePrice, setIsConsumablePrice] = useState(true)
const [assetTimeout, setAssetTimeout] = useState('')
const [data, setData] = useState<OrdersData>()
const isMounted = useIsMounted()
useEffect(() => {
if (!ddo || !accountId) return
const context = getQueryContext(ddo.chainId)
const variables = {
id: ddo.services[0].datatokenAddress?.toLowerCase(),
account: accountId?.toLowerCase()
}
fetchData(previousOrderQuery, variables, context).then((result: any) => {
isMounted() && setData(result.data)
})
}, [ddo, accountId, hasPreviousOrder, isMounted])
useEffect(() => {
if (
!data ||
!assetTimeout ||
data.orders.length === 0 ||
!accountId ||
!isAssetNetwork
)
return
const lastOrder = data.orders[0]
if (assetTimeout === '0') {
setPreviousOrderId(lastOrder.tx)
setHasPreviousOrder(true)
} else {
const expiry = new BigNumber(lastOrder.createdTimestamp).plus(
assetTimeout
)
const unixTime = new BigNumber(Math.floor(Date.now() / 1000))
if (unixTime.isLessThan(expiry)) {
setPreviousOrderId(lastOrder.tx)
setHasPreviousOrder(true)
} else {
setHasPreviousOrder(false)
}
}
}, [data, assetTimeout, accountId, isAssetNetwork])
useEffect(() => {
if (!ddo) return
@ -115,12 +55,12 @@ export default function Consume({
}, [ddo])
useEffect(() => {
if (!price) return
if (!accessDetails) return
setIsConsumablePrice(
price.isConsumable !== undefined ? price.isConsumable === 'true' : true
)
}, [price])
setIsConsumablePrice(accessDetails.isConsumable)
setHasPreviousOrder(accessDetails.owned)
setPreviousOrderId(accessDetails.validOrderTx)
}, [accessDetails])
useEffect(() => {
setHasDatatoken(Number(dtBalance) >= 1)
@ -151,10 +91,10 @@ export default function Consume({
])
async function handleConsume() {
if (!hasPreviousOrder && !hasDatatoken) {
const tx = await buyDT('1', price, ddo)
if (tx === undefined) return
}
// if (!hasPreviousOrder && !hasDatatoken) {
// const tx = await buyDT('1', price, ddo)
// if (tx === undefined) return
// }
const error = await consume(
ddo.id,
ddo.services[0].datatokenAddress,
@ -188,7 +128,7 @@ export default function Consume({
assetType={ddo?.metadata?.type}
stepText={consumeStepText || pricingStepText}
isLoading={pricingIsLoading || isLoading}
priceType={price?.type}
priceType={accessDetails?.type}
isConsumable={isConsumable}
isBalanceSufficient={isBalanceSufficient}
consumableFeedback={consumableFeedback}
@ -202,7 +142,7 @@ export default function Consume({
<FileIcon file={file} isLoading={fileIsLoading} />
</div>
<div className={styles.pricewrapper}>
<Price price={price} conversion />
<Price accessDetails={accessDetails} conversion />
{!isInPurgatory && <PurchaseButton />}
</div>
</div>

View File

@ -60,8 +60,14 @@ const initialPoolInfoCreator: Partial<PoolInfoUser> = initialPoolInfoUser
export default function Pool(): ReactElement {
const { accountId } = useWeb3()
const { isInPurgatory, ddo, owner, price, refreshInterval, isAssetNetwork } =
useAsset()
const {
isInPurgatory,
assetExtended,
owner,
accessDetails,
refreshInterval,
isAssetNetwork
} = useAsset()
const [poolData, setPoolData] = useState<PoolDataPoolData>()
const [poolInfo, setPoolInfo] = useState<PoolInfo>(
@ -82,11 +88,11 @@ export default function Pool(): ReactElement {
const [fetchInterval, setFetchInterval] = useState<NodeJS.Timeout>()
const fetchAllData = useCallback(async () => {
if (!ddo?.chainId || !price?.address || !owner) return
if (!assetExtended?.chainId || !accessDetails?.addressOrId || !owner) return
const response = await getPoolData(
ddo.chainId,
price.address,
assetExtended.chainId,
accessDetails.addressOrId,
owner,
accountId || ''
)
@ -101,7 +107,7 @@ export default function Pool(): ReactElement {
LoggerInstance.log('[pool] Fetched pool data:', response.poolData)
LoggerInstance.log('[pool] Fetched user data:', response.poolDataUser)
LoggerInstance.log('[pool] Fetched pool snapshots:', response.poolSnapshots)
}, [ddo?.chainId, price?.address, owner, accountId])
}, [assetExtended?.chainId, accessDetails?.addressOrId, owner, accountId])
// Helper: start interval fetching
const initFetchInterval = useCallback(() => {
@ -233,7 +239,12 @@ export default function Pool(): ReactElement {
// 3 User Pool Info
//
useEffect(() => {
if (!poolData || !poolInfo?.totalPoolTokens || !ddo?.chainId || !accountId)
if (
!poolData ||
!poolInfo?.totalPoolTokens ||
!assetExtended?.chainId ||
!accountId
)
return
const poolShare =
@ -299,7 +310,7 @@ export default function Pool(): ReactElement {
poolData,
poolInfoUser?.poolShares,
accountId,
ddo?.chainId,
assetExtended?.chainId,
owner,
poolInfo?.totalPoolTokens
])
@ -317,7 +328,7 @@ export default function Pool(): ReactElement {
{showAdd ? (
<Add
setShowAdd={setShowAdd}
poolAddress={price?.address}
poolAddress={accessDetails?.addressOrId}
totalPoolTokens={poolInfo?.totalPoolTokens}
totalBalance={{
baseToken: new Decimal(poolData?.baseTokenLiquidity).toString(),
@ -332,7 +343,7 @@ export default function Pool(): ReactElement {
) : showRemove ? (
<Remove
setShowRemove={setShowRemove}
poolAddress={price?.address}
poolAddress={accessDetails?.addressOrId}
poolTokens={poolInfoUser?.poolShares}
totalPoolTokens={poolInfo?.totalPoolTokens}
tokenOutAddress={poolInfo?.baseTokenAddress}
@ -350,17 +361,18 @@ export default function Pool(): ReactElement {
<Tooltip content={content.pool.tooltips.price} />
<div className={styles.dataTokenLinks}>
<ExplorerLink
networkId={ddo?.chainId}
path={`address/${price?.address}`}
networkId={assetExtended?.chainId}
path={`address/${accessDetails?.addressOrId}`}
>
Pool
</ExplorerLink>
<ExplorerLink
networkId={ddo?.chainId}
networkId={assetExtended?.chainId}
path={
ddo?.chainId === 2021000 || ddo?.chainId === 1287
? `tokens/${ddo.services[0].datatokenAddress}`
: `token/${ddo.services[0].datatokenAddress}`
assetExtended?.chainId === 2021000 ||
assetExtended?.chainId === 1287
? `tokens/${assetExtended.services[0].datatokenAddress}`
: `token/${assetExtended.services[0].datatokenAddress}`
}
>
Datatoken
@ -466,8 +478,8 @@ export default function Pool(): ReactElement {
<AssetActionHistoryTable title="Your Pool Transactions">
<PoolTransactions
accountId={accountId}
poolAddress={price?.address}
poolChainId={[ddo?.chainId]}
poolAddress={accessDetails?.addressOrId}
poolChainId={[assetExtended?.chainId]}
minimal
/>
</AssetActionHistoryTable>

View File

@ -14,19 +14,18 @@ import { useAsset } from '@context/Asset'
import { FormTradeData } from './_types'
import { initialValues } from './_constants'
import content from '../../../../../content/price.json'
import { AssetExtended } from 'src/@types/AssetExtended'
export default function FormTrade({
ddo,
assetExtended,
balance,
maxDt,
maxOcean,
price
maxOcean
}: {
ddo: Asset
assetExtended: AssetExtended
balance: PoolBalance
maxDt: string
maxOcean: string
price: BestPrice
}): ReactElement {
const { accountId } = useWeb3()
const { isAssetNetwork } = useAsset()
@ -111,11 +110,10 @@ export default function FormTrade({
<>
{isWarningAccepted ? (
<Swap
ddo={ddo}
assetExtended={assetExtended}
balance={balance}
maxDt={maxDt}
maxOcean={maxOcean}
price={price}
setCoin={setCoinFrom}
setMaximumOcean={setMaximumOcean}
setMaximumDt={setMaximumDt}

View File

@ -12,24 +12,23 @@ import Decimal from 'decimal.js'
import { useAsset } from '@context/Asset'
import { FormTradeData, TradeItem } from './_types'
import { Asset } from '@oceanprotocol/lib'
import { AssetExtended } from 'src/@types/AssetExtended'
Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
export default function Swap({
ddo,
assetExtended,
maxDt,
maxOcean,
balance,
price,
setMaximumDt,
setMaximumOcean,
setCoin
}: {
ddo: Asset
assetExtended: AssetExtended
maxDt: string
maxOcean: string
balance: PoolBalance
price: BestPrice
setMaximumDt: (value: string) => void
setMaximumOcean: (value: string) => void
setCoin: (value: string) => void
@ -37,12 +36,12 @@ export default function Swap({
const { isAssetNetwork } = useAsset()
const [oceanItem, setOceanItem] = useState<TradeItem>({
amount: '0',
token: price.oceanSymbol,
token: assetExtended.accessDetails.baseToken?.symbol,
maxAmount: '0'
})
const [dtItem, setDtItem] = useState<TradeItem>({
amount: '0',
token: ddo.datatokens[0].symbol,
token: assetExtended.accessDetails.datatoken.symbol,
maxAmount: '0'
})
@ -59,7 +58,7 @@ export default function Swap({
const [tokenAmount, setTokenAmount] = useState<string>()
useEffect(() => {
if (!ddo || !balance || !values?.type || !price) return
if (!assetExtended || !balance || !values?.type) return
async function calculateMaximum() {
const amountDataToken =
@ -115,11 +114,10 @@ export default function Swap({
}
calculateMaximum()
}, [
ddo,
assetExtended,
maxOcean,
maxDt,
balance,
price,
values?.type,
setMaximumDt,
setMaximumOcean
@ -127,7 +125,9 @@ export default function Swap({
const switchTokens = () => {
setFieldValue('type', values.type === 'buy' ? 'sell' : 'buy')
setCoin(values.type === 'sell' ? 'OCEAN' : ddo.datatokens[0].symbol)
setCoin(
values.type === 'sell' ? 'OCEAN' : assetExtended.datatokens[0].symbol
)
// don't reset form because we don't want to reset type
setFieldValue('datatoken', 0)
setFieldValue('ocean', 0)
@ -223,7 +223,7 @@ export default function Swap({
<Output
dtSymbol={dtItem.token}
oceanSymbol={oceanItem.token}
poolAddress={price?.address}
poolAddress={assetExtended.accessDetails?.addressOrId}
/>
<PriceImpact

View File

@ -2,8 +2,6 @@ import React, { ReactElement, useEffect, useState } from 'react'
import FormTrade from './FormTrade'
import { useAsset } from '@context/Asset'
import { useWeb3 } from '@context/Web3'
import { isValidNumber } from '@utils/numbers'
import Decimal from 'decimal.js'
import { Datatoken } from '@oceanprotocol/lib'
@ -13,7 +11,7 @@ export default function Trade(): ReactElement {
const { accountId, balance, web3 } = useWeb3()
const { isAssetNetwork } = useAsset()
const [tokenBalance, setTokenBalance] = useState<PoolBalance>()
const { price, ddo } = useAsset()
const { assetExtended } = useAsset()
const [maxDt, setMaxDt] = useState('0')
const [maxOcean, setMaxOcean] = useState('0')
@ -25,14 +23,14 @@ export default function Trade(): ReactElement {
!isAssetNetwork ||
!balance?.ocean ||
!accountId ||
!ddo?.services[0].datatokenAddress
!assetExtended?.services[0].datatokenAddress
)
return
async function getTokenBalance() {
const datatokenInstance = new Datatoken(web3)
const dtBalance = await datatokenInstance.balance(
ddo.services[0].datatokenAddress,
assetExtended.services[0].datatokenAddress,
accountId
)
setTokenBalance({
@ -41,11 +39,16 @@ export default function Trade(): ReactElement {
})
}
getTokenBalance()
}, [web3, balance.ocean, accountId, ddo, isAssetNetwork])
}, [web3, balance.ocean, accountId, assetExtended, isAssetNetwork])
// Get maximum amount for either OCEAN or datatoken
useEffect(() => {
if (!isAssetNetwork || !price || price.value === 0) return
if (
!isAssetNetwork ||
!assetExtended.accessDetails ||
assetExtended.accessDetails.price === 0
)
return
async function getMaximum() {
// const maxTokensInPool = await ocean.pool.getDTMaxBuyQuantity(
@ -66,12 +69,11 @@ export default function Trade(): ReactElement {
// )
}
getMaximum()
}, [isAssetNetwork, balance.ocean, price])
}, [isAssetNetwork, balance.ocean, assetExtended])
return (
<FormTrade
ddo={ddo}
price={price}
assetExtended={assetExtended}
balance={tokenBalance}
maxDt={maxDt}
maxOcean={maxOcean}

View File

@ -24,10 +24,10 @@ import { FormPublishData } from 'src/components/Publish/_types'
export default function AssetActions({
ddo,
price
accessDetails
}: {
ddo: Asset
price: BestPrice
accessDetails: AccessDetails
}): ReactElement {
const { accountId, balance, web3 } = useWeb3()
const { isAssetNetwork } = useAsset()
@ -112,22 +112,24 @@ export default function AssetActions({
// Check user balance against price
useEffect(() => {
if (price?.type === 'free') setIsBalanceSufficient(true)
if (!price?.value || !accountId || !balance?.ocean || !dtBalance) return
if (accessDetails?.type === 'free') setIsBalanceSufficient(true)
if (!accessDetails?.price || !accountId || !balance?.ocean || !dtBalance)
return
setIsBalanceSufficient(
compareAsBN(balance.ocean, `${price.value}`) || Number(dtBalance) >= 1
compareAsBN(balance.ocean, `${accessDetails.price}`) ||
Number(dtBalance) >= 1
)
return () => {
setIsBalanceSufficient(false)
}
}, [balance, accountId, price, dtBalance])
}, [balance, accountId, accessDetails, dtBalance])
const UseContent = isCompute ? (
<Compute
ddo={ddo}
price={price}
accessDetails={accessDetails}
dtBalance={dtBalance}
file={fileMetadata}
fileIsLoading={fileIsLoading}
@ -137,7 +139,7 @@ export default function AssetActions({
) : (
<Consume
ddo={ddo}
price={price}
accessDetails={accessDetails}
dtBalance={dtBalance}
isBalanceSufficient={isBalanceSufficient}
file={fileMetadata}
@ -154,17 +156,17 @@ export default function AssetActions({
}
]
price?.type === 'dynamic' &&
accessDetails?.type === 'dynamic' &&
tabs.push(
{
title: 'Pool',
content: <Pool />,
disabled: !price.datatoken
disabled: !accessDetails.datatoken
},
{
title: 'Trade',
content: <Trade />,
disabled: !price.datatoken
disabled: !accessDetails.datatoken
}
)

View File

@ -26,7 +26,7 @@ const getReceipts = gql`
`
export default function EditHistory(): ReactElement {
const { ddo } = useAsset()
const { assetExtended } = useAsset()
function getUpdateType(type: string): string {
switch (type) {
@ -49,17 +49,17 @@ export default function EditHistory(): ReactElement {
const [queryContext, setQueryContext] = useState<OperationContext>()
useEffect(() => {
if (!ddo) return
if (!assetExtended) return
const queryContext = getQueryContext(ddo.chainId)
const queryContext = getQueryContext(assetExtended.chainId)
setQueryContext(queryContext)
}, [ddo])
}, [assetExtended])
const [result] = useQuery({
query: getReceipts,
variables: { address: ddo?.nft.address.toLowerCase() },
variables: { address: assetExtended?.nft.address.toLowerCase() },
context: queryContext,
pause: !ddo || !queryContext
pause: !assetExtended || !queryContext
})
const { data } = result
@ -80,7 +80,10 @@ export default function EditHistory(): ReactElement {
<ul className={styles.history}>
{receipts?.map((receipt) => (
<li key={receipt.id} className={styles.item}>
<ExplorerLink networkId={ddo?.chainId} path={`/tx/${receipt.tx}`}>
<ExplorerLink
networkId={assetExtended?.chainId}
path={`/tx/${receipt.tx}`}
>
{getUpdateType(receipt.type)}{' '}
<Time date={`${receipt.timestamp}`} relative isUnix />
</ExplorerLink>

View File

@ -16,11 +16,11 @@ import content from '../../../../content/purgatory.json'
import { Asset } from '@oceanprotocol/lib'
export default function AssetContent({
ddo,
price
asset,
accessDetails
}: {
ddo: Asset
price: BestPrice
asset: Asset
accessDetails: AccessDetails
}): ReactElement {
const { debug } = useUserPreferences()
const { isInPurgatory, purgatoryData } = useAsset()
@ -28,14 +28,14 @@ export default function AssetContent({
return (
<>
<div className={styles.networkWrap}>
<NetworkName networkId={ddo?.chainId} className={styles.network} />
<NetworkName networkId={asset?.chainId} className={styles.network} />
</div>
<article className={styles.grid}>
<div>
<div className={styles.content}>
<MetaMain ddo={ddo} />
{price?.datatoken !== null && <Bookmark did={ddo?.id} />}
<MetaMain ddo={asset} />
{accessDetails?.datatoken !== null && <Bookmark did={asset?.id} />}
{isInPurgatory === true ? (
<Alert
@ -48,20 +48,20 @@ export default function AssetContent({
<>
<Markdown
className={styles.description}
text={ddo?.metadata.description || ''}
text={asset?.metadata.description || ''}
/>
<MetaSecondary ddo={ddo} />
<MetaSecondary ddo={asset} />
</>
)}
<MetaFull ddo={ddo} />
<MetaFull ddo={asset} />
<EditHistory />
{debug === true && <DebugOutput title="DDO" output={ddo} />}
{debug === true && <DebugOutput title="DDO" output={asset} />}
</div>
</div>
<div className={styles.actions}>
<AssetActions ddo={ddo} price={price} />
<AssetActions ddo={asset} accessDetails={accessDetails} />
{/*
TODO: restore edit actions, ideally put edit screens on new page

View File

@ -8,7 +8,6 @@ import { useUserPreferences } from '@context/UserPreferences'
import DebugEditCompute from './DebugEditCompute'
import styles from './index.module.css'
// import { transformComputeFormToServiceComputePrivacy } from '@utils/compute'
import { setMinterToDispenser, setMinterToPublisher } from '@utils/freePrice'
import Web3Feedback from '@shared/Web3Feedback'
import { getInitialValues, validationSchema } from './_constants'
import content from '../../../../content/pages/editComputeDataset.json'
@ -20,7 +19,7 @@ export default function EditComputeDataset({
}): ReactElement {
const { debug } = useUserPreferences()
const { accountId } = useWeb3()
const { ddo, price, isAssetNetwork, refreshDdo } = useAsset()
const { assetExtended, isAssetNetwork, refreshAsset } = useAsset()
const [success, setSuccess] = useState<string>()
const [error, setError] = useState<string>()
@ -112,7 +111,7 @@ export default function EditComputeDataset({
/>
</article>
<Web3Feedback
networkId={ddo?.chainId}
networkId={assetExtended?.chainId}
isAssetNetwork={isAssetNetwork}
/>
{debug === true && (

View File

@ -28,12 +28,12 @@ export default function FormEditComputeDataset({
setShowEdit: (show: boolean) => void
}): ReactElement {
const { appConfig } = useSiteMetadata()
const { ddo } = useAsset()
const { assetExtended } = useAsset()
const { values }: FormikContextType<ComputePrivacyForm> = useFormikContext()
const [allAlgorithms, setAllAlgorithms] = useState<AssetSelectionAsset[]>()
const newCancelToken = useCancelToken()
const { publisherTrustedAlgorithms } = getServiceByName(
ddo,
assetExtended,
'compute'
).compute
@ -41,14 +41,14 @@ export default function FormEditComputeDataset({
publisherTrustedAlgorithms: PublisherTrustedAlgorithm[]
): Promise<AssetSelectionAsset[]> {
const baseParams = {
chainIds: [ddo.chainId],
chainIds: [assetExtended.chainId],
sort: { sortBy: SortTermOptions.Created },
filters: [getFilterTerm('service.attributes.main.type', 'algorithm')]
} as BaseQueryParams
const query = generateBaseQuery(baseParams)
const querryResult = await queryMetadata(query, newCancelToken())
const datasetComputeService = getServiceByName(ddo, 'compute')
const datasetComputeService = getServiceByName(assetExtended, 'compute')
const algorithmSelectionList = await transformDDOToAssetSelection(
datasetComputeService?.serviceEndpoint,
querryResult.results,

View File

@ -10,7 +10,6 @@ import { getServiceByName, mapTimeoutStringToSeconds } from '@utils/ddo'
import styles from './index.module.css'
import { LoggerInstance } from '@oceanprotocol/lib'
import { useWeb3 } from '@context/Web3'
import { setMinterToDispenser, setMinterToPublisher } from '@utils/freePrice'
import content from '../../../../content/pages/edit.json'
import { MetadataEditForm } from './_types'
@ -23,11 +22,11 @@ export default function Edit({
}): ReactElement {
const { debug } = useUserPreferences()
const { accountId } = useWeb3()
const { ddo, refreshDdo, price } = useAsset()
const { assetExtended, refreshAsset, accessDetails } = useAsset()
const [success, setSuccess] = useState<string>()
const [error, setError] = useState<string>()
const [timeoutStringValue, setTimeoutStringValue] = useState<string>()
const { timeout } = ddo.services[0]
const { timeout } = assetExtended.services[0]
const hasFeedback = error || success
@ -119,7 +118,11 @@ export default function Edit({
return (
<Formik
initialValues={getInitialValues(ddo.metadata, timeout, price.value)}
initialValues={getInitialValues(
assetExtended.metadata,
timeout,
accessDetails.price
)}
validationSchema={validationSchema}
onSubmit={async (values, { resetForm }) => {
// move user's focus to top of screen
@ -145,7 +148,7 @@ export default function Edit({
/> */}
<aside>
<Web3Feedback networkId={ddo?.chainId} />
<Web3Feedback networkId={assetExtended?.chainId} />
</aside>
{/* {debug === true && <Debug values={values} ddo={ddo} />} */}

View File

@ -6,20 +6,21 @@ import { useAsset } from '@context/Asset'
import AssetContent from './AssetContent'
export default function AssetDetails({ uri }: { uri: string }): ReactElement {
const { ddo, title, error, isInPurgatory, loading, price } = useAsset()
const { assetExtended, title, error, isInPurgatory, loading, accessDetails } =
useAsset()
const [pageTitle, setPageTitle] = useState<string>()
useEffect(() => {
if (!ddo || error) {
if (!assetExtended || error) {
setPageTitle('Could not retrieve asset')
return
}
setPageTitle(isInPurgatory ? '' : title)
}, [ddo, error, isInPurgatory, title])
}, [assetExtended, error, isInPurgatory, title])
return ddo && pageTitle !== undefined && !loading ? (
return assetExtended && pageTitle !== undefined && !loading ? (
<Page title={pageTitle} uri={uri}>
<AssetContent ddo={ddo} price={price} />
<AssetContent asset={assetExtended} accessDetails={accessDetails} />
</Page>
) : error ? (
<Page title={pageTitle} noPageHeader uri={uri}>

View File

@ -6,27 +6,29 @@ import Price from '@shared/Price'
import Tooltip from '@shared/atoms/Tooltip'
import AssetTitle from '@shared/AssetList/AssetListTitle'
import { retrieveDDOListByDIDs } from '@utils/aquarius'
import { getAssetsBestPrices, AssetListPrices } from '@utils/subgraph'
import { CancelToken } from 'axios'
import { useSiteMetadata } from '@hooks/useSiteMetadata'
import { useCancelToken } from '@hooks/useCancelToken'
import { AssetExtended } from 'src/@types/AssetExtended'
import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing'
import { useWeb3 } from '@context/Web3'
const columns = [
{
name: 'Data Set',
selector: function getAssetRow(row: AssetListPrices) {
const { metadata } = row.ddo
return <AssetTitle title={metadata.name} ddo={row.ddo} />
selector: function getAssetRow(row: AssetExtended) {
const { metadata } = row
return <AssetTitle title={metadata.name} asset={row} />
},
maxWidth: '45rem',
grow: 1
},
{
name: 'Datatoken Symbol',
selector: function getAssetRow(row: AssetListPrices) {
selector: function getAssetRow(row: AssetExtended) {
return (
<Tooltip content={row.ddo.datatokens[0].name}>
{row.ddo.datatokens[0].symbol}
<Tooltip content={row.datatokens[0].name}>
{row.datatokens[0].symbol}
</Tooltip>
)
},
@ -34,8 +36,8 @@ const columns = [
},
{
name: 'Price',
selector: function getAssetRow(row: AssetListPrices) {
return <Price price={row.price} small />
selector: function getAssetRow(row: AssetExtended) {
return <Price accessDetails={row.accessDetails} small />
},
right: true
}
@ -43,9 +45,10 @@ const columns = [
export default function Bookmarks(): ReactElement {
const { appConfig } = useSiteMetadata()
const { accountId } = useWeb3()
const { bookmarks } = useUserPreferences()
const [pinned, setPinned] = useState<AssetListPrices[]>()
const [pinned, setPinned] = useState<AssetExtended[]>()
const [isLoading, setIsLoading] = useState<boolean>()
const { chainIds } = useUserPreferences()
const newCancelToken = useCancelToken()
@ -87,8 +90,9 @@ export default function Bookmarks(): ReactElement {
chainIds,
newCancelToken()
)
const pinnedAssets: AssetListPrices[] = await getAssetsBestPrices(
resultPinned
const pinnedAssets: AssetExtended[] = await getAccessDetailsForAssets(
resultPinned,
accountId
)
setPinned(pinnedAssets)
} catch (error) {
@ -102,6 +106,7 @@ export default function Bookmarks(): ReactElement {
appConfig?.metadataCacheUri,
bookmarks,
chainIds,
accountId,
getAssetsBookmarked,
newCancelToken
])

View File

@ -53,7 +53,7 @@ function SectionQueryResult({
queryData?: string[]
}) {
const { chainIds } = useUserPreferences()
const [result, setResult] = useState<any>()
const [result, setResult] = useState<PagedAssets>()
const [loading, setLoading] = useState<boolean>()
const isMounted = useIsMounted()
const newCancelToken = useCancelToken()

View File

@ -3,7 +3,6 @@ import React, { useEffect, useState, ReactElement } from 'react'
import { useUserPreferences } from '@context/UserPreferences'
import {
getAccountLiquidityInOwnAssets,
getAssetsBestPrices,
UserLiquidity,
calculateUserLiquidity
} from '@utils/subgraph'
@ -12,6 +11,7 @@ import NumberUnit from './NumberUnit'
import styles from './Stats.module.css'
import { useProfile } from '@context/Profile'
import { PoolShares_poolShares as PoolShare } from '../../../@types/subgraph/PoolShares'
import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing'
async function getPoolSharesLiquidity(
poolShares: PoolShare[]
@ -50,10 +50,12 @@ export default function Stats({
async function getPublisherLiquidity() {
try {
const accountPoolAdresses: string[] = []
const assetsPrices = await getAssetsBestPrices(assets)
const assetsPrices = await getAccessDetailsForAssets(assets)
for (const priceInfo of assetsPrices) {
if (priceInfo.price.type === 'dynamic') {
accountPoolAdresses.push(priceInfo.price.address.toLowerCase())
if (priceInfo.accessDetails.type === 'dynamic') {
accountPoolAdresses.push(
priceInfo.accessDetails.addressOrId.toLowerCase()
)
}
}
const userLiquidity = await getAccountLiquidityInOwnAssets(

View File

@ -3,7 +3,7 @@ import Time from '@shared/atoms/Time'
import Button from '@shared/atoms/Button'
import Modal from '@shared/atoms/Modal'
import External from '@images/external.svg'
import { retrieveDDO } from '@utils/aquarius'
import { retrieveAsset } from '@utils/aquarius'
import Results from './Results'
import styles from './Details.module.css'
import { useCancelToken } from '@hooks/useCancelToken'
@ -46,7 +46,7 @@ function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
const newCancelToken = useCancelToken()
useEffect(() => {
async function getAlgoMetadata() {
const ddo = await retrieveDDO(job.algoDID, newCancelToken())
const ddo = await retrieveAsset(job.algoDID, newCancelToken())
setAlgoDtSymbol(ddo.datatokens[0].symbol)
setAlgoName(ddo?.metadata.name)
}

View File

@ -74,7 +74,7 @@ export default function ComputeJobs({
minimal?: boolean
}): ReactElement {
const { accountId, networkId } = useWeb3()
const { ddo } = useAsset()
const { assetExtended } = useAsset()
const { chainIds } = useUserPreferences()
const [isLoading, setIsLoading] = useState(false)
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
@ -96,7 +96,7 @@ export default function ComputeJobs({
} catch (error) {
LoggerInstance.error(error.message)
}
}, [chainIds, accountId, ddo, isMounted])
}, [chainIds, accountId, assetExtended, isMounted])
useEffect(() => {
fetchJobs()

View File

@ -9,7 +9,7 @@ const columns = [
{
name: 'Data Set',
selector: function getAssetRow(row: DownloadedAsset) {
return <AssetTitle ddo={row.ddo} />
return <AssetTitle asset={row.asset} />
}
},
{

View File

@ -27,7 +27,7 @@ interface AssetPoolShare {
poolShare: PoolShare
networkId: number
createTime: number
ddo: Asset
asset: Asset
}
function Liquidity({ row, type }: { row: AssetPoolShare; type: string }) {
@ -83,7 +83,7 @@ const columns = [
{
name: 'Data Set',
selector: function getAssetRow(row: AssetPoolShare) {
return <AssetTitle ddo={row.ddo} />
return <AssetTitle asset={row.asset} />
},
grow: 2
},
@ -140,7 +140,7 @@ async function getPoolSharesAssets(
userLiquidity: userLiquidity,
networkId: ddoList[i].chainId,
createTime: data[i].pool.createdTimestamp,
ddo: ddoList[i]
asset: ddoList[i]
})
}
const assets = assetList.sort((a, b) => b.createTime - a.createTime)

View File

@ -7,24 +7,34 @@ import { transformPublishFormToDdo } from '../_utils'
import { Asset } from '@oceanprotocol/lib'
export default function Preview(): ReactElement {
const [ddo, setDdo] = useState<Asset>()
const [price, setPrice] = useState<BestPrice>()
const [asset, setAsset] = useState<Asset>()
const [accessDetails, setAccessDetails] = useState<AccessDetails>()
const { values } = useFormikContext<FormPublishData>()
useEffect(() => {
async function makeDdo() {
const ddo = await transformPublishFormToDdo(values)
setDdo(ddo as Asset)
const asset = await transformPublishFormToDdo(values)
setAsset(asset as Asset)
// dummy BestPrice to trigger certain AssetActions
const price: BestPrice = {
const accessDetails: AccessDetails = {
type: values.pricing.type,
address: '0x...',
value: values.pricing.price,
pools: [],
oceanSymbol: 'OCEAN'
addressOrId: '0x...',
price: values.pricing.price,
baseToken: {
address: '0x..',
name: '',
symbol: ''
},
datatoken: {
address: '0x..',
name: '',
symbol: ''
},
owned: false,
validOrderTx: ''
}
setPrice(price)
setAccessDetails(accessDetails)
}
makeDdo()
}, [values])
@ -34,7 +44,7 @@ export default function Preview(): ReactElement {
<h2 className={styles.previewTitle}>Preview</h2>
<h3 className={styles.assetTitle}>{values.metadata.name}</h3>
<AssetContent ddo={ddo} price={price} />
<AssetContent asset={asset} accessDetails={accessDetails} />
</div>
)
}

View File

@ -86,7 +86,7 @@ export async function transformPublishFormToDdo(
const did = nftAddress ? generateDid(nftAddress, chainId) : '0x...'
const currentTime = dateToStringNoMS(new Date())
const isPreview = !datatokenAddress && !nftAddress
console.log('did', did, isPreview)
// Transform from files[0].url to string[] assuming only 1 file
const filesTransformed = files?.length &&
files[0].valid && [files[0].url.replace('javascript:', '')]

View File

@ -6,7 +6,6 @@ import AssetProvider from '@context/Asset'
export default function PageAssetDetails(): ReactElement {
const router = useRouter()
const { did } = router.query
return (
<AssetProvider did={did as string}>
<PageTemplateAssetDetails uri={router.pathname} />

View File

@ -5,8 +5,8 @@ import { accountTruncate } from '@utils/web3'
import { useWeb3 } from '@context/Web3'
import ProfileProvider from '@context/Profile'
import { getEnsAddress, getEnsName } from '@utils/ens'
import ethereumAddress from 'ethereum-address'
import { useRouter } from 'next/router'
import web3 from 'web3'
export default function PageProfile(): ReactElement {
const router = useRouter()
@ -29,7 +29,7 @@ export default function PageProfile(): ReactElement {
const pathAccount = router.query.account as string
// Path has ETH addreess
if (ethereumAddress.isAddress(pathAccount)) {
if (web3.utils.isAddress(pathAccount)) {
const finalAccountId = pathAccount || accountId
setFinalAccountId(finalAccountId)

View File

@ -2,9 +2,9 @@ import React, { ReactElement, useState } from 'react'
import Search from '../components/Search'
import Page from '@shared/Page'
import { accountTruncate } from '@utils/web3'
import ethereumAddress from 'ethereum-address'
import { MAXIMUM_NUMBER_OF_PAGES_WITH_RESULTS } from '@utils/aquarius'
import { useRouter } from 'next/router'
import web3 from 'web3'
export default function PageSearch(): ReactElement {
const router = useRouter()
@ -13,7 +13,7 @@ export default function PageSearch(): ReactElement {
const [totalResults, setTotalResults] = useState<number>()
const [totalPagesNumber, setTotalPagesNumber] = useState<number>()
const isETHAddress = ethereumAddress.isAddress(text as string)
const isETHAddress = web3.utils.isAddress(text as string)
const searchValue =
(isETHAddress ? accountTruncate(text as string) : text) ||
tags ||