mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Create wallet network switcher (#676)
* created component for wallet network switching * component styling * display network names * created networks config * fix get network config function * wip * moved switcher component inside consume * use isAssetNetwork to show Switcher component, added to publish * get network properties using networkList and oceanConfig * error fix * hide wallet network switcher if no provider * use chainId from useAsset ddo * added switcher component to Compute * added component to edit metadata and compute settings * added component to advance settings form * fixed lint errors * included component inside Web3Feedback * updated text and icon design * button design update, and Web3Feedback position on edit asset * fixed lint error * message update * tag error fixes * disabled pool and trade buttons if not asset network * mainnet aquarius fallback url * filename typo fix * replace NetworkName component with getNetworkDisplayName function * added method to switch to EthereumChain networks, removed logs * fixed lint error * style tweaks * markup and styles simplification * restrict add datatoken Co-authored-by: Norbi <katunanorbert@gmai.com> Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
483ce88d42
commit
565c0324f9
@ -78,6 +78,7 @@
|
||||
color: var(--brand-pink);
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
/* Size Modifiers */
|
||||
|
@ -13,6 +13,8 @@ import { transformTags } from '../../utils/metadata'
|
||||
import NetworkName from '../atoms/NetworkName'
|
||||
import { useWeb3 } from '../../providers/Web3'
|
||||
import styles from './MetadataPreview.module.css'
|
||||
import Web3Feedback from './Web3Feedback'
|
||||
import { useAsset } from '../../providers/Asset'
|
||||
|
||||
function Description({ description }: { description: string }) {
|
||||
const [fullDescription, setFullDescription] = useState<boolean>(false)
|
||||
@ -95,6 +97,7 @@ export function MetadataPreview({
|
||||
values: Partial<MetadataPublishFormDataset>
|
||||
}): ReactElement {
|
||||
const { networkId } = useWeb3()
|
||||
const { isAssetNetwork } = useAsset()
|
||||
|
||||
return (
|
||||
<div className={styles.preview}>
|
||||
@ -126,6 +129,9 @@ export function MetadataPreview({
|
||||
</header>
|
||||
|
||||
<MetaFull values={values} />
|
||||
{isAssetNetwork === false && (
|
||||
<Web3Feedback isAssetNetwork={isAssetNetwork} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
.text {
|
||||
color: var(--color-secondary);
|
||||
margin-top: calc(var(--spacer) / 4);
|
||||
margin-bottom: calc(var(--spacer) / 2);
|
||||
}
|
51
src/components/molecules/WalletNetworkSwitcher.tsx
Normal file
51
src/components/molecules/WalletNetworkSwitcher.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import { useWeb3 } from '../../providers/Web3'
|
||||
import {
|
||||
addCustomNetwork,
|
||||
getNetworkConfigObject,
|
||||
getNetworkDisplayName,
|
||||
getNetworkDataById
|
||||
} from '../../utils/web3'
|
||||
import Button from '../atoms/Button'
|
||||
import styles from './WalletNetworkSwitcher.module.css'
|
||||
import useNetworkMetadata from '../../hooks/useNetworkMetadata'
|
||||
import { getOceanConfig } from '../../utils/ocean'
|
||||
import { useAsset } from '../../providers/Asset'
|
||||
|
||||
export default function WalletNetworkSwitcher(): ReactElement {
|
||||
const { networkId, web3Provider } = useWeb3()
|
||||
const { ddo } = useAsset()
|
||||
const oceanConfig = getOceanConfig(ddo.chainId)
|
||||
const { networksList } = useNetworkMetadata()
|
||||
const ddoNetworkData = getNetworkDataById(networksList, ddo.chainId)
|
||||
const walletNetworkData = getNetworkDataById(networksList, networkId)
|
||||
|
||||
const ddoNetworkName = (
|
||||
<strong>{getNetworkDisplayName(ddoNetworkData, ddo.chainId)}</strong>
|
||||
)
|
||||
const walletNetworkName = (
|
||||
<strong>{getNetworkDisplayName(walletNetworkData, networkId)}</strong>
|
||||
)
|
||||
|
||||
async function switchWalletNetwork() {
|
||||
const networkNode = networksList.find(
|
||||
(data) => data.node.chainId === ddo.chainId
|
||||
).node
|
||||
const network = { ...networkNode, providerUri: oceanConfig.providerUri }
|
||||
const networkConfig = getNetworkConfigObject(network)
|
||||
addCustomNetwork(web3Provider, networkConfig)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className={styles.text}>
|
||||
This asset is published on {ddoNetworkName} but your wallet is connected
|
||||
to {walletNetworkName}. Connect to {ddoNetworkName} to interact with
|
||||
this asset.
|
||||
</p>
|
||||
<Button size="small" onClick={() => switchWalletNetwork()}>
|
||||
Switch to {ddoNetworkName}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
.feedback i {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(var(--spacer) / 1.5);
|
||||
top: calc(var(--spacer) / 1.45);
|
||||
}
|
||||
|
||||
.title {
|
||||
|
@ -2,6 +2,7 @@ import React, { ReactElement } from 'react'
|
||||
import { useWeb3 } from '../../providers/Web3'
|
||||
import Status from '../atoms/Status'
|
||||
import styles from './Web3Feedback.module.css'
|
||||
import WalletNetworkSwitcher from './WalletNetworkSwitcher'
|
||||
|
||||
export declare type Web3Error = {
|
||||
status: 'error' | 'warning' | 'success'
|
||||
@ -34,7 +35,7 @@ export default function Web3Feedback({
|
||||
: // : !ocean
|
||||
// ? 'Error connecting to Ocean'
|
||||
accountId && isAssetNetwork === false
|
||||
? 'Wrong network'
|
||||
? 'Not connected to asset network'
|
||||
: accountId
|
||||
? isBalanceSufficient === false
|
||||
? 'Insufficient balance'
|
||||
@ -47,15 +48,17 @@ export default function Web3Feedback({
|
||||
// ? 'Please try again.'
|
||||
isBalanceSufficient === false
|
||||
? 'You do not have enough OCEAN in your wallet to purchase this asset.'
|
||||
: isAssetNetwork === false
|
||||
? 'Connect to the asset network.'
|
||||
: 'Something went wrong.'
|
||||
|
||||
return showFeedback ? (
|
||||
<section className={styles.feedback}>
|
||||
<Status state={state} aria-hidden />
|
||||
<h3 className={styles.title}>{title}</h3>
|
||||
{message && <p className={styles.error}>{message}</p>}
|
||||
{isAssetNetwork === false ? (
|
||||
<WalletNetworkSwitcher />
|
||||
) : (
|
||||
message && <p className={styles.error}>{message}</p>
|
||||
)}
|
||||
</section>
|
||||
) : null
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
.info {
|
||||
display: flex;
|
||||
width: auto;
|
||||
padding: 0 calc(var(--spacer)) 0 calc(var(--spacer));
|
||||
}
|
||||
|
||||
.filewrapper {
|
||||
@ -15,4 +14,5 @@
|
||||
|
||||
.feedback {
|
||||
width: 100%;
|
||||
margin-top: calc(var(--spacer));
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
setMinterToDispenser,
|
||||
setMinterToPublisher
|
||||
} from '../../../../utils/freePrice'
|
||||
import Web3Feedback from '../../../molecules/Web3Feedback'
|
||||
|
||||
const contentQuery = graphql`
|
||||
query EditAvanceSettingsQuery {
|
||||
@ -72,7 +73,7 @@ export default function EditAdvancedSettings({
|
||||
const { debug } = useUserPreferences()
|
||||
const { accountId } = useWeb3()
|
||||
const { ocean } = useOcean()
|
||||
const { metadata, ddo, refreshDdo, price } = useAsset()
|
||||
const { isAssetNetwork, ddo, refreshDdo, price } = useAsset()
|
||||
const [success, setSuccess] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
const { appConfig } = useSiteMetadata()
|
||||
@ -168,7 +169,7 @@ export default function EditAdvancedSettings({
|
||||
setShowEdit={setShowEdit}
|
||||
/>
|
||||
</article>
|
||||
|
||||
<Web3Feedback isAssetNetwork={isAssetNetwork} />
|
||||
{debug === true && (
|
||||
<div className={styles.grid}>
|
||||
<DebugEditCredential
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
setMinterToDispenser,
|
||||
setMinterToPublisher
|
||||
} from '../../../../utils/freePrice'
|
||||
import Web3Feedback from '../../../molecules/Web3Feedback'
|
||||
|
||||
const contentQuery = graphql`
|
||||
query EditComputeDataQuery {
|
||||
@ -66,7 +67,7 @@ export default function EditComputeDataset({
|
||||
const { debug } = useUserPreferences()
|
||||
const { ocean } = useOcean()
|
||||
const { accountId } = useWeb3()
|
||||
const { ddo, refreshDdo, price } = useAsset()
|
||||
const { ddo, price, isAssetNetwork, refreshDdo } = useAsset()
|
||||
const [success, setSuccess] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
|
||||
@ -169,7 +170,7 @@ export default function EditComputeDataset({
|
||||
setShowEdit={setShowEdit}
|
||||
/>
|
||||
</article>
|
||||
|
||||
<Web3Feedback isAssetNetwork={isAssetNetwork} />
|
||||
{debug === true && (
|
||||
<div className={styles.grid}>
|
||||
<DebugEditCompute values={values} ddo={ddo} />
|
||||
|
@ -7,6 +7,7 @@ import { FormFieldProps } from '../../../../@types/Form'
|
||||
import { useOcean } from '../../../../providers/Ocean'
|
||||
import { useWeb3 } from '../../../../providers/Web3'
|
||||
import { AdvancedSettingsForm } from '../../../../models/FormEditCredential'
|
||||
import { useAsset } from '../../../../providers/Asset'
|
||||
|
||||
export default function FormAdvancedSettings({
|
||||
data,
|
||||
@ -16,6 +17,7 @@ export default function FormAdvancedSettings({
|
||||
setShowEdit: (show: boolean) => void
|
||||
}): ReactElement {
|
||||
const { accountId } = useWeb3()
|
||||
const { isAssetNetwork } = useAsset()
|
||||
const { ocean, config } = useOcean()
|
||||
const {
|
||||
isValid,
|
||||
@ -45,9 +47,11 @@ export default function FormAdvancedSettings({
|
||||
}
|
||||
/>
|
||||
))}
|
||||
|
||||
<footer className={styles.actions}>
|
||||
<Button style="primary" disabled={!ocean || !accountId || !isValid}>
|
||||
<Button
|
||||
style="primary"
|
||||
disabled={!ocean || !accountId || !isValid || !isAssetNetwork}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<Button style="text" onClick={() => setShowEdit(false)}>
|
||||
|
@ -17,7 +17,6 @@ import { ComputePrivacyForm } from '../../../../models/FormEditComputeDataset'
|
||||
import { publisherTrustedAlgorithm as PublisherTrustedAlgorithm } from '@oceanprotocol/lib'
|
||||
import axios from 'axios'
|
||||
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||
import { chainIds } from '../../../../../app.config'
|
||||
|
||||
export default function FormEditComputeDataset({
|
||||
data,
|
||||
@ -31,7 +30,7 @@ export default function FormEditComputeDataset({
|
||||
const { appConfig } = useSiteMetadata()
|
||||
const { accountId } = useWeb3()
|
||||
const { ocean } = useOcean()
|
||||
const { ddo } = useAsset()
|
||||
const { ddo, isAssetNetwork } = useAsset()
|
||||
const { isValid, values }: FormikContextType<ComputePrivacyForm> =
|
||||
useFormikContext()
|
||||
const [allAlgorithms, setAllAlgorithms] = useState<AssetSelectionAsset[]>()
|
||||
@ -89,7 +88,10 @@ export default function FormEditComputeDataset({
|
||||
/>
|
||||
))}
|
||||
<footer className={styles.actions}>
|
||||
<Button style="primary" disabled={!ocean || !accountId || !isValid}>
|
||||
<Button
|
||||
style="primary"
|
||||
disabled={!ocean || !accountId || !isValid || !isAssetNetwork}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<Button style="text" onClick={() => setShowEdit(false)}>
|
||||
|
@ -8,6 +8,7 @@ import { MetadataPublishFormDataset } from '../../../../@types/MetaData'
|
||||
import { checkIfTimeoutInPredefinedValues } from '../../../../utils/metadata'
|
||||
import { useOcean } from '../../../../providers/Ocean'
|
||||
import { useWeb3 } from '../../../../providers/Web3'
|
||||
import { useAsset } from '../../../../providers/Asset'
|
||||
|
||||
function handleTimeoutCustomOption(
|
||||
data: FormFieldProps[],
|
||||
@ -58,6 +59,7 @@ export default function FormEditMetadata({
|
||||
}): ReactElement {
|
||||
const { accountId } = useWeb3()
|
||||
const { ocean, config } = useOcean()
|
||||
const { isAssetNetwork } = useAsset()
|
||||
const {
|
||||
isValid,
|
||||
validateField,
|
||||
@ -99,7 +101,7 @@ export default function FormEditMetadata({
|
||||
<footer className={styles.actions}>
|
||||
<Button
|
||||
style="primary"
|
||||
disabled={!ocean || !accountId || !isValid}
|
||||
disabled={!ocean || !accountId || !isValid || !isAssetNetwork}
|
||||
onClick={() => setTimeoutStringValue(values.timeout)}
|
||||
>
|
||||
Submit
|
||||
|
@ -64,7 +64,8 @@ export default function Pool(): ReactElement {
|
||||
|
||||
const { accountId, networkId } = useWeb3()
|
||||
const { ocean } = useOcean()
|
||||
const { isInPurgatory, ddo, owner, price, refreshInterval } = useAsset()
|
||||
const { isInPurgatory, ddo, owner, price, refreshInterval, isAssetNetwork } =
|
||||
useAsset()
|
||||
const dtSymbol = ddo?.dataTokenInfo.symbol
|
||||
|
||||
const [poolTokens, setPoolTokens] = useState<string>()
|
||||
@ -333,14 +334,18 @@ export default function Pool(): ReactElement {
|
||||
style="primary"
|
||||
size="small"
|
||||
onClick={() => setShowAdd(true)}
|
||||
disabled={isInPurgatory}
|
||||
disabled={isInPurgatory || !isAssetNetwork}
|
||||
>
|
||||
Add Liquidity
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{hasAddedLiquidity && !isRemoveDisabled && (
|
||||
<Button size="small" onClick={() => setShowRemove(true)}>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => setShowRemove(true)}
|
||||
disabled={!isAssetNetwork}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
)}
|
||||
|
@ -14,6 +14,7 @@ import { FormTradeData, initialValues } from '../../../../models/FormTrade'
|
||||
import Decimal from 'decimal.js'
|
||||
import { useOcean } from '../../../../providers/Ocean'
|
||||
import { useWeb3 } from '../../../../providers/Web3'
|
||||
import { useAsset } from '../../../../providers/Asset'
|
||||
|
||||
const contentQuery = graphql`
|
||||
query TradeQuery {
|
||||
@ -49,6 +50,7 @@ export default function FormTrade({
|
||||
const content = data.content.edges[0].node.childContentJson.trade
|
||||
const { accountId } = useWeb3()
|
||||
const { ocean } = useOcean()
|
||||
const { isAssetNetwork } = useAsset()
|
||||
const { debug } = useUserPreferences()
|
||||
const [txId, setTxId] = useState<string>()
|
||||
|
||||
@ -139,7 +141,7 @@ export default function FormTrade({
|
||||
</div>
|
||||
)}
|
||||
<Actions
|
||||
isDisabled={!isWarningAccepted}
|
||||
isDisabled={!isWarningAccepted || !isAssetNetwork}
|
||||
isLoading={isSubmitting}
|
||||
loaderMessage="Swapping tokens..."
|
||||
successMessage="Successfully swapped tokens."
|
||||
|
@ -147,12 +147,10 @@ export default function AssetActions(): ReactElement {
|
||||
<Permission eventType="consume">
|
||||
<Tabs items={tabs} className={styles.actions} />
|
||||
</Permission>
|
||||
{type !== 'algorithm' && (
|
||||
<Web3Feedback
|
||||
isBalanceSufficient={isBalanceSufficient}
|
||||
isAssetNetwork={isAssetNetwork}
|
||||
/>
|
||||
)}
|
||||
<Web3Feedback
|
||||
isBalanceSufficient={isBalanceSufficient}
|
||||
isAssetNetwork={isAssetNetwork}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import AssetType from '../../atoms/AssetType'
|
||||
import styles from './MetaMain.module.css'
|
||||
|
||||
export default function MetaMain(): ReactElement {
|
||||
const { ddo, owner, type } = useAsset()
|
||||
const { ddo, owner, type, isAssetNetwork } = useAsset()
|
||||
const { networkId, web3ProviderInfo } = useWeb3()
|
||||
const isCompute = Boolean(ddo?.findServiceByType('compute'))
|
||||
const accessType = isCompute ? 'compute' : 'access'
|
||||
@ -35,7 +35,7 @@ export default function MetaMain(): ReactElement {
|
||||
{`${ddo?.dataTokenInfo.name} — ${ddo?.dataTokenInfo.symbol}`}
|
||||
</ExplorerLink>
|
||||
|
||||
{web3ProviderInfo?.name === 'MetaMask' && (
|
||||
{web3ProviderInfo?.name === 'MetaMask' && isAssetNetwork && (
|
||||
<span className={styles.addWrap}>
|
||||
<AddToken
|
||||
address={ddo?.dataTokenInfo.address}
|
||||
|
@ -46,7 +46,7 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
|
||||
const content = data.purgatory.edges[0].node.childContentJson.asset
|
||||
const { debug } = useUserPreferences()
|
||||
const { accountId } = useWeb3()
|
||||
const { owner, isInPurgatory, purgatoryData } = useAsset()
|
||||
const { owner, isInPurgatory, purgatoryData, isAssetNetwork } = useAsset()
|
||||
const [showPricing, setShowPricing] = useState(false)
|
||||
const [showEdit, setShowEdit] = useState<boolean>()
|
||||
const [showEditCompute, setShowEditCompute] = useState<boolean>()
|
||||
@ -110,7 +110,7 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
|
||||
|
||||
<MetaSecondary />
|
||||
|
||||
{isOwner && (
|
||||
{isOwner && isAssetNetwork && (
|
||||
<div className={styles.ownerActions}>
|
||||
<Button style="text" size="small" onClick={handleEditButton}>
|
||||
Edit Metadata
|
||||
|
@ -56,7 +56,6 @@ function TabContent({
|
||||
) : (
|
||||
<MetadataAlgorithmPreview values={values} />
|
||||
)}
|
||||
|
||||
<Web3Feedback />
|
||||
</div>
|
||||
</aside>
|
||||
|
@ -228,7 +228,6 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
||||
// -----------------------------------
|
||||
useEffect(() => {
|
||||
if (!networkId) return
|
||||
|
||||
const networkData = getNetworkDataById(networksList, networkId)
|
||||
setNetworkData(networkData)
|
||||
Logger.log(
|
||||
|
@ -19,6 +19,16 @@ export interface NetworkObject {
|
||||
urlList: string[]
|
||||
}
|
||||
|
||||
export function getNetworkConfigObject(node: any): NetworkObject {
|
||||
const networkConfig = {
|
||||
name: node.chain,
|
||||
symbol: node.nativeCurrency.symbol,
|
||||
chainId: node.chainId,
|
||||
urlList: [node.providerUri]
|
||||
}
|
||||
return networkConfig
|
||||
}
|
||||
|
||||
export function accountTruncate(account: string): string {
|
||||
if (!account) return
|
||||
const middle = account.substring(6, 38)
|
||||
@ -64,6 +74,7 @@ export function getNetworkDataById(
|
||||
data: { node: EthereumListsChain }[],
|
||||
networkId: number
|
||||
): EthereumListsChain {
|
||||
if (!networkId) return
|
||||
const networkData = data.filter(
|
||||
({ node }: { node: EthereumListsChain }) => node.chainId === networkId
|
||||
)
|
||||
@ -71,34 +82,48 @@ export function getNetworkDataById(
|
||||
return networkData[0]?.node
|
||||
}
|
||||
|
||||
export function addCustomNetwork(
|
||||
export async function addCustomNetwork(
|
||||
web3Provider: any,
|
||||
network: NetworkObject
|
||||
): void {
|
||||
): Promise<void> {
|
||||
const newNewtworkData = {
|
||||
chainId: `0x${network.chainId.toString(16)}`,
|
||||
chainName: network.name,
|
||||
rpcUrls: network.urlList
|
||||
}
|
||||
web3Provider.request(
|
||||
{
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [newNewtworkData]
|
||||
},
|
||||
(err: string, added: any) => {
|
||||
if (err || 'error' in added) {
|
||||
Logger.error(
|
||||
`Couldn't add ${network.name} (0x${
|
||||
network.chainId
|
||||
}) netowrk to MetaMask, error: ${err || added.error}`
|
||||
)
|
||||
} else {
|
||||
Logger.log(
|
||||
`Added ${network.name} (0x${network.chainId}) network to MetaMask`
|
||||
)
|
||||
}
|
||||
try {
|
||||
await web3Provider.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [{ chainId: newNewtworkData.chainId }]
|
||||
})
|
||||
} catch (switchError) {
|
||||
if (switchError.code === 4902) {
|
||||
web3Provider.request(
|
||||
{
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [newNewtworkData]
|
||||
},
|
||||
(err: string, added: any) => {
|
||||
if (err || 'error' in added) {
|
||||
Logger.error(
|
||||
`Couldn't add ${network.name} (0x${
|
||||
network.chainId
|
||||
}) netowrk to MetaMask, error: ${err || added.error}`
|
||||
)
|
||||
} else {
|
||||
Logger.log(
|
||||
`Added ${network.name} (0x${network.chainId}) network to MetaMask`
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Logger.error(
|
||||
`Couldn't add ${network.name} (0x${network.chainId}) netowrk to MetaMask, error: ${switchError}`
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
Logger.log(`Added ${network.name} (0x${network.chainId}) network to MetaMask`)
|
||||
}
|
||||
|
||||
export async function addTokenToWallet(
|
||||
|
Loading…
Reference in New Issue
Block a user