diff --git a/.env.example b/.env.example index f65b84a23..3015d26c8 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,8 @@ #NEXT_PUBLIC_INFURA_PROJECT_ID="xxx" #NEXT_PUBLIC_MARKET_FEE_ADDRESS="0xxx" #NEXT_PUBLIC_PUBLISHER_MARKET_ORDER_FEE="1" -#NEXT_PUBLIC_PUBLISHER_MARKET_POOL_SWAP_FEE="1" #NEXT_PUBLIC_PUBLISHER_MARKET_FIXED_SWAP_FEE="1" #NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE="1" -#NEXT_PUBLIC_CONSUME_MARKET_POOL_SWAP_FEE="1" #NEXT_PUBLIC_CONSUME_MARKET_FIXED_SWAP_FEE="1" # diff --git a/README.md b/README.md index 7213aceeb..442ff7f25 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ All displayed data in the app is presented around the concept of one data set, w - the actual data set files - the NFT which represents the data set - the datatokens representing access rights to the data set files -- financial data connected to these datatokens +- financial data connected to these datatokens, either a fixed rate exchange contract or a dispenser for free assets - calculations and conversions based on financial data - metadata about publisher accounts diff --git a/app.config.js b/app.config.js index a8b439129..39af889af 100644 --- a/app.config.js +++ b/app.config.js @@ -33,9 +33,6 @@ module.exports = { // publisher market fee that is taken upon ordering an asset, it is an absolute value, it is declared on erc20 creation publisherMarketOrderFee: process.env.NEXT_PUBLIC_PUBLISHER_MARKET_ORDER_FEE || '0', - // fee recieved by the publisher market when a dt is swaped from a pool, percent - publisherMarketPoolSwapFee: - process.env.NEXT_PUBLIC_PUBLISHER_MARKET_POOL_SWAP_FEE || '0', // fee recieved by the publisher market when a dt is bought from a fixed rate exchange, percent publisherMarketFixedSwapFee: process.env.NEXT_PUBLIC_PUBLISHER_MARKET_FIXED_SWAP_FEE || '0', @@ -43,9 +40,6 @@ module.exports = { // consume market fee that is taken upon ordering an asset, it is an absolute value, it is specified on order consumeMarketOrderFee: process.env.NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE || '0', - // fee recieved by the consume market when a dt is swaped from a pool, percent - consumeMarketPoolSwapFee: - process.env.NEXT_PUBLIC_CONSUME_MARKET_POOL_SWAP_FEE || '0', // fee recieved by the consume market when a dt is bought from a fixed rate exchange, percent consumeMarketFixedSwapFee: process.env.NEXT_PUBLIC_CONSUME_MARKET_FIXED_SWAP_FEE || '0', @@ -75,10 +69,9 @@ module.exports = { storageKey: 'oceanDarkMode' }, - // Used to show or hide the fixed, dynamic or free price options + // Used to show or hide the fixed or free price options // tab to publishers during the price creation. allowFixedPricing: process.env.NEXT_PUBLIC_ALLOW_FIXED_PRICING || 'true', - allowDynamicPricing: process.env.NEXT_PUBLIC_ALLOW_DYNAMIC_PRICING || 'true', allowFreePricing: process.env.NEXT_PUBLIC_ALLOW_FREE_PRICING || 'true', // Set the default privacy policy to initially display diff --git a/content/footer.json b/content/footer.json index 365c2b78b..c206ab996 100644 --- a/content/footer.json +++ b/content/footer.json @@ -14,6 +14,6 @@ } ], "stats": { - "note": "Counted on-chain from our NFT and pool factories. Includes assets in all Ocean Market forks and [purgatory](https://github.com/oceanprotocol/list-purgatory)." + "note": "Counted on-chain from our NFT factories. Includes assets in all Ocean Market forks and [purgatory](https://github.com/oceanprotocol/list-purgatory)." } } diff --git a/content/price.json b/content/price.json index 49870c00a..f9e6450f3 100644 --- a/content/price.json +++ b/content/price.json @@ -1,13 +1,5 @@ { "create": { - "empty": { - "title": "No Price Created", - "info": "This data set has no price yet. As the publisher you can create a fixed price, or a dynamic price for it. Onwards!", - "action": { - "name": "Create Pricing", - "help": "Create Pricing will mint your datatokens, approve spending, and create either a pool or a fixed rate exchange in one process. You will need to approve those multiple steps in your wallet." - } - }, "fixed": { "title": "Fixed", "info": "Set your price for accessing this data set. The datatoken for this data set will be worth the entered amount of OCEAN.", @@ -30,24 +22,10 @@ ] } }, - "pool": { + "approval": { "tooltips": { - "price": "The price is determined by an automated market maker, which is a type of decentralized exchange protocol that relies on a mathematical formula. It is an alternative to a traditional order book.", - "liquidity": "Providing liquidity will earn you SWAPFEE% on every transaction in this pool, proportionally to your share of the pool.", "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." - }, - "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.", - "output": { - "titleOutExpected": "Expected output", - "titleOutMinimum": "Minimum received" - }, - "action": "Remove" } - }, - "trade": { - "action": "Swap" } } diff --git a/content/publish/form.json b/content/publish/form.json index 4b6b1ef02..4a4474ca3 100644 --- a/content/publish/form.json +++ b/content/publish/form.json @@ -122,7 +122,7 @@ "label": "Algorithm Privacy", "type": "checkbox", "options": ["Keep my algorithm private"], - "help": "By default, your algorithm can be downloaded for a fixed or dynamic price in addition to running in compute jobs. Enabling this option will prevent downloading, so your algorithm can only be run as part of a compute job on a data set.", + "help": "By default, your algorithm can be downloaded for free or a fixed price, in addition to running in compute jobs. Enabling this option will prevent downloading, so your algorithm can only be run as part of a compute job on a data set.", "required": false }, { diff --git a/content/site.json b/content/site.json index db26be565..a6ada0ab3 100644 --- a/content/site.json +++ b/content/site.json @@ -14,7 +14,7 @@ "link": "/profile" } ], - "announcement": "Data NFTs, One-Sided Staking and more.", + "announcement": "Explore [OceanONDA V4](https://blog.oceanprotocol.com/how-to-publish-a-data-nft-f58ad2a622a9).", "warning": { "ctd": "Compute-to-Data is still in a testing phase, please use it only on test networks." } diff --git a/package-lock.json b/package-lock.json index 0b5fa9216..82c0000f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,6 @@ "@urql/exchange-refocus": "^0.2.5", "@walletconnect/web3-provider": "^1.7.8", "axios": "^0.27.2", - "chart.js": "^3.8.0", "classnames": "^2.3.1", "date-fns": "^2.29.1", "decimal.js": "^10.3.1", @@ -37,7 +36,6 @@ "next": "^12.1.6", "query-string": "^7.1.1", "react": "^18.2.0", - "react-chartjs-2": "^4.2.0", "react-clipboard.js": "^2.0.16", "react-data-table-component": "^7.5.2", "react-dom": "^18.1.0", @@ -70,7 +68,6 @@ "@svgr/webpack": "^6.2.1", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", - "@types/chart.js": "^2.9.37", "@types/js-cookie": "^3.0.2", "@types/loadable__component": "^5.13.4", "@types/lodash.debounce": "^4.0.7", @@ -17910,15 +17907,6 @@ "@types/node": "*" } }, - "node_modules/@types/chart.js": { - "version": "2.9.37", - "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.37.tgz", - "integrity": "sha512-9bosRfHhkXxKYfrw94EmyDQcdjMaQPkU1fH2tDxu8DWXxf1mjzWQAV4laJF51ZbC2ycYwNDvIm1rGez8Bug0vg==", - "dev": true, - "dependencies": { - "moment": "^2.10.2" - } - }, "node_modules/@types/clipboard": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.7.tgz", @@ -22298,11 +22286,6 @@ "node": ">=6" } }, - "node_modules/chart.js": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.8.0.tgz", - "integrity": "sha512-cr8xhrXjLIXVLOBZPkBZVF6NDeiVIrPLHcMhnON7UufudL+CNeRrD+wpYanswlm8NpudMdrt3CHoLMQMxJhHRg==" - }, "node_modules/checkpoint-store": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/checkpoint-store/-/checkpoint-store-1.1.0.tgz", @@ -39429,15 +39412,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-chartjs-2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.2.0.tgz", - "integrity": "sha512-9Vm9Sg9XAKiR579/FnBkesofjW9goaaFLfS7XlGTzUJlWFZGSE6A/pBI6+i/bP3pobKZoFcWJdFnjShytToqXw==", - "peerDependencies": { - "chart.js": "^3.5.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-clipboard.js": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/react-clipboard.js/-/react-clipboard.js-2.0.16.tgz", @@ -60513,15 +60487,6 @@ "@types/node": "*" } }, - "@types/chart.js": { - "version": "2.9.37", - "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.37.tgz", - "integrity": "sha512-9bosRfHhkXxKYfrw94EmyDQcdjMaQPkU1fH2tDxu8DWXxf1mjzWQAV4laJF51ZbC2ycYwNDvIm1rGez8Bug0vg==", - "dev": true, - "requires": { - "moment": "^2.10.2" - } - }, "@types/clipboard": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.7.tgz", @@ -64101,11 +64066,6 @@ "integrity": "sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ==", "dev": true }, - "chart.js": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.8.0.tgz", - "integrity": "sha512-cr8xhrXjLIXVLOBZPkBZVF6NDeiVIrPLHcMhnON7UufudL+CNeRrD+wpYanswlm8NpudMdrt3CHoLMQMxJhHRg==" - }, "checkpoint-store": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/checkpoint-store/-/checkpoint-store-1.1.0.tgz", @@ -77612,12 +77572,6 @@ "loose-envify": "^1.1.0" } }, - "react-chartjs-2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.2.0.tgz", - "integrity": "sha512-9Vm9Sg9XAKiR579/FnBkesofjW9goaaFLfS7XlGTzUJlWFZGSE6A/pBI6+i/bP3pobKZoFcWJdFnjShytToqXw==", - "requires": {} - }, "react-clipboard.js": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/react-clipboard.js/-/react-clipboard.js-2.0.16.tgz", diff --git a/package.json b/package.json index a35aca270..10e15f73e 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "@urql/exchange-refocus": "^0.2.5", "@walletconnect/web3-provider": "^1.7.8", "axios": "^0.27.2", - "chart.js": "^3.8.0", "classnames": "^2.3.1", "date-fns": "^2.29.1", "decimal.js": "^10.3.1", @@ -50,7 +49,6 @@ "next": "^12.1.6", "query-string": "^7.1.1", "react": "^18.2.0", - "react-chartjs-2": "^4.2.0", "react-clipboard.js": "^2.0.16", "react-data-table-component": "^7.5.2", "react-dom": "^18.1.0", @@ -83,7 +81,6 @@ "@svgr/webpack": "^6.2.1", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", - "@types/chart.js": "^2.9.37", "@types/js-cookie": "^3.0.2", "@types/loadable__component": "^5.13.4", "@types/lodash.debounce": "^4.0.7", diff --git a/src/@context/Asset.tsx b/src/@context/Asset.tsx index 3df0c85f9..9bbdeed06 100644 --- a/src/@context/Asset.tsx +++ b/src/@context/Asset.tsx @@ -13,7 +13,6 @@ import { checkV3Asset, retrieveAsset } from '@utils/aquarius' import { useWeb3 } from './Web3' 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' @@ -126,6 +125,7 @@ function AssetProvider({ // ----------------------------------- const fetchAccessDetails = useCallback(async (): Promise => { if (!asset?.chainId || !asset?.services) return + const accessDetails = await getAccessDetails( asset.chainId, asset.services[0].datatokenAddress, diff --git a/src/@context/MarketMetadata/_types.ts b/src/@context/MarketMetadata/_types.ts index 1be2cb03f..edeca7226 100644 --- a/src/@context/MarketMetadata/_types.ts +++ b/src/@context/MarketMetadata/_types.ts @@ -12,14 +12,11 @@ export interface AppConfig { chainIdsSupported: number[] marketFeeAddress: string publisherMarketOrderFee: string - publisherMarketPoolSwapFee: string publisherMarketFixedSwapFee: string consumeMarketOrderFee: string - consumeMarketPoolSwapFee: string consumeMarketFixedSwapFee: string currencies: string[] allowFixedPricing: string - allowDynamicPricing: string allowFreePricing: string defaultPrivacyPolicySlug: string privacyPreferenceCenter: string diff --git a/src/@context/Pool/_queries.ts b/src/@context/Pool/_queries.ts deleted file mode 100644 index fa6f610ea..000000000 --- a/src/@context/Pool/_queries.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { gql } from 'urql' - -export const poolDataQuery = gql` - query PoolData( - $pool: ID! - $poolAsString: String! - $owner: String! - $user: String - ) { - poolData: pool(id: $pool) { - id - totalShares - liquidityProviderSwapFee - publishMarketSwapFee - spotPrice - baseToken { - address - symbol - decimals - } - baseTokenWeight - baseTokenLiquidity - datatoken { - address - symbol - decimals - } - datatokenWeight - datatokenLiquidity - shares(where: { user: $owner }) { - shares - } - } - poolDataUser: pool(id: $pool) { - shares(where: { user: $user }) { - shares - } - } - poolSnapshots(first: 1000, where: { pool: $poolAsString }, orderBy: date) { - date - spotPrice - baseTokenLiquidity - datatokenLiquidity - swapVolume - baseToken { - address - symbol - decimals - } - datatoken { - address - symbol - decimals - } - } - } -` diff --git a/src/@context/Pool/_types.ts b/src/@context/Pool/_types.ts deleted file mode 100644 index ffac9d8e1..000000000 --- a/src/@context/Pool/_types.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - PoolData_poolSnapshots as PoolDataPoolSnapshots, - PoolData_poolData as PoolDataPoolData -} from 'src/@types/subgraph/PoolData' - -export interface PoolInfo { - liquidityProviderSwapFee: string - publishMarketSwapFee: string - weightBaseToken: string - weightDt: string - datatokenSymbol: string - datatokenAddress: string - datatokenDecimals: number - baseTokenSymbol: string - baseTokenAddress: string - baseTokenDecimals: number - totalPoolTokens: string -} - -export interface PoolInfoUser { - liquidity: string - poolShares: string - poolSharePercentage: string -} - -export interface PoolProviderValue { - poolData: PoolDataPoolData - poolInfo: PoolInfo - poolInfoOwner: PoolInfoUser - poolInfoUser: PoolInfoUser - poolSnapshots: PoolDataPoolSnapshots[] - hasUserAddedLiquidity: boolean - refreshInterval: number - fetchAllData: () => void -} diff --git a/src/@context/Pool/_utils.ts b/src/@context/Pool/_utils.ts deleted file mode 100644 index 09559febb..000000000 --- a/src/@context/Pool/_utils.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { isValidNumber } from '@utils/numbers' -import { getQueryContext, fetchData } from '@utils/subgraph' -import Decimal from 'decimal.js' -import { PoolData } from 'src/@types/subgraph/PoolData' -import { OperationResult } from 'urql' -import { poolDataQuery } from './_queries' - -export async function getPoolData( - chainId: number, - pool: string, - owner: string, - user: string -) { - const queryVariables = { - // Using `pool` & `poolAsString` is a workaround to make the mega query work. - // See https://github.com/oceanprotocol/ocean-subgraph/issues/301 - pool: pool.toLowerCase(), - poolAsString: pool.toLowerCase(), - owner: owner.toLowerCase(), - user: user.toLowerCase() - } - - const response: OperationResult = await fetchData( - poolDataQuery, - queryVariables, - getQueryContext(chainId) - ) - return response?.data -} - -export function getWeight(weight: string) { - return isValidNumber(weight) ? new Decimal(weight).mul(10).toString() : '0' -} - -export function getFee(fee: string) { - // fees are tricky: to get 0.1% you need to convert from 0.001 - return isValidNumber(fee) ? new Decimal(fee).mul(100).toString() : '0' -} diff --git a/src/@context/Pool/index.tsx b/src/@context/Pool/index.tsx deleted file mode 100644 index f17933321..000000000 --- a/src/@context/Pool/index.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import { LoggerInstance } from '@oceanprotocol/lib' -import Decimal from 'decimal.js' -import React, { - useContext, - useState, - useEffect, - createContext, - ReactElement, - useCallback, - ReactNode -} from 'react' -import { - PoolData_poolSnapshots as PoolDataPoolSnapshots, - PoolData_poolData as PoolDataPoolData -} from 'src/@types/subgraph/PoolData' -import { useAsset } from '../Asset' -import { useWeb3 } from '../Web3' -import { calcSingleOutGivenPoolIn } from '@utils/pool' -import { PoolProviderValue, PoolInfo, PoolInfoUser } from './_types' -import { getFee, getPoolData, getWeight } from './_utils' -import { useMarketMetadata } from '@context/MarketMetadata' - -const PoolContext = createContext({} as PoolProviderValue) - -const refreshInterval = 10000 // 10 sec. - -const initialPoolInfoUser: Partial = { - liquidity: '0', - poolShares: '0' -} - -const initialPoolInfoCreator: Partial = initialPoolInfoUser - -function PoolProvider({ children }: { children: ReactNode }): ReactElement { - const { accountId, web3, chainId } = useWeb3() - const { asset, owner } = useAsset() - const { getOpcFeeForToken } = useMarketMetadata() - const [poolData, setPoolData] = useState() - const [poolInfo, setPoolInfo] = useState() - const [poolInfoOwner, setPoolInfoOwner] = useState( - initialPoolInfoCreator as PoolInfoUser - ) - const [poolInfoUser, setPoolInfoUser] = useState( - initialPoolInfoUser as PoolInfoUser - ) - const [poolSnapshots, setPoolSnapshots] = useState() - const [hasUserAddedLiquidity, setUserHasAddedLiquidity] = useState(false) - - const fetchAllData = useCallback(async () => { - if (!asset?.chainId || !asset?.accessDetails?.addressOrId || !owner) return - - const response = await getPoolData( - asset.chainId, - asset.accessDetails.addressOrId, - owner, - accountId || '' - ) - if (!response) return - - setPoolData(response.poolData) - - // calculate pool info user - const poolInfoShares = response.poolDataUser?.shares[0]?.shares || '0' - const userLiquidity = calcSingleOutGivenPoolIn( - response.poolData.baseTokenLiquidity, - response.poolData.totalShares, - poolInfoShares - ) - - // Pool share in %. We double it to compensate for ss bot - const poolSharePercentage = new Decimal(poolInfoShares) - .dividedBy(new Decimal(response.poolData.totalShares)) - .mul(200) - .toFixed(2) - - setUserHasAddedLiquidity(Number(poolSharePercentage) > 0) - - const newPoolInfoUser: PoolInfoUser = { - liquidity: userLiquidity, - poolShares: poolInfoShares, - poolSharePercentage - } - setPoolInfoUser((prevState: PoolInfoUser) => ({ - ...prevState, - ...newPoolInfoUser - })) - - setPoolSnapshots(response.poolSnapshots) - LoggerInstance.log('[pool] Fetched pool data:', response.poolData) - LoggerInstance.log('[pool] Fetched user data:', response.poolDataUser) - LoggerInstance.log('[pool] Fetched pool snapshots:', response.poolSnapshots) - }, [asset?.chainId, asset?.accessDetails?.addressOrId, owner, accountId]) - - // 0 Fetch all the data on mount if we are on a pool. - // All further effects depend on the fetched data - // and only do further data checking and manipulation. - // - useEffect(() => { - if (asset?.accessDetails?.type !== 'dynamic') return - fetchAllData() - const interval = setInterval(() => { - fetchAllData() - }, refreshInterval) - return () => clearInterval(interval) - }, [fetchAllData, asset?.accessDetails?.type]) - - // - // 1 General Pool Info - // - useEffect(() => { - if (!poolData) return - - const newPoolInfo = { - liquidityProviderSwapFee: getFee(poolData.liquidityProviderSwapFee), - publishMarketSwapFee: getFee(poolData.publishMarketSwapFee), - opcFee: getFee( - getOpcFeeForToken(poolData.baseToken.address, asset?.chainId) - ), - weightBaseToken: getWeight(poolData.baseTokenWeight), - weightDt: getWeight(poolData.datatokenWeight), - datatokenSymbol: poolData.datatoken.symbol, - datatokenAddress: poolData.datatoken.address, - datatokenDecimals: poolData.datatoken.decimals, - baseTokenSymbol: poolData.baseToken.symbol, - baseTokenAddress: poolData.baseToken.address, - baseTokenDecimals: poolData.baseToken.decimals, - totalPoolTokens: poolData.totalShares - } - - setPoolInfo(newPoolInfo) - LoggerInstance.log('[pool] Created new pool info:', newPoolInfo) - }, [asset?.chainId, chainId, getOpcFeeForToken, poolData, web3]) - - // - // 2 Pool Creator Info - // - useEffect(() => { - if ( - !poolData || - !poolInfo?.totalPoolTokens || - !poolData.shares[0]?.shares || - poolData.shares[0]?.shares === '0' - ) - return - - // Pool share tokens. We double it to compensate for ss bot - const poolSharePercentage = new Decimal(poolData.shares[0]?.shares) - .dividedBy(poolInfo.totalPoolTokens) - .mul(200) - .toFixed(2) - - const ownerLiquidity = calcSingleOutGivenPoolIn( - poolData.baseTokenLiquidity, - poolData.totalShares, - poolData?.shares[0]?.shares - ) - - const newPoolOwnerInfo = { - liquidity: ownerLiquidity, - poolShares: poolData.shares[0]?.shares, - poolSharePercentage - } - setPoolInfoOwner(newPoolOwnerInfo) - LoggerInstance.log('[pool] Created new pool creatorinfo:', newPoolOwnerInfo) - }, [ - asset?.chainId, - poolData, - poolInfo?.baseTokenAddress, - poolInfo?.totalPoolTokens - ]) - - return ( - - {children} - - ) -} - -// Helper hook to access the provider values -const usePool = (): PoolProviderValue => useContext(PoolContext) - -export { PoolProvider, usePool, PoolContext } -export default PoolProvider diff --git a/src/@context/Profile.tsx b/src/@context/Profile.tsx index 0090f1db3..bd895c6a0 100644 --- a/src/@context/Profile.tsx +++ b/src/@context/Profile.tsx @@ -7,13 +7,8 @@ import React, { useCallback, ReactNode } from 'react' -import { - getPoolSharesData, - getUserSales, - getUserTokenOrders -} from '@utils/subgraph' +import { getUserSales, getUserTokenOrders } from '@utils/subgraph' 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 { accountTruncate } from '@utils/web3' @@ -24,8 +19,6 @@ import { useMarketMetadata } from './MarketMetadata' interface ProfileProviderValue { profile: Profile - poolShares: PoolShare[] - isPoolSharesLoading: boolean assets: Asset[] assetsTotal: number isEthAddress: boolean @@ -121,55 +114,6 @@ function ProfileProvider({ } }, [accountId, accountEns, isEthAddress]) - // - // POOL SHARES - // - const [poolShares, setPoolShares] = useState() - const [isPoolSharesLoading, setIsPoolSharesLoading] = useState(false) - const [poolSharesInterval, setPoolSharesInterval] = useState() - - const fetchPoolShares = useCallback( - async (accountId: string, chainIds: number[], isEthAddress: boolean) => { - if (!accountId || !chainIds || !isEthAddress) return - - try { - setIsPoolSharesLoading(true) - const poolShares = await getPoolSharesData(accountId, chainIds) - setPoolShares(poolShares) - LoggerInstance.log( - `[profile] Fetched ${poolShares.length} pool shares.`, - poolShares - ) - } catch (error) { - LoggerInstance.error('Error fetching pool shares: ', error.message) - } finally { - setIsPoolSharesLoading(false) - } - }, - [] - ) - - useEffect(() => { - async function init() { - await fetchPoolShares(accountId, chainIds, isEthAddress) - - if (poolSharesInterval) return - - const interval = setInterval(async () => { - LoggerInstance.log( - `[profile] Re-fetching pool shares after ${refreshInterval / 1000}s.` - ) - await fetchPoolShares(accountId, chainIds, isEthAddress) - }, refreshInterval) - setPoolSharesInterval(interval) - } - init() - - return () => { - clearInterval(poolSharesInterval) - } - }, [poolSharesInterval, fetchPoolShares, accountId, chainIds, isEthAddress]) - // // PUBLISHED ASSETS // @@ -300,8 +244,6 @@ function ProfileProvider({ 0) { const order = tokenPrice.orders[0] const reusedOrder = order?.reuses?.length > 0 ? order.reuses[0] : null @@ -198,7 +174,6 @@ function getAccessDetailsFromTokenPrice( name: dispenser.token.name, symbol: dispenser.token.symbol } - return accessDetails } // checking for fixed price @@ -219,30 +194,8 @@ function getAccessDetailsFromTokenPrice( name: fixed.datatoken.name, symbol: fixed.datatoken.symbol } - return accessDetails } - // checking for pools - if (tokenPrice?.pools?.length > 0) { - const pool = tokenPrice.pools[0] - accessDetails.type = 'dynamic' - accessDetails.addressOrId = pool.id - accessDetails.price = pool.spotPrice - // TODO: pool.datatokenLiquidity > 3 is kinda random here, we shouldn't run into this anymore now , needs more thinking/testing - accessDetails.isPurchasable = - pool.isFinalized && pool.datatokenLiquidity > 3 - accessDetails.baseToken = { - address: pool.baseToken.address, - name: pool.baseToken.name, - symbol: pool.baseToken.symbol - } - accessDetails.datatoken = { - address: pool.datatoken.address, - name: pool.datatoken.name, - symbol: pool.datatoken.symbol - } - return accessDetails - } return accessDetails } @@ -254,17 +207,13 @@ function getAccessDetailsFromTokenPrice( export async function getOrderPriceAndFees( asset: AssetExtended, accountId?: string, - paramsForPool?: CalcInGivenOutParams, providerFees?: ProviderFees ): Promise { const orderPriceAndFee = { price: '0', - publisherMarketOrderFee: - asset?.accessDetails?.publisherMarketOrderFee || '0', - publisherMarketPoolSwapFee: '0', + publisherMarketOrderFee: publisherMarketOrderFee || '0', publisherMarketFixedSwapFee: '0', consumeMarketOrderFee: consumeMarketOrderFee || '0', - consumeMarketPoolSwapFee: '0', consumeMarketFixedSwapFee: '0', providerFee: { providerFeeAmount: '0' @@ -273,7 +222,6 @@ export async function getOrderPriceAndFees( } as OrderPriceAndFees // fetch provider fee - const initializeData = !providerFees && (await ProviderInstance.initialize( @@ -286,27 +234,12 @@ export async function getOrderPriceAndFees( orderPriceAndFee.providerFee = providerFees || initializeData.providerFee // fetch price and swap fees - switch (asset?.accessDetails?.type) { - case 'dynamic': { - const poolPrice = calcInGivenOut(paramsForPool) - orderPriceAndFee.price = poolPrice.tokenAmount - orderPriceAndFee.liquidityProviderSwapFee = - poolPrice.liquidityProviderSwapFeeAmount - orderPriceAndFee.publisherMarketPoolSwapFee = - poolPrice.publishMarketSwapFeeAmount - orderPriceAndFee.consumeMarketPoolSwapFee = - poolPrice.consumeMarketSwapFeeAmount - break - } - case 'fixed': { - const fixed = await getFixedBuyPrice(asset?.accessDetails, asset?.chainId) - orderPriceAndFee.price = fixed.baseTokenAmount - orderPriceAndFee.opcFee = fixed.oceanFeeAmount - orderPriceAndFee.publisherMarketFixedSwapFee = fixed.marketFeeAmount - orderPriceAndFee.consumeMarketFixedSwapFee = fixed.consumeMarketFeeAmount - - break - } + if (asset?.accessDetails?.type === 'fixed') { + const fixed = await getFixedBuyPrice(asset?.accessDetails, asset?.chainId) + orderPriceAndFee.price = fixed.baseTokenAmount + orderPriceAndFee.opcFee = fixed.oceanFeeAmount + orderPriceAndFee.publisherMarketFixedSwapFee = fixed.marketFeeAmount + orderPriceAndFee.consumeMarketFixedSwapFee = fixed.consumeMarketFeeAmount } // calculate full price, we assume that all the values are in ocean, otherwise this will be incorrect @@ -314,6 +247,7 @@ export async function getOrderPriceAndFees( .add(new Decimal(+orderPriceAndFee?.consumeMarketOrderFee || 0)) .add(new Decimal(+orderPriceAndFee?.publisherMarketOrderFee || 0)) .toString() + return orderPriceAndFee } diff --git a/src/@utils/assetConvertor.ts b/src/@utils/assetConvertor.ts index de12c9fa3..cbd2e4498 100644 --- a/src/@utils/assetConvertor.ts +++ b/src/@utils/assetConvertor.ts @@ -1,5 +1,4 @@ import { getAccessDetailsForAssets } from './accessDetailsAndPricing' -import { AssetExtended } from 'src/@types/AssetExtended' import { PublisherTrustedAlgorithm, Asset } from '@oceanprotocol/lib' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' import { getServiceByName } from './ddo' diff --git a/src/@utils/compute.ts b/src/@utils/compute.ts index 187f73e12..9ee895d09 100644 --- a/src/@utils/compute.ts +++ b/src/@utils/compute.ts @@ -24,7 +24,6 @@ import { getServiceById, getServiceByName } from './ddo' import { SortTermOptions } from 'src/@types/aquarius/SearchQuery' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' import { transformAssetToAssetSelection } from './assetConvertor' -import { AssetExtended } from 'src/@types/AssetExtended' import { ComputeEditForm } from 'src/components/Asset/Edit/_types' import { getFileDidInfo } from './provider' diff --git a/src/@utils/feedback.ts b/src/@utils/feedback.ts index 6e936cc5c..e6c047f8e 100644 --- a/src/@utils/feedback.ts +++ b/src/@utils/feedback.ts @@ -4,7 +4,7 @@ export function getOrderFeedback( datatokenSymbol: string ): { [key in number]: string } { return { - 0: `Approving and buying one ${datatokenSymbol} from pool`, + 0: `Approving and buying one ${datatokenSymbol}`, 1: `Ordering asset`, 2: `Approving ${baseTokenSymbol} and ordering asset`, 3: 'Generating signature to access download url' diff --git a/src/@utils/fixedRateExchange.ts b/src/@utils/fixedRateExchange.ts index e8d1b1360..eb164e721 100644 --- a/src/@utils/fixedRateExchange.ts +++ b/src/@utils/fixedRateExchange.ts @@ -1,8 +1,7 @@ import { FixedRateExchange, PriceAndFees } from '@oceanprotocol/lib' -import { AccessDetails } from 'src/@types/Price' +import { consumeMarketFixedSwapFee } from '../../app.config' import Web3 from 'web3' import { getOceanConfig } from './ocean' -import { consumeMarketPoolSwapFee } from '../../app.config' import { getDummyWeb3 } from './web3' /** @@ -30,7 +29,7 @@ export async function getFixedBuyPrice( const estimatedPrice = await fixed.calcBaseInGivenOutDT( accessDetails.addressOrId, '1', - consumeMarketPoolSwapFee + consumeMarketFixedSwapFee ) return estimatedPrice } diff --git a/src/@utils/order.ts b/src/@utils/order.ts index 0e331afbc..2c4070eba 100644 --- a/src/@utils/order.ts +++ b/src/@utils/order.ts @@ -10,21 +10,17 @@ import { ProviderFees, ProviderInstance } from '@oceanprotocol/lib' -import { AssetExtended } from 'src/@types/AssetExtended' import Web3 from 'web3' import { getOceanConfig } from './ocean' import { TransactionReceipt } from 'web3-eth' -import { OrderPriceAndFees } from 'src/@types/Price' import { marketFeeAddress, consumeMarketOrderFee, consumeMarketFixedSwapFee } from '../../app.config' -import { buyDtFromPool } from './pool' import { toast } from 'react-toastify' /** - * For pool you need to buy the datatoken beforehand, this always assumes you want to order the first service * @param web3 * @param asset * @param orderPriceAndFees @@ -102,17 +98,6 @@ export async function order( return tx } - case 'dynamic': { - const tx = await datatoken.startOrder( - asset.accessDetails.datatoken.address, - accountId, - computeConsumerAddress || accountId, - 0, - providerFees || initializeData.providerFee - ) - return tx - } - case 'free': { const tx = await datatoken.buyFromDispenserAndOrder( asset.services[0].datatokenAddress, @@ -191,14 +176,6 @@ async function startOrder( initializeData: ProviderComputeInitialize, computeConsumerAddress?: string ): Promise { - if (!hasDatatoken && asset?.accessDetails.type === 'dynamic') { - const poolTx = await buyDtFromPool(asset?.accessDetails, accountId, web3) - LoggerInstance.log('[compute] Bought datatoken from pool: ', poolTx) - if (!poolTx) { - toast.error('Failed to buy datatoken from pool!') - return - } - } const tx = await order( web3, asset, diff --git a/src/@utils/pool.ts b/src/@utils/pool.ts deleted file mode 100644 index 6db2cb590..000000000 --- a/src/@utils/pool.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { approve, Pool, PoolPriceAndFees } from '@oceanprotocol/lib' -import Web3 from 'web3' -import { getDummyWeb3 } from './web3' -import { TransactionReceipt } from 'web3-eth' -import Decimal from 'decimal.js' -import { AccessDetails } from 'src/@types/Price' -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 - * @param {Web3?} [web3] - * @param {number?} [chainId] - * @return {Promise} - */ -export async function calculateBuyPrice( - accessDetails: AccessDetails, - chainId?: number, - web3?: Web3 -): Promise { - if (!web3 && !chainId) - throw new Error("web3 and chainId can't be undefined at the same time!") - - if (!web3) { - web3 = await getDummyWeb3(chainId) - } - - const pool = new Pool(web3) - - const estimatedPrice = await pool.getAmountInExactOut( - accessDetails.addressOrId, - accessDetails.baseToken.address, - accessDetails.datatoken.address, - '1', - consumeMarketPoolSwapFee, - accessDetails.baseToken.decimals, - accessDetails.datatoken.decimals - ) - - return estimatedPrice -} - -export async function buyDtFromPool( - accessDetails: AccessDetails, - accountId: string, - web3: Web3 -): Promise { - const pool = new Pool(web3) - // we need to calculate the actual price to buy one datatoken - const dtPrice = await calculateBuyPrice(accessDetails, null, web3) - const approveTx = await approve( - web3, - accountId, - accessDetails.baseToken.address, - accessDetails.addressOrId, - dtPrice.tokenAmount, - false, - accessDetails.baseToken.decimals - ) - if (!approveTx) { - return - } - const result = await pool.swapExactAmountOut( - accountId, - accessDetails.addressOrId, - { - marketFeeAddress, - tokenIn: accessDetails.baseToken.address, - tokenOut: accessDetails.datatoken.address, - tokenInDecimals: accessDetails.baseToken.decimals, - tokenOutDecimals: accessDetails.datatoken.decimals - }, - { - // this is just to be safe - maxAmountIn: new Decimal(dtPrice.tokenAmount).mul(10).toString(), - swapMarketFee: consumeMarketPoolSwapFee, - tokenAmountOut: '1' - } - ) - - return result -} - -/** - * 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 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 -} - -/** - * 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 tokenLiquidityD = new Decimal(tokenLiquidity) - const poolSupplyD = new Decimal(poolSupply) - const poolShareAmountD = new Decimal(poolShareAmount).mul(2) - const newPoolSupply = poolSupplyD.sub(poolShareAmountD) - const poolRatio = newPoolSupply.div(poolSupplyD) - - const tokenOutRatio = new Decimal(1).sub(poolRatio) - const newTokenBalanceOut = tokenLiquidityD.mul(tokenOutRatio) - return newTokenBalanceOut.toString() -} - -/** - * 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, - tokenDecimals: number, - shares: string, - chainId: number -): Promise { - // we only use the dummyWeb3 connection here - const web3 = await getDummyWeb3(chainId) - - const poolInstance = new Pool(web3) - // get shares VL in ocean - const amountBaseToken = await poolInstance.calcSingleOutGivenPoolIn( - pool, - tokenAddress, - shares, - 18, - tokenDecimals - ) - - return amountBaseToken -} diff --git a/src/@utils/provider.ts b/src/@utils/provider.ts index e2691950b..552b73ee0 100644 --- a/src/@utils/provider.ts +++ b/src/@utils/provider.ts @@ -8,7 +8,6 @@ import { ProviderComputeInitializeResults, ProviderInstance } from '@oceanprotocol/lib' -import { AssetExtended } from 'src/@types/AssetExtended' import Web3 from 'web3' import { getValidUntilTime } from './compute' diff --git a/src/@utils/subgraph.ts b/src/@utils/subgraph.ts index 78df23fca..dd7a3bff1 100644 --- a/src/@utils/subgraph.ts +++ b/src/@utils/subgraph.ts @@ -1,23 +1,11 @@ import { gql, OperationResult, TypedDocumentNode, OperationContext } from 'urql' -import { Asset, LoggerInstance } from '@oceanprotocol/lib' +import { LoggerInstance } from '@oceanprotocol/lib' import { getUrqlClientInstance } from '@context/UrqlProvider' import { getOceanConfig } from './ocean' -import { AssetPoolPrice } from '../@types/subgraph/AssetPoolPrice' import { AssetPreviousOrder } from '../@types/subgraph/AssetPreviousOrder' -import { - HighestLiquidityAssets_pools as HighestLiquidityAssetsPool, - HighestLiquidityAssets as HighestLiquidityGraphAssets -} from '../@types/subgraph/HighestLiquidityAssets' -import { - PoolShares as PoolSharesList, - PoolShares_poolShares as PoolShare -} from '../@types/subgraph/PoolShares' 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 { calcSingleOutGivenPoolIn } from './pool' -import Decimal from 'decimal.js' -import { MAX_DECIMALS } from './constants' + import { getPublishedAssets, getTopPublishers } from '@utils/aquarius' export interface UserLiquidity { price: string @@ -28,24 +16,6 @@ export interface PriceList { [key: string]: string } -const AssetPoolPriceQuery = gql` - query AssetPoolPrice($datatokenAddress: String) { - pools(where: { datatoken: $datatokenAddress }) { - id - spotPrice - datatoken { - address - symbol - } - baseToken { - symbol - } - datatokenLiquidity - baseTokenLiquidity - } - } -` - const PreviousOrderQuery = gql` query AssetPreviousOrder($id: String!, $account: String!) { orders( @@ -59,84 +29,6 @@ const PreviousOrderQuery = gql` } } ` -const HighestLiquidityAssets = gql` - query HighestLiquidityAssets { - pools( - where: { datatokenLiquidity_gte: 1 } - orderBy: baseTokenLiquidity - orderDirection: desc - first: 15 - ) { - id - datatoken { - address - } - baseToken { - symbol - } - baseTokenLiquidity - datatokenLiquidity - } - } -` - -const UserSharesQuery = gql` - query UserSharesQuery($user: String, $pools: [String!]) { - poolShares(where: { user: $user, pool_in: $pools }) { - id - shares - user { - id - } - pool { - id - datatoken { - address - symbol - } - baseToken { - address - symbol - } - datatokenLiquidity - baseTokenLiquidity - totalShares - spotPrice - createdTimestamp - } - } - } -` - -const userPoolSharesQuery = gql` - query PoolShares($user: String) { - poolShares(where: { user: $user, shares_gt: 0.001 }, first: 1000) { - id - shares - user { - id - } - pool { - id - datatoken { - id - address - symbol - } - baseToken { - id - address - symbol - } - baseTokenLiquidity - datatokenLiquidity - totalShares - spotPrice - createdTimestamp - } - } - } -` const UserTokenOrders = gql` query OrdersData($user: String!) { @@ -163,24 +55,6 @@ const UserTokenOrders = gql` } ` -const UserSalesQuery = gql` - query UserSalesQuery($user: ID!) { - users(where: { id: $user }) { - id - totalSales - } - } -` - -const TopSalesQuery = gql` - query TopSalesQuery { - users(first: 20, orderBy: totalSales, orderDirection: desc) { - id - totalSales - } - } -` - const OpcFeesQuery = gql` query OpcFeesQuery($id: ID!) { opc(id: $id) { @@ -289,99 +163,6 @@ export async function getPreviousOrders( } } -export async function getSpotPrice(asset: Asset): Promise { - const poolVariables = { - datatokenAddress: asset?.services[0].datatokenAddress.toLowerCase() - } - const queryContext = getQueryContext(Number(asset.chainId)) - - const poolPriceResponse: OperationResult = await fetchData( - AssetPoolPriceQuery, - poolVariables, - queryContext - ) - - return poolPriceResponse.data.pools[0].spotPrice -} - -export async function getHighestLiquidityDatatokens( - chainIds: number[] -): Promise { - const dtList: string[] = [] - let highestLiquidityAssets: HighestLiquidityAssetsPool[] = [] - - for (const chain of chainIds) { - const queryContext = getQueryContext(Number(chain)) - const fetchedPools: OperationResult = - await fetchData(HighestLiquidityAssets, null, queryContext) - highestLiquidityAssets = highestLiquidityAssets.concat( - fetchedPools?.data?.pools - ) - } - highestLiquidityAssets.sort( - (a, b) => b.baseTokenLiquidity - a.baseTokenLiquidity - ) - - for (let i = 0; i < highestLiquidityAssets.length; i++) { - if (!highestLiquidityAssets[i]?.datatoken?.address) continue - dtList.push(highestLiquidityAssets[i].datatoken.address) - } - return dtList -} - -export async function getAccountLiquidityInOwnAssets( - accountId: string, - chainIds: number[], - pools: string[] -): Promise { - const queryVariables = { - user: accountId.toLowerCase(), - pools - } - const results: PoolSharesList[] = await fetchDataForMultipleChains( - UserSharesQuery, - queryVariables, - chainIds - ) - let totalLiquidity = new Decimal(0) - - for (const result of results) { - for (const poolShare of result.poolShares) { - const poolUserLiquidity = calcSingleOutGivenPoolIn( - poolShare.pool.baseTokenLiquidity, - poolShare.pool.totalShares, - poolShare.shares - ) - - totalLiquidity = totalLiquidity.add(new Decimal(poolUserLiquidity)) - } - } - return totalLiquidity.toDecimalPlaces(MAX_DECIMALS).toString() -} - -export async function getPoolSharesData( - accountId: string, - chainIds: number[] -): Promise { - const variables = { user: accountId?.toLowerCase() } - const data: PoolShare[] = [] - try { - const result = await fetchDataForMultipleChains( - userPoolSharesQuery, - variables, - chainIds - ) - for (let i = 0; i < result.length; i++) { - result[i].poolShares.forEach((poolShare: PoolShare) => { - data.push(poolShare) - }) - } - return data - } catch (error) { - LoggerInstance.error('Error getPoolSharesData: ', error.message) - } -} - export async function getUserTokenOrders( accountId: string, chainIds: number[] diff --git a/src/components/@shared/AssetList/index.tsx b/src/components/@shared/AssetList/index.tsx index b9e2b3c15..6fa0cc51e 100644 --- a/src/components/@shared/AssetList/index.tsx +++ b/src/components/@shared/AssetList/index.tsx @@ -6,9 +6,6 @@ import classNames from 'classnames/bind' import Loader from '@shared/atoms/Loader' import { useUserPreferences } from '@context/UserPreferences' import { useIsMounted } from '@hooks/useIsMounted' -// not sure why this import is required -import { AssetExtended } from 'src/@types/AssetExtended' -import { Asset } from '@oceanprotocol/lib' import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing' import { useWeb3 } from '@context/Web3' @@ -23,7 +20,7 @@ function LoaderArea() { } declare type AssetListProps = { - assets: Asset[] + assets: AssetExtended[] showPagination: boolean page?: number totalPages?: number @@ -51,6 +48,7 @@ export default function AssetList({ useEffect(() => { if (!assets) return + setAssetsWithPrices(assets as AssetExtended[]) setLoading(false) async function fetchPrices() { @@ -58,7 +56,7 @@ export default function AssetList({ assets, accountId || '' ) - if (!isMounted()) return + if (!isMounted() || !assetsWithPrices) return setAssetsWithPrices([...assetsWithPrices]) } fetchPrices() diff --git a/src/components/@shared/AssetTeaser/AssetTeaser.tsx b/src/components/@shared/AssetTeaser/AssetTeaser.tsx index d6b2eb054..d1dbeea34 100644 --- a/src/components/@shared/AssetTeaser/AssetTeaser.tsx +++ b/src/components/@shared/AssetTeaser/AssetTeaser.tsx @@ -8,7 +8,6 @@ import AssetType from '@shared/AssetType' import NetworkName from '@shared/NetworkName' import styles from './AssetTeaser.module.css' import { getServiceByName } from '@utils/ddo' -import { AssetExtended } from 'src/@types/AssetExtended' declare type AssetTeaserProps = { asset: AssetExtended diff --git a/src/components/@shared/ButtonBuy/index.tsx b/src/components/@shared/ButtonBuy/index.tsx index 8d321d610..f8979bedb 100644 --- a/src/components/@shared/ButtonBuy/index.tsx +++ b/src/components/@shared/ButtonBuy/index.tsx @@ -10,7 +10,6 @@ interface ButtonBuyProps { hasDatatoken: boolean dtSymbol: string dtBalance: string - datasetLowPoolLiquidity: boolean assetType: string assetTimeout: string isConsumable: boolean @@ -19,7 +18,6 @@ interface ButtonBuyProps { hasDatatokenSelectedComputeAsset?: boolean dtSymbolSelectedComputeAsset?: string dtBalanceSelectedComputeAsset?: string - selectedComputeAssetLowPoolLiquidity?: boolean selectedComputeAssetType?: string isBalanceSufficient: boolean isLoading?: boolean @@ -39,7 +37,6 @@ function getConsumeHelpText( dtSymbol: string, hasDatatoken: boolean, hasPreviousOrder: boolean, - lowPoolLiquidity: boolean, assetType: string, isConsumable: boolean, isBalanceSufficient: boolean, @@ -52,11 +49,9 @@ function getConsumeHelpText( ? `You bought this ${assetType} already allowing you to use it without paying again.` : hasDatatoken ? `You own ${dtBalance} ${dtSymbol} allowing you to use this data set by spending 1 ${dtSymbol}, but without paying OCEAN again.` - : lowPoolLiquidity - ? `There are not enought ${dtSymbol} available in the pool for the transaction to take place` : isBalanceSufficient === false ? 'You do not have enough OCEAN in your wallet to purchase this asset.' - : `For using this ${assetType}, you will buy 1 ${dtSymbol} and immediately spend it back to the publisher and pool.` + : `For using this ${assetType}, you will buy 1 ${dtSymbol} and immediately spend it back to the publisher.` return text } @@ -65,16 +60,14 @@ function getComputeAssetHelpText( hasDatatoken: boolean, dtSymbol: string, dtBalance: string, - lowPoolLiquidity: boolean, - assetType: string, isConsumable: boolean, consumableFeedback: string, isBalanceSufficient: boolean, hasPreviousOrderSelectedComputeAsset?: boolean, hasDatatokenSelectedComputeAsset?: boolean, + assetType?: string, dtSymbolSelectedComputeAsset?: string, dtBalanceSelectedComputeAsset?: string, - selectedComputeAssettLowPoolLiquidity?: boolean, selectedComputeAssetType?: string, isAlgorithmConsumable?: boolean, hasProviderFee?: boolean @@ -84,12 +77,12 @@ function getComputeAssetHelpText( dtSymbol, hasDatatoken, hasPreviousOrder, - lowPoolLiquidity, assetType, isConsumable, isBalanceSufficient, consumableFeedback ) + const computeAlgoHelpText = (!dtSymbolSelectedComputeAsset && !dtBalanceSelectedComputeAsset) || isConsumable === false || @@ -99,19 +92,13 @@ function getComputeAssetHelpText( ? `You already bought the selected ${selectedComputeAssetType}, allowing you to use it without paying again.` : hasDatatokenSelectedComputeAsset ? `You own ${dtBalanceSelectedComputeAsset} ${dtSymbolSelectedComputeAsset} allowing you to use the selected ${selectedComputeAssetType} by spending 1 ${dtSymbolSelectedComputeAsset}, but without paying OCEAN again.` - : selectedComputeAssettLowPoolLiquidity - ? `There are not enought ${dtSymbolSelectedComputeAsset} available in the pool for the transaction to take place` : isBalanceSufficient === false ? '' - : `Additionally, you will buy 1 ${dtSymbolSelectedComputeAsset} for the ${selectedComputeAssetType} and spend it back to its publisher and pool.` + : `Additionally, you will buy 1 ${dtSymbolSelectedComputeAsset} for the ${selectedComputeAssetType} and spend it back to its publisher.` const providerFeeHelpText = hasProviderFee ? 'In order to start the job you also need to pay the fees for renting the c2d resources.' : 'C2D resources required to start the job are available, no payment required for those fees.' - const computeHelpText = selectedComputeAssettLowPoolLiquidity - ? computeAlgoHelpText - : lowPoolLiquidity - ? computeAssetHelpText - : `${computeAssetHelpText} ${computeAlgoHelpText} ${providerFeeHelpText}` + const computeHelpText = `${computeAssetHelpText} ${computeAlgoHelpText} ${providerFeeHelpText}` return computeHelpText } @@ -122,7 +109,6 @@ export default function ButtonBuy({ hasDatatoken, dtSymbol, dtBalance, - datasetLowPoolLiquidity, assetType, assetTimeout, isConsumable, @@ -132,7 +118,6 @@ export default function ButtonBuy({ hasDatatokenSelectedComputeAsset, dtSymbolSelectedComputeAsset, dtBalanceSelectedComputeAsset, - selectedComputeAssetLowPoolLiquidity, selectedComputeAssetType, onClick, stepText, @@ -180,7 +165,6 @@ export default function ButtonBuy({ dtSymbol, hasDatatoken, hasPreviousOrder, - datasetLowPoolLiquidity, assetType, isConsumable, isBalanceSufficient, @@ -191,16 +175,14 @@ export default function ButtonBuy({ hasDatatoken, dtSymbol, dtBalance, - datasetLowPoolLiquidity, - assetType, isConsumable, consumableFeedback, isBalanceSufficient, hasPreviousOrderSelectedComputeAsset, hasDatatokenSelectedComputeAsset, + assetType, dtSymbolSelectedComputeAsset, dtBalanceSelectedComputeAsset, - selectedComputeAssetLowPoolLiquidity, selectedComputeAssetType, isAlgorithmConsumable, hasProviderFee diff --git a/src/components/@shared/PoolTransactions/Title.module.css b/src/components/@shared/PoolTransactions/Title.module.css deleted file mode 100644 index bb3bb7d69..000000000 --- a/src/components/@shared/PoolTransactions/Title.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.titleText { - white-space: pre; -} diff --git a/src/components/@shared/PoolTransactions/Title.tsx b/src/components/@shared/PoolTransactions/Title.tsx deleted file mode 100644 index e827fb792..000000000 --- a/src/components/@shared/PoolTransactions/Title.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useState, useEffect, ReactElement } from 'react' -import { PoolTransaction } from '.' -import { useUserPreferences } from '@context/UserPreferences' -import ExplorerLink from '@shared/ExplorerLink' -import { formatPrice } from '@shared/Price/PriceUnit' -import styles from './Title.module.css' - -function getTitle(row: PoolTransaction, locale: string) { - let title = '' - - switch (row.type) { - case 'SWAP': { - const { datatoken, baseToken, datatokenValue, baseTokenValue } = row - - const outToken = - (datatokenValue < 0 && datatoken) || (baseTokenValue < 0 && baseToken) - const outTokenValue = - (datatokenValue < 0 && datatokenValue) || - (baseTokenValue < 0 && baseTokenValue) - const outTokenSymbol = outToken?.symbol - - const inToken = - (datatokenValue > 0 && datatoken) || (baseTokenValue > 0 && baseToken) - const inTokenValue = - (datatokenValue > 0 && datatokenValue) || - (baseTokenValue > 0 && baseTokenValue) - const inTokenSymbol = inToken?.symbol - - title += `Swap ${formatPrice( - Math.abs(inTokenValue).toString(), - locale - )}${inTokenSymbol} for ${formatPrice( - Math.abs(outTokenValue).toString(), - locale - )}${outTokenSymbol}` - - break - } - case 'SETUP': { - const firstToken = row.baseToken - const firstTokenSymbol = firstToken?.symbol - title += `Create pool with ${formatPrice( - Math.abs(row.baseTokenValue).toString(), - locale - )}${firstTokenSymbol}` - break - } - case 'JOIN': - case 'EXIT': { - const tokenMoved = - Math.abs(row.baseTokenValue) > 0 ? row.baseToken : row.datatoken - const tokenValueMoved = - Math.abs(row.baseTokenValue) > 0 - ? row.baseTokenValue - : row.datatokenValue - const tokenSymbol = tokenMoved.symbol - - title += `${row.type === 'JOIN' ? 'Add' : 'Remove'} ${formatPrice( - Math.abs(tokenValueMoved).toString(), - locale - )}${tokenSymbol}` - - break - } - } - - return title -} - -export default function Title({ row }: { row: PoolTransaction }): ReactElement { - const [title, setTitle] = useState() - const { locale } = useUserPreferences() - - useEffect(() => { - if (!locale || !row) return - - const title = getTitle(row, locale) - setTitle(title) - }, [row, locale]) - - return title ? ( - - {title} - - ) : null -} diff --git a/src/components/@shared/PoolTransactions/index.module.css b/src/components/@shared/PoolTransactions/index.module.css deleted file mode 100644 index 197568bba..000000000 --- a/src/components/@shared/PoolTransactions/index.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.time { - color: var(--color-secondary); -} diff --git a/src/components/@shared/PoolTransactions/index.tsx b/src/components/@shared/PoolTransactions/index.tsx deleted file mode 100644 index 8e9c870e5..000000000 --- a/src/components/@shared/PoolTransactions/index.tsx +++ /dev/null @@ -1,264 +0,0 @@ -import React, { ReactElement, useCallback, useEffect, useState } from 'react' -import Time from '@shared/atoms/Time' -import Table, { TableOceanColumn } from '@shared/atoms/Table' -import AssetTitle from '@shared/AssetList/AssetListTitle' -import { useUserPreferences } from '@context/UserPreferences' -import { gql } from 'urql' -import { TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions } from '../../../@types/subgraph/TransactionHistory' -import { fetchDataForMultipleChains } from '@utils/subgraph' -import NetworkName from '@shared/NetworkName' -import { getAssetsFromDtList } from '@utils/aquarius' -import { getAsset } from '../../Profile/History/PoolShares/_utils' -import { CancelToken } from 'axios' -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 - -const txHistoryQueryByPool = gql` - query TransactionHistoryByPool($user: String, $pool: String) { - poolTransactions( - orderBy: timestamp - orderDirection: desc - where: { pool: $pool, user: $user } - first: 1000 - ) { - baseToken { - symbol - address - } - baseTokenValue - datatoken { - symbol - address - } - datatokenValue - type - tx - timestamp - pool { - datatoken { - id - } - id - } - } - } -` -const txHistoryQuery = gql` - query TransactionHistory($user: String) { - poolTransactions( - orderBy: timestamp - orderDirection: desc - where: { user: $user } - first: 1000 - ) { - baseToken { - symbol - address - } - baseTokenValue - datatoken { - symbol - address - } - datatokenValue - type - tx - timestamp - pool { - datatoken { - id - } - id - } - } - } -` - -export interface PoolTransaction extends TransactionHistoryPoolTransactions { - networkId: number - asset: Asset -} - -const columns: TableOceanColumn[] = [ - { - name: 'Title', - selector: (row) => - }, - { - name: 'Data Set', - selector: (row) => <AssetTitle asset={row.asset} /> - }, - { - name: 'Network', - selector: (row) => <NetworkName networkId={row.networkId} />, - maxWidth: '12rem' - }, - { - name: 'Time', - selector: (row) => ( - <Time - className={styles.time} - date={row.timestamp.toString()} - relative - isUnix - /> - ), - maxWidth: '10rem' - } -] - -// hack! if we use a function to omit one field this will display a strange refresh to the enduser for each row -const columnsMinimal = [columns[0], columns[3]] - -export default function PoolTransactions({ - poolAddress, - poolChainId, - minimal, - accountId -}: { - poolAddress?: string - poolChainId?: number - minimal?: boolean - accountId: string -}): ReactElement { - const { chainIds } = useUserPreferences() - const { appConfig } = useMarketMetadata() - const cancelToken = useCancelToken() - - const [transactions, setTransactions] = useState<PoolTransaction[]>() - const [isLoading, setIsLoading] = useState<boolean>(false) - const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>() - const [data, setData] = useState<PoolTransaction[]>() - - const getPoolTransactionData = useCallback(async () => { - const variables = { - user: accountId?.toLowerCase(), - pool: poolAddress?.toLowerCase() - } - const transactions: PoolTransaction[] = [] - const result = await fetchDataForMultipleChains( - poolAddress ? txHistoryQueryByPool : txHistoryQuery, - variables, - poolAddress ? [poolChainId] : chainIds - ) - - for (let i = 0; i < result.length; i++) { - result[i].poolTransactions.forEach((poolTransaction: PoolTransaction) => { - transactions.push(poolTransaction) - }) - } - - if (JSON.stringify(data) !== JSON.stringify(transactions)) { - setData(transactions) - } - }, [accountId, chainIds, data, poolAddress, poolChainId]) - - const getPoolTransactions = useCallback( - async (cancelToken: CancelToken) => { - if (!data) { - return - } - const poolTransactions: PoolTransaction[] = [] - let dtList: string[] = [] - - dtList = [...new Set(data.map((item) => item.pool.datatoken.id))] - if (dtList.length === 0) { - setTransactions([]) - setIsLoading(false) - return - } - const ddoList = !minimal - ? await getAssetsFromDtList(dtList, chainIds, cancelToken) - : [] - for (let i = 0; i < data.length; i++) { - poolTransactions.push({ - ...data[i], - networkId: !minimal - ? getAsset(ddoList, data[i].pool.datatoken.id)?.chainId - : poolChainId, - asset: !minimal ? getAsset(ddoList, data[i].pool.datatoken.id) : null - }) - } - const sortedTransactions = poolTransactions.sort( - (a, b) => b.timestamp - a.timestamp - ) - - setTransactions(sortedTransactions) - setIsLoading(false) - }, - [data, minimal, chainIds, poolChainId] - ) - - // - // Get data, periodically - // - useEffect(() => { - if (!appConfig?.metadataCacheUri) return - - async function getTransactions() { - try { - await getPoolTransactionData() - if (dataFetchInterval) return - const interval = setInterval(async () => { - await getPoolTransactionData() - }, REFETCH_INTERVAL) - setDataFetchInterval(interval) - } catch (error) { - LoggerInstance.error( - 'Error fetching pool transactions: ', - error.message - ) - } - } - getTransactions() - - return () => { - clearInterval(dataFetchInterval) - } - }, [getPoolTransactionData, dataFetchInterval, appConfig.metadataCacheUri]) - - // - // Transform to final transactions - // - useEffect(() => { - if (!cancelToken()) return - async function transformData() { - try { - setIsLoading(true) - await getPoolTransactions(cancelToken()) - } catch (error) { - LoggerInstance.error( - 'Error fetching pool transactions: ', - error.message - ) - } - } - transformData() - - return () => { - cancelToken() - } - }, [cancelToken, getPoolTransactions]) - - return accountId ? ( - <Table - columns={minimal ? columnsMinimal : columns} - data={transactions} - isLoading={isLoading} - noTableHead={minimal} - dense={minimal} - pagination={ - minimal ? transactions?.length >= 4 : transactions?.length >= 9 - } - paginationPerPage={minimal ? 5 : 10} - emptyMessage={chainIds.length === 0 ? 'No network selected' : null} - /> - ) : ( - <div>Please connect your Web3 wallet.</div> - ) -} diff --git a/src/components/@shared/Price/Conversion.module.css b/src/components/@shared/Price/Conversion.module.css index 0d1849ae8..e7df076f4 100644 --- a/src/components/@shared/Price/Conversion.module.css +++ b/src/components/@shared/Price/Conversion.module.css @@ -6,9 +6,6 @@ font-weight: var(--font-weight-base); } -.removeTvlPadding { - padding-left: 0 !important; -} /* fiat currency symbol */ .conversion strong span { font-weight: var(--font-weight-base); diff --git a/src/components/@shared/Price/PriceUnit.tsx b/src/components/@shared/Price/PriceUnit.tsx index 7885871b2..65db01726 100644 --- a/src/components/@shared/Price/PriceUnit.tsx +++ b/src/components/@shared/Price/PriceUnit.tsx @@ -33,16 +33,13 @@ export default function PriceUnit({ return ( <div className={`${styles.price} ${styles[size]} ${className}`}> - {type && type === 'free' ? ( - <div> Free </div> + {type === 'free' ? ( + <div>Free</div> ) : ( <> <div> {Number.isNaN(Number(price)) ? '-' : formatPrice(price, locale)}{' '} <span className={styles.symbol}>{symbol}</span> - {type && type === 'dynamic' && ( - <Badge label="pool" className={styles.badge} /> - )} </div> {conversion && <Conversion price={price} />} </> diff --git a/src/components/@shared/Price/index.module.css b/src/components/@shared/Price/index.module.css deleted file mode 100644 index 7b0cb4b4e..000000000 --- a/src/components/@shared/Price/index.module.css +++ /dev/null @@ -1,4 +0,0 @@ -.empty { - color: var(--color-secondary); - font-weight: var(--font-weight-bold); -} diff --git a/src/components/@shared/Price/index.tsx b/src/components/@shared/Price/index.tsx index 3ac519ae3..cff963a62 100644 --- a/src/components/@shared/Price/index.tsx +++ b/src/components/@shared/Price/index.tsx @@ -1,9 +1,5 @@ import React, { ReactElement } from 'react' -import styles from './index.module.css' -import Loader from '../atoms/Loader' -import Tooltip from '../atoms/Tooltip' import PriceUnit from './PriceUnit' -import { AccessDetails, OrderPriceAndFees } from 'src/@types/Price' export default function Price({ accessDetails, @@ -18,7 +14,10 @@ export default function Price({ conversion?: boolean size?: 'small' | 'mini' | 'large' }): ReactElement { - return accessDetails?.price || accessDetails?.type === 'free' ? ( + const isSupported = + accessDetails?.type === 'fixed' || accessDetails?.type === 'free' + + return isSupported ? ( <PriceUnit price={`${orderPriceAndFees?.price || accessDetails?.price}`} symbol={accessDetails.baseToken?.symbol} @@ -27,18 +26,5 @@ export default function Price({ conversion={conversion} type={accessDetails.type} /> - ) : !accessDetails || accessDetails?.type === '' ? ( - <div className={styles.empty}> - No price set{' '} - <Tooltip content="No pricing mechanism has been set on this asset yet." /> - </div> - ) : ( - // TODO: Hacky hack, put back some check for low liquidity - // ) : price.isConsumable !== 'true' ? ( - // <div className={styles.empty}> - // Low liquidity{' '} - // <Tooltip content="This pool does not have enough liquidity for using this data set." /> - // </div> - <Loader message="Retrieving price..." /> - ) + ) : null } diff --git a/src/components/@shared/Token/index.module.css b/src/components/@shared/Token/index.module.css deleted file mode 100644 index d8526518f..000000000 --- a/src/components/@shared/Token/index.module.css +++ /dev/null @@ -1,54 +0,0 @@ -.token { - font-weight: var(--font-weight-bold); - white-space: nowrap; -} - -.symbol { - font-weight: var(--font-weight-base); - color: var(--color-secondary); - font-size: var(--font-size-base); -} - -.icon { - display: inline-block; - border: 1px solid var(--border-color); - border-radius: 50%; - padding: 0.3rem; - vertical-align: middle; - margin-right: calc(var(--spacer) / 8); - margin-top: -0.2rem; - background: var(--background-body); -} - -.icon svg { - width: var(--font-size-base); - height: var(--font-size-base); -} - -.conversion { - composes: token; - margin-bottom: 0; - font-weight: var(--font-weight-base) !important; - font-size: var(--font-size-small); - padding-left: var(--font-size-base); - padding-top: calc(var(--spacer) / 10); -} - -.conversion strong { - font-size: var(--font-size-base); - color: var(--font-color-heading); - line-height: 1; -} - -/* Data Token Icon Style */ -.icon:not([class*='OCEAN']) path { - fill: var(--brand-violet); -} - -.noIcon { - opacity: 0; -} - -.token.mini { - font-size: var(--font-size-mini); -} diff --git a/src/components/@shared/Token/index.tsx b/src/components/@shared/Token/index.tsx deleted file mode 100644 index 992dc35a4..000000000 --- a/src/components/@shared/Token/index.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React, { ReactElement } from 'react' -import styles from './index.module.css' -import PriceUnit from '@shared/Price/PriceUnit' -import Logo from '@shared/atoms/Logo' -import Conversion from '@shared/Price/Conversion' - -export default function Token({ - symbol, - balance, - conversion, - noIcon, - size -}: { - symbol: string - balance: string - conversion?: boolean - noIcon?: boolean - size?: 'small' | 'mini' -}): ReactElement { - return ( - <> - <div className={`${styles.token} ${size ? styles[size] : ''}`}> - <figure - className={`${styles.icon} ${symbol} ${noIcon ? styles.noIcon : ''}`} - > - <Logo noWordmark /> - </figure> - <PriceUnit price={balance} symbol={symbol} size={size} /> - </div> - {conversion && ( - <Conversion price={balance} className={`${styles.conversion}`} /> - )} - </> - ) -} diff --git a/src/components/@shared/TokenApproval/ButtonApprove.tsx b/src/components/@shared/TokenApproval/ButtonApprove.tsx deleted file mode 100644 index 9583d494c..000000000 --- a/src/components/@shared/TokenApproval/ButtonApprove.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { ReactElement } from 'react' -import Button from '@shared/atoms/Button' -import Loader from '@shared/atoms/Loader' -import { useUserPreferences } from '@context/UserPreferences' -import Tooltip from '@shared/atoms/Tooltip' -import content from '../../../../content/price.json' - -export function ButtonApprove({ - amount, - tokenSymbol, - approveTokens, - isLoading -}: { - amount: string - tokenSymbol: string - approveTokens: (amount: string) => void - isLoading: boolean -}): ReactElement { - const { infiniteApproval } = useUserPreferences() - - return isLoading ? ( - <Loader message={`Approving ${tokenSymbol}...`} /> - ) : infiniteApproval ? ( - <Button - style="primary" - size="small" - disabled={parseInt(amount) < 1} - onClick={() => approveTokens(`${2 ** 53 - 1}`)} - > - Approve {tokenSymbol}{' '} - <Tooltip - content={content.pool.tooltips.approveInfinite.replace( - 'COIN', - tokenSymbol - )} - /> - </Button> - ) : ( - <Button style="primary" size="small" onClick={() => approveTokens(amount)}> - Approve {amount} {tokenSymbol} - <Tooltip - content={content.pool.tooltips.approveSpecific.replace( - 'COIN', - tokenSymbol - )} - /> - </Button> - ) -} diff --git a/src/components/@shared/TokenApproval/index.tsx b/src/components/@shared/TokenApproval/index.tsx deleted file mode 100644 index ce5ca2630..000000000 --- a/src/components/@shared/TokenApproval/index.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { ReactElement, useCallback, useEffect, useState } from 'react' -import { useAsset } from '@context/Asset' -import { useWeb3 } from '@context/Web3' -import Decimal from 'decimal.js' -import { ButtonApprove } from './ButtonApprove' -import { allowance, approve, LoggerInstance } from '@oceanprotocol/lib' - -export default function TokenApproval({ - actionButton, - disabled, - amount, - tokenAddress, - tokenSymbol, - setSubmitting, - setIsTokenApproved -}: { - actionButton: JSX.Element - disabled: boolean - amount: string - tokenAddress: string - tokenSymbol: string - setSubmitting?: (isSubmitting: boolean) => void - setIsTokenApproved: (isApproved: boolean) => void -}): ReactElement { - const { asset, isAssetNetwork } = useAsset() - const [tokenApproved, setTokenApproved] = useState(false) - const [loading, setLoading] = useState(false) - const { web3, accountId } = useWeb3() - - const spender = asset?.accessDetails?.addressOrId - - const checkTokenApproval = useCallback(async () => { - if (!web3 || !tokenAddress || !spender || !isAssetNetwork || !amount) return - - const allowanceValue = await allowance( - web3, - tokenAddress, - accountId, - spender - ) - LoggerInstance.log(`[token approval] allowanceValue: ${allowanceValue}`) - - if (!allowanceValue) return - - new Decimal(amount).greaterThan(new Decimal('0')) && - setTokenApproved( - new Decimal(allowanceValue).greaterThanOrEqualTo(new Decimal(amount)) - ) - setIsTokenApproved( - new Decimal(allowanceValue).greaterThanOrEqualTo(new Decimal(amount)) - ) - }, [web3, tokenAddress, spender, accountId, amount, isAssetNetwork]) - - useEffect(() => { - checkTokenApproval() - }, [checkTokenApproval]) - - async function approveTokens(amount: string) { - setLoading(true) - setSubmitting(true) - - try { - const tx = await approve(web3, accountId, tokenAddress, spender, amount) - LoggerInstance.log(`[token approval] Approve tokens tx:`, tx) - } catch (error) { - LoggerInstance.error( - `[token approval] Approve tokens tx failed:`, - error.message - ) - } finally { - await checkTokenApproval() - setLoading(false) - setSubmitting(false) - } - } - - return ( - <> - {tokenApproved || - disabled || - amount === '0' || - amount === '' || - !amount || - typeof amount === 'undefined' ? ( - actionButton - ) : ( - <ButtonApprove - amount={amount} - tokenSymbol={tokenSymbol} - approveTokens={approveTokens} - isLoading={loading} - /> - )} - </> - ) -} diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index dff602cab..e9eb4580a 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -19,26 +19,11 @@ export default function App({ const { siteContent, appConfig } = useMarketMetadata() const { accountId } = useWeb3() const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId) - function openInNewTab() { - window - .open( - 'https://blog.oceanprotocol.com/how-to-publish-a-data-nft-f58ad2a622a9', - '_blank' - ) - .focus() - } return ( <div className={styles.app}> {siteContent?.announcement !== '' && ( - <AnnouncementBanner - text={siteContent?.announcement} - action={{ - name: 'Explore OceanONDA V4.', - style: 'link', - handleAction: openInNewTab - }} - /> + <AnnouncementBanner text={siteContent?.announcement} /> )} <Header /> diff --git a/src/components/Asset/AssetActions/Compute/AlgorithmDatasetsListForCompute.tsx b/src/components/Asset/AssetActions/Compute/AlgorithmDatasetsListForCompute.tsx index 79a7b24b4..3ef54b0bf 100644 --- a/src/components/Asset/AssetActions/Compute/AlgorithmDatasetsListForCompute.tsx +++ b/src/components/Asset/AssetActions/Compute/AlgorithmDatasetsListForCompute.tsx @@ -5,7 +5,6 @@ import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' import AssetComputeList from '@shared/AssetList/AssetComputeList' import { useCancelToken } from '@hooks/useCancelToken' import { getServiceByName } from '@utils/ddo' -import { AssetExtended } from 'src/@types/AssetExtended' export default function AlgorithmDatasetsListForCompute({ asset, @@ -19,7 +18,7 @@ export default function AlgorithmDatasetsListForCompute({ useState<AssetSelectionAsset[]>() useEffect(() => { - if (!asset) return + if (!asset || !asset?.accessDetails?.type) return async function getDatasetsAllowedForCompute() { const isCompute = Boolean(getServiceByName(asset, 'compute')) diff --git a/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx b/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx index a0f17b43a..6f144cf53 100644 --- a/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx +++ b/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx @@ -10,9 +10,7 @@ import { useAsset } from '@context/Asset' import { useWeb3 } from '@context/Web3' import content from '../../../../../content/pages/startComputeDataset.json' import { Asset } from '@oceanprotocol/lib' -import { OrderPriceAndFees } from 'src/@types/Price' import { getAccessDetails } from '@utils/accessDetailsAndPricing' -import { AssetExtended } from 'src/@types/AssetExtended' import Decimal from 'decimal.js' import { MAX_DECIMALS } from '@utils/constants' import { useMarketMetadata } from '@context/MarketMetadata' @@ -28,7 +26,6 @@ export default function FormStartCompute({ hasPreviousOrder, hasDatatoken, dtBalance, - datasetLowPoolLiquidity, assetType, assetTimeout, hasPreviousOrderSelectedComputeAsset, @@ -36,7 +33,6 @@ export default function FormStartCompute({ oceanSymbol, dtSymbolSelectedComputeAsset, dtBalanceSelectedComputeAsset, - selectedComputeAssetLowPoolLiquidity, selectedComputeAssetType, selectedComputeAssetTimeout, stepText, @@ -56,7 +52,6 @@ export default function FormStartCompute({ hasPreviousOrder: boolean hasDatatoken: boolean dtBalance: string - datasetLowPoolLiquidity: boolean assetType: string assetTimeout: string hasPreviousOrderSelectedComputeAsset?: boolean @@ -64,7 +59,6 @@ export default function FormStartCompute({ oceanSymbol?: string dtSymbolSelectedComputeAsset?: string dtBalanceSelectedComputeAsset?: string - selectedComputeAssetLowPoolLiquidity?: boolean selectedComputeAssetType?: string selectedComputeAssetTimeout?: string stepText: string @@ -180,15 +174,17 @@ export default function FormStartCompute({ state="info" text={siteContent.warning.ctd} /> - {content.form.data.map((field: FormFieldContent) => ( - <Field - key={field.name} - {...field} - options={algorithms} - component={Input} - disabled={isLoading} - /> - ))} + {content.form.data.map((field: FormFieldContent) => { + return ( + <Field + key={field.name} + {...field} + options={algorithms} + component={Input} + disabled={isLoading || isComputeButtonDisabled} + /> + ) + })} <PriceOutput hasPreviousOrder={hasPreviousOrder} @@ -221,7 +217,6 @@ export default function FormStartCompute({ hasDatatoken={hasDatatoken} dtSymbol={asset?.datatokens[0]?.symbol} dtBalance={dtBalance} - datasetLowPoolLiquidity={datasetLowPoolLiquidity} assetTimeout={assetTimeout} assetType={assetType} hasPreviousOrderSelectedComputeAsset={ @@ -230,9 +225,6 @@ export default function FormStartCompute({ hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset} dtSymbolSelectedComputeAsset={dtSymbolSelectedComputeAsset} dtBalanceSelectedComputeAsset={dtBalanceSelectedComputeAsset} - selectedComputeAssetLowPoolLiquidity={ - selectedComputeAssetLowPoolLiquidity - } selectedComputeAssetType={selectedComputeAssetType} stepText={stepText} isLoading={isLoading} diff --git a/src/components/Asset/AssetActions/AssetActionHistoryTable.module.css b/src/components/Asset/AssetActions/Compute/History.module.css similarity index 58% rename from src/components/Asset/AssetActions/AssetActionHistoryTable.module.css rename to src/components/Asset/AssetActions/Compute/History.module.css index e3e5f5503..1a654d009 100644 --- a/src/components/Asset/AssetActions/AssetActionHistoryTable.module.css +++ b/src/components/Asset/AssetActions/Compute/History.module.css @@ -1,5 +1,37 @@ +.container { + margin-left: calc(-1 * var(--spacer) / 1.5); + margin-right: calc(-1 * var(--spacer) / 1.5); + padding: calc(var(--spacer) / 1.5) calc(var(--spacer) / 1.5) + calc(var(--spacer) / 2) calc(var(--spacer) / 1.5); +} + +@media (min-width: 40rem) { + .container { + padding-left: var(--spacer); + padding-right: var(--spacer); + margin-left: calc(-1 * var(--spacer)); + margin-right: calc(-1 * var(--spacer)); + } +} + +.section { + composes: container; + + border-top: 1px solid var(--border-color); + position: relative; +} + +.section.highlight { + background: var(--background-highlight); +} + +.section:first-child { + padding-top: 0; + border-top: 0; +} + .actions { - composes: section from './Pool/Section/index.module.css'; + composes: section; margin-top: calc(var(--spacer) / 1.5); padding: calc(var(--spacer) / 1.5); background: var(--background-highlight); @@ -11,7 +43,9 @@ } .title { - composes: title from './Pool/Section/Title.module.css'; + font-size: var(--font-size-base); + margin-bottom: calc(var(--spacer) / 3); + color: var(--color-secondary); margin-bottom: 0; display: flex; align-items: center; diff --git a/src/components/Asset/AssetActions/AssetActionHistoryTable.tsx b/src/components/Asset/AssetActions/Compute/History.tsx similarity index 88% rename from src/components/Asset/AssetActions/AssetActionHistoryTable.tsx rename to src/components/Asset/AssetActions/Compute/History.tsx index 129620b89..9fa285476 100644 --- a/src/components/Asset/AssetActions/AssetActionHistoryTable.tsx +++ b/src/components/Asset/AssetActions/Compute/History.tsx @@ -1,9 +1,9 @@ import React, { ReactElement, ReactNode, useState } from 'react' import Button from '@shared/atoms/Button' -import styles from './AssetActionHistoryTable.module.css' +import styles from './History.module.css' import Caret from '@images/caret.svg' -export default function AssetActionHistoryTable({ +export default function ComputeHistory({ title, children }: { diff --git a/src/components/Asset/AssetActions/Compute/PriceOutput.tsx b/src/components/Asset/AssetActions/Compute/PriceOutput.tsx index 3cfe3f18a..f142d0385 100644 --- a/src/components/Asset/AssetActions/Compute/PriceOutput.tsx +++ b/src/components/Asset/AssetActions/Compute/PriceOutput.tsx @@ -3,7 +3,6 @@ import { useAsset } from '@context/Asset' import PriceUnit from '@shared/Price/PriceUnit' import Tooltip from '@shared/atoms/Tooltip' import styles from './PriceOutput.module.css' -import { AccessDetails } from 'src/@types/Price' import { MAX_DECIMALS } from '@utils/constants' import Decimal from 'decimal.js' @@ -17,8 +16,8 @@ interface PriceOutputProps { hasDatatokenSelectedComputeAsset: boolean algorithmConsumeDetails: AccessDetails selectedComputeAssetTimeout: string - datasetOrderPrice?: number - algoOrderPrice?: number + datasetOrderPrice?: string + algoOrderPrice?: string providerFeeAmount?: string validUntil?: string } diff --git a/src/components/Asset/AssetActions/Compute/index.module.css b/src/components/Asset/AssetActions/Compute/index.module.css index b1c7e162a..a0c880beb 100644 --- a/src/components/Asset/AssetActions/Compute/index.module.css +++ b/src/components/Asset/AssetActions/Compute/index.module.css @@ -11,6 +11,11 @@ calc(var(--spacer) * 1.5); } +.warning { + padding-bottom: 0; + border-bottom: 0; +} + .feedback { width: 100%; margin-top: calc(var(--spacer) / 2); diff --git a/src/components/Asset/AssetActions/Compute/index.tsx b/src/components/Asset/AssetActions/Compute/index.tsx index 658d90c4d..3a45f7e38 100644 --- a/src/components/Asset/AssetActions/Compute/index.tsx +++ b/src/components/Asset/AssetActions/Compute/index.tsx @@ -33,19 +33,14 @@ import { } from '@utils/compute' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute' -import AssetActionHistoryTable from '../AssetActionHistoryTable' +import ComputeHistory from './History' import ComputeJobs from '../../../Profile/History/ComputeJobs' import { useCancelToken } from '@hooks/useCancelToken' import { Decimal } from 'decimal.js' import { useAbortController } from '@hooks/useAbortController' import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing' -import { OrderPriceAndFees } from 'src/@types/Price' import { handleComputeOrder } from '@utils/order' -import { AssetExtended } from 'src/@types/AssetExtended' import { getComputeFeedback } from '@utils/feedback' -import { usePool } from '@context/Pool' -import { useMarketMetadata } from '@context/MarketMetadata' -import { getPoolData } from '@context/Pool/_utils' import { getDummyWeb3 } from '@utils/web3' import { initializeProviderForCompute } from '@utils/provider' @@ -63,8 +58,6 @@ export default function Compute({ consumableFeedback?: string }): ReactElement { const { accountId, web3 } = useWeb3() - const { getOpcFeeForToken } = useMarketMetadata() - const { poolData } = usePool() const newAbortController = useAbortController() const newCancelToken = useCancelToken() @@ -108,6 +101,8 @@ export default function Compute({ !hasAlgoAssetDatatoken && !isConsumableaAlgorithmPrice) + const isUnsupportedPricing = asset?.accessDetails?.type === 'NOT_SUPPORTED' + async function checkAssetDTBalance(asset: DDO): Promise<boolean> { if (!asset?.services[0].datatokenAddress) return const web3 = await getDummyWeb3(asset?.chainId) @@ -168,26 +163,9 @@ export default function Compute({ asset.metadata.type )[0] ) - const poolParams = - asset?.accessDetails?.type === 'dynamic' - ? { - tokenInLiquidity: poolData?.baseTokenLiquidity, - tokenOutLiquidity: poolData?.datatokenLiquidity, - tokenOutAmount: '1', - opcFee: getOpcFeeForToken( - asset?.accessDetails?.baseToken.address, - asset?.chainId - ), - lpSwapFee: poolData?.liquidityProviderSwapFee, - publishMarketSwapFee: - asset?.accessDetails?.publisherMarketOrderFee, - consumeMarketSwapFee: '0' - } - : null const datasetPriceAndFees = await getOrderPriceAndFees( asset, ZERO_ADDRESS, - poolParams, initializedProvider?.datasets?.[0]?.providerFee ) if (!datasetPriceAndFees) @@ -208,32 +186,9 @@ export default function Compute({ selectedAlgorithmAsset?.metadata?.type )[0] ) - let algoPoolParams = null - if (selectedAlgorithmAsset?.accessDetails?.type === 'dynamic') { - const response = await getPoolData( - selectedAlgorithmAsset.chainId, - selectedAlgorithmAsset.accessDetails?.addressOrId, - selectedAlgorithmAsset?.nft.owner, - accountId || '' - ) - algoPoolParams = { - tokenInLiquidity: response?.poolData?.baseTokenLiquidity, - tokenOutLiquidity: response?.poolData?.datatokenLiquidity, - tokenOutAmount: '1', - opcFee: getOpcFeeForToken( - selectedAlgorithmAsset?.accessDetails?.baseToken.address, - selectedAlgorithmAsset?.chainId - ), - lpSwapFee: response?.poolData?.liquidityProviderSwapFee, - publishMarketSwapFee: - selectedAlgorithmAsset?.accessDetails?.publisherMarketOrderFee, - consumeMarketSwapFee: '0' - } - } const algorithmOrderPriceAndFees = await getOrderPriceAndFees( selectedAlgorithmAsset, ZERO_ADDRESS, - algoPoolParams, initializedProvider.algorithm.providerFee ) if (!algorithmOrderPriceAndFees) @@ -248,11 +203,11 @@ export default function Compute({ } useEffect(() => { - if (!asset?.accessDetails || !accountId) return + if (!asset?.accessDetails || !accountId || isUnsupportedPricing) return setIsConsumablePrice(asset?.accessDetails?.isPurchasable) setValidOrderTx(asset?.accessDetails?.validOrderTx) - }, [asset?.accessDetails, accountId]) + }, [asset?.accessDetails, accountId, isUnsupportedPricing]) useEffect(() => { if (!selectedAlgorithmAsset?.accessDetails || !accountId) return @@ -276,7 +231,8 @@ export default function Compute({ }, [selectedAlgorithmAsset, accountId]) useEffect(() => { - if (!asset) return + if (!asset?.accessDetails || isUnsupportedPricing) return + getAlgorithmsForAsset(asset, newCancelToken()).then((algorithmsAssets) => { setDdoAlgorithmList(algorithmsAssets) getAlgorithmAssetSelectionList(asset, algorithmsAssets).then( @@ -285,7 +241,7 @@ export default function Compute({ } ) }) - }, [asset]) + }, [asset, isUnsupportedPricing]) // Output errors in toast UI useEffect(() => { @@ -323,13 +279,7 @@ export default function Compute({ selectedAlgorithmAsset.accessDetails.baseToken?.symbol, selectedAlgorithmAsset.accessDetails.datatoken?.symbol, selectedAlgorithmAsset.metadata.type - )[ - selectedAlgorithmAsset.accessDetails?.type === 'fixed' - ? 2 - : selectedAlgorithmAsset.accessDetails?.type === 'dynamic' - ? 1 - : 3 - ] + )[selectedAlgorithmAsset.accessDetails?.type === 'fixed' ? 2 : 3] ) const algorithmOrderTx = await handleComputeOrder( @@ -348,13 +298,7 @@ export default function Compute({ asset.accessDetails.baseToken?.symbol, asset.accessDetails.datatoken?.symbol, asset.metadata.type - )[ - asset.accessDetails?.type === 'fixed' - ? 2 - : asset.accessDetails?.type === 'dynamic' - ? 1 - : 3 - ] + )[asset.accessDetails?.type === 'fixed' ? 2 : 3] ) const datasetOrderTx = await handleComputeOrder( web3, @@ -406,17 +350,37 @@ export default function Compute({ return ( <> - <div className={styles.info}> + <div + className={`${styles.info} ${ + isUnsupportedPricing ? styles.warning : null + }`} + > <FileIcon file={file} isLoading={fileIsLoading} small /> - <Price accessDetails={asset?.accessDetails} conversion /> - </div> - - {asset.metadata.type === 'algorithm' ? ( - <> + {isUnsupportedPricing ? ( <Alert - text="This algorithm has been set to private by the publisher and can't be downloaded. You can run it against any allowed data sets though!" + text={`No pricing schema available for this asset.`} state="info" /> + ) : ( + <Price + accessDetails={asset?.accessDetails} + orderPriceAndFees={datasetOrderPriceAndFees} + conversion + size="large" + /> + )} + </div> + + {isUnsupportedPricing ? null : asset.metadata.type === 'algorithm' ? ( + <> + {asset.services[0].type === 'compute' && ( + <Alert + text={ + "This algorithm has been set to private by the publisher and can't be downloaded. You can run it against any allowed data sets though!" + } + state="info" + /> + )} <AlgorithmDatasetsListForCompute algorithmDid={asset.id} asset={asset} @@ -442,7 +406,6 @@ export default function Compute({ hasPreviousOrder={validOrderTx !== undefined} hasDatatoken={hasDatatoken} dtBalance={dtBalance} - datasetLowPoolLiquidity={!isConsumablePrice} assetType={asset?.metadata.type} assetTimeout={secondsToString(asset?.services[0].timeout)} hasPreviousOrderSelectedComputeAsset={ @@ -480,13 +443,13 @@ export default function Compute({ )} </footer> {accountId && asset?.accessDetails?.datatoken && ( - <AssetActionHistoryTable title="Your Compute Jobs"> + <ComputeHistory title="Your Compute Jobs"> <ComputeJobs minimal assetChainIds={[asset?.chainId]} refetchJobs={refetchJobs} /> - </AssetActionHistoryTable> + </ComputeHistory> )} </> ) diff --git a/src/components/Asset/AssetActions/Download.module.css b/src/components/Asset/AssetActions/Download.module.css index d6dc45629..b56cc46b1 100644 --- a/src/components/Asset/AssetActions/Download.module.css +++ b/src/components/Asset/AssetActions/Download.module.css @@ -1,11 +1,14 @@ .consume { - display: flex; - flex-wrap: wrap; + width: auto; + margin-bottom: calc(var(--spacer) / 2); + margin-top: -1rem; + margin-left: -2rem; } .info { display: flex; width: auto; + padding: 0 calc(var(--spacer) / 2) 0 calc(var(--spacer) * 1.5); } .filewrapper { diff --git a/src/components/Asset/AssetActions/Download.tsx b/src/components/Asset/AssetActions/Download.tsx index fe536b94f..7f4a05448 100644 --- a/src/components/Asset/AssetActions/Download.tsx +++ b/src/components/Asset/AssetActions/Download.tsx @@ -9,16 +9,13 @@ import AlgorithmDatasetsListForCompute from './Compute/AlgorithmDatasetsListForC import styles from './Download.module.css' import { FileInfo, LoggerInstance, ZERO_ADDRESS } from '@oceanprotocol/lib' import { order } from '@utils/order' -import { AssetExtended } from 'src/@types/AssetExtended' -import { buyDtFromPool } from '@utils/pool' import { downloadFile } from '@utils/provider' import { getOrderFeedback } from '@utils/feedback' 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' +import Alert from '@shared/atoms/Alert' export default function Download({ asset, @@ -38,7 +35,6 @@ export default function Download({ const { accountId, web3 } = useWeb3() const { getOpcFeeForToken } = useMarketMetadata() const { isInPurgatory, isAssetNetwork } = useAsset() - const { poolData } = usePool() const isMounted = useIsMounted() const [isDisabled, setIsDisabled] = useState(true) @@ -50,63 +46,50 @@ export default function Download({ const [orderPriceAndFees, setOrderPriceAndFees] = useState<OrderPriceAndFees>() - useEffect(() => { - if (!asset?.accessDetails) return + const isUnsupportedPricing = asset?.accessDetails?.type === 'NOT_SUPPORTED' - asset?.accessDetails?.isOwned && setIsOwned(asset?.accessDetails?.isOwned) - asset?.accessDetails?.validOrderTx && + useEffect(() => { + if (!asset?.accessDetails || isUnsupportedPricing) return + + asset.accessDetails.isOwned && setIsOwned(asset?.accessDetails?.isOwned) + asset.accessDetails.validOrderTx && setValidOrderTx(asset?.accessDetails?.validOrderTx) // get full price and fees async function init() { if ( - asset?.accessDetails?.addressOrId === ZERO_ADDRESS || - asset?.accessDetails?.type === 'free' || - (!poolData && asset?.accessDetails?.type === 'dynamic') || + asset.accessDetails.addressOrId === ZERO_ADDRESS || + asset.accessDetails.type === 'free' || isLoading ) return - !orderPriceAndFees && setIsLoading(true) - setStatusText('Refreshing price') - // this is needed just for pool - const paramsForPool: CalcInGivenOutParams = { - tokenInLiquidity: poolData?.baseTokenLiquidity, - tokenOutLiquidity: poolData?.datatokenLiquidity, - tokenOutAmount: '1', - opcFee: getOpcFeeForToken( - asset?.accessDetails?.baseToken.address, - asset?.chainId - ), - lpSwapFee: poolData?.liquidityProviderSwapFee, - publishMarketSwapFee: asset?.accessDetails?.publisherMarketOrderFee, - consumeMarketSwapFee: '0' - } - const _orderPriceAndFees = await getOrderPriceAndFees( - asset, - ZERO_ADDRESS, - paramsForPool - ) - + const _orderPriceAndFees = await getOrderPriceAndFees(asset, ZERO_ADDRESS) setOrderPriceAndFees(_orderPriceAndFees) - !orderPriceAndFees && setIsLoading(false) } init() + /** * we listen to the assets' changes to get the most updated price * based on the asset and the poolData's information. * Not adding isLoading and getOpcFeeForToken because we set these here. It is a compromise */ // eslint-disable-next-line react-hooks/exhaustive-deps - }, [asset, accountId, poolData, getOpcFeeForToken]) + }, [asset, accountId, getOpcFeeForToken, isUnsupportedPricing]) useEffect(() => { setHasDatatoken(Number(dtBalance) >= 1) }, [dtBalance]) useEffect(() => { - if (!isMounted || !accountId || !asset?.accessDetails) return + if ( + !isMounted || + !accountId || + !asset?.accessDetails || + isUnsupportedPricing + ) + return /** * disabled in these cases: @@ -128,7 +111,8 @@ export default function Download({ isAssetNetwork, hasDatatoken, accountId, - isOwned + isOwned, + isUnsupportedPricing ]) async function handleOrderOrDownload() { @@ -138,30 +122,18 @@ export default function Download({ if (isOwned) { setStatusText( getOrderFeedback( - asset.accessDetails?.baseToken?.symbol, - asset.accessDetails?.datatoken?.symbol + asset.accessDetails.baseToken?.symbol, + asset.accessDetails.datatoken?.symbol )[3] ) await downloadFile(web3, asset, accountId, validOrderTx) } else { - if (!hasDatatoken && asset.accessDetails.type === 'dynamic') { - setStatusText( - getOrderFeedback( - asset.accessDetails.baseToken?.symbol, - asset.accessDetails.datatoken?.symbol - )[0] - ) - const tx = await buyDtFromPool(asset.accessDetails, accountId, web3) - if (!tx) { - throw new Error() - } - } setStatusText( getOrderFeedback( asset.accessDetails.baseToken?.symbol, asset.accessDetails.datatoken?.symbol - )[asset.accessDetails?.type === 'fixed' ? 2 : 1] + )[asset.accessDetails.type === 'fixed' ? 2 : 1] ) const orderTx = await order(web3, asset, orderPriceAndFees, accountId) if (!orderTx) { @@ -188,7 +160,6 @@ export default function Download({ hasDatatoken={hasDatatoken} dtSymbol={asset?.datatokens[0]?.symbol} dtBalance={dtBalance} - datasetLowPoolLiquidity={!asset.accessDetails?.isPurchasable} onClick={handleOrderOrDownload} assetTimeout={secondsToString(asset.services[0].timeout)} assetType={asset?.metadata?.type} @@ -201,21 +172,37 @@ export default function Download({ /> ) + const AssetAction = ({ asset }: { asset: AssetExtended }) => { + return ( + <div> + {isUnsupportedPricing ? ( + <Alert + className={styles.fieldWarning} + state="info" + text={`No pricing schema available for this asset.`} + /> + ) : ( + <> + <Price + accessDetails={asset.accessDetails} + orderPriceAndFees={orderPriceAndFees} + conversion + size="large" + /> + {!isInPurgatory && <PurchaseButton />} + </> + )} + </div> + ) + } + return ( <aside className={styles.consume}> <div className={styles.info}> <div className={styles.filewrapper}> - <FileIcon file={file} isLoading={fileIsLoading} /> - </div> - <div className={styles.pricewrapper}> - <Price - accessDetails={asset.accessDetails} - orderPriceAndFees={orderPriceAndFees} - conversion - size="large" - /> - {!isInPurgatory && <PurchaseButton />} + <FileIcon file={file} isLoading={fileIsLoading} small /> </div> + <AssetAction asset={asset} /> </div> {asset?.metadata?.type === 'algorithm' && ( diff --git a/src/components/Asset/AssetActions/Pool/Actions/Header.module.css b/src/components/Asset/AssetActions/Pool/Actions/Header.module.css deleted file mode 100644 index d3d0f4625..000000000 --- a/src/components/Asset/AssetActions/Pool/Actions/Header.module.css +++ /dev/null @@ -1,28 +0,0 @@ -.header { - display: flex; - justify-content: center; - margin-bottom: var(--spacer); - padding-bottom: calc(var(--spacer) / 2); - border-bottom: 1px solid var(--border-color); - 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; - margin-right: auto; - margin-left: -3rem; -} - -.back { - margin-right: auto; -} diff --git a/src/components/Asset/AssetActions/Pool/Actions/Header.tsx b/src/components/Asset/AssetActions/Pool/Actions/Header.tsx deleted file mode 100644 index 1c9e3d68c..000000000 --- a/src/components/Asset/AssetActions/Pool/Actions/Header.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { ReactElement } from 'react' -import styles from './Header.module.css' -import Button from '@shared/atoms/Button' - -export default function Header({ - title, - backAction -}: { - title: string - backAction: () => void -}): ReactElement { - return ( - <header className={styles.header}> - <Button - className={styles.back} - style="text" - size="small" - onClick={backAction} - > - ← Back - </Button> - <h3 className={styles.headerTitle}>{title}</h3> - </header> - ) -} diff --git a/src/components/Asset/AssetActions/Pool/Actions/index.module.css b/src/components/Asset/AssetActions/Pool/Actions/index.module.css deleted file mode 100644 index d6b521977..000000000 --- a/src/components/Asset/AssetActions/Pool/Actions/index.module.css +++ /dev/null @@ -1,25 +0,0 @@ -.actions { - margin-left: -2rem; - margin-right: -2rem; - padding-left: var(--spacer); - padding-right: var(--spacer); - padding-top: calc(var(--spacer) / 1.5); - border-top: 1px solid var(--border-color); - text-align: center; - display: flex; - justify-content: center; -} - -.success { - margin-top: calc(var(--spacer) / 2); - margin-bottom: calc(var(--spacer) / 2); -} - -.actions button { - margin-left: calc(var(--spacer) / 4); - margin-right: calc(var(--spacer) / 4); -} - -.actions button svg { - fill: currentColor; -} diff --git a/src/components/Asset/AssetActions/Pool/Actions/index.tsx b/src/components/Asset/AssetActions/Pool/Actions/index.tsx deleted file mode 100644 index a653d4d10..000000000 --- a/src/components/Asset/AssetActions/Pool/Actions/index.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React, { ReactElement, useState } from 'react' -import Loader from '@shared/atoms/Loader' -import Button from '@shared/atoms/Button' -import styles from './index.module.css' -import ExplorerLink from '@shared/ExplorerLink' -import SuccessConfetti from '@shared/SuccessConfetti' -import { useWeb3 } from '@context/Web3' -import TokenApproval from '@shared/TokenApproval' -import Decimal from 'decimal.js' - -export default function Actions({ - isLoading, - loaderMessage, - successMessage, - slippage, - txId, - actionName, - amount, - action, - isDisabled, - tokenAddress, - tokenSymbol, - setSubmitting -}: { - isLoading: boolean - loaderMessage: string - successMessage: string - slippage?: string - txId: string - actionName: string - amount?: string - action: () => void - isDisabled?: boolean - tokenAddress: string - tokenSymbol: string - setSubmitting?: (isSubmitting: boolean) => void -}): ReactElement { - const { networkId } = useWeb3() - const [isTokenApproved, setIsTokenApproved] = useState(false) - - const actionButton = ( - <Button - style="primary" - size="small" - onClick={() => action()} - disabled={isDisabled} - > - {actionName} - </Button> - ) - - const applySlippage = (amount: string) => { - if (!amount) return '0' - const newAmount = new Decimal(amount) - .mul( - new Decimal(1) - .plus(new Decimal(slippage).div(new Decimal(100))) - .toString() - ) - .toString() - return newAmount - } - - return ( - <> - <div className={styles.actions}> - {isLoading ? ( - <Loader - message={ - isTokenApproved || actionName === 'Remove' - ? loaderMessage - : `Approving ${tokenSymbol}...` - } - /> - ) : actionName === 'Supply' || actionName === 'Swap' ? ( - <TokenApproval - actionButton={actionButton} - amount={slippage ? applySlippage(amount) : amount} - setIsTokenApproved={setIsTokenApproved} - tokenAddress={tokenAddress} - tokenSymbol={tokenSymbol} - disabled={isDisabled} - setSubmitting={setSubmitting} - /> - ) : ( - actionButton - )} - </div> - {txId && ( - <SuccessConfetti - className={styles.success} - success={successMessage} - action={ - <ExplorerLink networkId={networkId} path={`/tx/${txId}`}> - View transaction - </ExplorerLink> - } - /> - )} - </> - ) -} diff --git a/src/components/Asset/AssetActions/Pool/Graph/Nav.module.css b/src/components/Asset/AssetActions/Pool/Graph/Nav.module.css deleted file mode 100644 index 085ef452b..000000000 --- a/src/components/Asset/AssetActions/Pool/Graph/Nav.module.css +++ /dev/null @@ -1,27 +0,0 @@ -.type { - position: relative; - z-index: 1; - width: 100%; - text-align: center; - padding: 5px var(--spacer); - border-top: 1px solid var(--border-color); - border-bottom: 1px solid var(--border-color); -} - -.button, -.button:hover, -.button:focus, -.button:active { - display: inline-block; - color: var(--color-secondary); - font-size: var(--font-size-mini); - border: 1px solid transparent; - border-radius: var(--border-radius); - padding: calc(var(--spacer) / 16) calc(var(--spacer) / 4) !important; - transform: none; -} - -.button.active { - color: var(--font-color-text); - border-color: var(--border-color); -} diff --git a/src/components/Asset/AssetActions/Pool/Graph/Nav.tsx b/src/components/Asset/AssetActions/Pool/Graph/Nav.tsx deleted file mode 100644 index 4fef95a0c..000000000 --- a/src/components/Asset/AssetActions/Pool/Graph/Nav.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import Button from '@shared/atoms/Button' -import React, { - ChangeEvent, - Dispatch, - ReactElement, - SetStateAction -} from 'react' -import styles from './Nav.module.css' -import { graphTypes, GraphType } from './_constants' - -export default function Nav({ - graphType, - setGraphType -}: { - graphType: GraphType - setGraphType: Dispatch<SetStateAction<GraphType>> -}): ReactElement { - function handleGraphTypeSwitch(e: ChangeEvent<HTMLButtonElement>) { - e.preventDefault() - setGraphType(e.currentTarget.textContent.toLowerCase() as GraphType) - } - - return ( - <nav className={styles.type}> - {graphTypes.map((type: GraphType) => ( - <Button - key={type} - style="text" - size="small" - onClick={handleGraphTypeSwitch} - className={`${styles.button} ${ - graphType === type.toLowerCase() ? styles.active : null - }`} - > - {type} - </Button> - ))} - </nav> - ) -} diff --git a/src/components/Asset/AssetActions/Pool/Graph/_constants.ts b/src/components/Asset/AssetActions/Pool/Graph/_constants.ts deleted file mode 100644 index 93baf95cc..000000000 --- a/src/components/Asset/AssetActions/Pool/Graph/_constants.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - Chart as ChartJS, - LinearScale, - CategoryScale, - PointElement, - Tooltip, - BarElement, - LineElement, - LineController, - BarController, - ChartDataset, - TooltipOptions, - defaults -} from 'chart.js' - -export declare type GraphType = 'tvl' | 'price' | 'volume' - -export const graphTypes = ['TVL', 'Price', 'Volume'] - -// Chart.js global defaults -ChartJS.register( - LineElement, - BarElement, - PointElement, - LinearScale, - CategoryScale, - Tooltip, - LineController, - BarController -) - -defaults.font.family = `'Sharp Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif` -defaults.animation = { easing: 'easeInOutQuart', duration: 1000 } - -export const lineStyle: Partial<ChartDataset> = { - fill: false, - borderWidth: 2, - pointBorderWidth: 1, - pointRadius: 2, - pointHoverRadius: 4, - pointHoverBorderWidth: 0, - pointHitRadius: 2, - pointHoverBackgroundColor: '#ff4092' -} - -export const tooltipOptions: Partial<TooltipOptions> = { - intersect: false, - displayColors: false, - padding: 10, - cornerRadius: 3, - borderWidth: 1, - caretSize: 7, - bodyFont: { - size: 13, - weight: 'bold', - lineHeight: 1, - style: 'normal', - family: defaults.font.family - }, - titleFont: { - size: 10, - weight: 'normal', - lineHeight: 1, - style: 'normal', - family: defaults.font.family - } -} diff --git a/src/components/Asset/AssetActions/Pool/Graph/_utils.ts b/src/components/Asset/AssetActions/Pool/Graph/_utils.ts deleted file mode 100644 index 6d5ec2451..000000000 --- a/src/components/Asset/AssetActions/Pool/Graph/_utils.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { formatPrice } from '@shared/Price/PriceUnit' -import { ChartOptions, TooltipItem } from 'chart.js' -import { tooltipOptions } from './_constants' - -export function getOptions( - locale: string, - isDarkMode: boolean, - symbol: string -): ChartOptions { - return { - layout: { - padding: { - left: 0, - right: 0, - top: 0, - bottom: 20 - } - }, - plugins: { - tooltip: { - ...tooltipOptions, - backgroundColor: isDarkMode ? `#141414` : `#fff`, - titleColor: isDarkMode ? `#e2e2e2` : `#303030`, - bodyColor: isDarkMode ? `#fff` : `#141414`, - borderColor: isDarkMode ? `#41474e` : `#e2e2e2`, - callbacks: { - label: (tooltipItem: TooltipItem<any>) => - `${tooltipItem.formattedValue} ${symbol}` - } - } - }, - hover: { intersect: false }, - scales: { - y: { display: false, beginAtZero: true }, - x: { display: false, offset: true } - } - } -} diff --git a/src/components/Asset/AssetActions/Pool/Graph/index.module.css b/src/components/Asset/AssetActions/Pool/Graph/index.module.css deleted file mode 100644 index ccfc416fb..000000000 --- a/src/components/Asset/AssetActions/Pool/Graph/index.module.css +++ /dev/null @@ -1,18 +0,0 @@ -.graphWrap { - composes: container from '../Section/index.module.css'; - - padding: 0; - grid-column: full; - min-height: 120px; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - margin-bottom: calc(var(--spacer) / 2); - position: relative; -} - -.graphWrap canvas { - position: relative; - z-index: 0; -} diff --git a/src/components/Asset/AssetActions/Pool/Graph/index.tsx b/src/components/Asset/AssetActions/Pool/Graph/index.tsx deleted file mode 100644 index 2e973b635..000000000 --- a/src/components/Asset/AssetActions/Pool/Graph/index.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React, { ReactElement, useEffect, useState } from 'react' -import { ChartData, ChartOptions } from 'chart.js' -import { Bar, Line } from 'react-chartjs-2' -import Loader from '@shared/atoms/Loader' -import { useUserPreferences } from '@context/UserPreferences' -import useDarkMode from 'use-dark-mode' -import { darkModeConfig } from '../../../../../../app.config' -import { LoggerInstance } from '@oceanprotocol/lib' -import styles from './index.module.css' -import Decimal from 'decimal.js' -import { lineStyle, GraphType } from './_constants' -import Nav from './Nav' -import { getOptions } from './_utils' -import { usePrices } from '@context/Prices' -import { MAX_DECIMALS } from '@utils/constants' -import { usePool } from '@context/Pool' - -export default function Graph(): ReactElement { - const { locale, currency } = useUserPreferences() - const { prices } = usePrices() - const { poolSnapshots } = usePool() - const darkMode = useDarkMode(false, darkModeConfig) - - const [options, setOptions] = useState<ChartOptions<any>>() - const [graphType, setGraphType] = useState<GraphType>('tvl') - const [graphData, setGraphData] = useState<ChartData<any>>() - - // - // 0 Get Graph options - // - useEffect(() => { - if (!poolSnapshots) return - - LoggerInstance.log('[pool graph] Fired getOptions().') - const symbol = - graphType === 'tvl' ? currency : poolSnapshots[0]?.baseToken?.symbol - const options = getOptions(locale, darkMode.value, symbol) - setOptions(options) - }, [locale, darkMode.value, graphType, currency, poolSnapshots]) - - // - // 1 Data manipulation - // - useEffect(() => { - if (!poolSnapshots) return - - const timestamps = poolSnapshots.map((item) => { - const date = new Date(item.date * 1000) - return `${date.toLocaleDateString(locale)}` - }) - - const tvlHistory = poolSnapshots.map((item) => { - const conversionSpotPrice = prices[currency.toLowerCase()] - - const tvl = new Decimal(item.baseTokenLiquidity) - .mul(conversionSpotPrice) // convert to user currency - .toString() - return tvl - }) - - const priceHistory = poolSnapshots.map((item) => item.spotPrice) - const volumeHistory = poolSnapshots.map((item) => { - const volume = new Decimal(item.swapVolume) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - return volume - }) - - let data - switch (graphType) { - case 'price': - data = priceHistory - break - case 'volume': - data = volumeHistory - break - default: - data = tvlHistory - break - } - - const newGraphData = { - labels: timestamps, - datasets: [ - { - ...lineStyle, - data, - borderColor: `#8b98a9`, - backgroundColor: darkMode.value ? '#201f1f' : '#f7f7f7' - } - ] - } - setGraphData(newGraphData) - LoggerInstance.log('[pool graph] New graph data created:', newGraphData) - }, [poolSnapshots, graphType, currency, prices, locale, darkMode.value]) - - return ( - <div className={styles.graphWrap}> - {!graphData ? ( - <Loader /> - ) : ( - <> - {graphType === 'volume' ? ( - <Bar width={416} height={120} data={graphData} options={options} /> - ) : ( - <Line width={416} height={120} data={graphData} options={options} /> - )} - - <Nav graphType={graphType} setGraphType={setGraphType} /> - </> - )} - </div> - ) -} diff --git a/src/components/Asset/AssetActions/Pool/Remove/_utils.ts b/src/components/Asset/AssetActions/Pool/Remove/_utils.ts deleted file mode 100644 index cc42bf2b3..000000000 --- a/src/components/Asset/AssetActions/Pool/Remove/_utils.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { PoolInfo, PoolInfoUser } from '@context/Pool/_types' -import { calcMaxExactOut, Pool } from '@oceanprotocol/lib' -import Decimal from 'decimal.js' -import { PoolData_poolData as PoolData } from 'src/@types/subgraph/PoolData' - -export async function getMax( - poolInstance: Pool, - poolInfo: PoolInfo, - poolInfoUser: PoolInfoUser, - poolData: PoolData -) { - const maxBaseTokensRemovable = calcMaxExactOut(poolData.baseTokenLiquidity) - - const maxPoolSharesRemovable = await poolInstance.calcPoolInGivenSingleOut( - poolData.id, - poolInfo.baseTokenAddress, - maxBaseTokensRemovable.toString() - ) - - const userPoolShares = poolInfoUser.poolShares - - const maxPoolSharesRemovableDecimal = new Decimal(maxPoolSharesRemovable) - const userPoolSharesDecimal = new Decimal(userPoolShares) - - const maxPoolSharesRemovableByUser = - maxPoolSharesRemovableDecimal.greaterThan(userPoolSharesDecimal) - ? userPoolSharesDecimal - : maxPoolSharesRemovableDecimal - - // small hack because if the values are equal the decimal.js result is 99.(9) - const maxPercent = maxPoolSharesRemovableByUser.equals(userPoolSharesDecimal) - ? new Decimal(100) - : new Decimal(100) - .mul(maxPoolSharesRemovableByUser) - .div(userPoolSharesDecimal) - - return maxPercent.toDecimalPlaces(0, Decimal.ROUND_DOWN).toString() -} diff --git a/src/components/Asset/AssetActions/Pool/Remove/index.module.css b/src/components/Asset/AssetActions/Pool/Remove/index.module.css deleted file mode 100644 index ca433cf7d..000000000 --- a/src/components/Asset/AssetActions/Pool/Remove/index.module.css +++ /dev/null @@ -1,79 +0,0 @@ -.removeInput { - 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); - margin-bottom: 0; -} - -.range { - margin-top: calc(var(--spacer) / 2); - text-align: center; -} - -.range h3 { - margin-bottom: calc(var(--spacer) / 4); -} - -.range input { - width: 100%; -} - -.range p { - margin-bottom: 0; - margin-left: -2rem; - margin-right: -2rem; -} - -.slider { - position: relative; - z-index: 1; -} - -.maximum { - position: absolute; - right: 0; - bottom: 2.5rem; - font-size: var(--font-size-mini); - min-width: 5rem; - text-align: center; -} - -.maximum:hover { - transform: none; -} - -.toggle { - margin-top: calc(var(--spacer) / 2); - margin-bottom: 0; - font-size: var(--font-size-mini); - margin-bottom: -2rem; -} - -.output { - composes: container from '../Section/index.module.css'; - display: grid; - gap: var(--spacer); - grid-template-columns: 1fr 1fr; -} - -.output p { - font-weight: var(--font-weight-bold); - margin-bottom: calc(var(--spacer) / 8); - font-size: var(--font-size-small); -} - -.output [class*='token'] > figure { - display: none; -} - -.slippage { - composes: slippage from '../../Trade/Slippage.module.css'; -} diff --git a/src/components/Asset/AssetActions/Pool/Remove/index.tsx b/src/components/Asset/AssetActions/Pool/Remove/index.tsx deleted file mode 100644 index 66da99969..000000000 --- a/src/components/Asset/AssetActions/Pool/Remove/index.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import React, { - ReactElement, - useState, - ChangeEvent, - useEffect, - useRef -} from 'react' -import styles from './index.module.css' -import Header from '../Actions/Header' -import { toast } from 'react-toastify' -import Actions from '../Actions' -import { LoggerInstance, Pool } from '@oceanprotocol/lib' -import Token from '../../../../@shared/Token' -import FormHelp from '@shared/FormInput/Help' -import Button from '@shared/atoms/Button' -import debounce from 'lodash.debounce' -import UserLiquidity from '../../UserLiquidity' -import InputElement from '@shared/FormInput/InputElement' -import { useWeb3 } from '@context/Web3' -import Decimal from 'decimal.js' -import { useAsset } from '@context/Asset' -import content from '../../../../../../content/price.json' -import { usePool } from '@context/Pool' -import { getMax } from './_utils' - -const slippagePresets = ['5', '10', '15', '25', '50'] - -export default function Remove({ - setShowRemove -}: { - setShowRemove: (show: boolean) => void -}): ReactElement { - const { accountId, web3 } = useWeb3() - const { isAssetNetwork } = useAsset() - const { poolData, poolInfo, poolInfoUser, fetchAllData } = usePool() - - const [amountPercent, setAmountPercent] = useState('0') - const [amountMaxPercent, setAmountMaxPercent] = useState('100') - const [amountPoolShares, setAmountPoolShares] = useState('0') - const [amountOcean, setAmountOcean] = useState('0') - const [isLoading, setIsLoading] = useState<boolean>() - const [txId, setTxId] = useState<string>() - const [slippage, setSlippage] = useState(slippagePresets[0]) - const [minOceanAmount, setMinOceanAmount] = useState<string>('0') - const [poolInstance, setPoolInstance] = useState<Pool>() - - useEffect(() => { - if (!web3) return - setPoolInstance(new Pool(web3)) - }, [web3]) - - async function handleRemoveLiquidity() { - setIsLoading(true) - - try { - const result = await poolInstance.exitswapPoolAmountIn( - accountId, - poolData?.id, - amountPoolShares, - minOceanAmount - ) - setTxId(result?.transactionHash) - // fetch new data - fetchAllData() - } catch (error) { - LoggerInstance.error(error.message) - toast.error(error.message) - } finally { - // reset slider after transaction - setAmountPercent('0') - setAmountOcean('0') - setMinOceanAmount('0') - setIsLoading(false) - } - } - - // - // Calculate and set maximum shares user is able to remove - // - useEffect(() => { - if (!accountId || !poolInfoUser || !poolInfo || !poolInstance) return - - getMax(poolInstance, poolInfo, poolInfoUser, poolData).then((max) => - setAmountMaxPercent(max) - ) - }, [accountId, poolInfoUser, poolInfo, poolInstance, poolData]) - - const getValues = useRef( - debounce(async (poolInstance, id, poolInfo, newAmountPoolShares) => { - if (newAmountPoolShares === '0') { - setAmountOcean('0') - return - } - const newAmountOcean = await poolInstance.calcSingleOutGivenPoolIn( - id, - poolInfo.baseTokenAddress, - newAmountPoolShares, - 18, - poolInfo.baseTokenDecimals - ) - setAmountOcean(newAmountOcean) - }, 150) - ) - - // Check and set outputs when amountPoolShares changes - useEffect(() => { - if (!accountId || !poolInfo || !poolData?.id || !poolInstance) return - getValues.current(poolInstance, poolData?.id, poolInfo, amountPoolShares) - }, [amountPoolShares, accountId, poolInfo, poolData?.id, poolInstance]) - - useEffect(() => { - if (!amountOcean || amountPercent === '0') { - setMinOceanAmount('0') - return - } - - const minOceanAmount = new Decimal(amountOcean) - .mul(new Decimal(100).minus(new Decimal(slippage))) - .dividedBy(100) - .toString() - - setMinOceanAmount(minOceanAmount.slice(0, 18)) - }, [slippage, amountOcean, amountPercent]) - - // Set amountPoolShares based on set slider value - function handleAmountPercentChange(e: ChangeEvent<HTMLInputElement>) { - setAmountPercent(e.target.value) - if (!poolInfoUser?.poolShares) return - - const amountPoolShares = new Decimal(e.target.value) - .dividedBy(100) - .mul(new Decimal(poolInfoUser.poolShares)) - .toString() - setAmountPoolShares(amountPoolShares) - } - - function handleMaxButton(e: ChangeEvent<HTMLInputElement>) { - e.preventDefault() - setAmountPercent(amountMaxPercent) - const amountPoolShares = new Decimal(amountMaxPercent) - .dividedBy(100) - .mul(new Decimal(poolInfoUser?.poolShares)) - .toString() - - setAmountPoolShares(amountPoolShares) - } - - function handleSlippageChange(e: ChangeEvent<HTMLSelectElement>) { - setSlippage(e.target.value) - } - - return ( - <div className={styles.remove}> - <Header - title={content.pool.remove.title} - backAction={() => { - setShowRemove(false) - fetchAllData() - }} - /> - - <form className={styles.removeInput}> - <UserLiquidity amount={poolInfoUser?.poolShares} symbol="pool shares" /> - <div className={styles.range}> - <h3>{amountPercent}%</h3> - <div className={styles.slider}> - <input - type="range" - min="0" - max={amountMaxPercent} - disabled={!isAssetNetwork || isLoading} - value={amountPercent} - onChange={handleAmountPercentChange} - /> - <Button - style="text" - size="small" - className={styles.maximum} - disabled={!isAssetNetwork || isLoading} - onClick={handleMaxButton} - > - {`${amountMaxPercent}% max`} - </Button> - </div> - - <FormHelp>{content.pool.remove.simple}</FormHelp> - </div> - </form> - <div className={styles.output}> - <div> - <p>{content.pool.remove.output.titleOutExpected}</p> - <Token - symbol={poolInfo?.baseTokenSymbol} - balance={amountOcean} - noIcon - /> - </div> - <div> - <p>{content.pool.remove.output.titleOutMinimum}</p> - <Token symbol={poolInfo?.baseTokenSymbol} balance={minOceanAmount} /> - </div> - </div> - <div className={styles.slippage}> - <strong>Slippage Tolerance</strong> - <InputElement - name="slippage" - type="select" - size="mini" - postfix="%" - sortOptions={false} - options={slippagePresets} - disabled={!isAssetNetwork || isLoading} - value={slippage} - onChange={handleSlippageChange} - /> - </div> - <Actions - isLoading={isLoading} - loaderMessage="Removing Liquidity..." - actionName={content.pool.remove.action} - action={handleRemoveLiquidity} - successMessage="Successfully removed liquidity." - isDisabled={ - !isAssetNetwork || - amountPercent === '0' || - amountOcean === '0' || - poolInfo?.totalPoolTokens === '0' - } - txId={txId} - tokenAddress={poolInfo?.baseTokenAddress} - tokenSymbol={poolInfo?.baseTokenSymbol} - /> - </div> - ) -} diff --git a/src/components/Asset/AssetActions/Pool/Section/Title.module.css b/src/components/Asset/AssetActions/Pool/Section/Title.module.css deleted file mode 100644 index a376e0183..000000000 --- a/src/components/Asset/AssetActions/Pool/Section/Title.module.css +++ /dev/null @@ -1,13 +0,0 @@ -.title { - font-size: var(--font-size-base); - margin-bottom: calc(var(--spacer) / 3); - color: var(--color-secondary); -} - -.titlePostfix { - font-size: var(--font-size-mini); - font-family: var(--font-family-base); - font-weight: var(--font-weight-base); - display: inline-block; - margin-left: 0.3rem; -} diff --git a/src/components/Asset/AssetActions/Pool/Section/Title.tsx b/src/components/Asset/AssetActions/Pool/Section/Title.tsx deleted file mode 100644 index 67ddbe713..000000000 --- a/src/components/Asset/AssetActions/Pool/Section/Title.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import Tooltip from '@shared/atoms/Tooltip' -import React from 'react' -import styles from './Title.module.css' - -export default function Title({ - title, - tooltip, - titlePostfix, - titlePostfixTitle -}: { - title: string - tooltip?: string - titlePostfix?: string - titlePostfixTitle?: string -}) { - return ( - <h3 className={styles.title}> - {title} {tooltip && <Tooltip content={tooltip} />}{' '} - {titlePostfix && ( - <span className={styles.titlePostfix} title={titlePostfixTitle}> - {titlePostfix} - </span> - )} - </h3> - ) -} diff --git a/src/components/Asset/AssetActions/Pool/Section/index.module.css b/src/components/Asset/AssetActions/Pool/Section/index.module.css deleted file mode 100644 index 1bca7a276..000000000 --- a/src/components/Asset/AssetActions/Pool/Section/index.module.css +++ /dev/null @@ -1,39 +0,0 @@ -.container { - margin-left: calc(-1 * var(--spacer) / 1.5); - margin-right: calc(-1 * var(--spacer) / 1.5); - padding: calc(var(--spacer) / 1.5) calc(var(--spacer) / 1.5) - calc(var(--spacer) / 2) calc(var(--spacer) / 1.5); -} - -@media (min-width: 40rem) { - .container { - padding-left: var(--spacer); - padding-right: var(--spacer); - margin-left: calc(-1 * var(--spacer)); - margin-right: calc(-1 * var(--spacer)); - } -} - -.section { - composes: container; - - border-top: 1px solid var(--border-color); - position: relative; -} - -.section.highlight { - background: var(--background-highlight); -} - -.section:first-child { - padding-top: 0; - border-top: 0; -} - -.grid { - display: grid; - gap: calc(var(--spacer) / 2); - grid-template-columns: - [full-start] minmax(13rem, 1fr) [break] minmax(7rem, 1fr) - [full-end]; -} diff --git a/src/components/Asset/AssetActions/Pool/Section/index.tsx b/src/components/Asset/AssetActions/Pool/Section/index.tsx deleted file mode 100644 index dac852fb8..000000000 --- a/src/components/Asset/AssetActions/Pool/Section/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { ReactElement, ReactNode } from 'react' -import styles from './index.module.css' -import Title from './Title' - -export default function PoolSection({ - title, - tooltip, - titlePostfix, - titlePostfixTitle, - children, - highlight, - className -}: { - title?: string - children: ReactNode - tooltip?: string - titlePostfix?: string - titlePostfixTitle?: string - highlight?: boolean - className?: string -}): ReactElement { - return ( - <div - className={`${styles.section} ${highlight ? styles.highlight : ''} ${ - className || '' - }`} - > - {title && ( - <Title - title={title} - tooltip={tooltip} - titlePostfix={titlePostfix} - titlePostfixTitle={titlePostfixTitle} - /> - )} - <div className={styles.grid}>{children}</div> - </div> - ) -} diff --git a/src/components/Asset/AssetActions/Pool/Sections/Update.module.css b/src/components/Asset/AssetActions/Pool/Sections/Update.module.css deleted file mode 100644 index 78ea4fbcc..000000000 --- a/src/components/Asset/AssetActions/Pool/Sections/Update.module.css +++ /dev/null @@ -1,38 +0,0 @@ -.update { - display: block; - margin: 0; - font-size: var(--font-size-mini); - color: var(--color-secondary); - text-align: center; - padding-top: calc(var(--spacer) / 2); - padding-bottom: calc(var(--spacer) / 2); -} - -.update:before { - content: ''; - width: 6px; - height: 6px; - border-radius: 50%; - display: inline-block; - border: 1px solid var(--brand-alert-green); - margin-right: 0.2rem; - margin-top: -0.1rem; - vertical-align: middle; - animation: pulse 2s ease-in-out infinite; -} - -.button { - font-size: var(--font-size-mini); -} - -@keyframes pulse { - 0% { - background: transparent; - } - 50% { - background: var(--brand-alert-green); - } - 100% { - background: transparent; - } -} diff --git a/src/components/Asset/AssetActions/Pool/Sections/Update.tsx b/src/components/Asset/AssetActions/Pool/Sections/Update.tsx deleted file mode 100644 index 393b67b07..000000000 --- a/src/components/Asset/AssetActions/Pool/Sections/Update.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { usePool } from '@context/Pool' -import { useUserPreferences } from '@context/UserPreferences' -import Button from '@shared/atoms/Button' -import React from 'react' -import styles from './Update.module.css' - -export default function Update() { - const { debug } = useUserPreferences() - const { fetchAllData, refreshInterval } = usePool() - - return ( - <p className={styles.update}> - Fetching every {refreshInterval / 1000} sec.{' '} - {debug && ( - <Button - style="text" - size="small" - onClick={() => fetchAllData()} - className={styles.button} - > - Refresh Data - </Button> - )} - </p> - ) -} diff --git a/src/components/Asset/AssetActions/Pool/Sections/index.module.css b/src/components/Asset/AssetActions/Pool/Sections/index.module.css deleted file mode 100644 index 685d2f445..000000000 --- a/src/components/Asset/AssetActions/Pool/Sections/index.module.css +++ /dev/null @@ -1,35 +0,0 @@ -.dataToken { - font-size: var(--font-size-large); - text-align: center; -} - -.dataToken > div { - display: block; -} - -.dataTokenLinks { - display: flex; - justify-content: center; - font-size: var(--font-size-small); - margin-top: calc(var(--spacer) / 4); -} - -.dataTokenLinks a { - margin-left: calc(var(--spacer) / 3); - margin-right: calc(var(--spacer) / 3); -} - -.fees { - border-top: none; - padding-top: 0; - margin-top: -0.5rem; -} - -.fees > div { - grid-template-columns: repeat(auto-fit, minmax(5rem, 1fr)); - text-align: center; -} - -.fees figure { - display: none; -} diff --git a/src/components/Asset/AssetActions/Pool/Sections/index.tsx b/src/components/Asset/AssetActions/Pool/Sections/index.tsx deleted file mode 100644 index 0a4854014..000000000 --- a/src/components/Asset/AssetActions/Pool/Sections/index.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import { useAsset } from '@context/Asset' -import { usePool } from '@context/Pool' -import Tooltip from '@shared/atoms/Tooltip' -import ExplorerLink from '@shared/ExplorerLink' -import PriceUnit from '@shared/Price/PriceUnit' -import React, { useEffect, useState } from 'react' -import Graph from '../Graph' -import PoolSection from '../Section' -import Token from '../../../../@shared/Token' -import content from '../../../../../../content/price.json' -import styles from './index.module.css' -import Update from './Update' -import { useMarketMetadata } from '@context/MarketMetadata' -import { OpcFeesQuery_opc as OpcFeesData } from '../../../../../@types/subgraph/OpcFeesQuery' -import { getOpcFees } from '@utils/subgraph' -import { useWeb3 } from '@context/Web3' -import Decimal from 'decimal.js' - -export default function PoolSections() { - const { asset } = useAsset() - const { poolData, poolInfo, poolInfoUser, poolInfoOwner } = usePool() - const { getOpcFeeForToken } = useMarketMetadata() - const { chainId } = useWeb3() - const [oceanCommunitySwapFee, setOceanCommunitySwapFee] = useState<string>('') - useEffect(() => { - getOpcFees(chainId || 1).then((response: OpcFeesData) => { - setOceanCommunitySwapFee( - response?.swapOceanFee - ? new Decimal(response.swapOceanFee).mul(100).toString() - : '0' - ) - }) - }, [chainId]) - - return ( - <> - <PoolSection className={styles.dataToken}> - <PriceUnit price="1" symbol={poolInfo?.datatokenSymbol} size="large" />{' '} - ={' '} - <PriceUnit - price={`${poolData?.spotPrice}`} - symbol={poolInfo?.baseTokenSymbol} - size="large" - /> - <Tooltip content={content.pool.tooltips.price} /> - <div className={styles.dataTokenLinks}> - <ExplorerLink - networkId={asset?.chainId} - path={`address/${asset?.accessDetails?.addressOrId}`} - > - Pool - </ExplorerLink> - <ExplorerLink - networkId={asset?.chainId} - path={ - asset?.chainId === 2021000 || asset?.chainId === 1287 - ? `tokens/${asset?.services[0].datatokenAddress}` - : `token/${asset?.services[0].datatokenAddress}` - } - > - Datatoken - </ExplorerLink> - </div> - </PoolSection> - - <PoolSection - title="Your liquidity" - titlePostfix={ - poolInfoUser?.poolSharePercentage && - `${poolInfoUser?.poolSharePercentage}% of pool` - } - tooltip={content.pool.tooltips.liquidity.replace( - 'SWAPFEE', - poolInfo?.liquidityProviderSwapFee - )} - highlight - > - <Token - symbol={poolInfo?.baseTokenSymbol} - balance={poolInfoUser?.liquidity} - conversion - /> - </PoolSection> - - <PoolSection - title="Owner liquidity" - titlePostfix={`${poolInfoOwner?.poolSharePercentage}% of pool`} - > - <Token - symbol={poolInfo?.baseTokenSymbol} - balance={poolInfoOwner?.liquidity} - conversion - /> - </PoolSection> - - <PoolSection title="Total Value Locked"> - <Token - symbol={poolInfo?.baseTokenSymbol} - balance={poolData?.baseTokenLiquidity.toString()} - conversion - /> - </PoolSection> - - <PoolSection - title="Pool Statistics" - titlePostfix={ - poolInfo?.weightDt && - `${poolInfo?.weightBaseToken}/${poolInfo?.weightDt}` - } - titlePostfixTitle={`Weight of ${poolInfo?.weightBaseToken}% ${poolInfo?.baseTokenSymbol} & ${poolInfo?.weightDt}% ${poolInfo?.datatokenSymbol}`} - > - <Graph /> - </PoolSection> - - <PoolSection className={styles.fees}> - <Token - symbol="% swap fee" - balance={poolInfo?.liquidityProviderSwapFee} - noIcon - size="mini" - /> - <Token - symbol="% market fee" - balance={poolInfo?.publishMarketSwapFee} - noIcon - size="mini" - /> - <Token - symbol="% OPC fee" - balance={oceanCommunitySwapFee} - noIcon - size="mini" - /> - </PoolSection> - - <Update /> - </> - ) -} diff --git a/src/components/Asset/AssetActions/Pool/index.tsx b/src/components/Asset/AssetActions/Pool/index.tsx deleted file mode 100644 index 085d0eb72..000000000 --- a/src/components/Asset/AssetActions/Pool/index.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { ReactElement, useState } from 'react' -import stylesActions from './Actions/index.module.css' -import Button from '@shared/atoms/Button' -import Remove from './Remove' -import AssetActionHistoryTable from '../AssetActionHistoryTable' -import { useAsset } from '@context/Asset' -import { useWeb3 } from '@context/Web3' -import PoolTransactions from '@shared/PoolTransactions' - -import { usePool } from '@context/Pool' -import PoolSections from './Sections' - -export default function Pool(): ReactElement { - const { asset, isAssetNetwork } = useAsset() - const { hasUserAddedLiquidity } = usePool() - const { accountId } = useWeb3() - - const [showRemove, setShowRemove] = useState(false) - - return ( - <> - {showRemove ? ( - <Remove setShowRemove={setShowRemove} /> - ) : ( - <> - <PoolSections /> - - {hasUserAddedLiquidity && ( - <div className={stylesActions.actions}> - <Button - style="primary" - size="small" - onClick={() => setShowRemove(true)} - disabled={!isAssetNetwork} - > - Remove Liquidity - </Button> - </div> - )} - - {accountId && ( - <AssetActionHistoryTable title="Your Pool Transactions"> - <PoolTransactions - accountId={accountId} - poolAddress={asset?.accessDetails?.addressOrId} - poolChainId={asset?.chainId} - minimal - /> - </AssetActionHistoryTable> - )} - </> - )} - </> - ) -} diff --git a/src/components/Asset/AssetActions/Trade/FormTrade.module.css b/src/components/Asset/AssetActions/Trade/FormTrade.module.css deleted file mode 100644 index 996909b3b..000000000 --- a/src/components/Asset/AssetActions/Trade/FormTrade.module.css +++ /dev/null @@ -1,5 +0,0 @@ -.alertWrap { - min-height: 320px; - display: flex; - align-items: center; -} diff --git a/src/components/Asset/AssetActions/Trade/FormTrade.tsx b/src/components/Asset/AssetActions/Trade/FormTrade.tsx deleted file mode 100644 index e09fd8cad..000000000 --- a/src/components/Asset/AssetActions/Trade/FormTrade.tsx +++ /dev/null @@ -1,240 +0,0 @@ -import React, { ReactElement, useState } from 'react' -import { - AmountsInMaxFee, - AmountsOutMaxFee, - LoggerInstance, - Pool, - TokenInOutMarket -} from '@oceanprotocol/lib' -import * as Yup from 'yup' -import { Formik } from 'formik' -import Actions from '../Pool/Actions' -import { useUserPreferences } from '@context/UserPreferences' -import { toast } from 'react-toastify' -import Swap from './Swap' -import Alert from '@shared/atoms/Alert' -import styles from './FormTrade.module.css' -import Decimal from 'decimal.js' -import { useWeb3 } from '@context/Web3' -import { useAsset } from '@context/Asset' -import { FormTradeData } from './_types' -import { initialValues } from './_constants' -import content from '../../../../../content/price.json' -import { AssetExtended } from 'src/@types/AssetExtended' -import { usePool } from '@context/Pool' -import { useMarketMetadata } from '@context/MarketMetadata' - -export default function FormTrade({ - asset, - balance -}: { - asset: AssetExtended - balance: PoolBalance -}): ReactElement { - const { web3, accountId } = useWeb3() - const { isAssetNetwork } = useAsset() - const { debug } = useUserPreferences() - const { appConfig } = useMarketMetadata() - const { poolInfo, fetchAllData } = usePool() - - const [txId, setTxId] = useState<string>() - const [coinFrom, setCoinFrom] = useState<string>('OCEAN') - const [maximumBaseToken, setMaximumBaseToken] = useState('0') - const [maximumDt, setMaximumDt] = useState('0') - - const validationSchema: Yup.SchemaOf<FormTradeData> = Yup.object() - .shape({ - baseToken: Yup.number() - .max( - Number(maximumBaseToken), - (param) => `Must be less or equal to ${param.max}` - ) - .min(0.001, (param) => `Must be more or equal to ${param.min}`) - .required('Required') - .nullable(), - datatoken: Yup.number() - .max( - Number(maximumDt), - (param) => `Must be less or equal to ${param.max}` - ) - .min(0.00001, (param) => `Must be more or equal to ${param.min}`) - .required('Required') - .nullable(), - type: Yup.string(), - slippage: Yup.string() - }) - .defined() - - async function handleTrade(values: FormTradeData) { - if (!web3 || !asset || !poolInfo || !values || !appConfig) return - - try { - const poolInstance = new Pool(web3) - let tx - if (values.output === 'exactIn') { - const tokenInOutMarket: TokenInOutMarket = { - tokenIn: - values.type === 'sell' - ? poolInfo.datatokenAddress - : poolInfo.baseTokenAddress, - tokenOut: - values.type === 'sell' - ? poolInfo.baseTokenAddress - : poolInfo.datatokenAddress, - marketFeeAddress: appConfig.marketFeeAddress, - tokenInDecimals: - values.type === 'sell' - ? poolInfo.datatokenDecimals - : poolInfo.baseTokenDecimals, - tokenOutDecimals: - values.type === 'sell' - ? poolInfo.baseTokenDecimals - : poolInfo.datatokenDecimals - } - - const amountsInOutMaxFee: AmountsInMaxFee = { - tokenAmountIn: - values.type === 'sell' ? values.datatoken : values.baseToken, - minAmountOut: new Decimal( - values.type === 'sell' ? values.baseToken : values.datatoken - ) - .mul( - new Decimal(1) - .minus(new Decimal(values.slippage).div(new Decimal(100))) - .toString() - ) - .toString(), - swapMarketFee: appConfig.consumeMarketPoolSwapFee - } - tx = await poolInstance.swapExactAmountIn( - accountId, - asset.accessDetails.addressOrId, - tokenInOutMarket, - amountsInOutMaxFee - ) - - if (!tx) { - throw new Error('Failed to swap tokens!') - } - } - if (values.output === 'exactOut') { - const tokenOutMarket: TokenInOutMarket = { - tokenIn: - values.type === 'sell' - ? poolInfo.datatokenAddress - : poolInfo.baseTokenAddress, - tokenOut: - values.type === 'sell' - ? poolInfo.baseTokenAddress - : poolInfo.datatokenAddress, - marketFeeAddress: appConfig.marketFeeAddress, - tokenInDecimals: - values.type === 'sell' - ? poolInfo.datatokenDecimals - : poolInfo.baseTokenDecimals, - tokenOutDecimals: - values.type === 'sell' - ? poolInfo.baseTokenDecimals - : poolInfo.datatokenDecimals - } - - const amountsOutMaxFee: AmountsOutMaxFee = { - maxAmountIn: new Decimal( - values.type === 'sell' ? values.datatoken : values.baseToken - ) - .mul( - new Decimal(1) - .plus(new Decimal(values.slippage).div(new Decimal(100))) - .toString() - ) - .toString(), - tokenAmountOut: - values.type === 'sell' ? values.baseToken : values.datatoken, - swapMarketFee: appConfig.consumeMarketPoolSwapFee - } - tx = await poolInstance.swapExactAmountOut( - accountId, - asset.accessDetails.addressOrId, - tokenOutMarket, - amountsOutMaxFee - ) - if (!tx) { - throw new Error('Failed to swap tokens!') - } - } - await fetchAllData() - setTxId(tx?.transactionHash) - } catch (error) { - LoggerInstance.error(error.message) - toast.error(error.message) - } - } - - return ( - <Formik - initialValues={initialValues} - validationSchema={validationSchema} - onSubmit={async (values, { setFieldValue, setSubmitting, resetForm }) => { - await handleTrade(values) - await setFieldValue('baseToken', '') - await setFieldValue('datatoken', '') - resetForm() - setSubmitting(false) - }} - > - {({ isSubmitting, setSubmitting, submitForm, values, isValid }) => ( - <> - <Swap - asset={asset} - balance={balance} - setCoin={setCoinFrom} - setMaximumBaseToken={setMaximumBaseToken} - setMaximumDt={setMaximumDt} - isLoading={isSubmitting} - /> - <Actions - isDisabled={ - !isValid || - !isAssetNetwork || - values.datatoken === undefined || - values.baseToken === undefined - } - isLoading={isSubmitting} - loaderMessage="Swapping tokens..." - successMessage="Successfully swapped tokens." - actionName={content.trade.action} - slippage={values.slippage} - amount={ - values.type === 'sell' - ? values.datatoken - ? `${values.datatoken}` - : undefined - : values.baseToken - ? `${values.baseToken}` - : undefined - } - action={submitForm} - txId={txId} - tokenAddress={ - values.type === 'buy' - ? poolInfo.baseTokenAddress - : poolInfo.datatokenAddress - } - tokenSymbol={ - values.type === 'buy' - ? poolInfo.baseTokenSymbol - : poolInfo.datatokenSymbol - } - setSubmitting={setSubmitting} - /> - - {debug && ( - <pre> - <code>{JSON.stringify(values, null, 2)}</code> - </pre> - )} - </> - )} - </Formik> - ) -} diff --git a/src/components/Asset/AssetActions/Trade/Output.module.css b/src/components/Asset/AssetActions/Trade/Output.module.css deleted file mode 100644 index 39c3fe672..000000000 --- a/src/components/Asset/AssetActions/Trade/Output.module.css +++ /dev/null @@ -1,24 +0,0 @@ -.output { - border-top: 1px solid var(--border-color); - padding: var(--spacer); - padding-bottom: calc(var(--spacer) / 1.5); - display: grid; - gap: var(--spacer); - grid-template-columns: 1fr 1fr; - margin-left: -2rem; - margin-right: -2rem; -} - -.output p { - font-weight: var(--font-weight-bold); - margin-bottom: calc(var(--spacer) / 8); - font-size: var(--font-size-small); -} - -.output [class*='token'] { - white-space: normal; -} - -.output [class*='token'] > figure { - display: none; -} diff --git a/src/components/Asset/AssetActions/Trade/Output.tsx b/src/components/Asset/AssetActions/Trade/Output.tsx deleted file mode 100644 index c80c0a104..000000000 --- a/src/components/Asset/AssetActions/Trade/Output.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { FormikContextType, useFormikContext } from 'formik' -import React, { ReactElement, useEffect, useState } from 'react' -import { useAsset } from '@context/Asset' -import Token from '../../../@shared/Token' -import styles from './Output.module.css' - -import Decimal from 'decimal.js' -import { FormTradeData } from './_types' -import { usePool } from '@context/Pool' - -Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 }) - -export default function Output({ - poolAddress, - lpSwapFee -}: { - poolAddress: string - lpSwapFee: string -}): ReactElement { - const { isAssetNetwork } = useAsset() - const { poolInfo } = usePool() - const [outputWithSlippage, setOutputWithSlippage] = useState<string>('0') - // Connect with form - const { values }: FormikContextType<FormTradeData> = useFormikContext() - - // Get output values - useEffect(() => { - if (!poolAddress || !isAssetNetwork) return - - async function getOutput() { - if (!values.baseToken || !values.datatoken || !values.output) return - - const output = - values.output === 'exactIn' - ? new Decimal( - values.type === 'sell' ? values.baseToken : values.datatoken - ) - .mul( - new Decimal(1) - .minus(new Decimal(values.slippage).div(new Decimal(100))) - .toString() - ) - .toString() - : new Decimal( - values.type === 'sell' ? values.datatoken : values.baseToken - ) - .mul( - new Decimal(1) - .plus(new Decimal(values.slippage).div(new Decimal(100))) - .toString() - ) - .toString() - - setOutputWithSlippage(output) - } - getOutput() - }, [poolAddress, values, isAssetNetwork]) - - return ( - <div className={styles.output}> - <div> - <p> - {values.output === 'exactIn' ? 'Minimum Received' : 'Maximum Sent'} - </p> - <Token - symbol={ - values.type === 'buy' - ? values.output === 'exactIn' - ? poolInfo.datatokenSymbol - : poolInfo.baseTokenSymbol - : values.output === 'exactIn' - ? poolInfo.baseTokenSymbol - : poolInfo.datatokenSymbol - } - balance={outputWithSlippage} - /> - </div> - <div> - <p>Swap fee</p> - <Token - symbol={`${ - values.type === 'buy' - ? poolInfo.baseTokenSymbol - : poolInfo.datatokenSymbol - } ${ - poolInfo.liquidityProviderSwapFee - ? `(${poolInfo.liquidityProviderSwapFee}%)` - : '' - }`} - balance={lpSwapFee} - /> - </div> - </div> - ) -} diff --git a/src/components/Asset/AssetActions/Trade/PriceImpact.module.css b/src/components/Asset/AssetActions/Trade/PriceImpact.module.css deleted file mode 100644 index 525a20fd4..000000000 --- a/src/components/Asset/AssetActions/Trade/PriceImpact.module.css +++ /dev/null @@ -1,24 +0,0 @@ -.priceImpact { - font-size: var(--font-size-small); - border-top: 1px solid var(--border-color); - margin-left: -2rem; - margin-right: -2rem; - padding: calc(var(--spacer) / 4) var(--spacer); - color: var(--color-secondary); - display: grid; - grid-template-columns: 1fr 1fr; - gap: calc(var(--spacer) / 3); -} - -.priceImpact strong { - font-weight: var(--font-weight-base); - text-align: right; -} - -.alert { - color: var(--brand-alert-red); -} - -.number { - font-weight: var(--font-weight-bold); -} diff --git a/src/components/Asset/AssetActions/Trade/PriceImpact.tsx b/src/components/Asset/AssetActions/Trade/PriceImpact.tsx deleted file mode 100644 index aa6dd269e..000000000 --- a/src/components/Asset/AssetActions/Trade/PriceImpact.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { ReactElement, useEffect, useState } from 'react' -import Decimal from 'decimal.js' -import Tooltip from '@shared/atoms/Tooltip' -import styles from './PriceImpact.module.css' - -export default function PriceImpact({ - totalValue, - tokenAmount, - spotPrice -}: { - /// how much the user actually pays (doesn't matter witch token it is) - totalValue: string - /// how many tokens the user trades (doesn't matter witch token it is) - tokenAmount: string - /// the spot price of the traded token (doesn't matter witch token it is)) - spotPrice: string -}): ReactElement { - const [priceImpact, setPriceImpact] = useState<string>('0') - - async function getPriceImpact( - totalValue: string, - tokenAmount: string, - spotPrice: string - ) { - const dtotalValue = new Decimal(totalValue) - const dTokenAmount = new Decimal(tokenAmount) - const dSpotPrice = new Decimal(spotPrice) - let priceImpact = Decimal.abs( - dtotalValue.div(dTokenAmount.times(dSpotPrice)).minus(1) - ).mul(100) - if (priceImpact.isNaN()) priceImpact = new Decimal(0) - return priceImpact.toDecimalPlaces(2, Decimal.ROUND_DOWN) - } - - useEffect(() => { - if (!totalValue || !tokenAmount || !spotPrice) { - setPriceImpact('0') - return - } - - async function init() { - const newPriceImpact = await getPriceImpact( - totalValue, - tokenAmount, - spotPrice - ) - setPriceImpact(newPriceImpact.toString()) - } - init() - }, [totalValue, tokenAmount, spotPrice]) - - return ( - <div className={styles.priceImpact}> - <strong>Price Impact</strong> - <div> - <span - className={`${styles.number} ${ - parseInt(priceImpact) > 5 && styles.alert - }`} - >{`${priceImpact}%`}</span> - <Tooltip content="The difference between the market price and estimated price due to trade size." /> - </div> - </div> - ) -} diff --git a/src/components/Asset/AssetActions/Trade/Slippage.module.css b/src/components/Asset/AssetActions/Trade/Slippage.module.css deleted file mode 100644 index 47041a7f2..000000000 --- a/src/components/Asset/AssetActions/Trade/Slippage.module.css +++ /dev/null @@ -1,30 +0,0 @@ -.slippage { - font-size: var(--font-size-small); - border-top: 1px solid var(--border-color); - margin-left: -2rem; - margin-right: -2rem; - padding: calc(var(--spacer) / 4) var(--spacer); - color: var(--color-secondary); - display: grid; - grid-template-columns: 1fr 1fr; - gap: calc(var(--spacer) / 3); -} - -.slippage strong { - font-weight: var(--font-weight-base); - text-align: right; -} - -.title { - font-family: var(--font-family-base); - font-weight: var(--font-weight-base); - font-size: var(--font-size-mini); - text-align: center; - margin-bottom: calc(var(--spacer) / 4); - color: var(--color-secondary); -} - -.slippage select { - width: fit-content; - display: inline-block; -} diff --git a/src/components/Asset/AssetActions/Trade/Slippage.tsx b/src/components/Asset/AssetActions/Trade/Slippage.tsx deleted file mode 100644 index f8b2b9373..000000000 --- a/src/components/Asset/AssetActions/Trade/Slippage.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { FormikContextType, useFormikContext } from 'formik' -import React, { ChangeEvent, ReactElement } from 'react' -import InputElement from '@shared/FormInput/InputElement' -import Tooltip from '@shared/atoms/Tooltip' -import styles from './Slippage.module.css' -import { FormTradeData } from './_types' -import { slippagePresets } from './_constants' - -export default function Slippage({ - disabled -}: { - disabled: boolean -}): ReactElement { - // Connect with form - const { setFieldValue, values }: FormikContextType<FormTradeData> = - useFormikContext() - - function handleChange(e: ChangeEvent<HTMLSelectElement>) { - setFieldValue('slippage', e.target.value) - } - - return ( - <div className={styles.slippage}> - <strong>Slippage Tolerance</strong> - <div> - <InputElement - name="slippage" - type="select" - size="mini" - postfix="%" - sortOptions={false} - options={slippagePresets} - value={values.slippage} - disabled={disabled} - onChange={handleChange} - /> - <Tooltip content="Your transaction will revert if the price changes unfavorably by more than this percentage." /> - </div> - </div> - ) -} diff --git a/src/components/Asset/AssetActions/Trade/Swap.module.css b/src/components/Asset/AssetActions/Trade/Swap.module.css deleted file mode 100644 index 50b399ce6..000000000 --- a/src/components/Asset/AssetActions/Trade/Swap.module.css +++ /dev/null @@ -1,27 +0,0 @@ -.swap { - margin-top: -2rem; -} - -.swapButton, -.swapButton:hover, -.swapButton:focus, -.swapButton:active { - padding: 0; - display: block; - width: calc(100% + 4rem); - text-align: center; - margin-left: -2rem; - margin-right: -2rem; - border-top: 1px solid var(--border-color); - border-bottom: 1px solid var(--border-color); - padding: calc(var(--spacer) / 3) 0 calc(var(--spacer) / 6) 0; - transform: none; -} - -.swapButton svg { - display: inline-block; - width: var(--font-size-large); - height: var(--font-size-large); - fill: var(--brand-pink); - transform: rotate(90deg); -} diff --git a/src/components/Asset/AssetActions/Trade/Swap.tsx b/src/components/Asset/AssetActions/Trade/Swap.tsx deleted file mode 100644 index fa615095d..000000000 --- a/src/components/Asset/AssetActions/Trade/Swap.tsx +++ /dev/null @@ -1,355 +0,0 @@ -import React, { ReactElement, useEffect, useState } from 'react' -import styles from './Swap.module.css' -import TradeInput from './TradeInput' -import Button from '@shared/atoms/Button' -import Arrow from '@images/arrow.svg' -import { FormikContextType, useFormikContext } from 'formik' -import Output from './Output' -import Slippage from './Slippage' -import PriceImpact from './PriceImpact' -import Decimal from 'decimal.js' -import { useAsset } from '@context/Asset' -import { useWeb3 } from '@context/Web3' -import { FormTradeData, TradeItem } from './_types' -import { - calcMaxExactIn, - calcMaxExactOut, - LoggerInstance, - Pool, - PoolPriceAndFees -} from '@oceanprotocol/lib' -import { AssetExtended } from 'src/@types/AssetExtended' -import { usePool } from '@context/Pool' -import { MAX_DECIMALS } from '@utils/constants' -import { useMarketMetadata } from '@context/MarketMetadata' - -// Decimal.set({ toExpNeg: -15, precision: 5, rounding: Decimal.ROUND_DOWN }) - -export default function Swap({ - asset, - balance, - setMaximumDt, - setMaximumBaseToken, - setCoin, - isLoading -}: { - asset: AssetExtended - balance: PoolBalance - setMaximumDt: (value: string) => void - setMaximumBaseToken: (value: string) => void - setCoin: (value: string) => void - isLoading: boolean -}): ReactElement { - const { isAssetNetwork } = useAsset() - const { web3 } = useWeb3() - const { poolInfo, poolData } = usePool() - const { appConfig } = useMarketMetadata() - - const [baseTokenItem, setBaseTokenItem] = useState<TradeItem>({ - amount: '0', - token: poolInfo?.baseTokenSymbol, - maxAmount: '0', - address: poolInfo?.baseTokenAddress - }) - const [dtItem, setDtItem] = useState<TradeItem>({ - amount: '0', - token: poolInfo?.datatokenSymbol, - maxAmount: '0', - address: poolInfo?.datatokenAddress - }) - const { - setFieldValue, - values, - setErrors, - validateForm - }: FormikContextType<FormTradeData> = useFormikContext() - - // Values used for calculation of price impact - const [spotPrice, setSpotPrice] = useState<string>() - const [totalValue, setTotalValue] = useState<string>() - const [tokenAmount, setTokenAmount] = useState<string>() - const [lpSwapFee, setLpSwapFee] = useState<string>() - - useEffect(() => { - if (!asset || !balance || !values?.type || !web3 || !appConfig || !poolInfo) - return - const poolInstance = new Pool(web3) - - async function calculateMaximum() { - const datatokenLiquidity = await poolInstance.getReserve( - poolData.id, - poolData.datatoken.address, - poolData.datatoken.decimals - ) - const baseTokenLiquidity = await poolInstance.getReserve( - poolData.id, - poolData.baseToken.address, - poolData.baseToken.decimals - ) - if (values.type === 'buy') { - const maxBaseTokenFromPool = calcMaxExactIn(baseTokenLiquidity) - - const maxBaseTokens = maxBaseTokenFromPool.greaterThan( - new Decimal(balance.baseToken) - ) - ? balance.baseToken - : maxBaseTokenFromPool.toDecimalPlaces(MAX_DECIMALS).toString() - - const maxDt = await poolInstance.getAmountOutExactIn( - asset.accessDetails?.addressOrId, - poolInfo.baseTokenAddress, - poolInfo.datatokenAddress, - maxBaseTokens.toString(), - appConfig.consumeMarketPoolSwapFee, - poolInfo.baseTokenDecimals, - poolInfo.datatokenDecimals - ) - const maximumDt = new Decimal(maxDt.tokenAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - setMaximumDt(maximumDt) - setMaximumBaseToken(maxBaseTokens) - - setBaseTokenItem((prevState) => ({ - ...prevState, - amount: balance.baseToken, - maxAmount: maxBaseTokens - })) - - setDtItem((prevState) => ({ - ...prevState, - amount: datatokenLiquidity, - maxAmount: maximumDt - })) - } else { - const maxDtFromPool = calcMaxExactIn(datatokenLiquidity) - const maxDatatokens = maxDtFromPool.greaterThan( - new Decimal(balance.datatoken) - ) - ? balance.datatoken - : maxDtFromPool.toDecimalPlaces(MAX_DECIMALS).toString() - - const maxBaseTokens = await poolInstance.getAmountOutExactIn( - asset.accessDetails?.addressOrId, - poolInfo?.datatokenAddress, - poolInfo?.baseTokenAddress, - maxDatatokens.toString(), - appConfig.consumeMarketPoolSwapFee, - poolInfo.datatokenDecimals, - poolInfo.baseTokenDecimals - ) - const maximumBasetokens = new Decimal(maxBaseTokens.tokenAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - setMaximumDt(maxDatatokens) - setMaximumBaseToken(maximumBasetokens) - - setDtItem((prevState) => ({ - ...prevState, - amount: balance.datatoken, - maxAmount: maxDatatokens - })) - setBaseTokenItem((prevState) => ({ - ...prevState, - amount: baseTokenLiquidity, - maxAmount: maximumBasetokens - })) - } - } - calculateMaximum() - }, [ - poolData, - balance, - values.type, - setMaximumDt, - setMaximumBaseToken, - asset, - web3, - dtItem.token, - dtItem.amount, - baseTokenItem.token, - baseTokenItem.amount, - appConfig, - poolInfo - ]) - - const switchTokens = () => { - setFieldValue('type', values.type === 'buy' ? 'sell' : 'buy') - setCoin( - values.type === 'sell' - ? poolInfo.baseTokenSymbol - : poolInfo.datatokenSymbol - ) - // don't reset form because we don't want to reset type - setFieldValue('datatoken', 0) - setFieldValue('baseToken', 0) - setErrors({}) - } - - const handleValueChange = async (name: string, value: number) => { - try { - let tokenIn = '' - let tokenOut = '' - const poolInstance = new Pool(web3) - let newValue: PoolPriceAndFees - - if (name === 'baseToken') { - if (values.type === 'sell') { - newValue = await poolInstance.getAmountInExactOut( - asset.accessDetails.addressOrId, - dtItem.address, - baseTokenItem.address, - value.toString(), - appConfig.consumeMarketPoolSwapFee - ) - - setFieldValue('output', 'exactOut') - - setTotalValue( - new Decimal(newValue.tokenAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - ) - setLpSwapFee( - new Decimal(newValue.liquidityProviderSwapFeeAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - ) - setTokenAmount(value.toString()) - - tokenIn = poolInfo.datatokenAddress - tokenOut = poolInfo.baseTokenAddress - } else { - newValue = await poolInstance.getAmountOutExactIn( - asset.accessDetails.addressOrId, - baseTokenItem.address, - dtItem.address, - value.toString(), - appConfig.consumeMarketPoolSwapFee - ) - - setFieldValue('output', 'exactIn') - setTotalValue(value.toString()) - setTokenAmount( - new Decimal(newValue.tokenAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - ) - setLpSwapFee( - new Decimal(newValue.liquidityProviderSwapFeeAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - ) - tokenIn = poolInfo.baseTokenAddress - tokenOut = poolInfo.datatokenAddress - } - } else { - if (values.type === 'sell') { - newValue = await poolInstance.getAmountOutExactIn( - asset.accessDetails.addressOrId, - dtItem.address, - baseTokenItem.address, - value.toString(), - appConfig.consumeMarketPoolSwapFee - ) - - setFieldValue('output', 'exactIn') - setTotalValue(value.toString()) - setTokenAmount( - new Decimal(newValue.tokenAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - ) - setLpSwapFee( - new Decimal(newValue.liquidityProviderSwapFeeAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - ) - tokenIn = poolInfo.datatokenAddress - tokenOut = poolInfo.baseTokenAddress - } else { - newValue = await poolInstance.getAmountInExactOut( - asset.accessDetails.addressOrId, - baseTokenItem.address, - dtItem.address, - value.toString(), - appConfig.consumeMarketPoolSwapFee - ) - - setFieldValue('output', 'exactOut') - - setTotalValue( - new Decimal(newValue.tokenAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - ) - setTokenAmount(value.toString()) - setLpSwapFee( - new Decimal(newValue.liquidityProviderSwapFeeAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - ) - tokenIn = poolInfo.baseTokenAddress - tokenOut = poolInfo.datatokenAddress - } - } - - await setFieldValue( - name === 'baseToken' ? 'datatoken' : 'baseToken', - new Decimal(newValue.tokenAmount) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - ) - - const spotPrice = await poolInstance.getSpotPrice( - asset.accessDetails.addressOrId, - tokenIn, - tokenOut, - appConfig.consumeMarketPoolSwapFee - ) - setSpotPrice(spotPrice) - validateForm() - } catch (ex) { - LoggerInstance.error(ex) - } - } - - return ( - <div className={styles.swap}> - <TradeInput - name={values.type === 'sell' ? 'datatoken' : 'baseToken'} - item={values.type === 'sell' ? dtItem : baseTokenItem} - disabled={!isAssetNetwork || isLoading} - handleValueChange={handleValueChange} - /> - - <Button - className={styles.swapButton} - style="text" - onClick={switchTokens} - disabled={!isAssetNetwork || isLoading} - > - <Arrow /> - </Button> - - <TradeInput - name={values.type === 'sell' ? 'baseToken' : 'datatoken'} - item={values.type === 'sell' ? baseTokenItem : dtItem} - disabled={!isAssetNetwork || isLoading} - handleValueChange={handleValueChange} - /> - - <Output - poolAddress={asset.accessDetails?.addressOrId} - lpSwapFee={lpSwapFee} - /> - - <PriceImpact - totalValue={totalValue} - tokenAmount={tokenAmount} - spotPrice={spotPrice} - /> - <Slippage disabled={!isAssetNetwork || isLoading} /> - </div> - ) -} diff --git a/src/components/Asset/AssetActions/Trade/TradeInput.module.css b/src/components/Asset/AssetActions/Trade/TradeInput.module.css deleted file mode 100644 index 860d36436..000000000 --- a/src/components/Asset/AssetActions/Trade/TradeInput.module.css +++ /dev/null @@ -1,34 +0,0 @@ -.tradeInput { - position: relative; - padding: var(--spacer) calc(var(--spacer) * 2); - margin-left: -2rem; - margin-right: -2rem; - background: var(--background-highlight); -} - -.tradeInput input { - text-align: center; -} - -.tradeInput div[class*='field'] { - margin-bottom: 0; -} - -.tradeInput div[class*='prefix'] { - min-width: 6.5rem; - justify-content: center; -} - -.label { - font-family: var(--font-family-heading); - font-size: var(--font-size-base); - text-align: center; - display: block; -} - -.buttonMax { - position: absolute; - font-size: var(--font-size-mini); - bottom: calc(var(--spacer) / 2); - right: calc(var(--spacer) * 2); -} diff --git a/src/components/Asset/AssetActions/Trade/TradeInput.tsx b/src/components/Asset/AssetActions/Trade/TradeInput.tsx deleted file mode 100644 index e3b01c7bd..000000000 --- a/src/components/Asset/AssetActions/Trade/TradeInput.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { ChangeEvent, ReactElement } from 'react' -import styles from './TradeInput.module.css' -import { - Field, - FieldInputProps, - FormikContextType, - useFormikContext, - useField -} from 'formik' -import debounce from 'lodash.debounce' -import Button from '@shared/atoms/Button' -import Input from '@shared/FormInput' -import Error from '@shared/FormInput/Error' -import { FormTradeData, TradeItem } from './_types' -import UserLiquidity from '../UserLiquidity' -import { useWeb3 } from '@context/Web3' - -export default function TradeInput({ - name, - item, - disabled, - handleValueChange -}: { - name: string - item: TradeItem - disabled: boolean - handleValueChange: (name: string, value: number) => void -}): ReactElement { - const { accountId } = useWeb3() - - // Connect with form - const { - handleChange, - validateForm, - values - }: FormikContextType<FormTradeData> = useFormikContext() - const [field, meta] = useField(name) - const isTopField = - (name === 'baseToken' && values.type === 'buy') || - (name === 'datatoken' && values.type === 'sell') - const titleAvailable = isTopField ? `Your Balance` : `Pool Balance` - const titleMaximum = isTopField ? `Maximum to spend` : `Maximum to receive` - - return ( - <section className={styles.tradeInput}> - <UserLiquidity - amount={`${item?.amount}`} - amountMax={`${item?.maxAmount}`} - symbol={item?.token} - titleAvailable={titleAvailable} - titleMaximum={titleMaximum} - /> - - <Field name={name}> - {({ field, form }: { field: FieldInputProps<number>; form: any }) => ( - <Input - type="number" - max={`${item?.maxAmount}`} - min="0" - prefix={item?.token} - placeholder="0" - field={field} - form={form} - disabled={!accountId || disabled} - additionalComponent={<Error meta={meta} />} - value={`${field.value}`} - {...field} - onChange={(e: ChangeEvent<HTMLInputElement>) => { - handleChange(e) - handleValueChange(name, Number(e.target.value)) - // debounce needed to avoid validating the wrong (pass) value - debounce(() => validateForm(), 100) - }} - /> - )} - </Field> - - {/* {!isTopField && ( - <Button - className={styles.buttonMax} - disabled={disabled} - style="text" - size="small" - onClick={() => { - setFieldValue(name, item?.maxAmount) - handleValueChange(name, Number(item?.maxAmount)) - }} - > - Use Max - </Button> - )} */} - </section> - ) -} diff --git a/src/components/Asset/AssetActions/Trade/_constants.ts b/src/components/Asset/AssetActions/Trade/_constants.ts deleted file mode 100644 index e9cc62c7b..000000000 --- a/src/components/Asset/AssetActions/Trade/_constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { FormTradeData } from './_types' - -export const initialValues: FormTradeData = { - baseToken: undefined, - datatoken: undefined, - type: 'buy', - output: 'exactIn', - slippage: '5' -} - -export const slippagePresets = ['5', '10', '15', '25', '50'] - -// validationSchema lives in components/organisms/AssetActions/Trade/FormTrade.tsx diff --git a/src/components/Asset/AssetActions/Trade/_types.ts b/src/components/Asset/AssetActions/Trade/_types.ts deleted file mode 100644 index ea5ded0da..000000000 --- a/src/components/Asset/AssetActions/Trade/_types.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface FormTradeData extends PoolBalance { - // in reference to datatoken, buy = swap from ocean to dt ( buy dt) , sell = swap from dt to ocean (sell dt) - type: 'buy' | 'sell' - // based on what the user inputs, if he fill the top input it is 'exactIn' - output: 'exactIn' | 'exactOut' - slippage: string -} - -export interface TradeItem { - amount: string - token: string - maxAmount: string - address: string -} diff --git a/src/components/Asset/AssetActions/Trade/index.tsx b/src/components/Asset/AssetActions/Trade/index.tsx deleted file mode 100644 index 0d0875aa9..000000000 --- a/src/components/Asset/AssetActions/Trade/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { ReactElement, useEffect, useState } from 'react' -import FormTrade from './FormTrade' -import { useAsset } from '@context/Asset' -import { useWeb3 } from '@context/Web3' -import { isValidNumber } from '@utils/numbers' -import Decimal from 'decimal.js' -import { Datatoken, LoggerInstance, Pool } from '@oceanprotocol/lib' -import { usePool } from '@context/Pool' - -Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 }) - -export default function Trade(): ReactElement { - const { accountId, balance, web3 } = useWeb3() - const { isAssetNetwork } = useAsset() - const [tokenBalance, setTokenBalance] = useState<PoolBalance>() - const { asset } = useAsset() - const { poolInfo } = usePool() - - // Get datatoken balance, and combine with OCEAN balance from hooks into one object - useEffect(() => { - if ( - !web3 || - !accountId || - !isAssetNetwork || - !balance?.ocean || - !accountId || - !poolInfo?.datatokenAddress - ) - return - - async function getTokenBalance() { - const datatokenInstance = new Datatoken(web3) - const dtBalance = await datatokenInstance.balance( - poolInfo.datatokenAddress, - accountId - ) - setTokenBalance({ - baseToken: new Decimal(balance.ocean).toString(), - datatoken: new Decimal(dtBalance).toString() - }) - } - getTokenBalance() - }, [ - web3, - balance.ocean, - accountId, - poolInfo?.datatokenAddress, - isAssetNetwork - ]) - - return <FormTrade asset={asset} balance={tokenBalance} /> -} diff --git a/src/components/Asset/AssetActions/UserLiquidity.module.css b/src/components/Asset/AssetActions/UserLiquidity.module.css deleted file mode 100644 index 0e7c5763f..000000000 --- a/src/components/Asset/AssetActions/UserLiquidity.module.css +++ /dev/null @@ -1,12 +0,0 @@ -.userLiquidity > div { - display: flex; - justify-content: space-between; - align-items: center; - font-size: var(--font-size-mini); - color: var(--color-secondary); -} - -.userLiquidity span + div { - transform: scale(0.8); - transform-origin: right center; -} diff --git a/src/components/Asset/AssetActions/UserLiquidity.tsx b/src/components/Asset/AssetActions/UserLiquidity.tsx deleted file mode 100644 index 772fa2f73..000000000 --- a/src/components/Asset/AssetActions/UserLiquidity.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { ReactElement } from 'react' -import PriceUnit from '@shared/Price/PriceUnit' -import styles from './UserLiquidity.module.css' - -function UserLiquidityLine({ - title, - amount, - symbol -}: { - title: string - amount: string - symbol: string -}) { - return ( - <div> - <span>{title}</span> - <PriceUnit price={amount} symbol={symbol} size="small" /> - </div> - ) -} - -export default function UserLiquidity({ - amount, - symbol, - amountMax, - titleAvailable = 'Balance', - titleMaximum = 'Maximum' -}: { - amount: string - symbol: string - titleAvailable?: string - titleMaximum?: string - amountMax?: string -}): ReactElement { - return ( - <div className={styles.userLiquidity}> - <UserLiquidityLine - title={titleAvailable} - amount={amount} - symbol={symbol} - /> - {amountMax && ( - <UserLiquidityLine - title={titleMaximum} - amount={amountMax} - symbol={symbol} - /> - )} - </div> - ) -} diff --git a/src/components/Asset/AssetActions/index.tsx b/src/components/Asset/AssetActions/index.tsx index 83fbabc55..b4118812c 100644 --- a/src/components/Asset/AssetActions/index.tsx +++ b/src/components/Asset/AssetActions/index.tsx @@ -4,8 +4,6 @@ import Consume from './Download' import { FileInfo, LoggerInstance, Datatoken } from '@oceanprotocol/lib' import Tabs, { TabsItem } from '@shared/atoms/Tabs' import { compareAsBN } from '@utils/numbers' -import Pool from './Pool' -import Trade from './Trade' import { useAsset } from '@context/Asset' import { useWeb3 } from '@context/Web3' import Web3Feedback from '@shared/Web3Feedback' @@ -16,8 +14,6 @@ import { useIsMounted } from '@hooks/useIsMounted' import styles from './index.module.css' import { useFormikContext } from 'formik' import { FormPublishData } from 'src/components/Publish/_types' -import { AssetExtended } from 'src/@types/AssetExtended' -import PoolProvider from '@context/Pool' import AssetStats from './AssetStats' export default function AssetActions({ @@ -148,18 +144,13 @@ export default function AssetActions({ const tabs: TabsItem[] = [{ title: 'Use', content: UseContent }] - asset?.accessDetails?.type === 'dynamic' && - tabs.push({ title: 'Pool', content: <Pool /> }) - return ( <> - <PoolProvider> - <Tabs items={tabs} className={styles.actions} /> - <Web3Feedback - networkId={asset?.chainId} - isAssetNetwork={isAssetNetwork} - /> - </PoolProvider> + <Tabs items={tabs} className={styles.actions} /> + <Web3Feedback + networkId={asset?.chainId} + isAssetNetwork={isAssetNetwork} + /> </> ) } diff --git a/src/components/Asset/AssetContent/MetaMain/MetaAsset.tsx b/src/components/Asset/AssetContent/MetaMain/MetaAsset.tsx index 22877d365..6418bec11 100644 --- a/src/components/Asset/AssetContent/MetaMain/MetaAsset.tsx +++ b/src/components/Asset/AssetContent/MetaMain/MetaAsset.tsx @@ -5,7 +5,6 @@ import AddToken from '@shared/AddToken' import ExplorerLink from '@shared/ExplorerLink' import Publisher from '@shared/Publisher' import React, { ReactElement } from 'react' -import { AssetExtended } from 'src/@types/AssetExtended' import styles from './MetaAsset.module.css' export default function MetaAsset({ diff --git a/src/components/Asset/AssetContent/MetaMain/MetaInfo.tsx b/src/components/Asset/AssetContent/MetaMain/MetaInfo.tsx index 3188aca70..b2f092a95 100644 --- a/src/components/Asset/AssetContent/MetaMain/MetaInfo.tsx +++ b/src/components/Asset/AssetContent/MetaMain/MetaInfo.tsx @@ -1,10 +1,8 @@ -import { useAsset } from '@context/Asset' import AssetType from '@shared/AssetType' import Time from '@shared/atoms/Time' import Publisher from '@shared/Publisher' import { getServiceByName } from '@utils/ddo' import React, { ReactElement } from 'react' -import { AssetExtended } from 'src/@types/AssetExtended' import styles from './MetaInfo.module.css' export default function MetaInfo({ diff --git a/src/components/Asset/AssetContent/MetaMain/index.tsx b/src/components/Asset/AssetContent/MetaMain/index.tsx index 463444a49..223e73cee 100644 --- a/src/components/Asset/AssetContent/MetaMain/index.tsx +++ b/src/components/Asset/AssetContent/MetaMain/index.tsx @@ -3,7 +3,6 @@ import styles from './index.module.css' import MetaAsset from './MetaAsset' import MetaInfo from './MetaInfo' import Nft from '../Nft' -import { AssetExtended } from 'src/@types/AssetExtended' const blockscoutNetworks = [1287, 2021000, 2021001, 44787, 246, 1285] diff --git a/src/components/Asset/AssetContent/index.tsx b/src/components/Asset/AssetContent/index.tsx index ab779e568..63736dc55 100644 --- a/src/components/Asset/AssetContent/index.tsx +++ b/src/components/Asset/AssetContent/index.tsx @@ -1,5 +1,4 @@ import React, { ReactElement, useState, useEffect } from 'react' -import Link from 'next/link' import Markdown from '@shared/Markdown' import MetaFull from './MetaFull' import MetaSecondary from './MetaSecondary' @@ -14,7 +13,6 @@ import EditHistory from './EditHistory' import styles from './index.module.css' import NetworkName from '@shared/NetworkName' import content from '../../../../content/purgatory.json' -import { AssetExtended } from 'src/@types/AssetExtended' import Web3 from 'web3' import Button from '@shared/atoms/Button' diff --git a/src/components/Asset/Edit/EditComputeDataset.tsx b/src/components/Asset/Edit/EditComputeDataset.tsx index 9b9618252..400ad5eb8 100644 --- a/src/components/Asset/Edit/EditComputeDataset.tsx +++ b/src/components/Asset/Edit/EditComputeDataset.tsx @@ -17,7 +17,6 @@ import { computeSettingsValidationSchema } from './_constants' import content from '../../../../content/pages/editComputeDataset.json' -import { AssetExtended } from 'src/@types/AssetExtended' import { getServiceByName } from '@utils/ddo' import { setMinterToPublisher, setMinterToDispenser } from '@utils/dispenser' import { transformComputeFormToServiceComputeOptions } from '@utils/compute' diff --git a/src/components/Asset/Edit/EditMetadata.tsx b/src/components/Asset/Edit/EditMetadata.tsx index e559aa4d9..b8e440776 100644 --- a/src/components/Asset/Edit/EditMetadata.tsx +++ b/src/components/Asset/Edit/EditMetadata.tsx @@ -16,7 +16,6 @@ import FormEditMetadata from './FormEditMetadata' import { mapTimeoutStringToSeconds } from '@utils/ddo' import styles from './index.module.css' import content from '../../../../content/pages/editMetadata.json' -import { AssetExtended } from 'src/@types/AssetExtended' import { useAbortController } from '@hooks/useAbortController' import DebugEditMetadata from './DebugEditMetadata' import { getOceanConfig } from '@utils/ocean' diff --git a/src/components/Header/UserPreferences/TokenApproval.tsx b/src/components/Header/UserPreferences/TokenApproval.tsx index ad35904e0..e0c49ae0c 100644 --- a/src/components/Header/UserPreferences/TokenApproval.tsx +++ b/src/components/Header/UserPreferences/TokenApproval.tsx @@ -9,7 +9,7 @@ export default function TokenApproval(): ReactElement { <li> <Input label="Token Approvals" - help="Use infinite amount when approving tokens in _Use_, _Pool_, or _Trade_." + help="Use infinite amount when approving tokens in _Use_." name="infiniteApproval" type="checkbox" options={['Allow infinite amount']} diff --git a/src/components/Home/Bookmarks.tsx b/src/components/Home/Bookmarks.tsx index 1ec9314d0..5772b9a3e 100644 --- a/src/components/Home/Bookmarks.tsx +++ b/src/components/Home/Bookmarks.tsx @@ -7,7 +7,6 @@ import Tooltip from '@shared/atoms/Tooltip' import AssetTitle from '@shared/AssetList/AssetListTitle' import { retrieveDDOListByDIDs } from '@utils/aquarius' import { useCancelToken } from '@hooks/useCancelToken' -import { AssetExtended } from 'src/@types/AssetExtended' import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing' import { useWeb3 } from '@context/Web3' import { useMarketMetadata } from '@context/MarketMetadata' diff --git a/src/components/Home/index.tsx b/src/components/Home/index.tsx index 9034edea1..26b27ad4d 100644 --- a/src/components/Home/index.tsx +++ b/src/components/Home/index.tsx @@ -2,12 +2,7 @@ import React, { ReactElement, useEffect, useState } from 'react' import AssetList from '@shared/AssetList' import Button from '@shared/atoms/Button' import Bookmarks from './Bookmarks' -import { - generateBaseQuery, - getFilterTerm, - queryMetadata -} from '@utils/aquarius' -import { getHighestLiquidityDatatokens } from '@utils/subgraph' +import { generateBaseQuery, queryMetadata } from '@utils/aquarius' import { Asset, LoggerInstance } from '@oceanprotocol/lib' import { useUserPreferences } from '@context/UserPreferences' import styles from './index.module.css' @@ -16,21 +11,6 @@ import { useCancelToken } from '@hooks/useCancelToken' import { SortTermOptions } from '../../@types/aquarius/SearchQuery' import PublishersWithMostSales from './PublishersWithMostSales' -async function getQueryHighest( - chainIds: number[] -): Promise<[SearchQuery, string[]]> { - const dtList = await getHighestLiquidityDatatokens(chainIds) - const baseQueryParams = { - chainIds, - esPaginationOptions: { - size: dtList.length > 0 ? dtList.length : 1 - }, - filters: [getFilterTerm('services.datatokenAddress', dtList)] - } as BaseQueryParams - const queryHighest = generateBaseQuery(baseQueryParams) - return [queryHighest, dtList] -} - function sortElements(items: Asset[], sorted: string[]) { items.sort(function (a, b) { return ( @@ -109,15 +89,11 @@ function SectionQueryResult({ } export default function HomePage(): ReactElement { - const [queryAndDids, setQueryAndDids] = useState<[SearchQuery, string[]]>() const [queryLatest, setQueryLatest] = useState<SearchQuery>() + const [queryMostSales, setQueryMostSales] = useState<SearchQuery>() const { chainIds } = useUserPreferences() useEffect(() => { - getQueryHighest(chainIds).then((results) => { - setQueryAndDids(results) - }) - const baseParams = { chainIds, esPaginationOptions: { @@ -128,6 +104,17 @@ export default function HomePage(): ReactElement { } as SortOptions } as BaseQueryParams setQueryLatest(generateBaseQuery(baseParams)) + + const baseParamsSales = { + chainIds, + esPaginationOptions: { + size: 9 + }, + sortOptions: { + sortBy: SortTermOptions.Stats + } as SortOptions + } as BaseQueryParams + setQueryMostSales(generateBaseQuery(baseParamsSales)) }, [chainIds]) return ( @@ -137,11 +124,7 @@ export default function HomePage(): ReactElement { <Bookmarks /> </section> - <SectionQueryResult - title="Highest Liquidity" - query={queryAndDids?.[0]} - queryData={queryAndDids?.[1]} - /> + <SectionQueryResult title="Most Sales" query={queryMostSales} /> <SectionQueryResult title="Recently Published" diff --git a/src/components/Profile/Header/Stats.tsx b/src/components/Profile/Header/Stats.tsx index 590e3cdb9..b3402c3ac 100644 --- a/src/components/Profile/Header/Stats.tsx +++ b/src/components/Profile/Header/Stats.tsx @@ -1,31 +1,11 @@ import { LoggerInstance } from '@oceanprotocol/lib' import React, { useEffect, useState, ReactElement } from 'react' import { useUserPreferences } from '@context/UserPreferences' -import { getAccountLiquidityInOwnAssets } from '@utils/subgraph' import Conversion from '@shared/Price/Conversion' import NumberUnit from './NumberUnit' import styles from './Stats.module.css' import { useProfile } from '@context/Profile' -import { PoolShares_poolShares as PoolShare } from '../../../@types/subgraph/PoolShares' import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing' -import { calcSingleOutGivenPoolIn } from '@utils/pool' -import Decimal from 'decimal.js' -import { MAX_DECIMALS } from '@utils/constants' - -function getPoolSharesLiquidity(poolShares: PoolShare[]): string { - let liquidity = new Decimal(0) - - for (const poolShare of poolShares) { - const poolUserLiquidity = calcSingleOutGivenPoolIn( - poolShare.pool.baseTokenLiquidity, - poolShare.pool.totalShares, - poolShare.shares - ) - liquidity = liquidity.add(new Decimal(poolUserLiquidity)) - } - - return liquidity.toDecimalPlaces(MAX_DECIMALS).toString() -} export default function Stats({ accountId @@ -33,68 +13,36 @@ export default function Stats({ accountId: string }): ReactElement { const { chainIds } = useUserPreferences() - const { poolShares, assets, assetsTotal, sales } = useProfile() + const { assets, assetsTotal, sales } = useProfile() - const [publisherTvl, setPublisherTvl] = useState('0') - const [totalTvl, setTotalTvl] = useState('0') - - useEffect(() => { - if (!accountId || chainIds.length === 0) { - setPublisherTvl('0') - setTotalTvl('0') - } - }, [accountId, chainIds]) + const [totalSales, setTotalSales] = useState('0') useEffect(() => { if (!assets || !accountId || !chainIds) return - async function getPublisherLiquidity() { + async function getPublisherTotalSales() { try { - const accountPoolAdresses: string[] = [] const assetsPrices = await getAccessDetailsForAssets(assets) + let count = 0 for (const priceInfo of assetsPrices) { - if (priceInfo.accessDetails.type === 'dynamic') { - accountPoolAdresses.push( - priceInfo.accessDetails.addressOrId.toLowerCase() - ) + if (priceInfo?.accessDetails?.price && priceInfo.stats.orders > 0) { + count += + parseInt(priceInfo.accessDetails.price) * priceInfo.stats.orders } } - const userTvl = await getAccountLiquidityInOwnAssets( - accountId, - chainIds, - accountPoolAdresses - ) - setPublisherTvl(userTvl) + setTotalSales(JSON.stringify(count)) } catch (error) { LoggerInstance.error(error.message) } } - getPublisherLiquidity() + getPublisherTotalSales() }, [assets, accountId, chainIds]) - useEffect(() => { - if (!poolShares) return - - async function getTotalLiquidity() { - try { - const totalTvl = await getPoolSharesLiquidity(poolShares) - setTotalTvl(totalTvl) - } catch (error) { - LoggerInstance.error('Error fetching pool shares: ', error.message) - } - } - getTotalLiquidity() - }, [poolShares]) - return ( <div className={styles.stats}> <NumberUnit - label="Liquidity in Own Assets" - value={<Conversion price={publisherTvl} hideApproximateSymbol />} - /> - <NumberUnit - label="Liquidity" - value={<Conversion price={totalTvl} hideApproximateSymbol />} + label="Total Sales" + value={<Conversion price={totalSales} hideApproximateSymbol />} /> <NumberUnit label={`Sale${sales === 1 ? '' : 's'}`} value={sales} /> <NumberUnit label="Published" value={assetsTotal} /> diff --git a/src/components/Profile/History/PoolShares/Liquidity.module.css b/src/components/Profile/History/PoolShares/Liquidity.module.css deleted file mode 100644 index b5fba26fa..000000000 --- a/src/components/Profile/History/PoolShares/Liquidity.module.css +++ /dev/null @@ -1,37 +0,0 @@ -.totalLiquidity { - margin-bottom: 0; - font-weight: var(--font-weight-base) !important; - font-size: var(--font-size-small); - padding-left: var(--font-size-base); -} - -.totalLiquidity strong { - font-size: var(--font-size-base); - color: var(--font-color-text); - line-height: 1; -} - -.userLiquidity { - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.userLiquidity [class*='Conversion_conversion'] { - margin-bottom: calc(var(--spacer) / 8); -} - -.userLiquidity [class*='Conversion_conversion'] strong { - font-size: var(--font-size-base); -} - -.userLiquidity [class*='Token_token'] { - display: flex; - align-items: center; - justify-content: flex-end; - margin-bottom: calc(var(--spacer) / 8); -} - -.userLiquidity [class*='Token_token'] div { - font-size: var(--font-size-small); -} diff --git a/src/components/Profile/History/PoolShares/Liquidity.tsx b/src/components/Profile/History/PoolShares/Liquidity.tsx deleted file mode 100644 index 718671bdc..000000000 --- a/src/components/Profile/History/PoolShares/Liquidity.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React, { useEffect, useState } from 'react' -import Conversion from '@shared/Price/Conversion' -import styles from './Liquidity.module.css' -import Token from '../../../@shared/Token' -import Decimal from 'decimal.js' -import { AssetPoolShare } from './index' -import { calcSingleOutGivenPoolIn } from '@utils/pool' - -export function Liquidity({ - row, - type -}: { - row: AssetPoolShare - type: string -}) { - const [liquidity, setLiquidity] = useState('0') - - useEffect(() => { - let calculatedLiquidity = '0' - if (type === 'user') { - calculatedLiquidity = calcSingleOutGivenPoolIn( - row.poolShare.pool.baseTokenLiquidity, - row.poolShare.pool.totalShares, - row.poolShare.shares - ) - } - if (type === 'pool') { - calculatedLiquidity = new Decimal( - row.poolShare.pool.baseTokenLiquidity - ).toString() - } - setLiquidity(calculatedLiquidity) - }, [ - row.poolShare.pool.baseTokenLiquidity, - row.poolShare.pool.totalShares, - row.poolShare.shares, - type - ]) - - return ( - <div className={styles.userLiquidity}> - <Conversion - price={liquidity} - className={styles.totalLiquidity} - hideApproximateSymbol - /> - <Token - symbol={row.poolShare.pool.baseToken.symbol} - balance={liquidity} - noIcon - /> - </div> - ) -} diff --git a/src/components/Profile/History/PoolShares/_utils.ts b/src/components/Profile/History/PoolShares/_utils.ts deleted file mode 100644 index c864f765d..000000000 --- a/src/components/Profile/History/PoolShares/_utils.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { getAssetsFromDtList } from '@utils/aquarius' -import { calcSingleOutGivenPoolIn, getLiquidityByShares } from '@utils/pool' -import { CancelToken } from 'axios' -import { PoolShares_poolShares as PoolShare } from '../../../../@types/subgraph/PoolShares' -import { AssetPoolShare } from '.' -import { Asset } from '@oceanprotocol/lib' - -export function getAsset(items: Asset[], datatoken: string): Asset { - for (let i = 0; i < items.length; i++) { - if ( - items[i].datatokens[0].address.toLowerCase() === datatoken.toLowerCase() - ) - return items[i] - } - return null -} - -export async function getAssetsFromPoolShares( - data: PoolShare[], - chainIds: number[], - cancelToken: CancelToken -) { - if (data.length < 1) return [] - - const assetList: AssetPoolShare[] = [] - const dtList: string[] = [] - - for (let i = 0; i < data.length; i++) { - dtList.push(data[i].pool.datatoken.address) - } - const ddoList = await getAssetsFromDtList(dtList, chainIds, cancelToken) - - for (let i = 0; i < data.length; i++) { - const userLiquidity = calcSingleOutGivenPoolIn( - data[i].pool.baseTokenLiquidity, - data[i].pool.totalShares, - data[i].shares - ) - assetList.push({ - poolShare: data[i], - userLiquidity, - networkId: getAsset(ddoList, data[i].pool.datatoken.address)?.chainId, - createTime: data[i].pool.createdTimestamp, - asset: getAsset(ddoList, data[i].pool.datatoken.address) - }) - } - return assetList -} diff --git a/src/components/Profile/History/PoolShares/index.module.css b/src/components/Profile/History/PoolShares/index.module.css deleted file mode 100644 index 4c59442d1..000000000 --- a/src/components/Profile/History/PoolShares/index.module.css +++ /dev/null @@ -1,18 +0,0 @@ -.poolSharesTable [role='gridcell'] { - align-items: flex-start; - margin: calc(var(--spacer) / 2) 0; -} - -.poolSharesTable [class*='AssetListTitle_title'] { - line-height: 0 !important; -} - -.poolSharesTable [class*='Token_token'] div { - color: var(--color-secondary); -} - -@media (min-width: 30rem) { - .poolSharesTable [class*='AssetListTitle_title'] { - line-height: 0 !important; - } -} diff --git a/src/components/Profile/History/PoolShares/index.tsx b/src/components/Profile/History/PoolShares/index.tsx deleted file mode 100644 index f6504885c..000000000 --- a/src/components/Profile/History/PoolShares/index.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import React, { ReactElement, useCallback, useEffect, useState } from 'react' -import Table, { TableOceanColumn } from '@shared/atoms/Table' -import styles from './index.module.css' -import AssetTitle from '@shared/AssetList/AssetListTitle' -import { PoolShares_poolShares as PoolShare } from '../../../../@types/subgraph/PoolShares' -import NetworkName from '@shared/NetworkName' -import Decimal from 'decimal.js' -import { useProfile } from '@context/Profile' -import { useCancelToken } from '@hooks/useCancelToken' -import { useIsMounted } from '@hooks/useIsMounted' -import { useUserPreferences } from '@context/UserPreferences' -import { Asset, LoggerInstance } from '@oceanprotocol/lib' -import { Liquidity } from './Liquidity' -import { getAssetsFromPoolShares } from './_utils' - -Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 }) - -export interface AssetPoolShare { - userLiquidity: string - poolShare: PoolShare - networkId: number - createTime: number - asset: Asset -} - -const columns: TableOceanColumn<AssetPoolShare>[] = [ - { - name: 'Data Set', - selector: (row) => <AssetTitle asset={row.asset} />, - grow: 2 - }, - { - name: 'Network', - selector: (row) => <NetworkName networkId={row.networkId} /> - }, - { - name: 'Your liquidity', - selector: (row) => <Liquidity row={row} type="user" />, - right: true - }, - { - name: 'Total Value Locked', - selector: (row) => <Liquidity row={row} type="pool" />, - right: true - } -] - -export default function PoolShares({ - accountId -}: { - accountId: string -}): ReactElement { - const { poolShares } = useProfile() - const { chainIds } = useUserPreferences() - const newCancelToken = useCancelToken() - const isMounted = useIsMounted() - - const [assets, setAssets] = useState<AssetPoolShare[]>() - const [loading, setLoading] = useState<boolean>(false) - - // - // Helper: fetch assets from pool shares data - // - const fetchPoolSharesAssets = useCallback(async () => { - if (!poolShares || !chainIds) return - - try { - const assets = await getAssetsFromPoolShares( - poolShares, - chainIds, - newCancelToken() - ) - setAssets(assets) - } catch (error) { - LoggerInstance.error('Error fetching pool shares: ', error.message) - } finally { - setLoading(false) - } - // We do not need to react to `chainIds` changes here, cause changing them - // triggers change of `poolShares` from `useProfile()` already. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [poolShares, newCancelToken]) - - // - // 1. Kick off data fetching - // - useEffect(() => { - if (!isMounted()) return - - setLoading(true) - fetchPoolSharesAssets() - }, [fetchPoolSharesAssets, isMounted]) - - return accountId ? ( - <Table - columns={columns} - className={styles.poolSharesTable} - data={assets} - pagination - paginationPerPage={5} - isLoading={loading} - sortField="userLiquidity" - sortAsc={false} - emptyMessage={chainIds.length === 0 ? 'No network selected' : null} - /> - ) : ( - <div>Please connect your Web3 wallet.</div> - ) -} diff --git a/src/components/Profile/History/index.tsx b/src/components/Profile/History/index.tsx index 9ec2b2adf..10af47f11 100644 --- a/src/components/Profile/History/index.tsx +++ b/src/components/Profile/History/index.tsx @@ -1,7 +1,5 @@ import React, { ReactElement } from 'react' import Tabs from '@shared/atoms/Tabs' -import PoolShares from './PoolShares' -import PoolTransactions from '@shared/PoolTransactions' import PublishedList from './PublishedList' import Downloads from './Downloads' import ComputeJobs from './ComputeJobs' @@ -19,14 +17,6 @@ function getTabs(accountId: string, userAccountId: string): HistoryTab[] { title: 'Published', content: <PublishedList accountId={accountId} /> }, - { - title: 'Pool Shares', - content: <PoolShares accountId={accountId} /> - }, - { - title: 'Pool Transactions', - content: <PoolTransactions accountId={accountId} /> - }, { title: 'Downloads', content: <Downloads accountId={accountId} /> diff --git a/src/components/Publish/Preview/index.tsx b/src/components/Publish/Preview/index.tsx index 4bf74060f..3749e5e1d 100644 --- a/src/components/Publish/Preview/index.tsx +++ b/src/components/Publish/Preview/index.tsx @@ -4,7 +4,6 @@ import { FormPublishData } from '../_types' import { useFormikContext } from 'formik' import AssetContent from 'src/components/Asset/AssetContent' import { transformPublishFormToDdo } from '../_utils' -import { AssetExtended } from 'src/@types/AssetExtended' import { ZERO_ADDRESS } from '@oceanprotocol/lib' export default function Preview(): ReactElement { @@ -18,7 +17,7 @@ export default function Preview(): ReactElement { asset.accessDetails = { type: values.pricing.type, addressOrId: ZERO_ADDRESS, - price: values.pricing.price, + price: `${values.pricing.price}`, baseToken: { address: ZERO_ADDRESS, name: 'OCEAN', @@ -31,7 +30,8 @@ export default function Preview(): ReactElement { }, isPurchasable: true, isOwned: false, - validOrderTx: '' + validOrderTx: '', + publisherMarketOrderFee: '0' } setAsset(asset) } diff --git a/src/components/Publish/Pricing/Coin.module.css b/src/components/Publish/Pricing/Coin.module.css deleted file mode 100644 index d844ebb61..000000000 --- a/src/components/Publish/Pricing/Coin.module.css +++ /dev/null @@ -1,84 +0,0 @@ -.coin { - padding: calc(var(--spacer) / 2); - padding-top: calc(var(--spacer) / 1.5); -} - -.coin:last-child { - border-left: 1px solid var(--border-color); -} - -.token { - display: flex; - align-items: center; - justify-content: flex-start; - margin-bottom: calc(var(--spacer) / 2); -} - -.icon { - composes: box from '@shared/atoms/Box.module.css'; - padding: calc(var(--spacer) / 2); - width: 4rem; - height: 4rem; - display: flex; - align-items: center; - justify-content: center; - border: 1px solid var(--border-color); - border-radius: 50%; - background-color: var(--background-body); - margin: 0; - margin-right: calc(var(--spacer) / 4); - fill: var(--font-color-text); -} - -.icon svg { - width: 100%; - height: 100%; -} - -.coin:last-child .icon { - fill: var(--brand-violet); -} - -.tokenName { - font-size: var(--font-size-base); - color: var(--font-color-text); - margin-bottom: 0; -} - -.weight { - width: 100%; - margin-bottom: 0; - text-transform: uppercase; - font-size: var(--font-size-small); - color: var(--color-secondary); -} - -.data { - position: relative; - width: 100%; - display: flex; - flex-wrap: wrap; - justify-content: center; - text-align: center; - margin: auto 0; -} - -.max { - position: absolute; - right: var(--spacer); - top: calc(var(--spacer) / 2.5); -} - -.weight strong { - color: var(--font-color-text); -} - -@media screen and (min-width: 41rem) { - .token { - justify-content: center; - } - .data { - margin: auto; - max-width: 12rem; - } -} diff --git a/src/components/Publish/Pricing/Coin.tsx b/src/components/Publish/Pricing/Coin.tsx deleted file mode 100644 index 47da24d83..000000000 --- a/src/components/Publish/Pricing/Coin.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { ReactElement } from 'react' -import styles from './Coin.module.css' -import InputElement from '@shared/FormInput/InputElement' -import Logo from '@images/logo.svg' -import Conversion from '@shared/Price/Conversion' -import { useField } from 'formik' -import Error from '@shared/FormInput/Error' - -export default function Coin({ - datatokenOptions, - name, - weight, - readOnly -}: { - datatokenOptions: { name: string; symbol: string } - name: string - weight: string - readOnly?: boolean -}): ReactElement { - const [field, meta] = useField(`pricing.${name}`) - - return ( - <div className={styles.coin}> - <div className={styles.token}> - <figure className={styles.icon}> - <Logo /> - </figure> - <div> - <h4 className={styles.tokenName}> - {datatokenOptions?.name || 'Data Token'} - </h4> - - <div className={styles.weight}> - Weight <strong>{weight}</strong> - </div> - </div> - </div> - - <div className={styles.data}> - <InputElement - type="number" - readOnly={readOnly} - prefix={datatokenOptions?.symbol || 'DT'} - min="1" - name={name} - value={field.value} - {...field} - /> - {datatokenOptions?.symbol === 'OCEAN' && ( - <Conversion price={field.value} /> - )} - <div> - <Error meta={meta} /> - </div> - </div> - </div> - ) -} diff --git a/src/components/Publish/Pricing/Dynamic.module.css b/src/components/Publish/Pricing/Dynamic.module.css deleted file mode 100644 index f2a1b3973..000000000 --- a/src/components/Publish/Pricing/Dynamic.module.css +++ /dev/null @@ -1,32 +0,0 @@ -.title { - font-size: var(--font-size-base); - margin-top: var(--spacer); - margin-bottom: 0; - padding-bottom: calc(var(--spacer) / 4); -} - -.tokens { - display: grid; - border: 1px solid var(--border-color); - background: var(--background-highlight); - border-radius: var(--border-radius); -} - -@media screen and (min-width: 40rem) { - .tokens { - grid-template-columns: 1fr 1fr; - } -} - -.subtitle { - display: inline-block; - color: var(--color-secondary); - margin-left: calc(var(--spacer) / 4); - font-family: var(--font-family-base); - font-weight: var(--font-weight-base); - font-size: var(--font-size-small); -} - -.alertArea { - padding: 0 calc(var(--spacer) / 2); -} diff --git a/src/components/Publish/Pricing/Dynamic.tsx b/src/components/Publish/Pricing/Dynamic.tsx deleted file mode 100644 index 7940aeab4..000000000 --- a/src/components/Publish/Pricing/Dynamic.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { ReactElement, useEffect, useState } from 'react' -import Alert from '@shared/atoms/Alert' -import FormHelp from '@shared/FormInput/Help' -import Tooltip from '@shared/atoms/Tooltip' -import Coin from './Coin' -import styles from './Dynamic.module.css' -import Fees from './Fees' -import { FormikContextType, useFormikContext } from 'formik' -import Price from './Price' -import Decimal from 'decimal.js' -import { useWeb3 } from '@context/Web3' -import { FormPublishData } from '../_types' - -export default function Dynamic({ content }: { content: any }): ReactElement { - const { networkId, accountId, balance } = useWeb3() - const [firstPrice, setFirstPrice] = useState<string>() - - // Connect with form - const { values }: FormikContextType<FormPublishData> = useFormikContext() - const { dataTokenOptions } = values.services[0] - - const { - weightOnDataToken, - weightOnOcean, - swapFee, - amountDataToken, - amountOcean - } = values.pricing - - const [error, setError] = useState<string>() - - // Calculate firstPrice whenever user values change - useEffect(() => { - if (`${amountOcean}` === '') return - - const tokenAmountOut = 1 - const weightRatio = new Decimal(weightOnDataToken).div( - new Decimal(weightOnOcean) - ) - const diff = new Decimal(amountDataToken).minus(tokenAmountOut) - const y = new Decimal(amountDataToken).div(diff) - const foo = y.pow(weightRatio).minus(new Decimal(1)) - const tokenAmountIn = new Decimal(amountOcean) - .times(foo) - .div(new Decimal(1).minus(new Decimal(swapFee / 100))) - setFirstPrice(`${tokenAmountIn}`) - }, [swapFee, weightOnOcean, weightOnDataToken, amountDataToken, amountOcean]) - - // Check: account, network & insufficient balance - useEffect(() => { - if (!accountId) { - setError(`No account connected. Please connect your Web3 wallet.`) - } else if (Number(balance.ocean) < Number(amountOcean)) { - setError(`Insufficient balance. You need at least ${amountOcean} OCEAN.`) - } else { - setError(undefined) - } - }, [amountOcean, networkId, accountId, balance]) - - return ( - <> - <FormHelp>{content.info}</FormHelp> - - <h4 className={styles.title}> - Price <Tooltip content={content.tooltips.poolInfo} /> - </h4> - - <Price firstPrice={firstPrice} /> - - <h4 className={styles.title}> - Datatoken Liquidity Pool <Tooltip content={content.tooltips.poolInfo} />{' '} - <span className={styles.subtitle}>100% share of pool</span> - </h4> - - <div className={styles.tokens}> - <Coin - name="amountOcean" - datatokenOptions={{ symbol: 'OCEAN', name: 'Ocean Token' }} - weight={`${Number(weightOnOcean) * 10}%`} - /> - <Coin - name="amountDataToken" - datatokenOptions={{ - symbol: dataTokenOptions.symbol, - name: dataTokenOptions.name - }} - weight={`${Number(weightOnDataToken) * 10}%`} - readOnly - /> - </div> - - <Fees tooltips={content.tooltips} pricingType="dynamic" /> - - {error && ( - <div className={styles.alertArea}> - <Alert text={error} state="error" /> - </div> - )} - </> - ) -} diff --git a/src/components/Publish/Pricing/Fees.tsx b/src/components/Publish/Pricing/Fees.tsx index c4deb2bb5..c2fa9ea7a 100644 --- a/src/components/Publish/Pricing/Fees.tsx +++ b/src/components/Publish/Pricing/Fees.tsx @@ -1,10 +1,8 @@ import React, { ReactElement, useEffect, useState } from 'react' import Tooltip from '@shared/atoms/Tooltip' import styles from './Fees.module.css' -import { useField } from 'formik' import Input from '@shared/FormInput' -import Error from '@shared/FormInput/Error' -import { getOpcFees } from '../../../@utils/subgraph' +import { getOpcFees } from '@utils/subgraph' import { OpcFeesQuery_opc as OpcFeesData } from '../../../@types/subgraph/OpcFeesQuery' import { useWeb3 } from '@context/Web3' import { useMarketMetadata } from '@context/MarketMetadata' @@ -37,13 +35,10 @@ const Default = ({ ) export default function Fees({ - tooltips, - pricingType + tooltips }: { tooltips: { [key: string]: string } - pricingType: 'dynamic' | 'fixed' }): ReactElement { - const [field, meta] = useField('pricing.swapFee') const [oceanCommunitySwapFee, setOceanCommunitySwapFee] = useState<string>('') const { chainId } = useWeb3() const { appConfig } = useMarketMetadata() @@ -61,25 +56,6 @@ export default function Fees({ return ( <> <div className={styles.fees}> - {pricingType === 'dynamic' && ( - <Input - label={ - <> - Swap Fee - <Tooltip content={tooltips.swapFee} /> - </> - } - type="number" - postfix="%" - min="0.1" - max="10" - step="0.1" - size="small" - {...field} - additionalComponent={<Error meta={meta} />} - /> - )} - <Default title="Community Swap Fee" name="communityFee" @@ -91,11 +67,7 @@ export default function Fees({ title="Marketplace Fee" name="marketplaceFee" tooltip={tooltips.marketplaceFee} - value={ - pricingType === 'dynamic' - ? appConfig?.publisherMarketPoolSwapFee - : appConfig?.publisherMarketFixedSwapFee - } + value={appConfig?.publisherMarketFixedSwapFee} /> </div> </> diff --git a/src/components/Publish/Pricing/Fixed.tsx b/src/components/Publish/Pricing/Fixed.tsx index 4f8b56cc8..f77f5f5de 100644 --- a/src/components/Publish/Pricing/Fixed.tsx +++ b/src/components/Publish/Pricing/Fixed.tsx @@ -2,7 +2,7 @@ import React, { ReactElement } from 'react' import FormHelp from '@shared/FormInput/Help' import Price from './Price' import Fees from './Fees' -import styles from './Dynamic.module.css' +import styles from './index.module.css' export default function Fixed({ content }: { content: any }): ReactElement { return ( @@ -12,7 +12,7 @@ export default function Fixed({ content }: { content: any }): ReactElement { <h4 className={styles.title}>Price</h4> <Price /> - <Fees tooltips={content.tooltips} pricingType="fixed" /> + <Fees tooltips={content.tooltips} /> </> ) } diff --git a/src/components/Publish/Pricing/Free.tsx b/src/components/Publish/Pricing/Free.tsx index 5efa3dab1..90f2ddc06 100644 --- a/src/components/Publish/Pricing/Free.tsx +++ b/src/components/Publish/Pricing/Free.tsx @@ -3,7 +3,7 @@ import { useFormikContext } from 'formik' import { FormPublishData } from '../_types' import FormHelp from '@shared/FormInput/Help' import Price from './Price' -import styles from './Dynamic.module.css' +import styles from './index.module.css' export default function Free({ content }: { content: any }): ReactElement { // connect with Form state, use for conditional field rendering diff --git a/src/components/Publish/Pricing/Price.module.css b/src/components/Publish/Pricing/Price.module.css index 843579c56..7e5849c51 100644 --- a/src/components/Publish/Pricing/Price.module.css +++ b/src/components/Publish/Pricing/Price.module.css @@ -1,72 +1,50 @@ -.form { - position: relative; -} - -.form *, -.form label { - margin-bottom: 0; -} - .price { background: var(--background-highlight); border: 1px solid var(--border-color); border-radius: var(--border-radius); + padding: calc(var(--spacer) / 1.5) calc(var(--spacer) / 2); } -.grid { - display: grid; - gap: calc(var(--spacer) / 2); - grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); - padding: calc(var(--spacer) / 2); - max-width: 30rem; - margin-left: auto; - margin-right: auto; +.form { + display: flex; + justify-content: center; align-items: center; } +.inputWrap { + position: relative; + max-width: 15rem; +} + +.inputWrap > div { + margin-bottom: 0; +} + .fixed label { display: none; } .datatoken { - color: var(--color-secondary); - font-size: var(--font-size-small); - font-weight: var(--font-weight-bold); -} - -.datatoken h4 { font-size: var(--font-size-base); color: var(--color-secondary); margin: 0; + margin-left: calc(var(--spacer) / 2); } .datatoken strong { color: var(--font-color-text); } -.firstPrice { - font-size: var(--font-size-small); - color: var(--color-secondary); - text-align: center; - padding-top: calc(var(--spacer) / 3); - padding-bottom: calc(var(--spacer) / 3); - border-top: 1px solid var(--border-color); -} - -.firstPrice div { - display: inline-block; -} - .conversion strong { color: var(--color-secondary); } .free { text-align: left; - margin: calc(var(--spacer) / 2); font-size: var(--font-size-base); } -.free [class*='FormInput_field'] { +.free [class*='FormInput_field'], +.free [class*='InputRadio_radioGroup'] { margin: 0; } diff --git a/src/components/Publish/Pricing/Price.tsx b/src/components/Publish/Pricing/Price.tsx index 70ff6dd4d..73d0b45d6 100644 --- a/src/components/Publish/Pricing/Price.tsx +++ b/src/components/Publish/Pricing/Price.tsx @@ -3,64 +3,46 @@ import { Field, useField, useFormikContext } from 'formik' import React, { ReactElement } from 'react' import Input from '@shared/FormInput' import Error from '@shared/FormInput/Error' -import PriceUnit from '@shared/Price/PriceUnit' import styles from './Price.module.css' import { FormPublishData } from '../_types' import { getFieldContent } from '@utils/form' -export default function Price({ - firstPrice, - content -}: { - firstPrice?: string - content?: any -}): ReactElement { +export default function Price({ content }: { content?: any }): ReactElement { const [field, meta] = useField('pricing.price') const { values } = useFormikContext<FormPublishData>() const { dataTokenOptions } = values.services[0] + const classNames = `${styles.price} ${ + values.pricing.type === 'free' ? styles.free : styles.fixed + }` + return ( - <div className={styles.price}> + <div className={classNames}> {values.pricing.type === 'free' ? ( - <div className={styles.free}> - <Field - {...getFieldContent('freeAgreement', content.fields)} - component={Input} - name="pricing.freeAgreement" - /> - </div> + <Field + {...getFieldContent('freeAgreement', content.fields)} + component={Input} + name="pricing.freeAgreement" + /> ) : ( - <> - <div className={styles.grid}> - <div className={styles.form}> - <Input - type="number" - min="1" - placeholder="0" - prefix="OCEAN" - {...field} - /> - <Error meta={meta} /> - </div> - <div className={styles.datatoken}> - <h4> - = <strong>1</strong> {dataTokenOptions.symbol}{' '} - <Conversion price={field.value} className={styles.conversion} /> - </h4> - </div> + <div className={styles.form}> + <div className={styles.inputWrap}> + <Input + type="number" + min="1" + placeholder="0" + prefix="OCEAN" + {...field} + /> + <Error meta={meta} /> </div> - {firstPrice && ( - <aside className={styles.firstPrice}> - Expected first price:{' '} - <PriceUnit - price={Number(firstPrice) > 0 ? firstPrice : '-'} - size="small" - conversion - /> - </aside> - )} - </> + + <h4 className={styles.datatoken}> + = <strong>1</strong> {dataTokenOptions.symbol}{' '} + <Conversion price={field.value} className={styles.conversion} /> + </h4> + </div> )} </div> ) diff --git a/src/components/Publish/Pricing/index.module.css b/src/components/Publish/Pricing/index.module.css index 0bb34deff..fb98dbc5f 100644 --- a/src/components/Publish/Pricing/index.module.css +++ b/src/components/Publish/Pricing/index.module.css @@ -10,3 +10,10 @@ padding-left: 0; padding-right: 0; } + +.title { + font-size: var(--font-size-base); + margin-top: var(--spacer); + margin-bottom: 0; + padding-bottom: calc(var(--spacer) / 4); +} diff --git a/src/components/Publish/Pricing/index.tsx b/src/components/Publish/Pricing/index.tsx index e6806a3af..1c485da52 100644 --- a/src/components/Publish/Pricing/index.tsx +++ b/src/components/Publish/Pricing/index.tsx @@ -1,11 +1,7 @@ import React, { ReactElement, useEffect } from 'react' import { useFormikContext } from 'formik' import Tabs from '@shared/atoms/Tabs' -import { isValidNumber } from '@utils/numbers' -import Decimal from 'decimal.js' import { FormPublishData } from '../_types' -import { initialValues } from '../_constants' -import Dynamic from './Dynamic' import Fixed from './Fixed' import Free from './Free' import content from '../../../../content/price.json' @@ -26,7 +22,6 @@ export default function PricingFields(): ReactElement { setFieldValue('pricing.type', type) setFieldValue('pricing.price', 0) setFieldValue('pricing.freeAgreement', false) - type !== 'free' && setFieldValue('pricing.amountDataToken', 1000) } // Update price when price is changed diff --git a/src/components/Publish/Steps.tsx b/src/components/Publish/Steps.tsx index 36324d82d..4c3e2a338 100644 --- a/src/components/Publish/Steps.tsx +++ b/src/components/Publish/Steps.tsx @@ -33,14 +33,8 @@ export function Steps({ ...feedback, '1': { ...feedback['1'], - txCount: values.pricing.type === 'dynamic' ? 2 : 1, - description: - values.pricing.type === 'dynamic' - ? feedback['1'].description.replace( - 'a single transaction', - 'a single transaction, after an initial approve transaction' - ) - : initialPublishFeedback['1'].description + txCount: 1, + description: initialPublishFeedback['1'].description } }) }, [values.pricing.type, feedback, setFieldValue]) diff --git a/src/components/Publish/_constants.tsx b/src/components/Publish/_constants.tsx index 6804f6e61..65f7549b9 100644 --- a/src/components/Publish/_constants.tsx +++ b/src/components/Publish/_constants.tsx @@ -1,6 +1,11 @@ import React from 'react' -import { allowDynamicPricing, allowFixedPricing } from '../../../app.config.js' -import { FormPublishData, PublishFeedback, StepContent } from './_types' +import { allowFixedPricing } from '../../../app.config.js' +import { + FormPublishData, + MetadataAlgorithmContainer, + PublishFeedback, + StepContent +} from './_types' import content from '../../../content/publish/form.json' import PricingFields from './Pricing' import MetadataFields from './Metadata' @@ -83,22 +88,10 @@ export const initialValues: FormPublishData = { pricing: { price: 0, type: allowFixedPricing === 'true' ? 'fixed' : 'free', - amountDataToken: 1000, - amountOcean: 100, - weightOnOcean: '5', // 50% on OCEAN - weightOnDataToken: '5', // 50% on datatoken - swapFee: 0.1, // in % freeAgreement: false } } -export interface MetadataAlgorithmContainer { - entrypoint: string - image: string - tag: string - checksum: string -} - export const algorithmContainerPresets: MetadataAlgorithmContainer[] = [ { image: 'node', diff --git a/src/components/Publish/_types.ts b/src/components/Publish/_types.ts index ee1c7409d..bae8306a3 100644 --- a/src/components/Publish/_types.ts +++ b/src/components/Publish/_types.ts @@ -1,7 +1,6 @@ import { ServiceComputeOptions } from '@oceanprotocol/lib' import { NftMetadata } from '@utils/nft' import { ReactElement } from 'react' -import { PriceOptions } from 'src/@types/Price' interface FileInfo { url: string @@ -43,7 +42,7 @@ export interface FormPublishData { dockerImageCustomChecksum?: string } services: FormPublishService[] - pricing: PriceOptions + pricing: PricePublishOptions feedback?: PublishFeedback } @@ -63,3 +62,10 @@ export interface PublishFeedback { txHash?: string } } + +export interface MetadataAlgorithmContainer { + entrypoint: string + image: string + tag: string + checksum: string +} diff --git a/src/components/Publish/_utils.ts b/src/components/Publish/_utils.ts index 76ebb9f28..6f0a0c649 100644 --- a/src/components/Publish/_utils.ts +++ b/src/components/Publish/_utils.ts @@ -1,5 +1,4 @@ import { - approve, Config, DDO, Erc20CreateParams, @@ -10,25 +9,19 @@ import { Metadata, NftCreateData, NftFactory, - PoolCreationParams, Service, ZERO_ADDRESS } from '@oceanprotocol/lib' import { mapTimeoutStringToSeconds } from '@utils/ddo' import { generateNftCreateData } from '@utils/nft' import { getEncryptedFiles } from '@utils/provider' -import Decimal from 'decimal.js' import slugify from 'slugify' import Web3 from 'web3' -import { - algorithmContainerPresets, - MetadataAlgorithmContainer -} from './_constants' -import { FormPublishData } from './_types' +import { algorithmContainerPresets } from './_constants' +import { FormPublishData, MetadataAlgorithmContainer } from './_types' import { marketFeeAddress, publisherMarketOrderFee, - publisherMarketPoolSwapFee, publisherMarketFixedSwapFee } from '../../../app.config' import { sanitizeUrl } from '@utils/url' @@ -226,7 +219,6 @@ export async function createTokensAndPricing( let erc721Address, datatokenAddress, txHash - // TODO: cleaner code for this huge switch !??!? switch (values.pricing.type) { case 'fixed': { const freParams: FreCreationParams = { diff --git a/src/components/Publish/_validation.ts b/src/components/Publish/_validation.ts index 4a07332cf..39ac4245a 100644 --- a/src/components/Publish/_validation.ts +++ b/src/components/Publish/_validation.ts @@ -1,7 +1,5 @@ import { MAX_DECIMALS } from '@utils/constants' -import { initialValues } from './_constants' import * as Yup from 'yup' -import Decimal from 'decimal.js' import { getMaxDecimalsValidation } from '@utils/numbers' // TODO: conditional validation @@ -37,7 +35,7 @@ const validationService = { valid: Yup.boolean().isTrue().required('File must be valid.') }) ) - .min(1, (param) => `At least one file is required.`) + .min(1, `At least one file is required.`) .required('Enter a valid URL and click ADD FILE.'), links: Yup.array<{ url: string; valid: boolean }[]>() .of( @@ -66,7 +64,7 @@ const validationService = { const validationPricing = { type: Yup.string() - .matches(/fixed|dynamic|free/g, { excludeEmptyString: true }) + .matches(/fixed|free/g, { excludeEmptyString: true }) .required('Required'), // https://github.com/jquense/yup#mixedwhenkeys-string--arraystring-builder-object--value-schema-schema-schema @@ -81,46 +79,6 @@ const validationPricing = { `Must have maximum ${MAX_DECIMALS} decimal digits`, (param) => getMaxDecimalsValidation(MAX_DECIMALS).test(param?.toString()) ) - .required('Required'), - amountDataToken: Yup.number().required('Required'), - amountOcean: Yup.number() - .test('validator-min-amountOcean', '', function (value) { - if (this.parent.type === 'fixed') return true - const minValue = - this.parent.price > 0 - ? new Decimal(this.parent.price) - .mul(this.parent.weightOnOcean) - .mul(10) - .mul(2) - .toDecimalPlaces(MAX_DECIMALS) - .toString() - : initialValues.pricing.amountOcean.toString() - return value < parseInt(minValue) - ? this.createError({ - message: `Must be more or equal to ${minValue}, as at least ${initialValues.pricing.amountDataToken} datatokens are required for this pool to work properly` - }) - : true - }) - .max( - 1000000, - (param: { max: number }) => `Must be less than or equal to ${param.max}` - ) - .test( - 'maxDigitsAfterDecimal', - `Must have maximum ${MAX_DECIMALS} decimal digits`, - (param) => getMaxDecimalsValidation(MAX_DECIMALS).test(param?.toString()) - ) - .required('Required'), - weightOnDataToken: Yup.string().required('Required'), - weightOnOcean: Yup.string().required('Required'), - swapFee: Yup.number() - .min(0.1, (param) => `Must be more or equal to ${param.min}`) - .max(10, 'Maximum is 10%') - .test( - 'maxDigitsAfterDecimal', - `Must have maximum ${MAX_DECIMALS} decimal digits`, - (param) => getMaxDecimalsValidation(MAX_DECIMALS).test(param?.toString()) - ) .required('Required') } @@ -137,36 +95,3 @@ export const validationSchema: Yup.SchemaOf<any> = Yup.object().shape({ services: Yup.array().of(Yup.object().shape(validationService)), pricing: Yup.object().shape(validationPricing) }) - -// export const validationSchemaAlgo: Yup.SchemaOf<MetadataPublishFormAlgorithm> = -// Yup.object() -// .shape({ -// // ---- required fields ---- -// name: Yup.string() -// .min(4, (param) => `Title must be at least ${param.min} characters`) -// .required('Required'), -// description: Yup.string().min(10).required('Required'), -// files: Yup.array<FileInfo>().required('Required').nullable(), -// timeout: Yup.string().required('Required'), -// dataTokenOptions: Yup.object() -// .shape({ -// name: Yup.string(), -// symbol: Yup.string() -// }) -// .required('Required'), -// dockerImage: Yup.string() -// .matches(/node:latest|python:latest|custom image/g, { -// excludeEmptyString: true -// }) -// .required('Required'), -// image: Yup.string().required('Required'), -// containerTag: Yup.string().required('Required'), -// entrypoint: Yup.string().required('Required'), -// author: Yup.string().required('Required'), -// termsAndConditions: Yup.boolean().required('Required'), -// // ---- optional fields ---- -// algorithmPrivacy: Yup.boolean().nullable(), -// tags: Yup.string().nullable(), -// links: Yup.array<FileInfo[]>().nullable() -// }) -// .defined()