mirror of
https://github.com/oceanprotocol/market.git
synced 2024-11-15 01:34:57 +01:00
Pool tx history (#307)
* graphql Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * ignore generated Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * delete generated Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix travis Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix travis Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix fetch Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix travis Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix fetch Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * update readme Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * pool creator liquidit& statistics Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * graph with the graph Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * cleanup Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix query Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * update poll interval Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * update graph url Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * ocean bump Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * run apollo codegen before starting gatsby * put back graph loading state * typing fix * graph tweak, add error state * readme update * remove unused functions, move graph provider Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix package-lock * fix graph when switching tabs * generate apollo files into one folder * fix loading Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * graph query Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * tx query Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix titles Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix text issues Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix query Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * local pagination Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * return refreshInterval to 10 sec Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * add Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix data set column Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
a2fe2fdee0
commit
79715df435
@ -87,7 +87,8 @@
|
|||||||
"slugify": "^1.4.6",
|
"slugify": "^1.4.6",
|
||||||
"swr": "^0.3.11",
|
"swr": "^0.3.11",
|
||||||
"use-dark-mode": "^2.3.1",
|
"use-dark-mode": "^2.3.1",
|
||||||
"yup": "^0.32.8"
|
"web3": "^1.3.1",
|
||||||
|
"yup": "^0.32.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.10",
|
"@babel/core": "^7.12.10",
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
NormalizedCacheObject
|
NormalizedCacheObject
|
||||||
} from '@apollo/client'
|
} from '@apollo/client'
|
||||||
import fetch from 'cross-fetch'
|
import fetch from 'cross-fetch'
|
||||||
|
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
|
||||||
const contentQuery = graphql`
|
const contentQuery = graphql`
|
||||||
query AppQuery {
|
query AppQuery {
|
||||||
purgatory: allFile(filter: { relativePath: { eq: "purgatory.json" } }) {
|
purgatory: allFile(filter: { relativePath: { eq: "purgatory.json" } }) {
|
||||||
@ -54,7 +55,7 @@ export default function App({
|
|||||||
const newClient = new ApolloClient({
|
const newClient = new ApolloClient({
|
||||||
link: new HttpLink({
|
link: new HttpLink({
|
||||||
uri: `${
|
uri: `${
|
||||||
(config as any).subgraphUri
|
(config as ConfigHelperConfig).subgraphUri
|
||||||
}/subgraphs/name/oceanprotocol/ocean-subgraph`,
|
}/subgraphs/name/oceanprotocol/ocean-subgraph`,
|
||||||
fetch
|
fetch
|
||||||
}),
|
}),
|
||||||
|
@ -19,8 +19,7 @@ export default function AssetListTitle({
|
|||||||
const [assetTitle, setAssetTitle] = useState<string>(title)
|
const [assetTitle, setAssetTitle] = useState<string>(title)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (assetTitle || !config?.metadataCacheUri) return
|
if (title || !config?.metadataCacheUri) return
|
||||||
|
|
||||||
if (ddo) {
|
if (ddo) {
|
||||||
const { attributes } = ddo.findServiceByType('metadata')
|
const { attributes } = ddo.findServiceByType('metadata')
|
||||||
setAssetTitle(attributes.main.name)
|
setAssetTitle(attributes.main.name)
|
||||||
@ -31,6 +30,8 @@ export default function AssetListTitle({
|
|||||||
|
|
||||||
async function getDDO() {
|
async function getDDO() {
|
||||||
const ddo = await retrieveDDO(did, config.metadataCacheUri, source.token)
|
const ddo = await retrieveDDO(did, config.metadataCacheUri, source.token)
|
||||||
|
|
||||||
|
if (!ddo) return
|
||||||
const { attributes } = ddo.findServiceByType('metadata')
|
const { attributes } = ddo.findServiceByType('metadata')
|
||||||
setAssetTitle(attributes.main.name)
|
setAssetTitle(attributes.main.name)
|
||||||
}
|
}
|
||||||
@ -38,9 +39,10 @@ export default function AssetListTitle({
|
|||||||
!ddo && did && getDDO()
|
!ddo && did && getDDO()
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
console.log('canceled?')
|
||||||
source.cancel()
|
source.cancel()
|
||||||
}
|
}
|
||||||
}, [assetTitle, config?.metadataCacheUri, ddo, did])
|
}, [assetTitle, config?.metadataCacheUri, ddo, did, title])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h3 className={styles.title}>
|
<h3 className={styles.title}>
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
.time {
|
.time {
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.titleText {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { PoolTransaction } from '@oceanprotocol/lib/dist/node/balancer/OceanPool'
|
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import EtherscanLink from '../atoms/EtherscanLink'
|
import EtherscanLink from '../atoms/EtherscanLink'
|
||||||
@ -9,41 +8,61 @@ import styles from './PoolTransactions.module.css'
|
|||||||
import { useUserPreferences } from '../../providers/UserPreferences'
|
import { useUserPreferences } from '../../providers/UserPreferences'
|
||||||
import { Ocean } from '@oceanprotocol/lib'
|
import { Ocean } from '@oceanprotocol/lib'
|
||||||
import { formatPrice } from '../atoms/Price/PriceUnit'
|
import { formatPrice } from '../atoms/Price/PriceUnit'
|
||||||
import { gql } from '@apollo/client'
|
import { gql, useQuery } from '@apollo/client'
|
||||||
|
import {
|
||||||
|
TransactionHistory,
|
||||||
|
TransactionHistoryPoolTransactions
|
||||||
|
} from '../../@types/apollo/TransactionHistory'
|
||||||
|
|
||||||
const txHistoryQuery = gql`
|
import web3 from 'web3'
|
||||||
query Pool($id: ID!, $user: String!) {
|
|
||||||
pool(id: $id) {
|
const txHistoryQueryByPool = gql`
|
||||||
transactions(orderBy: timestamp, where: { userAddressStr: $user }) {
|
query TransactionHistoryByPool($user: String, $pool: String) {
|
||||||
tx
|
poolTransactions(
|
||||||
timestamp
|
orderBy: timestamp
|
||||||
spotPrice
|
orderDirection: desc
|
||||||
event
|
where: { userAddress: $user, poolAddress: $pool }
|
||||||
sharesTransferAmount
|
first: 1000
|
||||||
tokens {
|
) {
|
||||||
type
|
tx
|
||||||
value
|
event
|
||||||
tokenAddress
|
timestamp
|
||||||
poolToken {
|
poolAddress {
|
||||||
tokenId {
|
datatokenAddress
|
||||||
symbol
|
}
|
||||||
name
|
tokens {
|
||||||
}
|
value
|
||||||
tokenAddress
|
type
|
||||||
}
|
tokenAddress
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
const txHistoryQuery = gql`
|
||||||
async function getSymbol(
|
query TransactionHistory($user: String) {
|
||||||
ocean: Ocean,
|
poolTransactions(
|
||||||
tokenAddress: string,
|
orderBy: timestamp
|
||||||
oceanTokenAddress: string
|
orderDirection: desc
|
||||||
) {
|
where: { userAddress: $user }
|
||||||
|
first: 1000
|
||||||
|
) {
|
||||||
|
tx
|
||||||
|
event
|
||||||
|
timestamp
|
||||||
|
poolAddress {
|
||||||
|
datatokenAddress
|
||||||
|
}
|
||||||
|
tokens {
|
||||||
|
value
|
||||||
|
type
|
||||||
|
tokenAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
async function getSymbol(ocean: Ocean, tokenAddress: string) {
|
||||||
const symbol =
|
const symbol =
|
||||||
oceanTokenAddress === tokenAddress
|
ocean.pool.oceanAddress.toLowerCase() === tokenAddress.toLowerCase()
|
||||||
? 'OCEAN'
|
? 'OCEAN'
|
||||||
: await ocean.datatokens.getSymbol(tokenAddress)
|
: await ocean.datatokens.getSymbol(tokenAddress)
|
||||||
|
|
||||||
@ -52,52 +71,81 @@ async function getSymbol(
|
|||||||
|
|
||||||
async function getTitle(
|
async function getTitle(
|
||||||
ocean: Ocean,
|
ocean: Ocean,
|
||||||
row: PoolTransaction,
|
row: TransactionHistoryPoolTransactions,
|
||||||
locale: string,
|
locale: string
|
||||||
oceanTokenAddress: string
|
|
||||||
) {
|
) {
|
||||||
const addRemoveSymbol = await getSymbol(
|
let title = ''
|
||||||
ocean,
|
|
||||||
row.tokenIn || row.tokenOut,
|
|
||||||
oceanTokenAddress
|
|
||||||
)
|
|
||||||
|
|
||||||
const title =
|
switch (row.event) {
|
||||||
row.type === 'join'
|
case 'swap': {
|
||||||
? `Add ${formatPrice(row.tokenAmountIn, locale)} ${addRemoveSymbol}`
|
const inToken = row.tokens.filter((x) => x.type === 'in')[0]
|
||||||
: row.type === 'exit'
|
const inTokenSymbol = await getSymbol(ocean, inToken.tokenAddress)
|
||||||
? `Remove ${formatPrice(row.tokenAmountOut, locale)} ${addRemoveSymbol}`
|
const outToken = row.tokens.filter((x) => x.type === 'out')[0]
|
||||||
: `Swap ${formatPrice(row.tokenAmountIn, locale)} ${await getSymbol(
|
const outTokenSymbol = await getSymbol(ocean, outToken.tokenAddress)
|
||||||
ocean,
|
title += `Swap ${formatPrice(
|
||||||
row.tokenIn,
|
Math.abs(inToken.value).toString(),
|
||||||
oceanTokenAddress
|
locale
|
||||||
)} for ${formatPrice(row.tokenAmountOut, locale)} ${await getSymbol(
|
)}${inTokenSymbol} for ${formatPrice(
|
||||||
ocean,
|
Math.abs(outToken.value).toString(),
|
||||||
row.tokenOut,
|
locale
|
||||||
oceanTokenAddress
|
)}${outTokenSymbol}`
|
||||||
)}`
|
break
|
||||||
|
}
|
||||||
|
case 'setup': {
|
||||||
|
const firstToken = row.tokens.filter(
|
||||||
|
(x) =>
|
||||||
|
x.tokenAddress.toLowerCase() === ocean.pool.oceanAddress.toLowerCase()
|
||||||
|
)[0]
|
||||||
|
const firstTokenSymbol = await getSymbol(ocean, firstToken.tokenAddress)
|
||||||
|
const secondToken = row.tokens.filter(
|
||||||
|
(x) =>
|
||||||
|
x.tokenAddress.toLowerCase() !== ocean.pool.oceanAddress.toLowerCase()
|
||||||
|
)[0]
|
||||||
|
const secondTokenSymbol = await getSymbol(ocean, secondToken.tokenAddress)
|
||||||
|
title += `Create pool with ${formatPrice(
|
||||||
|
Math.abs(firstToken.value).toString(),
|
||||||
|
locale
|
||||||
|
)}${firstTokenSymbol} and ${formatPrice(
|
||||||
|
Math.abs(secondToken.value).toString(),
|
||||||
|
locale
|
||||||
|
)}${secondTokenSymbol}`
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'join':
|
||||||
|
case 'exit': {
|
||||||
|
for (let i = 0; i < row.tokens.length; i++) {
|
||||||
|
const tokenSymbol = await getSymbol(ocean, row.tokens[i].tokenAddress)
|
||||||
|
if (i > 0) title += '\n'
|
||||||
|
title += `${row.event === 'join' ? 'Add' : 'Remove'} ${formatPrice(
|
||||||
|
Math.abs(row.tokens[i].value).toString(),
|
||||||
|
locale
|
||||||
|
)}${tokenSymbol}`
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
|
|
||||||
function Title({ row }: { row: PoolTransaction }) {
|
function Title({ row }: { row: TransactionHistoryPoolTransactions }) {
|
||||||
const { ocean, networkId, config } = useOcean()
|
const { ocean, networkId } = useOcean()
|
||||||
const [title, setTitle] = useState<string>()
|
const [title, setTitle] = useState<string>()
|
||||||
const { locale } = useUserPreferences()
|
const { locale } = useUserPreferences()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ocean || !locale || !row || !config?.oceanTokenAddress) return
|
if (!ocean || !locale || !row) return
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const title = await getTitle(ocean, row, locale, config.oceanTokenAddress)
|
const title = await getTitle(ocean, row, locale)
|
||||||
setTitle(title)
|
setTitle(title)
|
||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
}, [ocean, row, locale, config?.oceanTokenAddress])
|
}, [ocean, row, locale])
|
||||||
|
|
||||||
return title ? (
|
return title ? (
|
||||||
<EtherscanLink networkId={networkId} path={`/tx/${row.transactionHash}`}>
|
<EtherscanLink networkId={networkId} path={`/tx/${row.tx}`}>
|
||||||
{title}
|
<span className={styles.titleText}>{title}</span>
|
||||||
</EtherscanLink>
|
</EtherscanLink>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
@ -106,21 +154,24 @@ function getColumns(minimal?: boolean) {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'Title',
|
name: 'Title',
|
||||||
selector: function getTitleRow(row: PoolTransaction) {
|
selector: function getTitleRow(row: TransactionHistoryPoolTransactions) {
|
||||||
return <Title row={row} />
|
return <Title row={row} />
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Data Set',
|
name: 'Data Set',
|
||||||
selector: function getAssetRow(row: PoolTransaction) {
|
selector: function getAssetRow(row: TransactionHistoryPoolTransactions) {
|
||||||
const did = row.dtAddress.replace('0x', 'did:op:')
|
const did = web3.utils
|
||||||
|
.toChecksumAddress(row.poolAddress.datatokenAddress)
|
||||||
|
.replace('0x', 'did:op:')
|
||||||
|
|
||||||
return <AssetTitle did={did} />
|
return <AssetTitle did={did} />
|
||||||
},
|
},
|
||||||
omit: minimal
|
omit: minimal
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Time',
|
name: 'Time',
|
||||||
selector: function getTimeRow(row: PoolTransaction) {
|
selector: function getTimeRow(row: TransactionHistoryPoolTransactions) {
|
||||||
return (
|
return (
|
||||||
<Time
|
<Time
|
||||||
className={styles.time}
|
className={styles.time}
|
||||||
@ -142,35 +193,30 @@ export default function PoolTransactions({
|
|||||||
poolAddress?: string
|
poolAddress?: string
|
||||||
minimal?: boolean
|
minimal?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean, accountId } = useOcean()
|
const { accountId } = useOcean()
|
||||||
const [logs, setLogs] = useState<PoolTransaction[]>()
|
const [logs, setLogs] = useState<TransactionHistoryPoolTransactions[]>()
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
|
||||||
|
const { data, loading } = useQuery<TransactionHistory>(
|
||||||
|
poolAddress ? txHistoryQueryByPool : txHistoryQuery,
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
user: accountId?.toLowerCase(),
|
||||||
|
pool: poolAddress?.toLowerCase()
|
||||||
|
},
|
||||||
|
pollInterval: 20000
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getLogs() {
|
if (!data) return
|
||||||
if (!ocean || !accountId) return
|
setLogs(data.poolTransactions)
|
||||||
|
}, [data, loading])
|
||||||
setIsLoading(true)
|
|
||||||
const logs = poolAddress
|
|
||||||
? await ocean.pool.getPoolLogs(poolAddress, 0, accountId)
|
|
||||||
: await ocean.pool.getAllPoolLogs(accountId)
|
|
||||||
// sort logs by date, newest first
|
|
||||||
const logsSorted = logs.sort((a, b) => {
|
|
||||||
if (a.timestamp > b.timestamp) return -1
|
|
||||||
if (a.timestamp < b.timestamp) return 1
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
setLogs(logsSorted)
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
getLogs()
|
|
||||||
}, [ocean, accountId, poolAddress])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
columns={getColumns(minimal)}
|
columns={getColumns(minimal)}
|
||||||
data={logs}
|
data={logs}
|
||||||
isLoading={isLoading}
|
isLoading={loading}
|
||||||
noTableHead={minimal}
|
noTableHead={minimal}
|
||||||
dense={minimal}
|
dense={minimal}
|
||||||
pagination={minimal ? logs?.length >= 4 : logs?.length >= 9}
|
pagination={minimal ? logs?.length >= 4 : logs?.length >= 9}
|
||||||
|
@ -3,14 +3,11 @@ import Button from '../../../atoms/Button'
|
|||||||
import PoolTransactions from '../../../molecules/PoolTransactions'
|
import PoolTransactions from '../../../molecules/PoolTransactions'
|
||||||
import styles from './Transactions.module.css'
|
import styles from './Transactions.module.css'
|
||||||
import { ReactComponent as Caret } from '../../../../images/caret.svg'
|
import { ReactComponent as Caret } from '../../../../images/caret.svg'
|
||||||
|
import { useAsset } from '../../../../providers/Asset'
|
||||||
|
|
||||||
export default function Transactions({
|
export default function Transactions(): ReactElement {
|
||||||
poolAddress
|
|
||||||
}: {
|
|
||||||
poolAddress: string
|
|
||||||
}): ReactElement {
|
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
const { ddo } = useAsset()
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
setOpen(!open)
|
setOpen(!open)
|
||||||
}
|
}
|
||||||
@ -31,7 +28,9 @@ export default function Transactions({
|
|||||||
{open ? 'Hide' : 'Show'} <Caret />
|
{open ? 'Hide' : 'Show'} <Caret />
|
||||||
</Button>
|
</Button>
|
||||||
</h3>
|
</h3>
|
||||||
{open === true && <PoolTransactions poolAddress={poolAddress} minimal />}
|
{open === true && (
|
||||||
|
<PoolTransactions poolAddress={ddo.price?.address} minimal />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -343,7 +343,7 @@ export default function Pool(): ReactElement {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{accountId && <Transactions poolAddress={price?.address} />}
|
{accountId && <Transactions />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -68,7 +68,7 @@ function AssetProvider({
|
|||||||
ddo.price.address
|
ddo.price.address
|
||||||
)
|
)
|
||||||
setPrice(newPrice)
|
setPrice(newPrice)
|
||||||
Logger.log(`Refreshed asset price: ${newPrice?.value}`)
|
Logger.log(`Refreshed asset price: ${newPrice?.value}`, newPrice)
|
||||||
}, [ocean, config, ddo, networkId, status])
|
}, [ocean, config, ddo, networkId, status])
|
||||||
|
|
||||||
const fetchDdo = async (token?: CancelToken) => {
|
const fetchDdo = async (token?: CancelToken) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user