mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Pool statistics from the graph (#288)
* 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> * fix codegen camelcase Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * bump apollo packages * document subgraph usage, add example * rewrite into Data Sources, add quick examples * more data sources docs * docs updates, typos Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
83dca873f8
commit
273769388c
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@ public/storybook
|
|||||||
.artifacts
|
.artifacts
|
||||||
.vercel
|
.vercel
|
||||||
repo-metadata.json
|
repo-metadata.json
|
||||||
|
src/@types/apollo
|
@ -17,6 +17,7 @@ before_script:
|
|||||||
# - './cc-test-reporter before-build'
|
# - './cc-test-reporter before-build'
|
||||||
script:
|
script:
|
||||||
# will run `npm ci` automatically here
|
# will run `npm ci` automatically here
|
||||||
|
- npm run apollo:codegen
|
||||||
- npm run lint
|
- npm run lint
|
||||||
# - './cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT'
|
# - './cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT'
|
||||||
- npm run build
|
- npm run build
|
||||||
|
167
README.md
167
README.md
@ -13,6 +13,11 @@
|
|||||||
- [🏄 Get Started](#-get-started)
|
- [🏄 Get Started](#-get-started)
|
||||||
- [Local components with Barge](#local-components-with-barge)
|
- [Local components with Barge](#local-components-with-barge)
|
||||||
- [🦑 Environment variables](#-environment-variables)
|
- [🦑 Environment variables](#-environment-variables)
|
||||||
|
- [🦀 Data Sources](#-data-sources)
|
||||||
|
- [Aquarius](#aquarius)
|
||||||
|
- [Ocean Protocol Subgraph](#ocean-protocol-subgraph)
|
||||||
|
- [3Box](#3box)
|
||||||
|
- [Purgatory](#purgatory)
|
||||||
- [🎨 Storybook](#-storybook)
|
- [🎨 Storybook](#-storybook)
|
||||||
- [✨ Code Style](#-code-style)
|
- [✨ Code Style](#-code-style)
|
||||||
- [👩🔬 Testing](#-testing)
|
- [👩🔬 Testing](#-testing)
|
||||||
@ -71,8 +76,158 @@ For local development, you can use a `.env` file:
|
|||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🦀 Data Sources
|
||||||
|
|
||||||
|
All displayed data in the app is presented around the concept of one data set, which is a combination of:
|
||||||
|
|
||||||
|
- metadata about a data set
|
||||||
|
- the actual data set files
|
||||||
|
- the datatoken which represents the data set
|
||||||
|
- financial data connected to this datatoken, either a pool or a fixed rate exchange contract
|
||||||
|
- calculations and conversions based on financial data
|
||||||
|
- metadata about publishers
|
||||||
|
|
||||||
|
All this data then comes from multiple sources:
|
||||||
|
|
||||||
|
### Aquarius
|
||||||
|
|
||||||
|
All initial data sets and their metadata (DDO) is retrieved client-side on run-time from the [Aquarius](https://github.com/oceanprotocol/aquarius) instance for each network. All app calls to Aquarius are done with 2 internal methods which mimic the same methods in ocean.js, but allow us:
|
||||||
|
|
||||||
|
- to cancel requests when components get unmounted in combination with [axios](https://github.com/axios/axios)
|
||||||
|
- hit Aquarius as early as possible without relying on any ocean.js initialization
|
||||||
|
|
||||||
|
Aquarius runs Elasticsearch under the hood so its stored metadata can be queried with [Elasticsearch queries](https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html) like so:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||||
|
import { queryMetadata } from '../../utils/aquarius'
|
||||||
|
|
||||||
|
const queryLatest = {
|
||||||
|
page: 1,
|
||||||
|
offset: 9,
|
||||||
|
query: {
|
||||||
|
nativeSearch: 1,
|
||||||
|
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html
|
||||||
|
query_string: { query: `-isInPurgatory:true` }
|
||||||
|
},
|
||||||
|
sort: { created: -1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
function Component() {
|
||||||
|
const { config } = useOcean()
|
||||||
|
const [result, setResult] = useState<QueryResult>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!config?.metadataCacheUri) return
|
||||||
|
const source = axios.CancelToken.source()
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
const result = await queryMetadata(
|
||||||
|
query,
|
||||||
|
config.metadataCacheUri,
|
||||||
|
source.token
|
||||||
|
)
|
||||||
|
setResult(result)
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
source.cancel()
|
||||||
|
}
|
||||||
|
}, [config?.metadataCacheUri, query])
|
||||||
|
|
||||||
|
return <div>{result}</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For components within a single data set view the `useAsset()` hook can be used, which in the background gets the respective metadata from Aquarius.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useAsset } from '../../../providers/Asset'
|
||||||
|
|
||||||
|
function Component() {
|
||||||
|
const { ddo } = useAsset()
|
||||||
|
return <div>{ddo}</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ocean Protocol Subgraph
|
||||||
|
|
||||||
|
Most financial data in the market is retrieved with GraphQL from [our own subgraph](https://github.com/oceanprotocol/ocean-subgraph), rendered on top of the initial data coming from Aquarius.
|
||||||
|
|
||||||
|
The app has [Apollo Client](https://www.apollographql.com/docs/react/) setup to query the respective subgraph based on network. In any component this client can be used like so:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { gql, useQuery } from '@apollo/client'
|
||||||
|
|
||||||
|
const query = gql`
|
||||||
|
query PoolLiquidity($id: ID!, $shareId: ID) {
|
||||||
|
pool(id: $id) {
|
||||||
|
id
|
||||||
|
totalShares
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
function Component() {
|
||||||
|
const { data } = useQuery(query, {}, pollInterval: 5000 })
|
||||||
|
return <div>{data}</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3Box
|
||||||
|
|
||||||
|
Publishers can create a profile on [3Box Hub](https://www.3box.io/hub) and when found, it will be displayed in the app.
|
||||||
|
|
||||||
|
For this our own [3box-proxy](https://github.com/oceanprotocol/3box-proxy) is used, within the app the utility method `get3BoxProfile()` can be used to get all info:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import get3BoxProfile from '../../../utils/profile'
|
||||||
|
|
||||||
|
function Component() {
|
||||||
|
const [profile, setProfile] = useState<Profile>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!account) return
|
||||||
|
const source = axios.CancelToken.source()
|
||||||
|
|
||||||
|
async function get3Box() {
|
||||||
|
const profile = await get3BoxProfile(account, source.token)
|
||||||
|
if (!profile) return
|
||||||
|
|
||||||
|
setProfile(profile)
|
||||||
|
}
|
||||||
|
get3Box()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
source.cancel()
|
||||||
|
}
|
||||||
|
}, [account])
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{profile.emoji} {profile.name}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Purgatory
|
||||||
|
|
||||||
|
Based on [list-purgatory](https://github.com/oceanprotocol/list-purgatory) some data sets get additional data. Within most components this can be done with the internal `useAsset()` hook which fetches data from the [market-purgatory](https://github.com/oceanprotocol/market-purgatory) endpoint in the background.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useAsset } from '../../../providers/Asset'
|
||||||
|
|
||||||
|
function Component() {
|
||||||
|
const { isInPurgatory, purgatoryData } = useAsset()
|
||||||
|
return isInPurgatory ? <div>{purgatoryData.reason}</div> : null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 🎨 Storybook
|
## 🎨 Storybook
|
||||||
|
|
||||||
|
> TODO: this is broken for most components. See https://github.com/oceanprotocol/market/issues/128
|
||||||
|
|
||||||
[Storybook](https://storybook.js.org) is set up for this project and is used for UI development of components. Stories are created inside `src/components/` alongside each component in the form of `ComponentName.stories.tsx`.
|
[Storybook](https://storybook.js.org) is set up for this project and is used for UI development of components. Stories are created inside `src/components/` alongside each component in the form of `ComponentName.stories.tsx`.
|
||||||
|
|
||||||
To run the Storybook server, execute in your Terminal:
|
To run the Storybook server, execute in your Terminal:
|
||||||
@ -97,6 +252,8 @@ npm run format
|
|||||||
|
|
||||||
## 👩🔬 Testing
|
## 👩🔬 Testing
|
||||||
|
|
||||||
|
> TODO: this is broken and never runs in CI. See https://github.com/oceanprotocol/market/issues/128
|
||||||
|
|
||||||
Test suite for unit tests is setup with [Jest](https://jestjs.io) as a test runner and:
|
Test suite for unit tests is setup with [Jest](https://jestjs.io) as a test runner and:
|
||||||
|
|
||||||
- [react-testing-library](https://github.com/kentcdodds/react-testing-library) for all React components
|
- [react-testing-library](https://github.com/kentcdodds/react-testing-library) for all React components
|
||||||
@ -131,9 +288,15 @@ npm run serve
|
|||||||
|
|
||||||
## ⬆️ Deployment
|
## ⬆️ Deployment
|
||||||
|
|
||||||
Every branch or Pull Request is automatically deployed by [Netlify](https://netlify.com) with their GitHub integration. A link to a deployment will appear under each Pull Request.
|
Every branch or Pull Request is automatically deployed to multiple hosts for redundancy and emergency reasons:
|
||||||
|
|
||||||
The latest deployment of the `main` branch is automatically aliased to `market.oceanprotocol.com`.
|
- [Netlify](https://netlify.com)
|
||||||
|
- [Vercel](https://vercel.com)
|
||||||
|
- [S3](https://aws.amazon.com/s3/)
|
||||||
|
|
||||||
|
A link to a deployment will appear under each Pull Request.
|
||||||
|
|
||||||
|
The latest deployment of the `main` branch is automatically aliased to `market.oceanprotocol.com`, where the deployment on Netlify is the current live deployment.
|
||||||
|
|
||||||
## 💖 Contributing
|
## 💖 Contributing
|
||||||
|
|
||||||
|
11
apollo.config.js
Normal file
11
apollo.config.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = {
|
||||||
|
client: {
|
||||||
|
service: {
|
||||||
|
name: 'ocean',
|
||||||
|
url:
|
||||||
|
'https://subgraph.mainnet.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph',
|
||||||
|
// optional disable SSL validation check
|
||||||
|
skipSSLValidation: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,12 @@ const createMarkdownPages = require('./gatsby/createMarkdownPages')
|
|||||||
const execSync = require('child_process').execSync
|
const execSync = require('child_process').execSync
|
||||||
|
|
||||||
// Write out repo metadata
|
// Write out repo metadata
|
||||||
execSync(`node ./scripts/write-repo-metadata > repo-metadata.json`)
|
execSync(`node ./scripts/write-repo-metadata > repo-metadata.json`, {
|
||||||
|
stdio: 'inherit'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Generate Apollo typings
|
||||||
|
execSync(`npm run apollo:codegen`, { stdio: 'inherit' })
|
||||||
|
|
||||||
exports.onCreateNode = ({ node, actions, getNode }) => {
|
exports.onCreateNode = ({ node, actions, getNode }) => {
|
||||||
createFields(node, actions, getNode)
|
createFields(node, actions, getNode)
|
||||||
|
2763
package-lock.json
generated
2763
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@
|
|||||||
"build": "gatsby build && cp _redirects public/_redirects",
|
"build": "gatsby build && cp _redirects public/_redirects",
|
||||||
"serve": "serve -s public/",
|
"serve": "serve -s public/",
|
||||||
"jest": "NODE_ENV=test jest -c tests/unit/jest.config.js",
|
"jest": "NODE_ENV=test jest -c tests/unit/jest.config.js",
|
||||||
"test": "npm run lint && npm run jest",
|
"test": "npm run apollo:codegen && npm run lint && npm run jest",
|
||||||
"test:watch": "npm run lint && npm run jest -- --watch",
|
"test:watch": "npm run lint && npm run jest -- --watch",
|
||||||
"lint": "npm run write:repoMetadata && eslint --ignore-path .gitignore --ext .js --ext .ts --ext .tsx . && npm run type-check",
|
"lint": "npm run write:repoMetadata && eslint --ignore-path .gitignore --ext .js --ext .ts --ext .tsx . && npm run type-check",
|
||||||
"format": "prettier --ignore-path .gitignore './**/*.{css,yml,js,ts,tsx,json}' --write",
|
"format": "prettier --ignore-path .gitignore './**/*.{css,yml,js,ts,tsx,json}' --write",
|
||||||
@ -18,9 +18,11 @@
|
|||||||
"storybook": "start-storybook -p 4000 -c .storybook",
|
"storybook": "start-storybook -p 4000 -c .storybook",
|
||||||
"storybook:build": "build-storybook -c .storybook -o public/storybook",
|
"storybook:build": "build-storybook -c .storybook -o public/storybook",
|
||||||
"write:repoMetadata": "node ./scripts/write-repo-metadata > repo-metadata.json",
|
"write:repoMetadata": "node ./scripts/write-repo-metadata > repo-metadata.json",
|
||||||
"deploy": "./scripts/deploy.sh"
|
"deploy": "./scripts/deploy.sh",
|
||||||
|
"apollo:codegen": "mkdir -p src/@types/apollo/ && apollo client:codegen --target typescript --tsFileExtension='d.ts' --outputFlat './src/@types/apollo/' && find ./src/@types/apollo -type f -exec sed -i -r 's/_([a-z])/\\U\\1/gi' {} \\;"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@apollo/client": "^3.3.6",
|
||||||
"@coingecko/cryptoformat": "^0.4.2",
|
"@coingecko/cryptoformat": "^0.4.2",
|
||||||
"@loadable/component": "^5.14.1",
|
"@loadable/component": "^5.14.1",
|
||||||
"@oceanprotocol/art": "^3.0.0",
|
"@oceanprotocol/art": "^3.0.0",
|
||||||
@ -36,6 +38,7 @@
|
|||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"chart.js": "^2.9.4",
|
"chart.js": "^2.9.4",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
|
"cross-fetch": "^3.0.6",
|
||||||
"date-fns": "^2.16.1",
|
"date-fns": "^2.16.1",
|
||||||
"decimal.js": "^10.2.1",
|
"decimal.js": "^10.2.1",
|
||||||
"dom-confetti": "^0.2.2",
|
"dom-confetti": "^0.2.2",
|
||||||
@ -109,6 +112,7 @@
|
|||||||
"@types/remove-markdown": "^0.1.1",
|
"@types/remove-markdown": "^0.1.1",
|
||||||
"@types/shortid": "0.0.29",
|
"@types/shortid": "0.0.29",
|
||||||
"@types/yup": "^0.29.11",
|
"@types/yup": "^0.29.11",
|
||||||
|
"apollo": "^2.32.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.13.0",
|
"@typescript-eslint/eslint-plugin": "^4.13.0",
|
||||||
"@typescript-eslint/parser": "^4.13.0",
|
"@typescript-eslint/parser": "^4.13.0",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import Footer from './organisms/Footer'
|
import Footer from './organisms/Footer'
|
||||||
import Header from './organisms/Header'
|
import Header from './organisms/Header'
|
||||||
import Styles from '../global/Styles'
|
import Styles from '../global/Styles'
|
||||||
@ -7,7 +7,14 @@ import { useSiteMetadata } from '../hooks/useSiteMetadata'
|
|||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import Alert from './atoms/Alert'
|
import Alert from './atoms/Alert'
|
||||||
import { graphql, PageProps, useStaticQuery } from 'gatsby'
|
import { graphql, PageProps, useStaticQuery } from 'gatsby'
|
||||||
|
import {
|
||||||
|
ApolloClient,
|
||||||
|
ApolloProvider,
|
||||||
|
HttpLink,
|
||||||
|
InMemoryCache,
|
||||||
|
NormalizedCacheObject
|
||||||
|
} from '@apollo/client'
|
||||||
|
import fetch from 'cross-fetch'
|
||||||
const contentQuery = graphql`
|
const contentQuery = graphql`
|
||||||
query AppQuery {
|
query AppQuery {
|
||||||
purgatory: allFile(filter: { relativePath: { eq: "purgatory.json" } }) {
|
purgatory: allFile(filter: { relativePath: { eq: "purgatory.json" } }) {
|
||||||
@ -37,18 +44,31 @@ export default function App({
|
|||||||
const { warning } = useSiteMetadata()
|
const { warning } = useSiteMetadata()
|
||||||
const {
|
const {
|
||||||
isInPurgatory: isAccountInPurgatory,
|
isInPurgatory: isAccountInPurgatory,
|
||||||
purgatoryData: accountPurgatory
|
purgatoryData: accountPurgatory,
|
||||||
|
config
|
||||||
} = useOcean()
|
} = useOcean()
|
||||||
|
|
||||||
|
const [client, setClient] = useState<ApolloClient<NormalizedCacheObject>>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const newClient = new ApolloClient({
|
||||||
|
link: new HttpLink({
|
||||||
|
uri: `${
|
||||||
|
(config as any).subgraphUri
|
||||||
|
}/subgraphs/name/oceanprotocol/ocean-subgraph`,
|
||||||
|
fetch
|
||||||
|
}),
|
||||||
|
cache: new InMemoryCache()
|
||||||
|
})
|
||||||
|
setClient(newClient)
|
||||||
|
}, [config])
|
||||||
return (
|
return (
|
||||||
<Styles>
|
<Styles>
|
||||||
<div className={styles.app}>
|
<div className={styles.app}>
|
||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
{(props as PageProps).uri === '/' && (
|
{(props as PageProps).uri === '/' && (
|
||||||
<Alert text={warning} state="info" />
|
<Alert text={warning} state="info" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isAccountInPurgatory && (
|
{isAccountInPurgatory && (
|
||||||
<Alert
|
<Alert
|
||||||
title={purgatory.title}
|
title={purgatory.title}
|
||||||
@ -57,8 +77,13 @@ export default function App({
|
|||||||
state="error"
|
state="error"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{client ? (
|
||||||
|
<ApolloProvider client={client}>
|
||||||
<main className={styles.main}>{children}</main>
|
<main className={styles.main}>{children}</main>
|
||||||
|
</ApolloProvider>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
</Styles>
|
</Styles>
|
||||||
|
@ -9,6 +9,33 @@ 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'
|
||||||
|
|
||||||
|
const txHistoryQuery = gql`
|
||||||
|
query Pool($id: ID!, $user: String!) {
|
||||||
|
pool(id: $id) {
|
||||||
|
transactions(orderBy: timestamp, where: { userAddressStr: $user }) {
|
||||||
|
tx
|
||||||
|
timestamp
|
||||||
|
spotPrice
|
||||||
|
event
|
||||||
|
sharesTransferAmount
|
||||||
|
tokens {
|
||||||
|
type
|
||||||
|
value
|
||||||
|
tokenAddress
|
||||||
|
poolToken {
|
||||||
|
tokenId {
|
||||||
|
symbol
|
||||||
|
name
|
||||||
|
}
|
||||||
|
tokenAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
async function getSymbol(
|
async function getSymbol(
|
||||||
ocean: Ocean,
|
ocean: Ocean,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
import React, { ChangeEvent, ReactElement, useEffect, useState } from 'react'
|
import React, { ChangeEvent, ReactElement, useEffect, useState } from 'react'
|
||||||
import { Line, defaults } from 'react-chartjs-2'
|
import { Line, defaults } from 'react-chartjs-2'
|
||||||
import {
|
import {
|
||||||
@ -15,21 +16,16 @@ import useDarkMode from 'use-dark-mode'
|
|||||||
import { darkModeConfig } from '../../../../../app.config'
|
import { darkModeConfig } from '../../../../../app.config'
|
||||||
import Button from '../../../atoms/Button'
|
import Button from '../../../atoms/Button'
|
||||||
import { Logger } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
|
import { useAsset } from '../../../../providers/Asset'
|
||||||
export interface ChartDataLiqudity {
|
import { gql, useQuery } from '@apollo/client'
|
||||||
oceanAddRemove: ChartData[]
|
import { PoolHistory } from '../../../../@types/apollo/PoolHistory'
|
||||||
datatokenAddRemove: ChartData[]
|
|
||||||
oceanReserveHistory: ChartData[]
|
|
||||||
datatokenReserveHistory: ChartData[]
|
|
||||||
datatokenPriceHistory: ChartData[]
|
|
||||||
}
|
|
||||||
|
|
||||||
declare type GraphType = 'liquidity' | 'price'
|
declare type GraphType = 'liquidity' | 'price'
|
||||||
|
|
||||||
// Chart.js global defaults
|
// Chart.js global defaults
|
||||||
defaults.global.defaultFontFamily = `'Sharp Sans', -apple-system, BlinkMacSystemFont,
|
defaults.global.defaultFontFamily = `'Sharp Sans', -apple-system, BlinkMacSystemFont,
|
||||||
'Segoe UI', Helvetica, Arial, sans-serif`
|
'Segoe UI', Helvetica, Arial, sans-serif`
|
||||||
defaults.global.animation = { easing: 'easeInOutQuart', duration: 800 }
|
defaults.global.animation = { easing: 'easeInOutQuart', duration: 1000 }
|
||||||
|
|
||||||
const lineStyle: Partial<ChartDataSets> = {
|
const lineStyle: Partial<ChartDataSets> = {
|
||||||
fill: false,
|
fill: false,
|
||||||
@ -57,28 +53,6 @@ const tooltipOptions: Partial<ChartTooltipOptions> = {
|
|||||||
caretSize: 7
|
caretSize: 7
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructGraphData(data: ChartData[]): ChartData {
|
|
||||||
const timestamps = data.map((item: any) => {
|
|
||||||
// convert timestamps from epoch to locale date & time string
|
|
||||||
const date = new Date(item[1] * 1000)
|
|
||||||
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
|
|
||||||
})
|
|
||||||
const values = data.map((item: any) => item[0])
|
|
||||||
|
|
||||||
return {
|
|
||||||
labels: timestamps,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
...lineStyle,
|
|
||||||
label: 'Liquidity (OCEAN)',
|
|
||||||
data: values,
|
|
||||||
borderColor: `#8b98a9`,
|
|
||||||
pointBackgroundColor: `#8b98a9`
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOptions(locale: string, isDarkMode: boolean): ChartOptions {
|
function getOptions(locale: string, isDarkMode: boolean): ChartOptions {
|
||||||
return {
|
return {
|
||||||
layout: {
|
layout: {
|
||||||
@ -122,18 +96,45 @@ function getOptions(locale: string, isDarkMode: boolean): ChartOptions {
|
|||||||
|
|
||||||
const graphTypes = ['Liquidity', 'Price']
|
const graphTypes = ['Liquidity', 'Price']
|
||||||
|
|
||||||
export default function Graph({
|
const poolHistory = gql`
|
||||||
data
|
query PoolHistory($id: String!, $block: Int) {
|
||||||
}: {
|
poolTransactions(
|
||||||
data: ChartDataLiqudity
|
first: 1000
|
||||||
}): ReactElement {
|
where: { poolAddress: $id, block_gt: $block }
|
||||||
|
orderBy: block
|
||||||
|
) {
|
||||||
|
block
|
||||||
|
spotPrice
|
||||||
|
timestamp
|
||||||
|
oceanReserve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function Graph(): ReactElement {
|
||||||
const { locale } = useUserPreferences()
|
const { locale } = useUserPreferences()
|
||||||
const darkMode = useDarkMode(false, darkModeConfig)
|
const darkMode = useDarkMode(false, darkModeConfig)
|
||||||
|
|
||||||
const [graphData, setGraphData] = useState<ChartData>()
|
|
||||||
const [options, setOptions] = useState<ChartOptions>()
|
const [options, setOptions] = useState<ChartOptions>()
|
||||||
const [graphType, setGraphType] = useState<GraphType>('liquidity')
|
const [graphType, setGraphType] = useState<GraphType>('liquidity')
|
||||||
|
|
||||||
|
const { price } = useAsset()
|
||||||
|
|
||||||
|
const [lastBlock, setLastBlock] = useState(0)
|
||||||
|
const [priceHistory, setPriceHistory] = useState([])
|
||||||
|
const [liquidityHistory, setLiquidityHistory] = useState([])
|
||||||
|
const [timestamps, setTimestamps] = useState([])
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const [graphData, setGraphData] = useState<ChartData>()
|
||||||
|
|
||||||
|
const { data, refetch, error } = useQuery<PoolHistory>(poolHistory, {
|
||||||
|
variables: {
|
||||||
|
id: price.address.toLowerCase(),
|
||||||
|
block: lastBlock
|
||||||
|
},
|
||||||
|
pollInterval: 20000
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Logger.log('Fired GraphOptions!')
|
Logger.log('Fired GraphOptions!')
|
||||||
const options = getOptions(locale, darkMode.value)
|
const options = getOptions(locale, darkMode.value)
|
||||||
@ -143,11 +144,53 @@ export default function Graph({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data) return
|
if (!data) return
|
||||||
Logger.log('Fired GraphData!')
|
Logger.log('Fired GraphData!')
|
||||||
const graphData =
|
|
||||||
|
const latestTimestamps = [
|
||||||
|
...timestamps,
|
||||||
|
...data.poolTransactions.map((item) => {
|
||||||
|
const date = new Date(item.timestamp * 1000)
|
||||||
|
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
setTimestamps(latestTimestamps)
|
||||||
|
|
||||||
|
const latestLiquidtyHistory = [
|
||||||
|
...liquidityHistory,
|
||||||
|
...data.poolTransactions.map((item) => item.oceanReserve)
|
||||||
|
]
|
||||||
|
|
||||||
|
setLiquidityHistory(latestLiquidtyHistory)
|
||||||
|
|
||||||
|
const latestPriceHistory = [
|
||||||
|
...priceHistory,
|
||||||
|
...data.poolTransactions.map((item) => item.spotPrice)
|
||||||
|
]
|
||||||
|
setPriceHistory(latestPriceHistory)
|
||||||
|
|
||||||
|
if (data.poolTransactions.length > 0) {
|
||||||
|
setLastBlock(
|
||||||
|
data.poolTransactions[data.poolTransactions.length - 1].block
|
||||||
|
)
|
||||||
|
refetch()
|
||||||
|
} else {
|
||||||
|
setIsLoading(false)
|
||||||
|
setGraphData({
|
||||||
|
labels: timestamps.slice(0),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
...lineStyle,
|
||||||
|
label: 'Liquidity (OCEAN)',
|
||||||
|
data:
|
||||||
graphType === 'liquidity'
|
graphType === 'liquidity'
|
||||||
? constructGraphData(data.oceanReserveHistory)
|
? latestLiquidtyHistory.slice(0)
|
||||||
: constructGraphData(data.datatokenPriceHistory)
|
: latestPriceHistory.slice(0),
|
||||||
setGraphData(graphData)
|
borderColor: `#8b98a9`,
|
||||||
|
pointBackgroundColor: `#8b98a9`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
}, [data, graphType])
|
}, [data, graphType])
|
||||||
|
|
||||||
function handleGraphTypeSwitch(e: ChangeEvent<HTMLButtonElement>) {
|
function handleGraphTypeSwitch(e: ChangeEvent<HTMLButtonElement>) {
|
||||||
@ -157,7 +200,11 @@ export default function Graph({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.graphWrap}>
|
<div className={styles.graphWrap}>
|
||||||
{graphData ? (
|
{isLoading ? (
|
||||||
|
<Loader />
|
||||||
|
) : error ? (
|
||||||
|
<small>{error.message}</small>
|
||||||
|
) : (
|
||||||
<>
|
<>
|
||||||
<nav className={styles.type}>
|
<nav className={styles.type}>
|
||||||
{graphTypes.map((type: GraphType) => (
|
{graphTypes.map((type: GraphType) => (
|
||||||
@ -176,8 +223,6 @@ export default function Graph({
|
|||||||
</nav>
|
</nav>
|
||||||
<Line height={70} data={graphData} options={options} />
|
<Line height={70} data={graphData} options={options} />
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<Loader />
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -14,9 +14,10 @@ import TokenList from './TokenList'
|
|||||||
import { graphql, useStaticQuery } from 'gatsby'
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
import TokenBalance from '../../../../@types/TokenBalance'
|
import TokenBalance from '../../../../@types/TokenBalance'
|
||||||
import Transactions from './Transactions'
|
import Transactions from './Transactions'
|
||||||
import Graph, { ChartDataLiqudity } from './Graph'
|
import Graph from './Graph'
|
||||||
import axios from 'axios'
|
|
||||||
import { useAsset } from '../../../../providers/Asset'
|
import { useAsset } from '../../../../providers/Asset'
|
||||||
|
import { gql, useQuery } from '@apollo/client'
|
||||||
|
import { PoolLiquidity } from '../../../../@types/apollo/PoolLiquidity'
|
||||||
|
|
||||||
const contentQuery = graphql`
|
const contentQuery = graphql`
|
||||||
query PoolQuery {
|
query PoolQuery {
|
||||||
@ -36,12 +37,30 @@ const contentQuery = graphql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
const poolLiquidityQuery = gql`
|
||||||
|
query PoolLiquidity($id: ID!, $shareId: ID) {
|
||||||
|
pool(id: $id) {
|
||||||
|
id
|
||||||
|
totalShares
|
||||||
|
swapFee
|
||||||
|
tokens {
|
||||||
|
tokenAddress
|
||||||
|
balance
|
||||||
|
denormWeight
|
||||||
|
}
|
||||||
|
shares(where: { id: $shareId }) {
|
||||||
|
id
|
||||||
|
balance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export default function Pool(): ReactElement {
|
export default function Pool(): ReactElement {
|
||||||
const data = useStaticQuery(contentQuery)
|
const data = useStaticQuery(contentQuery)
|
||||||
const content = data.content.edges[0].node.childContentJson.pool
|
const content = data.content.edges[0].node.childContentJson.pool
|
||||||
|
|
||||||
const { ocean, accountId, networkId, config } = useOcean()
|
const { ocean, accountId, networkId } = useOcean()
|
||||||
const {
|
const {
|
||||||
isInPurgatory,
|
isInPurgatory,
|
||||||
ddo,
|
ddo,
|
||||||
@ -75,10 +94,70 @@ export default function Pool(): ReactElement {
|
|||||||
const [creatorLiquidity, setCreatorLiquidity] = useState<TokenBalance>()
|
const [creatorLiquidity, setCreatorLiquidity] = useState<TokenBalance>()
|
||||||
const [creatorPoolTokens, setCreatorPoolTokens] = useState<string>()
|
const [creatorPoolTokens, setCreatorPoolTokens] = useState<string>()
|
||||||
const [creatorPoolShare, setCreatorPoolShare] = useState<string>()
|
const [creatorPoolShare, setCreatorPoolShare] = useState<string>()
|
||||||
const [graphData, setGraphData] = useState<ChartDataLiqudity>()
|
|
||||||
|
|
||||||
// the purpose of the value is just to trigger the effect
|
// the purpose of the value is just to trigger the effect
|
||||||
const [refreshPool, setRefreshPool] = useState(false)
|
const [refreshPool, setRefreshPool] = useState(false)
|
||||||
|
const { data: dataLiquidity } = useQuery<PoolLiquidity>(poolLiquidityQuery, {
|
||||||
|
variables: {
|
||||||
|
id: ddo.price.address.toLowerCase(),
|
||||||
|
shareId: `${ddo.price.address.toLowerCase()}-${ddo.publicKey[0].owner.toLowerCase()}`
|
||||||
|
},
|
||||||
|
pollInterval: 5000
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function init() {
|
||||||
|
if (!dataLiquidity || !dataLiquidity.pool) return
|
||||||
|
|
||||||
|
// Total pool shares
|
||||||
|
const totalPoolTokens = dataLiquidity.pool.totalShares
|
||||||
|
setTotalPoolTokens(totalPoolTokens)
|
||||||
|
|
||||||
|
// Get swap fee
|
||||||
|
// swapFee is tricky: to get 0.1% you need to convert from 0.001
|
||||||
|
setSwapFee(`${Number(dataLiquidity.pool.swapFee) * 100}`)
|
||||||
|
|
||||||
|
// Get weights
|
||||||
|
const weightDt = dataLiquidity.pool.tokens.filter(
|
||||||
|
(token: any) => token.tokenAddress === ddo.dataToken.toLowerCase()
|
||||||
|
)[0].denormWeight
|
||||||
|
|
||||||
|
setWeightDt(`${Number(weightDt) * 10}`)
|
||||||
|
setWeightOcean(`${100 - Number(weightDt) * 10}`)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get everything the creator put into the pool
|
||||||
|
//
|
||||||
|
|
||||||
|
const creatorPoolTokens = dataLiquidity.pool.shares[0].balance
|
||||||
|
setCreatorPoolTokens(creatorPoolTokens)
|
||||||
|
|
||||||
|
// Calculate creator's provided liquidity based on pool tokens
|
||||||
|
const creatorOceanBalance =
|
||||||
|
(Number(creatorPoolTokens) / Number(totalPoolTokens)) * price.ocean
|
||||||
|
|
||||||
|
const creatorDtBalance =
|
||||||
|
(Number(creatorPoolTokens) / Number(totalPoolTokens)) * price.datatoken
|
||||||
|
|
||||||
|
const creatorLiquidity = {
|
||||||
|
ocean: creatorOceanBalance,
|
||||||
|
datatoken: creatorDtBalance
|
||||||
|
}
|
||||||
|
setCreatorLiquidity(creatorLiquidity)
|
||||||
|
|
||||||
|
const totalCreatorLiquidityInOcean =
|
||||||
|
creatorLiquidity?.ocean + creatorLiquidity?.datatoken * price?.value
|
||||||
|
setCreatorTotalLiquidityInOcean(totalCreatorLiquidityInOcean)
|
||||||
|
|
||||||
|
const creatorPoolShare =
|
||||||
|
price?.ocean &&
|
||||||
|
price?.datatoken &&
|
||||||
|
creatorLiquidity &&
|
||||||
|
((Number(creatorPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(2)
|
||||||
|
setCreatorPoolShare(creatorPoolShare)
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
}, [dataLiquidity, ddo.dataToken, price.datatoken, price.ocean, price?.value])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsRemoveDisabled(isInPurgatory && owner === accountId)
|
setIsRemoveDisabled(isInPurgatory && owner === accountId)
|
||||||
@ -104,14 +183,6 @@ export default function Pool(): ReactElement {
|
|||||||
if (!ocean || !accountId || !price) return
|
if (!ocean || !accountId || !price) return
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
//
|
|
||||||
// Get everything which is in the pool
|
|
||||||
//
|
|
||||||
const totalPoolTokens = await ocean.pool.getPoolSharesTotalSupply(
|
|
||||||
price.address
|
|
||||||
)
|
|
||||||
setTotalPoolTokens(totalPoolTokens)
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Get everything the user has put into the pool
|
// Get everything the user has put into the pool
|
||||||
//
|
//
|
||||||
@ -131,95 +202,12 @@ export default function Pool(): ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setUserLiquidity(userLiquidity)
|
setUserLiquidity(userLiquidity)
|
||||||
|
|
||||||
//
|
|
||||||
// Get everything the creator put into the pool
|
|
||||||
//
|
|
||||||
const creatorPoolTokens = await ocean.pool.sharesBalance(
|
|
||||||
owner,
|
|
||||||
price.address
|
|
||||||
)
|
|
||||||
setCreatorPoolTokens(creatorPoolTokens)
|
|
||||||
|
|
||||||
// Calculate creator's provided liquidity based on pool tokens
|
|
||||||
const creatorOceanBalance =
|
|
||||||
(Number(creatorPoolTokens) / Number(totalPoolTokens)) * price.ocean
|
|
||||||
|
|
||||||
const creatorDtBalance =
|
|
||||||
(Number(creatorPoolTokens) / Number(totalPoolTokens)) *
|
|
||||||
price.datatoken
|
|
||||||
|
|
||||||
const creatorLiquidity = {
|
|
||||||
ocean: creatorOceanBalance,
|
|
||||||
datatoken: creatorDtBalance
|
|
||||||
}
|
|
||||||
setCreatorLiquidity(creatorLiquidity)
|
|
||||||
|
|
||||||
const totalCreatorLiquidityInOcean =
|
|
||||||
creatorLiquidity?.ocean + creatorLiquidity?.datatoken * price?.value
|
|
||||||
setCreatorTotalLiquidityInOcean(totalCreatorLiquidityInOcean)
|
|
||||||
|
|
||||||
const creatorPoolShare =
|
|
||||||
price?.ocean &&
|
|
||||||
price?.datatoken &&
|
|
||||||
creatorLiquidity &&
|
|
||||||
((Number(creatorPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(
|
|
||||||
2
|
|
||||||
)
|
|
||||||
setCreatorPoolShare(creatorPoolShare)
|
|
||||||
|
|
||||||
// Get swap fee
|
|
||||||
// swapFee is tricky: to get 0.1% you need to convert from 0.001
|
|
||||||
const swapFee = await ocean.pool.getSwapFee(price.address)
|
|
||||||
setSwapFee(`${Number(swapFee) * 100}`)
|
|
||||||
|
|
||||||
// Get weights
|
|
||||||
const weightDt = await ocean.pool.getDenormalizedWeight(
|
|
||||||
price.address,
|
|
||||||
ddo.dataToken
|
|
||||||
)
|
|
||||||
setWeightDt(`${Number(weightDt) * 10}`)
|
|
||||||
setWeightOcean(`${100 - Number(weightDt) * 10}`)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(error.message)
|
Logger.error(error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
}, [ocean, accountId, price, ddo, refreshPool, owner])
|
}, [ocean, accountId, price, ddo, refreshPool, owner, totalPoolTokens])
|
||||||
|
|
||||||
// Get graph history data
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!price?.address ||
|
|
||||||
!price?.ocean ||
|
|
||||||
!price?.value ||
|
|
||||||
!config?.metadataCacheUri
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
const source = axios.CancelToken.source()
|
|
||||||
const url = `${config.metadataCacheUri}/api/v1/aquarius/pools/history/${price.address}`
|
|
||||||
|
|
||||||
async function getData() {
|
|
||||||
Logger.log('Fired GetGraphData!')
|
|
||||||
try {
|
|
||||||
const response = await axios(url, { cancelToken: source.token })
|
|
||||||
if (!response || response.status !== 200) return
|
|
||||||
setGraphData(response.data)
|
|
||||||
} catch (error) {
|
|
||||||
if (axios.isCancel(error)) {
|
|
||||||
Logger.log(error.message)
|
|
||||||
} else {
|
|
||||||
Logger.error(error.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getData()
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
source.cancel()
|
|
||||||
}
|
|
||||||
}, [config.metadataCacheUri, price?.address, price?.ocean, price?.value])
|
|
||||||
|
|
||||||
const refreshInfo = async () => {
|
const refreshInfo = async () => {
|
||||||
setRefreshPool(!refreshPool)
|
setRefreshPool(!refreshPool)
|
||||||
@ -318,7 +306,7 @@ export default function Pool(): ReactElement {
|
|||||||
{weightOcean}/{weightDt}
|
{weightOcean}/{weightDt}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<Graph data={graphData} />
|
<Graph />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
ocean={`${price?.ocean}`}
|
ocean={`${price?.ocean}`}
|
||||||
|
@ -3,6 +3,7 @@ import styles from './index.module.css'
|
|||||||
import Compute from './Compute'
|
import Compute from './Compute'
|
||||||
import Consume from './Consume'
|
import Consume from './Consume'
|
||||||
import { Logger } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
|
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
|
||||||
import Tabs from '../../atoms/Tabs'
|
import Tabs from '../../atoms/Tabs'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import compareAsBN from '../../../utils/compareAsBN'
|
import compareAsBN from '../../../utils/compareAsBN'
|
||||||
@ -13,6 +14,7 @@ import { useAsset } from '../../../providers/Asset'
|
|||||||
export default function AssetActions(): ReactElement {
|
export default function AssetActions(): ReactElement {
|
||||||
const { ocean, balance, accountId } = useOcean()
|
const { ocean, balance, accountId } = useOcean()
|
||||||
const { price, ddo, metadata } = useAsset()
|
const { price, ddo, metadata } = useAsset()
|
||||||
|
|
||||||
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
||||||
const [dtBalance, setDtBalance] = useState<string>()
|
const [dtBalance, setDtBalance] = useState<string>()
|
||||||
|
|
||||||
@ -21,6 +23,7 @@ export default function AssetActions(): ReactElement {
|
|||||||
// Get and set user DT balance
|
// Get and set user DT balance
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ocean || !accountId) return
|
if (!ocean || !accountId) return
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
const dtBalance = await ocean.datatokens.balance(
|
const dtBalance = await ocean.datatokens.balance(
|
||||||
|
Loading…
Reference in New Issue
Block a user