1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-06-26 03:06:49 +02:00

Merge branch 'main' into feature/compute

This commit is contained in:
Matthias Kretschmann 2021-03-17 12:54:47 +01:00
commit df587bb5b5
Signed by: m
GPG Key ID: 606EEEF3C479A91F
100 changed files with 2010 additions and 823 deletions

2
.husky/.gitignore vendored
View File

@ -1 +1 @@
_
_

View File

@ -18,6 +18,7 @@
- [Ocean Protocol Subgraph](#ocean-protocol-subgraph)
- [3Box](#3box)
- [Purgatory](#purgatory)
- [Network Metadata](#network-metadata)
- [🎨 Storybook](#-storybook)
- [✨ Code Style](#-code-style)
- [👩‍🔬 Testing](#-testing)
@ -229,6 +230,8 @@ function Component() {
Based on [list-purgatory](https://github.com/oceanprotocol/list-purgatory) some data sets get additional data. Within most components this can be done with the internal `useAsset()` hook which fetches data from the [market-purgatory](https://github.com/oceanprotocol/market-purgatory) endpoint in the background.
For asset purgatory:
```tsx
import { useAsset } from '../../../providers/Asset'
@ -238,6 +241,36 @@ function Component() {
}
```
For account purgatory:
```tsx
import { useWeb3 } from '../../../providers/Web3'
import { useAccountPurgatory } from '../../../hooks/useAccountPurgatory'
function Component() {
const { accountId } = useWeb3()
const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId)
return isInPurgatory ? <div>{purgatoryData.reason}</div> : null
}
```
### Network Metadata
All displayed chain & network metadata is retrieved from `https://chainid.network` on build time and integrated into Gatsby's GraphQL layer. This data source is a community-maintained GitHub repository under [ethereum-lists/chains](https://github.com/ethereum-lists/chains).
Within components this metadata can be queried for under `allNetworksMetadataJson`. The `useWeb3()` hook does this in the background to expose the final `networkDisplayName` for use in components:
```tsx
export default function NetworkName(): ReactElement {
const { networkDisplayName, isTestnet } = useWeb3()
return (
<>
{networkDisplayName} {isTestnet && `(Test)`}
</>
)
}
```
## 🎨 Storybook
> TODO: this is broken for most components. See https://github.com/oceanprotocol/market/issues/128

View File

@ -3,7 +3,7 @@ module.exports = {
service: {
name: 'ocean',
url:
'https://subgraph.mainnet.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph',
'https://subgraph.rinkeby.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph',
// optional disable SSL validation check
skipSSLValidation: true
}

View File

@ -16,6 +16,8 @@
"link": "/history"
}
],
"warning": "We are in beta. Please familiarize yourself with [the market](https://oceanprotocol.com/technology/marketplaces), [the risks](https://blog.oceanprotocol.com/on-staking-on-data-in-ocean-market-3d8e09eb0a13), and the [Terms of Use](/terms)."
"warning": "We are in beta. Please familiarize yourself with [the market](https://oceanprotocol.com/technology/marketplaces), [the risks](https://blog.oceanprotocol.com/on-staking-on-data-in-ocean-market-3d8e09eb0a13), and the [Terms of Use](/terms).",
"warningPolygon": "Polygon/Matic EVM support is in early stages. Use the Matic bridge to [get mOCEAN](https://docs.oceanprotocol.com/tutorials/polygon-bridge/).",
"warningPolygonPublish": "Only republish data sets with a pool from ETH Mainnet into Polygon/Matic if the liquidity is **less than or equal to 1000 OCEAN in the original pool**. Doing otherwise will lead to [purgatory](https://github.com/oceanprotocol/list-purgatory) for the data set in Polygon/Matic."
}
}

View File

@ -1,4 +1,3 @@
const path = require('path')
const createFields = require('./gatsby/createFields')
const createMarkdownPages = require('./gatsby/createMarkdownPages')
const execSync = require('child_process').execSync
@ -48,14 +47,6 @@ exports.onCreateWebpackConfig = ({ actions }) => {
fs: 'empty'
},
// fix for 'got'/'swarm-js' dependency
externals: ['got'],
// fix for being able to use `npm link` with @oceanprotocol/react
// see https://github.com/facebook/react/issues/13991
resolve: {
alias: {
react: path.resolve('./node_modules/react')
}
}
externals: ['got']
})
}

12
package-lock.json generated
View File

@ -3643,18 +3643,6 @@
"web3-eth-contract": "^1.3.4"
}
},
"@oceanprotocol/react": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/@oceanprotocol/react/-/react-0.5.5.tgz",
"integrity": "sha512-2xkdiU7vsV+MbS4FD3xrUIud6oZ6ognuLg8zesV8yGND4WlyNR95g2bp3L93H0asg65VdYh5ZSyNzxPHKjzqhg==",
"requires": {
"@oceanprotocol/lib": "^0.11.4",
"axios": "^0.21.1",
"decimal.js": "^10.2.1",
"web3": "1.3.4",
"web3modal": "^1.9.3"
}
},
"@oceanprotocol/typographies": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@oceanprotocol/typographies/-/typographies-0.1.0.tgz",

View File

@ -23,12 +23,11 @@
"postinstall": "husky install"
},
"dependencies": {
"@apollo/client": "^3.3.6",
"@apollo/client": "^3.3.11",
"@coingecko/cryptoformat": "^0.4.2",
"@loadable/component": "^5.14.1",
"@oceanprotocol/art": "^3.0.0",
"@oceanprotocol/lib": "^0.11.4",
"@oceanprotocol/react": "^0.5.5",
"@oceanprotocol/typographies": "^0.1.0",
"@portis/web3": "^3.0.3",
"@sindresorhus/slugify": "^1.0.0",
@ -87,7 +86,8 @@
"slugify": "^1.4.6",
"swr": "^0.3.11",
"use-dark-mode": "^2.3.1",
"web3": "^1.3.1",
"web3": "^1.3.4",
"web3modal": "^1.9.3",
"yup": "^0.32.6"
},
"devDependencies": {

View File

@ -4,7 +4,8 @@ import {
AdditionalInformation,
ServiceMetadata
} from '@oceanprotocol/lib'
import { DataTokenOptions, PriceOptions } from '@oceanprotocol/react'
import { DataTokenOptions } from '../hooks/usePublish'
import { PriceOptions } from '../hooks/usePricing'
export interface AdditionalInformationMarket extends AdditionalInformation {
links?: File[]

View File

@ -1,4 +1,9 @@
export default interface TokenBalance {
export interface PoolBalance {
ocean: number
datatoken: number
}
export interface UserBalance {
eth: string
ocean: string
}

View File

@ -4,9 +4,10 @@ import Header from './organisms/Header'
import Styles from '../global/Styles'
import styles from './App.module.css'
import { useSiteMetadata } from '../hooks/useSiteMetadata'
import { useOcean } from '@oceanprotocol/react'
import Alert from './atoms/Alert'
import { graphql, PageProps, useStaticQuery } from 'gatsby'
import { useAccountPurgatory } from '../hooks/useAccountPurgatory'
import { useWeb3 } from '../providers/Web3'
const contentQuery = graphql`
query AppQuery {
@ -34,23 +35,25 @@ export default function App({
const data = useStaticQuery(contentQuery)
const purgatory = data.purgatory.edges[0].node.childContentJson.account
const { warning } = useSiteMetadata()
const {
isInPurgatory: isAccountInPurgatory,
purgatoryData: accountPurgatory
} = useOcean()
const { warning, warningPolygon } = useSiteMetadata()
const { accountId } = useWeb3()
const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId)
const { networkId } = useWeb3()
return (
<Styles>
<div className={styles.app}>
<Header />
{(props as PageProps).uri === '/' && (
<Alert text={warning} state="info" />
<Alert
text={(networkId === 137 ? `${warningPolygon}\n\n` : '') + warning}
state="info"
/>
)}
{isAccountInPurgatory && (
{isInPurgatory && (
<Alert
title={purgatory.title}
badge={`Reason: ${accountPurgatory?.reason}`}
badge={`Reason: ${purgatoryData?.reason}`}
text={purgatory.description}
state="error"
/>

View File

@ -1,18 +0,0 @@
.dropzone {
padding: var(--spacer);
text-align: center;
color: var(--color-secondary);
border: 0.1rem dashed var(--color-secondary);
font-size: var(--font-size-small);
border-radius: var(--border-radius);
}
.dragover {
border-color: var(--color-primary);
}
.disabled {
}
.error {
}

View File

@ -1,8 +0,0 @@
import React from 'react'
import Dropzone from './Dropzone'
export default {
title: 'Atoms/Dropzone'
}
export const Default = () => <Dropzone handleOnDrop={() => null} />

View File

@ -1,68 +0,0 @@
import React, { ReactElement, useCallback } from 'react'
import { useDropzone } from 'react-dropzone'
import styles from './Dropzone.module.css'
import { formatBytes } from '../../utils'
export default function Dropzone({
handleOnDrop,
disabled,
multiple,
error
}: {
handleOnDrop(files: File[]): void
disabled?: boolean
multiple?: boolean
error?: string
}): ReactElement {
const onDrop = useCallback((acceptedFiles) => handleOnDrop(acceptedFiles), [
handleOnDrop
])
const {
getRootProps,
getInputProps,
isDragActive,
isDragReject,
acceptedFiles
} = useDropzone({ onDrop })
const files = acceptedFiles.map((file: any) => (
<li key={file.path}>
{file.path} - {formatBytes(file.size, 0)}
</li>
))
return (
<div
{...getRootProps({
className: isDragActive
? `${styles.dropzone} ${styles.dragover}`
: disabled
? `${styles.dropzone} ${styles.disabled}`
: styles.dropzone
})}
>
<div>
{acceptedFiles.length > 0 ? (
<aside>
<ul>{files}</ul>
</aside>
) : (
<>
<input {...getInputProps({ multiple })} />
{isDragActive && !isDragReject ? (
`Drop it like it's hot!`
) : multiple ? (
`Drag 'n' drop some files here, or click to select files`
) : error ? (
<div className={styles.error}>{error}</div>
) : (
`Drag 'n' drop a file here, or click to select a file`
)}
</>
)}
</div>
</div>
)
}

View File

@ -1,63 +0,0 @@
import React, { ReactElement, ReactNode, useEffect, useState } from 'react'
import { EthereumListsChain, getNetworkData } from '../../utils/wallet'
import { ReactComponent as External } from '../../images/external.svg'
import styles from './EtherscanLink.module.css'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
import { graphql, useStaticQuery } from 'gatsby'
const networksQuery = graphql`
query {
allNetworksMetadataJson {
edges {
node {
chain
network
networkId
}
}
}
}
`
export default function EtherscanLink({
networkId,
path,
children
}: {
networkId: number
path: string
children: ReactNode
}): ReactElement {
const data = useStaticQuery(networksQuery)
const networksList: { node: EthereumListsChain }[] =
data.allNetworksMetadataJson.edges
const { appConfig } = useSiteMetadata()
const [url, setUrl] = useState<string>()
useEffect(() => {
const networkData = networkId
? getNetworkData(networksList, networkId)
: null
const url =
(!networkId && appConfig.network === 'mainnet') || networkId === 1
? `https://etherscan.io`
: `https://${
networkData ? networkData.network : appConfig.network
}.etherscan.io`
setUrl(url)
}, [networkId, networksList, appConfig.network])
return (
<a
href={`${url}/${path}`}
title="View on Etherscan"
target="_blank"
rel="noreferrer"
className={styles.link}
>
{children} <External />
</a>
)
}

View File

@ -0,0 +1,33 @@
import React, { ReactElement, ReactNode, useEffect, useState } from 'react'
import { ReactComponent as External } from '../../images/external.svg'
import styles from './ExplorerLink.module.css'
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import { useOcean } from '../../providers/Ocean'
export default function ExplorerLink({
path,
children
}: {
networkId: number
path: string
children: ReactNode
}): ReactElement {
const { config } = useOcean()
const [url, setUrl] = useState<string>()
useEffect(() => {
setUrl((config as ConfigHelperConfig).explorerUri)
}, [config])
return (
<a
href={`${url}/${path}`}
title={`View on ${(config as ConfigHelperConfig).explorerUri}`}
target="_blank"
rel="noreferrer"
className={styles.link}
>
{children} <External />
</a>
)
}

View File

@ -12,7 +12,6 @@ const Markdown = ({
// fix react-markdown \n transformation
// https://github.com/rexxars/react-markdown/issues/105#issuecomment-351585313
const textCleaned = text.replace(/\\n/g, '\n ')
return (
<ReactMarkdown
source={textCleaned}

View File

@ -7,6 +7,6 @@ export default {
title: 'Atoms/Price'
}
export const Normal = () => <Price ddo={ddo as DDO} />
export const Normal = () => <Price price={(ddo as DDO).price} />
export const Small = () => <Price ddo={ddo as DDO} small />
export const Small = () => <Price price={(ddo as DDO).price} small />

View File

@ -1,25 +1,21 @@
import React, { ReactElement } from 'react'
import styles from './index.module.css'
import { useMetadata } from '@oceanprotocol/react'
import { DDO } from '@oceanprotocol/lib'
import { BestPrice } from '@oceanprotocol/lib'
import Loader from '../Loader'
import Tooltip from '../Tooltip'
import PriceUnit from './PriceUnit'
export default function Price({
ddo,
price,
className,
small,
conversion
}: {
ddo: DDO
price: BestPrice
className?: string
small?: boolean
conversion?: boolean
}): ReactElement {
// price is not fetched from the chain anymore , will update one AssetProvider is implemented
const { price } = useMetadata(ddo)
return price?.value ? (
<PriceUnit
price={`${price.value}`}

View File

@ -1,7 +1,7 @@
import React, { ReactElement } from 'react'
import styles from './ProfileDetails.module.css'
import { Profile } from '../../../models/Profile'
import EtherscanLink from '../EtherscanLink'
import ExplorerLink from '../ExplorerLink'
import PublisherLinks from './PublisherLinks'
export default function ProfileDetails({
@ -26,9 +26,9 @@ export default function ProfileDetails({
{profile?.emoji} {profile?.name}
</h3>
<EtherscanLink networkId={networkId} path={`address/${account}`}>
<ExplorerLink networkId={networkId} path={`address/${account}`}>
<code>{account}</code>
</EtherscanLink>
</ExplorerLink>
</header>
{profile?.description && (

View File

@ -5,13 +5,13 @@ import Tooltip from '../Tooltip'
import { Profile } from '../../../models/Profile'
import { Link } from 'gatsby'
import get3BoxProfile from '../../../utils/profile'
import EtherscanLink from '../EtherscanLink'
import { accountTruncate } from '../../../utils/wallet'
import ExplorerLink from '../ExplorerLink'
import { accountTruncate } from '../../../utils/web3'
import axios from 'axios'
import { useOcean } from '@oceanprotocol/react'
import { ReactComponent as Info } from '../../../images/info.svg'
import ProfileDetails from './ProfileDetails'
import Add from './Add'
import { useWeb3 } from '../../../providers/Web3'
const cx = classNames.bind(styles)
@ -24,7 +24,7 @@ export default function Publisher({
minimal?: boolean
className?: string
}): ReactElement {
const { networkId, accountId } = useOcean()
const { networkId, accountId } = useWeb3()
const [profile, setProfile] = useState<Profile>()
const [name, setName] = useState<string>()
@ -88,9 +88,9 @@ export default function Publisher({
</Tooltip>
)}
{showAdd && <Add />}
<EtherscanLink networkId={networkId} path={`address/${account}`}>
Etherscan
</EtherscanLink>
<ExplorerLink networkId={networkId} path={`address/${account}`}>
Explorer
</ExplorerLink>
</div>
</>
)}

View File

@ -1,6 +1,7 @@
import React, { ReactElement } from 'react'
import { Helmet } from 'react-helmet'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
import { isBrowser } from '../../utils'
export default function Seo({
title,
@ -24,7 +25,7 @@ export default function Seo({
>
<html lang="en" />
{typeof window !== 'undefined' &&
{isBrowser &&
window.location &&
window.location.hostname !== 'oceanprotocol.com' && (
<meta name="robots" content="noindex,nofollow" />

View File

@ -1,8 +1,8 @@
import { DDO } from '@oceanprotocol/lib'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../providers/Ocean'
import { Link } from 'gatsby'
import React, { ReactElement, useEffect, useState } from 'react'
import { retrieveDDO, getAssetsNames } from '../../utils/aquarius'
import { getAssetsNames } from '../../utils/aquarius'
import styles from './AssetListTitle.module.css'
import axios from 'axios'

View File

@ -6,7 +6,6 @@ import styles from './AssetTeaser.module.css'
import { DDO } from '@oceanprotocol/lib'
import removeMarkdown from 'remove-markdown'
import Publisher from '../atoms/Publisher'
import { useMetadata } from '@oceanprotocol/react'
import Time from '../atoms/Time'
import AssetType from '../atoms/AssetType'
@ -15,12 +14,12 @@ declare type AssetTeaserProps = {
}
const AssetTeaser: React.FC<AssetTeaserProps> = ({ ddo }: AssetTeaserProps) => {
const { owner } = useMetadata(ddo)
const { attributes } = ddo.findServiceByType('metadata')
const { name, type } = attributes.main
const { dataTokenInfo } = ddo
const isCompute = Boolean(ddo?.findServiceByType('compute'))
const accessType = isCompute ? 'compute' : 'access'
const { owner } = ddo.publicKey[0]
return (
<article className={`${styles.teaser} ${styles[type]}`}>
@ -48,7 +47,7 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({ ddo }: AssetTeaserProps) => {
</div>
<footer className={styles.foot}>
<Price ddo={ddo} small />
<Price price={ddo.price} small />
<p className={styles.date}>
<Time date={ddo?.created} relative />
</p>

View File

@ -2,7 +2,7 @@ import { useUserPreferences } from '../../providers/UserPreferences'
import React, { ReactElement, useEffect, useState } from 'react'
import Table from '../atoms/Table'
import { DDO, Logger } from '@oceanprotocol/lib'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../providers/Ocean'
import Price from '../atoms/Price'
import Tooltip from '../atoms/Tooltip'
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
@ -73,7 +73,7 @@ const columns = [
{
name: 'Price',
selector: function getAssetRow(row: DDO) {
return <Price ddo={row} small />
return <Price price={row.price} small />
},
right: true
}

View File

@ -1,6 +1,6 @@
import { useField } from 'formik'
import { InputProps } from '../../../atoms/Input'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../../../providers/Ocean'
import React, { ReactElement, useEffect } from 'react'
import styles from './index.module.css'

View File

@ -4,7 +4,7 @@ import { useField } from 'formik'
import { toast } from 'react-toastify'
import FileInfo from './Info'
import FileInput from './Input'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../../../providers/Ocean'
import { InputProps } from '../../../atoms/Input'
import { fileinfo } from '../../../../utils/provider'

View File

@ -22,8 +22,7 @@ export default function MarketStats(): ReactElement {
const { data } = useQuery(getTotalPoolsValues)
useEffect(() => {
if (!data) return
if (!data || !data.poolFactories || data.poolFactories.length === 0) return
setTotalValueLocked(data.poolFactories[0].totalValueLocked)
setTotalOceanLiquidity(data.poolFactories[0].totalOceanLiquidity)
setPoolCount(data.poolFactories[0].finalizedPoolCount)

View File

@ -1,6 +1,6 @@
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../providers/Ocean'
import React, { ReactElement, useEffect, useState } from 'react'
import EtherscanLink from '../atoms/EtherscanLink'
import ExplorerLink from '../atoms/ExplorerLink'
import Time from '../atoms/Time'
import Table from '../atoms/Table'
import AssetTitle from './AssetListTitle'
@ -15,6 +15,7 @@ import {
} from '../../@types/apollo/TransactionHistory'
import web3 from 'web3'
import { useWeb3 } from '../../providers/Web3'
const txHistoryQueryByPool = gql`
query TransactionHistoryByPool($user: String, $pool: String) {
@ -129,7 +130,8 @@ async function getTitle(
}
function Title({ row }: { row: TransactionHistoryPoolTransactions }) {
const { ocean, networkId } = useOcean()
const { networkId } = useWeb3()
const { ocean } = useOcean()
const [title, setTitle] = useState<string>()
const { locale } = useUserPreferences()
@ -144,9 +146,9 @@ function Title({ row }: { row: TransactionHistoryPoolTransactions }) {
}, [ocean, row, locale])
return title ? (
<EtherscanLink networkId={networkId} path={`/tx/${row.tx}`}>
<ExplorerLink networkId={networkId} path={`/tx/${row.tx}`}>
<span className={styles.titleText}>{title}</span>
</EtherscanLink>
</ExplorerLink>
) : null
}
@ -193,7 +195,7 @@ export default function PoolTransactions({
poolAddress?: string
minimal?: boolean
}): ReactElement {
const { accountId } = useOcean()
const { accountId } = useWeb3()
const [logs, setLogs] = useState<TransactionHistoryPoolTransactions[]>()
const { data, loading } = useQuery<TransactionHistory>(

View File

@ -0,0 +1,19 @@
.buttons {
composes: buttons from './Appearance.module.css';
}
.button {
composes: button from './Appearance.module.css';
}
.button span {
display: block;
font-size: var(--font-size-small);
font-family: var(--font-family-base);
font-weight: var(--font-weight-base);
margin-top: calc(var(--spacer) / 10);
}
.selected {
composes: selected from './Appearance.module.css';
}

View File

@ -0,0 +1,53 @@
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import React, { ReactElement } from 'react'
import { useOcean } from '../../../providers/Ocean'
import { useWeb3 } from '../../../providers/Web3'
import { getOceanConfig } from '../../../utils/ocean'
import Button from '../../atoms/Button'
import FormHelp from '../../atoms/Input/Help'
import Label from '../../atoms/Input/Label'
import styles from './Chain.module.css'
export default function Chain(): ReactElement {
const { web3 } = useWeb3()
const { config, connect } = useOcean()
async function connectOcean(networkName: string) {
const config = getOceanConfig(networkName)
await connect(config)
}
const chains = [
{ name: 'ETH', oceanConfig: 'mainnet' },
{ name: 'Polygon/Matic', oceanConfig: 'polygon' }
]
// TODO: to fully solve https://github.com/oceanprotocol/market/issues/432
// there are more considerations for users with a wallet connected (wallet network vs. setting network).
// For now, only show the setting for non-wallet users.
return !web3 ? (
<li>
<Label htmlFor="">Chain</Label>
<div className={styles.buttons}>
{chains.map((button) => {
const selected =
(config as ConfigHelperConfig).network === button.oceanConfig
return (
<Button
key={button.name}
className={`${styles.button} ${selected ? styles.selected : ''}`}
size="small"
style="text"
onClick={() => connectOcean(button.oceanConfig)}
>
{button.name}
<span>Mainnet</span>
</Button>
)
})}
</div>
<FormHelp>Switch the data source for the interface.</FormHelp>
</li>
) : null
}

View File

@ -8,6 +8,7 @@ import { ReactComponent as Caret } from '../../../images/caret.svg'
import useDarkMode from 'use-dark-mode'
import Appearance from './Appearance'
import { darkModeConfig } from '../../../../app.config'
import Chain from './Chain'
export default function UserPreferences(): ReactElement {
// Calling this here because <Theme /> is not mounted on first load
@ -19,6 +20,7 @@ export default function UserPreferences(): ReactElement {
<ul className={styles.preferencesDetails}>
<Currency />
<Appearance darkMode={darkMode} />
<Chain />
<Debug />
</ul>
}

View File

@ -1,11 +1,10 @@
import { useOcean } from '@oceanprotocol/react'
import { toDataUrl } from 'ethereum-blockies'
import React, { FormEvent } from 'react'
import { ReactComponent as Caret } from '../../../images/caret.svg'
import { accountTruncate } from '../../../utils/wallet'
import { accountTruncate } from '../../../utils/web3'
import Loader from '../../atoms/Loader'
import Status from '../../atoms/Status'
import styles from './Account.module.css'
import { useWeb3 } from '../../../providers/Web3'
const Blockies = ({ account }: { account: string | undefined }) => {
if (!account) return null
@ -24,8 +23,7 @@ const Blockies = ({ account }: { account: string | undefined }) => {
// Forward ref for Tippy.js
// eslint-disable-next-line
const Account = React.forwardRef((props, ref: any) => {
const { accountId, status, connect, web3Modal } = useOcean()
const hasSuccess = status === 1
const { accountId, web3Modal, connect } = useWeb3()
async function handleActivation(e: FormEvent<HTMLButtonElement>) {
// prevent accidentially submitting a form the button might be in
@ -50,9 +48,6 @@ const Account = React.forwardRef((props, ref: any) => {
<span className={styles.address} title={accountId}>
{accountTruncate(accountId)}
</span>
{!hasSuccess && (
<Status className={styles.status} state="warning" aria-hidden />
)}
<Caret aria-hidden="true" />
</button>
) : (

View File

@ -24,7 +24,7 @@
}
.symbol {
width: 20%;
width: 22%;
text-align: right;
font-weight: var(--font-weight-base);
font-size: var(--font-size-small);

View File

@ -1,28 +1,37 @@
import React, { ReactElement, useEffect, useState } from 'react'
import Button from '../../atoms/Button'
import styles from './Details.module.css'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../../providers/Ocean'
import Web3Feedback from './Feedback'
import { getProviderInfo, IProviderInfo } from 'web3modal'
import Conversion from '../../atoms/Price/Conversion'
import { formatCurrency } from '@coingecko/cryptoformat'
import { useUserPreferences } from '../../../providers/UserPreferences'
import { useWeb3 } from '../../../providers/Web3'
export default function Details(): ReactElement {
const { balance, connect, logout, web3Provider } = useOcean()
const { web3Provider, connect, logout, networkData, networkId } = useWeb3()
const { balance } = useOcean()
const { locale } = useUserPreferences()
const [providerInfo, setProviderInfo] = useState<IProviderInfo>()
const [mainCurrency, setMainCurrency] = useState<string>()
// const [portisNetwork, setPortisNetwork] = useState<string>()
// Workaround cause getInjectedProviderName() always returns `MetaMask`
// https://github.com/oceanprotocol/market/issues/332
useEffect(() => {
if (!web3Provider) return
const providerInfo = getProviderInfo(web3Provider)
setProviderInfo(providerInfo)
}, [web3Provider])
useEffect(() => {
if (!networkData) return
setMainCurrency(networkData.nativeCurrency.symbol)
}, [networkData])
// Handle network change for Portis
// async function handlePortisNetworkChange(e: ChangeEvent<HTMLSelectElement>) {
// setPortisNetwork(e.target.value)
@ -38,7 +47,13 @@ export default function Details(): ReactElement {
<ul>
{Object.entries(balance).map(([key, value]) => (
<li className={styles.balance} key={key}>
<span className={styles.symbol}>{key.toUpperCase()}</span>{' '}
<span className={styles.symbol}>
{key === 'eth'
? mainCurrency
: key === 'ocean' && networkId === 137
? 'mOCEAN'
: key.toUpperCase()}
</span>{' '}
{formatCurrency(Number(value), '', locale, false, {
significantFigures: 4
})}

View File

@ -1,7 +1,7 @@
import React, { ReactElement } from 'react'
import { useOcean } from '../../../providers/Ocean'
import Status from '../../atoms/Status'
import styles from './Feedback.module.css'
import { useOcean } from '@oceanprotocol/react'
export declare type Web3Error = {
status: 'error' | 'warning' | 'success'
@ -14,10 +14,8 @@ export default function Web3Feedback({
}: {
isBalanceSufficient?: boolean
}): ReactElement {
const { account, status } = useOcean()
const isOceanConnectionError = status === -1
const showFeedback =
!account || isOceanConnectionError || isBalanceSufficient === false
const { account, ocean } = useOcean()
const showFeedback = !account || !ocean || isBalanceSufficient === false
const state = !account
? 'error'
@ -27,7 +25,7 @@ export default function Web3Feedback({
const title = !account
? 'No account connected'
: isOceanConnectionError
: !ocean
? 'Error connecting to Ocean'
: account
? isBalanceSufficient === false
@ -37,7 +35,7 @@ export default function Web3Feedback({
const message = !account
? 'Please connect your Web3 wallet.'
: isOceanConnectionError
: !ocean
? 'Please try again.'
: isBalanceSufficient === false
? 'You do not have enough OCEAN in your wallet to purchase this asset.'

View File

@ -1,43 +1,19 @@
import React, { useState, useEffect, ReactElement } from 'react'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../../providers/Ocean'
import Status from '../../atoms/Status'
import {
EthereumListsChain,
getNetworkData,
getNetworkDisplayName
} from '../../../utils/wallet'
import { ConfigHelper } from '@oceanprotocol/lib'
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import styles from './Network.module.css'
import Badge from '../../atoms/Badge'
import Tooltip from '../../atoms/Tooltip'
import { graphql, useStaticQuery } from 'gatsby'
const networksQuery = graphql`
query NetworksQuery {
allNetworksMetadataJson {
edges {
node {
chain
network
networkId
}
}
}
}
`
import { useWeb3 } from '../../../providers/Web3'
export default function Network(): ReactElement {
const data = useStaticQuery(networksQuery)
const networksList: { node: EthereumListsChain }[] =
data.allNetworksMetadataJson.edges
const { config, networkId } = useOcean()
const { networkId, networkDisplayName, isTestnet } = useWeb3()
const { config } = useOcean()
const networkIdConfig = (config as ConfigHelperConfig).networkId
const [isEthMainnet, setIsEthMainnet] = useState<boolean>()
const [networkName, setNetworkName] = useState<string>()
const [isTestnet, setIsTestnet] = useState<boolean>()
const [isSupportedNetwork, setIsSupportedNetwork] = useState<boolean>()
useEffect(() => {
@ -51,23 +27,16 @@ export default function Network(): ReactElement {
// to figure out if network is supported.
const isSupportedNetwork = Boolean(new ConfigHelper().getConfig(network))
setIsSupportedNetwork(isSupportedNetwork)
}, [networkId, networkIdConfig])
// Figure out if we're on a chain's testnet, or not
const networkData = getNetworkData(networksList, network)
setIsTestnet(networkData.network !== 'mainnet')
const networkName = getNetworkDisplayName(networkData, network)
setNetworkName(networkName)
}, [networkId, networkIdConfig, networksList])
return !isEthMainnet && networkName ? (
return !isEthMainnet && networkDisplayName ? (
<div className={styles.network}>
{!isSupportedNetwork && (
<Tooltip content="No Ocean Protocol contracts are deployed to this network.">
<Status state="error" className={styles.warning} />
</Tooltip>
)}
<span className={styles.name}>{networkName}</span>
<span className={styles.name}>{networkDisplayName}</span>
{isTestnet && <Badge label="Test" className={styles.badge} />}
</div>
) : null

View File

@ -3,11 +3,11 @@ import Account from './Account'
import Details from './Details'
import Tooltip from '../../atoms/Tooltip'
import Network from './Network'
import { useOcean } from '@oceanprotocol/react'
import styles from './index.module.css'
import { useWeb3 } from '../../../providers/Web3'
export default function Wallet(): ReactElement {
const { accountId } = useOcean()
const { accountId } = useWeb3()
return (
<div className={styles.wallet}>

View File

@ -1,22 +1,18 @@
import React, { useState, ReactElement, ChangeEvent, useEffect } from 'react'
import { DDO, Logger } from '@oceanprotocol/lib'
import { DDO } from '@oceanprotocol/lib'
import Loader from '../../atoms/Loader'
import Web3Feedback from '../../molecules/Wallet/Feedback'
import Dropzone from '../../atoms/Dropzone'
import Price from '../../atoms/Price'
import File from '../../atoms/File'
import {
computeOptions,
useCompute,
readFileContent,
useOcean,
usePricing
} from '@oceanprotocol/react'
import { computeOptions, useCompute } from '../../../hooks/useCompute'
import styles from './Compute.module.css'
import Input from '../../atoms/Input'
import Alert from '../../atoms/Alert'
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
import checkPreviousOrder from '../../../utils/checkPreviousOrder'
import { useOcean } from '../../../providers/Ocean'
import { useWeb3 } from '../../../providers/Web3'
import { usePricing } from '../../../hooks/usePricing'
import { useAsset } from '../../../providers/Asset'
export default function Compute({
@ -29,12 +25,12 @@ export default function Compute({
dtBalance: string
}): ReactElement {
const { marketFeeAddress } = useSiteMetadata()
const { accountId } = useWeb3()
const { ocean } = useOcean()
const { type } = useAsset()
const { ocean, accountId } = useOcean()
const { compute, isLoading, computeStepText, computeError } = useCompute()
const { buyDT, dtSymbol } = usePricing(ddo)
const { price } = useAsset()
const computeService = ddo.findServiceByType('compute')
const metadataService = ddo.findServiceByType('metadata')
@ -68,12 +64,6 @@ export default function Compute({
checkPreviousOrders()
}, [ocean, ddo, accountId])
const onDrop = async (files: File[]) => {
setFile(files[0])
const fileText = await readFileContent(files[0])
setAlgorithmRawCode(fileText)
}
const handleSelectChange = (event: ChangeEvent<HTMLSelectElement>) => {
const comType = event.target.value
setComputeType(comType)
@ -83,36 +73,36 @@ export default function Compute({
setComputeContainer(selectedComputeOption.value)
}
const startJob = async () => {
try {
if (!ocean) return
// const startJob = async () => {
// try {
// if (!ocean) return
setIsJobStarting(true)
setIsPublished(false)
setError('')
// setIsJobStarting(true)
// setIsPublished(false)
// setError('')
!hasPreviousOrder && !hasDatatoken && (await buyDT('1'))
// !hasPreviousOrder && !hasDatatoken && (await buyDT('1'))
await compute(
ddo.id,
computeService,
ddo.dataToken,
algorithmRawCode,
computeContainer,
marketFeeAddress,
previousOrderId
)
// await compute(
// ddo.id,
// computeService,
// ddo.dataToken,
// algorithmRawCode,
// computeContainer,
// marketFeeAddress,
// previousOrderId
// )
setHasPreviousOrder(true)
setIsPublished(true)
setFile(null)
} catch (error) {
setError('Failed to start job!')
Logger.error(error.message)
} finally {
setIsJobStarting(false)
}
}
// setHasPreviousOrder(true)
// setIsPublished(true)
// setFile(null)
// } catch (error) {
// setError('Failed to start job!')
// Logger.error(error.message)
// } finally {
// setIsJobStarting(false)
// }
// }
return (
<>
@ -121,7 +111,7 @@ export default function Compute({
<File file={metadataService.attributes.main.files[0]} small />
</div>
<div className={styles.pricewrapper}>
<Price ddo={ddo} conversion />
<Price price={price} conversion />
{hasDatatoken && (
<div className={styles.hasTokens}>
You own {dtBalance} {dtSymbol} allowing you to use this data set
@ -154,7 +144,6 @@ export default function Compute({
onChange={handleSelectChange}
/>
)}
<Dropzone multiple={false} handleOnDrop={onDrop} />
<div className={styles.actions}>
{isLoading ? (

View File

@ -7,14 +7,16 @@ import Price from '../../atoms/Price'
import Web3Feedback from '../../molecules/Wallet/Feedback'
import styles from './Consume.module.css'
import Loader from '../../atoms/Loader'
import { useOcean, useConsume, usePricing } from '@oceanprotocol/react'
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
import checkPreviousOrder from '../../../utils/checkPreviousOrder'
import { useAsset } from '../../../providers/Asset'
import { secondsToString } from '../../../utils/metadata'
import { gql, useQuery } from '@apollo/client'
import { OrdersData } from '../../../@types/apollo/OrdersData'
import BigNumber from 'bignumber.js'
import { useOcean } from '../../../providers/Ocean'
import { useWeb3 } from '../../../providers/Web3'
import { usePricing } from '../../../hooks/usePricing'
import { useConsume } from '../../../hooks/useConsume'
const previousOrderQuery = gql`
query PreviousOrder($id: String!, $account: String!) {
@ -61,7 +63,8 @@ export default function Consume({
isBalanceSufficient: boolean
dtBalance: string
}): ReactElement {
const { ocean, accountId } = useOcean()
const { accountId } = useWeb3()
const { ocean } = useOcean()
const { marketFeeAddress } = useSiteMetadata()
const [hasPreviousOrder, setHasPreviousOrder] = useState(false)
const [previousOrderId, setPreviousOrderId] = useState<string>()
@ -141,7 +144,7 @@ export default function Consume({
])
async function handleConsume() {
!hasPreviousOrder && !hasDatatoken && (await buyDT('1'))
!hasPreviousOrder && !hasDatatoken && (await buyDT('1', price))
await consume(
ddo.id,
ddo.dataToken,
@ -191,7 +194,7 @@ export default function Consume({
<File file={file} />
</div>
<div className={styles.pricewrapper}>
<Price ddo={ddo} conversion />
<Price price={price} conversion />
{!isInPurgatory && <PurchaseButton />}
</div>
</div>

View File

@ -3,10 +3,11 @@ import styles from './FormEditMetadata.module.css'
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
import Button from '../../../atoms/Button'
import Input from '../../../atoms/Input'
import { useOcean } from '@oceanprotocol/react'
import { FormFieldProps } from '../../../../@types/Form'
import { MetadataPublishFormDataset } from '../../../../@types/MetaData'
import { checkIfTimeoutInPredefinedValues } from '../../../../utils/metadata'
import { useOcean } from '../../../../providers/Ocean'
import { useWeb3 } from '../../../../providers/Web3'
function handleTimeoutCustomOption(
data: FormFieldProps[],
@ -53,7 +54,8 @@ export default function FormEditMetadata({
setTimeoutStringValue: (value: string) => void
values: Partial<MetadataPublishFormDataset>
}): ReactElement {
const { ocean, accountId } = useOcean()
const { accountId } = useWeb3()
const { ocean } = useOcean()
const {
isValid,
validateField,

View File

@ -1,4 +1,3 @@
import { useOcean } from '@oceanprotocol/react'
import { Formik } from 'formik'
import React, { ReactElement, useState } from 'react'
import { MetadataEditForm } from '../../../../@types/MetaData'
@ -17,6 +16,8 @@ import styles from './index.module.css'
import { Logger } from '@oceanprotocol/lib'
import MetadataFeedback from '../../../molecules/MetadataFeedback'
import { graphql, useStaticQuery } from 'gatsby'
import { useWeb3 } from '../../../../providers/Web3'
import { useOcean } from '../../../../providers/Ocean'
const contentQuery = graphql`
query EditMetadataQuery {
@ -57,7 +58,8 @@ export default function Edit({
const content = data.content.edges[0].node.childPagesJson
const { debug } = useUserPreferences()
const { ocean, accountId } = useOcean()
const { accountId } = useWeb3()
const { ocean } = useOcean()
const { metadata, ddo, refreshDdo } = useAsset()
const [success, setSuccess] = useState<string>()
const [error, setError] = useState<string>()

View File

@ -2,9 +2,10 @@ import React, { ReactElement } from 'react'
import Loader from '../../../atoms/Loader'
import Button from '../../../atoms/Button'
import styles from './Actions.module.css'
import EtherscanLink from '../../../atoms/EtherscanLink'
import ExplorerLink from '../../../atoms/ExplorerLink'
import SuccessConfetti from '../../../atoms/SuccessConfetti'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../../../providers/Ocean'
import { useWeb3 } from '../../../../providers/Web3'
export default function Actions({
isLoading,
@ -23,7 +24,8 @@ export default function Actions({
action: () => void
isDisabled?: boolean
}): ReactElement {
const { networkId, ocean } = useOcean()
const { networkId } = useWeb3()
const { ocean } = useOcean()
return (
<>
@ -46,9 +48,9 @@ export default function Actions({
className={styles.success}
success={successMessage}
action={
<EtherscanLink networkId={networkId} path={`/tx/${txId}`}>
See on Etherscan
</EtherscanLink>
<ExplorerLink networkId={networkId} path={`/tx/${txId}`}>
View transaction
</ExplorerLink>
}
/>
)}

View File

@ -10,9 +10,9 @@ import {
import Button from '../../../../atoms/Button'
import CoinSelect from '../CoinSelect'
import { FormAddLiquidity } from '.'
import { useOcean } from '@oceanprotocol/react'
import TokenBalance from '../../../../../@types/TokenBalance'
import { PoolBalance } from '../../../../../@types/TokenBalance'
import UserLiquidity from '../../../../atoms/UserLiquidity'
import { useOcean } from '../../../../../providers/Ocean'
export default function FormAdd({
coin,
@ -32,7 +32,7 @@ export default function FormAdd({
amountMax: string
setCoin: (value: string) => void
totalPoolTokens: string
totalBalance: TokenBalance
totalBalance: PoolBalance
poolAddress: string
setNewPoolTokens: (value: string) => void
setNewPoolShare: (value: string) => void

View File

@ -2,7 +2,7 @@ import { FormikContextType, useFormikContext } from 'formik'
import { graphql, useStaticQuery } from 'gatsby'
import React, { ReactElement, useEffect, useState } from 'react'
import { FormAddLiquidity } from '.'
import TokenBalance from '../../../../../@types/TokenBalance'
import { PoolBalance } from '../../../../../@types/TokenBalance'
import FormHelp from '../../../../atoms/Input/Help'
import Token from '../Token'
import styles from './Output.module.css'
@ -44,7 +44,7 @@ export default function Output({
swapFee: string
dtSymbol: string
totalPoolTokens: string
totalBalance: TokenBalance
totalBalance: PoolBalance
coin: string
}): ReactElement {
const data = useStaticQuery(contentQuery)

View File

@ -1,5 +1,4 @@
import React, { ReactElement, useState, useEffect } from 'react'
import { useOcean } from '@oceanprotocol/react'
import Header from '../Header'
import { toast } from 'react-toastify'
import Actions from '../Actions'
@ -9,10 +8,12 @@ import { Formik } from 'formik'
import FormAdd from './FormAdd'
import styles from './index.module.css'
import Alert from '../../../../atoms/Alert'
import TokenBalance from '../../../../../@types/TokenBalance'
import { PoolBalance } from '../../../../../@types/TokenBalance'
import { useUserPreferences } from '../../../../../providers/UserPreferences'
import Output from './Output'
import DebugOutput from '../../../../atoms/DebugOutput'
import { useOcean } from '../../../../../providers/Ocean'
import { useWeb3 } from '../../../../../providers/Web3'
const contentQuery = graphql`
query PoolAddQuery {
@ -56,7 +57,7 @@ export default function Add({
refreshInfo: () => void
poolAddress: string
totalPoolTokens: string
totalBalance: TokenBalance
totalBalance: PoolBalance
swapFee: string
dtSymbol: string
dtAddress: string
@ -64,7 +65,8 @@ export default function Add({
const data = useStaticQuery(contentQuery)
const content = data.content.edges[0].node.childContentJson.pool.add
const { ocean, accountId, balance } = useOcean()
const { accountId } = useWeb3()
const { ocean, balance } = useOcean()
const { debug } = useUserPreferences()
const [txId, setTxId] = useState<string>()
const [coin, setCoin] = useState('OCEAN')

View File

@ -7,7 +7,6 @@ import React, {
useRef
} from 'react'
import styles from './Remove.module.css'
import { useOcean } from '@oceanprotocol/react'
import Header from './Header'
import { toast } from 'react-toastify'
import Actions from './Actions'
@ -19,6 +18,8 @@ import { getMaxPercentRemove } from './utils'
import { graphql, useStaticQuery } from 'gatsby'
import debounce from 'lodash.debounce'
import UserLiquidity from '../../../atoms/UserLiquidity'
import { useOcean } from '../../../../providers/Ocean'
import { useWeb3 } from '../../../../providers/Web3'
const contentQuery = graphql`
query PoolRemoveQuery {
@ -63,7 +64,8 @@ export default function Remove({
const data = useStaticQuery(contentQuery)
const content = data.content.edges[0].node.childContentJson.pool.remove
const { ocean, accountId } = useOcean()
const { accountId } = useWeb3()
const { ocean } = useOcean()
const [amountPercent, setAmountPercent] = useState('0')
const [amountMaxPercent, setAmountMaxPercent] = useState('100')
const [amountPoolShares, setAmountPoolShares] = useState('0')

View File

@ -1,5 +1,4 @@
import React, { ReactElement, useEffect, useState } from 'react'
import { useOcean } from '@oceanprotocol/react'
import { Logger } from '@oceanprotocol/lib'
import styles from './index.module.css'
import stylesActions from './Actions.module.css'
@ -8,16 +7,18 @@ import Button from '../../../atoms/Button'
import Add from './Add'
import Remove from './Remove'
import Tooltip from '../../../atoms/Tooltip'
import EtherscanLink from '../../../atoms/EtherscanLink'
import ExplorerLink from '../../../atoms/ExplorerLink'
import Token from './Token'
import TokenList from './TokenList'
import { graphql, useStaticQuery } from 'gatsby'
import TokenBalance from '../../../../@types/TokenBalance'
import { PoolBalance } from '../../../../@types/TokenBalance'
import Transactions from './Transactions'
import Graph from './Graph'
import { useAsset } from '../../../../providers/Asset'
import { gql, useQuery } from '@apollo/client'
import { PoolLiquidity } from '../../../../@types/apollo/PoolLiquidity'
import { useOcean } from '../../../../providers/Ocean'
import { useWeb3 } from '../../../../providers/Web3'
const contentQuery = graphql`
query PoolQuery {
@ -60,20 +61,14 @@ export default function Pool(): ReactElement {
const data = useStaticQuery(contentQuery)
const content = data.content.edges[0].node.childContentJson.pool
const { ocean, accountId, networkId } = useOcean()
const {
isInPurgatory,
ddo,
owner,
price,
refreshInterval,
refreshPrice
} = useAsset()
const { accountId, networkId } = useWeb3()
const { ocean } = useOcean()
const { isInPurgatory, ddo, owner, price, refreshInterval } = useAsset()
const dtSymbol = ddo?.dataTokenInfo.symbol
const [poolTokens, setPoolTokens] = useState<string>()
const [totalPoolTokens, setTotalPoolTokens] = useState<string>()
const [userLiquidity, setUserLiquidity] = useState<TokenBalance>()
const [userLiquidity, setUserLiquidity] = useState<PoolBalance>()
const [swapFee, setSwapFee] = useState<string>()
const [weightOcean, setWeightOcean] = useState<string>()
const [weightDt, setWeightDt] = useState<string>()
@ -91,7 +86,7 @@ export default function Pool(): ReactElement {
creatorTotalLiquidityInOcean,
setCreatorTotalLiquidityInOcean
] = useState(0)
const [creatorLiquidity, setCreatorLiquidity] = useState<TokenBalance>()
const [creatorLiquidity, setCreatorLiquidity] = useState<PoolBalance>()
const [creatorPoolTokens, setCreatorPoolTokens] = useState<string>()
const [creatorPoolShare, setCreatorPoolShare] = useState<string>()
@ -209,7 +204,9 @@ export default function Pool(): ReactElement {
const refreshInfo = async () => {
setRefreshPool(!refreshPool)
await refreshPrice()
// need some form of replacement or something.
// await refreshPrice()
}
return (
@ -244,18 +241,22 @@ export default function Pool(): ReactElement {
<PriceUnit price={`${price?.value}`} />
<Tooltip content={content.tooltips.price} />
<div className={styles.dataTokenLinks}>
<EtherscanLink
<ExplorerLink
networkId={networkId}
path={`address/${price?.address}`}
>
Pool
</EtherscanLink>
<EtherscanLink
</ExplorerLink>
<ExplorerLink
networkId={networkId}
path={`token/${ddo.dataToken}`}
path={
networkId === 137
? `tokens/${ddo.dataToken}`
: `token/${ddo.dataToken}`
}
>
Datatoken
</EtherscanLink>
</ExplorerLink>
</div>
</div>

View File

@ -1,5 +1,4 @@
import React, { ReactElement, useState } from 'react'
import { useOcean } from '@oceanprotocol/react'
import { BestPrice, DDO, Logger } from '@oceanprotocol/lib'
import * as Yup from 'yup'
import { Formik } from 'formik'
@ -8,11 +7,13 @@ import { graphql, useStaticQuery } from 'gatsby'
import { useUserPreferences } from '../../../../providers/UserPreferences'
import { toast } from 'react-toastify'
import Swap from './Swap'
import TokenBalance from '../../../../@types/TokenBalance'
import { PoolBalance } from '../../../../@types/TokenBalance'
import Alert from '../../../atoms/Alert'
import styles from './FormTrade.module.css'
import { FormTradeData, initialValues } from '../../../../models/FormTrade'
import Decimal from 'decimal.js'
import { useOcean } from '../../../../providers/Ocean'
import { useWeb3 } from '../../../../providers/Web3'
const contentQuery = graphql`
query TradeQuery {
@ -39,14 +40,15 @@ export default function FormTrade({
price
}: {
ddo: DDO
balance: TokenBalance
balance: PoolBalance
maxDt: number
maxOcean: number
price: BestPrice
}): ReactElement {
const data = useStaticQuery(contentQuery)
const content = data.content.edges[0].node.childContentJson.trade
const { ocean, accountId } = useOcean()
const { accountId } = useWeb3()
const { ocean } = useOcean()
const { debug } = useUserPreferences()
const [txId, setTxId] = useState<string>()

View File

@ -1,7 +1,7 @@
import { useOcean } from '@oceanprotocol/react'
import { FormikContextType, useFormikContext } from 'formik'
import React, { ReactElement, useEffect, useState } from 'react'
import { FormTradeData } from '../../../../models/FormTrade'
import { useOcean } from '../../../../providers/Ocean'
import Token from '../Pool/Token'
import styles from './Output.module.css'

View File

@ -1,15 +1,15 @@
import React, { ReactElement, useEffect, useState } from 'react'
import { useOcean } from '@oceanprotocol/react'
import { BestPrice, DDO } from '@oceanprotocol/lib'
import styles from './Swap.module.css'
import TradeInput from './TradeInput'
import Button from '../../../atoms/Button'
import { ReactComponent as Arrow } from '../../../../images/arrow.svg'
import { FormikContextType, useFormikContext } from 'formik'
import TokenBalance from '../../../../@types/TokenBalance'
import { PoolBalance } from '../../../../@types/TokenBalance'
import Output from './Output'
import Slippage from './Slippage'
import { FormTradeData, TradeItem } from '../../../../models/FormTrade'
import { useOcean } from '../../../../providers/Ocean'
export default function Swap({
ddo,
@ -23,7 +23,7 @@ export default function Swap({
ddo: DDO
maxDt: number
maxOcean: number
balance: TokenBalance
balance: PoolBalance
price: BestPrice
setMaximumDt: (value: number) => void
setMaximumOcean: (value: number) => void

View File

@ -1,18 +1,16 @@
import React, { ChangeEvent, ReactElement } from 'react'
import styles from './TradeInput.module.css'
import {
Field,
FieldInputProps,
FormikContextType,
useFormikContext
} from 'formik'
import { useOcean } from '@oceanprotocol/react'
import Input from '../../../atoms/Input'
import Button from '../../../atoms/Button'
import UserLiquidity from '../../../atoms/UserLiquidity'
import { FormTradeData, TradeItem } from '../../../../models/FormTrade'
import { useOcean } from '../../../../providers/Ocean'
export default function TradeInput({
name,

View File

@ -1,12 +1,14 @@
import React, { ReactElement, useEffect, useState } from 'react'
import { useOcean } from '@oceanprotocol/react'
import FormTrade from './FormTrade'
import TokenBalance from '../../../../@types/TokenBalance'
import { PoolBalance } from '../../../../@types/TokenBalance'
import { useAsset } from '../../../../providers/Asset'
import { useOcean } from '../../../../providers/Ocean'
import { useWeb3 } from '../../../../providers/Web3'
export default function Trade(): ReactElement {
const { ocean, balance, accountId } = useOcean()
const [tokenBalance, setTokenBalance] = useState<TokenBalance>()
const { accountId } = useWeb3()
const { ocean, balance } = useOcean()
const [tokenBalance, setTokenBalance] = useState<PoolBalance>()
const { price, ddo } = useAsset()
const [maxDt, setMaxDt] = useState(0)
const [maxOcean, setMaxOcean] = useState(0)

View File

@ -4,14 +4,16 @@ import Compute from './Compute'
import Consume from './Consume'
import { Logger } from '@oceanprotocol/lib'
import Tabs from '../../atoms/Tabs'
import { useOcean } from '@oceanprotocol/react'
import compareAsBN from '../../../utils/compareAsBN'
import Pool from './Pool'
import Trade from './Trade'
import { useAsset } from '../../../providers/Asset'
import { useOcean } from '../../../providers/Ocean'
import { useWeb3 } from '../../../providers/Web3'
export default function AssetActions(): ReactElement {
const { ocean, balance, accountId } = useOcean()
const { accountId } = useWeb3()
const { ocean, balance, account } = useOcean()
const { price, ddo, metadata } = useAsset()
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
@ -39,7 +41,7 @@ export default function AssetActions(): ReactElement {
// Check user balance against price
useEffect(() => {
if (!price?.value || !accountId || !balance?.ocean || !dtBalance) return
if (!price?.value || !account || !balance?.ocean || !dtBalance) return
setIsBalanceSufficient(
compareAsBN(balance.ocean, `${price.value}`) || Number(dtBalance) >= 1
@ -48,7 +50,7 @@ export default function AssetActions(): ReactElement {
return () => {
setIsBalanceSufficient(false)
}
}, [balance, accountId, price, dtBalance])
}, [balance, account, price, dtBalance])
const UseContent = isCompute ? (
<Compute

View File

@ -2,8 +2,8 @@ import { useUserPreferences } from '../../../providers/UserPreferences'
import React, { ReactElement } from 'react'
import styles from './Bookmark.module.css'
import { ReactComponent as BookmarkIcon } from '../../../images/bookmark.svg'
import { useOcean } from '@oceanprotocol/react'
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import { useOcean } from '../../../providers/Ocean'
export default function Bookmark({ did }: { did: string }): ReactElement {
const { config } = useOcean()

View File

@ -1,17 +1,15 @@
import { useOcean } from '@oceanprotocol/react'
import React, { ReactElement, useEffect, useState } from 'react'
import { useAsset } from '../../../providers/Asset'
import EtherscanLink from '../../atoms/EtherscanLink'
import ExplorerLink from '../../atoms/ExplorerLink'
import Time from '../../atoms/Time'
import styles from './EditHistory.module.css'
import { gql, useQuery } from '@apollo/client'
import { ReceiptData_datatokens_updates as ReceiptData } from '../../../@types/apollo/ReceiptData'
import { useWeb3 } from '../../../providers/Web3'
const getReceipts = gql`
query ReceiptData($address: ID!) {
datatokens(where: { id: $address }) {
createTime
tx
updates(orderBy: timestamp, orderDirection: desc) {
id
tx
@ -22,7 +20,7 @@ const getReceipts = gql`
`
export default function EditHistory(): ReactElement {
const { networkId } = useOcean()
const { networkId } = useWeb3()
const { ddo } = useAsset()
const { data } = useQuery(getReceipts, {
variables: { address: ddo?.dataToken.toLowerCase() }
@ -33,8 +31,15 @@ export default function EditHistory(): ReactElement {
useEffect(() => {
if (!data || data.datatokens.length === 0) return
setReceipts(data.datatokens[0].updates)
setCreationTx(data.datatokens[0].tx)
const receiptCollectionLength = data.datatokens[0].updates.length
const creationData = data.datatokens[0].updates[receiptCollectionLength - 1]
setCreationTx(creationData.tx)
const receiptCollection = [...data.datatokens[0].updates]
receiptCollection.splice(-1, 1)
setReceipts(receiptCollection)
}, [data])
return (
@ -43,16 +48,16 @@ export default function EditHistory(): ReactElement {
<ul className={styles.history}>
{receipts?.map((receipt) => (
<li key={receipt.id} className={styles.item}>
<EtherscanLink networkId={networkId} path={`/tx/${receipt.tx}`}>
<ExplorerLink networkId={networkId} path={`/tx/${receipt.tx}`}>
edited{' '}
<Time date={receipt.timestamp.toString()} relative isUnix />
</EtherscanLink>
</ExplorerLink>
</li>
))}
<li className={styles.item}>
<EtherscanLink networkId={networkId} path={`/tx/${creationTx}`}>
<ExplorerLink networkId={networkId} path={`/tx/${creationTx}`}>
published <Time date={ddo.created} relative />
</EtherscanLink>
</ExplorerLink>
</li>
</ul>
</>

View File

@ -1,5 +1,4 @@
import React, { ReactElement } from 'react'
import Time from '../../atoms/Time'
import MetaItem from './MetaItem'
import styles from './MetaFull.module.css'
import Publisher from '../../atoms/Publisher'

View File

@ -1,7 +1,7 @@
import { useOcean } from '@oceanprotocol/react'
import React, { ReactElement } from 'react'
import { useAsset } from '../../../providers/Asset'
import EtherscanLink from '../../atoms/EtherscanLink'
import { useWeb3 } from '../../../providers/Web3'
import ExplorerLink from '../../atoms/ExplorerLink'
import Publisher from '../../atoms/Publisher'
import Time from '../../atoms/Time'
import styles from './MetaMain.module.css'
@ -9,16 +9,23 @@ import AssetType from '../../atoms/AssetType'
export default function MetaMain(): ReactElement {
const { ddo, owner, type } = useAsset()
const { networkId } = useOcean()
const { networkId } = useWeb3()
const isCompute = Boolean(ddo?.findServiceByType('compute'))
const accessType = isCompute ? 'compute' : 'access'
return (
<aside className={styles.meta}>
<p>
<EtherscanLink networkId={networkId} path={`token/${ddo?.dataToken}`}>
<ExplorerLink
networkId={networkId}
path={
networkId === 137
? `tokens/${ddo?.dataToken}`
: `token/${ddo?.dataToken}`
}
>
{`${ddo?.dataTokenInfo.name}${ddo?.dataTokenInfo.symbol}`}
</EtherscanLink>
</ExplorerLink>
</p>
<div>
Published By <Publisher account={owner} />

View File

@ -1,10 +1,10 @@
import React, { ChangeEvent, ReactElement } from 'react'
import React, { ReactElement } from 'react'
import stylesIndex from './index.module.css'
import styles from './Coin.module.css'
import InputElement from '../../../../atoms/Input/InputElement'
import { ReactComponent as Logo } from '../../../../../images/logo.svg'
import Conversion from '../../../../atoms/Price/Conversion'
import { DataTokenOptions } from '@oceanprotocol/react'
import { DataTokenOptions } from '../../../../../hooks/usePublish'
import { useField } from 'formik'
import Error from './Error'

View File

@ -1,4 +1,3 @@
import { useOcean, usePricing } from '@oceanprotocol/react'
import PriceUnit from '../../../../atoms/Price/PriceUnit'
import React, { ReactElement, useEffect, useState } from 'react'
import Alert from '../../../../atoms/Alert'
@ -14,8 +13,8 @@ import { PriceOptionsMarket } from '../../../../../@types/MetaData'
import { DDO } from '@oceanprotocol/lib'
import Price from './Price'
import Decimal from 'decimal.js'
const refreshInterval = 10000 // 10 sec.
import { useOcean } from '../../../../../providers/Ocean'
import { useWeb3 } from '../../../../../providers/Web3'
export default function Dynamic({
ddo,
@ -24,7 +23,8 @@ export default function Dynamic({
ddo: DDO
content: any
}): ReactElement {
const { account, balance, networkId, refreshBalance } = useOcean()
const { networkId } = useWeb3()
const { account, balance } = useOcean()
const [firstPrice, setFirstPrice] = useState<string>()
// Connect with form
@ -69,18 +69,6 @@ export default function Dynamic({
}
}, [price, networkId, account, balance])
// refetch balance periodically
useEffect(() => {
if (!account) return
refreshBalance()
const balanceInterval = setInterval(() => refreshBalance(), refreshInterval)
return () => {
clearInterval(balanceInterval)
}
}, [networkId, account])
return (
<div className={styles.dynamic}>
<FormHelp className={stylesIndex.help}>{content.info}</FormHelp>

View File

@ -1,4 +1,3 @@
import { usePricing } from '@oceanprotocol/react'
import Conversion from '../../../../atoms/Price/Conversion'
import { useField } from 'formik'
import React, { ReactElement } from 'react'
@ -7,6 +6,7 @@ import styles from './Price.module.css'
import Error from './Error'
import { DDO } from '@oceanprotocol/lib'
import PriceUnit from '../../../../atoms/Price/PriceUnit'
import usePricing from '../../../../../hooks/usePricing'
export default function Price({
ddo,

View File

@ -2,7 +2,6 @@ import React, { FormEvent, ReactElement, useState } from 'react'
import { Formik } from 'formik'
import { initialValues, validationSchema } from '../../../../models/FormPricing'
import { DDO, Logger } from '@oceanprotocol/lib'
import { usePricing } from '@oceanprotocol/react'
import { PriceOptionsMarket } from '../../../../@types/MetaData'
import Alert from '../../../atoms/Alert'
import styles from './index.module.css'
@ -10,6 +9,7 @@ import FormPricing from './FormPricing'
import { toast } from 'react-toastify'
import Feedback from './Feedback'
import { graphql, useStaticQuery } from 'gatsby'
import { usePricing } from '../../../../hooks/usePricing'
const query = graphql`
query PricingQuery {

View File

@ -7,7 +7,6 @@ import styles from './index.module.css'
import AssetActions from '../AssetActions'
import { useUserPreferences } from '../../../providers/UserPreferences'
import Pricing from './Pricing'
import { useOcean } from '@oceanprotocol/react'
import Bookmark from './Bookmark'
import { useAsset } from '../../../providers/Asset'
import Alert from '../../atoms/Alert'
@ -16,6 +15,7 @@ import Edit from '../AssetActions/Edit'
import DebugOutput from '../../atoms/DebugOutput'
import MetaMain from './MetaMain'
import EditHistory from './EditHistory'
import { useWeb3 } from '../../../providers/Web3'
export interface AssetContentProps {
path?: string
@ -42,7 +42,7 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
const data = useStaticQuery(contentQuery)
const content = data.purgatory.edges[0].node.childContentJson.asset
const { debug } = useUserPreferences()
const { accountId } = useOcean()
const { accountId } = useWeb3()
const { owner, isInPurgatory, purgatoryData } = useAsset()
const [showPricing, setShowPricing] = useState(false)
const [showEdit, setShowEdit] = useState<boolean>()

View File

@ -1,5 +1,4 @@
import { Logger } from '@oceanprotocol/lib'
import { useOcean } from '@oceanprotocol/react'
import React, { ReactElement, useEffect, useState } from 'react'
import Loader from '../../atoms/Loader'
import Modal from '../../atoms/Modal'
@ -9,6 +8,7 @@ import shortid from 'shortid'
import styles from './ComputeDetails.module.css'
import { Status } from './ComputeJobs'
import { ListItem } from '../../atoms/Lists'
import { useOcean } from '../../../providers/Ocean'
export default function ComputeDetailsModal({
computeJob,
@ -19,7 +19,7 @@ export default function ComputeDetailsModal({
isOpen: boolean
onToggleModal: () => void
}): ReactElement {
const { ocean, status, account } = useOcean()
const { ocean, account } = useOcean()
const [isLoading, setIsLoading] = useState(false)
const isFinished = computeJob.dateFinished !== null
@ -48,7 +48,7 @@ export default function ComputeDetailsModal({
}
}
getDetails()
}, [ocean, status, account, isOpen, computeJob, isFinished])
}, [ocean, account, isOpen, computeJob, isFinished])
return (
<Modal

View File

@ -1,4 +1,3 @@
import { useOcean } from '@oceanprotocol/react'
import React, { ReactElement, useEffect, useState } from 'react'
import Time from '../../atoms/Time'
import styles from './ComputeJobs.module.css'
@ -9,6 +8,7 @@ import { Link } from 'gatsby'
import { Logger } from '@oceanprotocol/lib'
import Dotdotdot from 'react-dotdotdot'
import Table from '../../atoms/Table'
import { useOcean } from '../../../providers/Ocean'
function DetailsButton({ row }: { row: ComputeJobMetaData }): ReactElement {
const [isDialogOpen, setIsDialogOpen] = useState(false)

View File

@ -1,11 +1,11 @@
import React, { ReactElement, useEffect, useState } from 'react'
import { useOcean } from '@oceanprotocol/react'
import Table from '../../atoms/Table'
import { gql, useQuery } from '@apollo/client'
import Time from '../../atoms/Time'
import { OrdersData_tokenOrders as OrdersDataTokenOrders } from '../../../@types/apollo/OrdersData'
import web3 from 'web3'
import AssetTitle from '../../molecules/AssetListTitle'
import { useWeb3 } from '../../../providers/Web3'
const getTokenOrders = gql`
query OrdersData($user: String!) {
@ -50,7 +50,7 @@ const columns = [
]
export default function ComputeDownloads(): ReactElement {
const { accountId } = useOcean()
const { accountId } = useWeb3()
const [orders, setOrders] = useState<OrdersDataTokenOrders[]>()
const { data } = useQuery(getTokenOrders, {
variables: { user: accountId?.toLowerCase() }

View File

@ -1,4 +1,3 @@
import { useOcean } from '@oceanprotocol/react'
import React, { ReactElement, useEffect, useState } from 'react'
import Table from '../../atoms/Table'
import Conversion from '../../atoms/Price/Conversion'
@ -12,6 +11,7 @@ import {
} from '../../../@types/apollo/PoolShares'
import web3 from 'web3'
import Token from '../../organisms/AssetActions/Pool/Token'
import { useWeb3 } from '../../../providers/Web3'
const poolSharesQuery = gql`
query PoolShares($user: String) {
@ -139,7 +139,7 @@ const columns = [
]
export default function PoolShares(): ReactElement {
const { accountId } = useOcean()
const { accountId } = useWeb3()
const [assets, setAssets] = useState<Asset[]>()
const { data, loading } = useQuery<PoolSharesList>(poolSharesQuery, {
variables: {

View File

@ -1,20 +1,23 @@
import { Logger } from '@oceanprotocol/lib'
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
import { useOcean } from '@oceanprotocol/react'
import React, { ReactElement, useEffect, useState } from 'react'
import Loader from '../../atoms/Loader'
import AssetList from '../../organisms/AssetList'
import axios from 'axios'
import { queryMetadata } from '../../../utils/aquarius'
import { useWeb3 } from '../../../providers/Web3'
import { useOcean } from '../../../providers/Ocean'
export default function PublishedList(): ReactElement {
const { accountId } = useOcean()
const { accountId } = useWeb3()
const { config } = useOcean()
const [queryResult, setQueryResult] = useState<QueryResult>()
const [isLoading, setIsLoading] = useState(false)
const { config } = useOcean()
const source = axios.CancelToken.source()
const [page, setPage] = useState<number>(1)
const source = axios.CancelToken.source()
useEffect(() => {
async function getPublished() {
if (!accountId) return

View File

@ -5,7 +5,7 @@ import AssetList from '../organisms/AssetList'
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
import Container from '../atoms/Container'
import Loader from '../atoms/Loader'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../providers/Ocean'
import Button from '../atoms/Button'
import Bookmarks from '../molecules/Bookmarks'
import axios from 'axios'

View File

@ -7,7 +7,7 @@ import React, {
} from 'react'
import { useStaticQuery, graphql } from 'gatsby'
import styles from './FormPublish.module.css'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../../providers/Ocean'
import { useFormikContext, Field, Form, FormikContextType } from 'formik'
import Input from '../../atoms/Input'
import Button from '../../atoms/Button'

View File

@ -1,14 +1,14 @@
import React, { ReactElement, useEffect, FormEvent, ChangeEvent } from 'react'
import { useStaticQuery, graphql } from 'gatsby'
import styles from './FormPublish.module.css'
import { useOcean } from '@oceanprotocol/react'
import { useFormikContext, Field, Form, FormikContextType } from 'formik'
import Input from '../../atoms/Input'
import Button from '../../atoms/Button'
import { FormContent, FormFieldProps } from '../../../@types/Form'
import { MetadataPublishFormDataset } from '../../../@types/MetaData'
import { initialValues as initialValuesDataset } from '../../../models/FormAlgoPublish'
import { useOcean } from '../../../providers/Ocean'
import stylesIndex from './index.module.css'
import styles from './FormPublish.module.css'
const query = graphql`
query {

View File

@ -1,6 +1,6 @@
import React, { ReactElement, useState, useEffect } from 'react'
import { Formik, FormikState } from 'formik'
import { usePublish, useOcean } from '@oceanprotocol/react'
import { usePublish } from '../../../hooks/usePublish'
import styles from './index.module.css'
import FormPublish from './FormPublish'
import FormAlgoPublish from './FormAlgoPublish'
@ -31,6 +31,9 @@ import { Persist } from '../../atoms/FormikPersist'
import Debug from './Debug'
import Alert from '../../atoms/Alert'
import MetadataFeedback from '../../molecules/MetadataFeedback'
import { useAccountPurgatory } from '../../../hooks/useAccountPurgatory'
import { useWeb3 } from '../../../providers/Web3'
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
const formNameDatasets = 'ocean-publish-form-datasets'
const formNameAlgorithms = 'ocean-publish-form-algorithms'
@ -66,9 +69,11 @@ export default function PublishPage({
}: {
content: { warning: string }
}): ReactElement {
const { warningPolygonPublish } = useSiteMetadata()
const { debug } = useUserPreferences()
const { accountId, networkId } = useWeb3()
const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId)
const { publish, publishError, isLoading, publishStepText } = usePublish()
const { isInPurgatory, purgatoryData } = useOcean()
const [success, setSuccess] = useState<string>()
const [error, setError] = useState<string>()
const [title, setTitle] = useState<string>()

View File

@ -10,7 +10,7 @@ import { getResults } from './utils'
import { navigate } from 'gatsby'
import { updateQueryStringParameter } from '../../../utils'
import Loader from '../../atoms/Loader'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from '../../../providers/Ocean'
export default function SearchPage({
location,

View File

@ -1,101 +0,0 @@
import React, { ReactElement, useEffect } from 'react'
import { useOcean } from '@oceanprotocol/react'
import { getOceanConfig } from './wrapRootElement'
import { Logger } from '@oceanprotocol/lib'
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import contractAddresses from '@oceanprotocol/contracts/artifacts/address.json'
const refreshInterval = 5000 // 5 sec.
export function getDevelopmentConfig(): Partial<ConfigHelperConfig> {
return {
factoryAddress: contractAddresses.development?.DTFactory,
poolFactoryAddress: contractAddresses.development?.BFactory,
fixedRateExchangeAddress: contractAddresses.development?.FixedRateExchange,
metadataContractAddress: contractAddresses.development?.Metadata,
oceanTokenAddress: contractAddresses.development?.Ocean,
// There is no subgraph in barge so we hardcode the Rinkeby one for now
subgraphUri: 'https://subgraph.rinkeby.oceanprotocol.com'
}
}
export function NetworkMonitor(): ReactElement {
const {
connect,
web3Provider,
web3,
networkId,
config,
refreshBalance,
account
} = useOcean()
async function handleNetworkChanged(chainId: string | number) {
const initialNewConfig = getOceanConfig(
typeof chainId === 'string' ? Number(chainId.replace('0x', '')) : chainId
)
const newConfig = {
...initialNewConfig,
// add local dev values
...(chainId === '8996' && {
...getDevelopmentConfig()
})
}
try {
await connect(newConfig)
} catch (error) {
Logger.error(error.message)
}
}
// Periodically refresh wallet balance
useEffect(() => {
if (!account) return
refreshBalance()
const balanceInterval = setInterval(() => refreshBalance(), refreshInterval)
return () => {
clearInterval(balanceInterval)
}
}, [networkId, account])
// Re-connect on mount when network is different from user network.
// Bit nasty to just overwrite the initialConfig passed to OceanProvider
// while it's connecting to that, but YOLO.
useEffect(() => {
if (!web3 || !networkId) return
async function init() {
const chainIdWeb3 = await web3.eth.getChainId()
const chainIdConfig = (config as ConfigHelperConfig).networkId
// HEADS UP! MetaMask built-in `Localhost 8545` network selection
// will have `1337` as chainId but we use `8996` in our config
if (
chainIdWeb3 === chainIdConfig ||
(chainIdWeb3 === 1337 && chainIdConfig === 8996)
)
return
await handleNetworkChanged(networkId)
}
init()
}, [web3, networkId])
// Handle network change events
useEffect(() => {
if (!web3Provider) return
web3Provider.on('chainChanged', handleNetworkChanged)
return () => {
web3Provider.removeListener('chainChanged', handleNetworkChanged)
}
}, [web3Provider])
return <></>
}

View File

@ -1,25 +1,11 @@
import React, { ReactElement } from 'react'
import { OceanProvider } from '@oceanprotocol/react'
import { ConfigHelper, Config } from '@oceanprotocol/lib'
import { web3ModalOpts } from '../utils/wallet'
import { getDevelopmentConfig, NetworkMonitor } from './NetworkMonitor'
import Web3Provider from '../providers/Web3'
import appConfig from '../../app.config'
import {
ConfigHelperNetworkName,
ConfigHelperNetworkId
} from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import { UserPreferencesProvider } from '../providers/UserPreferences'
import PricesProvider from '../providers/Prices'
import ApolloClientProvider from '../providers/ApolloClientProvider'
export function getOceanConfig(
network: ConfigHelperNetworkName | ConfigHelperNetworkId
): Config {
return new ConfigHelper().getConfig(
network,
process.env.GATSBY_INFURA_PROJECT_ID
)
}
import OceanProvider from '../providers/Ocean'
import { getDevelopmentConfig, getOceanConfig } from '../utils/ocean'
export default function wrapRootElement({
element
@ -37,16 +23,14 @@ export default function wrapRootElement({
}
return (
<OceanProvider
initialConfig={oceanInitialConfig}
web3ModalOpts={web3ModalOpts}
>
<ApolloClientProvider>
<UserPreferencesProvider>
<NetworkMonitor />
<PricesProvider>{element}</PricesProvider>
</UserPreferencesProvider>
</ApolloClientProvider>
</OceanProvider>
<Web3Provider>
<OceanProvider initialConfig={oceanInitialConfig}>
<ApolloClientProvider>
<UserPreferencesProvider>
<PricesProvider>{element}</PricesProvider>
</UserPreferencesProvider>
</ApolloClientProvider>
</OceanProvider>
</Web3Provider>
)
}

View File

@ -0,0 +1,51 @@
import { useCallback, useEffect, useState } from 'react'
import { Logger } from '@oceanprotocol/lib'
import {
PurgatoryDataAccount,
getAccountPurgatoryData
} from '../utils/purgatory'
interface UseAccountPurgatory {
isInPurgatory: boolean
purgatoryData: PurgatoryDataAccount
isLoading: boolean
}
function useAccountPurgatory(accountId: string): UseAccountPurgatory {
const [isInPurgatory, setIsInPurgatory] = useState(false)
const [purgatoryData, setPurgatoryData] = useState<PurgatoryDataAccount>()
const [isLoading, setIsLoading] = useState(false)
const setAccountPurgatory = useCallback(
async (address: string): Promise<void> => {
if (!address) return
try {
setIsLoading(true)
const result = await getAccountPurgatoryData(address)
const isInPurgatory = result?.address !== undefined
setIsInPurgatory(isInPurgatory)
isInPurgatory && setPurgatoryData(result)
} catch (error) {
Logger.error(error)
} finally {
setIsLoading(false)
}
},
[]
)
useEffect(() => {
if (!accountId) return
setAccountPurgatory(accountId)
}, [accountId, setAccountPurgatory])
return {
isInPurgatory,
purgatoryData,
isLoading
}
}
export { useAccountPurgatory, UseAccountPurgatory }
export default useAccountPurgatory

156
src/hooks/useCompute.ts Normal file
View File

@ -0,0 +1,156 @@
import { useState } from 'react'
import { Logger, ServiceCompute } from '@oceanprotocol/lib'
import { MetadataAlgorithm } from '@oceanprotocol/lib/dist/node/ddo/interfaces/MetadataAlgorithm'
import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/ComputeJob'
import { computeFeedback } from '../utils/feedback'
import { useOcean } from '../providers/Ocean'
import { useWeb3 } from '../providers/Web3'
interface ComputeValue {
entrypoint: string
image: string
tag: string
}
interface ComputeOption {
name: string
value: ComputeValue
}
const computeOptions: ComputeOption[] = [
{
name: 'nodejs',
value: {
entrypoint: 'node $ALGO',
image: 'node',
tag: '10'
}
},
{
name: 'python3.7',
value: {
entrypoint: 'python $ALGO',
image: 'oceanprotocol/algo_dockers',
tag: 'python-panda'
}
}
]
interface UseCompute {
compute: (
did: string,
computeService: ServiceCompute,
dataTokenAddress: string,
algorithmRawCode: string,
computeContainer: ComputeValue,
marketFeeAddress?: string,
orderId?: string
) => Promise<ComputeJob | void>
computeStep?: number
computeStepText?: string
computeError?: string
isLoading: boolean
}
const rawAlgorithmMeta: MetadataAlgorithm = {
rawcode: `console.log('Hello world'!)`,
format: 'docker-image',
version: '0.1',
container: {
entrypoint: '',
image: '',
tag: ''
}
}
function useCompute(): UseCompute {
const { accountId } = useWeb3()
const { ocean, account } = useOcean()
const [computeStep, setComputeStep] = useState<number | undefined>()
const [computeStepText, setComputeStepText] = useState<string | undefined>()
const [computeError, setComputeError] = useState<string | undefined>()
const [isLoading, setIsLoading] = useState(false)
function setStep(index?: number) {
if (!index) {
setComputeStep(undefined)
setComputeStepText(undefined)
return
}
setComputeStep(index)
setComputeStepText(computeFeedback[index])
}
async function compute(
did: string,
computeService: ServiceCompute,
dataTokenAddress: string,
algorithmRawCode: string,
computeContainer: ComputeValue,
marketFeeAddress?: string,
orderId?: string
): Promise<ComputeJob | void> {
if (!ocean || !account) return
setComputeError(undefined)
try {
setIsLoading(true)
setStep(0)
rawAlgorithmMeta.container = computeContainer
rawAlgorithmMeta.rawcode = algorithmRawCode
const output = {}
if (!orderId) {
const userOwnedTokens = await ocean.accounts.getTokenBalance(
dataTokenAddress,
account
)
if (parseFloat(userOwnedTokens) < 1) {
setComputeError('Not enough datatokens')
} else {
Logger.log(
'compute order',
accountId,
did,
computeService,
rawAlgorithmMeta,
marketFeeAddress
)
orderId = await ocean.compute.orderAsset(
accountId,
did,
computeService.index,
undefined,
rawAlgorithmMeta,
marketFeeAddress
)
setStep(1)
}
}
setStep(2)
if (orderId) {
const response = await ocean.compute.start(
did,
orderId,
dataTokenAddress,
account,
undefined,
rawAlgorithmMeta,
output,
`${computeService.index}`,
computeService.type
)
return response
}
} catch (error) {
Logger.error(error)
setComputeError(error.message)
} finally {
setStep(undefined)
setIsLoading(false)
}
}
return { compute, computeStep, computeStepText, computeError, isLoading }
}
export { useCompute, UseCompute, ComputeValue, ComputeOption, computeOptions }
export default UseCompute

93
src/hooks/useConsume.ts Normal file
View File

@ -0,0 +1,93 @@
import { useState } from 'react'
import { consumeFeedback } from '../utils/feedback'
import { DID, Logger, ServiceType } from '@oceanprotocol/lib'
import { useOcean } from '../providers/Ocean'
import { useWeb3 } from '../providers/Web3'
interface UseConsume {
consume: (
did: DID | string,
dataTokenAddress: string,
serviceType: ServiceType,
marketFeeAddress: string,
orderId?: string
) => Promise<void>
consumeStep?: number
consumeStepText?: string
consumeError?: string
isLoading: boolean
}
function useConsume(): UseConsume {
const { accountId } = useWeb3()
const { ocean, account } = useOcean()
const [isLoading, setIsLoading] = useState(false)
const [consumeStep, setConsumeStep] = useState<number | undefined>()
const [consumeStepText, setConsumeStepText] = useState<string | undefined>()
const [consumeError, setConsumeError] = useState<string | undefined>()
function setStep(index: number) {
setConsumeStep(index)
setConsumeStepText(consumeFeedback[index])
}
async function consume(
did: DID | string,
dataTokenAddress: string,
serviceType: ServiceType = 'access',
marketFeeAddress: string,
orderId?: string
): Promise<void> {
if (!ocean || !account || !accountId) return
setIsLoading(true)
setConsumeError(undefined)
try {
setStep(0)
if (!orderId) {
// if we don't have a previous valid order, get one
const userOwnedTokens = await ocean.accounts.getTokenBalance(
dataTokenAddress,
account
)
if (parseFloat(userOwnedTokens) < 1) {
setConsumeError('Not enough datatokens')
} else {
setStep(1)
orderId = await ocean.assets.order(
did as string,
serviceType,
accountId,
undefined,
marketFeeAddress
)
Logger.log('order created', orderId)
setStep(2)
}
}
setStep(3)
if (orderId)
await ocean.assets.download(
did as string,
orderId,
dataTokenAddress,
account,
''
)
setStep(4)
} catch (error) {
setConsumeError(error.message)
Logger.error(error)
} finally {
setConsumeStep(undefined)
setConsumeStepText(undefined)
setIsLoading(false)
}
}
return { consume, consumeStep, consumeStepText, consumeError, isLoading }
}
export { useConsume, UseConsume }
export default useConsume

299
src/hooks/usePricing.ts Normal file
View File

@ -0,0 +1,299 @@
import { DDO, Logger, BestPrice } from '@oceanprotocol/lib'
import { useEffect, useState } from 'react'
import { TransactionReceipt } from 'web3-core'
import { Decimal } from 'decimal.js'
import { getFirstPoolPrice } from '../utils/dtUtils'
import {
getCreatePricingPoolFeedback,
getCreatePricingExchangeFeedback,
getBuyDTFeedback,
getSellDTFeedback
} from '../utils/feedback'
import { sleep } from '../utils'
import { useOcean } from '../providers/Ocean'
import { useWeb3 } from '../providers/Web3'
interface PriceOptions {
price: number
dtAmount: number
oceanAmount: number
type: 'fixed' | 'dynamic' | string
weightOnDataToken: string
swapFee: string
}
interface UsePricing {
dtSymbol?: string
dtName?: string
createPricing: (
priceOptions: PriceOptions
) => Promise<TransactionReceipt | string | void>
sellDT: (dtAmount: number | string) => Promise<TransactionReceipt | void>
mint: (tokensToMint: string) => Promise<TransactionReceipt | void>
buyDT: (
dtAmount: number | string,
price: BestPrice
) => Promise<TransactionReceipt | void>
pricingStep?: number
pricingStepText?: string
pricingError?: string
pricingIsLoading: boolean
}
function usePricing(ddo: DDO): UsePricing {
const { accountId } = useWeb3()
const { ocean, config } = useOcean()
const [pricingIsLoading, setPricingIsLoading] = useState(false)
const [pricingStep, setPricingStep] = useState<number>()
const [pricingStepText, setPricingStepText] = useState<string>()
const [pricingError, setPricingError] = useState<string>()
const [dtSymbol, setDtSymbol] = useState<string>()
const [dtName, setDtName] = useState<string>()
const { dataToken, dataTokenInfo } = ddo
// Get Datatoken info, from DDO first, then from chain
useEffect(() => {
if (!dataToken) return
async function init() {
const dtSymbol = dataTokenInfo
? dataTokenInfo.symbol
: await ocean?.datatokens.getSymbol(dataToken)
setDtSymbol(dtSymbol)
const dtName = dataTokenInfo
? dataTokenInfo.name
: await ocean?.datatokens.getName(dataToken)
setDtName(dtName)
}
init()
}, [ocean, dataToken, dataTokenInfo])
// Helper for setting steps & feedback for all flows
function setStep(index: number, type: 'pool' | 'exchange' | 'buy' | 'sell') {
setPricingStep(index)
if (!dtSymbol) return
let messages
switch (type) {
case 'pool':
messages = getCreatePricingPoolFeedback(dtSymbol)
break
case 'exchange':
messages = getCreatePricingExchangeFeedback(dtSymbol)
break
case 'buy':
messages = getBuyDTFeedback(dtSymbol)
break
case 'sell':
messages = getSellDTFeedback(dtSymbol)
break
}
setPricingStepText(messages[index])
}
async function mint(
tokensToMint: string
): Promise<TransactionReceipt | void> {
Logger.log('mint function', dataToken, accountId)
const balance = new Decimal(
await ocean.datatokens.balance(dataToken, accountId)
)
const tokens = new Decimal(tokensToMint)
if (tokens.greaterThan(balance)) {
const mintAmount = tokens.minus(balance)
const tx = await ocean.datatokens.mint(
dataToken,
accountId,
mintAmount.toString()
)
return tx
}
}
async function buyDT(
dtAmount: number | string,
price: BestPrice
): Promise<TransactionReceipt | void> {
if (!ocean || !accountId) return
let tx
try {
setPricingIsLoading(true)
setPricingError(undefined)
setStep(1, 'buy')
Logger.log('Price found for buying', price)
switch (price?.type) {
case 'pool': {
const oceanAmmount = new Decimal(price.value).times(1.05).toString()
const maxPrice = new Decimal(price.value).times(2).toString()
setStep(2, 'buy')
Logger.log('Buying token from pool', price, accountId, price)
tx = await ocean.pool.buyDT(
accountId,
price.address,
String(dtAmount),
oceanAmmount,
maxPrice
)
setStep(3, 'buy')
Logger.log('DT buy response', tx)
break
}
case 'exchange': {
if (!config.oceanTokenAddress) {
Logger.error(`'oceanTokenAddress' not set in config`)
return
}
if (!config.fixedRateExchangeAddress) {
Logger.error(`'fixedRateExchangeAddress' not set in config`)
return
}
Logger.log('Buying token from exchange', price, accountId)
await ocean.datatokens.approve(
config.oceanTokenAddress,
config.fixedRateExchangeAddress,
`${price.value}`,
accountId
)
setStep(2, 'buy')
tx = await ocean.fixedRateExchange.buyDT(
price.address,
`${dtAmount}`,
accountId
)
setStep(3, 'buy')
Logger.log('DT exchange buy response', tx)
break
}
}
} catch (error) {
setPricingError(error.message)
Logger.error(error)
} finally {
setStep(0, 'buy')
setPricingStepText(undefined)
setPricingIsLoading(false)
}
return tx
}
async function sellDT(
dtAmount: number | string
): Promise<TransactionReceipt | void> {
if (!ocean || !accountId) return
if (!config.oceanTokenAddress) {
Logger.error(`'oceanTokenAddress' not set in config`)
return
}
try {
setPricingIsLoading(true)
setPricingError(undefined)
setStep(1, 'sell')
const pool = await getFirstPoolPrice(ocean, dataToken)
if (!pool || pool.value === 0) return
const price = new Decimal(pool.value).times(0.95).toString()
setStep(2, 'sell')
Logger.log('Selling token to pool', pool, accountId, price)
const tx = await ocean.pool.sellDT(
accountId,
pool.address,
`${dtAmount}`,
price
)
setStep(3, 'sell')
Logger.log('DT sell response', tx)
return tx
} catch (error) {
setPricingError(error.message)
Logger.error(error)
} finally {
setStep(0, 'sell')
setPricingStepText(undefined)
setPricingIsLoading(false)
}
}
async function createPricing(
priceOptions: PriceOptions
): Promise<TransactionReceipt | void> {
if (!ocean || !accountId || !dtSymbol) return
const {
type,
oceanAmount,
price,
weightOnDataToken,
swapFee
} = priceOptions
let { dtAmount } = priceOptions
const isPool = type === 'dynamic'
if (!isPool && !config.fixedRateExchangeAddress) {
Logger.error(`'fixedRateExchangeAddress' not set in config.`)
return
}
setPricingIsLoading(true)
setPricingError(undefined)
setStep(99, 'pool')
try {
// if fixedPrice set dt to max amount
if (!isPool) dtAmount = 1000
await mint(`${dtAmount}`)
// dtAmount for fixed price is set to max
const tx = isPool
? await ocean.pool
.create(
accountId,
dataToken,
`${dtAmount}`,
weightOnDataToken,
`${oceanAmount}`,
swapFee
)
.next((step: number) => setStep(step, 'pool'))
: await ocean.fixedRateExchange
.create(dataToken, `${price}`, accountId, `${dtAmount}`)
.next((step: number) => setStep(step, 'exchange'))
await sleep(20000)
return tx
} catch (error) {
setPricingError(error.message)
Logger.error(error)
} finally {
setPricingStep(0)
setPricingStepText(undefined)
setPricingIsLoading(false)
}
}
return {
dtSymbol,
dtName,
createPricing,
buyDT,
sellDT,
mint,
pricingStep,
pricingStepText,
pricingIsLoading,
pricingError
}
}
export { usePricing, UsePricing, PriceOptions }
export default usePricing

171
src/hooks/usePublish.ts Normal file
View File

@ -0,0 +1,171 @@
import { DDO, Logger, Metadata } from '@oceanprotocol/lib'
import {
Service,
ServiceComputePrivacy,
ServiceType
} from '@oceanprotocol/lib/dist/node/ddo/interfaces/Service'
import { useState } from 'react'
import { sleep } from '../utils'
import { publishFeedback } from '../utils/feedback'
import { useOcean } from '../providers/Ocean'
interface DataTokenOptions {
cap?: string
name?: string
symbol?: string
}
interface UsePublish {
publish: (
asset: Metadata,
serviceType: ServiceType,
dataTokenOptions?: DataTokenOptions,
timeout?: number,
providerUri?: string
) => Promise<DDO | undefined | null>
publishStep?: number
publishStepText?: string
publishError?: string
isLoading: boolean
}
function usePublish(): UsePublish {
const { ocean, account } = useOcean()
const [isLoading, setIsLoading] = useState(false)
const [publishStep, setPublishStep] = useState<number | undefined>()
const [publishStepText, setPublishStepText] = useState<string | undefined>()
const [publishError, setPublishError] = useState<string | undefined>()
function setStep(index?: number) {
setPublishStep(index)
index && setPublishStepText(publishFeedback[index])
}
/**
* Publish an asset. It also creates the datatoken, mints tokens and gives the market allowance
* @param {Metadata} asset The metadata of the asset.
* @param {PriceOptions} priceOptions : number of tokens to mint, datatoken weight , liquidity fee, type : fixed, dynamic
* @param {ServiceType} serviceType Desired service type of the asset access or compute
* @param {DataTokenOptions} dataTokenOptions custom name, symbol and cap for datatoken
* @return {Promise<DDO>} Returns the newly published ddo
*/
async function publish(
asset: Metadata,
serviceType: ServiceType,
dataTokenOptions?: DataTokenOptions,
timeout?: number,
providerUri?: string
): Promise<DDO | undefined | null> {
if (!ocean || !account) return null
setIsLoading(true)
setPublishError(undefined)
setStep(0)
try {
const publishedDate =
new Date(Date.now()).toISOString().split('.')[0] + 'Z'
const services: Service[] = []
const price = '1'
switch (serviceType) {
case 'access': {
if (!timeout) timeout = 0
const accessService = await ocean.assets.createAccessServiceAttributes(
account,
price,
publishedDate,
timeout,
providerUri
)
Logger.log('access service created', accessService)
services.push(accessService)
break
}
case 'compute': {
if (!timeout) timeout = 3600
const cluster = ocean.compute.createClusterAttributes(
'Kubernetes',
'http://10.0.0.17/xxx'
)
const servers = [
ocean.compute.createServerAttributes(
'1',
'xlsize',
'50',
'16',
'0',
'128gb',
'160gb',
timeout
)
]
const containers = [
ocean.compute.createContainerAttributes(
'tensorflow/tensorflow',
'latest',
'sha256:cb57ecfa6ebbefd8ffc7f75c0f00e57a7fa739578a429b6f72a0df19315deadc'
)
]
const provider = ocean.compute.createProviderAttributes(
'Azure',
'Compute service with 16gb ram for each node.',
cluster,
containers,
servers
)
const origComputePrivacy: ServiceComputePrivacy = {
allowRawAlgorithm: true,
allowNetworkAccess: false,
publisherTrustedAlgorithms: []
}
const computeService = ocean.compute.createComputeService(
account,
price,
publishedDate,
provider,
origComputePrivacy,
timeout,
providerUri
)
services.push(computeService)
break
}
}
Logger.log('services created', services)
const ddo = await ocean.assets
.create(
asset,
account,
services,
undefined,
dataTokenOptions?.cap,
dataTokenOptions?.name,
dataTokenOptions?.symbol,
providerUri
)
.next(setStep)
Logger.log('ddo created', ddo)
await sleep(20000)
setStep(7)
return ddo
} catch (error) {
setPublishError(error.message)
Logger.error(error)
setStep()
} finally {
setIsLoading(false)
}
}
return {
publish,
publishStep,
publishStepText,
isLoading,
publishError
}
}
export { usePublish, UsePublish, DataTokenOptions }
export default usePublish

View File

@ -14,6 +14,8 @@ const query = graphql`
link
}
warning
warningPolygon
warningPolygonPublish
appConfig {
infuraProjectId
network

View File

@ -1,6 +1,6 @@
import TokenBalance from '../@types/TokenBalance'
import { PoolBalance } from '../@types/TokenBalance'
export interface FormTradeData extends TokenBalance {
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'
slippage: string

View File

@ -3,7 +3,7 @@ import PageSearch from '../components/templates/Search'
import { PageProps } from 'gatsby'
import Page from '../components/templates/Page'
import queryString from 'query-string'
import { accountTruncate } from '../utils/wallet'
import { accountTruncate } from '../utils/web3'
import ethereumAddress from 'ethereum-address'
export default function PageGatsbySearch(props: PageProps): ReactElement {

View File

@ -7,7 +7,7 @@ import {
} from '@apollo/client'
import { Logger } from '@oceanprotocol/lib'
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from './Ocean'
import fetch from 'cross-fetch'
import React, { useState, useEffect, ReactNode, ReactElement } from 'react'

View File

@ -9,12 +9,14 @@ import React, {
} from 'react'
import { Logger, DDO, BestPrice, MetadataMain } from '@oceanprotocol/lib'
import { PurgatoryData } from '@oceanprotocol/lib/dist/node/ddo/interfaces/PurgatoryData'
import { getDataTokenPrice, useOcean } from '@oceanprotocol/react'
import getAssetPurgatoryData from '../utils/purgatory'
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import axios, { CancelToken } from 'axios'
import { retrieveDDO } from '../utils/aquarius'
import { MetadataMarket } from '../@types/MetaData'
import { useOcean } from './Ocean'
import { gql, useQuery } from '@apollo/client'
import { PoolPrice } from '../@types/apollo/PoolPrice'
import { FrePrice } from '../@types/apollo/FrePrice'
interface AssetProviderValue {
isInPurgatory: boolean
@ -29,9 +31,24 @@ interface AssetProviderValue {
error?: string
refreshInterval: number
refreshDdo: (token?: CancelToken) => Promise<void>
refreshPrice: () => Promise<void>
}
const poolQuery = gql`
query PoolPrice($datatoken: String) {
pools(where: { datatokenAddress: $datatoken }) {
spotPrice
}
}
`
const freQuery = gql`
query FrePrice($datatoken: String) {
fixedRateExchanges(orderBy: id, where: { datatoken: $datatoken }) {
rate
}
}
`
const AssetContext = createContext({} as AssetProviderValue)
const refreshInterval = 10000 // 10 sec.
@ -43,7 +60,7 @@ function AssetProvider({
asset: string | DDO
children: ReactNode
}): ReactElement {
const { ocean, status, config, networkId } = useOcean()
const { config } = useOcean()
const [isInPurgatory, setIsInPurgatory] = useState(false)
const [purgatoryData, setPurgatoryData] = useState<PurgatoryData>()
const [ddo, setDDO] = useState<DDO>()
@ -54,27 +71,51 @@ function AssetProvider({
const [owner, setOwner] = useState<string>()
const [error, setError] = useState<string>()
const [type, setType] = useState<MetadataMain['type']>()
const [variables, setVariables] = useState({})
const refreshPrice = useCallback(async () => {
if (
!ddo ||
status !== 1 ||
networkId !== (config as ConfigHelperConfig).networkId
)
return
const {
refetch: refetchFre,
startPolling: startPollingFre,
data: frePrice
} = useQuery<FrePrice>(freQuery, {
variables,
skip: true
})
const {
refetch: refetchPool,
startPolling: startPollingPool,
data: poolPrice
} = useQuery<PoolPrice>(poolQuery, {
variables,
skip: true
})
const newPrice = await getDataTokenPrice(
ocean,
ddo.dataToken,
ddo?.price?.type,
ddo.price.address
)
setPrice(newPrice)
Logger.log(`Refreshed asset price: ${newPrice?.value}`, newPrice)
}, [ocean, config, ddo, networkId, status])
useEffect(() => {
if (!ddo || !variables) return
if (ddo.price.type === 'exchange') {
refetchFre(variables)
startPollingFre(refreshInterval)
} else {
refetchPool(variables)
startPollingPool(refreshInterval)
}
}, [ddo, variables])
useEffect(() => {
if (!frePrice || frePrice.fixedRateExchanges.length === 0) return
price.value = frePrice.fixedRateExchanges[0].rate
setPrice(price)
}, [frePrice])
useEffect(() => {
if (!poolPrice || poolPrice.pools.length === 0) return
price.value = poolPrice.pools[0].spotPrice
price.value = 3222222
setPrice(price)
}, [poolPrice])
const fetchDdo = async (token?: CancelToken) => {
Logger.log('Init asset, get ddo')
Logger.log('[asset] Init asset, get DDO')
const ddo = await retrieveDDO(
asset as string,
config.metadataCacheUri,
@ -83,7 +124,7 @@ function AssetProvider({
if (!ddo) {
setError(
`The DDO for ${asset} was not found in MetadataCache. If you just published a new data set, wait some seconds and refresh this page.`
`[asset] The DDO for ${asset} was not found in MetadataCache. If you just published a new data set, wait some seconds and refresh this page.`
)
} else {
setError(undefined)
@ -93,9 +134,10 @@ function AssetProvider({
const refreshDdo = async (token?: CancelToken) => {
const ddo = await fetchDdo(token)
Logger.debug('DDO', ddo)
Logger.debug('[asset] Got DDO', ddo)
setDDO(ddo)
}
//
// Get and set DDO based on passed DDO or DID
//
@ -104,12 +146,11 @@ function AssetProvider({
const source = axios.CancelToken.source()
let isMounted = true
Logger.log('Init asset, get ddo')
async function init() {
const ddo = await fetchDdo(source.token)
if (!isMounted) return
Logger.debug('DDO', ddo)
Logger.debug('[asset] Got DDO', ddo)
setDDO(ddo)
setDID(asset as string)
}
@ -120,57 +161,37 @@ function AssetProvider({
}
}, [asset, config?.metadataCacheUri])
useEffect(() => {
// Re-fetch price periodically, triggering re-calculation of everything
let isMounted = true
const interval = setInterval(() => {
if (!isMounted) return
refreshPrice()
}, refreshInterval)
return () => {
clearInterval(interval)
isMounted = false
}
}, [ddo, networkId, refreshPrice])
const setPurgatory = useCallback(async (did: string): Promise<void> => {
if (!did) return
try {
const result = await getAssetPurgatoryData(did)
if (result?.did !== undefined) {
setIsInPurgatory(true)
setPurgatoryData(result)
return
}
setIsInPurgatory(false)
const isInPurgatory = result?.did !== undefined
setIsInPurgatory(isInPurgatory)
isInPurgatory && setPurgatoryData(result)
} catch (error) {
Logger.error(error)
}
}, [])
const initMetadata = useCallback(
async (ddo: DDO): Promise<void> => {
if (!ddo) return
const initMetadata = useCallback(async (ddo: DDO): Promise<void> => {
if (!ddo) return
Logger.log('Init metadata')
// Set price & metadata from DDO first
setPrice(ddo.price)
const { attributes } = ddo.findServiceByType('metadata')
setMetadata((attributes as unknown) as MetadataMarket)
setTitle(attributes?.main.name)
setType(attributes.main.type)
setOwner(ddo.publicKey[0].owner)
setIsInPurgatory(ddo.isInPurgatory === 'true')
// Set price & metadata from DDO first
setPrice(ddo.price)
setVariables({ datatoken: ddo?.dataToken.toLowerCase() })
await setPurgatory(ddo.id)
await refreshPrice()
},
[refreshPrice, setPurgatory]
)
// Get metadata from DDO
const { attributes } = ddo.findServiceByType('metadata')
setMetadata((attributes as unknown) as MetadataMarket)
setTitle(attributes?.main.name)
setType(attributes.main.type)
setOwner(ddo.publicKey[0].owner)
Logger.log('[asset] Got Metadata from DDO', attributes)
setIsInPurgatory(ddo.isInPurgatory === 'true')
await setPurgatory(ddo.id)
}, [])
useEffect(() => {
if (!ddo) return
@ -192,8 +213,7 @@ function AssetProvider({
isInPurgatory,
purgatoryData,
refreshInterval,
refreshDdo,
refreshPrice
refreshDdo
} as AssetProviderValue
}
>

163
src/providers/Ocean.tsx Normal file
View File

@ -0,0 +1,163 @@
import React, {
useContext,
useState,
createContext,
ReactElement,
useCallback,
ReactNode,
useEffect
} from 'react'
import { Ocean, Logger, Account, Config } from '@oceanprotocol/lib'
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import { useWeb3 } from './Web3'
import {
getDevelopmentConfig,
getOceanConfig,
getUserInfo
} from '../utils/ocean'
import { UserBalance } from '../@types/TokenBalance'
const refreshInterval = 20000 // 20 sec.
interface OceanProviderValue {
ocean: Ocean
config: ConfigHelperConfig | Config
account: Account
balance: UserBalance
connect: (config?: Config) => Promise<void>
refreshBalance: () => Promise<void>
}
const OceanContext = createContext({} as OceanProviderValue)
function OceanProvider({
initialConfig,
children
}: {
initialConfig: Config | ConfigHelperConfig
children: ReactNode
}): ReactElement {
const { web3, accountId, networkId } = useWeb3()
const [ocean, setOcean] = useState<Ocean>()
const [account, setAccount] = useState<Account>()
const [balance, setBalance] = useState<UserBalance>({
eth: undefined,
ocean: undefined
})
const [config, setConfig] = useState<ConfigHelperConfig | Config>(
initialConfig
)
// -----------------------------------
// Create Ocean instance
// -----------------------------------
const connect = useCallback(
async (newConfig?: ConfigHelperConfig | Config) => {
try {
const usedConfig = newConfig || config
Logger.log('[ocean] Connecting Ocean...', usedConfig)
usedConfig.web3Provider = web3 || initialConfig.web3Provider
if (newConfig) {
setConfig(usedConfig)
}
if (usedConfig.web3Provider) {
const newOcean = await Ocean.getInstance(usedConfig)
setOcean(newOcean)
Logger.log('[ocean] Ocean instance created.', newOcean)
}
} catch (error) {
Logger.error('[ocean] Error: ', error.message)
}
},
[web3, config, initialConfig.web3Provider]
)
async function refreshBalance() {
if (!ocean || !account || !web3) return
const { balance } = await getUserInfo(ocean)
setBalance(balance)
}
// -----------------------------------
// Initial connection
// -----------------------------------
useEffect(() => {
async function init() {
await connect()
}
init()
// init periodic refresh of wallet balance
const balanceInterval = setInterval(() => refreshBalance(), refreshInterval)
return () => {
clearInterval(balanceInterval)
}
}, [])
// -----------------------------------
// Get user info, handle account change from web3
// -----------------------------------
useEffect(() => {
if (!ocean || !accountId || !web3) return
async function getInfo() {
const { account, balance } = await getUserInfo(ocean)
setAccount(account)
setBalance(balance)
}
getInfo()
}, [ocean, accountId, web3])
// -----------------------------------
// Handle network change from web3
// -----------------------------------
useEffect(() => {
if (!networkId) return
async function reconnect() {
const newConfig = {
...getOceanConfig(networkId),
// add local dev values
...(networkId === 8996 && {
...getDevelopmentConfig()
})
}
try {
await connect(newConfig)
} catch (error) {
Logger.error('[ocean] Error: ', error.message)
}
}
reconnect()
}, [networkId])
return (
<OceanContext.Provider
value={
{
ocean,
account,
balance,
config,
connect,
refreshBalance
} as OceanProviderValue
}
>
{children}
</OceanContext.Provider>
)
}
// Helper hook to access the provider values
const useOcean = (): OceanProviderValue => useContext(OceanContext)
export { OceanProvider, useOcean, OceanProviderValue, OceanContext }
export default OceanProvider

View File

@ -39,7 +39,7 @@ export default function PricesProvider({
const onSuccess = async (data: { [tokenId]: { [key: string]: number } }) => {
if (!data) return
Logger.log(`Got new prices. ${JSON.stringify(data[tokenId])}`)
Logger.log('[prices] Got new OCEAN spot prices.', data[tokenId])
setPrices(data[tokenId])
}

View File

@ -8,8 +8,9 @@ import React, {
} from 'react'
import { Logger } from '@oceanprotocol/lib'
import { LogLevel } from '@oceanprotocol/lib/dist/node/utils/Logger'
import { useOcean } from '@oceanprotocol/react'
import { useOcean } from './Ocean'
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import { isBrowser } from '../utils'
interface UserPreferencesValue {
debug: boolean
@ -30,14 +31,13 @@ const localStorageKey = 'ocean-user-preferences'
function getLocalStorage(): UserPreferencesValue {
const storageParsed =
typeof window !== 'undefined' &&
JSON.parse(window.localStorage.getItem(localStorageKey))
isBrowser && JSON.parse(window.localStorage.getItem(localStorageKey))
return storageParsed
}
function setLocalStorage(values: Partial<UserPreferencesValue>) {
return (
typeof window !== 'undefined' &&
isBrowser &&
window.localStorage.setItem(localStorageKey, JSON.stringify(values))
)
}

253
src/providers/Web3.tsx Normal file
View File

@ -0,0 +1,253 @@
import React, {
useContext,
useState,
useEffect,
createContext,
ReactElement,
ReactNode,
useCallback
} from 'react'
import Web3 from 'web3'
import Web3Modal from 'web3modal'
import { infuraProjectId as infuraId, portisId } from '../../app.config'
import WalletConnectProvider from '@walletconnect/web3-provider'
import { Logger } from '@oceanprotocol/lib'
import { isBrowser } from '../utils'
import {
EthereumListsChain,
getNetworkData,
getNetworkDisplayName
} from '../utils/web3'
import { graphql, useStaticQuery } from 'gatsby'
interface Web3ProviderValue {
web3: Web3
web3Provider: any
web3Modal: Web3Modal
accountId: string
networkId: number
networkDisplayName: string
networkData: EthereumListsChain
isTestnet: boolean
connect: () => Promise<void>
logout: () => Promise<void>
}
const web3ModalTheme = {
background: 'var(--background-body)',
main: 'var(--font-color-heading)',
secondary: 'var(--brand-grey-light)',
border: 'var(--border-color)',
hover: 'var(--background-highlight)'
}
// HEADS UP! We inline-require some packages so the Gatsby SSR build does not break.
// We only need them client-side.
const providerOptions = isBrowser
? {
walletconnect: {
package: WalletConnectProvider,
options: { infuraId }
},
portis: {
package: require('@portis/web3'),
options: {
id: portisId
}
}
// torus: {
// package: require('@toruslabs/torus-embed')
// // options: {
// // networkParams: {
// // host: oceanConfig.url, // optional
// // chainId: 1337, // optional
// // networkId: 1337 // optional
// // }
// // }
// }
}
: {}
export const web3ModalOpts = {
cacheProvider: true,
providerOptions,
theme: web3ModalTheme
}
const networksQuery = graphql`
query {
allNetworksMetadataJson {
edges {
node {
chain
network
networkId
chainId
nativeCurrency {
name
symbol
decimals
}
}
}
}
}
`
const Web3Context = createContext({} as Web3ProviderValue)
function Web3Provider({ children }: { children: ReactNode }): ReactElement {
const data = useStaticQuery(networksQuery)
const networksList: { node: EthereumListsChain }[] =
data.allNetworksMetadataJson.edges
const [web3, setWeb3] = useState<Web3>()
const [web3Provider, setWeb3Provider] = useState<any>()
const [web3Modal, setWeb3Modal] = useState<Web3Modal>()
const [networkId, setNetworkId] = useState<number>()
const [networkDisplayName, setNetworkDisplayName] = useState<string>()
const [networkData, setNetworkData] = useState<EthereumListsChain>()
const [isTestnet, setIsTestnet] = useState<boolean>()
const [accountId, setAccountId] = useState<string>()
const connect = useCallback(async () => {
if (!web3Modal) return
try {
Logger.log('[web3] Connecting Web3...')
const provider = await web3Modal?.connect()
setWeb3Provider(provider)
const web3 = new Web3(provider)
setWeb3(web3)
Logger.log('[web3] Web3 created.', web3)
const networkId = await web3.eth.net.getId()
setNetworkId(networkId)
Logger.log('[web3] network id ', networkId)
const accountId = (await web3.eth.getAccounts())[0]
setAccountId(accountId)
Logger.log('[web3] account id', accountId)
} catch (error) {
Logger.error('[web3] Error: ', error.message)
}
}, [web3Modal])
// -----------------------------------
// Create initial Web3Modal instance
// -----------------------------------
useEffect(() => {
if (web3Modal) return
async function init() {
// note: needs artificial await here so the log message is reached and output
const web3ModalInstance = await new Web3Modal(web3ModalOpts)
setWeb3Modal(web3ModalInstance)
Logger.log('[web3] Web3Modal instance created.', web3ModalInstance)
}
init()
}, [connect, web3Modal])
// -----------------------------------
// Reconnect automatically for returning users
// -----------------------------------
useEffect(() => {
if (!web3Modal?.cachedProvider) return
async function connectCached() {
Logger.log(
'[web3] Connecting to cached provider: ',
web3Modal.cachedProvider
)
await connect()
}
connectCached()
}, [connect, web3Modal])
// -----------------------------------
// Get and set network metadata
// -----------------------------------
useEffect(() => {
if (!networkId) return
const networkData = getNetworkData(networksList, networkId)
setNetworkData(networkData)
Logger.log('[web3] Network metadata found.', networkData)
// Construct network display name
const networkDisplayName = getNetworkDisplayName(networkData, networkId)
setNetworkDisplayName(networkDisplayName)
// Figure out if we're on a chain's testnet, or not
setIsTestnet(networkData.network !== 'mainnet')
Logger.log(`[web3] Network display name set to: ${networkDisplayName}`)
}, [networkId, networksList])
// -----------------------------------
// Logout helper
// -----------------------------------
async function logout() {
web3Modal?.clearCachedProvider()
}
// -----------------------------------
// Handle change events
// -----------------------------------
async function handleNetworkChanged(networkId: string) {
Logger.log('[web3] Network changed', networkId)
// const networkId = Number(chainId.replace('0x', ''))
setNetworkId(Number(networkId))
}
async function handleAccountsChanged(accounts: string[]) {
Logger.log('[web3] Account changed', accounts[0])
setAccountId(accounts[0])
}
useEffect(() => {
if (!web3Provider || !web3) return
//
// HEADS UP! We should rather listen to `chainChanged` exposing the `chainId`
// but for whatever reason the exposed `chainId` is wildly different from
// what is shown on https://chainid.network, in turn breaking our network/config
// mapping. The networkChanged is deprecated but works as expected for our case.
// See: https://eips.ethereum.org/EIPS/eip-1193#chainchanged
//
web3Provider.on('networkChanged', handleNetworkChanged)
web3Provider.on('accountsChanged', handleAccountsChanged)
return () => {
web3Provider.removeListener('networkChanged')
web3Provider.removeListener('accountsChanged')
}
}, [web3Provider, web3])
return (
<Web3Context.Provider
value={{
web3,
web3Provider,
web3Modal,
accountId,
networkId,
networkDisplayName,
networkData,
isTestnet,
connect,
logout
}}
>
{children}
</Web3Context.Provider>
)
}
// Helper hook to access the provider values
const useWeb3 = (): Web3ProviderValue => useContext(Web3Context)
export { Web3Provider, useWeb3, Web3ProviderValue, Web3Context }
export default Web3Provider

93
src/utils/dtUtils.ts Normal file
View File

@ -0,0 +1,93 @@
import { Ocean, BestPrice, Logger } from '@oceanprotocol/lib'
const priceError: BestPrice = {
type: '',
address: '',
pools: [],
datatoken: 0,
value: 0,
isConsumable: ''
}
export async function getFirstExchangePrice(
ocean: Ocean,
dataTokenAddress: string
): Promise<BestPrice> {
try {
const tokenExchanges = await ocean.fixedRateExchange.searchforDT(
dataTokenAddress,
'1'
)
if (tokenExchanges === undefined || tokenExchanges.length === 0) {
return priceError
}
const [tokenExchange] = tokenExchanges
return {
type: 'exchange',
pools: [],
address: tokenExchange.exchangeID || '',
value: Number(tokenExchange.fixedRate),
ocean: 0,
datatoken: Number(tokenExchange.supply),
isConsumable: Number(tokenExchange.supply) > 0 ? 'true' : 'false'
}
} catch (err) {
Logger.log(err)
return priceError
}
}
export async function getFirstPoolPrice(
ocean: Ocean,
dataTokenAddress: string,
poolAddress?: string
): Promise<BestPrice> {
let firstPoolAddress = poolAddress
if (!poolAddress) {
const tokenPools = await ocean.pool.searchPoolforDT(dataTokenAddress)
if (tokenPools === undefined || tokenPools.length === 0) {
return priceError
}
;[firstPoolAddress] = tokenPools
}
if (!firstPoolAddress) return priceError
const firstPoolPrice = await ocean.pool.calcInGivenOut(
firstPoolAddress,
ocean.pool.oceanAddress,
dataTokenAddress,
'1'
)
const usePrice = await ocean.pool.getOceanNeeded(firstPoolAddress, '1')
const oceanReserve = await ocean.pool.getOceanReserve(firstPoolAddress)
const dtReserve = await ocean.pool.getDTReserve(firstPoolAddress)
return {
type: 'pool',
pools: [firstPoolAddress],
address: firstPoolAddress,
value: Number(firstPoolPrice),
ocean: Number(oceanReserve),
datatoken: Number(dtReserve),
isConsumable: Number(usePrice) > 0 ? 'true' : 'false'
}
}
export async function getDataTokenPrice(
ocean: Ocean,
dataTokenAddress: string,
type: string,
poolAddress?: string
): Promise<BestPrice> {
const price =
type === 'pool'
? await getFirstPoolPrice(ocean, dataTokenAddress, poolAddress)
: await getFirstExchangePrice(ocean, dataTokenAddress)
return price
}

69
src/utils/feedback.ts Normal file
View File

@ -0,0 +1,69 @@
export const feedback: { [key in number]: string } = {
99: 'Decrypting file URL...',
0: '1/3 Looking for data token. Buying if none found...',
1: '2/3 Transfering data token.',
2: '3/3 Payment confirmed. Requesting access...'
}
export const publishFeedback: { [key in number]: string } = {
0: '1/5 Creating datatoken ...',
2: '2/5 Encrypting files ...',
4: '3/5 Storing ddo ...',
6: '4/5 Minting tokens ...',
8: '5/5 Asset published succesfully'
}
// TODO: do something with this object,
// consumeStep should probably return one of those strings
// instead of just a number
export const consumeFeedback: { [key in number]: string } = {
...feedback,
3: '3/3 Access granted. Consuming file...'
}
// TODO: customize for compute
export const computeFeedback: { [key in number]: string } = {
0: '1/3 Ordering asset...',
1: '2/3 Transfering data token.',
2: '3/3 Access granted. Starting job...'
}
export function getCreatePricingPoolFeedback(
dtSymbol: string
): { [key: number]: string } {
return {
99: `Minting ${dtSymbol} ...`,
0: 'Creating pool ...',
1: `Approving ${dtSymbol} ...`,
2: 'Approving OCEAN ...',
3: 'Setup pool ...',
4: 'Pool created.'
}
}
export function getCreatePricingExchangeFeedback(
dtSymbol: string
): { [key: number]: string } {
return {
99: `Minting ${dtSymbol} ...`,
0: 'Creating exchange ...',
1: `Approving ${dtSymbol} ...`,
2: 'Fixed exchange created.'
}
}
export function getBuyDTFeedback(dtSymbol: string): { [key: number]: string } {
return {
1: '1/3 Approving OCEAN ...',
2: `2/3 Buying ${dtSymbol} ...`,
3: `3/3 ${dtSymbol} bought.`
}
}
export function getSellDTFeedback(dtSymbol: string): { [key: number]: string } {
return {
1: '1/3 Approving OCEAN ...',
2: `2/3 Selling ${dtSymbol} ...`,
3: `3/3 ${dtSymbol} sold.`
}
}

View File

@ -54,16 +54,8 @@ export async function fetchData(url: string): Promise<AxiosResponse['data']> {
}
}
export function isDid(did: string | undefined): boolean {
const didMatch = (did as string).match(/^did:op:([a-f0-9]{64})$/i)
return !!didMatch
}
export function formatBytes(a: number, b: number): string {
if (a === 0) return '0 Bytes'
const c = 1024
const d = b || 2
const e = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const f = Math.floor(Math.log(a) / Math.log(c))
return parseFloat((a / Math.pow(c, f)).toFixed(d)) + ' ' + e[f]
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}

47
src/utils/ocean.ts Normal file
View File

@ -0,0 +1,47 @@
import { Account, Config, Logger, Ocean } from '@oceanprotocol/lib'
import contractAddresses from '@oceanprotocol/contracts/artifacts/address.json'
import {
ConfigHelper,
ConfigHelperConfig,
ConfigHelperNetworkId,
ConfigHelperNetworkName
} from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
import { UserBalance } from '../@types/TokenBalance'
export function getOceanConfig(
network: ConfigHelperNetworkName | ConfigHelperNetworkId
): Config {
return new ConfigHelper().getConfig(
network,
process.env.GATSBY_INFURA_PROJECT_ID
)
}
export function getDevelopmentConfig(): Partial<ConfigHelperConfig> {
return {
factoryAddress: contractAddresses.development?.DTFactory,
poolFactoryAddress: contractAddresses.development?.BFactory,
fixedRateExchangeAddress: contractAddresses.development?.FixedRateExchange,
metadataContractAddress: contractAddresses.development?.Metadata,
oceanTokenAddress: contractAddresses.development?.Ocean,
// There is no subgraph in barge so we hardcode the Rinkeby one for now
subgraphUri: 'https://subgraph.rinkeby.oceanprotocol.com'
}
}
export async function getUserInfo(
ocean: Ocean
): Promise<{ account: Account; balance: UserBalance }> {
if (!ocean) return { account: null, balance: { eth: '0', ocean: '0' } }
const account = (await ocean.accounts.list())[0]
Logger.log('[ocean] Account: ', account)
const balance = {
eth: await account.getEtherBalance(),
ocean: await account.getOceanBalance()
}
Logger.log('[ocean] Balance: ', JSON.stringify(balance))
return { account, balance }
}

View File

@ -0,0 +1,15 @@
export function getBuyDTFeedback(dtSymbol: string): { [key: number]: string } {
return {
1: '1/3 Approving OCEAN ...',
2: `2/3 Buying ${dtSymbol} ...`,
3: `3/3 ${dtSymbol} bought.`
}
}
export function getSellDTFeedback(dtSymbol: string): { [key: number]: string } {
return {
1: '1/3 Approving OCEAN ...',
2: `2/3 Selling ${dtSymbol} ...`,
3: `3/3 ${dtSymbol} sold.`
}
}

View File

@ -1,11 +1,23 @@
import { PurgatoryData } from '@oceanprotocol/lib'
import axios from 'axios'
import { PurgatoryData as PurgatoryDataAsset } from '@oceanprotocol/lib'
import { fetchData } from '.'
const purgatoryUrl = 'https://market-purgatory.oceanprotocol.com/api/'
export interface PurgatoryDataAccount {
address: string
reason: string
}
export default async function getAssetPurgatoryData(
did: string
): Promise<PurgatoryData> {
const response = await axios(`${purgatoryUrl}asset?did=${did}`)
const responseJson = await response.data[0]
return { did: responseJson?.did, reason: responseJson?.reason }
): Promise<PurgatoryDataAsset> {
const data = await fetchData(`${purgatoryUrl}asset?did=${did}`)
return { did: data[0]?.did, reason: data[0]?.reason }
}
export async function getAccountPurgatoryData(
address: string
): Promise<PurgatoryDataAccount> {
const data = await fetchData(`${purgatoryUrl}account?address=${address}`)
return { address: data[0]?.address, reason: data[0]?.reason }
}

View File

@ -1,87 +0,0 @@
import { infuraProjectId as infuraId, portisId } from '../../app.config'
import WalletConnectProvider from '@walletconnect/web3-provider'
export interface EthereumListsChain {
name: string
chainId: number
shortName: string
chain: string
network: string
networkId: number
nativeCurrency: { name: string; symbol: string; decimals: number }
rpc: string[]
faucets: string[]
infoURL: string
}
const web3ModalTheme = {
background: 'var(--background-body)',
main: 'var(--font-color-heading)',
secondary: 'var(--brand-grey-light)',
border: 'var(--border-color)',
hover: 'var(--background-highlight)'
}
// HEADS UP! We inline-require some packages so the Gatsby SSR build does not break.
// We only need them client-side.
const providerOptions =
typeof window !== 'undefined'
? {
walletconnect: {
package: WalletConnectProvider,
options: { infuraId }
},
portis: {
package: require('@portis/web3'),
options: {
id: portisId
}
}
// torus: {
// package: require('@toruslabs/torus-embed')
// // options: {
// // networkParams: {
// // host: oceanConfig.url, // optional
// // chainId: 1337, // optional
// // networkId: 1337 // optional
// // }
// // }
// }
}
: {}
export const web3ModalOpts = {
cacheProvider: true,
providerOptions,
theme: web3ModalTheme
}
export function accountTruncate(account: string): string {
if (!account) return
const middle = account.substring(6, 38)
const truncated = account.replace(middle, '…')
return truncated
}
export function getNetworkDisplayName(
data: EthereumListsChain,
networkId: number
): string {
const displayName = data
? `${data.chain} ${data.network === 'mainnet' ? '' : data.network}`
: networkId === 8996
? 'Development'
: 'Unknown'
return displayName
}
export function getNetworkData(
data: { node: EthereumListsChain }[],
networkId: number
): EthereumListsChain {
const networkData = data.filter(
({ node }: { node: EthereumListsChain }) => node.networkId === networkId
)[0]
return networkData.node
}

42
src/utils/web3.ts Normal file
View File

@ -0,0 +1,42 @@
export interface EthereumListsChain {
name: string
chainId: number
shortName: string
chain: string
network: string
networkId: number
nativeCurrency: { name: string; symbol: string; decimals: number }
rpc: string[]
faucets: string[]
infoURL: string
}
export function accountTruncate(account: string): string {
if (!account) return
const middle = account.substring(6, 38)
const truncated = account.replace(middle, '…')
return truncated
}
export function getNetworkDisplayName(
data: EthereumListsChain,
networkId: number
): string {
const displayName = data
? `${data.chain} ${data.network === 'mainnet' ? '' : data.network}`
: networkId === 8996
? 'Development'
: 'Unknown'
return displayName
}
export function getNetworkData(
data: { node: EthereumListsChain }[],
networkId: number
): EthereumListsChain {
const networkData = data.filter(
({ node }: { node: EthereumListsChain }) => node.networkId === networkId
)[0]
return networkData.node
}

View File

@ -19,30 +19,7 @@ const reactMock = {
accountId: '0x0000000011111111aaaaaaaabbbbbbbb22222222',
balance: '0.12'
}
},
useConsume: () => {
return {
consume: () => null as any,
consumeStepText: '',
isLoading: false
}
},
useCompute: () => {
return {
compute: () => null as any,
isLoading: false,
computeStepText: 0,
computeError: ''
}
},
useMetadata: () => {
return {
getCuration: () => {
return Promise.resolve({ rating: 0, numVotes: 0 })
}
}
},
computeOptions: ['', '']
}
}
export default reactMock

View File

@ -1,7 +1,6 @@
import '@testing-library/jest-dom/extend-expect'
import * as Gatsby from 'gatsby'
import siteMetadata from './__fixtures__/siteMetadata.json'
// import mockReact from './__mocks__/@oceanprotocol/react'
if (typeof window.IntersectionObserver === 'undefined') {
import('intersection-observer')
@ -20,7 +19,6 @@ export const globalMock = {
beforeAll(() => {
jest.mock('web3')
jest.mock('@oceanprotocol/lib')
jest.mock('@oceanprotocol/react')
// useOcean.mockImplementation(() => mockReact.useOcean())
useStaticQuery.mockImplementation(() => globalMock)

View File

@ -1,12 +1,4 @@
import axios, { AxiosResponse } from 'axios'
import {
toStringNoMS,
updateQueryStringParameter,
isDid
} from '../../../src/utils'
jest.mock('axios')
import { toStringNoMS, updateQueryStringParameter } from '../../../src/utils'
describe('updateQueryStringParameter()', () => {
it('transform a URI', () => {
@ -26,17 +18,3 @@ describe('toStringNoMS()', () => {
)
})
})
describe('isDid()', () => {
it('checks correct DID', () => {
expect(
isDid(
'did:op:bb6b9e960b2e40e3840ca5eafc8eb97af431b4d190b54e2f9926e1f792cdc54f'
)
).toBe(true)
})
it('errors when no DID', () => {
expect(isDid('hello')).toBe(false)
})
})