diff --git a/src/components/organisms/AssetActions/Pool/Actions.module.css b/src/components/organisms/AssetActions/Pool/Actions.module.css
new file mode 100644
index 000000000..d6b521977
--- /dev/null
+++ b/src/components/organisms/AssetActions/Pool/Actions.module.css
@@ -0,0 +1,25 @@
+.actions {
+ margin-left: -2rem;
+ margin-right: -2rem;
+ padding-left: var(--spacer);
+ padding-right: var(--spacer);
+ padding-top: calc(var(--spacer) / 1.5);
+ border-top: 1px solid var(--border-color);
+ text-align: center;
+ display: flex;
+ justify-content: center;
+}
+
+.success {
+ margin-top: calc(var(--spacer) / 2);
+ margin-bottom: calc(var(--spacer) / 2);
+}
+
+.actions button {
+ margin-left: calc(var(--spacer) / 4);
+ margin-right: calc(var(--spacer) / 4);
+}
+
+.actions button svg {
+ fill: currentColor;
+}
diff --git a/src/components/organisms/AssetActions/Pool/Actions.tsx b/src/components/organisms/AssetActions/Pool/Actions.tsx
new file mode 100644
index 000000000..3aa1ff3b2
--- /dev/null
+++ b/src/components/organisms/AssetActions/Pool/Actions.tsx
@@ -0,0 +1,75 @@
+import React, { ReactElement } from 'react'
+import Loader from '../../../atoms/Loader'
+import Button from '../../../atoms/Button'
+import styles from './Actions.module.css'
+import ExplorerLink from '../../../atoms/ExplorerLink'
+import SuccessConfetti from '../../../atoms/SuccessConfetti'
+import { useOcean } from '../../../../providers/Ocean'
+import { useWeb3 } from '../../../../providers/Web3'
+import TokenApproval from '../../../molecules/TokenApproval'
+
+export default function Actions({
+ isLoading,
+ loaderMessage,
+ successMessage,
+ txId,
+ actionName,
+ amount,
+ coin,
+ action,
+ isDisabled
+}: {
+ isLoading: boolean
+ loaderMessage: string
+ successMessage: string
+ txId: string
+ actionName: string
+ amount?: string
+ coin?: string
+ action: () => void
+ isDisabled?: boolean
+}): ReactElement {
+ const { networkId } = useWeb3()
+ const { ocean } = useOcean()
+
+ const actionButton = (
+
+ )
+
+ return (
+ <>
+
+ {isLoading ? (
+
+ ) : actionName === 'Supply' || actionName === 'Swap' ? (
+
+ ) : (
+ actionButton
+ )}
+
+ {txId && (
+
+ View transaction
+
+ }
+ />
+ )}
+ >
+ )
+}
diff --git a/src/components/organisms/AssetActions/Pool/Add/Output.module.css b/src/components/organisms/AssetActions/Pool/Add/Output.module.css
new file mode 100644
index 000000000..c21bfc785
--- /dev/null
+++ b/src/components/organisms/AssetActions/Pool/Add/Output.module.css
@@ -0,0 +1,24 @@
+.output {
+ display: grid;
+ gap: var(--spacer);
+ grid-template-columns: 1fr 1fr;
+ padding-bottom: calc(var(--spacer) / 2);
+}
+
+.output p {
+ font-weight: var(--font-weight-bold);
+ margin-bottom: calc(var(--spacer) / 8);
+ font-size: var(--font-size-small);
+}
+
+.output div:first-child [class*='token'] {
+ white-space: normal;
+}
+
+.output div:first-child [class*='token'] > figure {
+ display: none;
+}
+
+.help {
+ text-align: center;
+}
diff --git a/src/components/organisms/AssetActions/Pool/Add/index.module.css b/src/components/organisms/AssetActions/Pool/Add/index.module.css
new file mode 100644
index 000000000..3f5b2967c
--- /dev/null
+++ b/src/components/organisms/AssetActions/Pool/Add/index.module.css
@@ -0,0 +1,24 @@
+.addInput {
+ margin: 0 auto calc(var(--spacer) / 1.5) auto;
+ background: var(--background-highlight);
+ padding: var(--spacer) calc(var(--spacer) * 2.5) calc(var(--spacer) * 1.2)
+ calc(var(--spacer) * 2.5);
+ border-bottom: 1px solid var(--border-color);
+ margin-top: -2rem;
+ margin-left: -2rem;
+ margin-right: -2rem;
+ position: relative;
+}
+
+.addInput input {
+ text-align: center;
+}
+
+.addInput div[class*='field'] {
+ margin-bottom: 0;
+}
+
+.warning {
+ margin-left: -3rem;
+ margin-right: -3rem;
+}
diff --git a/src/components/organisms/AssetActions/Pool/Remove.module.css b/src/components/organisms/AssetActions/Pool/Remove.module.css
new file mode 100644
index 000000000..1d3d229e8
--- /dev/null
+++ b/src/components/organisms/AssetActions/Pool/Remove.module.css
@@ -0,0 +1,70 @@
+.removeInput {
+ composes: addInput from './Add/index.module.css';
+ padding-left: calc(var(--spacer) * 2);
+ padding-right: calc(var(--spacer) * 2);
+ padding-bottom: calc(var(--spacer) / 2);
+}
+
+.range {
+ margin-top: calc(var(--spacer) / 2);
+ text-align: center;
+}
+
+.range h3 {
+ margin-bottom: calc(var(--spacer) / 4);
+}
+
+.range input {
+ width: 100%;
+}
+
+.range p {
+ margin-bottom: 0;
+ margin-left: -2rem;
+ margin-right: -2rem;
+}
+
+.slider {
+ position: relative;
+ z-index: 1;
+}
+
+.maximum {
+ position: absolute;
+ right: -2rem;
+ bottom: 2rem;
+ font-size: var(--font-size-mini);
+ min-width: 5rem;
+ text-align: center;
+}
+
+.toggle {
+ margin-top: calc(var(--spacer) / 2);
+ margin-bottom: 0;
+ font-size: var(--font-size-mini);
+ margin-bottom: -2rem;
+}
+
+.output {
+ composes: output from './Add/Output.module.css';
+}
+
+.output [class*='token'] {
+ white-space: nowrap;
+}
+
+.output [class*='token'] > figure {
+ display: inline-block;
+}
+
+.output figure[class*='pool shares'] {
+ display: none;
+}
+
+.output p {
+ font-size: var(--font-size-small);
+}
+
+.slippage {
+ composes: slippage from '../Trade/Slippage.module.css';
+}
diff --git a/src/components/organisms/AssetActions/Pool/Remove.tsx b/src/components/organisms/AssetActions/Pool/Remove.tsx
new file mode 100644
index 000000000..77c3b3a7f
--- /dev/null
+++ b/src/components/organisms/AssetActions/Pool/Remove.tsx
@@ -0,0 +1,313 @@
+import React, {
+ ReactElement,
+ useState,
+ ChangeEvent,
+ useEffect,
+ FormEvent,
+ useRef
+} from 'react'
+import styles from './Remove.module.css'
+import Header from './Header'
+import { toast } from 'react-toastify'
+import Actions from './Actions'
+import { Logger } from '@oceanprotocol/lib'
+import Token from './Token'
+import FormHelp from '../../../atoms/Input/Help'
+import Button from '../../../atoms/Button'
+import { getMaxPercentRemove } from './utils'
+import { graphql, useStaticQuery } from 'gatsby'
+import debounce from 'lodash.debounce'
+import UserLiquidity from '../../../atoms/UserLiquidity'
+import InputElement from '../../../atoms/Input/InputElement'
+import { useOcean } from '../../../../providers/Ocean'
+import { useWeb3 } from '../../../../providers/Web3'
+import Decimal from 'decimal.js'
+import { useAsset } from '../../../../providers/Asset'
+
+const contentQuery = graphql`
+ query PoolRemoveQuery {
+ content: allFile(filter: { relativePath: { eq: "price.json" } }) {
+ edges {
+ node {
+ childContentJson {
+ pool {
+ remove {
+ title
+ simple
+ advanced
+ output {
+ titleIn
+ titleOut
+ }
+ action
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+`
+
+export default function Remove({
+ setShowRemove,
+ refreshInfo,
+ poolAddress,
+ poolTokens,
+ totalPoolTokens,
+ dtSymbol
+}: {
+ setShowRemove: (show: boolean) => void
+ refreshInfo: () => void
+ poolAddress: string
+ poolTokens: string
+ totalPoolTokens: string
+ dtSymbol: string
+}): ReactElement {
+ const data = useStaticQuery(contentQuery)
+ const content = data.content.edges[0].node.childContentJson.pool.remove
+
+ const slippagePresets = ['5', '10', '15', '25', '50']
+ const { accountId } = useWeb3()
+ const { ocean } = useOcean()
+ const { isAssetNetwork } = useAsset()
+ const [amountPercent, setAmountPercent] = useState('0')
+ const [amountMaxPercent, setAmountMaxPercent] = useState('100')
+ const [amountPoolShares, setAmountPoolShares] = useState('0')
+ const [amountOcean, setAmountOcean] = useState('0')
+ const [amountDatatoken, setAmountDatatoken] = useState('0')
+ const [isAdvanced, setIsAdvanced] = useState(false)
+ const [isLoading, setIsLoading] = useState()
+ const [txId, setTxId] = useState()
+ const [slippage, setSlippage] = useState('5')
+ const [minOceanAmount, setMinOceanAmount] = useState('0')
+ const [minDatatokenAmount, setMinDatatokenAmount] = useState('0')
+
+ Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
+
+ async function handleRemoveLiquidity() {
+ setIsLoading(true)
+ try {
+ const result =
+ isAdvanced === true
+ ? await ocean.pool.removePoolLiquidity(
+ accountId,
+ poolAddress,
+ amountPoolShares,
+ minDatatokenAmount,
+ minOceanAmount
+ )
+ : await ocean.pool.removeOceanLiquidityWithMinimum(
+ accountId,
+ poolAddress,
+ amountPoolShares,
+ minOceanAmount
+ )
+
+ setTxId(result?.transactionHash)
+ refreshInfo()
+ } catch (error) {
+ Logger.error(error.message)
+ toast.error(error.message)
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ // Get and set max percentage
+ useEffect(() => {
+ if (!ocean || !poolTokens) return
+
+ async function getMax() {
+ const amountMaxPercent =
+ isAdvanced === true
+ ? '100'
+ : await getMaxPercentRemove(ocean, poolAddress, poolTokens)
+ setAmountMaxPercent(amountMaxPercent)
+ }
+ getMax()
+ }, [ocean, isAdvanced, poolAddress, poolTokens])
+
+ const getValues = useRef(
+ debounce(async (newAmountPoolShares, isAdvanced) => {
+ if (isAdvanced === true) {
+ const tokens = await ocean.pool.getTokensRemovedforPoolShares(
+ poolAddress,
+ `${newAmountPoolShares}`
+ )
+ setAmountOcean(tokens?.oceanAmount)
+ setAmountDatatoken(tokens?.dtAmount)
+ return
+ }
+
+ const amountOcean = await ocean.pool.getOceanRemovedforPoolShares(
+ poolAddress,
+ newAmountPoolShares
+ )
+ setAmountOcean(amountOcean)
+ }, 150)
+ )
+ // Check and set outputs when amountPoolShares changes
+ useEffect(() => {
+ if (!ocean || !poolTokens) return
+ getValues.current(amountPoolShares, isAdvanced)
+ }, [
+ amountPoolShares,
+ isAdvanced,
+ ocean,
+ poolTokens,
+ poolAddress,
+ totalPoolTokens
+ ])
+
+ useEffect(() => {
+ const minOceanAmount = new Decimal(amountOcean)
+ .mul(new Decimal(100).minus(new Decimal(slippage)))
+ .dividedBy(100)
+ .toString()
+
+ const minDatatokenAmount = new Decimal(amountDatatoken)
+ .mul(new Decimal(100).minus(new Decimal(slippage)))
+ .dividedBy(100)
+ .toString()
+
+ setMinOceanAmount(minOceanAmount.slice(0, 18))
+ setMinDatatokenAmount(minDatatokenAmount.slice(0, 18))
+ }, [slippage, amountOcean, amountDatatoken, isAdvanced])
+
+ // Set amountPoolShares based on set slider value
+ function handleAmountPercentChange(e: ChangeEvent) {
+ setAmountPercent(e.target.value)
+ if (!poolTokens) return
+
+ const amountPoolShares = new Decimal(e.target.value)
+ .dividedBy(100)
+ .mul(new Decimal(poolTokens))
+ .toString()
+
+ setAmountPoolShares(`${amountPoolShares.slice(0, 18)}`)
+ }
+
+ function handleMaxButton(e: ChangeEvent) {
+ e.preventDefault()
+ setAmountPercent(amountMaxPercent)
+
+ const amountPoolShares = new Decimal(amountMaxPercent)
+ .dividedBy(100)
+ .mul(new Decimal(poolTokens))
+ .toString()
+
+ setAmountPoolShares(`${amountPoolShares.slice(0, 18)}`)
+ }
+
+ function handleAdvancedButton(e: FormEvent) {
+ e.preventDefault()
+ setIsAdvanced(!isAdvanced)
+
+ setAmountPoolShares('0')
+ setAmountPercent('0')
+ setAmountOcean('0')
+ setSlippage('5')
+ setMinOceanAmount('0')
+ setMinDatatokenAmount('0')
+
+ if (isAdvanced === true) {
+ setAmountDatatoken('0')
+ }
+ }
+
+ function handleSlippageChange(e: ChangeEvent) {
+ setSlippage(e.target.value)
+ }
+
+ return (
+
+ )
+}
diff --git a/src/components/organisms/AssetActions/Pool/index.tsx b/src/components/organisms/AssetActions/Pool/index.tsx
index a2cccdc11..72eeb6404 100644
--- a/src/components/organisms/AssetActions/Pool/index.tsx
+++ b/src/components/organisms/AssetActions/Pool/index.tsx
@@ -1,8 +1,8 @@
import React, { ReactElement, useEffect, useState } from 'react'
import { Logger } from '@oceanprotocol/lib'
import styles from './index.module.css'
+import stylesActions from './Actions.module.css'
import PriceUnit from '../../../atoms/Price/PriceUnit'
-import Alert from '../../../atoms/Alert'
import Tooltip from '../../../atoms/Tooltip'
import ExplorerLink from '../../../atoms/ExplorerLink'
import Token from './Token'
@@ -19,10 +19,8 @@ import PoolTransactions from '../../../molecules/PoolTransactions'
import { fetchData, getQueryContext } from '../../../../utils/subgraph'
import { isValidNumber } from './../../../../utils/numberValidations'
import Decimal from 'decimal.js'
-import {
- MigrationStatus,
- useMigrationStatus
-} from '../../../../providers/Migration'
+import Remove from './Remove'
+import Button from '../../../atoms/Button'
const REFETCH_INTERVAL = 5000
@@ -86,11 +84,10 @@ export default function Pool(): ReactElement {
const content = data.content.edges[0].node.childContentJson.pool
const { accountId } = useWeb3()
- const { status } = useMigrationStatus()
const [dtSymbol, setDtSymbol] = useState()
const [oceanSymbol, setOceanSymbol] = useState()
- const { ddo, owner, price } = useAsset()
+ const { ddo, owner, price, isInPurgatory, isAssetNetwork } = useAsset()
const [poolTokens, setPoolTokens] = useState()
const [totalPoolTokens, setTotalPoolTokens] = useState()
@@ -116,6 +113,13 @@ export default function Pool(): ReactElement {
const [liquidityFetchInterval, setLiquidityFetchInterval] =
useState()
+ const [showRemove, setShowRemove] = useState(false)
+ const [isRemoveDisabled, setIsRemoveDisabled] = useState(false)
+
+ const [hasAddedLiquidity, setHasAddedLiquidity] = useState(false)
+ // the purpose of the value is just to trigger the effect
+ const [refreshPool, setRefreshPool] = useState(false)
+
async function getPoolLiquidity() {
const queryContext = getQueryContext(ddo.chainId)
const queryVariables = {
@@ -267,6 +271,10 @@ export default function Pool(): ReactElement {
init()
}, [dataLiquidity, ddo.dataToken, price.datatoken, price.ocean, price?.value])
+ useEffect(() => {
+ setIsRemoveDisabled(isInPurgatory && owner === accountId)
+ }, [isInPurgatory, owner, accountId])
+
useEffect(() => {
if (!dataLiquidity) return
const poolShare =
@@ -280,6 +288,7 @@ export default function Pool(): ReactElement {
.toFixed(5)
setPoolShare(poolShare)
+ setHasAddedLiquidity(Number(poolShare) > 0)
const totalUserLiquidityInOcean =
isValidNumber(userLiquidity?.ocean) &&
@@ -350,7 +359,23 @@ export default function Pool(): ReactElement {
init()
}, [accountId, price, ddo, owner, totalPoolTokens])
- return (
+ const refreshInfo = async () => {
+ setRefreshPool(!refreshPool)
+
+ // need some form of replacement or something.
+ // await refreshPrice()
+ }
+
+ return showRemove ? (
+
+ ) : (
<>
={' '}
@@ -433,15 +458,16 @@ export default function Pool(): ReactElement {
>
-
+
+
+
+
{accountId && (
diff --git a/src/components/organisms/AssetActions/Pool/utils.ts b/src/components/organisms/AssetActions/Pool/utils.ts
new file mode 100644
index 000000000..68fe113e2
--- /dev/null
+++ b/src/components/organisms/AssetActions/Pool/utils.ts
@@ -0,0 +1,37 @@
+import { Ocean } from '@oceanprotocol/lib'
+
+import { isValidNumber } from './../../../../utils/numberValidations'
+import Decimal from 'decimal.js'
+
+Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
+
+export async function getMaxPercentRemove(
+ ocean: Ocean,
+ poolAddress: string,
+ poolTokens: string
+): Promise {
+ const amountMaxOcean = await ocean.pool.getOceanMaxRemoveLiquidity(
+ poolAddress
+ )
+
+ const amountMaxPoolShares =
+ await ocean.pool.getPoolSharesRequiredToRemoveOcean(
+ poolAddress,
+ amountMaxOcean
+ )
+
+ let amountMaxPercent =
+ isValidNumber(amountMaxPoolShares) && isValidNumber(poolTokens)
+ ? new Decimal(amountMaxPoolShares)
+ .dividedBy(new Decimal(poolTokens))
+ .mul(100)
+ .floor()
+ .toString()
+ : '0'
+
+ if (Number(amountMaxPercent) > 100) {
+ amountMaxPercent = '100'
+ }
+
+ return amountMaxPercent
+}
diff --git a/src/components/organisms/AssetActions/Trade/Slippage.module.css b/src/components/organisms/AssetActions/Trade/Slippage.module.css
new file mode 100644
index 000000000..47041a7f2
--- /dev/null
+++ b/src/components/organisms/AssetActions/Trade/Slippage.module.css
@@ -0,0 +1,30 @@
+.slippage {
+ font-size: var(--font-size-small);
+ border-top: 1px solid var(--border-color);
+ margin-left: -2rem;
+ margin-right: -2rem;
+ padding: calc(var(--spacer) / 4) var(--spacer);
+ color: var(--color-secondary);
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: calc(var(--spacer) / 3);
+}
+
+.slippage strong {
+ font-weight: var(--font-weight-base);
+ text-align: right;
+}
+
+.title {
+ font-family: var(--font-family-base);
+ font-weight: var(--font-weight-base);
+ font-size: var(--font-size-mini);
+ text-align: center;
+ margin-bottom: calc(var(--spacer) / 4);
+ color: var(--color-secondary);
+}
+
+.slippage select {
+ width: fit-content;
+ display: inline-block;
+}
diff --git a/src/components/organisms/AssetActions/index.tsx b/src/components/organisms/AssetActions/index.tsx
index ef7f62cc7..e1eeaa9bb 100644
--- a/src/components/organisms/AssetActions/index.tsx
+++ b/src/components/organisms/AssetActions/index.tsx
@@ -135,7 +135,6 @@ export default function AssetActions(): ReactElement {
return (
<>
- {price?.type === 'pool' && }