mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
merged multinetwork into poc-urql
This commit is contained in:
commit
8d50eb92c0
22
.env.example
22
.env.example
@ -1,15 +1,27 @@
|
||||
# Default network, possible values:
|
||||
# "development", "ropsten", "rinkeby", "mainnet", "polygon", "moonbeamalpha"
|
||||
# "development", "ropsten", "rinkeby", "mainnet", "polygon", "moonbeamalpha",
|
||||
# "gaiaxtestnet", "mumbai", "bsc"
|
||||
GATSBY_NETWORK="rinkeby"
|
||||
|
||||
## Define a GATSBY_RBAC_URL to implement permission based restrictions
|
||||
#GATSBY_RBAC_URL="http://localhost:3000"
|
||||
|
||||
#GATSBY_INFURA_PROJECT_ID="xxx"
|
||||
#GATSBY_MARKET_FEE_ADDRESS="0xxx"
|
||||
#GATSBY_ANALYTICS_ID="xxx"
|
||||
#GATSBY_PORTIS_ID="xxx"
|
||||
|
||||
|
||||
#
|
||||
# ADVANCED SETTINGS
|
||||
#
|
||||
|
||||
# Toggle pricing options presented during price creation
|
||||
#GATSBY_ALLOW_FIXED_PRICING="true"
|
||||
#GATSBY_ALLOW_DYNAMIC_PRICING="true"
|
||||
#GATSBY_ALLOW_FREE_PRICING="false"
|
||||
|
||||
# Define RBAC server URL to implement permission based restrictions
|
||||
#GATSBY_RBAC_URL="http://localhost:3000"
|
||||
|
||||
# Enables another asset editing button holder further advanced settings
|
||||
#GATSBY_ALLOW_ADVANCED_SETTINGS="true"
|
||||
|
||||
# Allow/Deny Lists
|
||||
#GATSBY_CREDENTIAL_TYPE="address"
|
||||
|
11
README.md
11
README.md
@ -26,6 +26,7 @@
|
||||
- [⬆️ Deployment](#️-deployment)
|
||||
- [💖 Contributing](#-contributing)
|
||||
- [🍴 Forking](#-forking)
|
||||
- [💻 Advanced Features](#-advanced-features)
|
||||
- [🏛 License](#-license)
|
||||
|
||||
## 🏄 Get Started
|
||||
@ -374,6 +375,16 @@ Additionally, we would also advise that your retain the text saying "Powered by
|
||||
|
||||
Everything else is made open according to the apache2 license. We look forward to seeing your data marketplace!
|
||||
|
||||
## 💻 Advanced Features
|
||||
|
||||
Ocean Market also includes a number of advanced features that are suitable for an enterprise data market, such as:
|
||||
|
||||
- Role based access control
|
||||
- Allow and deny lists
|
||||
- Free pricing
|
||||
|
||||
[See our seperate guide on advanced features](docs/advancedSettings.md)
|
||||
|
||||
## 🏛 License
|
||||
|
||||
```text
|
||||
|
@ -8,7 +8,7 @@ module.exports = {
|
||||
chainIds: [1, 137],
|
||||
|
||||
// List of all supported chainIds. Used to populate the Chains user preferences list.
|
||||
chainIdsSupported: [1, 3, 4, 137, 1287],
|
||||
chainIdsSupported: [1, 3, 4, 137, 1287, 56],
|
||||
|
||||
rbacUrl: process.env.GATSBY_RBAC_URL,
|
||||
|
||||
@ -47,10 +47,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',
|
||||
|
@ -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": {
|
||||
|
29
docs/advancedSettings.md
Normal file
29
docs/advancedSettings.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Advanced Settings
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
- [Role based Access Control](#rbac-settings)
|
||||
- [Allow and Deny lists](#allow-and-deny-list-settings)
|
||||
- [Free Pricing](#free-pricing-settings)
|
||||
|
||||
## RBAC settings
|
||||
|
||||
- Setup and host the Ocean role based access control (RBAC) server. Follow the instructions in the [RBAC repository](https://github.com/oceanprotocol/RBAC-Server)
|
||||
- The RBAC server can store roles in [Keycloak](https://www.keycloak.org/) or a json file.
|
||||
- In your .env file, set the value of the `GATSBY_RBAC_URL` environmental variable to the URL of the Ocean RBAC server that you have hosted, e.g. `GATSBY_RBAC_URL= "http://localhost:3000"`
|
||||
- Users of your marketplace will now require the correct role ("user", "consumer", "publisher") to access features in your marketplace. The market will check the role that has been allocated to the user based on the address that they have connected to the market with.
|
||||
- The following features have been wrapped in the `Permission` component and will be restricted once the `GATSBY_RBAC_URL` has been defined:
|
||||
- Viewing or searching datasets requires the user to have permison to `browse`
|
||||
- Purchasing or trading a datatoken, or adding liquidity to a pool require the user to have permison to `consume`
|
||||
- Publishing a dataset requires the user to have permison to `publish`
|
||||
- You can change the permission resrictions by either removing the `Permission` component or passing in a different eventType prop e.g. `<Permission eventType="browse">`.
|
||||
|
||||
## Allow and Deny List Settings
|
||||
|
||||
- To enable allow and deny lists you need to add the following environmental variable to your .env file: `GATSBY_ALLOW_ADVANCED_SETTINGS="true"`
|
||||
- Publishers in your market will now have the ability to restrict who can consume their datasets.
|
||||
|
||||
## Free Pricing Settings
|
||||
|
||||
- To allow publishers to set pricing as "Free" you need to add the following environmental variable to your .env file: `GATSBY_ALLOW_FREE_PRICING="true"`
|
||||
- This allocates the datatokens to the [dispenser contract](https://github.com/oceanprotocol/contracts/blob/main/contracts/dispenser/Dispenser.sol) which dispenses data tokens to users for free. Publishers in your market will now be able to offer their datasets to users for free (excluding gas costs).
|
25960
package-lock.json
generated
25960
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,7 @@
|
||||
"@coingecko/cryptoformat": "^0.4.2",
|
||||
"@loadable/component": "^5.15.0",
|
||||
"@oceanprotocol/art": "^3.0.0",
|
||||
"@oceanprotocol/lib": "^0.15.1",
|
||||
"@oceanprotocol/lib": "^0.16.1",
|
||||
"@oceanprotocol/typographies": "^0.1.0",
|
||||
"@portis/web3": "^4.0.4",
|
||||
"@sindresorhus/slugify": "^2.1.0",
|
||||
@ -60,7 +60,6 @@
|
||||
"gatsby-transformer-remark": "^2.16.1",
|
||||
"gatsby-transformer-sharp": "^2.12.1",
|
||||
"graphql": "14.7.0",
|
||||
"graphql-schema-typescript": "^1.5.2",
|
||||
"is-url-superb": "^6.0.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
|
@ -21,6 +21,8 @@ interface ButtonBuyProps {
|
||||
onClick?: (e: FormEvent<HTMLButtonElement>) => 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 (
|
||||
|
@ -42,15 +42,20 @@ export default function PriceUnit({
|
||||
|
||||
return (
|
||||
<div className={styleClasses}>
|
||||
<div>
|
||||
{Number.isNaN(Number(price)) ? '-' : formatPrice(price, locale)}{' '}
|
||||
<span className={styles.symbol}>{symbol || 'OCEAN'}</span>
|
||||
{type && type === 'pool' && (
|
||||
<Badge label="pool" className={styles.badge} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{conversion && <Conversion price={price} />}
|
||||
{type && type === 'free' ? (
|
||||
<div> Free </div>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
{Number.isNaN(Number(price)) ? '-' : formatPrice(price, locale)}{' '}
|
||||
<span className={styles.symbol}>{symbol || 'OCEAN'}</span>
|
||||
{type && type === 'pool' && (
|
||||
<Badge label="pool" className={styles.badge} />
|
||||
)}
|
||||
</div>
|
||||
{conversion && <Conversion price={price} />}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ export default function Price({
|
||||
small?: boolean
|
||||
conversion?: boolean
|
||||
}): ReactElement {
|
||||
return price?.value ? (
|
||||
return price?.value || price?.type === 'free' ? (
|
||||
<PriceUnit
|
||||
price={`${price.value}`}
|
||||
className={className}
|
||||
|
@ -107,7 +107,12 @@ export default function AssetSelection({
|
||||
</Dotdotdot>
|
||||
</label>
|
||||
|
||||
<PriceUnit price={asset.price} small className={styles.price} />
|
||||
<PriceUnit
|
||||
price={asset.price}
|
||||
type={asset.price === '0' ? 'free' : undefined}
|
||||
small
|
||||
className={styles.price}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
|
@ -23,7 +23,11 @@ export default function SearchBar({
|
||||
e.preventDefault()
|
||||
if (value === '') value = ' '
|
||||
const urlEncodedValue = encodeURIComponent(value)
|
||||
const url = await addExistingParamsToUrl(location, 'text')
|
||||
const url = await addExistingParamsToUrl(location, [
|
||||
'text',
|
||||
'owner',
|
||||
'tags'
|
||||
])
|
||||
navigate(`${url}&text=${urlEncodedValue}`)
|
||||
}
|
||||
|
||||
@ -31,7 +35,11 @@ export default function SearchBar({
|
||||
const searchParams = new URLSearchParams(window.location.href)
|
||||
const text = searchParams.get('text')
|
||||
if (text !== ('' || undefined || null)) {
|
||||
const url = await addExistingParamsToUrl(location, 'text')
|
||||
const url = await addExistingParamsToUrl(location, [
|
||||
'text',
|
||||
'owner',
|
||||
'tags'
|
||||
])
|
||||
navigate(`${url}&text=%20`)
|
||||
}
|
||||
}
|
||||
|
@ -166,6 +166,8 @@ export default function FormStartCompute({
|
||||
stepText={stepText}
|
||||
isLoading={isLoading}
|
||||
type="submit"
|
||||
priceType={price?.type}
|
||||
algorithmPriceType={algorithmPrice?.type}
|
||||
/>
|
||||
</Form>
|
||||
)
|
||||
|
@ -157,7 +157,9 @@ export default function Compute({
|
||||
source.token
|
||||
)
|
||||
setDdoAlgorithmList(gueryResults.results)
|
||||
const datasetComputeService = ddo.findServiceByType('compute')
|
||||
algorithmSelectionList = await transformDDOToAssetSelection(
|
||||
datasetComputeService?.serviceEndpoint,
|
||||
gueryResults.results,
|
||||
appConfig.metadataCacheUri,
|
||||
[]
|
||||
@ -384,7 +386,10 @@ export default function Compute({
|
||||
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={ddo.id} />
|
||||
<AlgorithmDatasetsListForCompute
|
||||
algorithmDid={ddo.id}
|
||||
dataset={ddo}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Formik
|
||||
|
@ -12,6 +12,7 @@ import { useWeb3 } from '../../../providers/Web3'
|
||||
import { usePricing } from '../../../hooks/usePricing'
|
||||
import { useConsume } from '../../../hooks/useConsume'
|
||||
import ButtonBuy from '../../atoms/ButtonBuy'
|
||||
import { secondsToString } from '../../../utils/metadata'
|
||||
import AlgorithmDatasetsListForCompute from '../AssetContent/AlgorithmDatasetsListForCompute'
|
||||
import styles from './Consume.module.css'
|
||||
|
||||
@ -157,10 +158,11 @@ export default function Consume({
|
||||
dtSymbol={ddo.dataTokenInfo?.symbol}
|
||||
dtBalance={dtBalance}
|
||||
onClick={handleConsume}
|
||||
assetTimeout={assetTimeout}
|
||||
assetTimeout={secondsToString(parseInt(assetTimeout))}
|
||||
assetType={type}
|
||||
stepText={consumeStepText || pricingStepText}
|
||||
isLoading={pricingIsLoading || isLoading}
|
||||
priceType={price?.type}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -176,7 +178,7 @@ export default function Consume({
|
||||
</div>
|
||||
</div>
|
||||
{type === 'algorithm' && (
|
||||
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} />
|
||||
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} dataset={ddo} />
|
||||
)}
|
||||
</aside>
|
||||
)
|
||||
|
@ -16,6 +16,10 @@ import {
|
||||
} from '../../../../models/FormEditCredential'
|
||||
import DebugEditCredential from './DebugEditAdvancedSettings'
|
||||
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||
import {
|
||||
setMinterToDispenser,
|
||||
setMinterToPublisher
|
||||
} from '../../../../utils/freePrice'
|
||||
|
||||
const contentQuery = graphql`
|
||||
query EditAvanceSettingsQuery {
|
||||
@ -68,7 +72,7 @@ export default function EditAdvancedSettings({
|
||||
const { debug } = useUserPreferences()
|
||||
const { accountId } = useWeb3()
|
||||
const { ocean } = useOcean()
|
||||
const { metadata, ddo, refreshDdo } = useAsset()
|
||||
const { metadata, ddo, refreshDdo, price } = useAsset()
|
||||
const [success, setSuccess] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
const { appConfig } = useSiteMetadata()
|
||||
@ -82,6 +86,16 @@ export default function EditAdvancedSettings({
|
||||
resetForm: () => void
|
||||
) {
|
||||
try {
|
||||
if (price.type === 'free') {
|
||||
const tx = await setMinterToPublisher(
|
||||
ocean,
|
||||
ddo.dataToken,
|
||||
accountId,
|
||||
setError
|
||||
)
|
||||
if (!tx) return
|
||||
}
|
||||
|
||||
let newDdo: DDO
|
||||
newDdo = await ocean.assets.updateCredentials(
|
||||
ddo,
|
||||
@ -103,6 +117,15 @@ export default function EditAdvancedSettings({
|
||||
Logger.error(content.form.error)
|
||||
return
|
||||
} else {
|
||||
if (price.type === 'free') {
|
||||
const tx = await setMinterToDispenser(
|
||||
ocean,
|
||||
ddo.dataToken,
|
||||
accountId,
|
||||
setError
|
||||
)
|
||||
if (!tx) return
|
||||
}
|
||||
setSuccess(content.form.success)
|
||||
resetForm()
|
||||
}
|
||||
|
@ -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<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
|
||||
@ -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()
|
||||
|
@ -56,7 +56,9 @@ export default function FormEditComputeDataset({
|
||||
appConfig.metadataCacheUri,
|
||||
source.token
|
||||
)
|
||||
const datasetComputeService = ddo.findServiceByType('compute')
|
||||
const algorithmSelectionList = await transformDDOToAssetSelection(
|
||||
datasetComputeService?.serviceEndpoint,
|
||||
querryResult.results,
|
||||
appConfig.metadataCacheUri,
|
||||
publisherTrustedAlgorithms
|
||||
|
@ -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()
|
||||
|
@ -122,11 +122,10 @@ export default function Graph(): ReactElement {
|
||||
|
||||
const { price } = useAsset()
|
||||
|
||||
const [lastBlock, setLastBlock] = useState(0)
|
||||
const [lastBlock, setLastBlock] = useState<number>(0)
|
||||
const [priceHistory, setPriceHistory] = useState([])
|
||||
const [liquidityHistory, setLiquidityHistory] = useState([])
|
||||
const [timestamps, setTimestamps] = useState([])
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [graphData, setGraphData] = useState<ChartData>()
|
||||
|
||||
@ -157,7 +156,6 @@ export default function Graph(): ReactElement {
|
||||
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
|
||||
})
|
||||
]
|
||||
|
||||
setTimestamps(latestTimestamps)
|
||||
|
||||
const latestLiquidtyHistory = [
|
||||
@ -171,17 +169,20 @@ export default function Graph(): ReactElement {
|
||||
...priceHistory,
|
||||
...data.poolTransactions.map((item) => item.spotPrice)
|
||||
]
|
||||
|
||||
setPriceHistory(latestPriceHistory)
|
||||
|
||||
if (data.poolTransactions.length > 0) {
|
||||
const newBlock =
|
||||
data.poolTransactions[data.poolTransactions.length - 1].block
|
||||
if (newBlock === lastBlock) return
|
||||
setLastBlock(
|
||||
data.poolTransactions[data.poolTransactions.length - 1].block
|
||||
)
|
||||
refetch()
|
||||
} else {
|
||||
setIsLoading(false)
|
||||
setGraphData({
|
||||
labels: timestamps.slice(0),
|
||||
labels: latestTimestamps.slice(0),
|
||||
datasets: [
|
||||
{
|
||||
...lineStyle,
|
||||
@ -195,6 +196,7 @@ export default function Graph(): ReactElement {
|
||||
}
|
||||
]
|
||||
})
|
||||
setIsLoading(false)
|
||||
}
|
||||
}, [data, graphType])
|
||||
|
||||
|
@ -12,7 +12,7 @@ import { useAsset } from '../../../providers/Asset'
|
||||
import { useOcean } from '../../../providers/Ocean'
|
||||
import { useWeb3 } from '../../../providers/Web3'
|
||||
import Web3Feedback from '../../molecules/Web3Feedback'
|
||||
import { getFileInfo } from '../../../utils/provider'
|
||||
import { fileinfo, getFileInfo } from '../../../utils/provider'
|
||||
import axios from 'axios'
|
||||
|
||||
export default function AssetActions(): ReactElement {
|
||||
@ -27,29 +27,27 @@ export default function AssetActions(): ReactElement {
|
||||
const isCompute = Boolean(ddo?.findServiceByType('compute'))
|
||||
|
||||
useEffect(() => {
|
||||
if (!config) return
|
||||
|
||||
const source = axios.CancelToken.source()
|
||||
async function initFileInfo() {
|
||||
setFileIsLoading(true)
|
||||
try {
|
||||
const fileInfo = await getFileInfo(
|
||||
DID.parse(`${ddo.id}`),
|
||||
config.providerUri,
|
||||
source.token
|
||||
)
|
||||
setFileMetadata(fileInfo.data[0])
|
||||
} catch (error) {
|
||||
Logger.error(error.message)
|
||||
} finally {
|
||||
setFileIsLoading(false)
|
||||
}
|
||||
}
|
||||
initFileInfo()
|
||||
|
||||
return () => {
|
||||
source.cancel()
|
||||
}
|
||||
const { attributes } = ddo.findServiceByType('metadata')
|
||||
setFileMetadata(attributes.main.files[0])
|
||||
// !!!!! do not remove this, we will enable this again after fileInfo endpoint is fixed !!!
|
||||
// if (!config) return
|
||||
// const source = axios.CancelToken.source()
|
||||
// async function initFileInfo() {
|
||||
// setFileIsLoading(true)
|
||||
// try {
|
||||
// const fileInfo = await getFileInfo(
|
||||
// DID.parse(`${ddo.id}`),
|
||||
// config.providerUri,
|
||||
// source.token
|
||||
// )
|
||||
// setFileMetadata(fileInfo.data[0])
|
||||
// } catch (error) {
|
||||
// Logger.error(error.message)
|
||||
// } finally {
|
||||
// setFileIsLoading(false)
|
||||
// }
|
||||
// }
|
||||
// initFileInfo()
|
||||
}, [config, ddo.id])
|
||||
|
||||
// Get and set user DT balance
|
||||
@ -72,6 +70,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(
|
||||
|
@ -5,11 +5,14 @@ import { AssetSelectionAsset } from '../../molecules/FormFields/AssetSelection'
|
||||
import AssetComputeList from '../../molecules/AssetComputeList'
|
||||
import { useOcean } from '../../../providers/Ocean'
|
||||
import { useAsset } from '../../../providers/Asset'
|
||||
import { DDO } from '@oceanprotocol/lib'
|
||||
|
||||
export default function AlgorithmDatasetsListForCompute({
|
||||
algorithmDid
|
||||
algorithmDid,
|
||||
dataset
|
||||
}: {
|
||||
algorithmDid: string
|
||||
dataset: DDO
|
||||
}): ReactElement {
|
||||
const { config } = useOcean()
|
||||
const { type } = useAsset()
|
||||
@ -18,8 +21,10 @@ export default function AlgorithmDatasetsListForCompute({
|
||||
|
||||
useEffect(() => {
|
||||
async function getDatasetsAllowedForCompute() {
|
||||
const datasetComputeService = dataset.findServiceByType('compute')
|
||||
const datasets = await getAlgorithmDatasetsForCompute(
|
||||
algorithmDid,
|
||||
datasetComputeService?.serviceEndpoint,
|
||||
config.metadataCacheUri
|
||||
)
|
||||
setDatasetsForCompute(datasets)
|
||||
|
@ -0,0 +1,3 @@
|
||||
.free {
|
||||
composes: content from './index.module.css';
|
||||
}
|
@ -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 (
|
||||
<div className={styles.free}>
|
||||
<FormHelp className={stylesIndex.help}>{content.info}</FormHelp>
|
||||
<Price ddo={ddo} free />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -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({
|
||||
<div className={styles.price}>
|
||||
<div className={styles.grid}>
|
||||
<div className={styles.form}>
|
||||
<Input
|
||||
value={field.value}
|
||||
name="price"
|
||||
type="number"
|
||||
prefix="OCEAN"
|
||||
min="1"
|
||||
{...field}
|
||||
additionalComponent={
|
||||
<Conversion price={field.value} className={styles.conversion} />
|
||||
}
|
||||
/>
|
||||
{free ? (
|
||||
<Input
|
||||
value="0"
|
||||
name="price"
|
||||
type="number"
|
||||
prefix="OCEAN"
|
||||
readOnly
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
value={field.value}
|
||||
name="price"
|
||||
type="number"
|
||||
prefix="OCEAN"
|
||||
min="1"
|
||||
{...field}
|
||||
additionalComponent={
|
||||
<Conversion price={field.value} className={styles.conversion} />
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Error meta={meta} />
|
||||
</div>
|
||||
<div className={styles.datatoken}>
|
||||
|
@ -45,3 +45,8 @@
|
||||
padding-left: var(--spacer);
|
||||
padding-right: var(--spacer);
|
||||
}
|
||||
|
||||
.free {
|
||||
text-align: center;
|
||||
margin-bottom: calc(var(--spacer) / 1.5);
|
||||
}
|
||||
|
@ -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: <Dynamic content={content.dynamic} ddo={ddo} />
|
||||
}
|
||||
: undefined,
|
||||
appConfig.allowFreePricing === 'true'
|
||||
? {
|
||||
title: content.free.title,
|
||||
content: <Free content={content.free} ddo={ddo} />
|
||||
}
|
||||
: undefined
|
||||
].filter((tab) => tab !== undefined)
|
||||
|
||||
|
@ -40,6 +40,10 @@ const query = graphql`
|
||||
marketplaceFee
|
||||
}
|
||||
}
|
||||
free {
|
||||
title
|
||||
info
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,11 @@ const columns = [
|
||||
{
|
||||
name: 'Finished',
|
||||
selector: function getTimeRow(row: ComputeJobMetaData) {
|
||||
return <Time date={row.dateFinished} isUnix relative />
|
||||
return row.dateFinished ? (
|
||||
<Time date={row.dateFinished} isUnix relative />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -117,6 +121,7 @@ export default function ComputeJobs(): ReactElement {
|
||||
|
||||
setIsLoading(true)
|
||||
|
||||
// await refetch()
|
||||
const dtList = []
|
||||
const computeJobs: ComputeJobMetaData[] = []
|
||||
for (let i = 0; i < data.tokenOrders.length; i++) {
|
||||
|
@ -85,7 +85,7 @@ function SectionQueryResult({
|
||||
appConfig.metadataCacheUri,
|
||||
source.token
|
||||
)
|
||||
if (result.totalResults <= 15) {
|
||||
if (queryData && result.totalResults > 0 && result.totalResults <= 15) {
|
||||
const searchDIDs = queryData.split(' ')
|
||||
const sortedAssets = sortElements(result.results, searchDIDs)
|
||||
// We take more assets than we need from the subgraph (to make sure
|
||||
@ -95,7 +95,6 @@ function SectionQueryResult({
|
||||
sortedAssets.splice(sortedAssets.length - overflow, overflow)
|
||||
result.results = sortedAssets
|
||||
}
|
||||
if (result.results.length === 0) return
|
||||
setResult(result)
|
||||
setLoading(false)
|
||||
} catch (error) {
|
||||
|
@ -25,7 +25,7 @@ export default function FilterPrice({
|
||||
const [serviceSelections, setServiceSelections] = useState<string[]>([])
|
||||
|
||||
async function applyServiceFilter(filterBy: string) {
|
||||
let urlLocation = await addExistingParamsToUrl(location, 'serviceType')
|
||||
let urlLocation = await addExistingParamsToUrl(location, ['serviceType'])
|
||||
if (filterBy && location.search.indexOf('&serviceType') === -1) {
|
||||
urlLocation = `${urlLocation}&serviceType=${filterBy}`
|
||||
}
|
||||
@ -59,7 +59,7 @@ export default function FilterPrice({
|
||||
}
|
||||
|
||||
async function applyClearFilter() {
|
||||
let urlLocation = await addExistingParamsToUrl(location, 'serviceType')
|
||||
let urlLocation = await addExistingParamsToUrl(location, ['serviceType'])
|
||||
|
||||
urlLocation = `${urlLocation}`
|
||||
|
||||
|
@ -32,7 +32,6 @@ export default function SearchPage({
|
||||
|
||||
useEffect(() => {
|
||||
if (!appConfig.metadataCacheUri) return
|
||||
|
||||
async function initSearch() {
|
||||
setLoading(true)
|
||||
setTotalResults(undefined)
|
||||
@ -66,7 +65,7 @@ export default function SearchPage({
|
||||
<Permission eventType="browse">
|
||||
<>
|
||||
<div className={styles.search}>
|
||||
{(text || owner) && (
|
||||
{(text || owner || tags) && (
|
||||
<SearchBar initialValue={(text || owner) as string} />
|
||||
)}
|
||||
<div className={styles.row}>
|
||||
|
@ -11,7 +11,10 @@ import classNames from 'classnames/bind'
|
||||
|
||||
const cx = classNames.bind(styles)
|
||||
|
||||
const sortItems = [{ display: 'Published', value: SortTermOptions.Created }]
|
||||
const sortItems = [
|
||||
// { display: 'Relevance', value: SortTermOptions.Relevance },
|
||||
{ display: 'Published', value: SortTermOptions.Created }
|
||||
]
|
||||
|
||||
export default function Sort({
|
||||
sortType,
|
||||
@ -31,10 +34,11 @@ export default function Sort({
|
||||
async function sortResults(sortBy?: string, direction?: string) {
|
||||
let urlLocation: string
|
||||
if (sortBy) {
|
||||
urlLocation = await addExistingParamsToUrl(location, ['sort'])
|
||||
urlLocation = `${urlLocation}&sort=${sortBy}`
|
||||
setSortType(sortBy)
|
||||
} else if (direction) {
|
||||
urlLocation = await addExistingParamsToUrl(location, 'sortOrder')
|
||||
urlLocation = await addExistingParamsToUrl(location, ['sortOrder'])
|
||||
urlLocation = `${urlLocation}&sortOrder=${direction}`
|
||||
setSortDirection(direction)
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
import {
|
||||
SearchQuery,
|
||||
QueryResult
|
||||
} from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||
import { MetadataCache, Logger } from '@oceanprotocol/lib'
|
||||
import queryString from 'query-string'
|
||||
|
||||
export const SortTermOptions = {
|
||||
Created: 'created'
|
||||
Created: 'created',
|
||||
Relevance: '_score'
|
||||
} as const
|
||||
type SortTermOptions = typeof SortTermOptions[keyof typeof SortTermOptions]
|
||||
|
||||
@ -39,8 +37,11 @@ function addTypeFilterToQuery(sortTerm: string, typeFilter: string): string {
|
||||
return sortTerm
|
||||
}
|
||||
|
||||
function getSortType(): string {
|
||||
const sortTerm = SortTermOptions.Created
|
||||
function getSortType(sortParam: string): string {
|
||||
const sortTerm =
|
||||
sortParam === SortTermOptions.Created
|
||||
? SortTermOptions.Created
|
||||
: SortTermOptions.Relevance
|
||||
return sortTerm
|
||||
}
|
||||
|
||||
@ -54,9 +55,11 @@ export function getSearchQuery(
|
||||
sort?: string,
|
||||
sortOrder?: string,
|
||||
serviceType?: string
|
||||
): SearchQuery {
|
||||
const sortTerm = getSortType()
|
||||
): any {
|
||||
const sortTerm = getSortType(sort)
|
||||
const sortValue = sortOrder === SortValueOptions.Ascending ? 1 : -1
|
||||
const emptySearchTerm = text === undefined || text === ''
|
||||
|
||||
let searchTerm = owner
|
||||
? `(publicKey.owner:${owner})`
|
||||
: tags
|
||||
@ -67,41 +70,84 @@ export function getSearchQuery(
|
||||
`(service.attributes.additionalInformation.categories:\"${categories}\")`
|
||||
: text || ''
|
||||
|
||||
// HACK: resolves the case sensitivity related to dataTokenInfo.symbol
|
||||
searchTerm = '*' + searchTerm.toUpperCase() + '*'
|
||||
searchTerm = searchTerm.trim()
|
||||
let modifiedSearchTerm = searchTerm.split(' ').join(' OR ').trim()
|
||||
modifiedSearchTerm = addTypeFilterToQuery(modifiedSearchTerm, serviceType)
|
||||
searchTerm = addTypeFilterToQuery(searchTerm, serviceType)
|
||||
const prefixedSearchTerm =
|
||||
emptySearchTerm && searchTerm
|
||||
? searchTerm
|
||||
: !emptySearchTerm && searchTerm
|
||||
? '*' + searchTerm + '*'
|
||||
: '**'
|
||||
|
||||
return {
|
||||
page: Number(page) || 1,
|
||||
offset: Number(offset) || 21,
|
||||
query: {
|
||||
query_string: {
|
||||
query: `${searchTerm} -isInPurgatory:true`,
|
||||
fields: [
|
||||
'dataTokenInfo.name',
|
||||
'dataTokenInfo.symbol',
|
||||
'service.attributes.main.name',
|
||||
'service.attributes.main.author',
|
||||
'service.attributes.additionalInformation.description'
|
||||
],
|
||||
default_operator: 'AND'
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
query_string: {
|
||||
query: `${modifiedSearchTerm}`,
|
||||
fields: [
|
||||
'id',
|
||||
'publicKey.owner',
|
||||
'dataToken',
|
||||
'dataTokenInfo.name',
|
||||
'dataTokenInfo.symbol',
|
||||
'service.attributes.main.name^10',
|
||||
'service.attributes.main.author',
|
||||
'service.attributes.additionalInformation.description',
|
||||
'service.attributes.additionalInformation.tags'
|
||||
],
|
||||
minimum_should_match: '2<75%',
|
||||
phrase_slop: 2,
|
||||
boost: 5
|
||||
}
|
||||
},
|
||||
{
|
||||
match_phrase: {
|
||||
content: {
|
||||
query: `${searchTerm}`,
|
||||
boost: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
query_string: {
|
||||
query: `${prefixedSearchTerm}`,
|
||||
fields: [
|
||||
'id',
|
||||
'publicKey.owner',
|
||||
'dataToken',
|
||||
'dataTokenInfo.name',
|
||||
'dataTokenInfo.symbol',
|
||||
'service.attributes.main.name',
|
||||
'service.attributes.main.author',
|
||||
'service.attributes.additionalInformation.description',
|
||||
'service.attributes.additionalInformation.tags'
|
||||
],
|
||||
default_operator: 'AND'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
term: {
|
||||
isInPurgatory: false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
// ...(owner && { 'publicKey.owner': [owner] }),
|
||||
// ...(tags && { tags: [tags] }),
|
||||
// ...(categories && { categories: [categories] })
|
||||
},
|
||||
sort: {
|
||||
[sortTerm]: sortValue
|
||||
}
|
||||
|
||||
// Something in ocean.js is weird when using 'tags: [tag]'
|
||||
// which is the only way the query actually returns desired results.
|
||||
// But it doesn't follow 'SearchQuery' interface so we have to assign
|
||||
// it here.
|
||||
// } as SearchQuery
|
||||
|
||||
// And the next hack,
|
||||
// nativeSearch is not implmeneted on ocean.js typings
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,9 +169,9 @@ export async function getResults(
|
||||
text,
|
||||
owner,
|
||||
tags,
|
||||
categories,
|
||||
page,
|
||||
offset,
|
||||
categories,
|
||||
sort,
|
||||
sortOrder,
|
||||
serviceType
|
||||
@ -143,25 +189,20 @@ export async function getResults(
|
||||
sortOrder,
|
||||
serviceType
|
||||
)
|
||||
|
||||
const queryResult = await metadataCache.queryMetadata(searchQuery)
|
||||
return queryResult
|
||||
}
|
||||
|
||||
export async function addExistingParamsToUrl(
|
||||
location: Location,
|
||||
excludedParam: string,
|
||||
secondExcludedParam?: string
|
||||
excludedParams: string[]
|
||||
): Promise<string> {
|
||||
const parsed = queryString.parse(location.search)
|
||||
let urlLocation = '/search?'
|
||||
if (Object.keys(parsed).length > 0) {
|
||||
for (const querryParam in parsed) {
|
||||
if (
|
||||
querryParam !== excludedParam &&
|
||||
querryParam !== secondExcludedParam
|
||||
) {
|
||||
if (querryParam === 'page' && excludedParam === 'text') {
|
||||
if (!excludedParams.includes(querryParam)) {
|
||||
if (querryParam === 'page' && excludedParams.includes('text')) {
|
||||
Logger.log('remove page when starting a new search')
|
||||
} else {
|
||||
const value = parsed[querryParam]
|
||||
@ -170,7 +211,10 @@ export async function addExistingParamsToUrl(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
urlLocation = `${urlLocation}sort=${SortTermOptions.Created}&sortOrder=${SortValueOptions.Descending}&`
|
||||
// sort should be relevance when fixed in aqua
|
||||
urlLocation = `${urlLocation}sort=${encodeURIComponent(
|
||||
SortTermOptions.Created
|
||||
)}&sortOrder=${SortValueOptions.Descending}&`
|
||||
}
|
||||
urlLocation = urlLocation.slice(0, -1)
|
||||
return urlLocation
|
||||
|
@ -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) {
|
||||
|
@ -29,6 +29,7 @@ interface UseSiteMetadata {
|
||||
portisId: string
|
||||
allowFixedPricing: string
|
||||
allowDynamicPricing: string
|
||||
allowFreePricing: string
|
||||
allowAdvancedSettings: string
|
||||
credentialType: string
|
||||
}
|
||||
@ -65,6 +66,7 @@ const query = graphql`
|
||||
portisId
|
||||
allowFixedPricing
|
||||
allowDynamicPricing
|
||||
allowFreePricing
|
||||
allowAdvancedSettings
|
||||
credentialType
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ function getCredentialList(
|
||||
const credentialByType = credential.find(
|
||||
(credential) => credential.type === credentialType
|
||||
)
|
||||
return credentialByType.value && credentialByType.value.length > 0
|
||||
return credentialByType &&
|
||||
credentialByType.value &&
|
||||
credentialByType.value.length > 0
|
||||
? credentialByType.value
|
||||
: []
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ export const validationSchema: Yup.SchemaOf<PriceOptionsMarket> =
|
||||
.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'),
|
||||
|
@ -121,6 +121,7 @@ export async function getAssetsNames(
|
||||
}
|
||||
|
||||
export async function transformDDOToAssetSelection(
|
||||
datasetProviderEndpoint: string,
|
||||
ddoList: DDO[],
|
||||
metadataCacheUri: string,
|
||||
selectedAlgorithms?: PublisherTrustedAlgorithm[]
|
||||
@ -129,14 +130,22 @@ export async function transformDDOToAssetSelection(
|
||||
const didList: string[] = []
|
||||
const priceList: PriceList = await getAssetsPriceList(ddoList)
|
||||
const symbolList: any = {}
|
||||
const didProviderEndpointMap: any = {}
|
||||
for (const ddo of ddoList) {
|
||||
didList.push(ddo.id)
|
||||
symbolList[ddo.id] = ddo.dataTokenInfo.symbol
|
||||
const algoComputeService = ddo.findServiceByType('compute')
|
||||
algoComputeService?.serviceEndpoint &&
|
||||
(didProviderEndpointMap[ddo.id] = algoComputeService?.serviceEndpoint)
|
||||
}
|
||||
const ddoNames = await getAssetsNames(didList, metadataCacheUri, source.token)
|
||||
const algorithmList: AssetSelectionAsset[] = []
|
||||
didList?.forEach((did: string) => {
|
||||
if (priceList[did]) {
|
||||
if (
|
||||
priceList[did] &&
|
||||
(!didProviderEndpointMap[did] ||
|
||||
didProviderEndpointMap[did] === datasetProviderEndpoint)
|
||||
) {
|
||||
let selected = false
|
||||
selectedAlgorithms?.forEach((algorithm: PublisherTrustedAlgorithm) => {
|
||||
if (algorithm.did === did) {
|
||||
@ -165,6 +174,7 @@ export async function transformDDOToAssetSelection(
|
||||
|
||||
export async function getAlgorithmDatasetsForCompute(
|
||||
algorithmId: string,
|
||||
datasetProviderUri: string,
|
||||
metadataCacheUri: string
|
||||
): Promise<AssetSelectionAsset[]> {
|
||||
const source = axios.CancelToken.source()
|
||||
@ -186,6 +196,7 @@ export async function getAlgorithmDatasetsForCompute(
|
||||
return []
|
||||
}
|
||||
const datasets = await transformDDOToAssetSelection(
|
||||
datasetProviderUri,
|
||||
computeDatasetsForCurrentAlgorithm,
|
||||
metadataCacheUri,
|
||||
[]
|
||||
|
@ -1,6 +1,8 @@
|
||||
const cleanupContentType = (contentType: string): string => {
|
||||
// strip away the `charset=utf-8`
|
||||
const contentSplit = contentType.split(';')[0]
|
||||
// strip away the 'application/' part
|
||||
const contentTypeSplit = contentType.split('/')[1]
|
||||
const contentTypeSplit = contentSplit.split('/')[1]
|
||||
|
||||
if (!contentTypeSplit) return contentType
|
||||
|
||||
|
@ -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}.`
|
||||
}
|
||||
}
|
||||
|
37
src/utils/freePrice.ts
Normal file
37
src/utils/freePrice.ts
Normal file
@ -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<any> {
|
||||
// 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<any> {
|
||||
// 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
|
||||
}
|
@ -1,23 +1,18 @@
|
||||
import {
|
||||
ConfigHelper,
|
||||
ConfigHelperConfig,
|
||||
ConfigHelperNetworkId,
|
||||
ConfigHelperNetworkName,
|
||||
Logger
|
||||
} from '@oceanprotocol/lib'
|
||||
import { ConfigHelper, ConfigHelperConfig, Logger } from '@oceanprotocol/lib'
|
||||
import contractAddresses from '@oceanprotocol/contracts/artifacts/address.json'
|
||||
import { AbiItem } from 'web3-utils/types'
|
||||
import Web3 from 'web3'
|
||||
|
||||
export function getOceanConfig(
|
||||
network: ConfigHelperNetworkName | ConfigHelperNetworkId
|
||||
): ConfigHelperConfig {
|
||||
export function getOceanConfig(network: string | number): ConfigHelperConfig {
|
||||
const config = new ConfigHelper().getConfig(
|
||||
network,
|
||||
network === 'polygon' ||
|
||||
network === 137 ||
|
||||
network === 'moonbeamalpha' ||
|
||||
network === 1287
|
||||
network === 1287 ||
|
||||
network === 'bsc' ||
|
||||
network === 56 ||
|
||||
network === 'gaiaxtestnet' ||
|
||||
network === 2021000
|
||||
? undefined
|
||||
: process.env.GATSBY_INFURA_PROJECT_ID
|
||||
)
|
||||
|
@ -4,6 +4,7 @@ import { getUrqlClientInstance } from '../providers/UrqlProvider'
|
||||
import { getOceanConfig } from './ocean'
|
||||
import web3 from 'web3'
|
||||
import schema from '../../src/@types/schema.json'
|
||||
import { AssetsFreePrice } from '../@types/apollo/AssetsFreePrice'
|
||||
|
||||
export interface PriceList {
|
||||
[key: string]: string
|
||||
@ -18,6 +19,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 }) {
|
||||
@ -141,7 +172,8 @@ export async function getPreviousOrders(
|
||||
|
||||
function transformPriceToBestPrice(
|
||||
frePrice: AssetsFrePriceFixedRateExchanges[],
|
||||
poolPrice: AssetsPoolPricePools[]
|
||||
poolPrice: AssetsPoolPricePools[],
|
||||
freePrice: AssetFreePriceDispenser[]
|
||||
) {
|
||||
if (poolPrice?.length > 0) {
|
||||
const price: BestPrice = {
|
||||
@ -171,6 +203,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: '',
|
||||
@ -190,8 +234,9 @@ async function getAssetsPoolsExchangesAndDatatokenMap(
|
||||
assets: DDO[]
|
||||
): Promise<
|
||||
[
|
||||
AssetsPoolPricePools[],
|
||||
AssetsFrePriceFixedRateExchanges[],
|
||||
ApolloQueryResult<AssetsPoolPrice>,
|
||||
ApolloQueryResult<AssetsFrePrice>,
|
||||
ApolloQueryResult<AssetsFreePrice>,
|
||||
DidAndDatatokenMap
|
||||
]
|
||||
> {
|
||||
@ -210,6 +255,7 @@ async function getAssetsPoolsExchangesAndDatatokenMap(
|
||||
}
|
||||
let poolPriceResponse: AssetsPoolPricePools[] = []
|
||||
let frePriceResponse: AssetsFrePriceFixedRateExchanges[] = []
|
||||
let freePriceResponse: AssetFreePriceDispenser[] = []
|
||||
|
||||
for (const chainKey in chainAssetLists) {
|
||||
const freVariables = {
|
||||
@ -218,7 +264,9 @@ async function getAssetsPoolsExchangesAndDatatokenMap(
|
||||
const poolVariables = {
|
||||
datatokenAddress_in: chainAssetLists[chainKey]
|
||||
}
|
||||
|
||||
const freeVariables = {
|
||||
datatoken_in: chainAssetLists[chainKey]
|
||||
}
|
||||
// harcoded until we have chainId on assets
|
||||
const queryContext: OperationContext = {
|
||||
url: `${getSubgrahUri(1)}/subgraphs/name/oceanprotocol/ocean-subgraph`,
|
||||
@ -236,22 +284,29 @@ async function getAssetsPoolsExchangesAndDatatokenMap(
|
||||
frePriceResponse = frePriceResponse.concat(
|
||||
chainFrePriceResponse.data.fixedRateExchanges
|
||||
)
|
||||
}
|
||||
|
||||
return [poolPriceResponse, frePriceResponse, didDTMap]
|
||||
const chainFreePriceResponse: OperationResult<AssetsFreePrice> =
|
||||
await fetchData(FreeQuery, freeVariables, queryContext)
|
||||
freePriceResponse = freePriceResponse.concat(
|
||||
chainFreePriceResponse.data.dispensers
|
||||
)
|
||||
}
|
||||
return [poolPriceResponse, frePriceResponse, freePriceResponse, didDTMap]
|
||||
}
|
||||
|
||||
export async function getAssetsPriceList(assets: DDO[]): Promise<PriceList> {
|
||||
const priceList: PriceList = {}
|
||||
|
||||
const values: [
|
||||
AssetsPoolPricePools[],
|
||||
AssetsFrePriceFixedRateExchanges[],
|
||||
ApolloQueryResult<AssetsPoolPrice>,
|
||||
ApolloQueryResult<AssetsFrePrice>,
|
||||
ApolloQueryResult<AssetsFreePrice>,
|
||||
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) {
|
||||
priceList[didDTMap[poolPrice.datatokenAddress]] =
|
||||
@ -262,6 +317,9 @@ export async function getAssetsPriceList(assets: DDO[]): Promise<PriceList> {
|
||||
for (const frePrice of frePriceResponse) {
|
||||
priceList[didDTMap[frePrice.datatoken?.address]] = frePrice.rate
|
||||
}
|
||||
for (const freePrice of freePriceResponse.data?.dispensers) {
|
||||
priceList[didDTMap[freePrice.datatoken?.address]] = '0'
|
||||
}
|
||||
return priceList
|
||||
}
|
||||
|
||||
@ -269,7 +327,9 @@ export async function getPrice(asset: DDO): Promise<BestPrice> {
|
||||
const freVariables = {
|
||||
datatoken: asset?.dataToken.toLowerCase()
|
||||
}
|
||||
|
||||
const freeVariables = {
|
||||
datatoken: asset?.dataToken.toLowerCase()
|
||||
}
|
||||
const poolVariables = {
|
||||
datatokenAddress: asset?.dataToken.toLowerCase()
|
||||
}
|
||||
@ -289,10 +349,16 @@ export async function getPrice(asset: DDO): Promise<BestPrice> {
|
||||
freVariables,
|
||||
queryContext
|
||||
)
|
||||
const freePriceResponse: ApolloQueryResult<AssetsFreePrice> = await fetchData(
|
||||
AssetFreeQuery,
|
||||
freeVariables,
|
||||
queryContext
|
||||
)
|
||||
|
||||
const bestPrice: BestPrice = transformPriceToBestPrice(
|
||||
frePriceResponse.data.fixedRateExchanges,
|
||||
poolPriceResponse.data.pools
|
||||
poolPriceResponse.data.pools,
|
||||
freePriceResponse.data.dispensers
|
||||
)
|
||||
|
||||
return bestPrice
|
||||
@ -304,26 +370,34 @@ export async function getAssetsBestPrices(
|
||||
const assetsWithPrice: AssetListPrices[] = []
|
||||
|
||||
const values: [
|
||||
AssetsPoolPricePools[],
|
||||
AssetsFrePriceFixedRateExchanges[],
|
||||
ApolloQueryResult<AssetsPoolPrice>,
|
||||
ApolloQueryResult<AssetsFrePrice>,
|
||||
ApolloQueryResult<AssetsFreePrice>,
|
||||
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 pool = poolPriceResponse.find(
|
||||
const freePrice: AssetFreePriceDispenser[] = []
|
||||
const pool = poolPriceResponse.data?.pools.find(
|
||||
(pool: any) => pool.datatokenAddress === dataToken
|
||||
)
|
||||
|
||||
pool && poolPrice.push(pool)
|
||||
const fre = frePriceResponse.find(
|
||||
(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
|
||||
|
@ -44,6 +44,9 @@ export function getNetworkDisplayName(
|
||||
case 8996:
|
||||
displayName = 'Development'
|
||||
break
|
||||
case 2021000:
|
||||
displayName = 'GAIA-X'
|
||||
break
|
||||
default:
|
||||
displayName = `${data.chain} ${
|
||||
data.network === 'mainnet' ? '' : data.network
|
||||
|
Loading…
Reference in New Issue
Block a user