From b043eab047ca4b0a0172e8a2a8b762cd10901c3d Mon Sep 17 00:00:00 2001
From: Norby <37236152+KatunaNorbert@users.noreply.github.com>
Date: Wed, 10 Feb 2021 17:16:24 +0200
Subject: [PATCH] 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
---
src/components/atoms/Price/Conversion.tsx | 7 +-
src/components/atoms/Table.module.css | 6 +-
src/components/atoms/Table.tsx | 10 +-
.../AssetActions/Pool/Add/FormAdd.tsx | 1 +
.../organisms/AssetActions/Pool/index.tsx | 2 -
.../pages/History/PoolShares.module.css | 49 ++++-
src/components/pages/History/PoolShares.tsx | 178 +++++++++++++-----
7 files changed, 202 insertions(+), 51 deletions(-)
diff --git a/src/components/atoms/Price/Conversion.tsx b/src/components/atoms/Price/Conversion.tsx
index cbab887b06..558d9d0733 100644
--- a/src/components/atoms/Price/Conversion.tsx
+++ b/src/components/atoms/Price/Conversion.tsx
@@ -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"
>
- ≈ {' '}
+ {!hideApproximateSymbol && '≈ '}
+ {' '}
{!isFiat && currency}
)
diff --git a/src/components/atoms/Table.module.css b/src/components/atoms/Table.module.css
index 30804dd25d..7150eb98b7 100644
--- a/src/components/atoms/Table.module.css
+++ b/src/components/atoms/Table.module.css
@@ -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);
}
diff --git a/src/components/atoms/Table.tsx b/src/components/atoms/Table.tsx
index 9fd807aeaa..79a764f2b3 100644
--- a/src/components/atoms/Table.tsx
+++ b/src/components/atoms/Table.tsx
@@ -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 (
= 9}
paginationPerPage={paginationPerPage || 10}
@@ -33,6 +39,8 @@ export default function Table({
noDataComponent={}
progressPending={isLoading}
progressComponent={}
+ defaultSortField={sortField}
+ defaultSortAsc={sortAsc}
{...props}
/>
)
diff --git a/src/components/organisms/AssetActions/Pool/Add/FormAdd.tsx b/src/components/organisms/AssetActions/Pool/Add/FormAdd.tsx
index f63c730c14..793b36a4dc 100644
--- a/src/components/organisms/AssetActions/Pool/Add/FormAdd.tsx
+++ b/src/components/organisms/AssetActions/Pool/Add/FormAdd.tsx
@@ -115,6 +115,7 @@ export default function FormAdd({
type="number"
name="amount"
max={amountMax}
+ min="0"
value={`${values.amount}`}
step="any"
prefix={}
diff --git a/src/components/organisms/AssetActions/Pool/index.tsx b/src/components/organisms/AssetActions/Pool/index.tsx
index 7712b8edd4..77e644ee1d 100644
--- a/src/components/organisms/AssetActions/Pool/index.tsx
+++ b/src/components/organisms/AssetActions/Pool/index.tsx
@@ -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])
diff --git a/src/components/pages/History/PoolShares.module.css b/src/components/pages/History/PoolShares.module.css
index cd9d72bc3e..9cd00bbe61 100644
--- a/src/components/pages/History/PoolShares.module.css
+++ b/src/components/pages/History/PoolShares.module.css
@@ -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;
+}
diff --git a/src/components/pages/History/PoolShares.tsx b/src/components/pages/History/PoolShares.tsx
index b892c46586..ec44ff6b6d 100644
--- a/src/components/pages/History/PoolShares.tsx
+++ b/src/components/pages/History/PoolShares.tsx
@@ -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 (
-
+
+
+
+
+
)
}
@@ -28,59 +109,68 @@ const columns = [
{
name: 'Data Set',
selector: function getAssetRow(row: Asset) {
- return
+ const did = web3.utils
+ .toChecksumAddress(row.poolShare.poolId.datatokenAddress)
+ .replace('0x', 'did:op:')
+ return
},
grow: 2
},
{
name: 'Datatoken',
- selector: 'ddo.dataTokenInfo.symbol'
+ selector: function getSymbol(row: Asset) {
+ return
+ }
},
{
- name: 'Your Pool Shares',
+ name: 'Your Liquidity',
selector: function getAssetRow(row: Asset) {
- return
+ return
},
right: true
},
{
- name: 'Total Pool Liquidity',
+ name: 'Pool Liquidity',
selector: function getAssetRow(row: Asset) {
- return
+ return
},
right: true
}
]
export default function PoolShares(): ReactElement {
- const { ocean, accountId, config } = useOcean()
+ const { accountId } = useOcean()
const [assets, setAssets] = useState()
- const [isLoading, setIsLoading] = useState(false)
+ const { data, loading } = useQuery(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
+ return (
+
+ )
}