mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Merge pull request #109 from oceanprotocol/feature/add-liquidity
Add liquidity checks
This commit is contained in:
commit
e5563c88b1
@ -10,7 +10,7 @@
|
||||
"titleIn": "You will receive",
|
||||
"titleOut": "You will earn"
|
||||
},
|
||||
"action": "Supply"
|
||||
"action": "Approve & Supply"
|
||||
},
|
||||
"remove": {
|
||||
"title": "Remove Liquidity",
|
||||
@ -20,7 +20,7 @@
|
||||
"titleIn": "You will spend",
|
||||
"titleOut": "You will receive"
|
||||
},
|
||||
"action": "Remove"
|
||||
"action": "Approve & Remove"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
margin: 0;
|
||||
border-radius: var(--border-radius);
|
||||
transition: 0.2s ease-out;
|
||||
min-height: 43px;
|
||||
height: 43px;
|
||||
min-width: 0;
|
||||
appearance: none;
|
||||
display: block;
|
||||
@ -49,6 +49,11 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
composes: input;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.select {
|
||||
composes: input;
|
||||
height: 43px;
|
||||
@ -179,15 +184,16 @@
|
||||
.prefix,
|
||||
.postfix {
|
||||
border: 1px solid var(--brand-grey-lighter);
|
||||
min-height: 43px;
|
||||
height: 43px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: calc(var(--spacer) / 4);
|
||||
padding-right: calc(var(--spacer) / 4);
|
||||
color: var(--color-secondary);
|
||||
color: var(--brand-grey);
|
||||
font-size: var(--font-size-small);
|
||||
transition: border 0.2s ease-out;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.prefix {
|
||||
|
@ -57,7 +57,12 @@ export default function InputElement({
|
||||
)
|
||||
case 'textarea':
|
||||
return (
|
||||
<textarea name={name} id={name} className={styles.input} {...props} />
|
||||
<textarea
|
||||
name={name}
|
||||
id={name}
|
||||
className={styles.textarea}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
|
@ -13,12 +13,18 @@
|
||||
}
|
||||
|
||||
.error {
|
||||
font-size: var(--font-size-small);
|
||||
color: var(--brand-alert-red);
|
||||
display: inline-block;
|
||||
font-size: var(--font-size-mini);
|
||||
line-height: 1.2;
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--brand-white);
|
||||
background: var(--brand-alert-red);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 0.2rem 0.4rem;
|
||||
position: absolute;
|
||||
text-align: right;
|
||||
right: 0;
|
||||
top: 0;
|
||||
top: 85%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.hasError label {
|
||||
@ -26,7 +32,10 @@
|
||||
}
|
||||
|
||||
.hasError input,
|
||||
.hasError input:focus,
|
||||
.hasError select,
|
||||
.hasError textarea {
|
||||
.hasError textarea,
|
||||
.hasError [class*='prefix'],
|
||||
.hasError [class*='postfix'] {
|
||||
border-color: var(--brand-alert-red);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import InputElement from './InputElement'
|
||||
import Help from './Help'
|
||||
import Label from './Label'
|
||||
import styles from './index.module.css'
|
||||
import { ErrorMessage } from 'formik'
|
||||
import { ErrorMessage, FieldInputProps } from 'formik'
|
||||
import classNames from 'classnames/bind'
|
||||
|
||||
const cx = classNames.bind(styles)
|
||||
@ -33,7 +33,7 @@ export interface InputProps {
|
||||
max?: string
|
||||
disabled?: boolean
|
||||
readOnly?: boolean
|
||||
field?: any
|
||||
field?: FieldInputProps<any>
|
||||
form?: any
|
||||
prefix?: string | ReactElement
|
||||
postfix?: string | ReactElement
|
||||
@ -71,7 +71,7 @@ export default function Input(props: Partial<InputProps>): ReactElement {
|
||||
</Label>
|
||||
<InputElement small={small} {...field} {...props} />
|
||||
|
||||
{field && field.name !== 'price' && (
|
||||
{field && field.name !== 'price' && hasError && (
|
||||
<div className={styles.error}>
|
||||
<ErrorMessage name={field.name} />
|
||||
</div>
|
||||
|
4
src/components/atoms/SuccessConfetti.module.css
Normal file
4
src/components/atoms/SuccessConfetti.module.css
Normal file
@ -0,0 +1,4 @@
|
||||
.action {
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
import Alert from '../../atoms/Alert'
|
||||
import Button from '../../atoms/Button'
|
||||
import React, { ReactElement, useEffect } from 'react'
|
||||
import Alert from './Alert'
|
||||
import React, { ReactElement, ReactNode, useEffect } from 'react'
|
||||
import { confetti } from 'dom-confetti'
|
||||
import styles from './Success.module.css'
|
||||
import styles from './SuccessConfetti.module.css'
|
||||
|
||||
const confettiConfig = {
|
||||
angle: 90,
|
||||
@ -24,33 +23,29 @@ const confettiConfig = {
|
||||
]
|
||||
}
|
||||
|
||||
export default function Success({
|
||||
export default function SuccessConfetti({
|
||||
success,
|
||||
did
|
||||
action
|
||||
}: {
|
||||
success: string
|
||||
did: string
|
||||
action: ReactNode
|
||||
}): ReactElement {
|
||||
// Have some confetti upon success
|
||||
useEffect(() => {
|
||||
if (!success || typeof window === 'undefined') return
|
||||
|
||||
const startElement: HTMLElement = document.querySelector('a[data-confetti]')
|
||||
const startElement: HTMLElement = document.querySelector(
|
||||
'span[data-confetti]'
|
||||
)
|
||||
confetti(startElement, confettiConfig)
|
||||
}, [success])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Alert text={success} state="success" />
|
||||
<Button
|
||||
style="primary"
|
||||
size="small"
|
||||
href={`/asset/${did}`}
|
||||
className={styles.action}
|
||||
data-confetti
|
||||
>
|
||||
Go to data set →
|
||||
</Button>
|
||||
<span className={styles.action} data-confetti>
|
||||
{action}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
@ -11,18 +11,18 @@
|
||||
|
||||
.balance {
|
||||
text-align: center;
|
||||
font-size: var(--font-size-small);
|
||||
font-size: var(--font-size-small) !important;
|
||||
border: 1px solid var(--brand-grey-lighter);
|
||||
border-right: 0;
|
||||
margin-right: -3px;
|
||||
padding: calc(var(--spacer) / 4.5) calc(var(--spacer) / 2);
|
||||
height: 35px;
|
||||
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 2)
|
||||
calc(var(--spacer) / 4.5) calc(var(--spacer) / 2);
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.balance strong {
|
||||
color: var(--brand-grey);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { DataTokenOptions, useOcean } from '@oceanprotocol/react'
|
||||
import PriceUnit from '../../../atoms/Price/PriceUnit'
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import { PriceOptionsMarket } from '../../../../@types/MetaData'
|
||||
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||
@ -66,9 +67,12 @@ export default function Dynamic({
|
||||
|
||||
<aside className={styles.wallet}>
|
||||
{balance?.ocean && (
|
||||
<div className={styles.balance}>
|
||||
OCEAN <strong>{balance.ocean}</strong>
|
||||
</div>
|
||||
<PriceUnit
|
||||
className={styles.balance}
|
||||
price={balance.ocean}
|
||||
symbol="OCEAN"
|
||||
small
|
||||
/>
|
||||
)}
|
||||
<Wallet />
|
||||
</aside>
|
||||
|
@ -1,9 +1,9 @@
|
||||
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'
|
||||
import EtherscanLink from '../../../atoms/EtherscanLink'
|
||||
import SuccessConfetti from '../../../atoms/SuccessConfetti'
|
||||
|
||||
export default function Actions({
|
||||
isLoading,
|
||||
@ -30,15 +30,14 @@ export default function Actions({
|
||||
)}
|
||||
</div>
|
||||
{txId && (
|
||||
<>
|
||||
<Alert
|
||||
text={`Successfully added liquidity. Transaction ID: ${txId}`}
|
||||
state="success"
|
||||
/>
|
||||
<EtherscanLink network="rinkeby" path={`/tx/${txId}`}>
|
||||
Etherscan
|
||||
</EtherscanLink>
|
||||
</>
|
||||
<SuccessConfetti
|
||||
success="Successfully added liquidity."
|
||||
action={
|
||||
<EtherscanLink network="rinkeby" path={`/tx/${txId}`}>
|
||||
See on Etherscan
|
||||
</EtherscanLink>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
@ -1,8 +1,8 @@
|
||||
.addInput {
|
||||
margin: 0 auto calc(var(--spacer) / 1.5) auto;
|
||||
background: var(--brand-grey-dimmed);
|
||||
padding: var(--spacer) calc(var(--spacer) * 3) calc(var(--spacer) * 1.2)
|
||||
calc(var(--spacer) * 3);
|
||||
padding: var(--spacer) calc(var(--spacer) * 2.5) calc(var(--spacer) * 1.2)
|
||||
calc(var(--spacer) * 2.5);
|
||||
border-bottom: 1px solid var(--brand-grey-lighter);
|
||||
margin-top: -2rem;
|
||||
margin-left: -2rem;
|
||||
@ -14,45 +14,34 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.addInput div[class*='field'] {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.buttonMax {
|
||||
position: absolute;
|
||||
font-size: var(--font-size-mini);
|
||||
bottom: calc(var(--spacer) / 2);
|
||||
right: calc(var(--spacer) * 3);
|
||||
right: calc(var(--spacer) * 2.5);
|
||||
}
|
||||
|
||||
.userLiquidity {
|
||||
.userLiquidity > div {
|
||||
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);
|
||||
}
|
||||
|
||||
.userLiquidity > div:last-child {
|
||||
margin-bottom: calc(var(--spacer) / 4);
|
||||
}
|
||||
|
||||
.userLiquidity span + div {
|
||||
transform: scale(0.8);
|
||||
transform-origin: right center;
|
||||
}
|
||||
|
||||
.coinswitch,
|
||||
.coinPopover li {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.coinswitch svg {
|
||||
width: 0.6em;
|
||||
height: 0.6em;
|
||||
display: inline-block;
|
||||
fill: currentColor;
|
||||
margin-right: 0.5rem;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.coinPopover li {
|
||||
padding: calc(var(--spacer) / 4) calc(var(--spacer) / 2);
|
||||
}
|
||||
|
||||
.output {
|
||||
display: grid;
|
||||
gap: var(--spacer);
|
||||
|
@ -1,17 +1,18 @@
|
||||
import React, { ReactElement, useState, ChangeEvent, useEffect } from 'react'
|
||||
import React, { ReactElement, useState, useEffect } 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 Button from '../../../atoms/Button'
|
||||
import Token from './Token'
|
||||
import { Balance } from './'
|
||||
import PriceUnit from '../../../atoms/Price/PriceUnit'
|
||||
import Actions from './Actions'
|
||||
import Tooltip from '../../../atoms/Tooltip'
|
||||
import { ReactComponent as Caret } from '../../../../images/caret.svg'
|
||||
import { graphql, useStaticQuery } from 'gatsby'
|
||||
import * as Yup from 'yup'
|
||||
import { Field, FieldInputProps, Formik } from 'formik'
|
||||
import Input from '../../../atoms/Input'
|
||||
import CoinSelect from './CoinSelect'
|
||||
|
||||
const contentQuery = graphql`
|
||||
query PoolAddQuery {
|
||||
@ -36,6 +37,14 @@ const contentQuery = graphql`
|
||||
}
|
||||
`
|
||||
|
||||
interface FormAddLiquidity {
|
||||
amount: number
|
||||
}
|
||||
|
||||
const initialValues: FormAddLiquidity = {
|
||||
amount: undefined
|
||||
}
|
||||
|
||||
export default function Add({
|
||||
setShowAdd,
|
||||
poolAddress,
|
||||
@ -57,131 +66,187 @@ export default function Add({
|
||||
const content = data.content.edges[0].node.childContentJson.pool.add
|
||||
|
||||
const { ocean, accountId, balance } = useOcean()
|
||||
const [amount, setAmount] = useState('')
|
||||
const [txId, setTxId] = useState<string>('')
|
||||
const [isLoading, setIsLoading] = useState<boolean>()
|
||||
const [coin, setCoin] = useState<string>('OCEAN')
|
||||
const [txId, setTxId] = useState<string>()
|
||||
const [coin, setCoin] = useState('OCEAN')
|
||||
const [dtBalance, setDtBalance] = useState<string>()
|
||||
const [amountMax, setAmountMax] = useState<string>()
|
||||
|
||||
const newPoolTokens =
|
||||
totalBalance &&
|
||||
((Number(amount) / Number(totalBalance.ocean)) * 100).toFixed(2)
|
||||
|
||||
const newPoolShare =
|
||||
totalBalance &&
|
||||
((Number(newPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(2)
|
||||
// Live validation rules
|
||||
// https://github.com/jquense/yup#number
|
||||
const validationSchema = Yup.object().shape<FormAddLiquidity>({
|
||||
amount: Yup.number()
|
||||
.min(1, 'Must be more or equal to 1')
|
||||
.max(
|
||||
Number(amountMax),
|
||||
`Maximum you can add is ${Number(amountMax).toFixed(2)} ${coin}`
|
||||
)
|
||||
.required('Required')
|
||||
})
|
||||
|
||||
// Get datatoken balance when datatoken selected
|
||||
useEffect(() => {
|
||||
if (!ocean) return
|
||||
if (!ocean || coin === 'OCEAN') return
|
||||
|
||||
async function getDtBalance() {
|
||||
const dtBalance = await ocean.datatokens.balance(dtAddress, accountId)
|
||||
setDtBalance(dtBalance)
|
||||
}
|
||||
getDtBalance()
|
||||
}, [ocean, accountId, dtAddress])
|
||||
}, [ocean, accountId, dtAddress, coin])
|
||||
|
||||
async function handleAddLiquidity() {
|
||||
setIsLoading(true)
|
||||
// Get maximum amount for either OCEAN or datatoken
|
||||
useEffect(() => {
|
||||
if (!ocean) return
|
||||
|
||||
async function getMaximum() {
|
||||
const amountMaxPool =
|
||||
coin === 'OCEAN'
|
||||
? await ocean.pool.getOceanMaxAddLiquidity(poolAddress)
|
||||
: await ocean.pool.getDTMaxAddLiquidity(poolAddress)
|
||||
|
||||
const amountMax =
|
||||
coin === 'OCEAN'
|
||||
? Number(balance.ocean) > Number(amountMaxPool)
|
||||
? amountMaxPool
|
||||
: balance.ocean
|
||||
: Number(dtBalance) > Number(amountMaxPool)
|
||||
? amountMaxPool
|
||||
: dtBalance
|
||||
setAmountMax(amountMax)
|
||||
}
|
||||
getMaximum()
|
||||
}, [ocean, poolAddress, coin, dtBalance, balance.ocean])
|
||||
|
||||
// Submit
|
||||
async function handleAddLiquidity(amount: number, resetForm: () => void) {
|
||||
try {
|
||||
const result =
|
||||
coin === 'OCEAN'
|
||||
? await ocean.pool.addOceanLiquidity(accountId, poolAddress, amount)
|
||||
: await ocean.pool.addDTLiquidity(accountId, poolAddress, amount)
|
||||
? await ocean.pool.addOceanLiquidity(
|
||||
accountId,
|
||||
poolAddress,
|
||||
`${amount}`
|
||||
)
|
||||
: await ocean.pool.addDTLiquidity(accountId, poolAddress, `${amount}`)
|
||||
|
||||
setTxId(result?.transactionHash)
|
||||
resetForm()
|
||||
} catch (error) {
|
||||
console.error(error.message)
|
||||
toast.error(error.message)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
function handleAmountChange(e: ChangeEvent<HTMLInputElement>) {
|
||||
setAmount(e.target.value)
|
||||
}
|
||||
|
||||
function handleMax() {
|
||||
setAmount(coin === 'OCEAN' ? balance.ocean : dtBalance)
|
||||
}
|
||||
|
||||
// TODO: this is only a prototype and is an accessibility nightmare.
|
||||
// Needs to be refactored to either use custom select element instead of tippy.js,
|
||||
// or use <button> in this implementation.
|
||||
// Also needs to be closed when users click an option.
|
||||
const CoinSelect = () => (
|
||||
<ul className={styles.coinPopover}>
|
||||
<li onClick={() => setCoin('OCEAN')}>OCEAN</li>
|
||||
<li onClick={() => setCoin(dtSymbol)}>{dtSymbol}</li>
|
||||
</ul>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header title={content.title} backAction={() => setShowAdd(false)} />
|
||||
|
||||
<div className={styles.addInput}>
|
||||
<div className={styles.userLiquidity}>
|
||||
<span>Available: </span>
|
||||
{coin === 'OCEAN' ? (
|
||||
<PriceUnit price={balance.ocean} symbol="OCEAN" small />
|
||||
) : (
|
||||
<PriceUnit price={dtBalance} symbol={dtSymbol} small />
|
||||
)}
|
||||
</div>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={async (values, { setSubmitting, resetForm }) => {
|
||||
await handleAddLiquidity(values.amount, resetForm)
|
||||
setSubmitting(false)
|
||||
}}
|
||||
>
|
||||
{({
|
||||
values,
|
||||
touched,
|
||||
setTouched,
|
||||
isSubmitting,
|
||||
setFieldValue,
|
||||
submitForm,
|
||||
handleChange
|
||||
}) => {
|
||||
const newPoolTokens =
|
||||
totalBalance &&
|
||||
((values.amount / Number(totalBalance.ocean)) * 100).toFixed(2)
|
||||
|
||||
<InputElement
|
||||
value={amount}
|
||||
name="coin"
|
||||
type="number"
|
||||
prefix={
|
||||
<Tooltip
|
||||
content={<CoinSelect />}
|
||||
trigger="click focus"
|
||||
className={styles.coinswitch}
|
||||
placement="bottom"
|
||||
>
|
||||
{coin}
|
||||
<Caret aria-hidden="true" />
|
||||
</Tooltip>
|
||||
}
|
||||
placeholder="0"
|
||||
onChange={handleAmountChange}
|
||||
/>
|
||||
const newPoolShare =
|
||||
totalBalance &&
|
||||
((Number(newPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(2)
|
||||
|
||||
{(balance.ocean || dtBalance) > amount && (
|
||||
<Button
|
||||
className={styles.buttonMax}
|
||||
style="text"
|
||||
size="small"
|
||||
onClick={handleMax}
|
||||
>
|
||||
Use Max
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
return (
|
||||
<>
|
||||
<div className={styles.addInput}>
|
||||
<div className={styles.userLiquidity}>
|
||||
<div>
|
||||
<span>Available:</span>
|
||||
{coin === 'OCEAN' ? (
|
||||
<PriceUnit price={balance.ocean} symbol="OCEAN" small />
|
||||
) : (
|
||||
<PriceUnit price={dtBalance} symbol={dtSymbol} small />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span>Maximum:</span>
|
||||
<PriceUnit price={amountMax} symbol={coin} small />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.output}>
|
||||
<div>
|
||||
<p>{content.output.titleIn}</p>
|
||||
<Token symbol="pool shares" balance={newPoolTokens} />
|
||||
<Token symbol="% of pool" balance={newPoolShare} />
|
||||
</div>
|
||||
<div>
|
||||
<p>{content.output.titleOut}</p>
|
||||
<Token symbol="% swap fee" balance={swapFee} />
|
||||
</div>
|
||||
</div>
|
||||
<Field name="amount">
|
||||
{({
|
||||
field,
|
||||
form
|
||||
}: {
|
||||
field: FieldInputProps<FormAddLiquidity>
|
||||
form: any
|
||||
}) => (
|
||||
<Input
|
||||
type="number"
|
||||
max={amountMax}
|
||||
value={`${values.amount}`}
|
||||
prefix={
|
||||
<CoinSelect dtSymbol={dtSymbol} setCoin={setCoin} />
|
||||
}
|
||||
placeholder="0"
|
||||
field={field}
|
||||
form={form}
|
||||
onChange={(e) => {
|
||||
// Workaround so validation kicks in on first touch
|
||||
!touched?.amount && setTouched({ amount: true })
|
||||
handleChange(e)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Actions
|
||||
isLoading={isLoading}
|
||||
loaderMessage="Adding Liquidity..."
|
||||
actionName={content.action}
|
||||
action={handleAddLiquidity}
|
||||
txId={txId}
|
||||
/>
|
||||
{(Number(balance.ocean) || dtBalance) >
|
||||
(values.amount || 0) && (
|
||||
<Button
|
||||
className={styles.buttonMax}
|
||||
style="text"
|
||||
size="small"
|
||||
onClick={() => setFieldValue('amount', amountMax)}
|
||||
>
|
||||
Use Max
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.output}>
|
||||
<div>
|
||||
<p>{content.output.titleIn}</p>
|
||||
<Token symbol="pool shares" balance={newPoolTokens} />
|
||||
<Token symbol="% of pool" balance={newPoolShare} />
|
||||
</div>
|
||||
<div>
|
||||
<p>{content.output.titleOut}</p>
|
||||
<Token symbol="% swap fee" balance={swapFee} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Actions
|
||||
isLoading={isSubmitting}
|
||||
loaderMessage="Adding Liquidity..."
|
||||
actionName={content.action}
|
||||
action={submitForm}
|
||||
txId={txId}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
.coinSelect {
|
||||
composes: select from '../../../atoms/Input/InputElement.module.css';
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: var(--font-weight-base);
|
||||
border: none;
|
||||
margin-left: -0.5rem;
|
||||
margin-right: -0.5rem;
|
||||
background-color: var(--brand-grey-dimmed);
|
||||
width: auto;
|
||||
padding: 0 1.25rem 0 0.25rem;
|
||||
height: 41px;
|
||||
text-align: center;
|
||||
|
||||
/* custom arrow, without the divider line */
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
transparent 50%,
|
||||
var(--brand-purple) 50%
|
||||
),
|
||||
linear-gradient(135deg, var(--brand-grey) 50%, transparent 50%);
|
||||
background-position: calc(100% - 14px) 1.2rem, calc(100% - 9px) 1.2rem, 100% 0;
|
||||
}
|
||||
|
||||
.option {
|
||||
color: var(--brand-grey-dark);
|
||||
}
|
24
src/components/organisms/AssetActions/Pool/CoinSelect.tsx
Normal file
24
src/components/organisms/AssetActions/Pool/CoinSelect.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import styles from './CoinSelect.module.css'
|
||||
|
||||
export default function CoinSelect({
|
||||
dtSymbol,
|
||||
setCoin
|
||||
}: {
|
||||
dtSymbol: string
|
||||
setCoin: (coin: string) => void
|
||||
}): ReactElement {
|
||||
return (
|
||||
<select
|
||||
className={styles.coinSelect}
|
||||
onChange={(e) => setCoin(e.target.value)}
|
||||
>
|
||||
<option className={styles.option} value="OCEAN">
|
||||
OCEAN
|
||||
</option>
|
||||
<option className={styles.option} value={dtSymbol}>
|
||||
{dtSymbol}
|
||||
</option>
|
||||
</select>
|
||||
)
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import Alert from '../../atoms/Alert'
|
||||
import Success from './Success'
|
||||
import Button from '../../atoms/Button'
|
||||
import Loader from '../../atoms/Loader'
|
||||
import React, { ReactElement } from 'react'
|
||||
import styles from './Feedback.module.css'
|
||||
import SuccessConfetti from '../../atoms/SuccessConfetti'
|
||||
|
||||
export default function Feedback({
|
||||
error,
|
||||
@ -18,6 +18,17 @@ export default function Feedback({
|
||||
publishStepText: string
|
||||
setError: (error: string) => void
|
||||
}): ReactElement {
|
||||
const SuccessAction = () => (
|
||||
<Button
|
||||
style="primary"
|
||||
size="small"
|
||||
href={`/asset/${did}`}
|
||||
className={styles.action}
|
||||
>
|
||||
Go to data set →
|
||||
</Button>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={styles.feedback}>
|
||||
<div className={styles.box}>
|
||||
@ -35,7 +46,7 @@ export default function Feedback({
|
||||
</Button>
|
||||
</>
|
||||
) : success ? (
|
||||
<Success success={success} did={did} />
|
||||
<SuccessConfetti success={success} action={<SuccessAction />} />
|
||||
) : (
|
||||
<Loader message={publishStepText} />
|
||||
)}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { ReactElement, useEffect, FormEvent } from 'react'
|
||||
import styles from './PublishForm.module.css'
|
||||
import { useOcean } from '@oceanprotocol/react'
|
||||
import { useFormikContext, Form, Field } from 'formik'
|
||||
import { useFormikContext, Field } from 'formik'
|
||||
import Input from '../../atoms/Input'
|
||||
import Button from '../../atoms/Button'
|
||||
import { FormContent, FormFieldProps } from '../../../@types/Form'
|
||||
@ -37,7 +37,7 @@ export default function PublishForm({
|
||||
}
|
||||
|
||||
return (
|
||||
<Form
|
||||
<form
|
||||
className={styles.form}
|
||||
// do we need this?
|
||||
onChange={() => status === 'empty' && setStatus(null)}
|
||||
@ -61,6 +61,6 @@ export default function PublishForm({
|
||||
</Button>
|
||||
)}
|
||||
</footer>
|
||||
</Form>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
.action {
|
||||
margin-top: calc(var(--spacer) / 1.5);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user