mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Merge pull request #58 from oceanprotocol/feature/balancer
Pool interaction
This commit is contained in:
commit
5e772ca843
2130
package-lock.json
generated
2130
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
62
package.json
62
package.json
@ -22,35 +22,35 @@
|
|||||||
"@coingecko/cryptoformat": "^0.3.8",
|
"@coingecko/cryptoformat": "^0.3.8",
|
||||||
"@loadable/component": "^5.13.1",
|
"@loadable/component": "^5.13.1",
|
||||||
"@oceanprotocol/art": "^3.0.0",
|
"@oceanprotocol/art": "^3.0.0",
|
||||||
"@oceanprotocol/lib": "^0.1.12",
|
"@oceanprotocol/lib": "^0.1.16",
|
||||||
"@oceanprotocol/react": "^0.0.37",
|
"@oceanprotocol/react": "^0.0.40",
|
||||||
"@oceanprotocol/typographies": "^0.1.0",
|
"@oceanprotocol/typographies": "^0.1.0",
|
||||||
"@sindresorhus/slugify": "^1.0.0",
|
"@sindresorhus/slugify": "^1.0.0",
|
||||||
"@tippyjs/react": "^4.1.0",
|
"@tippyjs/react": "^4.1.0",
|
||||||
"@toruslabs/torus-embed": "^1.8.2",
|
"@toruslabs/torus-embed": "^1.8.3",
|
||||||
"@types/classnames": "^2.2.10",
|
"@types/classnames": "^2.2.10",
|
||||||
"@vercel/node": "^1.7.4",
|
"@vercel/node": "^1.8.1",
|
||||||
"@walletconnect/web3-provider": "^1.2.1",
|
"@walletconnect/web3-provider": "^1.2.2",
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.20.0",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"date-fns": "^2.15.0",
|
"date-fns": "^2.16.0",
|
||||||
"decimal.js": "^10.2.0",
|
"decimal.js": "^10.2.0",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"ethereum-blockies": "github:MyEtherWallet/blockies",
|
"ethereum-blockies": "github:MyEtherWallet/blockies",
|
||||||
"filesize": "^6.1.0",
|
"filesize": "^6.1.0",
|
||||||
"formik": "^2.1.5",
|
"formik": "^2.1.5",
|
||||||
"gatsby": "^2.24.47",
|
"gatsby": "^2.24.52",
|
||||||
"gatsby-image": "^2.4.16",
|
"gatsby-image": "^2.4.16",
|
||||||
"gatsby-plugin-manifest": "^2.4.23",
|
"gatsby-plugin-manifest": "^2.4.26",
|
||||||
"gatsby-plugin-react-helmet": "^3.3.10",
|
"gatsby-plugin-react-helmet": "^3.3.10",
|
||||||
"gatsby-plugin-remove-trailing-slashes": "^2.3.11",
|
"gatsby-plugin-remove-trailing-slashes": "^2.3.11",
|
||||||
"gatsby-plugin-sharp": "^2.6.27",
|
"gatsby-plugin-sharp": "^2.6.30",
|
||||||
"gatsby-plugin-svgr": "^2.0.2",
|
"gatsby-plugin-svgr": "^2.0.2",
|
||||||
"gatsby-plugin-webpack-size": "^1.0.0",
|
"gatsby-plugin-webpack-size": "^1.0.0",
|
||||||
"gatsby-source-filesystem": "^2.3.24",
|
"gatsby-source-filesystem": "^2.3.27",
|
||||||
"gatsby-source-graphql": "^2.7.1",
|
"gatsby-source-graphql": "^2.7.2",
|
||||||
"gatsby-transformer-json": "^2.4.11",
|
"gatsby-transformer-json": "^2.4.11",
|
||||||
"gatsby-transformer-remark": "^2.8.28",
|
"gatsby-transformer-remark": "^2.8.31",
|
||||||
"gatsby-transformer-sharp": "^2.5.13",
|
"gatsby-transformer-sharp": "^2.5.13",
|
||||||
"intersection-observer": "^0.11.0",
|
"intersection-observer": "^0.11.0",
|
||||||
"is-url-superb": "^4.0.0",
|
"is-url-superb": "^4.0.0",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"lodash.omit": "^4.5.0",
|
"lodash.omit": "^4.5.0",
|
||||||
"query-string": "^6.13.1",
|
"query-string": "^6.13.1",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-data-table-component": "^6.11.0",
|
"react-data-table-component": "^6.11.2",
|
||||||
"react-datepicker": "^3.1.3",
|
"react-datepicker": "^3.1.3",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-dotdotdot": "^1.3.1",
|
"react-dotdotdot": "^1.3.1",
|
||||||
@ -72,32 +72,32 @@
|
|||||||
"react-toastify": "^6.0.8",
|
"react-toastify": "^6.0.8",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"slugify": "^1.4.5",
|
"slugify": "^1.4.5",
|
||||||
"swr": "^0.3.0",
|
"swr": "^0.3.1",
|
||||||
"yup": "^0.29.3"
|
"yup": "^0.29.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.11.1",
|
"@babel/core": "^7.11.4",
|
||||||
"@types/lodash.debounce": "^4.0.3",
|
|
||||||
"@types/lodash.omit": "^4.5.6",
|
|
||||||
"@babel/preset-typescript": "^7.10.1",
|
"@babel/preset-typescript": "^7.10.1",
|
||||||
"@storybook/addon-actions": "^6.0.12",
|
"@storybook/addon-actions": "^6.0.20",
|
||||||
"@storybook/addon-storyshots": "^6.0.12",
|
"@storybook/addon-storyshots": "^6.0.20",
|
||||||
"@storybook/react": "^6.0.12",
|
"@storybook/react": "^6.0.20",
|
||||||
"@svgr/webpack": "^5.4.0",
|
"@svgr/webpack": "^5.4.0",
|
||||||
"@testing-library/jest-dom": "^5.11.3",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^10.4.8",
|
"@testing-library/react": "^10.4.9",
|
||||||
"@types/jest": "^26.0.10",
|
"@types/jest": "^26.0.10",
|
||||||
"@types/loadable__component": "^5.13.0",
|
"@types/loadable__component": "^5.13.0",
|
||||||
"@types/node": "^14.6.0",
|
"@types/lodash.debounce": "^4.0.3",
|
||||||
"@types/react": "^16.9.46",
|
"@types/lodash.omit": "^4.5.6",
|
||||||
|
"@types/node": "^14.6.1",
|
||||||
|
"@types/react": "^16.9.48",
|
||||||
"@types/react-datepicker": "^3.1.1",
|
"@types/react-datepicker": "^3.1.1",
|
||||||
"@types/react-helmet": "^6.1.0",
|
"@types/react-helmet": "^6.1.0",
|
||||||
"@types/react-paginate": "^6.2.1",
|
"@types/react-paginate": "^6.2.1",
|
||||||
"@types/react-tabs": "^2.3.2",
|
"@types/react-tabs": "^2.3.2",
|
||||||
"@types/shortid": "0.0.29",
|
"@types/shortid": "0.0.29",
|
||||||
"@types/yup": "^0.29.5",
|
"@types/yup": "^0.29.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^3.9.1",
|
"@typescript-eslint/eslint-plugin": "^3.10.1",
|
||||||
"@typescript-eslint/parser": "^3.9.1",
|
"@typescript-eslint/parser": "^3.10.1",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
"babel-preset-react-app": "^9.1.2",
|
"babel-preset-react-app": "^9.1.2",
|
||||||
"eslint": "^7.7.0",
|
"eslint": "^7.7.0",
|
||||||
@ -106,10 +106,10 @@
|
|||||||
"eslint-plugin-prettier": "^3.1.4",
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
"eslint-plugin-react": "^7.20.6",
|
"eslint-plugin-react": "^7.20.6",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^26.4.0",
|
"jest": "^26.4.2",
|
||||||
"prettier": "^2.0.5",
|
"prettier": "^2.1.1",
|
||||||
"serve": "^11.3.2",
|
"serve": "^11.3.2",
|
||||||
"source-map-explorer": "^2.4.2",
|
"source-map-explorer": "^2.5.0",
|
||||||
"typescript": "^3.9.7"
|
"typescript": "^3.9.7"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
24
src/components/atoms/EtherscanLink.module.css
Normal file
24
src/components/atoms/EtherscanLink.module.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
.link {
|
||||||
|
color: var(--brand-grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link svg {
|
||||||
|
width: 0.7em;
|
||||||
|
height: 0.7em;
|
||||||
|
display: inline-block;
|
||||||
|
fill: var(--brand-grey-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link code {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover,
|
||||||
|
.link:focus,
|
||||||
|
.link:hover *,
|
||||||
|
.link:focus * {
|
||||||
|
color: var(--brand-pink);
|
||||||
|
}
|
29
src/components/atoms/EtherscanLink.tsx
Normal file
29
src/components/atoms/EtherscanLink.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React, { ReactElement, ReactNode } from 'react'
|
||||||
|
import { ReactComponent as External } from '../../images/external.svg'
|
||||||
|
import styles from './EtherscanLink.module.css'
|
||||||
|
|
||||||
|
export default function EtherscanLink({
|
||||||
|
network,
|
||||||
|
path,
|
||||||
|
children
|
||||||
|
}: {
|
||||||
|
network?: 'rinkeby' | 'kovan' | 'ropsten'
|
||||||
|
path: string
|
||||||
|
children: ReactNode
|
||||||
|
}): ReactElement {
|
||||||
|
const url = network
|
||||||
|
? `https://${network}.etherscan.io`
|
||||||
|
: `https://etherscan.io`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={`${url}/${path}`}
|
||||||
|
title="View on Etherscan"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={styles.link}
|
||||||
|
>
|
||||||
|
{children} <External />
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
@ -3,4 +3,5 @@
|
|||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
margin-left: calc(var(--spacer) / 6);
|
margin-left: calc(var(--spacer) / 6);
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ export default function Conversion({
|
|||||||
|
|
||||||
const { eur } = data['ocean-protocol']
|
const { eur } = data['ocean-protocol']
|
||||||
const converted = eur * Number(price)
|
const converted = eur * Number(price)
|
||||||
setPriceEur(`${formatCurrency(converted, 'EUR', 'en', true)}`)
|
setPriceEur(`${formatCurrency(converted, 'EUR', undefined, true)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -61,7 +61,7 @@ export default function Conversion({
|
|||||||
className={styleClasses}
|
className={styleClasses}
|
||||||
title="Approximation based on current spot price on Coingecko"
|
title="Approximation based on current spot price on Coingecko"
|
||||||
>
|
>
|
||||||
≈ EUR {priceEur}
|
≈ {priceEur} EUR
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
19
src/components/atoms/Price/PriceUnit.module.css
Normal file
19
src/components/atoms/Price/PriceUnit.module.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.price {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
color: var(--brand-grey-dark);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price span:first-child {
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
/* lazy making-conversion-smaller-with-same-markup */
|
||||||
|
transform: scale(0.8);
|
||||||
|
transform-origin: left 80%;
|
||||||
|
}
|
39
src/components/atoms/Price/PriceUnit.tsx
Normal file
39
src/components/atoms/Price/PriceUnit.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import { formatCurrency } from '@coingecko/cryptoformat'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
import Conversion from './Conversion'
|
||||||
|
import styles from './PriceUnit.module.css'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
|
export default function PriceUnit({
|
||||||
|
price,
|
||||||
|
className,
|
||||||
|
small,
|
||||||
|
conversion,
|
||||||
|
symbol
|
||||||
|
}: {
|
||||||
|
price: string
|
||||||
|
className?: string
|
||||||
|
small?: boolean
|
||||||
|
conversion?: boolean
|
||||||
|
symbol?: string
|
||||||
|
}): ReactElement {
|
||||||
|
const styleClasses = cx({
|
||||||
|
price: true,
|
||||||
|
small: small,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styleClasses}>
|
||||||
|
{Number.isInteger(Number(price))
|
||||||
|
? price
|
||||||
|
: Number.isNaN(Number(price))
|
||||||
|
? '-'
|
||||||
|
: formatCurrency(Number(price), '', undefined, false, true)}{' '}
|
||||||
|
<span>{symbol || 'OCEAN'}</span>
|
||||||
|
{conversion && <Conversion price={price} />}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -1,23 +1,4 @@
|
|||||||
.price {
|
|
||||||
font-weight: var(--font-weight-bold);
|
|
||||||
font-size: var(--font-size-large);
|
|
||||||
color: var(--brand-grey-dark);
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.price span:first-child {
|
|
||||||
font-weight: var(--font-weight-base);
|
|
||||||
color: var(--color-secondary);
|
|
||||||
font-size: var(--font-size-base);
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
}
|
}
|
||||||
|
|
||||||
.small {
|
|
||||||
/* lazy making-conversion-smaller-with-same-markup */
|
|
||||||
transform: scale(0.8);
|
|
||||||
transform-origin: left 80%;
|
|
||||||
}
|
|
||||||
|
@ -1,61 +1,34 @@
|
|||||||
import React, { ReactElement, useState, useEffect } from 'react'
|
import React, { ReactElement, useState, useEffect } from 'react'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import PriceConversion from './Conversion'
|
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { formatCurrency } from '@coingecko/cryptoformat'
|
|
||||||
import { useMetadata, useOcean } from '@oceanprotocol/react'
|
import { useMetadata, useOcean } from '@oceanprotocol/react'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import Loader from '../Loader'
|
import Loader from '../Loader'
|
||||||
import Tooltip from '../Tooltip'
|
import Tooltip from '../Tooltip'
|
||||||
|
import PriceUnit from './PriceUnit'
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
export default function Price({
|
export default function Price({
|
||||||
ddo,
|
ddo,
|
||||||
className,
|
className,
|
||||||
small,
|
small,
|
||||||
setPriceOutside
|
conversion
|
||||||
}: {
|
}: {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
className?: string
|
className?: string
|
||||||
small?: boolean
|
small?: boolean
|
||||||
setPriceOutside?: (price: string) => void
|
conversion?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean, chainId, accountId } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const { getBestPrice } = useMetadata()
|
const { price } = useMetadata(ddo)
|
||||||
const [price, setPrice] = useState<string>()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function init() {
|
|
||||||
console.log(ocean)
|
|
||||||
const price = await getBestPrice(ddo.dataToken)
|
|
||||||
setPrice(price)
|
|
||||||
setPriceOutside && price !== '' && setPriceOutside(price)
|
|
||||||
}
|
|
||||||
init()
|
|
||||||
}, [chainId, accountId, ocean])
|
|
||||||
|
|
||||||
const styleClasses = cx({
|
|
||||||
price: true,
|
|
||||||
small: small,
|
|
||||||
[className]: className
|
|
||||||
})
|
|
||||||
|
|
||||||
const isFree = price === '0'
|
|
||||||
|
|
||||||
const displayPrice = isFree ? (
|
|
||||||
'Free'
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<span>OCEAN</span> {formatCurrency(Number(price), '', 'en', false, true)}
|
|
||||||
<PriceConversion price={price} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
return !ocean ? (
|
return !ocean ? (
|
||||||
<div className={styles.empty}>Please connect your wallet to view price</div>
|
<div className={styles.empty}>Connect your wallet to view price</div>
|
||||||
) : price ? (
|
) : price ? (
|
||||||
<div className={styleClasses}>{displayPrice}</div>
|
<PriceUnit
|
||||||
|
price={price}
|
||||||
|
className={className}
|
||||||
|
small={small}
|
||||||
|
conversion={conversion}
|
||||||
|
/>
|
||||||
) : price === '' ? (
|
) : price === '' ? (
|
||||||
<div className={styles.empty}>
|
<div className={styles.empty}>
|
||||||
No price found{' '}
|
No price found{' '}
|
||||||
|
@ -23,7 +23,7 @@ export default function Tabs({
|
|||||||
<Tab
|
<Tab
|
||||||
className={styles.tab}
|
className={styles.tab}
|
||||||
key={item.title}
|
key={item.title}
|
||||||
onClick={() => handleTabChange(item.title)}
|
onClick={handleTabChange ? () => handleTabChange(item.title) : null}
|
||||||
>
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Tab>
|
</Tab>
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 15px;
|
width: 1rem;
|
||||||
height: 15px;
|
height: 1rem;
|
||||||
cursor: help;
|
cursor: help;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: -0.1rem;
|
margin-bottom: -0.1rem;
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React from 'react'
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import Dotdotdot from 'react-dotdotdot'
|
import Dotdotdot from 'react-dotdotdot'
|
||||||
import { MetadataMarket } from '../../@types/Metadata'
|
import { MetadataMarket } from '../../@types/Metadata'
|
||||||
import Price from '../atoms/Price'
|
import Price from '../atoms/Price'
|
||||||
import styles from './AssetTeaser.module.css'
|
import styles from './AssetTeaser.module.css'
|
||||||
import { useMetadata } from '@oceanprotocol/react'
|
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import Loader from '../atoms/Loader'
|
|
||||||
|
|
||||||
declare type AssetTeaserProps = {
|
declare type AssetTeaserProps = {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
|
@ -17,7 +17,7 @@ export default function Details(): ReactElement {
|
|||||||
{Object.entries(balance).map(([key, value]) => (
|
{Object.entries(balance).map(([key, value]) => (
|
||||||
<li className={styles.balance} key={key}>
|
<li className={styles.balance} key={key}>
|
||||||
<span>{key.toUpperCase()}</span>{' '}
|
<span>{key.toUpperCase()}</span>{' '}
|
||||||
{formatCurrency(value, '', 'en', true, true)}
|
{formatCurrency(value, '', undefined, true, true)}
|
||||||
{key === 'ocean' && <Conversion price={value} />}
|
{key === 'ocean' && <Conversion price={value} />}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
@ -17,12 +17,10 @@ import Alert from '../../atoms/Alert'
|
|||||||
|
|
||||||
export default function Compute({
|
export default function Compute({
|
||||||
ddo,
|
ddo,
|
||||||
isBalanceSufficient,
|
isBalanceSufficient
|
||||||
setPrice
|
|
||||||
}: {
|
}: {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
isBalanceSufficient: boolean
|
isBalanceSufficient: boolean
|
||||||
setPrice: (price: string) => void
|
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const { compute, isLoading, computeStepText, computeError } = useCompute()
|
const { compute, isLoading, computeStepText, computeError } = useCompute()
|
||||||
@ -89,7 +87,7 @@ export default function Compute({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.compute}>
|
<div className={styles.compute}>
|
||||||
<Price ddo={ddo} setPriceOutside={setPrice} />
|
<Price ddo={ddo} conversion />
|
||||||
|
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
<div className={styles.selectType}>
|
<div className={styles.selectType}>
|
||||||
|
@ -12,13 +12,11 @@ import { useOcean, useConsume } from '@oceanprotocol/react'
|
|||||||
export default function Consume({
|
export default function Consume({
|
||||||
ddo,
|
ddo,
|
||||||
file,
|
file,
|
||||||
isBalanceSufficient,
|
isBalanceSufficient
|
||||||
setPrice
|
|
||||||
}: {
|
}: {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
file: FileMetadata
|
file: FileMetadata
|
||||||
isBalanceSufficient: boolean
|
isBalanceSufficient: boolean
|
||||||
setPrice: (price: string) => void
|
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const { consumeStepText, consume, consumeError } = useConsume()
|
const { consumeStepText, consume, consumeError } = useConsume()
|
||||||
@ -49,7 +47,7 @@ export default function Consume({
|
|||||||
<File file={file} />
|
<File file={file} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.pricewrapper}>
|
<div className={styles.pricewrapper}>
|
||||||
<Price ddo={ddo} setPriceOutside={setPrice} />
|
<Price ddo={ddo} conversion />
|
||||||
<PurchaseButton />
|
<PurchaseButton />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
.actions {
|
||||||
|
margin-left: -2rem;
|
||||||
|
margin-right: -2rem;
|
||||||
|
padding-left: var(--spacer);
|
||||||
|
padding-right: var(--spacer);
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
padding-top: calc(var(--spacer) / 1.5);
|
||||||
|
border-top: 1px solid var(--brand-grey-lighter);
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions button {
|
||||||
|
margin-left: calc(var(--spacer) / 4);
|
||||||
|
margin-right: calc(var(--spacer) / 4);
|
||||||
|
}
|
32
src/components/organisms/AssetActions/Pool/Actions.tsx
Normal file
32
src/components/organisms/AssetActions/Pool/Actions.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import Loader from '../../../atoms/Loader'
|
||||||
|
import Button from '../../../atoms/Button'
|
||||||
|
import Alert from '../../../atoms/Alert'
|
||||||
|
import styles from './Actions.module.css'
|
||||||
|
|
||||||
|
export default function Actions({
|
||||||
|
isLoading,
|
||||||
|
loaderMessage,
|
||||||
|
txId,
|
||||||
|
actionName,
|
||||||
|
action
|
||||||
|
}: {
|
||||||
|
isLoading: boolean
|
||||||
|
loaderMessage: string
|
||||||
|
txId: string
|
||||||
|
actionName: string
|
||||||
|
action: () => void
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className={styles.actions}>
|
||||||
|
{isLoading ? (
|
||||||
|
<Loader message={loaderMessage} />
|
||||||
|
) : (
|
||||||
|
<Button style="primary" size="small" onClick={() => action()}>
|
||||||
|
{actionName}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{txId && <Alert text={`Transaction ID: ${txId}`} state="success" />}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
30
src/components/organisms/AssetActions/Pool/Add.module.css
Normal file
30
src/components/organisms/AssetActions/Pool/Add.module.css
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
.add {
|
||||||
|
}
|
||||||
|
|
||||||
|
.addInput {
|
||||||
|
max-width: 12rem;
|
||||||
|
margin: 0 auto var(--spacer) auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userBalance {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
margin-bottom: calc(var(--spacer) / 4);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.userBalance span + div {
|
||||||
|
transform: scale(0.7);
|
||||||
|
transform-origin: right center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output p {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
margin-bottom: calc(var(--spacer) / 4);
|
||||||
|
}
|
95
src/components/organisms/AssetActions/Pool/Add.tsx
Normal file
95
src/components/organisms/AssetActions/Pool/Add.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import React, { ReactElement, useState, ChangeEvent } from 'react'
|
||||||
|
import styles from './Add.module.css'
|
||||||
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
|
import Header from './Header'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import InputElement from '../../../atoms/Input/InputElement'
|
||||||
|
import Token from './Token'
|
||||||
|
import { Balance } from './'
|
||||||
|
import PriceUnit from '../../../atoms/Price/PriceUnit'
|
||||||
|
import Actions from './Actions'
|
||||||
|
|
||||||
|
// TODO: handle and display all fees somehow
|
||||||
|
|
||||||
|
export default function Add({
|
||||||
|
setShowAdd,
|
||||||
|
poolAddress,
|
||||||
|
totalPoolTokens,
|
||||||
|
totalBalance
|
||||||
|
}: {
|
||||||
|
setShowAdd: (show: boolean) => void
|
||||||
|
poolAddress: string
|
||||||
|
totalPoolTokens: string
|
||||||
|
totalBalance: Balance
|
||||||
|
}): ReactElement {
|
||||||
|
const { ocean, accountId, balance } = useOcean()
|
||||||
|
const [amount, setAmount] = useState('')
|
||||||
|
const [txId, setTxId] = useState<string>('')
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>()
|
||||||
|
|
||||||
|
const newPoolTokens =
|
||||||
|
totalBalance &&
|
||||||
|
((Number(amount) / Number(totalBalance.ocean)) * 100).toFixed(2)
|
||||||
|
|
||||||
|
const newPoolShare =
|
||||||
|
totalBalance &&
|
||||||
|
((Number(newPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(2)
|
||||||
|
|
||||||
|
async function handleAddLiquidity() {
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await ocean.pool.addOceanLiquidity(
|
||||||
|
accountId,
|
||||||
|
poolAddress,
|
||||||
|
amount
|
||||||
|
)
|
||||||
|
setTxId(result.transactionHash)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message)
|
||||||
|
toast.error(error.message)
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAmountChange(e: ChangeEvent<HTMLInputElement>) {
|
||||||
|
setAmount(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.add}>
|
||||||
|
<Header title="Add Liquidity" backAction={() => setShowAdd(false)} />
|
||||||
|
|
||||||
|
<div className={styles.addInput}>
|
||||||
|
<div className={styles.userBalance}>
|
||||||
|
<span>Available:</span>
|
||||||
|
<PriceUnit price={balance.ocean} symbol="OCEAN" small />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<InputElement
|
||||||
|
value={amount}
|
||||||
|
name="ocean"
|
||||||
|
type="number"
|
||||||
|
prefix="OCEAN"
|
||||||
|
placeholder="0"
|
||||||
|
onChange={handleAmountChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.output}>
|
||||||
|
<p>You will receive</p>
|
||||||
|
<Token symbol="BPT" balance={newPoolTokens} />
|
||||||
|
<Token symbol="% of pool" balance={newPoolShare} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Actions
|
||||||
|
isLoading={isLoading}
|
||||||
|
loaderMessage="Adding Liquidity..."
|
||||||
|
actionName="Supply"
|
||||||
|
action={handleAddLiquidity}
|
||||||
|
txId={txId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
23
src/components/organisms/AssetActions/Pool/Header.module.css
Normal file
23
src/components/organisms/AssetActions/Pool/Header.module.css
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
|
padding-bottom: calc(var(--spacer) / 2);
|
||||||
|
border-bottom: 1px solid var(--brand-grey-lighter);
|
||||||
|
margin-top: -1rem;
|
||||||
|
margin-left: -2rem;
|
||||||
|
margin-right: -2rem;
|
||||||
|
padding-left: var(--spacer);
|
||||||
|
padding-right: var(--spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerTitle {
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
margin: 0;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: -3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back {
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
25
src/components/organisms/AssetActions/Pool/Header.tsx
Normal file
25
src/components/organisms/AssetActions/Pool/Header.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import styles from './Header.module.css'
|
||||||
|
import Button from '../../../atoms/Button'
|
||||||
|
|
||||||
|
export default function Header({
|
||||||
|
title,
|
||||||
|
backAction
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
backAction: () => void
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<header className={styles.header}>
|
||||||
|
<Button
|
||||||
|
className={styles.back}
|
||||||
|
style="text"
|
||||||
|
size="small"
|
||||||
|
onClick={backAction}
|
||||||
|
>
|
||||||
|
← Back
|
||||||
|
</Button>
|
||||||
|
<h3 className={styles.headerTitle}>{title}</h3>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
.removeInput {
|
||||||
|
max-width: 12rem;
|
||||||
|
margin: 0 auto var(--spacer) auto;
|
||||||
|
}
|
77
src/components/organisms/AssetActions/Pool/Remove.tsx
Normal file
77
src/components/organisms/AssetActions/Pool/Remove.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import React, { ReactElement, useState, ChangeEvent } from 'react'
|
||||||
|
import styles from './Remove.module.css'
|
||||||
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
|
import Header from './Header'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import InputElement from '../../../atoms/Input/InputElement'
|
||||||
|
import Actions from './Actions'
|
||||||
|
|
||||||
|
export default function Remove({
|
||||||
|
setShowRemove,
|
||||||
|
poolAddress,
|
||||||
|
totalPoolTokens
|
||||||
|
}: {
|
||||||
|
setShowRemove: (show: boolean) => void
|
||||||
|
poolAddress: string
|
||||||
|
totalPoolTokens: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { ocean, accountId } = useOcean()
|
||||||
|
const [amount, setAmount] = useState('')
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>()
|
||||||
|
const [txId, setTxId] = useState<string>('')
|
||||||
|
|
||||||
|
async function handleRemoveLiquidity() {
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await ocean.pool.removeOceanLiquidity(
|
||||||
|
accountId,
|
||||||
|
poolAddress,
|
||||||
|
amount,
|
||||||
|
totalPoolTokens
|
||||||
|
)
|
||||||
|
setTxId(result.transactionHash)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message)
|
||||||
|
toast.error(error.message)
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAmountChange(e: ChangeEvent<HTMLInputElement>) {
|
||||||
|
setAmount(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.remove}>
|
||||||
|
<Header
|
||||||
|
title="Remove Liquidity"
|
||||||
|
backAction={() => setShowRemove(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className={styles.removeInput}>
|
||||||
|
<InputElement
|
||||||
|
value={amount}
|
||||||
|
name="ocean"
|
||||||
|
type="number"
|
||||||
|
prefix="OCEAN"
|
||||||
|
placeholder="0"
|
||||||
|
onChange={handleAmountChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <Input name="dt" label={dtSymbol} type="number" placeholder="0" /> */}
|
||||||
|
|
||||||
|
<p>You will receive</p>
|
||||||
|
|
||||||
|
<Actions
|
||||||
|
isLoading={isLoading}
|
||||||
|
loaderMessage="Removing Liquidity..."
|
||||||
|
actionName="Remove"
|
||||||
|
action={handleRemoveLiquidity}
|
||||||
|
txId={txId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
36
src/components/organisms/AssetActions/Pool/Token.module.css
Normal file
36
src/components/organisms/AssetActions/Pool/Token.module.css
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
.token {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
margin-bottom: calc(var(--spacer) / 3);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.symbol {
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid var(--brand-grey-lighter);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 0.3rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: calc(var(--spacer) / 8);
|
||||||
|
margin-top: -0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon svg {
|
||||||
|
width: var(--font-size-base);
|
||||||
|
height: var(--font-size-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data Token Icon Style */
|
||||||
|
.icon[class*='DT'] path {
|
||||||
|
fill: var(--brand-violet);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon[class*='%'],
|
||||||
|
.icon[class*='BPT'] {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
21
src/components/organisms/AssetActions/Pool/Token.tsx
Normal file
21
src/components/organisms/AssetActions/Pool/Token.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import styles from './Token.module.css'
|
||||||
|
import { ReactComponent as Logo } from '../../../../images/logo.svg'
|
||||||
|
import PriceUnit from '../../../atoms/Price/PriceUnit'
|
||||||
|
|
||||||
|
export default function Token({
|
||||||
|
symbol,
|
||||||
|
balance
|
||||||
|
}: {
|
||||||
|
symbol: string
|
||||||
|
balance: string
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className={styles.token}>
|
||||||
|
<figure className={`${styles.icon} ${symbol}`}>
|
||||||
|
<Logo />
|
||||||
|
</figure>
|
||||||
|
<PriceUnit price={balance} symbol={symbol} small />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
34
src/components/organisms/AssetActions/Pool/index.module.css
Normal file
34
src/components/organisms/AssetActions/Pool/index.module.css
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
.dataToken {
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
|
padding-bottom: calc(var(--spacer) / 1.5);
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
border-bottom: 1px solid var(--brand-grey-lighter);
|
||||||
|
margin-left: -2rem;
|
||||||
|
margin-right: -2rem;
|
||||||
|
padding-left: var(--spacer);
|
||||||
|
padding-right: var(--spacer);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataTokenLinks {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataTokenLinks a {
|
||||||
|
margin-left: calc(var(--spacer) / 3);
|
||||||
|
margin-right: calc(var(--spacer) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.poolTokens {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--spacer);
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
margin-bottom: calc(var(--spacer) / 1.5);
|
||||||
|
}
|
183
src/components/organisms/AssetActions/Pool/index.tsx
Normal file
183
src/components/organisms/AssetActions/Pool/index.tsx
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import { useOcean, useMetadata } from '@oceanprotocol/react'
|
||||||
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import stylesActions from './Actions.module.css'
|
||||||
|
import Token from './Token'
|
||||||
|
import PriceUnit from '../../../atoms/Price/PriceUnit'
|
||||||
|
import Loader from '../../../atoms/Loader'
|
||||||
|
import Button from '../../../atoms/Button'
|
||||||
|
import Add from './Add'
|
||||||
|
import Remove from './Remove'
|
||||||
|
import Tooltip from '../../../atoms/Tooltip'
|
||||||
|
import Conversion from '../../../atoms/Price/Conversion'
|
||||||
|
import EtherscanLink from '../../../atoms/EtherscanLink'
|
||||||
|
|
||||||
|
export interface Balance {
|
||||||
|
ocean: string
|
||||||
|
dt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO: create tooltip copy
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function Pool({ ddo }: { ddo: DDO }): ReactElement {
|
||||||
|
const { ocean, accountId } = useOcean()
|
||||||
|
const { price, poolAddress } = useMetadata(ddo)
|
||||||
|
|
||||||
|
const [poolTokens, setPoolTokens] = useState<string>()
|
||||||
|
const [totalPoolTokens, setTotalPoolTokens] = useState<string>()
|
||||||
|
const [totalBalance, setTotalBalance] = useState<Balance>()
|
||||||
|
const [dtSymbol, setDtSymbol] = useState<string>()
|
||||||
|
const [userBalance, setUserBalance] = useState<Balance>()
|
||||||
|
|
||||||
|
const [showAdd, setShowAdd] = useState(false)
|
||||||
|
const [showRemove, setShowRemove] = useState(false)
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
|
||||||
|
const hasAddedLiquidity =
|
||||||
|
userBalance && (Number(userBalance.ocean) > 0 || Number(userBalance.dt) > 0)
|
||||||
|
|
||||||
|
const poolShare =
|
||||||
|
totalBalance &&
|
||||||
|
userBalance &&
|
||||||
|
((Number(poolTokens) / Number(totalPoolTokens)) * 100).toFixed(2)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ocean || !accountId || !poolAddress || !price) return
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
//
|
||||||
|
// Get data token symbol
|
||||||
|
//
|
||||||
|
const dtSymbol = await ocean.datatokens.getSymbol(
|
||||||
|
ddo.dataToken,
|
||||||
|
accountId
|
||||||
|
)
|
||||||
|
setDtSymbol(dtSymbol)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get everything which is in the pool
|
||||||
|
//
|
||||||
|
const oceanReserve = await ocean.pool.getOceanReserve(
|
||||||
|
accountId,
|
||||||
|
poolAddress
|
||||||
|
)
|
||||||
|
const dtReserve = await ocean.pool.getDTReserve(accountId, poolAddress)
|
||||||
|
setTotalBalance({
|
||||||
|
ocean: oceanReserve,
|
||||||
|
dt: dtReserve
|
||||||
|
})
|
||||||
|
|
||||||
|
const totalPoolTokens = await ocean.pool.totalSupply(poolAddress)
|
||||||
|
setTotalPoolTokens(totalPoolTokens)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get everything the user has put into the pool
|
||||||
|
//
|
||||||
|
const poolTokens = await ocean.pool.sharesBalance(
|
||||||
|
accountId,
|
||||||
|
poolAddress
|
||||||
|
)
|
||||||
|
setPoolTokens(poolTokens)
|
||||||
|
|
||||||
|
// calculate user's provided liquidity based on pool tokens
|
||||||
|
const userOceanBalance =
|
||||||
|
(Number(poolTokens) / Number(totalPoolTokens)) * Number(oceanReserve)
|
||||||
|
|
||||||
|
const userDtBalance =
|
||||||
|
(Number(poolTokens) / Number(totalPoolTokens)) * Number(dtReserve)
|
||||||
|
|
||||||
|
const userBalance = {
|
||||||
|
ocean: `${userOceanBalance}`,
|
||||||
|
dt: `${userDtBalance}`
|
||||||
|
}
|
||||||
|
|
||||||
|
setUserBalance(userBalance)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message)
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
}, [ocean, accountId, price, poolAddress])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isLoading && !userBalance ? (
|
||||||
|
<Loader message="Retrieving pools..." />
|
||||||
|
) : showAdd ? (
|
||||||
|
<Add
|
||||||
|
setShowAdd={setShowAdd}
|
||||||
|
poolAddress={poolAddress}
|
||||||
|
totalPoolTokens={totalPoolTokens}
|
||||||
|
totalBalance={totalBalance}
|
||||||
|
/>
|
||||||
|
) : showRemove ? (
|
||||||
|
<Remove
|
||||||
|
setShowRemove={setShowRemove}
|
||||||
|
poolAddress={poolAddress}
|
||||||
|
totalPoolTokens={totalPoolTokens}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className={styles.dataToken}>
|
||||||
|
<PriceUnit price="1" symbol={dtSymbol} /> ={' '}
|
||||||
|
<PriceUnit price={price} />
|
||||||
|
<Conversion price={price} />
|
||||||
|
<Tooltip content="Explain how this price is determined..." />
|
||||||
|
<div className={styles.dataTokenLinks}>
|
||||||
|
<EtherscanLink network="rinkeby" path={`address/${poolAddress}`}>
|
||||||
|
Pool
|
||||||
|
</EtherscanLink>
|
||||||
|
<EtherscanLink network="rinkeby" path={`token/${ddo.dataToken}`}>
|
||||||
|
Data Token
|
||||||
|
</EtherscanLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.poolTokens}>
|
||||||
|
<div className={styles.tokens}>
|
||||||
|
<h3 className={styles.title}>
|
||||||
|
Your Liquidity
|
||||||
|
<Tooltip content="Explain what this represents, advantage of providing liquidity..." />
|
||||||
|
</h3>
|
||||||
|
<Token symbol="OCEAN" balance={userBalance.ocean} />
|
||||||
|
<Token symbol={dtSymbol} balance={userBalance.dt} />
|
||||||
|
<Token symbol="BPT" balance={poolTokens} />
|
||||||
|
<Token symbol="% of pool" balance={poolShare} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.tokens}>
|
||||||
|
<h3 className={styles.title}>Pool Statistics</h3>
|
||||||
|
<Token symbol="OCEAN" balance={totalBalance.ocean} />
|
||||||
|
<Token symbol={dtSymbol} balance={totalBalance.dt} />
|
||||||
|
<Token symbol="BPT" balance={totalPoolTokens} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={stylesActions.actions}>
|
||||||
|
<Button
|
||||||
|
style="primary"
|
||||||
|
size="small"
|
||||||
|
onClick={() => setShowAdd(true)}
|
||||||
|
>
|
||||||
|
Add Liquidity
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{hasAddedLiquidity && (
|
||||||
|
<Button size="small" onClick={() => setShowRemove(true)}>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -2,24 +2,20 @@ import React, { ReactElement, useState, useEffect } from 'react'
|
|||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import Compute from './Compute'
|
import Compute from './Compute'
|
||||||
import Consume from './Consume'
|
import Consume from './Consume'
|
||||||
import { MetadataMarket } from '../../../@types/Metadata'
|
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import Tabs from '../../atoms/Tabs'
|
import Tabs from '../../atoms/Tabs'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean, useMetadata } from '@oceanprotocol/react'
|
||||||
import compareAsBN from '../../../utils/compareAsBN'
|
import compareAsBN from '../../../utils/compareAsBN'
|
||||||
|
import Pool from './Pool'
|
||||||
|
|
||||||
export default function AssetActions({
|
export default function AssetActions({ ddo }: { ddo: DDO }): ReactElement {
|
||||||
metadata,
|
|
||||||
ddo
|
|
||||||
}: {
|
|
||||||
metadata: MetadataMarket
|
|
||||||
ddo: DDO
|
|
||||||
}): ReactElement {
|
|
||||||
const { balance } = useOcean()
|
const { balance } = useOcean()
|
||||||
const [price, setPrice] = useState<string>()
|
const { price } = useMetadata(ddo)
|
||||||
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
||||||
|
|
||||||
const isCompute = Boolean(ddo.findServiceByType('compute'))
|
const isCompute = Boolean(ddo.findServiceByType('compute'))
|
||||||
|
const { attributes } = ddo.findServiceByType('metadata')
|
||||||
|
const { priceType } = attributes.additionalInformation
|
||||||
|
|
||||||
// Check user balance against price
|
// Check user balance against price
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -34,17 +30,12 @@ export default function AssetActions({
|
|||||||
}, [balance, price])
|
}, [balance, price])
|
||||||
|
|
||||||
const UseContent = isCompute ? (
|
const UseContent = isCompute ? (
|
||||||
<Compute
|
<Compute ddo={ddo} isBalanceSufficient={isBalanceSufficient} />
|
||||||
ddo={ddo}
|
|
||||||
isBalanceSufficient={isBalanceSufficient}
|
|
||||||
setPrice={setPrice}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<Consume
|
<Consume
|
||||||
ddo={ddo}
|
ddo={ddo}
|
||||||
isBalanceSufficient={isBalanceSufficient}
|
isBalanceSufficient={isBalanceSufficient}
|
||||||
file={metadata.main.files[0]}
|
file={attributes.main.files[0]}
|
||||||
setPrice={setPrice}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -53,9 +44,9 @@ export default function AssetActions({
|
|||||||
title: 'Use',
|
title: 'Use',
|
||||||
content: UseContent
|
content: UseContent
|
||||||
},
|
},
|
||||||
{
|
(!priceType || priceType === 'advanced') && {
|
||||||
title: 'Trade',
|
title: 'Pool',
|
||||||
content: 'Trade Me'
|
content: <Pool ddo={ddo} />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -14,10 +14,3 @@
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metaFull svg {
|
|
||||||
width: var(--font-size-mini);
|
|
||||||
height: var(--font-size-mini);
|
|
||||||
display: inline-block;
|
|
||||||
fill: currentColor;
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,7 @@ import MetaItem from './MetaItem'
|
|||||||
import styles from './MetaFull.module.css'
|
import styles from './MetaFull.module.css'
|
||||||
import { MetadataMarket } from '../../../@types/Metadata'
|
import { MetadataMarket } from '../../../@types/Metadata'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import { ReactComponent as External } from '../../../images/external.svg'
|
import EtherscanLink from '../../atoms/EtherscanLink'
|
||||||
|
|
||||||
export default function MetaFull({
|
export default function MetaFull({
|
||||||
ddo,
|
ddo,
|
||||||
@ -32,18 +32,11 @@ export default function MetaFull({
|
|||||||
<MetaItem title="DID" content={<code>{id}</code>} />
|
<MetaItem title="DID" content={<code>{id}</code>} />
|
||||||
|
|
||||||
<MetaItem
|
<MetaItem
|
||||||
title="Data Token Address"
|
title="Data Token"
|
||||||
content={
|
content={
|
||||||
<a
|
<EtherscanLink network="rinkeby" path={`token/${dataToken}`}>
|
||||||
href={`https://rinkeby.etherscan.io/token/${dataToken}`}
|
<code>{dataToken}</code>
|
||||||
title="View on Etherscan"
|
</EtherscanLink>
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
<code>
|
|
||||||
{dataToken} <External />
|
|
||||||
</code>
|
|
||||||
</a>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.grid {
|
.grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: calc(var(--spacer) * 2);
|
gap: calc(var(--spacer) * 1.5);
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-top: -1.5rem;
|
margin-top: -1.5rem;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ export default function AssetContent({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.sticky}>
|
<div className={styles.sticky}>
|
||||||
<AssetActions metadata={metadata} ddo={ddo} />
|
<AssetActions ddo={ddo} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
@ -26,7 +26,6 @@ export default function PageTemplateAssetDetails({
|
|||||||
try {
|
try {
|
||||||
const metadataStore = new MetadataStore(config.metadataStoreUri, Logger)
|
const metadataStore = new MetadataStore(config.metadataStoreUri, Logger)
|
||||||
const ddo = await metadataStore.retrieveDDO(did)
|
const ddo = await metadataStore.retrieveDDO(did)
|
||||||
setDdo(ddo)
|
|
||||||
|
|
||||||
if (!ddo) {
|
if (!ddo) {
|
||||||
setTitle('Could not retrieve asset')
|
setTitle('Could not retrieve asset')
|
||||||
@ -34,6 +33,8 @@ export default function PageTemplateAssetDetails({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDdo(ddo)
|
||||||
|
|
||||||
const { attributes }: ServiceMetadataMarket = ddo.findServiceByType(
|
const { attributes }: ServiceMetadataMarket = ddo.findServiceByType(
|
||||||
'metadata'
|
'metadata'
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user