1
0
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:
mihaisc 2021-01-22 19:05:02 +02:00 committed by GitHub
parent a2fe2fdee0
commit 79715df435
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 152 additions and 99 deletions

View File

@ -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",

View File

@ -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
}), }),

View File

@ -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}>

View File

@ -1,3 +1,7 @@
.time { .time {
color: var(--color-secondary); color: var(--color-secondary);
} }
.titleText {
white-space: pre;
}

View File

@ -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) {
poolTransactions(
orderBy: timestamp
orderDirection: desc
where: { userAddress: $user, poolAddress: $pool }
first: 1000
) {
tx tx
timestamp
spotPrice
event event
sharesTransferAmount timestamp
poolAddress {
datatokenAddress
}
tokens { tokens {
type
value value
type
tokenAddress tokenAddress
poolToken {
tokenId {
symbol
name
}
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}

View File

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

View File

@ -343,7 +343,7 @@ export default function Pool(): ReactElement {
)} )}
</div> </div>
{accountId && <Transactions poolAddress={price?.address} />} {accountId && <Transactions />}
</> </>
)} )}
</> </>

View File

@ -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) => {