1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

removed dynamic price on publish, liquidity button and trade tab ()

* removed dynamic price on publish, liquidity button and trade tab

* change label remove button

* removed dynamic pricing model references

* revert useEffects on pricing publish

* removed more references to dynamic pricing
This commit is contained in:
EnzoVezzaro 2022-07-12 12:52:45 -04:00 committed by GitHub
parent 16b391e656
commit 7788086bcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 27 additions and 507 deletions

View File

@ -16,16 +16,6 @@
"marketplaceFee": "Goes to the marketplace owner that is hosting and providing the marketplace and is collected when downloading or using an asset in a compute job. In Ocean Market, it is treated as network revenue that goes to the Ocean community."
}
},
"dynamic": {
"title": "Dynamic",
"info": "Let's create a decentralized, automated market for your data set. The datatoken for this data set will be worth the entered amount of OCEAN. Additionally, you will provide liquidity into a Datatoken/OCEAN liquidity pool with Balancer.",
"tooltips": {
"poolInfo": "The liquidity pool provides the funds for traders to trade against. The price of the asset is determined by the ratio of OCEAN to datatokens.",
"swapFee": "Liquidity providers earn this fee on all pool trades, proportionally to their share of the pool. The fee is set by the creator of the pool and is used to incentivize liquidity providers to join the pool.",
"communityFee": "Goes to Ocean DAO for teams to improve the tools, build apps, do outreach, and more. A small fraction is used to burn OCEAN. This fee is collected when downloading or using an asset in a compute job.",
"marketplaceFee": "Goes to the marketplace owner that is hosting and providing the marketplace and is collected when downloading or using an asset in a compute job. In Ocean Market, it is treated as network revenue that goes to the Ocean community."
}
},
"free": {
"title": "Free",
"info": "Set your data set as free. The datatoken for this data set will be given for free via creating a faucet.",
@ -47,15 +37,6 @@
"approveSpecific": "Give the smart contract permission to spend your COIN which has to be done for each transaction. You can optionally set this to infinite in your user preferences.",
"approveInfinite": "Give the smart contract permission to spend infinte amounts of your COIN so you have to do this only once. You can disable allowing infinite amounts in your user preferences."
},
"add": {
"title": "Add Liquidity",
"output": {
"help": "Providing liquidity will earn you SWAPFEE% on every transaction in this pool, proportionally to your share of the pool.",
"titleIn": "You will receive",
"titleOut": "Pool conversion"
},
"action": "Supply"
},
"remove": {
"title": "Remove Liquidity",
"simple": "Set the amount of your pool shares to spend. You will get the equivalent value in OCEAN, limited to maximum amount for pool protection.",

View File

@ -1,6 +0,0 @@
.buttonMax {
position: absolute;
font-size: var(--font-size-mini);
bottom: calc(var(--spacer) / 2.6);
right: calc(var(--spacer) * 2.5);
}

View File

@ -1,114 +0,0 @@
import React, { ReactElement, useEffect } from 'react'
import styles from './FormAdd.module.css'
import Input from '@shared/FormInput'
import Error from '@shared/FormInput/Error'
import { FormikContextType, useField, useFormikContext } from 'formik'
import Button from '@shared/atoms/Button'
import { FormAddLiquidity } from '.'
import UserLiquidity from '../../UserLiquidity'
import { useWeb3 } from '@context/Web3'
import { isValidNumber } from '@utils/numbers'
import Decimal from 'decimal.js'
import { useAsset } from '@context/Asset'
import { Pool } from '@oceanprotocol/lib'
import { usePool } from '@context/Pool'
export default function FormAdd({
amountMax,
setNewPoolTokens,
setNewPoolShare
}: {
amountMax: string
setNewPoolTokens: (value: string) => void
setNewPoolShare: (value: string) => void
}): ReactElement {
const { balance, web3 } = useWeb3()
const { isAssetNetwork } = useAsset()
const { poolData, poolInfo } = usePool()
// Connect with form
const {
setFieldValue,
values,
isSubmitting
}: FormikContextType<FormAddLiquidity> = useFormikContext()
const [field, meta] = useField('amount')
useEffect(() => {
async function calculatePoolShares() {
if (!web3 || !poolData?.id || !poolInfo?.totalPoolTokens) return
if (!values.amount || !poolInfo?.baseTokenAddress) {
setNewPoolTokens('0')
setNewPoolShare('0')
return
}
if (Number(values.amount) > Number(amountMax)) return
const poolInstance = new Pool(web3)
const poolTokens = await poolInstance.calcPoolOutGivenSingleIn(
poolData.id,
poolInfo.baseTokenAddress,
values.amount.toString(),
18,
poolInfo.baseTokenDecimals
)
setNewPoolTokens(poolTokens)
const newPoolShareDecimal =
isValidNumber(poolTokens) && isValidNumber(poolInfo.totalPoolTokens)
? new Decimal(poolTokens)
.dividedBy(
new Decimal(poolInfo.totalPoolTokens).plus(
new Decimal(poolTokens)
)
)
.mul(100)
.toString()
: '0'
setNewPoolShare(newPoolShareDecimal)
}
calculatePoolShares()
}, [
poolInfo?.baseTokenAddress,
poolInfo?.baseTokenDecimals,
web3,
values.amount,
poolInfo?.totalPoolTokens,
amountMax,
poolData?.id,
setNewPoolTokens,
setNewPoolShare
])
return (
<>
<UserLiquidity
amount={balance.ocean}
amountMax={amountMax}
symbol={poolInfo?.baseTokenSymbol}
/>
<Input
type="number"
min="0"
prefix={poolInfo?.baseTokenSymbol}
placeholder="0"
disabled={!isAssetNetwork || isSubmitting}
{...field}
additionalComponent={<Error meta={meta} />}
/>
{Number(balance.ocean) > 0 && (
<Button
className={styles.buttonMax}
style="text"
size="small"
disabled={!web3 || isSubmitting}
onClick={() => setFieldValue('amount', amountMax)}
>
Use Max
</Button>
)}
</>
)
}

View File

@ -1,13 +0,0 @@
.output {
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);
}
.help {
text-align: center;
}

View File

@ -1,30 +0,0 @@
import React, { ReactElement } from 'react'
import FormHelp from '@shared/FormInput/Help'
import Token from '../../../../@shared/Token'
import styles from './Output.module.css'
import content from '../../../../../../content/price.json'
import { usePool } from '@context/Pool'
export default function Output({
newPoolTokens,
newPoolShare
}: {
newPoolTokens: string
newPoolShare: string
}): ReactElement {
const { help, titleIn } = content.pool.add.output
const { poolInfo } = usePool()
return (
<>
<FormHelp className={styles.help}>
{help.replace('SWAPFEE', poolInfo?.liquidityProviderSwapFee)}
</FormHelp>
<div className={styles.output}>
<p>{titleIn}</p>
<Token symbol="pool shares" balance={newPoolTokens} noIcon />
<Token symbol="% of pool" balance={newPoolShare} noIcon />
</div>
</>
)
}

View File

@ -1,24 +0,0 @@
.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;
}

View File

@ -1,174 +0,0 @@
import React, { ReactElement, useState, useEffect } from 'react'
import Header from '../Actions/Header'
import { toast } from 'react-toastify'
import Actions from '../Actions'
import * as Yup from 'yup'
import { Formik } from 'formik'
import FormAdd from './FormAdd'
import styles from './index.module.css'
import Alert from '@shared/atoms/Alert'
import { useUserPreferences } from '@context/UserPreferences'
import Output from './Output'
import DebugOutput from '@shared/DebugOutput'
import { useWeb3 } from '@context/Web3'
import { useAsset } from '@context/Asset'
import content from '../../../../../../content/price.json'
import { calcMaxExactIn, LoggerInstance, Pool } from '@oceanprotocol/lib'
import { usePool } from '@context/Pool'
import { MAX_DECIMALS } from '@utils/constants'
import { getMaxDecimalsValidation } from '@utils/numbers'
import Decimal from 'decimal.js'
export interface FormAddLiquidity {
amount: number
}
const initialValues: FormAddLiquidity = {
amount: 0
}
export default function Add({
setShowAdd
}: {
setShowAdd: (show: boolean) => void
}): ReactElement {
const { accountId, balance, web3 } = useWeb3()
const { isAssetNetwork } = useAsset()
const { poolData, poolInfo, fetchAllData } = usePool()
const { debug } = useUserPreferences()
const [txId, setTxId] = useState<string>()
const [amountMax, setAmountMax] = useState<string>()
const [newPoolTokens, setNewPoolTokens] = useState('0')
const [newPoolShare, setNewPoolShare] = useState('0')
// Live validation rules
// https://github.com/jquense/yup#number
const validationSchema: Yup.SchemaOf<FormAddLiquidity> = Yup.object().shape({
amount: Yup.number()
.min(0.00001, (param) => `Must be more or equal to ${param.min}`)
.max(
Number(amountMax),
`Maximum you can add is ${Number(amountMax).toFixed(2)} OCEAN`
)
.test(
'maxDigitsAfterDecimal',
`Must have maximum ${MAX_DECIMALS} decimal digits`,
(param) =>
getMaxDecimalsValidation(MAX_DECIMALS).test(param?.toString())
)
.required('Required')
})
// Get maximum amount for OCEAN
useEffect(() => {
if (
!web3 ||
!accountId ||
!isAssetNetwork ||
!poolData?.id ||
!poolInfo?.baseTokenAddress
)
return
async function getMaximum() {
try {
const poolInstance = new Pool(web3)
const poolReserve = await poolInstance.getReserve(
poolData.id,
poolInfo.baseTokenAddress,
poolInfo.baseTokenDecimals
)
const amountMaxPool = calcMaxExactIn(poolReserve)
const oceanDecimal = new Decimal(balance.ocean)
const amountMax = oceanDecimal.greaterThan(amountMaxPool)
? amountMaxPool
: oceanDecimal
setAmountMax(amountMax.toFixed(3, Decimal.ROUND_DOWN))
} catch (error) {
LoggerInstance.error(error.message)
}
}
getMaximum()
}, [
web3,
accountId,
isAssetNetwork,
poolData?.id,
poolInfo?.baseTokenAddress,
poolInfo?.baseTokenDecimals,
balance?.ocean
])
// Submit
async function handleAddLiquidity(amount: string, resetForm: () => void) {
const poolInstance = new Pool(web3)
const minPoolAmountOut = '0' // ? how to get? : you would get this value by using `calcPoolOutGivenSingleIn` and substracting slippage from that , like we don in trade. it is ok to be 0 here. We can change after we implement global slippage
try {
const result = await poolInstance.joinswapExternAmountIn(
accountId,
poolData?.id,
amount,
minPoolAmountOut
)
setTxId(result?.transactionHash)
fetchAllData()
resetForm()
} catch (error) {
LoggerInstance.error(error.message)
toast.error(error.message)
}
}
return (
<>
<Header
title={content.pool.add.title}
backAction={() => {
setShowAdd(false)
fetchAllData()
}}
/>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={async (values, { setSubmitting, resetForm }) => {
await handleAddLiquidity(values.amount.toString(), resetForm)
setSubmitting(false)
}}
>
{({ isSubmitting, setSubmitting, submitForm, values, isValid }) => (
<>
<div className={styles.addInput}>
<FormAdd
amountMax={amountMax}
setNewPoolTokens={setNewPoolTokens}
setNewPoolShare={setNewPoolShare}
/>
</div>
{/* TODO: will be fixed in #1481 <Output newPoolTokens={newPoolTokens} newPoolShare={newPoolShare} /> */}
<Actions
isDisabled={!isValid || !values.amount || values.amount === 0}
isLoading={isSubmitting}
loaderMessage="Adding Liquidity..."
successMessage="Successfully added liquidity."
actionName={content.pool.add.action}
action={submitForm}
amount={values.amount.toString()}
tokenAddress={poolInfo?.baseTokenAddress}
tokenSymbol={poolInfo?.baseTokenSymbol}
txId={txId}
setSubmitting={setSubmitting}
/>
{debug && <DebugOutput output={values} />}
</>
)}
</Formik>
</>
)
}

View File

@ -1,5 +1,12 @@
.removeInput {
composes: addInput from '../Add/index.module.css';
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;
padding-left: calc(var(--spacer) * 2);
padding-right: calc(var(--spacer) * 2);
padding-bottom: calc(var(--spacer) / 2);

View File

@ -1,7 +1,6 @@
import React, { ReactElement, useState } from 'react'
import stylesActions from './Actions/index.module.css'
import Button from '@shared/atoms/Button'
import Add from './Add'
import Remove from './Remove'
import AssetActionHistoryTable from '../AssetActionHistoryTable'
import { useAsset } from '@context/Asset'
@ -12,43 +11,33 @@ import { usePool } from '@context/Pool'
import PoolSections from './Sections'
export default function Pool(): ReactElement {
const { isInPurgatory, asset, isAssetNetwork } = useAsset()
const { asset, isAssetNetwork } = useAsset()
const { hasUserAddedLiquidity } = usePool()
const { accountId } = useWeb3()
const [showAdd, setShowAdd] = useState(false)
const [showRemove, setShowRemove] = useState(false)
return (
<>
{showAdd ? (
<Add setShowAdd={setShowAdd} />
) : showRemove ? (
{showRemove ? (
<Remove setShowRemove={setShowRemove} />
) : (
<>
<PoolSections />
<div className={stylesActions.actions}>
<Button
style="primary"
size="small"
onClick={() => setShowAdd(true)}
disabled={isInPurgatory || !isAssetNetwork}
>
Add Liquidity
</Button>
{hasUserAddedLiquidity && (
{hasUserAddedLiquidity && (
<div className={stylesActions.actions}>
<Button
style="primary"
size="small"
onClick={() => setShowRemove(true)}
disabled={!isAssetNetwork}
>
Remove
Remove Liquidity
</Button>
)}
</div>
</div>
)}
{accountId && (
<AssetActionHistoryTable title="Your Pool Transactions">
<PoolTransactions

View File

@ -137,10 +137,7 @@ export default function AssetActions({
const tabs: TabsItem[] = [{ title: 'Use', content: UseContent }]
asset?.accessDetails?.type === 'dynamic' &&
tabs.push(
{ title: 'Pool', content: <Pool /> },
{ title: 'Trade', content: <Trade /> }
)
tabs.push({ title: 'Pool', content: <Pool /> })
return (
<>

View File

@ -18,7 +18,7 @@ export default function PricingFields(): ReactElement {
// Connect with main publish form
const { values, setFieldValue } = useFormikContext<FormPublishData>()
const { pricing } = values
const { price, amountOcean, weightOnOcean, weightOnDataToken, type } = pricing
const { price, type } = pricing
// Switch type value upon tab change
function handleTabChange(tabName: string) {
@ -29,36 +29,11 @@ export default function PricingFields(): ReactElement {
type !== 'free' && setFieldValue('pricing.amountDataToken', 1000)
}
// Update ocean amount when price is changed
// Update price when price is changed
useEffect(() => {
if (type === 'fixed' || type === 'free') return
const amountOcean =
isValidNumber(weightOnOcean) && isValidNumber(price) && price > 0
? new Decimal(price).mul(new Decimal(weightOnOcean).mul(10)).mul(2)
: new Decimal(initialValues.pricing.amountOcean)
setFieldValue('pricing.amountOcean', amountOcean)
}, [price, weightOnOcean, type, setFieldValue])
// Update dataToken value when ocean amount is changed
useEffect(() => {
if (type === 'fixed' || type === 'free') return
const amountDataToken =
isValidNumber(amountOcean) &&
isValidNumber(weightOnOcean) &&
isValidNumber(price) &&
isValidNumber(weightOnDataToken) &&
price > 0
? new Decimal(amountOcean)
.dividedBy(new Decimal(weightOnOcean))
.dividedBy(new Decimal(price))
.mul(new Decimal(weightOnDataToken))
: new Decimal(initialValues.pricing.amountDataToken)
setFieldValue('pricing.amountDataToken', amountDataToken)
}, [amountOcean, weightOnOcean, weightOnDataToken, type, setFieldValue])
setFieldValue('pricing.price', price)
setFieldValue('pricing.type', type)
}, [price, setFieldValue, type])
const tabs = [
appConfig.allowFixedPricing === 'true'
@ -67,12 +42,6 @@ export default function PricingFields(): ReactElement {
content: <Fixed content={content.create.fixed} />
}
: undefined,
appConfig.allowDynamicPricing === 'true'
? {
title: content.create.dynamic.title,
content: <Dynamic content={content.create.dynamic} />
}
: undefined,
appConfig.allowFreePricing === 'true'
? {
title: content.create.free.title,
@ -85,7 +54,7 @@ export default function PricingFields(): ReactElement {
<Tabs
items={tabs}
handleTabChange={handleTabChange}
defaultIndex={type === 'dynamic' ? 1 : type === 'free' ? 2 : 0}
defaultIndex={type === 'free' ? 1 : 0}
className={styles.pricing}
showRadio
/>

View File

@ -82,13 +82,8 @@ export const initialValues: FormPublishData = {
],
pricing: {
price: 0,
type:
allowDynamicPricing === 'true'
? 'dynamic'
: allowFixedPricing === 'true'
? 'fixed'
: 'free',
amountDataToken: allowDynamicPricing === 'true' ? 100 : 1000,
type: allowFixedPricing === 'true' ? 'fixed' : 'free',
amountDataToken: 1000,
amountOcean: 100,
weightOnOcean: '5', // 50% on OCEAN
weightOnDataToken: '5', // 50% on datatoken

View File

@ -210,7 +210,7 @@ export async function createTokensAndPricing(
// TODO: cap is hardcoded for now to 1000, this needs to be discussed at some point
const ercParams: Erc20CreateParams = {
templateIndex: values.pricing.type === 'dynamic' ? 1 : 2,
templateIndex: 2,
minter: accountId,
paymentCollector: accountId,
mpFeeAddress: marketFeeAddress,
@ -228,63 +228,6 @@ export async function createTokensAndPricing(
// TODO: cleaner code for this huge switch !??!?
switch (values.pricing.type) {
case 'dynamic': {
// no vesting in market by default, maybe at a later time , vestingAmount and vestedBlocks are hardcoded
// we use only ocean as basetoken
// swapFeeLiquidityProvider is the swap fee of the liquidity providers
// swapFeeMarketRunner is the swap fee of the market where the swap occurs
const poolParams: PoolCreationParams = {
ssContract: config.sideStakingAddress,
baseTokenAddress: config.oceanTokenAddress,
baseTokenSender: config.erc721FactoryAddress,
publisherAddress: accountId,
marketFeeCollector: marketFeeAddress,
poolTemplateAddress: config.poolTemplateAddress,
rate: new Decimal(1).div(values.pricing.price).toString(),
baseTokenDecimals: 18,
vestingAmount: '0',
vestedBlocks: 2726000,
initialBaseTokenLiquidity: values.pricing.amountOcean.toString(),
swapFeeLiquidityProvider: (values.pricing.swapFee / 100).toString(),
swapFeeMarketRunner: publisherMarketPoolSwapFee
}
LoggerInstance.log(
'[publish] Creating dynamic pricing with poolParams',
poolParams
)
// the spender in this case is the erc721Factory because we are delegating
const txApprove = await approve(
web3,
accountId,
config.oceanTokenAddress,
config.erc721FactoryAddress,
values.pricing.amountOcean.toString(),
false
)
LoggerInstance.log('[publish] pool.approve tx', txApprove, nftFactory)
if (!txApprove) {
throw new Error(
'MetaMask Approve TX Signature: User denied transaction signature'
)
}
const result = await nftFactory.createNftErc20WithPool(
accountId,
nftCreateData,
ercParams,
poolParams
)
erc721Address = result.events.NFTCreated.returnValues[0]
datatokenAddress = result.events.TokenCreated.returnValues[0]
txHash = result.transactionHash
LoggerInstance.log('[publish] createNftErcWithPool tx', txHash)
break
}
case 'fixed': {
const freParams: FreCreationParams = {
fixedRateAddress: config.fixedRateExchangeAddress,