1
0
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:
Norbi 2021-07-15 18:03:03 +03:00 committed by GitHub
parent 483ce88d42
commit 565c0324f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 156 additions and 52 deletions

View File

@ -78,6 +78,7 @@
color: var(--brand-pink);
box-shadow: none;
cursor: pointer;
min-width: auto;
}
/* Size Modifiers */

View File

@ -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>
)
}

View File

@ -0,0 +1,5 @@
.text {
color: var(--color-secondary);
margin-top: calc(var(--spacer) / 4);
margin-bottom: calc(var(--spacer) / 2);
}

View 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>
</>
)
}

View File

@ -10,7 +10,7 @@
.feedback i {
position: absolute;
left: 0;
top: calc(var(--spacer) / 1.5);
top: calc(var(--spacer) / 1.45);
}
.title {

View File

@ -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
}

View File

@ -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));
}

View File

@ -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

View File

@ -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} />

View File

@ -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)}>

View File

@ -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)}>

View File

@ -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

View File

@ -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>
)}

View File

@ -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."

View File

@ -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}
/>
</>
)
}

View File

@ -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}

View File

@ -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

View File

@ -56,7 +56,6 @@ function TabContent({
) : (
<MetadataAlgorithmPreview values={values} />
)}
<Web3Feedback />
</div>
</aside>

View File

@ -228,7 +228,6 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
// -----------------------------------
useEffect(() => {
if (!networkId) return
const networkData = getNetworkDataById(networksList, networkId)
setNetworkData(networkData)
Logger.log(

View File

@ -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(