Skip to content

Commit

Permalink
Get pool shares using The Graph (#360)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
KatunaNorbert authored Feb 10, 2021
1 parent 3e2ad96 commit b043eab
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 51 deletions.
7 changes: 5 additions & 2 deletions src/components/atoms/Price/Conversion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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>
)
Expand Down
6 changes: 5 additions & 1 deletion src/components/atoms/Table.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
10 changes: 9 additions & 1 deletion src/components/atoms/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -19,20 +22,25 @@ 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}
paginationComponentOptions={{ noRowsPerPage: true }}
noDataComponent={<Empty message={emptyMessage} />}
progressPending={isLoading}
progressComponent={<Loader />}
defaultSortField={sortField}
defaultSortAsc={sortAsc}
{...props}
/>
)
Expand Down
1 change: 1 addition & 0 deletions src/components/organisms/AssetActions/Pool/Add/FormAdd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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} />}
Expand Down
2 changes: 0 additions & 2 deletions src/components/organisms/AssetActions/Pool/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand All @@ -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])
Expand Down
49 changes: 48 additions & 1 deletion src/components/pages/History/PoolShares.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,58 @@
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 {
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;
}
}

.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;
}
178 changes: 134 additions & 44 deletions src/components/pages/History/PoolShares.tsx
Original file line number Diff line number Diff line change
@@ -1,86 +1,176 @@
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>
)
}

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)

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 })
}
if (!data) return
const assetList: Asset[] = []
data.poolShares.forEach((poolShare) => {
const userLiquidity = calculateUserLiquidity(poolShare)
assetList.push({
poolShare: poolShare,
userLiquidity: userLiquidity
})
})
setAssets(assetList)
}, [data, loading])

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}
/>
)
}

1 comment on commit b043eab

@vercel
Copy link

@vercel vercel bot commented on b043eab Feb 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.