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

Restore Pool Shares section (#1139)

* get poolShares dt addresses

* style fixes

* class names fix

* remove useless changes

* fix

* try/catch blocks, loading fix

* show pool shares fix

* delete logs, fix build

* more try/catch blocks

* check subgraph url, add try/catch block

* fixes

* pool fields fix

* minor code fixes

* fix subgraph fetch

Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro>

* remove unused function, fixes

* use LoggerInstance, remove useless setter

* error messages fix, get rid of dt column

* small tweaks and tests

* fixes

* fixes

* modified flow for pool shares

* loading UX fixes

* unified calculations for pool liquidity

* stop the refetch madness

* profile provider already sets interval fetching for pool shares
* pool shares will change when chainIds, accountId is changed so no need to listen for changes again

* calculation tweaks

* pool stats tweak

* fix pool transactions

* fix data display in pool shares section

* minor fix, delete comment

* subgraph typings generation fix

* pool stats display tweaks

* price sizing fix

* rabbit hole fixes

* more price UI fixes

* cleanup

* wording consistency

* render all frontpage sections by default, load in assets after

Co-authored-by: ClaudiaHolhos <claudia@oceanprotocol.com>
Co-authored-by: mihaisc <mihai.scarlat@smartcontrol.ro>
Co-authored-by: mihaisc <mihai@oceanprotocol.com>
Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
claudiaHash 2022-03-09 14:58:54 +02:00 committed by GitHub
parent af60018500
commit 4331c24c0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 656 additions and 592 deletions

View File

@ -172,7 +172,6 @@ function PoolProvider({ children }: { children: ReactNode }): ReactElement {
.mul(100) .mul(100)
.toFixed(2) .toFixed(2)
: '0' : '0'
const newPoolOwnerInfo = { const newPoolOwnerInfo = {
liquidity, liquidity,
poolShares: ownerPoolShares, poolShares: ownerPoolShares,

View File

@ -154,7 +154,11 @@ function ProfileProvider({
await fetchPoolShares(accountId, chainIds, isEthAddress) await fetchPoolShares(accountId, chainIds, isEthAddress)
if (poolSharesInterval) return if (poolSharesInterval) return
const interval = setInterval(async () => { const interval = setInterval(async () => {
LoggerInstance.log(
`[profile] Re-fetching pool shares after ${refreshInterval / 1000}s.`
)
await fetchPoolShares(accountId, chainIds, isEthAddress) await fetchPoolShares(accountId, chainIds, isEthAddress)
}, refreshInterval) }, refreshInterval)
setPoolSharesInterval(interval) setPoolSharesInterval(interval)

View File

@ -8,7 +8,7 @@ import {
TokensPriceQuery, TokensPriceQuery,
TokensPriceQuery_tokens as TokensPrice TokensPriceQuery_tokens as TokensPrice
} from '../@types/subgraph/TokensPriceQuery' } from '../@types/subgraph/TokensPriceQuery'
import { Asset, ProviderInstance } from '@oceanprotocol/lib' import { Asset, LoggerInstance, ProviderInstance } from '@oceanprotocol/lib'
import { AssetExtended } from 'src/@types/AssetExtended' import { AssetExtended } from 'src/@types/AssetExtended'
import { calculateBuyPrice } from './pool' import { calculateBuyPrice } from './pool'
import { getFixedBuyPrice } from './fixedRateExchange' import { getFixedBuyPrice } from './fixedRateExchange'
@ -229,41 +229,42 @@ function getAccessDetailsFromTokenPrice(
*/ */
export async function getOrderPriceAndFees( export async function getOrderPriceAndFees(
asset: AssetExtended, asset: AssetExtended,
accountId?: string accountId: string
): Promise<OrderPriceAndFees> { ): Promise<OrderPriceAndFees> {
const orderPriceAndFee = {
price: '0',
publisherMarketOrderFee: '0',
publisherMarketPoolSwapFee: '0',
publisherMarketFixedSwapFee: '0',
consumeMarketOrderFee: '0',
consumeMarketPoolSwapFee: '0',
consumeMarketFixedSwapFee: '0',
providerFee: {},
opcFee: '0'
} as OrderPriceAndFees
const { accessDetails } = asset
const { appConfig } = getSiteMetadata() const { appConfig } = getSiteMetadata()
// fetch publish market order fee const orderPriceAndFee = {
orderPriceAndFee.publisherMarketOrderFee = price: '0',
asset.accessDetails.publisherMarketOrderFee publisherMarketOrderFee:
// fetch consume market order fee asset?.accessDetails?.publisherMarketOrderFee || '0',
orderPriceAndFee.consumeMarketOrderFee = appConfig.consumeMarketOrderFee publisherMarketPoolSwapFee: '0',
publisherMarketFixedSwapFee: '0',
consumeMarketOrderFee: appConfig.consumeMarketOrderFee || '0',
consumeMarketPoolSwapFee: '0',
consumeMarketFixedSwapFee: '0',
providerFee: {
providerFeeAmount: '0'
},
opcFee: '0'
} as OrderPriceAndFees
// fetch provider fee // fetch provider fee
const initializeData = await ProviderInstance.initialize( const initializeData = await ProviderInstance.initialize(
asset.id, asset?.id,
asset.services[0].id, asset.services[0].id,
0, 0,
accountId, accountId,
asset.services[0].serviceEndpoint asset?.services[0].serviceEndpoint
) )
orderPriceAndFee.providerFee = initializeData.providerFee orderPriceAndFee.providerFee = initializeData.providerFee
// fetch price and swap fees // fetch price and swap fees
switch (accessDetails.type) { switch (asset?.accessDetails?.type) {
case 'dynamic': { case 'dynamic': {
const poolPrice = await calculateBuyPrice(accessDetails, asset.chainId) const poolPrice = await calculateBuyPrice(
asset?.accessDetails,
asset?.chainId
)
orderPriceAndFee.price = poolPrice.tokenAmount orderPriceAndFee.price = poolPrice.tokenAmount
orderPriceAndFee.liquidityProviderSwapFee = orderPriceAndFee.liquidityProviderSwapFee =
poolPrice.liquidityProviderSwapFeeAmount poolPrice.liquidityProviderSwapFeeAmount
@ -274,7 +275,7 @@ export async function getOrderPriceAndFees(
break break
} }
case 'fixed': { case 'fixed': {
const fixed = await getFixedBuyPrice(accessDetails, asset.chainId) const fixed = await getFixedBuyPrice(asset?.accessDetails, asset?.chainId)
orderPriceAndFee.price = fixed.baseTokenAmount orderPriceAndFee.price = fixed.baseTokenAmount
orderPriceAndFee.opcFee = fixed.oceanFeeAmount orderPriceAndFee.opcFee = fixed.oceanFeeAmount
orderPriceAndFee.publisherMarketFixedSwapFee = fixed.marketFeeAmount orderPriceAndFee.publisherMarketFixedSwapFee = fixed.marketFeeAmount
@ -297,9 +298,9 @@ export async function getOrderPriceAndFees(
/** /**
* @param {number} chain * @param {number} chain
* @param {string} datatokenAddress * @param {string} datatokenAddress
* @param {number=} timeout timout of the service, this is needed to return order details * @param {number} timeout timout of the service, this is needed to return order details
* @param {string=} account account that wants to buy, is needed to return order details * @param {string} account account that wants to buy, is needed to return order details
* @param {bool=} includeOrderPriceAndFees if false price will be spot price (pool) and rate (fre), if true you will get the order price including fees !! fees not yet done * @param {bool} includeOrderPriceAndFees if false price will be spot price (pool) and rate (fre), if true you will get the order price including fees !! fees not yet done
* @returns {Promise<AccessDetails>} * @returns {Promise<AccessDetails>}
*/ */
export async function getAccessDetails( export async function getAccessDetails(
@ -308,22 +309,26 @@ export async function getAccessDetails(
timeout?: number, timeout?: number,
account = '' account = ''
): Promise<AccessDetails> { ): Promise<AccessDetails> {
const queryContext = getQueryContext(Number(chainId)) try {
const tokenQueryResult: OperationResult< const queryContext = getQueryContext(Number(chainId))
TokenPriceQuery, const tokenQueryResult: OperationResult<
{ datatokenId: string; account: string } TokenPriceQuery,
> = await fetchData( { datatokenId: string; account: string }
TokenPriceQuery, > = await fetchData(
{ TokenPriceQuery,
datatokenId: datatokenAddress.toLowerCase(), {
account: account?.toLowerCase() datatokenId: datatokenAddress.toLowerCase(),
}, account: account?.toLowerCase()
queryContext },
) queryContext
)
const tokenPrice: TokenPrice = tokenQueryResult.data.token const tokenPrice: TokenPrice = tokenQueryResult.data.token
const accessDetails = getAccessDetailsFromTokenPrice(tokenPrice, timeout) const accessDetails = getAccessDetailsFromTokenPrice(tokenPrice, timeout)
return accessDetails return accessDetails
} catch (error) {
LoggerInstance.error('Error getting access details: ', error.message)
}
} }
export async function getAccessDetailsForAssets( export async function getAccessDetailsForAssets(
@ -333,43 +338,48 @@ export async function getAccessDetailsForAssets(
const assetsExtended: AssetExtended[] = assets const assetsExtended: AssetExtended[] = assets
const chainAssetLists: { [key: number]: string[] } = {} const chainAssetLists: { [key: number]: string[] } = {}
for (const asset of assets) { try {
if (chainAssetLists[asset.chainId]) { for (const asset of assets) {
chainAssetLists[asset.chainId].push( if (chainAssetLists[asset.chainId]) {
asset.services[0].datatokenAddress.toLowerCase() chainAssetLists[asset.chainId].push(
) asset.services[0].datatokenAddress.toLowerCase()
} else { )
chainAssetLists[asset.chainId] = [] } else {
chainAssetLists[asset.chainId].push( chainAssetLists[asset.chainId] = []
asset.services[0].datatokenAddress.toLowerCase() chainAssetLists[asset.chainId].push(
) asset.services[0].datatokenAddress.toLowerCase()
)
}
} }
}
for (const chainKey in chainAssetLists) { for (const chainKey in chainAssetLists) {
const queryContext = getQueryContext(Number(chainKey)) const queryContext = getQueryContext(Number(chainKey))
const tokenQueryResult: OperationResult< const tokenQueryResult: OperationResult<
TokensPriceQuery, TokensPriceQuery,
{ datatokenIds: [string]; account: string } { datatokenIds: [string]; account: string }
> = await fetchData( > = await fetchData(
TokensPriceQuery, TokensPriceQuery,
{ {
datatokenIds: chainAssetLists[chainKey], datatokenIds: chainAssetLists[chainKey],
account: account?.toLowerCase() account: account?.toLowerCase()
}, },
queryContext queryContext
)
tokenQueryResult.data?.tokens.forEach((token) => {
const currentAsset = assetsExtended.find(
(asset) => asset.services[0].datatokenAddress.toLowerCase() === token.id
)
const accessDetails = getAccessDetailsFromTokenPrice(
token,
currentAsset?.services[0]?.timeout
) )
tokenQueryResult.data?.tokens.forEach((token) => {
const currentAsset = assetsExtended.find(
(asset) =>
asset.services[0].datatokenAddress.toLowerCase() === token.id
)
const accessDetails = getAccessDetailsFromTokenPrice(
token,
currentAsset?.services[0]?.timeout
)
currentAsset.accessDetails = accessDetails currentAsset.accessDetails = accessDetails
}) })
}
return assetsExtended
} catch (error) {
LoggerInstance.error('Error getting access details: ', error.message)
} }
return assetsExtended
} }

View File

@ -170,6 +170,28 @@ export async function getAssetsFromDidList(
} }
} }
export async function getAssetsFromDtList(
dtList: string[],
chainIds: number[],
cancelToken: CancelToken
): Promise<Asset[]> {
try {
if (!(dtList.length > 0)) return
const baseParams = {
chainIds: chainIds,
filters: [getFilterTerm('services.datatokenAddress', dtList)],
ignorePurgatory: true
} as BaseQueryParams
const query = generateBaseQuery(baseParams)
const queryResult = await queryMetadata(query, cancelToken)
return queryResult?.results
} catch (error) {
LoggerInstance.error(error.message)
}
}
export async function retrieveDDOListByDIDs( export async function retrieveDDOListByDIDs(
didList: string[], didList: string[],
chainIds: number[], chainIds: number[],

View File

@ -16,9 +16,6 @@ export function getOceanConfig(network: string | number): Config {
? undefined ? undefined
: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID : process.env.NEXT_PUBLIC_INFURA_PROJECT_ID
) as Config ) as Config
// TODO: remove hack once address is fixed
if (network === 'rinkeby' || network === 4)
config.oceanTokenAddress = '0x8967bcf84170c91b0d24d4302c2376283b0b3a07'
return config as Config return config as Config
} }
@ -30,7 +27,7 @@ export function getDevelopmentConfig(): Config {
// metadataContractAddress: contractAddresses.development?.Metadata, // metadataContractAddress: contractAddresses.development?.Metadata,
// oceanTokenAddress: contractAddresses.development?.Ocean, // oceanTokenAddress: contractAddresses.development?.Ocean,
// There is no subgraph in barge so we hardcode the Rinkeby one for now // There is no subgraph in barge so we hardcode the Rinkeby one for now
subgraphUri: 'https://subgraphv4.rinkeby.oceanprotocol.com' subgraphUri: 'https://v4.subgraph.rinkeby.oceanprotocol.com'
} as Config } as Config
} }

View File

@ -5,7 +5,8 @@ import { getDummyWeb3 } from './web3'
import { TransactionReceipt } from 'web3-eth' import { TransactionReceipt } from 'web3-eth'
import Decimal from 'decimal.js' import Decimal from 'decimal.js'
import { AccessDetails } from 'src/@types/Price' import { AccessDetails } from 'src/@types/Price'
import { isValidNumber } from './numbers'
import { MAX_DECIMALS } from './constants'
/** /**
* This is used to calculate the price to buy one datatoken from a pool, that is different from spot price. You need to pass either a web3 object or a chainId. If you pass a chainId a dummy web3 object will be created * This is used to calculate the price to buy one datatoken from a pool, that is different from spot price. You need to pass either a web3 object or a chainId. If you pass a chainId a dummy web3 object will be created
* @param {AccessDetails} accessDetails * @param {AccessDetails} accessDetails
@ -73,3 +74,40 @@ export async function buyDtFromPool(
return result return result
} }
/**
* Calculate the base token liquidity based on shares info
* @param {string} shares
* @param {string} totalShares
* @param {string} baseTokenLiquidity
* @returns
*/
export function calculateUserLiquidity(
shares: string,
totalShares: string,
baseTokenLiquidity: string
): string {
const totalLiquidity =
isValidNumber(shares) &&
isValidNumber(totalShares) &&
isValidNumber(baseTokenLiquidity)
? new Decimal(shares)
.dividedBy(new Decimal(totalShares))
.mul(baseTokenLiquidity)
: new Decimal(0)
return totalLiquidity.toDecimalPlaces(MAX_DECIMALS).toString()
}
export function calculateUserTVL(
shares: string,
totalShares: string,
baseTokenLiquidity: string
): string {
const liquidity = calculateUserLiquidity(
shares,
totalShares,
baseTokenLiquidity
)
const tvl = new Decimal(liquidity).mul(2) // we multiply by 2 because of 50/50 weight
return tvl.toDecimalPlaces(MAX_DECIMALS).toString()
}

View File

@ -12,12 +12,12 @@ import {
PoolShares as PoolSharesList, PoolShares as PoolSharesList,
PoolShares_poolShares as PoolShare PoolShares_poolShares as PoolShare
} from '../@types/subgraph/PoolShares' } from '../@types/subgraph/PoolShares'
import { import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData'
OrdersData_orders as OrdersData,
OrdersData_orders_datatoken as OrdersDatatoken
} from '../@types/subgraph/OrdersData'
import { UserSalesQuery as UsersSalesList } from '../@types/subgraph/UserSalesQuery' import { UserSalesQuery as UsersSalesList } from '../@types/subgraph/UserSalesQuery'
import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery' import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery'
import { calculateUserTVL } from './pool'
import Decimal from 'decimal.js'
import { MAX_DECIMALS } from './constants'
export interface UserLiquidity { export interface UserLiquidity {
price: string price: string
@ -164,7 +164,7 @@ const UserTokenOrders = gql`
` `
const UserSalesQuery = gql` const UserSalesQuery = gql`
query UserSalesQuery($user: String!) { query UserSalesQuery($user: ID!) {
users(where: { id: $user }) { users(where: { id: $user }) {
id id
totalSales totalSales
@ -177,7 +177,7 @@ const TopSalesQuery = gql`
query TopSalesQuery { query TopSalesQuery {
users( users(
first: 20 first: 20
orderBy: tokensOwned orderBy: sharesOwned
orderDirection: desc orderDirection: desc
where: { tokenBalancesOwned_not: "0" } where: { tokenBalancesOwned_not: "0" }
) { ) {
@ -206,14 +206,17 @@ export function getSubgraphUri(chainId: number): string {
} }
export function getQueryContext(chainId: number): OperationContext { export function getQueryContext(chainId: number): OperationContext {
const queryContext: OperationContext = { try {
url: `${getSubgraphUri( const queryContext: OperationContext = {
Number(chainId) url: `${getSubgraphUri(
)}/subgraphs/name/oceanprotocol/ocean-subgraph`, Number(chainId)
requestPolicy: 'cache-and-network' )}/subgraphs/name/oceanprotocol/ocean-subgraph`,
requestPolicy: 'cache-and-network'
}
return queryContext
} catch (error) {
LoggerInstance.error('Get query context error: ', error.message)
} }
return queryContext
} }
export async function fetchData( export async function fetchData(
@ -223,12 +226,13 @@ export async function fetchData(
): Promise<any> { ): Promise<any> {
try { try {
const client = getUrqlClientInstance() const client = getUrqlClientInstance()
const response = await client.query(query, variables, context).toPromise() const response = await client.query(query, variables, context).toPromise()
return response return response
} catch (error) { } catch (error) {
console.error('Error fetchData: ', error.message) LoggerInstance.error('Error fetchData: ', error.message)
throw Error(error.message)
} }
return null
} }
export async function fetchDataForMultipleChains( export async function fetchDataForMultipleChains(
@ -237,21 +241,20 @@ export async function fetchDataForMultipleChains(
chainIds: number[] chainIds: number[]
): Promise<any[]> { ): Promise<any[]> {
let datas: any[] = [] let datas: any[] = []
for (const chainId of chainIds) { try {
const context: OperationContext = { for (const chainId of chainIds) {
url: `${getSubgraphUri( // console.log('fetch chainID', chainId)
chainId const context: OperationContext = getQueryContext(chainId)
)}/subgraphs/name/oceanprotocol/ocean-subgraph`,
requestPolicy: 'network-only'
}
try {
const response = await fetchData(query, variables, context) const response = await fetchData(query, variables, context)
datas = datas.concat(response.data) // console.log('fetch response', response)
} catch (error) { if (!response || response.error) continue
console.error('Error fetchData: ', error.message) datas = datas.concat(response?.data)
// console.log('fetch datas', datas)
} }
return datas
} catch (error) {
LoggerInstance.error('Error fetchDataForMultipleChains: ', error.message)
} }
return datas
} }
export async function getOpcFees(chainId: number) { export async function getOpcFees(chainId: number) {
@ -268,7 +271,7 @@ export async function getOpcFees(chainId: number) {
) )
opcFees = response?.data?.opc opcFees = response?.data?.opc
} catch (error) { } catch (error) {
console.error('Error fetchData: ', error.message) LoggerInstance.error('Error getOpcFees: ', error.message)
throw Error(error.message) throw Error(error.message)
} }
return opcFees return opcFees
@ -320,6 +323,7 @@ export async function getHighestLiquidityDatatokens(
): Promise<string[]> { ): Promise<string[]> {
const dtList: string[] = [] const dtList: string[] = []
let highestLiquidityAssets: HighestLiquidityAssetsPool[] = [] let highestLiquidityAssets: HighestLiquidityAssetsPool[] = []
for (const chain of chainIds) { for (const chain of chainIds) {
const queryContext = getQueryContext(Number(chain)) const queryContext = getQueryContext(Number(chain))
const fetchedPools: OperationResult<HighestLiquidityGraphAssets, any> = const fetchedPools: OperationResult<HighestLiquidityGraphAssets, any> =
@ -339,22 +343,11 @@ export async function getHighestLiquidityDatatokens(
return dtList return dtList
} }
export function calculateUserLiquidity(poolShare: PoolShare): number { export async function getAccountTVLInOwnAssets(
const ocean =
(poolShare.shares / poolShare.pool.totalShares) *
poolShare.pool.baseTokenLiquidity
const datatokens =
(poolShare.shares / poolShare.pool.totalShares) *
poolShare.pool.datatokenLiquidity
const totalLiquidity = ocean + datatokens * poolShare.pool.spotPrice
return totalLiquidity
}
export async function getAccountLiquidityInOwnAssets(
accountId: string, accountId: string,
chainIds: number[], chainIds: number[],
pools: string[] pools: string[]
): Promise<UserLiquidity> { ): Promise<string> {
const queryVariables = { const queryVariables = {
user: accountId.toLowerCase(), user: accountId.toLowerCase(),
pools: pools pools: pools
@ -364,22 +357,22 @@ export async function getAccountLiquidityInOwnAssets(
queryVariables, queryVariables,
chainIds chainIds
) )
let totalLiquidity = 0 let tvl = new Decimal(0)
let totalOceanLiquidity = 0 // console.log('resss', results)
for (const result of results) { for (const result of results) {
// console.log('result.poolShares', result.poolShares)
for (const poolShare of result.poolShares) { for (const poolShare of result.poolShares) {
const userShare = poolShare.shares / poolShare.pool.totalShares const poolUserTvl = calculateUserTVL(
const userBalance = userShare * poolShare.pool.baseTokenLiquidity poolShare.shares,
totalOceanLiquidity += userBalance poolShare.pool.totalShares,
const poolLiquidity = calculateUserLiquidity(poolShare) poolShare.pool.baseTokenLiquidity
totalLiquidity += poolLiquidity )
tvl = tvl.add(new Decimal(poolUserTvl))
// console.log('result.poolShares', tvl.toString())
} }
} }
return { return tvl.toDecimalPlaces(MAX_DECIMALS).toString()
price: totalLiquidity.toString(),
oceanBalance: totalOceanLiquidity.toString()
}
} }
export async function getPoolSharesData( export async function getPoolSharesData(
@ -388,17 +381,21 @@ export async function getPoolSharesData(
): Promise<PoolShare[]> { ): Promise<PoolShare[]> {
const variables = { user: accountId?.toLowerCase() } const variables = { user: accountId?.toLowerCase() }
const data: PoolShare[] = [] const data: PoolShare[] = []
const result = await fetchDataForMultipleChains( try {
userPoolSharesQuery, const result = await fetchDataForMultipleChains(
variables, userPoolSharesQuery,
chainIds variables,
) chainIds
for (let i = 0; i < result.length; i++) { )
result[i].poolShares.forEach((poolShare: PoolShare) => { for (let i = 0; i < result.length; i++) {
data.push(poolShare) result[i].poolShares.forEach((poolShare: PoolShare) => {
}) data.push(poolShare)
})
}
return data
} catch (error) {
LoggerInstance.error('Error getPoolSharesData: ', error.message)
} }
return data
} }
export async function getUserTokenOrders( export async function getUserTokenOrders(
@ -422,7 +419,7 @@ export async function getUserTokenOrders(
return data return data
} catch (error) { } catch (error) {
LoggerInstance.error(error.message) LoggerInstance.error('Error getUserTokenOrders', error.message)
} }
} }
@ -445,7 +442,7 @@ export async function getUserSales(
} }
return salesSum return salesSum
} catch (error) { } catch (error) {
LoggerInstance.log(error.message) LoggerInstance.error('Error getUserSales', error.message)
} }
} }

View File

@ -36,7 +36,11 @@ export default function AssetComputeSelection({
{asset.symbol} | {asset.did} {asset.symbol} | {asset.did}
</Dotdotdot> </Dotdotdot>
</div> </div>
<PriceUnit price={asset.price} small className={styles.price} /> <PriceUnit
price={asset.price}
size="small"
className={styles.price}
/>
</a> </a>
</Link> </Link>
)) ))

View File

@ -51,7 +51,7 @@ export default function AssetTeaser({
</div> </div>
<footer className={styles.foot}> <footer className={styles.foot}>
<Price accessDetails={asset.accessDetails} small /> <Price accessDetails={asset.accessDetails} size="small" />
<NetworkName networkId={asset.chainId} className={styles.network} /> <NetworkName networkId={asset.chainId} className={styles.network} />
</footer> </footer>
</a> </a>

View File

@ -110,7 +110,7 @@ export default function AssetSelection({
<PriceUnit <PriceUnit
price={asset.price} price={asset.price}
type={asset.price === '0' ? 'free' : undefined} type={asset.price === '0' ? 'free' : undefined}
small size="small"
className={styles.price} className={styles.price}
/> />
</div> </div>

View File

@ -5,11 +5,10 @@ import AssetTitle from '@shared/AssetList/AssetListTitle'
import { useUserPreferences } from '@context/UserPreferences' import { useUserPreferences } from '@context/UserPreferences'
import { gql } from 'urql' import { gql } from 'urql'
import { TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions } from '../../../@types/subgraph/TransactionHistory' import { TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions } from '../../../@types/subgraph/TransactionHistory'
import web3 from 'web3'
import { fetchDataForMultipleChains } from '@utils/subgraph' import { fetchDataForMultipleChains } from '@utils/subgraph'
import { useSiteMetadata } from '@hooks/useSiteMetadata' import { useSiteMetadata } from '@hooks/useSiteMetadata'
import NetworkName from '@shared/NetworkName' import NetworkName from '@shared/NetworkName'
import { retrieveDDOListByDIDs } from '@utils/aquarius' import { getAssetsFromDtList } from '@utils/aquarius'
import { CancelToken } from 'axios' import { CancelToken } from 'axios'
import Title from './Title' import Title from './Title'
import styles from './index.module.css' import styles from './index.module.css'
@ -128,13 +127,14 @@ export default function PoolTransactions({
minimal?: boolean minimal?: boolean
accountId: string accountId: string
}): ReactElement { }): ReactElement {
const [transactions, setTransactions] = useState<PoolTransaction[]>()
const [isLoading, setIsLoading] = useState<boolean>(false)
const { chainIds } = useUserPreferences() const { chainIds } = useUserPreferences()
const { appConfig } = useSiteMetadata() const { appConfig } = useSiteMetadata()
const cancelToken = useCancelToken()
const [transactions, setTransactions] = useState<PoolTransaction[]>()
const [isLoading, setIsLoading] = useState<boolean>(false)
const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>() const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>()
const [data, setData] = useState<PoolTransaction[]>() const [data, setData] = useState<PoolTransaction[]>()
const cancelToken = useCancelToken()
const getPoolTransactionData = useCallback(async () => { const getPoolTransactionData = useCallback(async () => {
const variables = { const variables = {
@ -165,24 +165,18 @@ export default function PoolTransactions({
return return
} }
const poolTransactions: PoolTransaction[] = [] const poolTransactions: PoolTransaction[] = []
const didList: string[] = [] const dtList: string[] = []
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const { address } = data[i].datatoken dtList.push(data[i]?.datatoken?.address)
const did = web3.utils
.toChecksumAddress(address)
.replace('0x', 'did:op:')
didList.push(did)
} }
if (didList.length === 0) {
if (dtList.length === 0) {
setIsLoading(false) setIsLoading(false)
return return
} }
const ddoList = await retrieveDDOListByDIDs( const ddoList = await getAssetsFromDtList(dtList, chainIds, cancelToken)
didList,
chainIds,
cancelToken
)
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
poolTransactions.push({ poolTransactions.push({
...data[i], ...data[i],

View File

@ -2,7 +2,7 @@
display: inline-block; display: inline-block;
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
font-family: var(--font-family-base); font-family: var(--font-family-base);
font-size: var(--font-size-large);
color: var(--font-color-text); color: var(--font-color-text);
line-height: 1.2; line-height: 1.2;
} }
@ -17,6 +17,10 @@
font-size: var(--font-size-base); font-size: var(--font-size-base);
} }
.price.large {
font-size: var(--font-size-large);
}
.price.small { .price.small {
display: inline-block; display: inline-block;
font-size: var(--font-size-base); font-size: var(--font-size-base);
@ -26,6 +30,15 @@
font-size: var(--font-size-small); font-size: var(--font-size-small);
} }
.price.mini {
display: inline-block;
font-size: var(--font-size-small);
}
.price.mini .symbol {
font-size: var(--font-size-mini);
}
.price .badge { .price .badge {
vertical-align: middle; vertical-align: middle;
margin-left: calc(var(--spacer) / 6); margin-left: calc(var(--spacer) / 6);

View File

@ -1,13 +1,10 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { formatCurrency } from '@coingecko/cryptoformat' import { formatCurrency } from '@coingecko/cryptoformat'
import classNames from 'classnames/bind'
import Conversion from './Conversion' import Conversion from './Conversion'
import styles from './PriceUnit.module.css' import styles from './PriceUnit.module.css'
import { useUserPreferences } from '@context/UserPreferences' import { useUserPreferences } from '@context/UserPreferences'
import Badge from '@shared/atoms/Badge' import Badge from '@shared/atoms/Badge'
const cx = classNames.bind(styles)
export function formatPrice(price: string, locale: string): string { export function formatPrice(price: string, locale: string): string {
return formatCurrency(Number(price), '', locale, false, { return formatCurrency(Number(price), '', locale, false, {
// Not exactly clear what `significant figures` are for this library, // Not exactly clear what `significant figures` are for this library,
@ -20,7 +17,7 @@ export function formatPrice(price: string, locale: string): string {
export default function PriceUnit({ export default function PriceUnit({
price, price,
className, className,
small, size = 'small',
conversion, conversion,
symbol, symbol,
type type
@ -28,20 +25,14 @@ export default function PriceUnit({
price: string price: string
type?: string type?: string
className?: string className?: string
small?: boolean size?: 'small' | 'mini' | 'large'
conversion?: boolean conversion?: boolean
symbol?: string symbol?: string
}): ReactElement { }): ReactElement {
const { locale } = useUserPreferences() const { locale } = useUserPreferences()
const styleClasses = cx({
price: true,
small: small,
[className]: className
})
return ( return (
<div className={styleClasses}> <div className={`${styles.price} ${styles[size]} ${className}`}>
{type && type === 'free' ? ( {type && type === 'free' ? (
<div> Free </div> <div> Free </div>
) : ( ) : (

View File

@ -9,21 +9,21 @@ export default function Price({
accessDetails, accessDetails,
orderPriceAndFees, orderPriceAndFees,
className, className,
small, size,
conversion conversion
}: { }: {
accessDetails: AccessDetails accessDetails: AccessDetails
orderPriceAndFees?: OrderPriceAndFees orderPriceAndFees?: OrderPriceAndFees
className?: string className?: string
small?: boolean
conversion?: boolean conversion?: boolean
size?: 'small' | 'mini' | 'large'
}): ReactElement { }): ReactElement {
return accessDetails?.price || accessDetails?.type === 'free' ? ( return accessDetails?.price || accessDetails?.type === 'free' ? (
<PriceUnit <PriceUnit
price={`${orderPriceAndFees?.price || accessDetails?.price}`} price={`${orderPriceAndFees?.price || accessDetails?.price}`}
symbol={accessDetails.baseToken?.symbol} symbol={accessDetails.baseToken?.symbol}
className={className} className={className}
small={small} size={size}
conversion={conversion} conversion={conversion}
type={accessDetails.type} type={accessDetails.type}
/> />

View File

@ -39,7 +39,7 @@ function Row({
<PriceUnit <PriceUnit
price={hasPreviousOrder || hasDatatoken ? '0' : `${price}`} price={hasPreviousOrder || hasDatatoken ? '0' : `${price}`}
symbol={symbol} symbol={symbol}
small size="small"
className={styles.price} className={styles.price}
/> />
<span className={styles.timeout}> <span className={styles.timeout}>
@ -68,7 +68,8 @@ export default function PriceOutput({
return ( return (
<div className={styles.priceComponent}> <div className={styles.priceComponent}>
You will pay <PriceUnit price={`${totalPrice}`} symbol={symbol} small /> You will pay{' '}
<PriceUnit price={`${totalPrice}`} symbol={symbol} size="small" />
<Tooltip <Tooltip
content={ content={
<div className={styles.calculation}> <div className={styles.calculation}>

View File

@ -389,7 +389,7 @@ export default function Compute({
<> <>
<div className={styles.info}> <div className={styles.info}>
<FileIcon file={file} isLoading={fileIsLoading} small /> <FileIcon file={file} isLoading={fileIsLoading} small />
<Price accessDetails={accessDetails} conversion /> <Price accessDetails={accessDetails} conversion size="large" />
</div> </div>
{ddo.metadata.type === 'algorithm' ? ( {ddo.metadata.type === 'algorithm' ? (

View File

@ -258,6 +258,7 @@ export default function Download({
accessDetails={asset.accessDetails} accessDetails={asset.accessDetails}
orderPriceAndFees={orderPriceAndFees} orderPriceAndFees={orderPriceAndFees}
conversion conversion
size="large"
/> />
{!isInPurgatory && <PurchaseButton />} {!isInPurgatory && <PurchaseButton />}
</div> </div>

View File

@ -34,3 +34,7 @@
.noIcon { .noIcon {
opacity: 0; opacity: 0;
} }
.token.mini {
font-size: var(--font-size-mini);
}

View File

@ -6,20 +6,22 @@ import Logo from '@shared/atoms/Logo'
export default function Token({ export default function Token({
symbol, symbol,
balance, balance,
noIcon noIcon,
size
}: { }: {
symbol: string symbol: string
balance: string balance: string
noIcon?: boolean noIcon?: boolean
size?: 'small' | 'mini'
}): ReactElement { }): ReactElement {
return ( return (
<div className={styles.token}> <div className={`${styles.token} ${size ? styles[size] : ''}`}>
<figure <figure
className={`${styles.icon} ${symbol} ${noIcon ? styles.noIcon : ''}`} className={`${styles.icon} ${symbol} ${noIcon ? styles.noIcon : ''}`}
> >
<Logo noWordmark /> <Logo noWordmark />
</figure> </figure>
<PriceUnit price={balance} symbol={symbol} small /> <PriceUnit price={balance} symbol={symbol} size={size} />
</div> </div>
) )
} }

View File

@ -12,31 +12,38 @@ export default function TokenList({
datatokenValue, datatokenValue,
datatokenSymbol, datatokenSymbol,
conversion, conversion,
highlight highlight,
size = 'small'
}: { }: {
title: string | ReactNode title?: string | ReactNode
children?: ReactNode children?: ReactNode
baseTokenValue: string baseTokenValue: string
baseTokenSymbol: string baseTokenSymbol: string
datatokenValue?: string datatokenValue?: string
datatokenSymbol?: string datatokenSymbol?: string
conversion: Decimal conversion?: Decimal
highlight?: boolean highlight?: boolean
size?: 'small' | 'mini'
}): ReactElement { }): ReactElement {
return ( return (
<div className={`${styles.tokenlist} ${highlight ? styles.highlight : ''}`}> <div className={`${styles.tokenlist} ${highlight ? styles.highlight : ''}`}>
<h3 className={styles.title}>{title}</h3> {title && <h3 className={styles.title}>{title}</h3>}
<div className={styles.tokens}> <div className={styles.tokens}>
<Token symbol={baseTokenSymbol} balance={baseTokenValue} /> <Token symbol={baseTokenSymbol} balance={baseTokenValue} size={size} />
{datatokenValue && (
<Token symbol={datatokenSymbol} balance={datatokenValue} /> {conversion?.greaterThan(0) && (
)}
{conversion.greaterThan(0) && (
<Conversion <Conversion
price={conversion.toString()} price={conversion.toString()}
className={styles.totalLiquidity} className={styles.totalLiquidity}
/> />
)} )}
{datatokenValue && (
<Token
symbol={datatokenSymbol}
balance={datatokenValue}
size={size}
/>
)}
{children} {children}
</div> </div>
</div> </div>

View File

@ -16,6 +16,7 @@ import PoolTransactions from '@shared/PoolTransactions'
import Decimal from 'decimal.js' import Decimal from 'decimal.js'
import content from '../../../../../content/price.json' import content from '../../../../../content/price.json'
import { usePool } from '@context/Pool' import { usePool } from '@context/Pool'
import Token from './Token'
export default function Pool(): ReactElement { export default function Pool(): ReactElement {
const { accountId } = useWeb3() const { accountId } = useWeb3()
@ -65,10 +66,16 @@ export default function Pool(): ReactElement {
) : ( ) : (
<> <>
<div className={styles.dataToken}> <div className={styles.dataToken}>
<PriceUnit price="1" symbol={poolInfo?.datatokenSymbol} /> ={' '} <PriceUnit
price="1"
symbol={poolInfo?.datatokenSymbol}
size="large"
/>{' '}
={' '}
<PriceUnit <PriceUnit
price={`${poolData?.spotPrice}`} price={`${poolData?.spotPrice}`}
symbol={poolInfo?.baseTokenSymbol} symbol={poolInfo?.baseTokenSymbol}
size="large"
/> />
<Tooltip content={content.pool.tooltips.price} /> <Tooltip content={content.pool.tooltips.price} />
<div className={styles.dataTokenLinks}> <div className={styles.dataTokenLinks}>
@ -90,7 +97,6 @@ export default function Pool(): ReactElement {
</ExplorerLink> </ExplorerLink>
</div> </div>
</div> </div>
<TokenList <TokenList
title={ title={
<> <>
@ -113,11 +119,10 @@ export default function Pool(): ReactElement {
conversion={poolInfoUser?.liquidity} conversion={poolInfoUser?.liquidity}
highlight highlight
/> />
<TokenList <TokenList
title={ title={
<> <>
Owner Liquidity Owner Value Locked
<span className={styles.titleInfo}> <span className={styles.titleInfo}>
{poolInfoOwner?.poolShare}% of pool {poolInfoOwner?.poolShare}% of pool
</span> </span>
@ -127,7 +132,6 @@ export default function Pool(): ReactElement {
baseTokenSymbol={poolInfo?.baseTokenSymbol} baseTokenSymbol={poolInfo?.baseTokenSymbol}
conversion={poolInfoOwner?.liquidity} conversion={poolInfoOwner?.liquidity}
/> />
<TokenList <TokenList
title={ title={
<> <>
@ -143,21 +147,41 @@ export default function Pool(): ReactElement {
<Graph poolSnapshots={poolSnapshots} /> <Graph poolSnapshots={poolSnapshots} />
</> </>
} }
baseTokenValue={`${poolInfo?.totalLiquidityInOcean}`}
baseTokenSymbol={poolInfo?.baseTokenSymbol}
conversion={poolInfo?.totalLiquidityInOcean}
/>
<TokenList
size="mini"
baseTokenValue={`${poolData?.baseTokenLiquidity}`} baseTokenValue={`${poolData?.baseTokenLiquidity}`}
baseTokenSymbol={poolInfo?.baseTokenSymbol} baseTokenSymbol={poolInfo?.baseTokenSymbol}
datatokenValue={`${poolData?.datatokenLiquidity}`} datatokenValue={`${poolData?.datatokenLiquidity}`}
datatokenSymbol={poolInfo?.datatokenSymbol} datatokenSymbol={poolInfo?.datatokenSymbol}
conversion={poolInfo?.totalLiquidityInOcean}
> >
{/* <Token symbol="% pool fee" balance={poolInfo?.poolFee} noIcon /> <Token
<Token symbol="% market fee" balance={poolInfo?.marketFee} noIcon /> symbol="% pool fee"
<Token symbol="% OPF fee" balance={poolInfo?.opfFee} noIcon /> */} balance={poolInfo?.liquidityProviderSwapFee}
noIcon
size="mini"
/>
<Token
symbol="% market fee"
balance={poolInfo?.publishMarketSwapFee}
noIcon
size="mini"
/>
<Token
symbol="% OPF fee"
balance={poolInfo?.opcFee}
noIcon
size="mini"
/>
</TokenList> </TokenList>
<div className={styles.update}> <div className={styles.update}>
Fetching every {refreshInterval / 1000} sec. Fetching every {refreshInterval / 1000} sec.
</div> </div>
<div className={stylesActions.actions}> <div className={stylesActions.actions}>
<Button <Button
style="primary" style="primary"
@ -178,7 +202,6 @@ export default function Pool(): ReactElement {
</Button> </Button>
)} )}
</div> </div>
{accountId && ( {accountId && (
<AssetActionHistoryTable title="Your Pool Transactions"> <AssetActionHistoryTable title="Your Pool Transactions">
<PoolTransactions <PoolTransactions

View File

@ -1,35 +0,0 @@
import { isValidNumber } from '@utils/numbers'
import Decimal from 'decimal.js'
Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
export async function getMaxPercentRemove(
poolAddress: string,
poolTokens: string
): Promise<string> {
// const amountMaxOcean = await ocean.pool.getOceanMaxRemoveLiquidity(
// poolAddress
// )
// const amountMaxPoolShares =
// await ocean.pool.getPoolSharesRequiredToRemoveOcean(
// poolAddress,
// amountMaxOcean
// )
// let amountMaxPercent =
// isValidNumber(amountMaxPoolShares) && isValidNumber(poolTokens)
// ? new Decimal(amountMaxPoolShares)
// .dividedBy(new Decimal(poolTokens))
// .mul(100)
// .floor()
// .toString()
// : '0'
// if (Number(amountMaxPercent) > 100) {
// amountMaxPercent = '100'
// }
// return amountMaxPercent
return '0'
}

View File

@ -14,7 +14,7 @@ function UserLiquidityLine({
return ( return (
<div> <div>
<span>{title}</span> <span>{title}</span>
<PriceUnit price={amount} symbol={symbol} small /> <PriceUnit price={amount} symbol={symbol} size="small" />
</div> </div>
) )
} }

View File

@ -34,7 +34,7 @@ export default function MarketStatsTooltip({
<PriceUnit <PriceUnit
price={totalOceanLiquidity?.[chainId] || '0'} price={totalOceanLiquidity?.[chainId] || '0'}
symbol="OCEAN" symbol="OCEAN"
small size="small"
/> />
</li> </li>
))} ))}

View File

@ -21,8 +21,12 @@ export default function MarketStatsTotal({
/>{' '} />{' '}
<abbr title="Total Value Locked">TVL</abbr> across{' '} <abbr title="Total Value Locked">TVL</abbr> across{' '}
<strong>{total.pools}</strong> asset pools that contain{' '} <strong>{total.pools}</strong> asset pools that contain{' '}
<PriceUnit price={`${total.totalOceanLiquidity}`} symbol="OCEAN" small />, <PriceUnit
plus datatokens for each pool. price={`${total.totalOceanLiquidity}`}
symbol="OCEAN"
size="small"
/>
, plus datatokens for each pool.
</> </>
) )
} }

View File

@ -36,7 +36,7 @@ const columns = [
{ {
name: 'Price', name: 'Price',
selector: function getAssetRow(row: AssetExtended) { selector: function getAssetRow(row: AssetExtended) {
return <Price accessDetails={row.accessDetails} small /> return <Price accessDetails={row.accessDetails} size="small" />
}, },
right: true right: true
} }

View File

@ -56,7 +56,10 @@ function SectionQueryResult({
const [loading, setLoading] = useState<boolean>() const [loading, setLoading] = useState<boolean>()
const isMounted = useIsMounted() const isMounted = useIsMounted()
const newCancelToken = useCancelToken() const newCancelToken = useCancelToken()
useEffect(() => { useEffect(() => {
if (!query) return
async function init() { async function init() {
if (chainIds.length === 0) { if (chainIds.length === 0) {
const result: PagedAssets = { const result: PagedAssets = {
@ -91,11 +94,13 @@ function SectionQueryResult({
return ( return (
<section className={styles.section}> <section className={styles.section}>
<h3>{title}</h3> <h3>{title}</h3>
<AssetList <AssetList
assets={result?.results} assets={result?.results}
showPagination={false} showPagination={false}
isLoading={loading} isLoading={loading || !query}
/> />
{action && action} {action && action}
</section> </section>
) )
@ -130,28 +135,24 @@ export default function HomePage(): ReactElement {
<Bookmarks /> <Bookmarks />
</section> </section>
{queryAndDids && ( <SectionQueryResult
<SectionQueryResult title="Highest Liquidity"
title="Highest Liquidity" query={queryAndDids?.[0]}
query={queryAndDids[0]} queryData={queryAndDids?.[1]}
queryData={queryAndDids[1]} />
/>
)}
{queryLatest && ( <SectionQueryResult
<SectionQueryResult title="Recently Published"
title="Recently Published" query={queryLatest}
query={queryLatest} action={
action={ <Button
<Button style="text"
style="text" to="/search?sort=metadata.created&sortOrder=desc"
to="/search?sort=metadata.created&sortOrder=desc" >
> All data sets and algorithms
All data sets and algorithms </Button>
</Button> }
} />
/>
)}
</> </>
) )
} }

View File

@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
import { useUserPreferences } from '@context/UserPreferences' import { useUserPreferences } from '@context/UserPreferences'
import ExplorerLink from '@shared/ExplorerLink' import ExplorerLink from '@shared/ExplorerLink'
import NetworkName from '@shared/NetworkName' import NetworkName from '@shared/NetworkName'
import jellyfish from '@oceanprotocol/art/creatures/jellyfish/jellyfish-grid.svg' import Jellyfish from '@oceanprotocol/art/creatures/jellyfish/jellyfish-grid.svg'
import Copy from '@shared/atoms/Copy' import Copy from '@shared/atoms/Copy'
import Blockies from '@shared/atoms/Blockies' import Blockies from '@shared/atoms/Blockies'
import styles from './Account.module.css' import styles from './Account.module.css'
@ -29,12 +29,7 @@ export default function Account({
) : accountId ? ( ) : accountId ? (
<Blockies accountId={accountId} className={styles.image} /> <Blockies accountId={accountId} className={styles.image} />
) : ( ) : (
<img <Jellyfish className={styles.image} />
src={jellyfish}
className={styles.image}
width="96"
height="96"
/>
)} )}
</figure> </figure>

View File

@ -1,28 +1,32 @@
import { LoggerInstance } from '@oceanprotocol/lib' import { LoggerInstance } from '@oceanprotocol/lib'
import React, { useEffect, useState, ReactElement } from 'react' import React, { useEffect, useState, ReactElement } from 'react'
import { useUserPreferences } from '@context/UserPreferences' import { useUserPreferences } from '@context/UserPreferences'
import { import { getAccountTVLInOwnAssets, UserLiquidity } from '@utils/subgraph'
getAccountLiquidityInOwnAssets,
UserLiquidity,
calculateUserLiquidity
} from '@utils/subgraph'
import Conversion from '@shared/Price/Conversion' import Conversion from '@shared/Price/Conversion'
import NumberUnit from './NumberUnit' import NumberUnit from './NumberUnit'
import styles from './Stats.module.css' import styles from './Stats.module.css'
import { useProfile } from '@context/Profile' import { useProfile } from '@context/Profile'
import { PoolShares_poolShares as PoolShare } from '../../../@types/subgraph/PoolShares' import { PoolShares_poolShares as PoolShare } from '../../../@types/subgraph/PoolShares'
import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing' import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing'
import { calculateUserTVL } from '@utils/pool'
import Decimal from 'decimal.js'
import { MAX_DECIMALS } from '@utils/constants'
async function getPoolSharesLiquidity( async function getPoolSharesLiquidity(
poolShares: PoolShare[] poolShares: PoolShare[]
): Promise<number> { ): Promise<string> {
let totalLiquidity = 0 let tvl = new Decimal(0)
for (const poolShare of poolShares) { for (const poolShare of poolShares) {
const poolLiquidity = calculateUserLiquidity(poolShare) const poolUserTvl = calculateUserTVL(
totalLiquidity += poolLiquidity poolShare.shares,
poolShare.pool.totalShares,
poolShare.pool.baseTokenLiquidity
)
tvl = tvl.add(new Decimal(poolUserTvl))
} }
return totalLiquidity return tvl.toDecimalPlaces(MAX_DECIMALS).toString()
} }
export default function Stats({ export default function Stats({
@ -33,13 +37,13 @@ export default function Stats({
const { chainIds } = useUserPreferences() const { chainIds } = useUserPreferences()
const { poolShares, assets, assetsTotal, sales } = useProfile() const { poolShares, assets, assetsTotal, sales } = useProfile()
const [publisherLiquidity, setPublisherLiquidity] = useState<UserLiquidity>() const [publisherTvl, setPublisherTvl] = useState('0')
const [totalLiquidity, setTotalLiquidity] = useState(0) const [totalTvl, setTotalTvl] = useState('0')
useEffect(() => { useEffect(() => {
if (!accountId || chainIds.length === 0) { if (!accountId || chainIds.length === 0) {
setPublisherLiquidity({ price: '0', oceanBalance: '0' }) setPublisherTvl('0')
setTotalLiquidity(0) setTotalTvl('0')
} }
}, [accountId, chainIds]) }, [accountId, chainIds])
@ -57,12 +61,12 @@ export default function Stats({
) )
} }
} }
const userLiquidity = await getAccountLiquidityInOwnAssets( const userTvl = await getAccountTVLInOwnAssets(
accountId, accountId,
chainIds, chainIds,
accountPoolAdresses accountPoolAdresses
) )
setPublisherLiquidity(userLiquidity) setPublisherTvl(userTvl)
} catch (error) { } catch (error) {
LoggerInstance.error(error.message) LoggerInstance.error(error.message)
} }
@ -75,10 +79,10 @@ export default function Stats({
async function getTotalLiquidity() { async function getTotalLiquidity() {
try { try {
const totalLiquidity = await getPoolSharesLiquidity(poolShares) const totalTvl = await getPoolSharesLiquidity(poolShares)
setTotalLiquidity(totalLiquidity) setTotalTvl(totalTvl)
} catch (error) { } catch (error) {
console.error('Error fetching pool shares: ', error.message) LoggerInstance.error('Error fetching pool shares: ', error.message)
} }
} }
getTotalLiquidity() getTotalLiquidity()
@ -87,14 +91,12 @@ export default function Stats({
return ( return (
<div className={styles.stats}> <div className={styles.stats}>
<NumberUnit <NumberUnit
label="Liquidity in Own Assets" label="TVL in Own Assets"
value={ value={<Conversion price={publisherTvl} hideApproximateSymbol />}
<Conversion price={publisherLiquidity?.price} hideApproximateSymbol />
}
/> />
<NumberUnit <NumberUnit
label="Total Liquidity" label="TVL"
value={<Conversion price={`${totalLiquidity}`} hideApproximateSymbol />} value={<Conversion price={totalTvl} hideApproximateSymbol />}
/> />
<NumberUnit label={`Sale${sales === 1 ? '' : 's'}`} value={sales} /> <NumberUnit label={`Sale${sales === 1 ? '' : 's'}`} value={sales} />
<NumberUnit label="Published" value={assetsTotal} /> <NumberUnit label="Published" value={assetsTotal} />

View File

@ -1,60 +0,0 @@
.totalLiquidity {
margin-bottom: 0;
font-weight: var(--font-weight-base) !important;
font-size: var(--font-size-small);
padding-left: var(--font-size-base);
}
.totalLiquidity strong {
font-size: var(--font-size-base);
color: var(--font-color-text);
line-height: 1;
}
.poolSharesTable [role='gridcell'] {
align-items: flex-start;
margin: calc(var(--spacer) / 2) 0;
}
.poolSharesTable [class*='AssetListTitle-module--title'] {
line-height: 0 !important;
}
.poolSharesTable [class*='Token-module--token'] div {
color: var(--color-secondary);
}
@media (min-width: 30rem) {
.poolSharesTable [class*='AssetListTitle-module--title'] {
line-height: 0 !important;
}
}
.userLiquidity {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.userLiquidity [class*='Conversion-module--'] {
margin-bottom: calc(var(--spacer) / 8);
}
.userLiquidity [class*='Conversion-module--'] strong {
font-size: var(--font-size-base);
}
.userLiquidity [class*='Token-module--token'] {
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: calc(var(--spacer) / 8);
}
.userLiquidity [class*='Token-module--token'] div {
font-size: var(--font-size-small);
}
.userLiquidity [class*='Token-module--icon'] {
display: none;
}

View File

@ -1,229 +0,0 @@
import React, { ReactElement, useCallback, useEffect, useState } from 'react'
import Table from '@shared/atoms/Table'
import Conversion from '@shared/Price/Conversion'
import styles from './PoolShares.module.css'
import AssetTitle from '@shared/AssetList/AssetListTitle'
import { PoolShares_poolShares as PoolShare } from '../../../@types/subgraph/PoolShares'
import web3 from 'web3'
import Token from '../../Asset/AssetActions/Pool/Token'
import { calculateUserLiquidity } from '@utils/subgraph'
import NetworkName from '@shared/NetworkName'
import { retrieveDDOListByDIDs } from '@utils/aquarius'
import { CancelToken } from 'axios'
import { isValidNumber } from '@utils/numbers'
import Decimal from 'decimal.js'
import { useProfile } from '@context/Profile'
import { useCancelToken } from '@hooks/useCancelToken'
import { useIsMounted } from '@hooks/useIsMounted'
import { useUserPreferences } from '@context/UserPreferences'
import { Asset } from '@oceanprotocol/lib'
Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
const REFETCH_INTERVAL = 20000
interface AssetPoolShare {
userLiquidity: number
poolShare: PoolShare
networkId: number
createTime: number
asset: Asset
}
function Liquidity({ row, type }: { row: AssetPoolShare; type: string }) {
let price = ``
let oceanTokenBalance = ''
let dataTokenBalance = ''
if (type === 'user') {
price = `${row.userLiquidity}`
const userShare = row.poolShare.shares / row.poolShare.pool.totalShares
oceanTokenBalance = (
userShare * row.poolShare.pool.baseTokenLiquidity
).toString()
dataTokenBalance = (
userShare * row.poolShare.pool.datatokenLiquidity
).toString()
}
if (type === 'pool') {
price =
isValidNumber(row.poolShare.pool.baseTokenLiquidity) &&
isValidNumber(row.poolShare.pool.datatokenLiquidity) &&
isValidNumber(row.poolShare.pool.spotPrice)
? new Decimal(row.poolShare.pool.datatokenLiquidity)
.mul(new Decimal(row.poolShare.pool.spotPrice))
.plus(row.poolShare.pool.baseTokenLiquidity)
.toString()
: '0'
oceanTokenBalance = row.poolShare.pool.baseTokenLiquidity.toString()
dataTokenBalance = row.poolShare.pool.datatokenLiquidity.toString()
}
return (
<div className={styles.userLiquidity}>
<Conversion
price={price}
className={styles.totalLiquidity}
hideApproximateSymbol
/>
<Token
symbol={row.poolShare.pool.baseToken.symbol}
balance={oceanTokenBalance}
noIcon
/>
<Token
symbol={row.poolShare.pool.datatoken.symbol}
balance={dataTokenBalance}
noIcon
/>
</div>
)
}
const columns = [
{
name: 'Data Set',
selector: function getAssetRow(row: AssetPoolShare) {
return <AssetTitle asset={row.asset} />
},
grow: 2
},
{
name: 'Network',
selector: function getNetwork(row: AssetPoolShare) {
return <NetworkName networkId={row.networkId} />
}
},
{
name: 'Datatoken',
selector: function getSymbol(row: AssetPoolShare) {
return <>{row.poolShare.pool.datatoken.symbol}</>
}
},
{
name: 'Liquidity',
selector: function getAssetRow(row: AssetPoolShare) {
return <Liquidity row={row} type="user" />
},
right: true
},
{
name: 'Pool Liquidity',
selector: function getAssetRow(row: AssetPoolShare) {
return <Liquidity row={row} type="pool" />
},
right: true
}
]
async function getPoolSharesAssets(
data: PoolShare[],
chainIds: number[],
cancelToken: CancelToken
) {
if (data.length < 1) return
const assetList: AssetPoolShare[] = []
const didList: string[] = []
for (let i = 0; i < data.length; i++) {
const did = web3.utils
.toChecksumAddress(data[i].pool.datatoken.address)
.replace('0x', 'did:op:')
didList.push(did)
}
const ddoList = await retrieveDDOListByDIDs(didList, chainIds, cancelToken)
for (let i = 0; i < data.length; i++) {
const userLiquidity = calculateUserLiquidity(data[i])
assetList.push({
poolShare: data[i],
userLiquidity: userLiquidity,
networkId: ddoList[i].chainId,
createTime: data[i].pool.createdTimestamp,
asset: ddoList[i]
})
}
const assets = assetList.sort((a, b) => b.createTime - a.createTime)
return assets
}
export default function PoolShares({
accountId
}: {
accountId: string
}): ReactElement {
const { poolShares, isPoolSharesLoading } = useProfile()
const [assets, setAssets] = useState<AssetPoolShare[]>()
const [loading, setLoading] = useState<boolean>(false)
const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>()
const { chainIds } = useUserPreferences()
const newCancelToken = useCancelToken()
const isMounted = useIsMounted()
const fetchPoolSharesAssets = useCallback(
async (
chainIds: number[],
poolShares: PoolShare[],
cancelToken: CancelToken
) => {
try {
const assets = await getPoolSharesAssets(
poolShares,
chainIds,
cancelToken
)
setAssets(assets)
} catch (error) {
console.error('Error fetching pool shares: ', error.message)
} finally {
setLoading(false)
}
},
[]
)
// do not add chainIds,dataFetchInterval to effect dep
useEffect(() => {
const cancelToken = newCancelToken()
async function init() {
setLoading(true)
if (!poolShares || isPoolSharesLoading || !chainIds || !isMounted())
return
await fetchPoolSharesAssets(chainIds, poolShares, cancelToken)
setLoading(false)
if (dataFetchInterval) return
const interval = setInterval(async () => {
await fetchPoolSharesAssets(chainIds, poolShares, cancelToken)
}, REFETCH_INTERVAL)
setDataFetchInterval(interval)
}
init()
return () => {
clearInterval(dataFetchInterval)
}
}, [
fetchPoolSharesAssets,
isPoolSharesLoading,
newCancelToken,
poolShares,
isMounted
])
return accountId ? (
<Table
columns={columns}
className={styles.poolSharesTable}
data={assets}
pagination
paginationPerPage={5}
isLoading={loading}
sortField="userLiquidity"
sortAsc={false}
/>
) : (
<div>Please connect your Web3 wallet.</div>
)
}

View File

@ -0,0 +1,37 @@
.totalLiquidity {
margin-bottom: 0;
font-weight: var(--font-weight-base) !important;
font-size: var(--font-size-small);
padding-left: var(--font-size-base);
}
.totalLiquidity strong {
font-size: var(--font-size-base);
color: var(--font-color-text);
line-height: 1;
}
.userLiquidity {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.userLiquidity [class*='Conversion_conversion'] {
margin-bottom: calc(var(--spacer) / 8);
}
.userLiquidity [class*='Conversion_conversion'] strong {
font-size: var(--font-size-base);
}
.userLiquidity [class*='Token_token'] {
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: calc(var(--spacer) / 8);
}
.userLiquidity [class*='Token_token'] div {
font-size: var(--font-size-small);
}

View File

@ -0,0 +1,59 @@
import React from 'react'
import Conversion from '@shared/Price/Conversion'
import styles from './Liquidity.module.css'
import Token from '../../../Asset/AssetActions/Pool/Token'
import { isValidNumber } from '@utils/numbers'
import Decimal from 'decimal.js'
import { AssetPoolShare } from './index'
import { calculateUserTVL } from '@utils/pool'
export function Liquidity({
row,
type
}: {
row: AssetPoolShare
type: string
}) {
let price = '0'
let liquidity = '0'
if (type === 'user') {
price = new Decimal(row.userLiquidity).mul(2).toString()
// Liquidity in base token, calculated from pool share tokens.
liquidity = calculateUserTVL(
row.poolShare.shares,
row.poolShare.pool.totalShares,
row.poolShare.pool.baseTokenLiquidity
)
}
if (type === 'pool') {
price =
isValidNumber(row.poolShare.pool.baseTokenLiquidity) &&
isValidNumber(row.poolShare.pool.datatokenLiquidity) &&
isValidNumber(row.poolShare.pool.spotPrice)
? new Decimal(row.poolShare.pool.datatokenLiquidity)
.mul(new Decimal(row.poolShare.pool.spotPrice))
.plus(row.poolShare.pool.baseTokenLiquidity)
.toString()
: '0'
liquidity = new Decimal(row.poolShare.pool.baseTokenLiquidity)
.mul(2)
.toString()
}
return (
<div className={styles.userLiquidity}>
<Conversion
price={price}
className={styles.totalLiquidity}
hideApproximateSymbol
/>
<Token
symbol={row.poolShare.pool.baseToken.symbol}
balance={liquidity}
noIcon
/>
</div>
)
}

View File

@ -0,0 +1,49 @@
import { getAssetsFromDtList } from '@utils/aquarius'
import { calculateUserLiquidity } from '@utils/pool'
import { CancelToken } from 'axios'
import { PoolShares_poolShares as PoolShare } from '../../../../@types/subgraph/PoolShares'
import { AssetPoolShare } from '.'
import { Asset } from '@oceanprotocol/lib'
function getAsset(items: Asset[], datatoken: string): Asset {
for (let i = 0; i < items.length; i++) {
if (
items[i].datatokens[0].address.toLowerCase() === datatoken.toLowerCase()
)
return items[i]
}
return null
}
export async function getAssetsFromPoolShares(
data: PoolShare[],
chainIds: number[],
cancelToken: CancelToken
) {
if (data.length < 1) return []
const assetList: AssetPoolShare[] = []
const dtList: string[] = []
for (let i = 0; i < data.length; i++) {
dtList.push(data[i].pool.datatoken.address)
}
const ddoList = await getAssetsFromDtList(dtList, chainIds, cancelToken)
for (let i = 0; i < data.length; i++) {
const userLiquidity = calculateUserLiquidity(
data[i].shares,
data[i].pool.totalShares,
data[i].pool.baseTokenLiquidity
)
console.log(data[i].pool.datatoken.address, userLiquidity)
assetList.push({
poolShare: data[i],
userLiquidity,
networkId: getAsset(ddoList, data[i].pool.datatoken.address).chainId,
createTime: data[i].pool.createdTimestamp,
asset: getAsset(ddoList, data[i].pool.datatoken.address)
})
}
return assetList
}

View File

@ -0,0 +1,18 @@
.poolSharesTable [role='gridcell'] {
align-items: flex-start;
margin: calc(var(--spacer) / 2) 0;
}
.poolSharesTable [class*='AssetListTitle_title'] {
line-height: 0 !important;
}
.poolSharesTable [class*='Token_token'] div {
color: var(--color-secondary);
}
@media (min-width: 30rem) {
.poolSharesTable [class*='AssetListTitle_title'] {
line-height: 0 !important;
}
}

View File

@ -0,0 +1,116 @@
import React, { ReactElement, useCallback, useEffect, useState } from 'react'
import Table from '@shared/atoms/Table'
import styles from './index.module.css'
import AssetTitle from '@shared/AssetList/AssetListTitle'
import { PoolShares_poolShares as PoolShare } from '../../../../@types/subgraph/PoolShares'
import NetworkName from '@shared/NetworkName'
import Decimal from 'decimal.js'
import { useProfile } from '@context/Profile'
import { useCancelToken } from '@hooks/useCancelToken'
import { useIsMounted } from '@hooks/useIsMounted'
import { useUserPreferences } from '@context/UserPreferences'
import { Asset, LoggerInstance } from '@oceanprotocol/lib'
import { Liquidity } from './Liquidity'
import { getAssetsFromPoolShares } from './_utils'
Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
export interface AssetPoolShare {
userLiquidity: string
poolShare: PoolShare
networkId: number
createTime: number
asset: Asset
}
const columns = [
{
name: 'Data Set',
selector: function getAssetRow(row: AssetPoolShare) {
return <AssetTitle asset={row.asset} />
},
grow: 2
},
{
name: 'Network',
selector: function getNetwork(row: AssetPoolShare) {
return <NetworkName networkId={row.networkId} />
}
},
{
name: 'Your Value Locked',
selector: function getAssetRow(row: AssetPoolShare) {
return <Liquidity row={row} type="user" />
},
right: true
},
{
name: 'Total Value Locked',
selector: function getAssetRow(row: AssetPoolShare) {
return <Liquidity row={row} type="pool" />
},
right: true
}
]
export default function PoolShares({
accountId
}: {
accountId: string
}): ReactElement {
const { poolShares } = useProfile()
const { chainIds } = useUserPreferences()
const newCancelToken = useCancelToken()
const isMounted = useIsMounted()
const [assets, setAssets] = useState<AssetPoolShare[]>()
const [loading, setLoading] = useState<boolean>(false)
//
// Helper: fetch assets from pool shares data
//
const fetchPoolSharesAssets = useCallback(async () => {
if (!poolShares || !chainIds) return
try {
const assets = await getAssetsFromPoolShares(
poolShares,
chainIds,
newCancelToken()
)
setAssets(assets)
} catch (error) {
LoggerInstance.error('Error fetching pool shares: ', error.message)
} finally {
setLoading(false)
}
// We do not need to react to `chainIds` changes here, cause changing them
// triggers change of `poolShares` from `useProfile()` already.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [poolShares, newCancelToken])
//
// 1. Kick off data fetching
//
useEffect(() => {
if (!isMounted()) return
setLoading(true)
fetchPoolSharesAssets()
}, [fetchPoolSharesAssets, isMounted])
return accountId ? (
<Table
columns={columns}
className={styles.poolSharesTable}
data={assets}
pagination
paginationPerPage={5}
isLoading={loading}
sortField="userLiquidity"
sortAsc={false}
/>
) : (
<div>Please connect your Web3 wallet.</div>
)
}

View File

@ -49,7 +49,7 @@ export default function Price({
Expected first price:{' '} Expected first price:{' '}
<PriceUnit <PriceUnit
price={Number(firstPrice) > 0 ? firstPrice : '-'} price={Number(firstPrice) > 0 ? firstPrice : '-'}
small size="small"
conversion conversion
/> />
</aside> </aside>