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

Get pool shares using The Graph (#360)

* added graph query

* fixed negative liquidity value for input error

* used graph to get poolShares

* replaced total pool liquidity with user liquidity, get ddo on row

* get symbol from graph, calculate userLiquidity

* fixed userLiquidity price and sorted table by userLiquidity

* removed ordering by balance

* displayed pool and client liquidity, disabled table header hover

* order Your Liquidity before Pool Liquidity

* removed line height on asset title in pool shares table

* limit table to 5 rows, refactor liquidity comp, changed balance color

* code climate similar blocks fix

* changed lockedValue to valueLocked in pool shares query

* removed husky file
This commit is contained in:
Norby 2021-02-10 17:16:24 +02:00 committed by GitHub
parent 3e2ad9674c
commit b043eab047
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 202 additions and 51 deletions

View File

@ -9,10 +9,12 @@ const cx = classNames.bind(styles)
export default function Conversion({
price,
className
className,
hideApproximateSymbol
}: {
price: string // expects price in OCEAN, not wei
className?: string
hideApproximateSymbol?: boolean
}): ReactElement {
const { prices } = usePrices()
const { currency, locale } = useUserPreferences()
@ -59,7 +61,8 @@ export default function Conversion({
className={styleClasses}
title="Approximation based on current OCEAN spot price on Coingecko"
>
<strong dangerouslySetInnerHTML={{ __html: priceConverted }} />{' '}
{!hideApproximateSymbol && '≈ '}
<strong dangerouslySetInnerHTML={{ __html: priceConverted }} />{' '}
{!isFiat && currency}
</span>
)

View File

@ -15,10 +15,14 @@
.table [role='columnheader'] {
text-transform: uppercase;
color: var(--color-secondary);
font-size: var(--font-size-small);
}
.table [role='columnheader'] > span,
.table [role='columnheader'] > div {
color: var(--color-secondary);
}
.table [role='row']:not(:last-of-type) {
border-color: var(--border-color);
}

View File

@ -6,6 +6,9 @@ import styles from './Table.module.css'
interface TableProps extends IDataTableProps {
isLoading?: boolean
emptyMessage?: string
sortField?: string
sortAsc?: boolean
className?: string
}
function Empty({ message }: { message?: string }): ReactElement {
@ -19,13 +22,16 @@ export default function Table({
emptyMessage,
pagination,
paginationPerPage,
sortField,
sortAsc,
className,
...props
}: TableProps): ReactElement {
return (
<DataTable
columns={columns}
data={data}
className={styles.table}
className={className ? styles.table + ` ${className}` : styles.table}
noHeader
pagination={pagination || data?.length >= 9}
paginationPerPage={paginationPerPage || 10}
@ -33,6 +39,8 @@ export default function Table({
noDataComponent={<Empty message={emptyMessage} />}
progressPending={isLoading}
progressComponent={<Loader />}
defaultSortField={sortField}
defaultSortAsc={sortAsc}
{...props}
/>
)

View File

@ -115,6 +115,7 @@ export default function FormAdd({
type="number"
name="amount"
max={amountMax}
min="0"
value={`${values.amount}`}
step="any"
prefix={<CoinSelect dtSymbol={dtSymbol} setCoin={setCoin} />}

View File

@ -148,7 +148,6 @@ export default function Pool(): ReactElement {
const totalCreatorLiquidityInOcean =
creatorLiquidity?.ocean + creatorLiquidity?.datatoken * price?.value
setCreatorTotalLiquidityInOcean(totalCreatorLiquidityInOcean)
const creatorPoolShare =
price?.ocean &&
price?.datatoken &&
@ -174,7 +173,6 @@ export default function Pool(): ReactElement {
const totalUserLiquidityInOcean =
userLiquidity?.ocean + userLiquidity?.datatoken * price?.value
setTotalUserLiquidityInOcean(totalUserLiquidityInOcean)
const totalLiquidityInOcean = price?.ocean + price?.datatoken * price?.value
setTotalLiquidityInOcean(totalLiquidityInOcean)
}, [userLiquidity, price, poolTokens, totalPoolTokens])

View File

@ -3,7 +3,6 @@
font-weight: var(--font-weight-base) !important;
font-size: var(--font-size-small);
padding-left: var(--font-size-base);
padding-top: calc(var(--spacer) / 10);
}
.totalLiquidity strong {
@ -11,3 +10,51 @@
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;
}
}
.yourLiquidity {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.yourLiquidity [class*='Conversion-module--'] {
margin-bottom: calc(var(--spacer) / 8);
}
.yourLiquidity [class*='Conversion-module--'] strong {
font-size: var(--font-size-base);
}
.yourLiquidity [class*='Token-module--token'] {
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: calc(var(--spacer) / 8);
}
.yourLiquidity [class*='Token-module--token'] div {
font-size: var(--font-size-small);
}
.yourLiquidity [class*='Token-module--icon'] {
display: none;
}

View File

@ -1,26 +1,107 @@
import { useMetadata, useOcean } from '@oceanprotocol/react'
import { useOcean } from '@oceanprotocol/react'
import React, { ReactElement, useEffect, useState } from 'react'
import Table from '../../atoms/Table'
import { DDO, Logger, MetadataCache } from '@oceanprotocol/lib'
import PriceUnit from '../../atoms/Price/PriceUnit'
import Conversion from '../../atoms/Price/Conversion'
import styles from './PoolShares.module.css'
import AssetTitle from '../../molecules/AssetListTitle'
import { gql, useQuery } from '@apollo/client'
import {
PoolShares as PoolSharesList,
PoolShares_poolShares as PoolShare,
PoolShares_poolShares_poolId_tokens as PoolSharePoolIdTokens
} from '../../../@types/apollo/PoolShares'
import web3 from 'web3'
import Token from '../../organisms/AssetActions/Pool/Token'
const poolSharesQuery = gql`
query PoolShares($user: String) {
poolShares(where: { userAddress: $user, balance_gt: 0.001 }, first: 1000) {
id
balance
userAddress {
id
}
poolId {
id
datatokenAddress
valueLocked
tokens {
tokenId {
symbol
}
}
oceanReserve
datatokenReserve
totalShares
consumePrice
spotPrice
}
}
}
`
interface Asset {
ddo: DDO
shares: string
userLiquidity: number
poolShare: PoolShare
}
function TotalLiquidity({ ddo }: { ddo: DDO }): ReactElement {
const { price } = useMetadata(ddo)
const totalLiquidityInOcean = price?.ocean + price?.datatoken * price?.value
function calculateUserLiquidity(poolShare: PoolShare) {
const ocean =
(poolShare.balance / poolShare.poolId.totalShares) *
poolShare.poolId.oceanReserve
const datatokens =
(poolShare.balance / poolShare.poolId.totalShares) *
poolShare.poolId.datatokenReserve
const totalLiquidity = ocean + datatokens * poolShare.poolId.consumePrice
return totalLiquidity
}
function findValidToken(tokens: PoolSharePoolIdTokens[]) {
const symbol = tokens.find((token) => token.tokenId !== null)
return symbol.tokenId.symbol
}
function Symbol({ tokens }: { tokens: PoolSharePoolIdTokens[] }) {
return <>{findValidToken(tokens)}</>
}
function Liquidity({ row, type }: { row: Asset; type: string }) {
let price = ``
let oceanTokenBalance = ''
let dataTokenBalance = ''
if (type === 'user') {
price = `${row.userLiquidity}`
const userShare = row.poolShare.balance / row.poolShare.poolId.totalShares
oceanTokenBalance = (
userShare * row.poolShare.poolId.oceanReserve
).toString()
dataTokenBalance = (
userShare * row.poolShare.poolId.datatokenReserve
).toString()
}
if (type === 'pool') {
price = `${
Number(row.poolShare.poolId.oceanReserve) +
Number(row.poolShare.poolId.datatokenReserve) *
row.poolShare.poolId.consumePrice
}`
oceanTokenBalance = row.poolShare.poolId.oceanReserve.toString()
dataTokenBalance = row.poolShare.poolId.datatokenReserve.toString()
}
return (
<Conversion
price={`${totalLiquidityInOcean}`}
className={styles.totalLiquidity}
/>
<div className={styles.yourLiquidity}>
<Conversion
price={price}
className={styles.totalLiquidity}
hideApproximateSymbol
/>
<Token symbol="OCEAN" balance={oceanTokenBalance} noIcon />
<Token
symbol={findValidToken(row.poolShare.poolId.tokens)}
balance={dataTokenBalance}
noIcon
/>
</div>
)
}
@ -28,59 +109,68 @@ const columns = [
{
name: 'Data Set',
selector: function getAssetRow(row: Asset) {
return <AssetTitle ddo={row.ddo} />
const did = web3.utils
.toChecksumAddress(row.poolShare.poolId.datatokenAddress)
.replace('0x', 'did:op:')
return <AssetTitle did={did} />
},
grow: 2
},
{
name: 'Datatoken',
selector: 'ddo.dataTokenInfo.symbol'
selector: function getSymbol(row: Asset) {
return <Symbol tokens={row.poolShare.poolId.tokens} />
}
},
{
name: 'Your Pool Shares',
name: 'Your Liquidity',
selector: function getAssetRow(row: Asset) {
return <PriceUnit price={row.shares} symbol="pool shares" small />
return <Liquidity row={row} type="user" />
},
right: true
},
{
name: 'Total Pool Liquidity',
name: 'Pool Liquidity',
selector: function getAssetRow(row: Asset) {
return <TotalLiquidity ddo={row.ddo} />
return <Liquidity row={row} type="pool" />
},
right: true
}
]
export default function PoolShares(): ReactElement {
const { ocean, accountId, config } = useOcean()
const { accountId } = useOcean()
const [assets, setAssets] = useState<Asset[]>()
const [isLoading, setIsLoading] = useState(false)
const { data, loading } = useQuery<PoolSharesList>(poolSharesQuery, {
variables: {
user: accountId?.toLowerCase()
},
pollInterval: 20000
})
useEffect(() => {
async function getAssets() {
if (!ocean || !accountId || !config?.metadataCacheUri) return
setIsLoading(true)
if (!data) return
const assetList: Asset[] = []
data.poolShares.forEach((poolShare) => {
const userLiquidity = calculateUserLiquidity(poolShare)
assetList.push({
poolShare: poolShare,
userLiquidity: userLiquidity
})
})
setAssets(assetList)
}, [data, loading])
try {
const pools = await ocean.pool.getPoolSharesByAddress(accountId)
const metadataCache = new MetadataCache(config.metadataCacheUri, Logger)
const result: Asset[] = []
for (const pool of pools) {
const ddo = await metadataCache.retrieveDDO(pool.did)
ddo && result.push({ ddo, shares: pool.shares })
}
setAssets(result)
} catch (error) {
Logger.error(error.message)
} finally {
setIsLoading(false)
}
}
getAssets()
}, [ocean, accountId, config.metadataCacheUri])
return <Table columns={columns} data={assets} isLoading={isLoading} />
return (
<Table
columns={columns}
className={styles.poolSharesTable}
data={assets}
pagination
paginationPerPage={5}
isLoading={loading}
sortField="userLiquidity"
sortAsc={false}
/>
)
}