diff --git a/README.md b/README.md index 163863524..863fce30e 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ const queryLatest = { } function Component() { - const { appConfig } = useSiteMetadata() + const { appConfig } = useMarketMetadata() const [result, setResult] = useState() useEffect(() => { diff --git a/app.config.js b/app.config.js index d082373d1..96d263a7b 100644 --- a/app.config.js +++ b/app.config.js @@ -2,8 +2,8 @@ module.exports = { // URI of single metadata cache instance for all networks. // While ocean.js includes this value for each network as part of its ConfigHelper, // it is assumed to be the same for all networks. - // In components can be accessed with the useSiteMetadata hook: - // const { appConfig } = useSiteMetadata() + // In components can be accessed with the useMarketMetadata hook: + // const { appConfig } = useMarketMetadata() // return appConfig.metadataCacheUri metadataCacheUri: process.env.NEXT_PUBLIC_METADATACACHE_URI || diff --git a/content/price.json b/content/price.json index b110f4f7a..cfb269533 100644 --- a/content/price.json +++ b/content/price.json @@ -61,8 +61,8 @@ "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.", "output": { - "titleIn": "You will spend", - "titleOut": "You will receive" + "titleOutExpected": "Expected output", + "titleOutMinimum": "Minimum received" }, "action": "Remove" } diff --git a/src/@context/Asset.tsx b/src/@context/Asset.tsx index 9e0f87d4e..c18fb0a60 100644 --- a/src/@context/Asset.tsx +++ b/src/@context/Asset.tsx @@ -11,12 +11,12 @@ import { Config, LoggerInstance, Purgatory } from '@oceanprotocol/lib' import { CancelToken } from 'axios' import { retrieveAsset } from '@utils/aquarius' import { useWeb3 } from './Web3' -import { useSiteMetadata } from '@hooks/useSiteMetadata' import { useCancelToken } from '@hooks/useCancelToken' import { getOceanConfig, getDevelopmentConfig } from '@utils/ocean' import { AssetExtended } from 'src/@types/AssetExtended' import { getAccessDetails } from '@utils/accessDetailsAndPricing' import { useIsMounted } from '@hooks/useIsMounted' +import { useMarketMetadata } from './MarketMetadata' interface AssetProviderValue { isInPurgatory: boolean @@ -40,7 +40,7 @@ function AssetProvider({ did: string children: ReactNode }): ReactElement { - const { appConfig } = useSiteMetadata() + const { appConfig } = useMarketMetadata() const { chainId, accountId } = useWeb3() const [isInPurgatory, setIsInPurgatory] = useState(false) diff --git a/src/@context/CookieConsent.tsx b/src/@context/CookieConsent.tsx index 6e1826e41..a8195f24d 100644 --- a/src/@context/CookieConsent.tsx +++ b/src/@context/CookieConsent.tsx @@ -8,7 +8,7 @@ import React, { } from 'react' import { deleteCookie, getCookieValue, setCookie } from '@utils/cookies' import { UseGdprMetadata, useGdprMetadata } from '@hooks/useGdprMetadata' -import { useSiteMetadata } from '@hooks/useSiteMetadata' +import { useMarketMetadata } from './MarketMetadata' export enum CookieConsentStatus { NOT_AVAILABLE = -1, @@ -32,7 +32,7 @@ const ConsentContext = createContext({} as ConsentProviderValue) function ConsentProvider({ children }: { children: ReactNode }): ReactElement { const cookies = useGdprMetadata() - const { privacyPreferenceCenter } = useSiteMetadata().appConfig + const { appConfig } = useMarketMetadata() const [consentStatus, setConsentStatus] = useState({} as ConsentStatus) @@ -80,7 +80,7 @@ function ConsentProvider({ children }: { children: ReactNode }): ReactElement { } useEffect(() => { - if (privacyPreferenceCenter !== 'true') return + if (appConfig?.privacyPreferenceCenter !== 'true') return const initialValues = {} as ConsentStatus cookies.optionalCookies?.map((cookie) => { @@ -100,8 +100,7 @@ function ConsentProvider({ children }: { children: ReactNode }): ReactElement { }) setConsentStatus(initialValues) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + }, [cookies.optionalCookies, appConfig]) useEffect(() => { Object.keys(consentStatus).map((cookieName) => { diff --git a/src/@context/MarketMetadata/_queries.ts b/src/@context/MarketMetadata/_queries.ts new file mode 100644 index 000000000..84586f82f --- /dev/null +++ b/src/@context/MarketMetadata/_queries.ts @@ -0,0 +1,12 @@ +import { gql } from 'urql' + +export const opcQuery = gql` + query OpcQuery { + opc(id: 1) { + swapOceanFee + swapNonOceanFee + approvedTokens + id + } + } +` diff --git a/src/@context/MarketMetadata/_types.ts b/src/@context/MarketMetadata/_types.ts new file mode 100644 index 000000000..b84adadc5 --- /dev/null +++ b/src/@context/MarketMetadata/_types.ts @@ -0,0 +1,58 @@ +export interface OpcFee { + chainId: number + swapNotApprovedFee: string + swapApprovedFee: string + approvedTokens: string[] +} + +export interface AppConfig { + metadataCacheUri: string + infuraProjectId: string + chainIds: number[] + chainIdsSupported: number[] + marketFeeAddress: string + publisherMarketOrderFee: string + publisherMarketPoolSwapFee: string + publisherMarketFixedSwapFee: string + consumeMarketOrderFee: string + consumeMarketPoolSwapFee: string + consumeMarketFixedSwapFee: string + currencies: string[] + portisId: string + allowFixedPricing: string + allowDynamicPricing: string + allowFreePricing: string + defaultPrivacyPolicySlug: string + privacyPreferenceCenter: string + darkModeConfig: { + classNameDark: string + classNameLight: string + storageKey: string + } +} +export interface SiteContent { + siteTitle: string + siteTagline: string + siteUrl: string + siteImage: string + copyright: string + menu: { + name: string + link: string + }[] + warning: { + main: string + polygonPublish: string + } + announcement: { + main: string + polygon: string + } +} + +export interface MarketMetadataProviderValue { + opcFees: OpcFee[] + siteContent: SiteContent + appConfig: AppConfig + getOpcFeeForToken: (tokenAddress: string, chainId: number) => string +} diff --git a/src/@context/MarketMetadata/index.tsx b/src/@context/MarketMetadata/index.tsx new file mode 100644 index 000000000..4af8136d2 --- /dev/null +++ b/src/@context/MarketMetadata/index.tsx @@ -0,0 +1,80 @@ +import React, { + createContext, + ReactElement, + ReactNode, + useCallback, + useContext, + useEffect, + useState +} from 'react' +import { OpcQuery } from 'src/@types/subgraph/OpcQuery' +import { OperationResult } from 'urql' +import { opcQuery } from './_queries' +import { MarketMetadataProviderValue, OpcFee } from './_types' +import siteContent from '../../../content/site.json' +import appConfig from '../../../app.config' +import { fetchData, getQueryContext } from '@utils/subgraph' + +const MarketMetadataContext = createContext({} as MarketMetadataProviderValue) + +function MarketMetadataProvider({ + children +}: { + children: ReactNode +}): ReactElement { + const [opcFees, setOpcFees] = useState() + + useEffect(() => { + async function getOpcData() { + const opcData = [] + for (let i = 0; i < appConfig.chainIdsSupported.length; i++) { + const response: OperationResult = await fetchData( + opcQuery, + null, + getQueryContext(appConfig.chainIdsSupported[i]) + ) + + opcData.push({ + chainId: appConfig.chainIdsSupported[i], + approvedTokens: response.data?.opc.approvedTokens, + swapApprovedFee: response.data?.opc.swapOceanFee, + swapNotApprovedFee: response.data.opc.swapNonOceanFee + } as OpcFee) + } + setOpcFees(opcData) + } + getOpcData() + }, []) + + const getOpcFeeForToken = useCallback( + (tokenAddress: string, chainId: number): string => { + if (!opcFees) return + + const opc = opcFees.filter((x) => x.chainId === chainId)[0] + const isTokenApproved = opc.approvedTokens.includes(tokenAddress) + return isTokenApproved ? opc.swapApprovedFee : opc.swapNotApprovedFee + }, + [opcFees] + ) + return ( + + {children} + + ) +} + +// Helper hook to access the provider values +const useMarketMetadata = (): MarketMetadataProviderValue => + useContext(MarketMetadataContext) + +export { MarketMetadataProvider, useMarketMetadata, MarketMetadataContext } +export default MarketMetadataProvider diff --git a/src/@context/Pool/_types.ts b/src/@context/Pool/_types.ts index f0d2c1fd7..3c5e4a8d4 100644 --- a/src/@context/Pool/_types.ts +++ b/src/@context/Pool/_types.ts @@ -1,4 +1,3 @@ -import Decimal from 'decimal.js' import { PoolData_poolSnapshots as PoolDataPoolSnapshots, PoolData_poolData as PoolDataPoolData @@ -15,13 +14,12 @@ export interface PoolInfo { baseTokenSymbol: string baseTokenAddress: string totalPoolTokens: string - totalLiquidityInOcean: Decimal } export interface PoolInfoUser { - liquidity: Decimal // liquidity in base token - poolShares: string // pool share tokens - poolShare: string // in % + liquidity: string + poolShares: string + poolSharePercentage: string } export interface PoolProviderValue { @@ -31,7 +29,6 @@ export interface PoolProviderValue { poolInfoUser: PoolInfoUser poolSnapshots: PoolDataPoolSnapshots[] hasUserAddedLiquidity: boolean - isRemoveDisabled: boolean refreshInterval: number fetchAllData: () => void } diff --git a/src/@context/Pool/index.tsx b/src/@context/Pool/index.tsx index e0fa7941a..71ad5ef1f 100644 --- a/src/@context/Pool/index.tsx +++ b/src/@context/Pool/index.tsx @@ -1,5 +1,4 @@ -import { LoggerInstance, Pool } from '@oceanprotocol/lib' -import { isValidNumber } from '@utils/numbers' +import { LoggerInstance } from '@oceanprotocol/lib' import Decimal from 'decimal.js' import React, { useContext, @@ -16,22 +15,17 @@ import { } from 'src/@types/subgraph/PoolData' import { useAsset } from '../Asset' import { useWeb3 } from '../Web3' -import { calculateSharesVL } from '@utils/pool' +import { calcSingleOutGivenPoolIn } from '@utils/pool' import { PoolProviderValue, PoolInfo, PoolInfoUser } from './_types' import { getFee, getPoolData, getWeight } from './_utils' - -Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 }) +import { useMarketMetadata } from '@context/MarketMetadata' const PoolContext = createContext({} as PoolProviderValue) const refreshInterval = 10000 // 10 sec. -const initialPoolInfo: Partial = { - totalLiquidityInOcean: new Decimal(0) -} - const initialPoolInfoUser: Partial = { - liquidity: new Decimal(0), + liquidity: '0', poolShares: '0' } @@ -39,12 +33,10 @@ const initialPoolInfoCreator: Partial = initialPoolInfoUser function PoolProvider({ children }: { children: ReactNode }): ReactElement { const { accountId, web3, chainId } = useWeb3() - const { isInPurgatory, asset, owner } = useAsset() - + const { asset, owner } = useAsset() + const { getOpcFeeForToken } = useMarketMetadata() const [poolData, setPoolData] = useState() - const [poolInfo, setPoolInfo] = useState( - initialPoolInfo as PoolInfo - ) + const [poolInfo, setPoolInfo] = useState() const [poolInfoOwner, setPoolInfoOwner] = useState( initialPoolInfoCreator as PoolInfoUser ) @@ -53,10 +45,7 @@ function PoolProvider({ children }: { children: ReactNode }): ReactElement { ) const [poolSnapshots, setPoolSnapshots] = useState() const [hasUserAddedLiquidity, setUserHasAddedLiquidity] = useState(false) - const [isRemoveDisabled, setIsRemoveDisabled] = useState(false) // const [fetchInterval, setFetchInterval] = useState() - const [ownerPoolShares, setOwnerPoolShares] = useState('0') - const [userPoolShares, setUserPoolShares] = useState('0') const fetchAllData = useCallback(async () => { if (!asset?.chainId || !asset?.accessDetails?.addressOrId || !owner) return @@ -87,8 +76,11 @@ function PoolProvider({ children }: { children: ReactNode }): ReactElement { // useEffect(() => { if (asset?.accessDetails?.type !== 'dynamic') return - fetchAllData() + const interval = setInterval(() => { + fetchAllData() + }, refreshInterval) + return () => clearInterval(interval) }, [fetchAllData, asset?.accessDetails?.type]) // @@ -97,37 +89,24 @@ function PoolProvider({ children }: { children: ReactNode }): ReactElement { useEffect(() => { if (!poolData) return - // once we have poolData, we need to get owner's pool shares (OVL) - calculateSharesVL( - poolData.id, - poolData.baseToken.address, - poolData.shares[0].shares, - asset.chainId - ).then((shares) => { - setOwnerPoolShares(shares) - }) - // Total Liquidity - const totalLiquidityInOcean = new Decimal( - poolData.baseTokenLiquidity * 2 || 0 - ) - const newPoolInfo = { liquidityProviderSwapFee: getFee(poolData.liquidityProviderSwapFee), publishMarketSwapFee: getFee(poolData.publishMarketSwapFee), - opcFee: getFee(poolData.opcFee), + opcFee: getFee( + getOpcFeeForToken(poolData.baseToken.address, asset?.chainId) + ), weightBaseToken: getWeight(poolData.baseTokenWeight), weightDt: getWeight(poolData.datatokenWeight), datatokenSymbol: poolData.datatoken.symbol, datatokenAddress: poolData.datatoken.address, baseTokenSymbol: poolData.baseToken.symbol, baseTokenAddress: poolData.baseToken.address, - totalPoolTokens: poolData.totalShares, - totalLiquidityInOcean + totalPoolTokens: poolData.totalShares } setPoolInfo(newPoolInfo) LoggerInstance.log('[pool] Created new pool info:', newPoolInfo) - }, [asset?.chainId, chainId, poolData, web3]) + }, [asset?.chainId, chainId, getOpcFeeForToken, poolData, web3]) // // 2 Pool Creator Info @@ -136,29 +115,34 @@ function PoolProvider({ children }: { children: ReactNode }): ReactElement { if ( !poolData || !poolInfo?.totalPoolTokens || - !poolInfo.totalLiquidityInOcean || - ownerPoolShares === '0' + poolData.shares[0]?.shares === '0' ) return // Pool share tokens. - const poolShare = new Decimal(ownerPoolShares) - .dividedBy(poolInfo.totalLiquidityInOcean) + const poolSharePercentage = new Decimal(poolData.shares[0]?.shares) + .dividedBy(poolInfo.totalPoolTokens) .mul(100) .toFixed(2) + const ownerLiquidity = calcSingleOutGivenPoolIn( + poolData.baseTokenLiquidity, + poolData.totalShares, + poolData?.shares[0]?.shares + ) + const newPoolOwnerInfo = { - liquidity: new Decimal(ownerPoolShares), // liquidity in base token, values from from `calcSingleOutGivenPoolIn` method - poolShares: ownerPoolShares, - poolShare + liquidity: ownerLiquidity, + poolShares: poolData.shares[0]?.shares, + poolSharePercentage } setPoolInfoOwner(newPoolOwnerInfo) - LoggerInstance.log('[pool] Created new owner pool info:', newPoolOwnerInfo) + LoggerInstance.log('[pool] Created new pool creatorinfo:', newPoolOwnerInfo) }, [ - ownerPoolShares, + asset?.chainId, poolData, - poolInfo.totalLiquidityInOcean, - poolInfo.totalPoolTokens + poolInfo?.baseTokenAddress, + poolInfo?.totalPoolTokens ]) // @@ -169,35 +153,29 @@ function PoolProvider({ children }: { children: ReactNode }): ReactElement { !poolData || !poolInfo?.totalPoolTokens || !poolInfoUser?.poolShares || - !poolInfo?.totalLiquidityInOcean || !poolData?.baseTokenLiquidity || - !asset?.chainId || - !accountId || - !poolInfoUser + !asset?.chainId ) return - // once we have poolData, we need to get user's pool shares (VL) - calculateSharesVL( - poolData.id, - poolData.baseToken.address, - poolInfoUser.poolShares, - asset.chainId - ).then((shares) => { - setUserPoolShares(shares) - }) + const userLiquidity = calcSingleOutGivenPoolIn( + poolData.baseTokenLiquidity, + poolData.totalShares, + poolInfoUser.poolShares + ) // Pool share in %. - const poolShare = new Decimal(userPoolShares) - .dividedBy(new Decimal(poolInfo.totalLiquidityInOcean)) + const poolSharePercentage = new Decimal(poolInfoUser.poolShares) + .dividedBy(new Decimal(poolInfo.totalPoolTokens)) .mul(100) .toFixed(2) - setUserHasAddedLiquidity(Number(poolShare) > 0) + setUserHasAddedLiquidity(Number(poolSharePercentage) > 0) - const newPoolInfoUser = { - liquidity: new Decimal(userPoolShares), // liquidity in base token, values from from `calcSingleOutGivenPoolIn` method - poolShare + const newPoolInfoUser: PoolInfoUser = { + liquidity: userLiquidity, + poolShares: poolInfoUser.poolShares, + poolSharePercentage } setPoolInfoUser((prevState: PoolInfoUser) => ({ ...prevState, @@ -205,29 +183,16 @@ function PoolProvider({ children }: { children: ReactNode }): ReactElement { })) LoggerInstance.log('[pool] Created new user pool info:', { - poolShares: userPoolShares, ...newPoolInfoUser }) - // poolInfoUser was not added on purpose, we use setPoolInfoUser so it will just loop - // eslint-disable-next-line react-hooks/exhaustive-deps }, [ poolData, poolInfoUser?.poolShares, - accountId, - userPoolShares, asset?.chainId, owner, poolInfo?.totalPoolTokens ]) - // - // Check if removing liquidity should be disabled. - // - useEffect(() => { - if (!owner || !accountId) return - setIsRemoveDisabled(isInPurgatory && owner === accountId) - }, [isInPurgatory, owner, accountId]) - return ( { + if (!appConfig) return + // comma-separated list + const currencies = appConfig.currencies.join(',') + const url = `https://api.coingecko.com/api/v3/simple/price?ids=${tokenId}&vs_currencies=${currencies}` + setUrl(url) + }, [appConfig]) const onSuccess = async (data: { [tokenId]: Prices }) => { if (!data) return diff --git a/src/@context/Profile.tsx b/src/@context/Profile.tsx index c3f5aa537..900e16180 100644 --- a/src/@context/Profile.tsx +++ b/src/@context/Profile.tsx @@ -16,11 +16,11 @@ import { useUserPreferences } from './UserPreferences' import { PoolShares_poolShares as PoolShare } from '../@types/subgraph/PoolShares' import { Asset, LoggerInstance } from '@oceanprotocol/lib' import { getDownloadAssets, getPublishedAssets } from '@utils/aquarius' -import { useSiteMetadata } from '@hooks/useSiteMetadata' import { accountTruncate } from '@utils/web3' import axios, { CancelToken } from 'axios' import get3BoxProfile from '@utils/profile' import web3 from 'web3' +import { useMarketMetadata } from './MarketMetadata' interface ProfileProviderValue { profile: Profile @@ -49,7 +49,7 @@ function ProfileProvider({ children: ReactNode }): ReactElement { const { chainIds } = useUserPreferences() - const { appConfig } = useSiteMetadata() + const { appConfig } = useMarketMetadata() const [isEthAddress, setIsEthAddress] = useState() diff --git a/src/@context/UserPreferences.tsx b/src/@context/UserPreferences.tsx index cc8b67dbd..0d963aa21 100644 --- a/src/@context/UserPreferences.tsx +++ b/src/@context/UserPreferences.tsx @@ -8,7 +8,7 @@ import React, { } from 'react' import { LoggerInstance, LogLevel } from '@oceanprotocol/lib' import { isBrowser } from '@utils/index' -import { useSiteMetadata } from '@hooks/useSiteMetadata' +import { useMarketMetadata } from './MarketMetadata' interface UserPreferencesValue { debug: boolean @@ -51,9 +51,8 @@ function UserPreferencesProvider({ }: { children: ReactNode }): ReactElement { - const { appConfig } = useSiteMetadata() + const { appConfig } = useMarketMetadata() const localStorage = getLocalStorage() - // Set default values from localStorage const [debug, setDebug] = useState(localStorage?.debug || false) const [currency, setCurrency] = useState( @@ -64,7 +63,7 @@ function UserPreferencesProvider({ const [chainIds, setChainIds] = useState( localStorage?.chainIds || appConfig.chainIds ) - const { defaultPrivacyPolicySlug } = useSiteMetadata().appConfig + const { defaultPrivacyPolicySlug } = appConfig const [privacyPolicySlug, setPrivacyPolicySlug] = useState( localStorage?.privacyPolicySlug || defaultPrivacyPolicySlug diff --git a/src/@hooks/useSiteMetadata/index.ts b/src/@hooks/useSiteMetadata/index.ts deleted file mode 100644 index 5692cf987..000000000 --- a/src/@hooks/useSiteMetadata/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UseSiteMetadata } from './types' -import { getSiteMetadata } from '@utils/siteConfig' - -export function useSiteMetadata(): UseSiteMetadata { - return getSiteMetadata() -} diff --git a/src/@hooks/useSiteMetadata/types.ts b/src/@hooks/useSiteMetadata/types.ts deleted file mode 100644 index 1159b4eb3..000000000 --- a/src/@hooks/useSiteMetadata/types.ts +++ /dev/null @@ -1,44 +0,0 @@ -export interface UseSiteMetadata { - siteTitle: string - siteTagline: string - siteUrl: string - siteImage: string - copyright: string - menu: { - name: string - link: string - }[] - warning: { - main: string - polygonPublish: string - } - announcement: { - main: string - polygon: string - } - appConfig: { - metadataCacheUri: string - infuraProjectId: string - chainIds: number[] - chainIdsSupported: number[] - marketFeeAddress: string - publisherMarketOrderFee: string - publisherMarketPoolSwapFee: string - publisherMarketFixedSwapFee: string - consumeMarketOrderFee: string - consumeMarketPoolSwapFee: string - consumeMarketFixedSwapFee: string - currencies: string[] - portisId: string - allowFixedPricing: string - allowDynamicPricing: string - allowFreePricing: string - defaultPrivacyPolicySlug: string - privacyPreferenceCenter: string - darkModeConfig: { - classNameDark: string - classNameLight: string - storageKey: string - } - } -} diff --git a/src/@types/Utils.d.ts b/src/@types/Utils.d.ts new file mode 100644 index 000000000..a5528a724 --- /dev/null +++ b/src/@types/Utils.d.ts @@ -0,0 +1,9 @@ +interface CalcInGivenOutParams { + tokenInLiquidity: string + tokenOutLiquidity: string + tokenOutAmount: string + opcFee: string + lpSwapFee: string + publishMarketSwapFee: string + consumeMarketSwapFee: string +} diff --git a/src/@utils/accessDetailsAndPricing.ts b/src/@utils/accessDetailsAndPricing.ts index 4469f41b0..8b15b8cfd 100644 --- a/src/@utils/accessDetailsAndPricing.ts +++ b/src/@utils/accessDetailsAndPricing.ts @@ -10,11 +10,11 @@ import { } from '../@types/subgraph/TokensPriceQuery' import { Asset, LoggerInstance, ProviderInstance } from '@oceanprotocol/lib' import { AssetExtended } from 'src/@types/AssetExtended' -import { calculateBuyPrice } from './pool' +import { calcInGivenOut } from './pool' import { getFixedBuyPrice } from './fixedRateExchange' -import { getSiteMetadata } from './siteConfig' import { AccessDetails, OrderPriceAndFees } from 'src/@types/Price' import Decimal from 'decimal.js' +import { consumeMarketOrderFee } from '../../app.config' const TokensPriceQuery = gql` query TokensPriceQuery($datatokenIds: [ID!], $account: String) { @@ -229,17 +229,16 @@ function getAccessDetailsFromTokenPrice( */ export async function getOrderPriceAndFees( asset: AssetExtended, - accountId: string + accountId: string, + paramsForPool: CalcInGivenOutParams ): Promise { - const { appConfig } = getSiteMetadata() - const orderPriceAndFee = { price: '0', publisherMarketOrderFee: asset?.accessDetails?.publisherMarketOrderFee || '0', publisherMarketPoolSwapFee: '0', publisherMarketFixedSwapFee: '0', - consumeMarketOrderFee: appConfig.consumeMarketOrderFee || '0', + consumeMarketOrderFee: consumeMarketOrderFee || '0', consumeMarketPoolSwapFee: '0', consumeMarketFixedSwapFee: '0', providerFee: { @@ -261,10 +260,7 @@ export async function getOrderPriceAndFees( // fetch price and swap fees switch (asset?.accessDetails?.type) { case 'dynamic': { - const poolPrice = await calculateBuyPrice( - asset?.accessDetails, - asset?.chainId - ) + const poolPrice = calcInGivenOut(paramsForPool) orderPriceAndFee.price = poolPrice.tokenAmount orderPriceAndFee.liquidityProviderSwapFee = poolPrice.liquidityProviderSwapFeeAmount @@ -279,7 +275,6 @@ export async function getOrderPriceAndFees( orderPriceAndFee.price = fixed.baseTokenAmount orderPriceAndFee.opcFee = fixed.oceanFeeAmount orderPriceAndFee.publisherMarketFixedSwapFee = fixed.marketFeeAmount - // hack because we don't have it in contracts orderPriceAndFee.consumeMarketFixedSwapFee = fixed.consumeMarketFeeAmount break diff --git a/src/@utils/numbers.ts b/src/@utils/numbers.ts index 55a3bc027..0d31581bf 100644 --- a/src/@utils/numbers.ts +++ b/src/@utils/numbers.ts @@ -30,3 +30,9 @@ export function randomIntFromInterval(min: number, max: number): number { // min and max are included return Math.floor(Math.random() * (max - min + 1) + min) } + +export function getMaxDecimalsValidation(max: number): RegExp { + // eslint-disable-next-line security/detect-non-literal-regexp + const maxDecimalsValidation = new RegExp('^\\d+(\\.\\d{1,' + max + '})?$') + return maxDecimalsValidation +} diff --git a/src/@utils/order.ts b/src/@utils/order.ts index 472f97da5..96d1acdce 100644 --- a/src/@utils/order.ts +++ b/src/@utils/order.ts @@ -9,8 +9,12 @@ import { AssetExtended } from 'src/@types/AssetExtended' import Web3 from 'web3' import { getOceanConfig } from './ocean' import { TransactionReceipt } from 'web3-eth' -import { getSiteMetadata } from './siteConfig' import { OrderPriceAndFees } from 'src/@types/Price' +import { + marketFeeAddress, + consumeMarketOrderFee, + consumeMarketFixedSwapFee +} from '../../app.config' /** * For pool you need to buy the datatoken beforehand, this always assumes you want to order the first service @@ -27,7 +31,6 @@ export async function order( ): Promise { const datatoken = new Datatoken(web3) const config = getOceanConfig(asset.chainId) - const { appConfig } = getSiteMetadata() const initializeData = await ProviderInstance.initialize( asset.id, @@ -42,8 +45,8 @@ export async function order( serviceIndex: 0, _providerFee: initializeData.providerFee, _consumeMarketFee: { - consumeMarketFeeAddress: appConfig.marketFeeAddress, - consumeMarketFeeAmount: appConfig.consumeMarketOrderFee, + consumeMarketFeeAddress: marketFeeAddress, + consumeMarketFeeAmount: consumeMarketOrderFee, consumeMarketFeeToken: config.oceanTokenAddress } } as OrderParams @@ -68,8 +71,8 @@ export async function order( exchangeContract: config.fixedRateExchangeAddress, exchangeId: asset.accessDetails.addressOrId, maxBaseTokenAmount: orderPriceAndFees.price, - swapMarketFee: appConfig.consumeMarketFixedSwapFee, - marketFeeAddress: appConfig.marketFeeAddress + swapMarketFee: consumeMarketFixedSwapFee, + marketFeeAddress } as FreOrderParams const tx = await datatoken.buyFromFreAndOrder( asset.accessDetails.datatoken.address, diff --git a/src/@utils/pool.ts b/src/@utils/pool.ts index 68e918634..803914714 100644 --- a/src/@utils/pool.ts +++ b/src/@utils/pool.ts @@ -1,12 +1,11 @@ import { approve, Pool, PoolPriceAndFees } from '@oceanprotocol/lib' import Web3 from 'web3' -import { getSiteMetadata } from './siteConfig' import { getDummyWeb3 } from './web3' import { TransactionReceipt } from 'web3-eth' import Decimal from 'decimal.js' import { AccessDetails } from 'src/@types/Price' -import { isValidNumber } from './numbers' -import { MAX_DECIMALS } from './constants' +import { consumeMarketPoolSwapFee, marketFeeAddress } from '../../app.config' + /** * This is used to calculate the price to buy one datatoken from a pool, that is different from spot price. You need to pass either a web3 object or a chainId. If you pass a chainId a dummy web3 object will be created * @param {AccessDetails} accessDetails @@ -27,13 +26,13 @@ export async function calculateBuyPrice( } const pool = new Pool(web3) - const { appConfig } = getSiteMetadata() + const estimatedPrice = await pool.getAmountInExactOut( accessDetails.addressOrId, accessDetails.baseToken.address, accessDetails.datatoken.address, '1', - appConfig.consumeMarketPoolSwapFee + consumeMarketPoolSwapFee ) return estimatedPrice @@ -45,7 +44,6 @@ export async function buyDtFromPool( web3: Web3 ): Promise { const pool = new Pool(web3) - const { appConfig } = getSiteMetadata() // we need to calculate the actual price to buy one datatoken const dtPrice = await calculateBuyPrice(accessDetails, null, web3) const approveTx = await approve( @@ -63,14 +61,14 @@ export async function buyDtFromPool( accountId, accessDetails.addressOrId, { - marketFeeAddress: appConfig.marketFeeAddress, + marketFeeAddress, tokenIn: accessDetails.baseToken.address, tokenOut: accessDetails.datatoken.address }, { // this is just to be safe maxAmountIn: new Decimal(dtPrice.tokenAmount).mul(10).toString(), - swapMarketFee: appConfig.consumeMarketPoolSwapFee, + swapMarketFee: consumeMarketPoolSwapFee, tokenAmountOut: '1' } ) @@ -79,61 +77,105 @@ export async function buyDtFromPool( } /** - * Calculate the base token liquidity based on shares info - * @param {string} shares - * @param {string} totalShares - * @param {string} baseTokenLiquidity + * This is used to calculate the actual price of buying a datatoken, it's a copy of the math in the contracts. + * @param params * @returns */ -export function calculateUserLiquidity( - shares: string, - totalShares: string, - baseTokenLiquidity: string -): string { - const totalLiquidity = - isValidNumber(shares) && - isValidNumber(totalShares) && - isValidNumber(baseTokenLiquidity) - ? new Decimal(shares) - .dividedBy(new Decimal(totalShares)) - .mul(baseTokenLiquidity) - : new Decimal(0) - return totalLiquidity.toDecimalPlaces(MAX_DECIMALS).toString() +export function calcInGivenOut(params: CalcInGivenOutParams): PoolPriceAndFees { + const result = { + tokenAmount: '0', + liquidityProviderSwapFeeAmount: '0', + oceanFeeAmount: '0', + publishMarketSwapFeeAmount: '0', + consumeMarketSwapFeeAmount: '0' + } as PoolPriceAndFees + const one = new Decimal(1) + const tokenOutLiqudity = new Decimal(params.tokenOutLiquidity) + const tokenInLiquidity = new Decimal(params.tokenInLiquidity) + const tokenOutAmount = new Decimal(params.tokenOutAmount) + const opcFee = new Decimal(params.opcFee) + const lpFee = new Decimal(params.lpSwapFee) + const publishMarketSwapFee = new Decimal(params.publishMarketSwapFee) + const consumeMarketSwapFee = new Decimal(params.consumeMarketSwapFee) + + const diff = tokenOutLiqudity.minus(tokenOutAmount) + const y = tokenOutLiqudity.div(diff) + let foo = y.pow(one) + foo = foo.minus(one) + const totalFee = lpFee + .plus(opcFee) + .plus(publishMarketSwapFee) + .plus(consumeMarketSwapFee) + + const tokenAmountIn = tokenInLiquidity.mul(foo).div(one.sub(totalFee)) + result.tokenAmount = tokenAmountIn.toString() + result.oceanFeeAmount = tokenAmountIn + .sub(tokenAmountIn.mul(one.sub(opcFee))) + .toString() + result.publishMarketSwapFeeAmount = tokenAmountIn + .sub(tokenAmountIn.mul(one.sub(publishMarketSwapFee))) + .toString() + result.consumeMarketSwapFeeAmount = tokenAmountIn + .sub(tokenAmountIn.mul(one.sub(consumeMarketSwapFee))) + .toString() + result.liquidityProviderSwapFeeAmount = tokenAmountIn + .sub(tokenAmountIn.mul(one.sub(lpFee))) + .toString() + + return result } -export function calculateUserTVL( - shares: string, - totalShares: string, - baseTokenLiquidity: string +/** + * Used to calculate swap values, it's a copy of the math in the contracts. + * @param tokenLiquidity + * @param poolSupply + * @param poolShareAmount + * @returns + */ +export function calcSingleOutGivenPoolIn( + tokenLiquidity: string, + poolSupply: string, + poolShareAmount: string ): string { - const liquidity = calculateUserLiquidity( - shares, - totalShares, - baseTokenLiquidity - ) - const tvl = new Decimal(liquidity).mul(2) // we multiply by 2 because of 50/50 weight - return tvl.toDecimalPlaces(MAX_DECIMALS).toString() + const tokenLiquidityD = new Decimal(tokenLiquidity) + const poolSupplyD = new Decimal(poolSupply) + const poolShareAmountD = new Decimal(poolShareAmount) + + const newPoolSupply = poolSupplyD.sub(poolShareAmountD) + const poolRatio = newPoolSupply.div(poolSupplyD) + + const tokenOutRatio = poolRatio.pow(2) + const newTokenBalanceOut = tokenLiquidityD.mul(tokenOutRatio) + + const tokensOut = tokenLiquidityD.sub(newTokenBalanceOut) + + return tokensOut.toString() } -export async function calculateSharesVL( +/** + * Returns the amount of tokens (based on tokenAddress) that can be withdrawn from the pool + * @param {string} poolAddress + * @param {string} tokenAddress + * @param {string} shares + * @param {number} chainId + * @returns + */ +export async function getLiquidityByShares( pool: string, tokenAddress: string, shares: string, - chainId?: number + chainId: number ): Promise { - if (!chainId) throw new Error("chainId can't be undefined at the same time!") - // we only use the dummyWeb3 connection here const web3 = await getDummyWeb3(chainId) const poolInstance = new Pool(web3) // get shares VL in ocean - const amountOcean = await poolInstance.calcSingleOutGivenPoolIn( + const amountBaseToken = await poolInstance.calcSingleOutGivenPoolIn( pool, tokenAddress, shares ) - const tvl = new Decimal(amountOcean || 0).mul(2) // we multiply by 2 because of 50/50 weight - return tvl.toDecimalPlaces(MAX_DECIMALS).toString() + return amountBaseToken } diff --git a/src/@utils/siteConfig.ts b/src/@utils/siteConfig.ts deleted file mode 100644 index f0de32f54..000000000 --- a/src/@utils/siteConfig.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { UseSiteMetadata } from '@hooks/useSiteMetadata/types' -import siteContent from '../../content/site.json' -import appConfig from '../../app.config' - -export function getSiteMetadata(): UseSiteMetadata { - const siteMeta: UseSiteMetadata = { - ...siteContent, - appConfig - } - - return siteMeta -} diff --git a/src/@utils/subgraph.ts b/src/@utils/subgraph.ts index 5c00f8424..9209cb3aa 100644 --- a/src/@utils/subgraph.ts +++ b/src/@utils/subgraph.ts @@ -15,7 +15,7 @@ import { import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData' import { UserSalesQuery as UsersSalesList } from '../@types/subgraph/UserSalesQuery' import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery' -import { calculateUserTVL } from './pool' +import { calcSingleOutGivenPoolIn, getLiquidityByShares } from './pool' import Decimal from 'decimal.js' import { MAX_DECIMALS } from './constants' @@ -243,13 +243,10 @@ export async function fetchDataForMultipleChains( let datas: any[] = [] try { for (const chainId of chainIds) { - // console.log('fetch chainID', chainId) const context: OperationContext = getQueryContext(chainId) const response = await fetchData(query, variables, context) - // console.log('fetch response', response) if (!response || response.error) continue datas = datas.concat(response?.data) - // console.log('fetch datas', datas) } return datas } catch (error) { @@ -343,7 +340,7 @@ export async function getHighestLiquidityDatatokens( return dtList } -export async function getAccountTVLInOwnAssets( +export async function getAccountLiquidityInOwnAssets( accountId: string, chainIds: number[], pools: string[] @@ -357,22 +354,20 @@ export async function getAccountTVLInOwnAssets( queryVariables, chainIds ) - let tvl = new Decimal(0) - // console.log('resss', results) + let totalLiquidity = new Decimal(0) for (const result of results) { - // console.log('result.poolShares', result.poolShares) for (const poolShare of result.poolShares) { - const poolUserTvl = calculateUserTVL( - poolShare.shares, + const poolUserLiquidity = calcSingleOutGivenPoolIn( + poolShare.pool.baseTokenLiquidity, poolShare.pool.totalShares, - poolShare.pool.baseTokenLiquidity + poolShare.shares ) - tvl = tvl.add(new Decimal(poolUserTvl)) - // console.log('result.poolShares', tvl.toString()) + + totalLiquidity = totalLiquidity.add(new Decimal(poolUserLiquidity)) } } - return tvl.toDecimalPlaces(MAX_DECIMALS).toString() + return totalLiquidity.toDecimalPlaces(MAX_DECIMALS).toString() } export async function getPoolSharesData( diff --git a/src/components/@shared/AssetList/AssetListTitle.tsx b/src/components/@shared/AssetList/AssetListTitle.tsx index d4ed4de15..74e3831e9 100644 --- a/src/components/@shared/AssetList/AssetListTitle.tsx +++ b/src/components/@shared/AssetList/AssetListTitle.tsx @@ -3,8 +3,8 @@ import React, { ReactElement, useEffect, useState } from 'react' import { getAssetsNames } from '@utils/aquarius' import styles from './AssetListTitle.module.css' import axios from 'axios' -import { useSiteMetadata } from '@hooks/useSiteMetadata' import { Asset } from '@oceanprotocol/lib' +import { useMarketMetadata } from '@context/MarketMetadata' export default function AssetListTitle({ asset, @@ -15,7 +15,7 @@ export default function AssetListTitle({ did?: string title?: string }): ReactElement { - const { appConfig } = useSiteMetadata() + const { appConfig } = useMarketMetadata() const [assetTitle, setAssetTitle] = useState(title) useEffect(() => { diff --git a/src/components/@shared/Page/Seo.tsx b/src/components/@shared/Page/Seo.tsx index 11cb3f402..771e36683 100644 --- a/src/components/@shared/Page/Seo.tsx +++ b/src/components/@shared/Page/Seo.tsx @@ -1,7 +1,8 @@ import React, { ReactElement } from 'react' import Head from 'next/head' -import { useSiteMetadata } from '@hooks/useSiteMetadata' + import { isBrowser } from '@utils/index' +import { useMarketMetadata } from '@context/MarketMetadata' export default function Seo({ title, @@ -12,14 +13,14 @@ export default function Seo({ description?: string uri: string }): ReactElement { - const { siteTitle, siteTagline, siteUrl, siteImage } = useSiteMetadata() + const { siteContent } = useMarketMetadata() // Remove trailing slash from all URLs - const canonical = `${siteUrl}${uri}`.replace(/\/$/, '') + const canonical = `${siteContent?.siteUrl}${uri}`.replace(/\/$/, '') const pageTitle = title - ? `${title} - ${siteTitle}` - : `${siteTitle} — ${siteTagline}` + ? `${title} - ${siteContent?.siteTitle}` + : `${siteContent?.siteTitle} — ${siteContent?.siteTagline}` return ( @@ -47,10 +48,16 @@ export default function Seo({ - - + + - + diff --git a/src/components/@shared/PoolTransactions/index.tsx b/src/components/@shared/PoolTransactions/index.tsx index 3318a5303..f9804bfc4 100644 --- a/src/components/@shared/PoolTransactions/index.tsx +++ b/src/components/@shared/PoolTransactions/index.tsx @@ -6,7 +6,6 @@ import { useUserPreferences } from '@context/UserPreferences' import { gql } from 'urql' import { TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions } from '../../../@types/subgraph/TransactionHistory' import { fetchDataForMultipleChains } from '@utils/subgraph' -import { useSiteMetadata } from '@hooks/useSiteMetadata' import NetworkName from '@shared/NetworkName' import { getAssetsFromDtList } from '@utils/aquarius' import { getAsset } from '../../Profile/History/PoolShares/_utils' @@ -15,6 +14,7 @@ import Title from './Title' import styles from './index.module.css' import { Asset, LoggerInstance } from '@oceanprotocol/lib' import { useCancelToken } from '@hooks/useCancelToken' +import { useMarketMetadata } from '@context/MarketMetadata' const REFETCH_INTERVAL = 20000 @@ -135,7 +135,7 @@ export default function PoolTransactions({ accountId: string }): ReactElement { const { chainIds } = useUserPreferences() - const { appConfig } = useSiteMetadata() + const { appConfig } = useMarketMetadata() const cancelToken = useCancelToken() const [transactions, setTransactions] = useState() @@ -199,7 +199,7 @@ export default function PoolTransactions({ setTransactions(sortedTransactions) setIsLoading(false) }, - [data, chainIds, setIsLoading] + [data, minimal, chainIds, poolChainId] ) // diff --git a/src/components/@shared/Token/index.tsx b/src/components/@shared/Token/index.tsx index 47576acdc..992dc35a4 100644 --- a/src/components/@shared/Token/index.tsx +++ b/src/components/@shared/Token/index.tsx @@ -2,9 +2,7 @@ import React, { ReactElement } from 'react' import styles from './index.module.css' import PriceUnit from '@shared/Price/PriceUnit' import Logo from '@shared/atoms/Logo' -import Decimal from 'decimal.js' import Conversion from '@shared/Price/Conversion' -import { MAX_DECIMALS } from '@utils/constants' export default function Token({ symbol, @@ -15,7 +13,7 @@ export default function Token({ }: { symbol: string balance: string - conversion?: Decimal + conversion?: boolean noIcon?: boolean size?: 'small' | 'mini' }): ReactElement { @@ -29,11 +27,8 @@ export default function Token({ - {conversion?.greaterThan(0) && ( - + {conversion && ( + )} ) diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index e467cbbe2..d2ef6dbc4 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -3,7 +3,6 @@ import Alert from '@shared/atoms/Alert' import Footer from '../Footer/Footer' import Header from '../Header' import { useWeb3 } from '@context/Web3' -import { useSiteMetadata } from '@hooks/useSiteMetadata' import { useAccountPurgatory } from '@hooks/useAccountPurgatory' import AnnouncementBanner from '@shared/AnnouncementBanner' import PrivacyPreferenceCenter from '../Privacy/PrivacyPreferenceCenter' @@ -11,6 +10,7 @@ import styles from './index.module.css' import { ToastContainer } from 'react-toastify' import { useRouter } from 'next/router' import content from '../../../content/purgatory.json' +import { useMarketMetadata } from '@context/MarketMetadata' export default function App({ children @@ -19,14 +19,14 @@ export default function App({ }): ReactElement { const router = useRouter() - const { warning, appConfig } = useSiteMetadata() + const { siteContent, appConfig } = useMarketMetadata() const { accountId } = useWeb3() const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId) return (
- {router.pathname === '/' && warning.main !== '' && ( - + {router.pathname === '/' && siteContent?.warning.main !== '' && ( + )}
@@ -41,7 +41,7 @@ export default function App({
{children}
- {appConfig.privacyPreferenceCenter === 'true' && ( + {appConfig?.privacyPreferenceCenter === 'true' && ( )} diff --git a/src/components/Asset/AssetActions/Compute/index.tsx b/src/components/Asset/AssetActions/Compute/index.tsx index 750b90458..643e86a4b 100644 --- a/src/components/Asset/AssetActions/Compute/index.tsx +++ b/src/components/Asset/AssetActions/Compute/index.tsx @@ -1,8 +1,5 @@ import React, { useState, ReactElement, useEffect, useCallback } from 'react' import { - LoggerInstance, - ComputeAlgorithm, - ComputeOutput, Asset, DDO, PublisherTrustedAlgorithm, @@ -12,7 +9,6 @@ import { toast } from 'react-toastify' import Price from '@shared/Price' import FileIcon from '@shared/FileIcon' import Alert from '@shared/atoms/Alert' -import { useSiteMetadata } from '@hooks/useSiteMetadata' import { useWeb3 } from '@context/Web3' import { generateBaseQuery, @@ -25,7 +21,7 @@ import axios from 'axios' import FormStartComputeDataset from './FormComputeDataset' import styles from './index.module.css' import SuccessConfetti from '@shared/SuccessConfetti' -import { getServiceByName, secondsToString } from '@utils/ddo' +import { getServiceByName } from '@utils/ddo' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute' import { getPreviousOrders } from '@utils/subgraph' @@ -37,6 +33,7 @@ import { SortTermOptions } from '../../../../@types/aquarius/SearchQuery' import { getAccessDetails } from '@utils/accessDetailsAndPricing' import { AccessDetails } from 'src/@types/Price' import { transformAssetToAssetSelection } from '@utils/assetConvertor' +import { useMarketMetadata } from '@context/MarketMetadata' export default function Compute({ ddo, @@ -55,7 +52,7 @@ export default function Compute({ isConsumable?: boolean consumableFeedback?: string }): ReactElement { - const { appConfig } = useSiteMetadata() + const { appConfig } = useMarketMetadata() const { accountId } = useWeb3() const [isJobStarting, setIsJobStarting] = useState(false) const [error, setError] = useState() diff --git a/src/components/Asset/AssetActions/Download.tsx b/src/components/Asset/AssetActions/Download.tsx index b3643121e..3da48e633 100644 --- a/src/components/Asset/AssetActions/Download.tsx +++ b/src/components/Asset/AssetActions/Download.tsx @@ -17,6 +17,8 @@ import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing' import { OrderPriceAndFees } from 'src/@types/Price' import { toast } from 'react-toastify' import { useIsMounted } from '@hooks/useIsMounted' +import { usePool } from '@context/Pool' +import { useMarketMetadata } from '@context/MarketMetadata' export default function Download({ asset, @@ -34,6 +36,7 @@ export default function Download({ consumableFeedback?: string }): ReactElement { const { accountId, web3 } = useWeb3() + const { getOpcFeeForToken } = useMarketMetadata() const { isInPurgatory, isAssetNetwork } = useAsset() const isMounted = useIsMounted() @@ -44,6 +47,7 @@ export default function Download({ const [isOwned, setIsOwned] = useState(false) const [validOrderTx, setValidOrderTx] = useState('') + const { poolData } = usePool() const [orderPriceAndFees, setOrderPriceAndFees] = useState() useEffect(() => { @@ -55,19 +59,35 @@ export default function Download({ async function init() { if ( asset?.accessDetails?.addressOrId === ZERO_ADDRESS || - asset?.accessDetails?.type === 'free' + asset?.accessDetails?.type === 'free' || + (!poolData && asset?.accessDetails?.type === 'dynamic') ) return setIsLoading(true) setStatusText('Calculating price including fees.') - const orderPriceAndFees = await getOrderPriceAndFees(asset, ZERO_ADDRESS) + + const params: CalcInGivenOutParams = { + tokenInLiquidity: poolData?.baseTokenLiquidity, + tokenOutLiquidity: poolData?.datatokenLiquidity, + tokenOutAmount: '1', + opcFee: getOpcFeeForToken(poolData.baseToken.address, asset?.chainId), + lpSwapFee: poolData?.liquidityProviderSwapFee, + publishMarketSwapFee: poolData?.publishMarketSwapFee, + consumeMarketSwapFee: '0' + } + const orderPriceAndFees = await getOrderPriceAndFees( + asset, + ZERO_ADDRESS, + params + ) + setOrderPriceAndFees(orderPriceAndFees) setIsLoading(false) } init() - }, [asset, accountId]) + }, [asset, accountId, poolData, getOpcFeeForToken]) useEffect(() => { setHasDatatoken(Number(dtBalance) >= 1) diff --git a/src/components/Asset/AssetActions/Pool/Actions/Header.module.css b/src/components/Asset/AssetActions/Pool/Actions/Header.module.css index a23bd412a..d3d0f4625 100644 --- a/src/components/Asset/AssetActions/Pool/Actions/Header.module.css +++ b/src/components/Asset/AssetActions/Pool/Actions/Header.module.css @@ -4,13 +4,18 @@ margin-bottom: var(--spacer); padding-bottom: calc(var(--spacer) / 2); border-bottom: 1px solid var(--border-color); - margin-top: -1rem; margin-left: -2rem; margin-right: -2rem; padding-left: var(--spacer); padding-right: var(--spacer); } +@media (min-width: 40rem) { + .header { + margin-top: -1rem; + } +} + .headerTitle { font-size: var(--font-size-large); margin: 0; diff --git a/src/components/Asset/AssetActions/Pool/Add/FormAdd.module.css b/src/components/Asset/AssetActions/Pool/Add/FormAdd.module.css index 2c3159a4b..c358e0219 100644 --- a/src/components/Asset/AssetActions/Pool/Add/FormAdd.module.css +++ b/src/components/Asset/AssetActions/Pool/Add/FormAdd.module.css @@ -1,6 +1,6 @@ .buttonMax { position: absolute; font-size: var(--font-size-mini); - bottom: calc(var(--spacer) / 2); + bottom: calc(var(--spacer) / 2.6); right: calc(var(--spacer) * 2.5); } diff --git a/src/components/Asset/AssetActions/Pool/Add/FormAdd.tsx b/src/components/Asset/AssetActions/Pool/Add/FormAdd.tsx index 0dda5bec4..a7e1c423b 100644 --- a/src/components/Asset/AssetActions/Pool/Add/FormAdd.tsx +++ b/src/components/Asset/AssetActions/Pool/Add/FormAdd.tsx @@ -1,12 +1,8 @@ import React, { ReactElement, useEffect } from 'react' import styles from './FormAdd.module.css' import Input from '@shared/FormInput' -import { - Field, - FieldInputProps, - FormikContextType, - useFormikContext -} from 'formik' +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' @@ -36,7 +32,7 @@ export default function FormAdd({ values, isSubmitting }: FormikContextType = useFormikContext() - + const [field, meta] = useField('amount') useEffect(() => { async function calculatePoolShares() { if (!web3 || !poolData?.id || !poolInfo?.totalPoolTokens) return @@ -53,7 +49,7 @@ export default function FormAdd({ const poolTokens = await poolInstance.calcPoolOutGivenSingleIn( poolData.id, poolInfo.baseTokenAddress, - values.amount + values.amount.toString() ) setNewPoolTokens(poolTokens) const newPoolShareDecimal = @@ -89,29 +85,15 @@ export default function FormAdd({ symbol={poolInfo?.baseTokenSymbol} /> - - {({ - field, - form - }: { - field: FieldInputProps - form: any - }) => ( - - )} - + } + /> {Number(balance.ocean) > 0 && ( - {/* Fetching every {refreshInterval / 1000} sec. */} -
+

+ Fetching every {refreshInterval / 1000} sec.{' '} + {debug && ( + + )} +

) } diff --git a/src/components/Asset/AssetActions/Pool/Sections/index.tsx b/src/components/Asset/AssetActions/Pool/Sections/index.tsx index 858ef6bfd..a7b4508ba 100644 --- a/src/components/Asset/AssetActions/Pool/Sections/index.tsx +++ b/src/components/Asset/AssetActions/Pool/Sections/index.tsx @@ -47,9 +47,10 @@ export default function PoolSections() { @@ -92,7 +93,6 @@ export default function PoolSections() { titlePostfixTitle={`Weight of ${poolInfo?.weightBaseToken}% ${poolInfo?.baseTokenSymbol} & ${poolInfo?.weightDt}% ${poolInfo?.datatokenSymbol}`} > - setShowAdd(true)} - disabled={isInPurgatory} + disabled={isInPurgatory || !isAssetNetwork} > Add Liquidity - {hasUserAddedLiquidity && !isRemoveDisabled && ( + {hasUserAddedLiquidity && (