From e02babf2c2fd80ae67e686fb026406bc4dfcfac3 Mon Sep 17 00:00:00 2001 From: Kris Liew <39853992+krisliew@users.noreply.github.com> Date: Wed, 16 Jun 2021 09:32:11 +0800 Subject: [PATCH] [EPIC] Free Pricing (#681) * Free Pricing Option at create Pricing (#621) * Free Pricing Option + env var toggle * Create Pricing step msg * Default 'allowFreePricing' to true temp for review * Fix price 0 on free tab * Attempt fix useSiteMetadata * Fix linting * Feature/free price support consume compute (#654) * Update fetch free price * Feedback change UI remove 0's * update button msg && fix * compute algorithm list show 'Free' instead of '0' * updateMetadata() v3 workaround solution for free pricing (#677) * compute algorithm list show 'Free' instead of '0' * workaround editMetaData free price * utils function for compute & download * `allowFreePricing` default to false --- .env.example | 1 + apollo.config.js | 3 +- app.config.js | 3 +- content/price.json | 4 + src/components/atoms/ButtonBuy.tsx | 10 ++- src/components/atoms/Price/PriceUnit.tsx | 23 +++-- src/components/atoms/Price/index.tsx | 2 +- .../molecules/FormFields/AssetSelection.tsx | 7 +- .../Compute/FormComputeDataset.tsx | 2 + .../organisms/AssetActions/Consume.tsx | 1 + .../AssetActions/Edit/EditComputeDataset.tsx | 24 ++++- .../organisms/AssetActions/Edit/index.tsx | 22 +++++ .../organisms/AssetActions/index.tsx | 1 + .../Pricing/FormPricing/Free.module.css | 3 + .../AssetContent/Pricing/FormPricing/Free.tsx | 21 +++++ .../Pricing/FormPricing/Price.tsx | 36 +++++--- .../Pricing/FormPricing/index.module.css | 5 ++ .../Pricing/FormPricing/index.tsx | 8 ++ .../organisms/AssetContent/Pricing/index.tsx | 4 + src/hooks/usePricing.ts | 53 +++++++++-- src/hooks/useSiteMetadata.ts | 2 + src/models/FormPricing.ts | 2 +- src/utils/feedback.ts | 20 +++++ src/utils/freePrice.ts | 37 ++++++++ src/utils/subgraph.ts | 88 +++++++++++++++++-- 25 files changed, 341 insertions(+), 41 deletions(-) create mode 100644 src/components/organisms/AssetContent/Pricing/FormPricing/Free.module.css create mode 100644 src/components/organisms/AssetContent/Pricing/FormPricing/Free.tsx create mode 100644 src/utils/freePrice.ts diff --git a/.env.example b/.env.example index 91f93b378..a44ef8bbb 100644 --- a/.env.example +++ b/.env.example @@ -11,5 +11,6 @@ GATSBY_NETWORK="rinkeby" #GATSBY_PORTIS_ID="xxx" #GATSBY_ALLOW_FIXED_PRICING="true" #GATSBY_ALLOW_DYNAMIC_PRICING="true" +#GATSBY_ALLOW_FREE_PRICING="true" #GATSBY_ALLOW_ADVANCED_SETTINGS="true" #GATSBY_CREDENTIAL_TYPE="address" diff --git a/apollo.config.js b/apollo.config.js index 8c4500450..3ca33ccad 100644 --- a/apollo.config.js +++ b/apollo.config.js @@ -2,8 +2,7 @@ module.exports = { client: { service: { name: 'ocean', - url: - 'https://subgraph.rinkeby.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', + url: 'https://subgraph.rinkeby.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', // optional disable SSL validation check skipSSLValidation: true } diff --git a/app.config.js b/app.config.js index 129d2076f..f24dab256 100644 --- a/app.config.js +++ b/app.config.js @@ -41,10 +41,11 @@ module.exports = { // Wallets portisId: process.env.GATSBY_PORTIS_ID || 'xxx', - // Used to show or hide the fixed and dynamic price options + // Used to show or hide the fixed, dynamic or free price options // tab to publishers during the price creation. allowFixedPricing: process.env.GATSBY_ALLOW_FIXED_PRICING || 'true', allowDynamicPricing: process.env.GATSBY_ALLOW_DYNAMIC_PRICING || 'true', + allowFreePricing: process.env.GATSBY_ALLOW_FREE_PRICING || 'false', // Used to show or hide advanced settings button in asset details page allowAdvancedSettings: process.env.GATSBY_ALLOW_ADVANCED_SETTINGS || 'false', diff --git a/content/price.json b/content/price.json index 4c3f278b0..517d74a75 100644 --- a/content/price.json +++ b/content/price.json @@ -21,6 +21,10 @@ "communityFee": "Explain community fee...", "marketplaceFee": "Explain marketplace fee..." } + }, + "free": { + "title": "Free", + "info": "Set your data set as free. The datatoken for this data set will be given for free via creating a faucet." } }, "pool": { diff --git a/src/components/atoms/ButtonBuy.tsx b/src/components/atoms/ButtonBuy.tsx index 6bdcec09e..50acd944d 100644 --- a/src/components/atoms/ButtonBuy.tsx +++ b/src/components/atoms/ButtonBuy.tsx @@ -21,6 +21,8 @@ interface ButtonBuyProps { onClick?: (e: FormEvent) => void stepText?: string type?: 'submit' + priceType?: string + algorithmPriceType?: string } function getConsumeHelpText( @@ -87,15 +89,21 @@ export default function ButtonBuy({ onClick, stepText, isLoading, - type + type, + priceType, + algorithmPriceType }: ButtonBuyProps): ReactElement { const buttonText = action === 'download' ? hasPreviousOrder ? 'Download' + : priceType === 'free' + ? 'Get' : `Buy ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}` : hasPreviousOrder && hasPreviousOrderSelectedComputeAsset ? 'Start Compute Job' + : priceType === 'free' && algorithmPriceType === 'free' + ? 'Order Compute Job' : `Buy Compute Job` return ( diff --git a/src/components/atoms/Price/PriceUnit.tsx b/src/components/atoms/Price/PriceUnit.tsx index 7d9e3bccd..a1f3bf54f 100644 --- a/src/components/atoms/Price/PriceUnit.tsx +++ b/src/components/atoms/Price/PriceUnit.tsx @@ -42,15 +42,20 @@ export default function PriceUnit({ return (
-
- {Number.isNaN(Number(price)) ? '-' : formatPrice(price, locale)}{' '} - {symbol || 'OCEAN'} - {type && type === 'pool' && ( - - )} -
- - {conversion && } + {type && type === 'free' ? ( +
Free
+ ) : ( + <> +
+ {Number.isNaN(Number(price)) ? '-' : formatPrice(price, locale)}{' '} + {symbol || 'OCEAN'} + {type && type === 'pool' && ( + + )} +
+ {conversion && } + + )}
) } diff --git a/src/components/atoms/Price/index.tsx b/src/components/atoms/Price/index.tsx index 18082436f..940aca870 100644 --- a/src/components/atoms/Price/index.tsx +++ b/src/components/atoms/Price/index.tsx @@ -16,7 +16,7 @@ export default function Price({ small?: boolean conversion?: boolean }): ReactElement { - return price?.value ? ( + return price?.value || price?.type === 'free' ? ( - + )) )} diff --git a/src/components/organisms/AssetActions/Compute/FormComputeDataset.tsx b/src/components/organisms/AssetActions/Compute/FormComputeDataset.tsx index 8bae8b2eb..36906a061 100644 --- a/src/components/organisms/AssetActions/Compute/FormComputeDataset.tsx +++ b/src/components/organisms/AssetActions/Compute/FormComputeDataset.tsx @@ -166,6 +166,8 @@ export default function FormStartCompute({ stepText={stepText} isLoading={isLoading} type="submit" + priceType={price?.type} + algorithmPriceType={algorithmPrice?.type} /> ) diff --git a/src/components/organisms/AssetActions/Consume.tsx b/src/components/organisms/AssetActions/Consume.tsx index 3dc526a7a..73055be6c 100644 --- a/src/components/organisms/AssetActions/Consume.tsx +++ b/src/components/organisms/AssetActions/Consume.tsx @@ -160,6 +160,7 @@ export default function Consume({ assetType={type} stepText={consumeStepText || pricingStepText} isLoading={pricingIsLoading || isLoading} + priceType={price?.type} /> ) diff --git a/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx b/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx index 8af2bbe37..1c65aaa70 100644 --- a/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx +++ b/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx @@ -16,6 +16,10 @@ import { useUserPreferences } from '../../../../providers/UserPreferences' import DebugEditCompute from './DebugEditCompute' import styles from './index.module.css' import { transformComputeFormToServiceComputePrivacy } from '../../../../utils/compute' +import { + setMinterToDispenser, + setMinterToPublisher +} from '../../../../utils/freePrice' const contentQuery = graphql` query EditComputeDataQuery { @@ -62,7 +66,7 @@ export default function EditComputeDataset({ const { debug } = useUserPreferences() const { ocean } = useOcean() const { accountId } = useWeb3() - const { ddo, refreshDdo } = useAsset() + const { ddo, refreshDdo, price } = useAsset() const [success, setSuccess] = useState() const [error, setError] = useState() @@ -73,6 +77,15 @@ export default function EditComputeDataset({ resetForm: () => void ) { try { + if (price.type === 'free') { + const tx = await setMinterToPublisher( + ocean, + ddo.dataToken, + accountId, + setError + ) + if (!tx) return + } const privacy = await transformComputeFormToServiceComputePrivacy( values, ocean @@ -99,6 +112,15 @@ export default function EditComputeDataset({ Logger.error(content.form.error) return } else { + if (price.type === 'free') { + const tx = await setMinterToDispenser( + ocean, + ddo.dataToken, + accountId, + setError + ) + if (!tx) return + } // Edit succeeded setSuccess(content.form.success) resetForm() diff --git a/src/components/organisms/AssetActions/Edit/index.tsx b/src/components/organisms/AssetActions/Edit/index.tsx index dfafa3a6f..a513e4854 100644 --- a/src/components/organisms/AssetActions/Edit/index.tsx +++ b/src/components/organisms/AssetActions/Edit/index.tsx @@ -18,6 +18,10 @@ import MetadataFeedback from '../../../molecules/MetadataFeedback' import { graphql, useStaticQuery } from 'gatsby' import { useWeb3 } from '../../../../providers/Web3' import { useOcean } from '../../../../providers/Ocean' +import { + setMinterToDispenser, + setMinterToPublisher +} from '../../../../utils/freePrice' const contentQuery = graphql` query EditMetadataQuery { @@ -88,6 +92,15 @@ export default function Edit({ resetForm: () => void ) { try { + if (price.type === 'free') { + const tx = await setMinterToPublisher( + ocean, + ddo.dataToken, + accountId, + setError + ) + if (!tx) return + } // Construct new DDO with new values const ddoEditedMetdata = await ocean.assets.editMetadata(ddo, { title: values.name, @@ -132,6 +145,15 @@ export default function Edit({ Logger.error(content.form.error) return } else { + if (price.type === 'free') { + const tx = await setMinterToDispenser( + ocean, + ddo.dataToken, + accountId, + setError + ) + if (!tx) return + } // Edit succeeded setSuccess(content.form.success) resetForm() diff --git a/src/components/organisms/AssetActions/index.tsx b/src/components/organisms/AssetActions/index.tsx index 03c916354..819cc4c55 100644 --- a/src/components/organisms/AssetActions/index.tsx +++ b/src/components/organisms/AssetActions/index.tsx @@ -65,6 +65,7 @@ export default function AssetActions(): ReactElement { // Check user balance against price useEffect(() => { + if (price?.type === 'free') setIsBalanceSufficient(true) if (!price?.value || !account || !balance?.ocean || !dtBalance) return setIsBalanceSufficient( diff --git a/src/components/organisms/AssetContent/Pricing/FormPricing/Free.module.css b/src/components/organisms/AssetContent/Pricing/FormPricing/Free.module.css new file mode 100644 index 000000000..5dcdab375 --- /dev/null +++ b/src/components/organisms/AssetContent/Pricing/FormPricing/Free.module.css @@ -0,0 +1,3 @@ +.free { + composes: content from './index.module.css'; +} diff --git a/src/components/organisms/AssetContent/Pricing/FormPricing/Free.tsx b/src/components/organisms/AssetContent/Pricing/FormPricing/Free.tsx new file mode 100644 index 000000000..70115699b --- /dev/null +++ b/src/components/organisms/AssetContent/Pricing/FormPricing/Free.tsx @@ -0,0 +1,21 @@ +import React, { ReactElement } from 'react' +import stylesIndex from './index.module.css' +import styles from './Free.module.css' +import FormHelp from '../../../../atoms/Input/Help' +import { DDO } from '@oceanprotocol/lib' +import Price from './Price' + +export default function Free({ + ddo, + content +}: { + ddo: DDO + content: any +}): ReactElement { + return ( +
+ {content.info} + +
+ ) +} diff --git a/src/components/organisms/AssetContent/Pricing/FormPricing/Price.tsx b/src/components/organisms/AssetContent/Pricing/FormPricing/Price.tsx index ad2669e7b..17576ceff 100644 --- a/src/components/organisms/AssetContent/Pricing/FormPricing/Price.tsx +++ b/src/components/organisms/AssetContent/Pricing/FormPricing/Price.tsx @@ -10,10 +10,12 @@ import usePricing from '../../../../../hooks/usePricing' export default function Price({ ddo, - firstPrice + firstPrice, + free }: { ddo: DDO firstPrice?: string + free?: boolean }): ReactElement { const [field, meta] = useField('price') const { getDTName, getDTSymbol } = usePricing() @@ -38,17 +40,27 @@ export default function Price({
- - } - /> + {free ? ( + + ) : ( + + } + /> + )}
diff --git a/src/components/organisms/AssetContent/Pricing/FormPricing/index.module.css b/src/components/organisms/AssetContent/Pricing/FormPricing/index.module.css index 22058c782..c8002d1f9 100644 --- a/src/components/organisms/AssetContent/Pricing/FormPricing/index.module.css +++ b/src/components/organisms/AssetContent/Pricing/FormPricing/index.module.css @@ -45,3 +45,8 @@ padding-left: var(--spacer); padding-right: var(--spacer); } + +.free { + text-align: center; + margin-bottom: calc(var(--spacer) / 1.5); +} diff --git a/src/components/organisms/AssetContent/Pricing/FormPricing/index.tsx b/src/components/organisms/AssetContent/Pricing/FormPricing/index.tsx index 64cb0755b..a64e84573 100644 --- a/src/components/organisms/AssetContent/Pricing/FormPricing/index.tsx +++ b/src/components/organisms/AssetContent/Pricing/FormPricing/index.tsx @@ -3,6 +3,7 @@ import styles from './index.module.css' import Tabs from '../../../../atoms/Tabs' import Fixed from './Fixed' import Dynamic from './Dynamic' +import Free from './Free' import { useFormikContext } from 'formik' import { useUserPreferences } from '../../../../../providers/UserPreferences' import { PriceOptionsMarket } from '../../../../../@types/MetaData' @@ -33,6 +34,7 @@ export default function FormPricing({ const type = tabName.toLowerCase() setFieldValue('type', type) type === 'fixed' && setFieldValue('dtAmount', 1000) + type === 'free' && price < 1 && setFieldValue('price', 1) } // Always update everything when price value changes @@ -57,6 +59,12 @@ export default function FormPricing({ title: content.dynamic.title, content: } + : undefined, + appConfig.allowFreePricing === 'true' + ? { + title: content.free.title, + content: + } : undefined ].filter((tab) => tab !== undefined) diff --git a/src/components/organisms/AssetContent/Pricing/index.tsx b/src/components/organisms/AssetContent/Pricing/index.tsx index c69552db3..b33aaafbb 100644 --- a/src/components/organisms/AssetContent/Pricing/index.tsx +++ b/src/components/organisms/AssetContent/Pricing/index.tsx @@ -40,6 +40,10 @@ const query = graphql` marketplaceFee } } + free { + title + info + } } } } diff --git a/src/hooks/usePricing.ts b/src/hooks/usePricing.ts index c01d1104b..cc956c279 100644 --- a/src/hooks/usePricing.ts +++ b/src/hooks/usePricing.ts @@ -5,7 +5,9 @@ import { Decimal } from 'decimal.js' import { getCreatePricingPoolFeedback, getCreatePricingExchangeFeedback, - getBuyDTFeedback + getBuyDTFeedback, + getCreateFreePricingFeedback, + getDispenseFeedback } from '../utils/feedback' import { sleep } from '../utils' @@ -16,7 +18,7 @@ interface PriceOptions { price: number dtAmount: number oceanAmount: number - type: 'fixed' | 'dynamic' | string + type: 'fixed' | 'dynamic' | 'free' | string weightOnDataToken: string swapFee: string } @@ -68,7 +70,7 @@ function usePricing(): UsePricing { // Helper for setting steps & feedback for all flows async function setStep( index: number, - type: 'pool' | 'exchange' | 'buy', + type: 'pool' | 'exchange' | 'free' | 'buy' | 'dispense', ddo: DDO ) { const dtSymbol = await getDTSymbol(ddo) @@ -84,9 +86,15 @@ function usePricing(): UsePricing { case 'exchange': messages = getCreatePricingExchangeFeedback(dtSymbol) break + case 'free': + messages = getCreateFreePricingFeedback(dtSymbol) + break case 'buy': messages = getBuyDTFeedback(dtSymbol) break + case 'dispense': + messages = getDispenseFeedback(dtSymbol) + break } setPricingStepText(messages[index]) @@ -180,6 +188,28 @@ function usePricing(): UsePricing { Logger.log('DT exchange buy response', tx) break } + case 'free': { + setStep(1, 'dispense', ddo) + const isDispensable = await ocean.OceanDispenser.isDispensable( + ddo.dataToken, + accountId, + '1' + ) + + if (!isDispensable) { + Logger.error(`Dispenser for ${ddo.dataToken} failed to dispense`) + return + } + + tx = await ocean.OceanDispenser.dispense( + ddo.dataToken, + accountId, + '1' + ) + setStep(2, 'dispense', ddo) + Logger.log('DT dispense response', tx) + break + } } } catch (error) { setPricingError(error.message) @@ -219,9 +249,14 @@ function usePricing(): UsePricing { setStep(99, 'pool', ddo) try { - // if fixedPrice set dt to max amount - if (!isPool) dtAmount = 1000 - await mint(`${dtAmount}`, ddo) + if (type === 'free') { + setStep(99, 'free', ddo) + await ocean.OceanDispenser.activate(dataToken, '1', '1', accountId) + } else { + // if fixedPrice set dt to max amount + if (!isPool) dtAmount = 1000 + await mint(`${dtAmount}`, ddo) + } // dtAmount for fixed price is set to max const tx = isPool @@ -235,9 +270,13 @@ function usePricing(): UsePricing { swapFee ) .next((step: number) => setStep(step, 'pool', ddo)) - : await ocean.fixedRateExchange + : type === 'fixed' + ? await ocean.fixedRateExchange .create(dataToken, `${price}`, accountId, `${dtAmount}`) .next((step: number) => setStep(step, 'exchange', ddo)) + : await ocean.OceanDispenser.makeMinter(dataToken, accountId).next( + (step: number) => setStep(step, 'free', ddo) + ) await sleep(20000) return tx } catch (error) { diff --git a/src/hooks/useSiteMetadata.ts b/src/hooks/useSiteMetadata.ts index 30ed596bd..b33ed34b1 100644 --- a/src/hooks/useSiteMetadata.ts +++ b/src/hooks/useSiteMetadata.ts @@ -27,6 +27,7 @@ interface UseSiteMetadata { portisId: string allowFixedPricing: string allowDynamicPricing: string + allowFreePricing: string allowAdvancedSettings: string credentialType: string } @@ -61,6 +62,7 @@ const query = graphql` portisId allowFixedPricing allowDynamicPricing + allowFreePricing allowAdvancedSettings credentialType } diff --git a/src/models/FormPricing.ts b/src/models/FormPricing.ts index 8ad89189d..c9ad3aac5 100644 --- a/src/models/FormPricing.ts +++ b/src/models/FormPricing.ts @@ -13,7 +13,7 @@ export const validationSchema: Yup.SchemaOf = .min(21, (param) => `Must be more or equal to ${param.min}`) .required('Required'), type: Yup.string() - .matches(/fixed|dynamic/g, { excludeEmptyString: true }) + .matches(/fixed|dynamic|free/g, { excludeEmptyString: true }) .required('Required'), weightOnDataToken: Yup.string().required('Required'), weightOnOcean: Yup.string().required('Required'), diff --git a/src/utils/feedback.ts b/src/utils/feedback.ts index 511f4c7b2..ab440966e 100644 --- a/src/utils/feedback.ts +++ b/src/utils/feedback.ts @@ -52,6 +52,17 @@ export function getCreatePricingExchangeFeedback(dtSymbol: string): { } } +export function getCreateFreePricingFeedback(dtSymbol: string): { + [key: number]: string +} { + return { + 99: `Creating ${dtSymbol} faucet...`, + 0: 'Setting faucet as minter ...', + 1: 'Approving minter...', + 2: 'Faucet created.' + } +} + export function getBuyDTFeedback(dtSymbol: string): { [key: number]: string } { return { 1: '1/3 Approving OCEAN ...', @@ -67,3 +78,12 @@ export function getSellDTFeedback(dtSymbol: string): { [key: number]: string } { 3: `3/3 ${dtSymbol} sold.` } } + +export function getDispenseFeedback(dtSymbol: string): { + [key: number]: string +} { + return { + 1: `1/2 Requesting ${dtSymbol}...`, + 2: `2/2 Received ${dtSymbol}.` + } +} diff --git a/src/utils/freePrice.ts b/src/utils/freePrice.ts new file mode 100644 index 000000000..68e6de649 --- /dev/null +++ b/src/utils/freePrice.ts @@ -0,0 +1,37 @@ +import { Logger, Ocean } from '@oceanprotocol/lib' + +export async function setMinterToPublisher( + ocean: Ocean, + dataTokenAddress: string, + accountId: string, + setError: (msg: string) => void +): Promise { + // free pricing v3 workaround part1 + const response = await ocean.OceanDispenser.cancelMinter( + dataTokenAddress, + accountId + ) + if (!response) { + setError('Updating DDO failed.') + Logger.error('Failed at cancelMinter') + } + return response +} + +export async function setMinterToDispenser( + ocean: Ocean, + dataTokenAddress: string, + accountId: string, + setError: (msg: string) => void +): Promise { + // free pricing v3 workaround part2 + const response = await ocean.OceanDispenser.makeMinter( + dataTokenAddress, + accountId + ) + if (!response) { + setError('Updating DDO failed.') + Logger.error('Failed at makeMinter') + } + return response +} diff --git a/src/utils/subgraph.ts b/src/utils/subgraph.ts index c5408ee53..115f8c432 100644 --- a/src/utils/subgraph.ts +++ b/src/utils/subgraph.ts @@ -10,6 +10,10 @@ import { AssetsFrePrice_fixedRateExchanges as AssetsFrePriceFixedRateExchanges } from '../@types/apollo/AssetsFrePrice' import { AssetPreviousOrder } from '../@types/apollo/AssetPreviousOrder' +import { + AssetsFreePrice, + AssetsFreePrice_dispensers as AssetFreePriceDispenser +} from '../@types/apollo/AssetsFreePrice' import web3 from 'web3' export interface PriceList { @@ -25,6 +29,36 @@ interface DidAndDatatokenMap { [name: string]: string } +const FreeQuery = gql` + query AssetsFreePrice($datatoken_in: [String!]) { + dispensers(orderBy: id, where: { datatoken_in: $datatoken_in }) { + datatoken { + id + address + } + } + } +` + +const AssetFreeQuery = gql` + query AssetFreePrice($datatoken: String) { + dispensers(orderBy: id, where: { datatoken: $datatoken }) { + active + owner { + id + } + minterApproved + isTrueMinter + maxTokens + maxBalance + balance + datatoken { + id + } + } + } +` + const FreQuery = gql` query AssetsFrePrice($datatoken_in: [String!]) { fixedRateExchanges(orderBy: id, where: { datatoken_in: $datatoken_in }) { @@ -146,7 +180,8 @@ export async function getPreviousOrders( function transformPriceToBestPrice( frePrice: AssetsFrePriceFixedRateExchanges[], - poolPrice: AssetsPoolPricePools[] + poolPrice: AssetsPoolPricePools[], + freePrice: AssetFreePriceDispenser[] ) { if (poolPrice?.length > 0) { const price: BestPrice = { @@ -176,6 +211,18 @@ function transformPriceToBestPrice( isConsumable: 'true' } return price + } else if (freePrice?.length > 0) { + const price: BestPrice = { + type: 'free', + value: 0, + address: freePrice[0]?.datatoken.id, + exchange_id: '', + ocean: 0, + datatoken: 0, + pools: [], + isConsumable: 'true' + } + return price } else { const price: BestPrice = { type: '', @@ -197,6 +244,7 @@ async function getAssetsPoolsExchangesAndDatatokenMap( [ ApolloQueryResult, ApolloQueryResult, + ApolloQueryResult, DidAndDatatokenMap ] > { @@ -214,6 +262,10 @@ async function getAssetsPoolsExchangesAndDatatokenMap( datatokenAddress_in: dataTokenList } + const freeVariables = { + datatoken_in: dataTokenList + } + const poolPriceResponse: ApolloQueryResult = await fetchData( PoolQuery, poolVariables @@ -223,7 +275,12 @@ async function getAssetsPoolsExchangesAndDatatokenMap( freVariables ) - return [poolPriceResponse, frePriceResponse, didDTMap] + const freePriceResponse: ApolloQueryResult = await fetchData( + FreeQuery, + freeVariables + ) + + return [poolPriceResponse, frePriceResponse, freePriceResponse, didDTMap] } export async function getAssetsPriceList(assets: DDO[]): Promise { @@ -232,11 +289,13 @@ export async function getAssetsPriceList(assets: DDO[]): Promise { const values: [ ApolloQueryResult, ApolloQueryResult, + ApolloQueryResult, DidAndDatatokenMap ] = await getAssetsPoolsExchangesAndDatatokenMap(assets) const poolPriceResponse = values[0] const frePriceResponse = values[1] - const didDTMap: DidAndDatatokenMap = values[2] + const freePriceResponse = values[2] + const didDTMap: DidAndDatatokenMap = values[3] for (const poolPrice of poolPriceResponse.data?.pools) { priceList[didDTMap[poolPrice.datatokenAddress]] = @@ -247,6 +306,9 @@ export async function getAssetsPriceList(assets: DDO[]): Promise { for (const frePrice of frePriceResponse.data?.fixedRateExchanges) { priceList[didDTMap[frePrice.datatoken?.address]] = frePrice.rate } + for (const freePrice of freePriceResponse.data?.dispensers) { + priceList[didDTMap[freePrice.datatoken?.address]] = '0' + } return priceList } @@ -259,6 +321,10 @@ export async function getPrice(asset: DDO): Promise { datatokenAddress: asset?.dataToken.toLowerCase() } + const freeVariables = { + datatoken: asset?.dataToken.toLowerCase() + } + const poolPriceResponse: ApolloQueryResult = await fetchData( AssetPoolPriceQuerry, poolVariables @@ -267,10 +333,15 @@ export async function getPrice(asset: DDO): Promise { AssetFreQuery, freVariables ) + const freePriceResponse: ApolloQueryResult = await fetchData( + AssetFreeQuery, + freeVariables + ) const bestPrice: BestPrice = transformPriceToBestPrice( frePriceResponse.data.fixedRateExchanges, - poolPriceResponse.data.pools + poolPriceResponse.data.pools, + freePriceResponse.data.dispensers ) return bestPrice @@ -284,15 +355,18 @@ export async function getAssetsBestPrices( const values: [ ApolloQueryResult, ApolloQueryResult, + ApolloQueryResult, DidAndDatatokenMap ] = await getAssetsPoolsExchangesAndDatatokenMap(assets) const poolPriceResponse = values[0] const frePriceResponse = values[1] + const freePriceResponse = values[2] for (const ddo of assets) { const dataToken = ddo.dataToken.toLowerCase() const poolPrice: AssetsPoolPricePools[] = [] const frePrice: AssetsFrePriceFixedRateExchanges[] = [] + const freePrice: AssetFreePriceDispenser[] = [] const pool = poolPriceResponse.data?.pools.find( (pool: any) => pool.datatokenAddress === dataToken ) @@ -301,7 +375,11 @@ export async function getAssetsBestPrices( (fre: any) => fre.datatoken.address === dataToken ) fre && frePrice.push(fre) - const bestPrice = transformPriceToBestPrice(frePrice, poolPrice) + const free = freePriceResponse.data?.dispensers.find( + (free: any) => free.datatoken.address === dataToken + ) + free && freePrice.push(free) + const bestPrice = transformPriceToBestPrice(frePrice, poolPrice, freePrice) assetsWithPrice.push({ ddo: ddo, price: bestPrice