feat(#175): ui-toolkit links (#453)

* feat: add enviromnemt provider to the ui-toolkit

* chore: replace etherscan links

* chore: wrap trading app into environment provider

* chore: move env provider to react-helpers and wrap every app

* chore: remove more env variables from libs and replace them with the env hook

* fix: lint

* fix: update readme with correct formatting command

* fix: warnings for web3 hook

* fix: wrap warning in conditional, print message only when env keys are missing

* fix: incorrect condition on deposit manager fauceting param

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>

* fix: cleanup token app ethereum config

* chore: add better error handling to the useEnvironment hook

* fix: lint

* fix: formatting

* fix: more lint

* fix: throw error if required env variables are missing

* fix: remove default eth chain id

* fix: add back etherscan testid to withdrawals links

* fix: imports

* fix: try using babel jest for smart contracts test transpilation

* fix: uniform ts syntax

* chore: set resolveJsonModule in base tsconfig

* fix: add missing data-ids for etherscan links

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
botond 2022-06-01 01:30:02 +01:00 committed by GitHub
parent bf07dac445
commit 3a27172e04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 751 additions and 533 deletions

View File

@ -96,7 +96,7 @@ To run tests locally using your own wallets you can add the following environmen
In CI linting, formatting and also run. These checks can be seen in the [CI workflow file](.github/workflows//test.yml). In CI linting, formatting and also run. These checks can be seen in the [CI workflow file](.github/workflows//test.yml).
- To fix linting errors locally run `yarn nx lint --fix` - To fix linting errors locally run `yarn nx lint --fix`
- To fix formatting errors local run `yarn nx format` - To fix formatting errors local run `yarn nx format:write`
- For either command you may use `--all` to run across the entire repository - For either command you may use `--all` to run across the entire repository
### Further help with Nx ### Further help with Nx

View File

@ -10,7 +10,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -2,7 +2,10 @@ import { useState, useEffect, useMemo } from 'react';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { ApolloProvider } from '@apollo/client'; import { ApolloProvider } from '@apollo/client';
import { ThemeContext } from '@vegaprotocol/react-helpers'; import { ThemeContext } from '@vegaprotocol/react-helpers';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers'; import {
useThemeSwitcher,
EnvironmentProvider,
} from '@vegaprotocol/react-helpers';
import { createClient } from './lib/apollo-client'; import { createClient } from './lib/apollo-client';
import { Nav } from './components/nav'; import { Nav } from './components/nav';
import { Header } from './components/header'; import { Header } from './components/header';
@ -23,27 +26,29 @@ function App() {
const client = useMemo(() => createClient(DATA_SOURCES.dataNodeUrl), []); const client = useMemo(() => createClient(DATA_SOURCES.dataNodeUrl), []);
return ( return (
<ThemeContext.Provider value={theme}> <EnvironmentProvider>
<TendermintWebsocketProvider> <ThemeContext.Provider value={theme}>
<ApolloProvider client={client}> <TendermintWebsocketProvider>
<div <ApolloProvider client={client}>
className={`${ <div
menuOpen && 'h-[100vh] overflow-hidden' className={`${
} antialiased m-0 bg-white dark:bg-black text-black dark:text-white`} menuOpen && 'h-[100vh] overflow-hidden'
> } antialiased m-0 bg-white dark:bg-black text-black dark:text-white`}
<div className="min-h-[100vh] max-w-[1300px] grid grid-rows-[repeat(2,_auto)_1fr] grid-cols-[1fr] md:grid-rows-[auto_minmax(700px,_1fr)] md:grid-cols-[300px_1fr] border-black dark:border-white lg:border-l-1 lg:border-r-1 mx-auto"> >
<Header <div className="min-h-[100vh] max-w-[1300px] grid grid-rows-[repeat(2,_auto)_1fr] grid-cols-[1fr] md:grid-rows-[auto_minmax(700px,_1fr)] md:grid-cols-[300px_1fr] border-black dark:border-white lg:border-l-1 lg:border-r-1 mx-auto">
toggleTheme={toggleTheme} <Header
menuOpen={menuOpen} toggleTheme={toggleTheme}
setMenuOpen={setMenuOpen} menuOpen={menuOpen}
/> setMenuOpen={setMenuOpen}
<Nav menuOpen={menuOpen} /> />
<Main /> <Nav menuOpen={menuOpen} />
<Main />
</div>
</div> </div>
</div> </ApolloProvider>
</ApolloProvider> </TendermintWebsocketProvider>
</TendermintWebsocketProvider> </ThemeContext.Provider>
</ThemeContext.Provider> </EnvironmentProvider>
); );
} }

View File

@ -11,8 +11,7 @@
"noPropertyAccessFromIndexSignature": false, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"lib": ["es5", "es6", "dom", "dom.iterable"], "lib": ["es5", "es6", "dom", "dom.iterable"]
"resolveJsonModule": true
}, },
"files": [], "files": [],
"include": [], "include": [],

View File

@ -9,6 +9,7 @@ import {
VegaManageDialog, VegaManageDialog,
VegaWalletProvider, VegaWalletProvider,
} from '@vegaprotocol/wallet'; } from '@vegaprotocol/wallet';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
import { VegaWalletConnectButton } from './components/vega-wallet-connect-button'; import { VegaWalletConnectButton } from './components/vega-wallet-connect-button';
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit'; import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
import { Connectors } from './lib/vega-connectors'; import { Connectors } from './lib/vega-connectors';
@ -37,51 +38,53 @@ function App() {
}, [location]); }, [location]);
return ( return (
<ThemeContext.Provider value={theme}> <EnvironmentProvider>
<ApolloProvider client={client}> <ThemeContext.Provider value={theme}>
<VegaWalletProvider> <ApolloProvider client={client}>
<AppLoader> <VegaWalletProvider>
<div className="h-full dark:bg-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]"> <AppLoader>
<div className="flex items-stretch border-b-[7px] border-vega-yellow"> <div className="h-full dark:bg-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
<DrawerToggle <div className="flex items-stretch border-b-[7px] border-vega-yellow">
onToggle={onToggle} <DrawerToggle
variant={DRAWER_TOGGLE_VARIANTS.OPEN} onToggle={onToggle}
className="xs:py-32 xs:px-16" variant={DRAWER_TOGGLE_VARIANTS.OPEN}
/> className="xs:py-32 xs:px-16"
<div className="flex items-center gap-4 ml-auto mr-8">
<VegaWalletConnectButton
setConnectDialog={(open) =>
setVegaWallet((x) => ({ ...x, connect: open }))
}
setManageDialog={(open) =>
setVegaWallet((x) => ({ ...x, manage: open }))
}
/> />
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
<div className="flex items-center gap-4 ml-auto mr-8">
<VegaWalletConnectButton
setConnectDialog={(open) =>
setVegaWallet((x) => ({ ...x, connect: open }))
}
setManageDialog={(open) =>
setVegaWallet((x) => ({ ...x, manage: open }))
}
/>
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
</div>
</div> </div>
<Main isMenuOpen={menuOpen} onToggle={onToggle} />
<VegaConnectDialog
connectors={Connectors}
dialogOpen={vegaWallet.connect}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, connect: open }))
}
/>
<VegaManageDialog
dialogOpen={vegaWallet.manage}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, manage: open }))
}
/>
</div> </div>
</AppLoader>
<Main isMenuOpen={menuOpen} onToggle={onToggle} /> </VegaWalletProvider>
</ApolloProvider>
<VegaConnectDialog </ThemeContext.Provider>
connectors={Connectors} </EnvironmentProvider>
dialogOpen={vegaWallet.connect}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, connect: open }))
}
/>
<VegaManageDialog
dialogOpen={vegaWallet.manage}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, manage: open }))
}
/>
</div>
</AppLoader>
</VegaWalletProvider>
</ApolloProvider>
</ThemeContext.Provider>
); );
} }

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -3,6 +3,7 @@ import { DATA_SOURCES } from './config';
import { Header } from './components/header'; import { Header } from './components/header';
import { StatsManager } from '@vegaprotocol/network-stats'; import { StatsManager } from '@vegaprotocol/network-stats';
import { ThemeContext } from '@vegaprotocol/react-helpers'; import { ThemeContext } from '@vegaprotocol/react-helpers';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers'; import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
const envName = DATA_SOURCES.envName; const envName = DATA_SOURCES.envName;
@ -14,19 +15,21 @@ function App() {
const [theme, toggleTheme] = useThemeSwitcher(); const [theme, toggleTheme] = useThemeSwitcher();
return ( return (
<ThemeContext.Provider value={theme}> <EnvironmentProvider>
<div className="w-screen min-h-screen grid pb-24 bg-white text-black-95 dark:bg-black dark:text-white-80"> <ThemeContext.Provider value={theme}>
<div className="layout-grid w-screen justify-self-center"> <div className="w-screen min-h-screen grid pb-24 bg-white text-black-95 dark:bg-black dark:text-white-80">
<Header toggleTheme={toggleTheme} /> <div className="layout-grid w-screen justify-self-center">
<StatsManager <Header toggleTheme={toggleTheme} />
envName={envName} <StatsManager
statsEndpoint={statsEndpoint} envName={envName}
nodesEndpoint={nodesEndpoint} statsEndpoint={statsEndpoint}
className="max-w-3xl px-24" nodesEndpoint={nodesEndpoint}
/> className="max-w-3xl px-24"
/>
</div>
</div> </div>
</div> </ThemeContext.Provider>
</ThemeContext.Provider> </EnvironmentProvider>
); );
} }

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -20,37 +20,40 @@ import { Web3Provider } from '@vegaprotocol/web3';
import { Connectors } from './lib/web3-connectors'; import { Connectors } from './lib/web3-connectors';
import { VegaWalletDialogs } from './components/vega-wallet-dialogs'; import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
import { VegaWalletProvider } from '@vegaprotocol/wallet'; import { VegaWalletProvider } from '@vegaprotocol/wallet';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
function App() { function App() {
const sideBar = React.useMemo(() => [<EthWallet />, <VegaWallet />], []); const sideBar = React.useMemo(() => [<EthWallet />, <VegaWallet />], []);
return ( return (
<GraphQlProvider> <GraphQlProvider>
<Router> <Router>
<AppStateProvider> <EnvironmentProvider>
<Web3Provider connectors={Connectors}> <AppStateProvider>
<Web3Connector> <Web3Provider connectors={Connectors}>
<VegaWalletProvider> <Web3Connector>
<ContractsProvider> <VegaWalletProvider>
<AppLoader> <ContractsProvider>
<BalanceManager> <AppLoader>
<> <BalanceManager>
<div className="app dark max-w-[1300px] mx-auto my-0 grid grid-rows-[min-content_1fr_min-content] min-h-full lg:border-l-1 lg:border-r-1 lg:border-white font-sans text-body lg:text-body-large text-white-80"> <>
<AppBanner /> <div className="app dark max-w-[1300px] mx-auto my-0 grid grid-rows-[min-content_1fr_min-content] min-h-full lg:border-l-1 lg:border-r-1 lg:border-white font-sans text-body lg:text-body-large text-white-80">
<TemplateSidebar sidebar={sideBar}> <AppBanner />
<AppRouter /> <TemplateSidebar sidebar={sideBar}>
</TemplateSidebar> <AppRouter />
<AppFooter /> </TemplateSidebar>
</div> <AppFooter />
<VegaWalletDialogs /> </div>
<TransactionModal /> <VegaWalletDialogs />
</> <TransactionModal />
</BalanceManager> </>
</AppLoader> </BalanceManager>
</ContractsProvider> </AppLoader>
</VegaWalletProvider> </ContractsProvider>
</Web3Connector> </VegaWalletProvider>
</Web3Provider> </Web3Connector>
</AppStateProvider> </Web3Provider>
</AppStateProvider>
</EnvironmentProvider>
</Router> </Router>
</GraphQlProvider> </GraphQlProvider>
); );

View File

@ -1,12 +1,13 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ADDRESSES } from '../../config'; import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useAddAssetSupported } from '../../hooks/use-add-asset-to-wallet'; import { useAddAssetSupported } from '../../hooks/use-add-asset-to-wallet';
import vegaVesting from '../../images/vega_vesting.png'; import vegaVesting from '../../images/vega_vesting.png';
import { AddTokenButtonLink } from '../add-token-button/add-token-button'; import { AddTokenButtonLink } from '../add-token-button/add-token-button';
import { Callout } from '@vegaprotocol/ui-toolkit'; import { Callout } from '@vegaprotocol/ui-toolkit';
export const AddLockedTokenAddress = () => { export const AddLockedTokenAddress = () => {
const { ADDRESSES } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const addSupported = useAddAssetSupported(); const addSupported = useAddAssetSupported();
return ( return (

View File

@ -2,7 +2,7 @@ import * as Sentry from '@sentry/react';
import { useWeb3React } from '@web3-react/core'; import { useWeb3React } from '@web3-react/core';
import React from 'react'; import React from 'react';
import { ADDRESSES } from '../../config'; import { useEnvironment } from '@vegaprotocol/react-helpers';
import { import {
AppStateActionType, AppStateActionType,
useAppState, useAppState,
@ -17,6 +17,7 @@ interface BalanceManagerProps {
} }
export const BalanceManager = ({ children }: BalanceManagerProps) => { export const BalanceManager = ({ children }: BalanceManagerProps) => {
const { ADDRESSES } = useEnvironment();
const contracts = useContracts(); const contracts = useContracts();
const { account } = useWeb3React(); const { account } = useWeb3React();
const { appDispatch } = useAppState(); const { appDispatch } = useAppState();
@ -55,7 +56,13 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
}; };
updateBalances(); updateBalances();
}, [appDispatch, contracts?.token, contracts?.vesting, account]); }, [
appDispatch,
contracts?.token,
contracts?.vesting,
account,
ADDRESSES.stakingBridge,
]);
// This use effect hook is very expensive and is kept separate to prevent expensive reloading of data. // This use effect hook is very expensive and is kept separate to prevent expensive reloading of data.
React.useEffect(() => { React.useEffect(() => {

View File

@ -3,7 +3,8 @@ import { useTranslation } from 'react-i18next';
import type { TransactionState } from '../../hooks/transaction-reducer'; import type { TransactionState } from '../../hooks/transaction-reducer';
import { TxState } from '../../hooks/transaction-reducer'; import { TxState } from '../../hooks/transaction-reducer';
import { truncateMiddle } from '../../lib/truncate-middle'; import { truncateMiddle } from '../../lib/truncate-middle';
import { Button, EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Button, Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { Error, HandUp, Tick } from '../icons'; import { Error, HandUp, Tick } from '../icons';
import { Loader } from '../loader'; import { Loader } from '../loader';
import { StatefulButton } from '../stateful-button'; import { StatefulButton } from '../stateful-button';
@ -122,6 +123,7 @@ export const TransactionButtonFooter = ({
txHash, txHash,
message, message,
}: TransactionButtonFooterProps) => { }: TransactionButtonFooterProps) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
if (message) { if (message) {
@ -142,7 +144,12 @@ export const TransactionButtonFooter = ({
<div className="transaction-button__footer"> <div className="transaction-button__footer">
<p className="flex justify-between items-start m-0 text-ui"> <p className="flex justify-between items-start m-0 text-ui">
<span>{t('transaction')}</span> <span>{t('transaction')}</span>
<EtherscanLink text={truncateMiddle(txHash)} tx={txHash} /> <Link
href={`${ETHERSCAN_URL}/tx/${txHash}`}
title={t('View on Etherscan')}
>
{truncateMiddle(txHash)}
</Link>
</p> </p>
</div> </div>
); );

View File

@ -1,6 +1,7 @@
import { Callout, Intent } from '@vegaprotocol/ui-toolkit'; import { Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
export const TransactionComplete = ({ export const TransactionComplete = ({
@ -14,6 +15,7 @@ export const TransactionComplete = ({
footer?: ReactElement | string; footer?: ReactElement | string;
body?: ReactElement | string; body?: ReactElement | string;
}) => { }) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Callout <Callout
@ -23,7 +25,12 @@ export const TransactionComplete = ({
> >
{body && <p data-testid="transaction-complete-body">{body}</p>} {body && <p data-testid="transaction-complete-body">{body}</p>}
<p> <p>
<EtherscanLink tx={hash} /> <Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${hash}`}
>
{hash}
</Link>
</p> </p>
{footer && <p data-testid="transaction-complete-footer">{footer}</p>} {footer && <p data-testid="transaction-complete-footer">{footer}</p>}
</Callout> </Callout>

View File

@ -1,8 +1,8 @@
import { Button, Callout, Intent } from '@vegaprotocol/ui-toolkit'; import { Button, Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import type { Error } from '../icons'; import { useEnvironment } from '@vegaprotocol/react-helpers';
export interface TransactionErrorProps { export interface TransactionErrorProps {
error: Error | null; error: Error | null;
@ -15,6 +15,7 @@ export const TransactionError = ({
hash, hash,
onActionClick, onActionClick,
}: TransactionErrorProps) => { }: TransactionErrorProps) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@ -22,7 +23,12 @@ export const TransactionError = ({
<p>{error ? error.message : t('Something went wrong')}</p> <p>{error ? error.message : t('Something went wrong')}</p>
{hash ? ( {hash ? (
<p> <p>
<EtherscanLink tx={hash} /> <Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${hash}`}
>
{hash}
</Link>
</p> </p>
) : null} ) : null}
<Button onClick={() => onActionClick()}>{t('Try again')}</Button> <Button onClick={() => onActionClick()}>{t('Try again')}</Button>

View File

@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import { Callout } from '@vegaprotocol/ui-toolkit'; import { Callout } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
export const TransactionPending = ({ export const TransactionPending = ({
hash, hash,
@ -18,6 +19,7 @@ export const TransactionPending = ({
footer?: React.ReactElement | string; footer?: React.ReactElement | string;
body?: React.ReactElement | string; body?: React.ReactElement | string;
}) => { }) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const remainingConfirmations = React.useMemo(() => { const remainingConfirmations = React.useMemo(() => {
if (requiredConfirmations) { if (requiredConfirmations) {
@ -38,7 +40,12 @@ export const TransactionPending = ({
<Callout iconName="refresh" title={title}> <Callout iconName="refresh" title={title}>
{body && <p data-testid="transaction-pending-body">{body}</p>} {body && <p data-testid="transaction-pending-body">{body}</p>}
<p> <p>
<EtherscanLink tx={hash} /> <Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${hash}`}
>
{hash}
</Link>
</p> </p>
{footer && <p data-testid="transaction-pending-footer">{footer}</p>} {footer && <p data-testid="transaction-pending-footer">{footer}</p>}
</Callout> </Callout>

View File

@ -1,5 +1,6 @@
import type { TxData } from '@vegaprotocol/smart-contracts'; import type { TxData } from '@vegaprotocol/smart-contracts';
import { Dialog, EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Dialog, Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -28,6 +29,7 @@ const TransactionModalStatus = ({
}) => <span className="flex gap-4 items-center">{children}</span>; }) => <span className="flex gap-4 items-center">{children}</span>;
export const TransactionModal = () => { export const TransactionModal = () => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { transactions } = useContracts(); const { transactions } = useContracts();
const { appState, appDispatch } = useAppState(); const { appState, appDispatch } = useAppState();
@ -76,16 +78,20 @@ export const TransactionModal = () => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{transactions.map((t) => { {transactions.map((transaction) => {
return ( return (
<tr key={t.tx.hash}> <tr key={transaction.tx.hash}>
<TransactionModalTd> <TransactionModalTd>
<EtherscanLink <Link
tx={t.tx.hash} title={t('View transaction on Etherscan')}
text={truncateMiddle(t.tx.hash)} href={`${ETHERSCAN_URL}/tx/${transaction.tx.hash}`}
/> >
{truncateMiddle(transaction.tx.hash)}
</Link>
</TransactionModalTd>
<TransactionModalTd>
{renderStatus(transaction)}
</TransactionModalTd> </TransactionModalTd>
<TransactionModalTd>{renderStatus(t)}</TransactionModalTd>
</tr> </tr>
); );
})} })}

View File

@ -6,7 +6,6 @@ import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { AccountType } from '../../__generated__/globalTypes'; import { AccountType } from '../../__generated__/globalTypes';
import { ADDRESSES } from '../../config';
import noIcon from '../../images/token-no-icon.png'; import noIcon from '../../images/token-no-icon.png';
import vegaBlack from '../../images/vega_black.png'; import vegaBlack from '../../images/vega_black.png';
import { BigNumber } from '../../lib/bignumber'; import { BigNumber } from '../../lib/bignumber';
@ -18,6 +17,7 @@ import type {
DelegationsVariables, DelegationsVariables,
} from './__generated__/Delegations'; } from './__generated__/Delegations';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { useEnvironment } from '@vegaprotocol/react-helpers';
const DELEGATIONS_QUERY = gql` const DELEGATIONS_QUERY = gql`
query Delegations($partyId: ID!) { query Delegations($partyId: ID!) {
@ -60,6 +60,7 @@ const DELEGATIONS_QUERY = gql`
`; `;
export const usePollForDelegations = () => { export const usePollForDelegations = () => {
const { ADDRESSES } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { keypair } = useVegaWallet(); const { keypair } = useVegaWallet();
const client = useApolloClient(); const client = useApolloClient();
@ -227,7 +228,7 @@ export const usePollForDelegations = () => {
clearInterval(interval); clearInterval(interval);
mounted = false; mounted = false;
}; };
}, [client, keypair?.pub, t]); }, [client, keypair?.pub, t, ADDRESSES.vegaTokenAddress]);
return { delegations, currentStakeAvailable, delegatedNodes, accounts }; return { delegations, currentStakeAvailable, delegatedNodes, accounts };
}; };

View File

@ -1,30 +0,0 @@
import type { EthereumChainId } from '@vegaprotocol/smart-contracts';
import {
EnvironmentConfig,
EthereumChainIds,
} from '@vegaprotocol/smart-contracts';
import type { Networks } from './vega';
type VegaContracts = typeof EnvironmentConfig[Networks];
const appChainId = Number(process.env['NX_ETHEREUM_CHAIN_ID'] || 3);
export const APP_ENV = process.env['NX_VEGA_ENV'] as Networks;
const Addresses: Record<number, VegaContracts> = {
1: EnvironmentConfig.MAINNET,
3: EnvironmentConfig[APP_ENV],
};
export type { EthereumChainId };
export { EthereumChainIds };
/** Contract addresses for the different contracts in the VEGA ecosystem */
export const ADDRESSES = Addresses[appChainId];
/**
* The Chain ID the environment is configured for.
* Normally this is 0x3 (Ropsten) for dev and 0x1 (Mainnet) for prod
*/
export const APP_CHAIN_ID = appChainId;

View File

@ -1,5 +1,4 @@
export * from './flags'; export * from './flags';
export * from './ethereum';
export * from './links'; export * from './links';
export * from './network-params'; export * from './network-params';
export * from './vega'; export * from './vega';

View File

@ -1,11 +1,4 @@
export enum Networks { import { Networks } from '@vegaprotocol/smart-contracts';
CUSTOM = 'CUSTOM',
TESTNET = 'TESTNET',
STAGNET = 'STAGNET',
STAGNET2 = 'STAGNET2',
DEVNET = 'DEVNET',
MAINNET = 'MAINNET',
}
interface VegaNode { interface VegaNode {
url: string; url: string;

View File

@ -10,9 +10,9 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
import { useWeb3React } from '@web3-react/core'; import { useWeb3React } from '@web3-react/core';
import uniqBy from 'lodash/uniqBy'; import uniqBy from 'lodash/uniqBy';
import React from 'react'; import React from 'react';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { SplashLoader } from '../../components/splash-loader'; import { SplashLoader } from '../../components/splash-loader';
import { ADDRESSES, APP_ENV } from '../../config';
import type { ContractsContextShape } from './contracts-context'; import type { ContractsContextShape } from './contracts-context';
import { ContractsContext } from './contracts-context'; import { ContractsContext } from './contracts-context';
import { defaultProvider } from '../../lib/web3-connectors'; import { defaultProvider } from '../../lib/web3-connectors';
@ -21,6 +21,7 @@ import { defaultProvider } from '../../lib/web3-connectors';
* Provides Vega Ethereum contract instances to its children. * Provides Vega Ethereum contract instances to its children.
*/ */
export const ContractsProvider = ({ children }: { children: JSX.Element }) => { export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
const { ADDRESSES, VEGA_ENV } = useEnvironment();
const { provider: activeProvider, account } = useWeb3React(); const { provider: activeProvider, account } = useWeb3React();
const [txs, setTxs] = React.useState<TxData[]>([]); const [txs, setTxs] = React.useState<TxData[]>([]);
const [contracts, setContracts] = React.useState<Pick< const [contracts, setContracts] = React.useState<Pick<
@ -53,20 +54,20 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
signer signer
), ),
// @ts-ignore Cant accept JsonRpcProvider provider // @ts-ignore Cant accept JsonRpcProvider provider
staking: new VegaStaking(APP_ENV, provider, signer), staking: new VegaStaking(VEGA_ENV, provider, signer),
// @ts-ignore Cant accept JsonRpcProvider provider // @ts-ignore Cant accept JsonRpcProvider provider
vesting: new VegaVesting(APP_ENV, provider, signer), vesting: new VegaVesting(VEGA_ENV, provider, signer),
// @ts-ignore Cant accept JsonRpcProvider provider // @ts-ignore Cant accept JsonRpcProvider provider
claim: new VegaClaim(APP_ENV, provider, signer), claim: new VegaClaim(VEGA_ENV, provider, signer),
erc20Bridge: new VegaErc20Bridge( erc20Bridge: new VegaErc20Bridge(
APP_ENV, VEGA_ENV,
// @ts-ignore Cant accept JsonRpcProvider provider // @ts-ignore Cant accept JsonRpcProvider provider
provider, provider,
signer signer
), ),
}); });
} }
}, [activeProvider, account]); }, [activeProvider, account, ADDRESSES.vegaTokenAddress, VEGA_ENV]);
React.useEffect(() => { React.useEffect(() => {
if (!contracts) return; if (!contracts) return;

View File

@ -1,9 +1,9 @@
import React from 'react';
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import { useWeb3React } from '@web3-react/core'; import { useWeb3React } from '@web3-react/core';
import { MetaMask } from '@web3-react/metamask'; import { MetaMask } from '@web3-react/metamask';
import React from 'react'; import { useEnvironment } from '@vegaprotocol/react-helpers';
import { Networks } from '@vegaprotocol/smart-contracts';
import { APP_ENV, Networks } from '../config';
export const useAddAssetSupported = () => { export const useAddAssetSupported = () => {
const { connector } = useWeb3React(); const { connector } = useWeb3React();
@ -19,6 +19,7 @@ export const useAddAssetToWallet = (
decimals: number, decimals: number,
image: string image: string
) => { ) => {
const { VEGA_ENV } = useEnvironment();
const { provider } = useWeb3React(); const { provider } = useWeb3React();
const addSupported = useAddAssetSupported(); const addSupported = useAddAssetSupported();
const add = React.useCallback(async () => { const add = React.useCallback(async () => {
@ -35,10 +36,10 @@ export const useAddAssetToWallet = (
address, address,
symbol: `${symbol}${ symbol: `${symbol}${
// Add the environment if not mainnet // Add the environment if not mainnet
APP_ENV === Networks.MAINNET VEGA_ENV === Networks.MAINNET
? '' ? ''
: // Remove NET as VEGA(TESTNET) is too long : // Remove NET as VEGA(TESTNET) is too long
` ${APP_ENV.replace('NET', '')}` ` ${VEGA_ENV.replace('NET', '')}`
}`, }`,
decimals, decimals,
image, image,
@ -48,7 +49,7 @@ export const useAddAssetToWallet = (
} catch (error) { } catch (error) {
Sentry.captureException(error); Sentry.captureException(error);
} }
}, [address, decimals, image, provider, symbol]); }, [address, decimals, image, provider, symbol, VEGA_ENV]);
return React.useMemo(() => { return React.useMemo(() => {
return { return {

View File

@ -2,7 +2,7 @@ import * as Sentry from '@sentry/react';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import React from 'react'; import React from 'react';
import { ADDRESSES } from '../config'; import { useEnvironment } from '@vegaprotocol/react-helpers';
import { import {
AppStateActionType, AppStateActionType,
useAppState, useAppState,
@ -10,6 +10,7 @@ import {
import { useContracts } from '../contexts/contracts/contracts-context'; import { useContracts } from '../contexts/contracts/contracts-context';
export const useRefreshBalances = (address: string) => { export const useRefreshBalances = (address: string) => {
const { ADDRESSES } = useEnvironment();
const { appDispatch } = useAppState(); const { appDispatch } = useAppState();
const { keypair } = useVegaWallet(); const { keypair } = useVegaWallet();
const { token, staking, vesting } = useContracts(); const { token, staking, vesting } = useContracts();
@ -44,5 +45,13 @@ export const useRefreshBalances = (address: string) => {
} catch (err) { } catch (err) {
Sentry.captureException(err); Sentry.captureException(err);
} }
}, [address, appDispatch, keypair?.pub, staking, token, vesting]); }, [
address,
appDispatch,
keypair?.pub,
staking,
token,
vesting,
ADDRESSES.stakingBridge,
]);
}; };

View File

@ -1,7 +1,7 @@
import { useFetch } from '@vegaprotocol/react-helpers'; import { useFetch } from '@vegaprotocol/react-helpers';
import type { Networks, Tranche } from '@vegaprotocol/smart-contracts'; import type { Networks, Tranche } from '@vegaprotocol/smart-contracts';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { APP_ENV } from '../config'; import { useEnvironment } from '@vegaprotocol/react-helpers';
import { BigNumber } from '../lib/bignumber'; import { BigNumber } from '../lib/bignumber';
@ -15,8 +15,9 @@ const TRANCHES_URLS: { [N in Networks]: string } = {
}; };
export function useTranches() { export function useTranches() {
const { VEGA_ENV } = useEnvironment();
const [tranches, setTranches] = React.useState<Tranche[] | null>(null); const [tranches, setTranches] = React.useState<Tranche[] | null>(null);
const url = React.useMemo(() => TRANCHES_URLS[APP_ENV], []); const url = React.useMemo(() => TRANCHES_URLS[VEGA_ENV], [VEGA_ENV]);
const { const {
state: { data, loading, error }, state: { data, loading, error },
} = useFetch<Tranche[] | null>(url); } = useFetch<Tranche[] | null>(url);

View File

@ -1,11 +1,7 @@
import { import { Callout, Intent, Link, Button } from '@vegaprotocol/ui-toolkit';
Callout, import { useEnvironment } from '@vegaprotocol/react-helpers';
Intent,
EtherscanLink,
Button,
} from '@vegaprotocol/ui-toolkit';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom'; import { Link as RouteLink } from 'react-router-dom';
import type { BigNumber } from '../../lib/bignumber'; import type { BigNumber } from '../../lib/bignumber';
import { formatNumber } from '../../lib/format-number'; import { formatNumber } from '../../lib/format-number';
@ -22,6 +18,7 @@ export const Complete = ({
commitTxHash: string | null; commitTxHash: string | null;
claimTxHash: string | null; claimTxHash: string | null;
}) => { }) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@ -38,18 +35,28 @@ export const Complete = ({
{commitTxHash && ( {commitTxHash && (
<p style={{ margin: 0 }}> <p style={{ margin: 0 }}>
{t('Link transaction')}:{' '} {t('Link transaction')}:{' '}
<EtherscanLink tx={commitTxHash} text={commitTxHash} /> <Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${commitTxHash}`}
>
{commitTxHash}
</Link>
</p> </p>
)} )}
{claimTxHash && ( {claimTxHash && (
<p> <p>
{t('Claim transaction')}:{' '} {t('Claim transaction')}:{' '}
<EtherscanLink tx={claimTxHash} text={claimTxHash} /> <Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${claimTxHash}`}
>
{claimTxHash}
</Link>
</p> </p>
)} )}
<Link to={Routes.VESTING}> <RouteLink to={Routes.VESTING}>
<Button className="fill">{t('Check your vesting VEGA tokens')}</Button> <Button className="fill">{t('Check your vesting VEGA tokens')}</Button>
</Link> </RouteLink>
</Callout> </Callout>
); );
}; };

View File

@ -1,8 +1,11 @@
import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { useTranslation } from 'react-i18next';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { Heading } from '../../components/heading'; import { Heading } from '../../components/heading';
import { ADDRESSES } from '../../config';
const Contracts = () => { const Contracts = () => {
const { ADDRESSES, ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
return ( return (
<section> <section>
<Heading title={'Contracts'} /> <Heading title={'Contracts'} />
@ -10,7 +13,12 @@ const Contracts = () => {
{Object.entries(ADDRESSES).map(([key, value]) => ( {Object.entries(ADDRESSES).map(([key, value]) => (
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>{key}:</div> <div>{key}:</div>
<EtherscanLink address={value as string} text={value as string} /> <Link
title={t('View address on Etherscan')}
href={`${ETHERSCAN_URL}/address/${value}`}
>
{value}
</Link>
</div> </div>
))} ))}
</section> </section>

View File

@ -1,13 +1,8 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import { Callout, Link, Intent, Splash } from '@vegaprotocol/ui-toolkit';
Callout, import { useEnvironment } from '@vegaprotocol/react-helpers';
EtherscanLink,
Intent,
Splash,
} from '@vegaprotocol/ui-toolkit';
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import { ADDRESSES } from '../../../config';
import { useTranches } from '../../../hooks/use-tranches'; import { useTranches } from '../../../hooks/use-tranches';
import type { BigNumber } from '../../../lib/bignumber'; import type { BigNumber } from '../../../lib/bignumber';
import { formatNumber } from '../../../lib/format-number'; import { formatNumber } from '../../../lib/format-number';
@ -21,6 +16,7 @@ export const TokenDetails = ({
totalSupply: BigNumber; totalSupply: BigNumber;
totalStaked: BigNumber; totalStaked: BigNumber;
}) => { }) => {
const { ADDRESSES, ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { tranches, loading, error } = useTranches(); const { tranches, loading, error } = useTranches();
@ -45,21 +41,25 @@ export const TokenDetails = ({
<KeyValueTable className={'token-details'}> <KeyValueTable className={'token-details'}>
<KeyValueTableRow> <KeyValueTableRow>
{t('Token address').toUpperCase()} {t('Token address').toUpperCase()}
<EtherscanLink <Link
data-testid="token-address" data-testid="token-address"
address={ADDRESSES.vegaTokenAddress} title={t('View address on Etherscan')}
text={ADDRESSES.vegaTokenAddress}
className="font-mono" className="font-mono"
/> href={`${ETHERSCAN_URL}/address/${ADDRESSES.vegaTokenAddress}`}
>
{ADDRESSES.vegaTokenAddress}
</Link>
</KeyValueTableRow> </KeyValueTableRow>
<KeyValueTableRow> <KeyValueTableRow>
{t('Vesting contract'.toUpperCase())} {t('Vesting contract'.toUpperCase())}
<EtherscanLink <Link
data-testid="token-contract" data-testid="token-contract"
address={ADDRESSES.vestingAddress} title={t('View address on Etherscan')}
text={ADDRESSES.vestingAddress}
className="font-mono" className="font-mono"
/> href={`${ETHERSCAN_URL}/address/${ADDRESSES.vestingAddress}`}
>
{ADDRESSES.vestingAddress}
</Link>
</KeyValueTableRow> </KeyValueTableRow>
<KeyValueTableRow> <KeyValueTableRow>
{t('Total supply').toUpperCase()} {t('Total supply').toUpperCase()}

View File

@ -3,7 +3,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { Link, useParams, useOutletContext } from 'react-router-dom'; import { Link, useParams, useOutletContext } from 'react-router-dom';
import { TransactionCallout } from '../../../components/transaction-callout'; import { TransactionCallout } from '../../../components/transaction-callout';
import { ADDRESSES } from '../../../config'; import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useAppState } from '../../../contexts/app-state/app-state-context'; import { useAppState } from '../../../contexts/app-state/app-state-context';
import { useContracts } from '../../../contexts/contracts/contracts-context'; import { useContracts } from '../../../contexts/contracts/contracts-context';
import { import {
@ -25,6 +25,7 @@ export const RedeemFromTranche = () => {
address: string; address: string;
}>(); }>();
const { vesting } = useContracts(); const { vesting } = useContracts();
const { ADDRESSES } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
appState: { lien, totalVestedBalance, trancheBalances, totalLockedBalance }, appState: { lien, totalVestedBalance, trancheBalances, totalLockedBalance },

View File

@ -1,12 +1,8 @@
import { import { Button, Callout, Link, Intent } from '@vegaprotocol/ui-toolkit';
Button, import { useEnvironment } from '@vegaprotocol/react-helpers';
Callout,
EtherscanLink,
Intent,
} from '@vegaprotocol/ui-toolkit';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom'; import { Link as RouteLink } from 'react-router-dom';
import { TransactionCallout } from '../../../components/transaction-callout'; import { TransactionCallout } from '../../../components/transaction-callout';
import type { import type {
@ -35,6 +31,7 @@ export const AssociateTransaction = ({
requiredConfirmations: number; requiredConfirmations: number;
linking: PartyStakeLinkings_party_stake_linkings | null; linking: PartyStakeLinkings_party_stake_linkings | null;
}) => { }) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const remainingConfirmations = React.useMemo(() => { const remainingConfirmations = React.useMemo(() => {
@ -71,7 +68,12 @@ export const AssociateTransaction = ({
})} })}
</p> </p>
<p> <p>
<EtherscanLink tx={state.txData.hash || ''} /> <Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${state.txData.hash}`}
>
{state.txData.hash}
</Link>
</p> </p>
<p data-testid="transaction-pending-footer"> <p data-testid="transaction-pending-footer">
{t('pendingAssociationText', { {t('pendingAssociationText', {
@ -90,11 +92,11 @@ export const AssociateTransaction = ({
{ vegaKey } { vegaKey }
)} )}
completeFooter={ completeFooter={
<Link to={Routes.STAKING}> <RouteLink to={Routes.STAKING}>
<Button className="fill"> <Button className="fill">
{t('Nominate Stake to Validator Node')} {t('Nominate Stake to Validator Node')}
</Button> </Button>
</Link> </RouteLink>
} }
pendingHeading={t('Associating Tokens')} pendingHeading={t('Associating Tokens')}
pendingBody={t( pendingBody={t(

View File

@ -2,7 +2,6 @@ import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { TokenInput } from '../../../components/token-input'; import { TokenInput } from '../../../components/token-input';
import { ADDRESSES } from '../../../config';
import { import {
AppStateActionType, AppStateActionType,
useAppState, useAppState,
@ -13,6 +12,7 @@ import { useTransaction } from '../../../hooks/use-transaction';
import { BigNumber } from '../../../lib/bignumber'; import { BigNumber } from '../../../lib/bignumber';
import { AssociateInfo } from './associate-info'; import { AssociateInfo } from './associate-info';
import type { VegaKeyExtended } from '@vegaprotocol/wallet'; import type { VegaKeyExtended } from '@vegaprotocol/wallet';
import { useEnvironment } from '@vegaprotocol/react-helpers';
export const WalletAssociate = ({ export const WalletAssociate = ({
perform, perform,
@ -27,6 +27,7 @@ export const WalletAssociate = ({
vegaKey: VegaKeyExtended; vegaKey: VegaKeyExtended;
address: string; address: string;
}) => { }) => {
const { ADDRESSES } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
appDispatch, appDispatch,
@ -56,7 +57,13 @@ export const WalletAssociate = ({
} }
}; };
run(); run();
}, [address, appDispatch, approveState.txState, token]); }, [
address,
appDispatch,
approveState.txState,
token,
ADDRESSES.stakingBridge,
]);
let pageContent = null; let pageContent = null;

View File

@ -1,10 +1,11 @@
import { Button, Callout, Intent } from '@vegaprotocol/ui-toolkit'; import { Button, Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useWeb3React } from '@web3-react/core'; import { useWeb3React } from '@web3-react/core';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom'; import { Link as RouteLink } from 'react-router-dom';
import { BulletHeader } from '../../components/bullet-header'; import { BulletHeader } from '../../components/bullet-header';
import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { Links } from '../../config'; import { Links } from '../../config';
import { import {
AppStateActionType, AppStateActionType,
@ -29,14 +30,9 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
<p className="mb-12">{t('stakingDescription3')}</p> <p className="mb-12">{t('stakingDescription3')}</p>
<p className="mb-12">{t('stakingDescription4')}</p> <p className="mb-12">{t('stakingDescription4')}</p>
<p className="mb-12"> <p className="mb-12">
<a <Link href={Links.STAKING_GUIDE} target="_blank">
className="underline"
href={Links.STAKING_GUIDE}
target="_blank"
rel="noreferrer"
>
{t('readMoreStaking')} {t('readMoreStaking')}
</a> </Link>
</p> </p>
</section> </section>
@ -65,6 +61,7 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
}; };
export const StakingStepConnectWallets = () => { export const StakingStepConnectWallets = () => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { account } = useWeb3React(); const { account } = useWeb3React();
const { keypair } = useVegaWallet(); const { keypair } = useVegaWallet();
@ -75,7 +72,12 @@ export const StakingStepConnectWallets = () => {
<Callout intent={Intent.Success} iconName="tick" title={'Connected'}> <Callout intent={Intent.Success} iconName="tick" title={'Connected'}>
<p> <p>
{t('Connected Ethereum address')}&nbsp; {t('Connected Ethereum address')}&nbsp;
<EtherscanLink address={account} text={account} /> <Link
title={t('View address on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${account}`}
>
{account}
</Link>
</p> </p>
<p> <p>
{t('stakingVegaWalletConnected', { {t('stakingVegaWalletConnected', {
@ -94,7 +96,7 @@ export const StakingStepConnectWallets = () => {
components={{ components={{
vegaWalletLink: ( vegaWalletLink: (
// eslint-disable-next-line jsx-a11y/anchor-has-content // eslint-disable-next-line jsx-a11y/anchor-has-content
<a href={Links.WALLET_GUIDE} target="_blank" rel="noreferrer" /> <Link href={Links.WALLET_GUIDE} target="_blank" />
), ),
}} }}
/> />
@ -171,17 +173,17 @@ export const StakingStepAssociate = ({
title={t('stakingHasAssociated', { tokens: formatNumber(associated) })} title={t('stakingHasAssociated', { tokens: formatNumber(associated) })}
> >
<p> <p>
<Link to="/staking/associate"> <RouteLink to="/staking/associate">
<Button data-testid="associate-more-tokens-btn"> <Button data-testid="associate-more-tokens-btn">
{t('stakingAssociateMoreButton')} {t('stakingAssociateMoreButton')}
</Button> </Button>
</Link> </RouteLink>
</p> </p>
<Link to="/staking/disassociate"> <RouteLink to="/staking/disassociate">
<Button data-testid="disassociate-tokens-btn"> <Button data-testid="disassociate-tokens-btn">
{t('stakingDisassociateButton')} {t('stakingDisassociateButton')}
</Button> </Button>
</Link> </RouteLink>
</Callout> </Callout>
); );
} }
@ -189,11 +191,11 @@ export const StakingStepAssociate = ({
return ( return (
<> <>
<p>{t('stakingStep2Text')}</p> <p>{t('stakingStep2Text')}</p>
<Link to="/staking/associate"> <RouteLink to="/staking/associate">
<Button data-testid="associate-tokens-btn"> <Button data-testid="associate-tokens-btn">
{t('associateButton')} {t('associateButton')}
</Button> </Button>
</Link> </RouteLink>
</> </>
); );
}; };

View File

@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import { BigNumber } from '../../lib/bignumber'; import { BigNumber } from '../../lib/bignumber';
import { formatNumber } from '../../lib/format-number'; import { formatNumber } from '../../lib/format-number';
@ -22,6 +23,7 @@ export const ValidatorTable = ({
stakedTotal, stakedTotal,
stakeThisEpoch, stakeThisEpoch,
}: ValidatorTableProps) => { }: ValidatorTableProps) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const stakePercentage = React.useMemo(() => { const stakePercentage = React.useMemo(() => {
const total = new BigNumber(stakedTotal); const total = new BigNumber(stakedTotal);
@ -56,10 +58,12 @@ export const ValidatorTable = ({
<KeyValueTableRow> <KeyValueTableRow>
<span>{t('ETHEREUM ADDRESS')}</span> <span>{t('ETHEREUM ADDRESS')}</span>
<span> <span>
<EtherscanLink <Link
text={node.ethereumAdddress} title={t('View address on Etherscan')}
address={node.ethereumAdddress} href={`${ETHERSCAN_URL}/address/${node.ethereumAdddress}`}
/> >
{node.ethereumAdddress}
</Link>
</span> </span>
</KeyValueTableRow> </KeyValueTableRow>
<KeyValueTableRow> <KeyValueTableRow>

View File

@ -1,6 +1,10 @@
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import { ADDRESSES, EthereumChainIds } from '../../config'; import {
EthereumChainIds,
EnvironmentConfig,
Networks,
} from '@vegaprotocol/smart-contracts';
import type { TrancheLabelProps } from './tranche-label'; import type { TrancheLabelProps } from './tranche-label';
import { TrancheLabel } from './tranche-label'; import { TrancheLabel } from './tranche-label';
@ -9,7 +13,7 @@ let props: TrancheLabelProps;
beforeEach(() => { beforeEach(() => {
props = { props = {
chainId: EthereumChainIds.Mainnet, chainId: EthereumChainIds.Mainnet,
contract: ADDRESSES.vestingAddress, contract: EnvironmentConfig[Networks.MAINNET].vestingAddress,
id: 5, id: 5,
}; };
}); });

View File

@ -1,5 +1,6 @@
import { ADDRESSES, EthereumChainIds } from '../../config'; import type { EthereumChainId } from '@vegaprotocol/smart-contracts';
import type { EthereumChainId } from '../../config'; import { EthereumChainIds } from '@vegaprotocol/smart-contracts';
import { useEnvironment } from '@vegaprotocol/react-helpers';
const TRANCHE_LABELS: Record<number, string[]> = { const TRANCHE_LABELS: Record<number, string[]> = {
'5': ['Coinlist Option 1', 'Community Whitelist'], '5': ['Coinlist Option 1', 'Community Whitelist'],
@ -26,6 +27,7 @@ export interface TrancheLabelProps {
* @param id The tranche ID on this contract * @param id The tranche ID on this contract
*/ */
export const TrancheLabel = ({ contract, chainId, id }: TrancheLabelProps) => { export const TrancheLabel = ({ contract, chainId, id }: TrancheLabelProps) => {
const { ADDRESSES } = useEnvironment();
// Only mainnet tranches on the known vesting contract have useful name // Only mainnet tranches on the known vesting contract have useful name
if ( if (
chainId && chainId &&

View File

@ -1,4 +1,7 @@
import type { Tranche as ITranche } from '@vegaprotocol/smart-contracts'; import type {
Tranche as ITranche,
EthereumChainId,
} from '@vegaprotocol/smart-contracts';
import { useWeb3React } from '@web3-react/core'; import { useWeb3React } from '@web3-react/core';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -6,9 +9,8 @@ import { useParams } from 'react-router';
import { Navigate } from 'react-router-dom'; import { Navigate } from 'react-router-dom';
import { useOutletContext } from 'react-router-dom'; import { useOutletContext } from 'react-router-dom';
import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import type { EthereumChainId } from '../../config'; import { useEnvironment } from '@vegaprotocol/react-helpers';
import { ADDRESSES } from '../../config';
import { BigNumber } from '../../lib/bignumber'; import { BigNumber } from '../../lib/bignumber';
import { formatNumber } from '../../lib/format-number'; import { formatNumber } from '../../lib/format-number';
import { TrancheItem } from '../redemption/tranche-item'; import { TrancheItem } from '../redemption/tranche-item';
@ -27,6 +29,7 @@ const TrancheProgressContents = ({
export const Tranche = () => { export const Tranche = () => {
const tranches = useOutletContext<ITranche[]>(); const tranches = useOutletContext<ITranche[]>();
const { ADDRESSES, ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { trancheId } = useParams<{ trancheId: string }>(); const { trancheId } = useParams<{ trancheId: string }>();
const { chainId } = useWeb3React(); const { chainId } = useWeb3React();
@ -81,7 +84,12 @@ export const Tranche = () => {
const locked = user.remaining_tokens.times(lockedData?.locked || 0); const locked = user.remaining_tokens.times(lockedData?.locked || 0);
return ( return (
<li className="pb-4" key={i}> <li className="pb-4" key={i}>
<EtherscanLink address={user.address} text={user.address} /> <Link
title={t('View address on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${user.address}`}
>
{user.address}
</Link>
<TrancheProgressContents> <TrancheProgressContents>
<span>{t('Locked')}</span> <span>{t('Locked')}</span>
<span>{t('Unlocked')}</span> <span>{t('Unlocked')}</span>

View File

@ -1,15 +1,14 @@
import { useOutletContext } from 'react-router-dom'; import { useOutletContext } from 'react-router-dom';
import type { Tranche } from '@vegaprotocol/smart-contracts'; import type { Tranche, EthereumChainId } from '@vegaprotocol/smart-contracts';
import { useWeb3React } from '@web3-react/core'; import { useWeb3React } from '@web3-react/core';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import type { EthereumChainId } from '../../config';
import { ADDRESSES } from '../../config';
import { TrancheItem } from '../redemption/tranche-item'; import { TrancheItem } from '../redemption/tranche-item';
import { TrancheLabel } from './tranche-label'; import { TrancheLabel } from './tranche-label';
import { VestingChart } from './vesting-chart'; import { VestingChart } from './vesting-chart';
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
const trancheMinimum = 10; const trancheMinimum = 10;
@ -17,6 +16,7 @@ const shouldShowTranche = (t: Tranche) =>
!t.total_added.isLessThanOrEqualTo(trancheMinimum); !t.total_added.isLessThanOrEqualTo(trancheMinimum);
export const Tranches = () => { export const Tranches = () => {
const { ADDRESSES } = useEnvironment();
const tranches = useOutletContext<Tranche[]>(); const tranches = useOutletContext<Tranche[]>();
const [showAll, setShowAll] = React.useState<boolean>(false); const [showAll, setShowAll] = React.useState<boolean>(false);
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -4,7 +4,8 @@ import orderBy from 'lodash/orderBy';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { Heading } from '../../components/heading'; import { Heading } from '../../components/heading';
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import { SplashLoader } from '../../components/splash-loader'; import { SplashLoader } from '../../components/splash-loader';
@ -99,6 +100,7 @@ interface WithdrawalProps {
} }
export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => { export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const renderStatus = ({ const renderStatus = ({
@ -148,12 +150,12 @@ export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => {
<KeyValueTableRow> <KeyValueTableRow>
{t('toEthereum')} {t('toEthereum')}
<span> <span>
<EtherscanLink <Link
address={withdrawal.details?.receiverAddress as string} title={t('View address on Etherscan')}
text={truncateMiddle( href={`${ETHERSCAN_URL}/tx/${withdrawal.details?.receiverAddress}`}
withdrawal.details?.receiverAddress as string >
)} {truncateMiddle(withdrawal.details?.receiverAddress ?? '')}
/> </Link>
</span> </span>
</KeyValueTableRow> </KeyValueTableRow>
<KeyValueTableRow> <KeyValueTableRow>
@ -169,10 +171,12 @@ export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => {
{t('withdrawalTransaction', { foreignChain: 'Ethereum' })} {t('withdrawalTransaction', { foreignChain: 'Ethereum' })}
<span> <span>
{withdrawal.txHash ? ( {withdrawal.txHash ? (
<EtherscanLink <Link
tx={withdrawal.txHash} title={t('View transaction on Etherscan')}
text={truncateMiddle(withdrawal.txHash)} href={`${ETHERSCAN_URL}/tx/${withdrawal.txHash}`}
/> >
{truncateMiddle(withdrawal.txHash)}
</Link>
) : ( ) : (
'-' '-'
)} )}

View File

@ -10,8 +10,7 @@
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": false, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true
"resolveJsonModule": true
}, },
"files": [], "files": [],
"include": [], "include": [],

View File

@ -11,7 +11,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -7,6 +7,7 @@ import {
VegaManageDialog, VegaManageDialog,
VegaWalletProvider, VegaWalletProvider,
} from '@vegaprotocol/wallet'; } from '@vegaprotocol/wallet';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
import { Connectors } from '../lib/vega-connectors'; import { Connectors } from '../lib/vega-connectors';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { createClient } from '../lib/apollo-client'; import { createClient } from '../lib/apollo-client';
@ -25,63 +26,68 @@ function VegaTradingApp({ Component, pageProps }: AppProps) {
const [theme, toggleTheme] = useThemeSwitcher(); const [theme, toggleTheme] = useThemeSwitcher();
return ( return (
<ThemeContext.Provider value={theme}> <EnvironmentProvider>
<ApolloProvider client={client}> <ThemeContext.Provider value={theme}>
<VegaWalletProvider> <ApolloProvider client={client}>
<AppLoader> <VegaWalletProvider>
<Head> <AppLoader>
<link <Head>
rel="preload" <link
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2" rel="preload"
as="font" href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
type="font/woff2" as="font"
crossOrigin="anonymous" type="font/woff2"
/> crossOrigin="anonymous"
<title>{t('Welcome to Vega trading!')}</title> />
<link <title>{t('Welcome to Vega trading!')}</title>
rel="icon" <link
type="image/x-icon" rel="icon"
href="https://static.vega.xyz/favicon.ico" type="image/x-icon"
/> href="https://static.vega.xyz/favicon.ico"
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" /> />
</Head> <link
<div className="h-full dark:bg-black dark:text-white-60 bg-white relative z-0 text-black-60 grid grid-rows-[min-content,1fr]"> rel="stylesheet"
<div className="flex items-stretch border-b-[7px] border-vega-yellow"> href="https://static.vega.xyz/fonts.css"
<Navbar /> />
<div className="flex items-center gap-4 ml-auto mr-8"> </Head>
<VegaWalletConnectButton <div className="h-full dark:bg-black dark:text-white-60 bg-white relative z-0 text-black-60 grid grid-rows-[min-content,1fr]">
setConnectDialog={(open) => <div className="flex items-stretch border-b-[7px] border-vega-yellow">
setVegaWallet((x) => ({ ...x, connect: open })) <Navbar />
} <div className="flex items-center gap-4 ml-auto mr-8">
setManageDialog={(open) => <VegaWalletConnectButton
setVegaWallet((x) => ({ ...x, manage: open })) setConnectDialog={(open) =>
} setVegaWallet((x) => ({ ...x, connect: open }))
/> }
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" /> setManageDialog={(open) =>
setVegaWallet((x) => ({ ...x, manage: open }))
}
/>
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
</div>
</div> </div>
<main data-testid={pageProps.page}>
{/* @ts-ignore conflict between @types/react and nextjs internal types */}
<Component {...pageProps} />
</main>
<VegaConnectDialog
connectors={Connectors}
dialogOpen={vegaWallet.connect}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, connect: open }))
}
/>
<VegaManageDialog
dialogOpen={vegaWallet.manage}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, manage: open }))
}
/>
</div> </div>
<main data-testid={pageProps.page}> </AppLoader>
{/* @ts-ignore conflict between @types/react and nextjs internal types */} </VegaWalletProvider>
<Component {...pageProps} /> </ApolloProvider>
</main> </ThemeContext.Provider>
<VegaConnectDialog </EnvironmentProvider>
connectors={Connectors}
dialogOpen={vegaWallet.connect}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, connect: open }))
}
/>
<VegaManageDialog
dialogOpen={vegaWallet.manage}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, manage: open }))
}
/>
</div>
</AppLoader>
</VegaWalletProvider>
</ApolloProvider>
</ThemeContext.Provider>
); );
} }

View File

@ -3,7 +3,7 @@ import { gql } from '@apollo/client';
import { PageQueryContainer } from '../../../components/page-query-container'; import { PageQueryContainer } from '../../../components/page-query-container';
import type { DepositPage } from './__generated__/DepositPage'; import type { DepositPage } from './__generated__/DepositPage';
import { DepositManager } from '@vegaprotocol/deposits'; import { DepositManager } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/react-helpers'; import { t, useEnvironment } from '@vegaprotocol/react-helpers';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { Splash } from '@vegaprotocol/ui-toolkit';
import { ASSET_FRAGMENT } from '../../../lib/query-fragments'; import { ASSET_FRAGMENT } from '../../../lib/query-fragments';
@ -28,6 +28,8 @@ export const DepositContainer = ({
ethereumConfig, ethereumConfig,
assetId, assetId,
}: DepositContainerProps) => { }: DepositContainerProps) => {
const { VEGA_ENV } = useEnvironment();
return ( return (
<PageQueryContainer<DepositPage> <PageQueryContainer<DepositPage>
query={DEPOSIT_PAGE_QUERY} query={DEPOSIT_PAGE_QUERY}
@ -46,6 +48,7 @@ export const DepositContainer = ({
requiredConfirmations={ethereumConfig.confirmations} requiredConfirmations={ethereumConfig.confirmations}
assets={data.assets} assets={data.assets}
initialAssetId={assetId} initialAssetId={assetId}
isFaucetable={VEGA_ENV !== 'MAINNET'}
/> />
); );
}} }}

View File

@ -9,7 +9,6 @@
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noEmit": true, "noEmit": true,
"resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"incremental": true "incremental": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -17,7 +17,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
} }

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -1 +0,0 @@
export const FAUCETABLE = process.env['NX_VEGA_ENV'] !== 'MAINNET';

View File

@ -41,6 +41,7 @@ beforeEach(() => {
max: new BigNumber(20), max: new BigNumber(20),
}, },
allowance: new BigNumber(30), allowance: new BigNumber(30),
isFaucetable: true,
}; };
}); });

View File

@ -24,7 +24,6 @@ import { useMemo } from 'react';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form'; import { useForm, useWatch } from 'react-hook-form';
import { DepositLimits } from './deposit-limits'; import { DepositLimits } from './deposit-limits';
import { FAUCETABLE } from '../config';
import type { Asset } from './deposit-manager'; import type { Asset } from './deposit-manager';
interface FormFields { interface FormFields {
@ -51,6 +50,7 @@ export interface DepositFormProps {
max: BigNumber; max: BigNumber;
} | null; } | null;
allowance: BigNumber | undefined; allowance: BigNumber | undefined;
isFaucetable?: boolean;
} }
export const DepositForm = ({ export const DepositForm = ({
@ -63,6 +63,7 @@ export const DepositForm = ({
requestFaucet, requestFaucet,
limits, limits,
allowance, allowance,
isFaucetable,
}: DepositFormProps) => { }: DepositFormProps) => {
const { account } = useWeb3React(); const { account } = useWeb3React();
const { keypair } = useVegaWallet(); const { keypair } = useVegaWallet();
@ -166,7 +167,7 @@ export const DepositForm = ({
{errors.asset.message} {errors.asset.message}
</InputError> </InputError>
)} )}
{FAUCETABLE && selectedAsset && ( {isFaucetable && selectedAsset && (
<UseButton onClick={requestFaucet}> <UseButton onClick={requestFaucet}>
{t(`Get ${selectedAsset.symbol}`)} {t(`Get ${selectedAsset.symbol}`)}
</UseButton> </UseButton>

View File

@ -34,6 +34,7 @@ interface DepositManagerProps {
bridgeAddress: string; bridgeAddress: string;
assets: Asset[]; assets: Asset[];
initialAssetId?: string; initialAssetId?: string;
isFaucetable?: boolean;
} }
export const DepositManager = ({ export const DepositManager = ({
@ -41,6 +42,7 @@ export const DepositManager = ({
bridgeAddress, bridgeAddress,
assets, assets,
initialAssetId, initialAssetId,
isFaucetable,
}: DepositManagerProps) => { }: DepositManagerProps) => {
const [assetId, setAssetId] = useState<string | undefined>(initialAssetId); const [assetId, setAssetId] = useState<string | undefined>(initialAssetId);
@ -54,7 +56,7 @@ export const DepositManager = ({
asset?.source.__typename === 'ERC20' asset?.source.__typename === 'ERC20'
? asset.source.contractAddress ? asset.source.contractAddress
: undefined, : undefined,
process.env['NX_VEGA_ENV'] !== 'MAINNET' isFaucetable
); );
const bridgeContract = useBridgeContract(); const bridgeContract = useBridgeContract();
@ -101,6 +103,7 @@ export const DepositManager = ({
requestFaucet={faucet.perform} requestFaucet={faucet.perform}
limits={limits} limits={limits}
allowance={allowance} allowance={allowance}
isFaucetable={isFaucetable}
/> />
<TransactionDialog {...approve.transaction} name="approve" /> <TransactionDialog {...approve.transaction} name="approve" />
<TransactionDialog {...faucet.transaction} name="faucet" /> <TransactionDialog {...faucet.transaction} name="faucet" />

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -2,3 +2,4 @@ export * from './use-apply-grid-transaction';
export * from './use-data-provider'; export * from './use-data-provider';
export * from './use-theme-switcher'; export * from './use-theme-switcher';
export * from './use-fetch'; export * from './use-fetch';
export * from './use-environment';

View File

@ -0,0 +1,125 @@
import type { ReactNode } from 'react';
import type { Networks } from '@vegaprotocol/smart-contracts';
import { EnvironmentConfig } from '@vegaprotocol/smart-contracts';
import { createContext, useContext } from 'react';
declare global {
interface Window {
_ENV?: RawEnvironment;
}
}
type VegaContracts = typeof EnvironmentConfig[Networks];
type EnvironmentProviderProps = {
definintions?: Partial<RawEnvironment>;
children?: ReactNode;
};
export const ENV_KEYS = [
'VEGA_URL',
'VEGA_ENV',
'ETHEREUM_CHAIN_ID',
'ETHEREUM_PROVIDER_URL',
'ETHERSCAN_URL',
] as const;
type EnvKey = typeof ENV_KEYS[number];
type RawEnvironment = Record<EnvKey, string>;
export type Environment = {
VEGA_URL: string;
VEGA_ENV: Networks;
ETHEREUM_CHAIN_ID: number;
ETHEREUM_PROVIDER_URL: string;
ETHERSCAN_URL: string;
ADDRESSES: VegaContracts;
};
const getBundledEnvironmentValue = (key: EnvKey) => {
switch (key) {
// need to have these hardcoded so on build time we can insert sensible defaults
case 'VEGA_URL':
return process.env['NX_VEGA_URL'];
case 'VEGA_ENV':
return process.env['NX_VEGA_ENV'];
case 'ETHEREUM_CHAIN_ID':
return process.env['NX_ETHEREUM_CHAIN_ID'];
case 'ETHEREUM_PROVIDER_URL':
return process.env['NX_ETHEREUM_PROVIDER_URL'];
case 'ETHERSCAN_URL':
return process.env['NX_ETHERSCAN_URL'];
}
};
const transformValue = (key: EnvKey, value?: string) => {
switch (key) {
case 'VEGA_ENV':
return value as Networks;
case 'ETHEREUM_CHAIN_ID':
return value && Number(value);
default:
return value;
}
};
const getValue = (key: EnvKey, definintions: Partial<RawEnvironment> = {}) => {
if (typeof window === 'undefined') {
return transformValue(
key,
definintions[key] ?? getBundledEnvironmentValue(key)
);
}
return transformValue(
key,
window._ENV?.[key] ?? definintions[key] ?? getBundledEnvironmentValue(key)
);
};
const EnvironmentContext = createContext({} as Environment);
export const EnvironmentProvider = ({
definintions,
children,
}: EnvironmentProviderProps) => {
const environment = ENV_KEYS.reduce(
(acc, key) => ({
...acc,
[key]: getValue(key, definintions),
}),
{} as Environment
);
const missingKeys = Object.keys(environment)
.filter((key) => typeof environment[key as EnvKey] === undefined)
.map((key) => `"${key}"`)
.join(', ');
if (missingKeys.length) {
throw new Error(
`Error setting up the app environment. The following variables are missing from your environment: ${missingKeys}.`
);
}
return (
<EnvironmentContext.Provider
value={{
...environment,
ADDRESSES: EnvironmentConfig[environment['VEGA_ENV']],
}}
>
{children}
</EnvironmentContext.Provider>
);
};
export const useEnvironment = () => {
const context = useContext(EnvironmentContext);
if (context === undefined) {
throw new Error(
'Error running "useEnvironment". No context found, make sure your component is wrapped in an <EnvironmentProvider />.'
);
}
return context;
};

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -1,3 +1,12 @@
{ {
"presets": ["@babel/preset-typescript"] "presets": [
[
"@nrwl/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
} }

View File

@ -1,13 +1,8 @@
module.exports = { module.exports = {
displayName: 'smart-contracts', displayName: 'smart-contracts',
preset: '../../jest.preset.js', preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
transform: { transform: {
'^.+\\.[tj]s$': 'ts-jest', '^.+\\.[tj]sx?$': 'babel-jest',
}, },
moduleFileExtensions: ['ts', 'js', 'html'], moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/libs/smart-contracts', coverageDirectory: '../../coverage/libs/smart-contracts',

View File

@ -1,14 +1,12 @@
{ {
"extends": "../../tsconfig.base.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": false, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"resolveJsonModule": true,
"esModuleInterop": true "esModuleInterop": true
}, },
"files": [], "files": [],

View File

@ -6,7 +6,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -6,6 +6,5 @@ module.exports = {
}, },
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/ui-toolkit', coverageDirectory: '../../coverage/libs/ui-toolkit',
setupFiles: ['./src/setup-test-env.ts'],
setupFilesAfterEnv: ['./src/setup-tests.ts'], setupFilesAfterEnv: ['./src/setup-tests.ts'],
}; };

View File

@ -1,35 +0,0 @@
import { render, screen } from '@testing-library/react';
import { EtherscanLink } from '.';
it('renders a link with the text', () => {
render(<EtherscanLink text="foo" tx="tx" />);
expect(screen.getByText('foo')).toBeInTheDocument();
});
it('renders a link with the tx hash if no text is provided', () => {
render(<EtherscanLink tx="tx" />);
expect(screen.getByText('tx')).toBeInTheDocument();
});
it('renders a link with the address if no text is provided', () => {
render(<EtherscanLink address="address" />);
expect(screen.getByText('address')).toBeInTheDocument();
});
it('links to etherscan address', () => {
const hash = 'hash';
render(<EtherscanLink address={hash} />);
expect(screen.getByTestId('etherscan-link')).toHaveAttribute(
'href',
`${process.env['NX_ETHERSCAN_URL']}/address/${hash}`
);
});
it('links to etherscan transaction', () => {
const hash = 'hash';
render(<EtherscanLink tx={hash} />);
expect(screen.getByTestId('etherscan-link')).toHaveAttribute(
'href',
`${process.env['NX_ETHERSCAN_URL']}/tx/${hash}`
);
});

View File

@ -1,24 +0,0 @@
import type { ComponentStory, ComponentMeta } from '@storybook/react';
import { EtherscanLink } from '.';
export default {
title: 'EtherscanLink',
component: EtherscanLink,
} as ComponentMeta<typeof EtherscanLink>;
const Template: ComponentStory<typeof EtherscanLink> = (args) => (
<EtherscanLink {...args} />
);
export const Transaction = Template.bind({});
Transaction.args = {
tx: 'foo',
text: 'View transaction on Etherscan',
};
export const Address = Template.bind({});
Address.args = {
address: 'foo',
text: 'View transaction on Etherscan',
};

View File

@ -1,65 +0,0 @@
import classNames from 'classnames';
import type { AnchorHTMLAttributes } from 'react';
const ETHERSCAN_URL = process.env['NX_ETHERSCAN_URL'] as string;
interface BaseEtherscanLinkProps
extends AnchorHTMLAttributes<HTMLAnchorElement> {
text?: string;
}
interface EtherscanAddressLinkProps extends BaseEtherscanLinkProps {
address: string;
}
interface EtherscanTransactionLinkProps extends BaseEtherscanLinkProps {
tx: string;
}
type EtherscanLinkProps =
| EtherscanAddressLinkProps
| EtherscanTransactionLinkProps;
/**
* Form an HTML link tag pointing to an appropriate Etherscan page
*/
export const EtherscanLink = ({
text,
className,
...props
}: EtherscanLinkProps) => {
let hash: string;
let txLink: string | null;
const anchorClasses = classNames('underline', className);
if ('tx' in props) {
hash = props.tx;
txLink = `${ETHERSCAN_URL}/tx/${hash}`;
} else if ('address' in props) {
hash = props.address;
txLink = `${ETHERSCAN_URL}/address/${hash}`;
} else {
throw new Error('Must provider either "tx" or "address" prop');
}
const linkText = text ? text : hash;
// Fallback: just render the TX id
if (!txLink) {
return <span>{hash}</span>;
}
return (
<a
data-testid="etherscan-link"
href={txLink}
target="_blank"
rel="noreferrer"
className={anchorClasses}
>
{linkText}
</a>
);
};
EtherscanLink.displayName = 'EtherScanLink';

View File

@ -1 +0,0 @@
export { EtherscanLink } from './etherscan-link';

View File

@ -7,7 +7,7 @@ export * from './card';
export * from './copy-with-tooltip'; export * from './copy-with-tooltip';
export * from './dialog'; export * from './dialog';
export * from './dropdown-menu'; export * from './dropdown-menu';
export * from './etherscan-link'; export * from './link';
export * from './form-group'; export * from './form-group';
export * from './icon'; export * from './icon';
export * from './indicator'; export * from './indicator';

View File

@ -0,0 +1 @@
export { Link } from './link';

View File

@ -0,0 +1,34 @@
import { render, screen } from '@testing-library/react';
import { Link } from '.';
it('renders a link with a text', () => {
render(
<Link href="127.0.0.1" title="Link title">
Link text
</Link>
);
const link = screen.getByRole('link', { name: 'Link title' });
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute('data-testid', 'link');
expect(link).toHaveAttribute('referrerPolicy', 'strict-origin');
expect(link).toHaveAttribute('href', '127.0.0.1');
expect(link).toHaveAttribute('title', 'Link title');
expect(link).toHaveClass('cursor-pointer');
expect(link).toHaveClass('underline');
});
it('renders a link with children elements', () => {
render(
<Link href="127.0.0.1" title="Link title">
<span>Link text</span>
</Link>
);
const link = screen.getByRole('link', { name: 'Link title' });
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute('data-testid', 'link');
expect(link).toHaveAttribute('referrerPolicy', 'strict-origin');
expect(link).toHaveAttribute('href', '127.0.0.1');
expect(link).toHaveAttribute('title', 'Link title');
expect(link).toHaveClass('cursor-pointer');
expect(link).not.toHaveClass('underline');
});

View File

@ -0,0 +1,25 @@
import type { ComponentStory, ComponentMeta } from '@storybook/react';
import { Link } from '.';
import { VegaLogo } from '../vega-logo';
export default {
title: 'Link',
component: Link,
} as ComponentMeta<typeof Link>;
const Template: ComponentStory<typeof Link> = (args) => <Link {...args} />;
export const Text = Template.bind({});
Text.args = {
title: 'Link title',
href: '/',
children: 'View link',
};
export const Element = Template.bind({});
Element.args = {
title: 'Link title',
href: '/',
children: <VegaLogo />,
};

View File

@ -0,0 +1,29 @@
import classNames from 'classnames';
import type { ReactNode, AnchorHTMLAttributes } from 'react';
type LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
children?: ReactNode;
};
/**
* Form an HTML link tag
*/
export const Link = ({ className, children, ...props }: LinkProps) => {
const anchorClassName = classNames(className, 'cursor-pointer', {
underline: typeof children === 'string',
});
return (
<a
role="link"
data-testid="link"
referrerPolicy="strict-origin"
className={anchorClassName}
{...props}
>
{children}
</a>
);
};
Link.displayName = 'Link';

View File

@ -1 +0,0 @@
process.env['NX_ETHERSCAN_URL'] = 'https://etherscan.io';

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -1,5 +1,6 @@
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { EthTxStatus } from '../use-ethereum-transaction'; import { EthTxStatus } from '../use-ethereum-transaction';
const ACTIVE_CLASSES = 'text-black dark:text-white'; const ACTIVE_CLASSES = 'text-black dark:text-white';
@ -31,6 +32,8 @@ export const TxRow = ({
requiredConfirmations, requiredConfirmations,
highlightComplete = true, highlightComplete = true,
}: TxRowProps) => { }: TxRowProps) => {
const { ETHERSCAN_URL } = useEnvironment();
if (status === EthTxStatus.Pending) { if (status === EthTxStatus.Pending) {
return ( return (
<p className={`flex justify-between ${ACTIVE_CLASSES}`}> <p className={`flex justify-between ${ACTIVE_CLASSES}`}>
@ -39,11 +42,13 @@ export const TxRow = ({
`Awaiting Ethereum transaction ${confirmations}/${requiredConfirmations} confirmations...` `Awaiting Ethereum transaction ${confirmations}/${requiredConfirmations} confirmations...`
)} )}
</span> </span>
<EtherscanLink <Link
tx={txHash || ''} href={`${ETHERSCAN_URL}/tx/${txHash}`}
title={t('View transaction on Etherscan')}
className="text-vega-pink dark:text-vega-yellow" className="text-vega-pink dark:text-vega-yellow"
text={t('View on Etherscan')} >
/> {t('View on Etherscan')}
</Link>
</p> </p>
); );
} }
@ -56,11 +61,13 @@ export const TxRow = ({
}`} }`}
> >
<span>{t('Ethereum transaction complete')}</span> <span>{t('Ethereum transaction complete')}</span>
<EtherscanLink <Link
tx={txHash || ''} href={`${ETHERSCAN_URL}/tx/${txHash}`}
title={t('View on Etherscan')}
className="text-vega-pink dark:text-vega-yellow" className="text-vega-pink dark:text-vega-yellow"
text={t('View on Etherscan')} >
/> {t('View transaction on Etherscan')}
</Link>
</p> </p>
); );
} }

View File

@ -74,7 +74,7 @@ it('Dialog states', () => {
expect( expect(
screen.getByText('Awaiting Ethereum transaction 0/1 confirmations...') screen.getByText('Awaiting Ethereum transaction 0/1 confirmations...')
).toBeInTheDocument(); ).toBeInTheDocument();
expect(screen.getByTestId('etherscan-link')).toBeInTheDocument(); expect(screen.getByTestId('link')).toBeInTheDocument();
rerender(generateJsx({ status: EthTxStatus.Complete, confirmations: 1 })); rerender(generateJsx({ status: EthTxStatus.Complete, confirmations: 1 }));
expect(screen.getByText(`${props.name} complete`)).toBeInTheDocument(); expect(screen.getByText(`${props.name} complete`)).toBeInTheDocument();

View File

@ -1,21 +1,18 @@
import type { Networks } from '@vegaprotocol/smart-contracts';
import { VegaErc20Bridge } from '@vegaprotocol/smart-contracts'; import { VegaErc20Bridge } from '@vegaprotocol/smart-contracts';
import { useWeb3React } from '@web3-react/core'; import { useWeb3React } from '@web3-react/core';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useEnvironment } from '@vegaprotocol/react-helpers';
export const useBridgeContract = () => { export const useBridgeContract = () => {
const { VEGA_ENV } = useEnvironment();
const { provider } = useWeb3React(); const { provider } = useWeb3React();
const contract = useMemo(() => { const contract = useMemo(() => {
if (!provider) { if (!provider) {
return null; return null;
} }
return new VegaErc20Bridge( return new VegaErc20Bridge(VEGA_ENV, provider, provider?.getSigner());
process.env['NX_VEGA_ENV'] as Networks, }, [provider, VEGA_ENV]);
provider,
provider?.getSigner()
);
}, [provider]);
return contract; return contract;
}; };

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },

View File

@ -1,10 +1,5 @@
import { import { Link, Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
Dialog, import { useEnvironment } from '@vegaprotocol/react-helpers';
EtherscanLink,
Icon,
Intent,
Loader,
} from '@vegaprotocol/ui-toolkit';
import type { VegaTxState } from '@vegaprotocol/wallet'; import type { VegaTxState } from '@vegaprotocol/wallet';
import { VegaTxStatus } from '@vegaprotocol/wallet'; import { VegaTxStatus } from '@vegaprotocol/wallet';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
@ -28,7 +23,8 @@ export const WithdrawDialog = ({
dialogOpen, dialogOpen,
onDialogChange, onDialogChange,
}: WithdrawDialogProps) => { }: WithdrawDialogProps) => {
const { intent, ...props } = getProps(approval, vegaTx, ethTx); const { ETHERSCAN_URL } = useEnvironment();
const { intent, ...props } = getProps(approval, vegaTx, ethTx, ETHERSCAN_URL);
return ( return (
<Dialog open={dialogOpen} intent={intent} onChange={onDialogChange}> <Dialog open={dialogOpen} intent={intent} onChange={onDialogChange}>
<DialogWrapper {...props} /> <DialogWrapper {...props} />
@ -85,7 +81,8 @@ interface DialogProps {
const getProps = ( const getProps = (
approval: Erc20Approval_erc20WithdrawalApproval | null, approval: Erc20Approval_erc20WithdrawalApproval | null,
vegaTx: VegaTxState, vegaTx: VegaTxState,
ethTx: EthTxState ethTx: EthTxState,
ethUrl: string
) => { ) => {
const vegaTxPropsMap: Record<VegaTxStatus, DialogProps> = { const vegaTxPropsMap: Record<VegaTxStatus, DialogProps> = {
[VegaTxStatus.Default]: { [VegaTxStatus.Default]: {
@ -156,11 +153,13 @@ const getProps = (
`Awaiting Ethereum transaction ${ethTx.confirmations}/1 confirmations...` `Awaiting Ethereum transaction ${ethTx.confirmations}/1 confirmations...`
)} )}
</span> </span>
<EtherscanLink <Link
tx={ethTx.txHash || ''} href={`${ethUrl}/tx/${ethTx.txHash}`}
title={t('View transaction on Etherscan')}
className="text-vega-pink dark:text-vega-yellow" className="text-vega-pink dark:text-vega-yellow"
text={t('View on Etherscan')} >
/> {t('View on Etherscan')}
</Link>
</Step> </Step>
), ),
}, },
@ -171,11 +170,13 @@ const getProps = (
children: ( children: (
<Step> <Step>
<span>{t('Ethereum transaction complete')}</span> <span>{t('Ethereum transaction complete')}</span>
<EtherscanLink <Link
tx={ethTx.txHash || ''} href={`${ethUrl}/tx/${ethTx.txHash}`}
title={t('View transaction on Etherscan')}
className="text-vega-pink dark:text-vega-yellow" className="text-vega-pink dark:text-vega-yellow"
text={t('View on Etherscan')} >
/> {t('View on Etherscan')}
</Link>
</Step> </Step>
), ),
}, },

View File

@ -10,10 +10,8 @@ import {
formatNumber, formatNumber,
} from '@vegaprotocol/react-helpers'; } from '@vegaprotocol/react-helpers';
import { WithdrawalStatus } from '@vegaprotocol/types'; import { WithdrawalStatus } from '@vegaprotocol/types';
import { import { Link, AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
EtherscanLink, import { useEnvironment } from '@vegaprotocol/react-helpers';
AgGridDynamic as AgGrid,
} from '@vegaprotocol/ui-toolkit';
import { TransactionDialog } from '@vegaprotocol/web3'; import { TransactionDialog } from '@vegaprotocol/web3';
import { useCompleteWithdraw } from './use-complete-withdraw'; import { useCompleteWithdraw } from './use-complete-withdraw';
import type { Withdrawals_party_withdrawals } from './__generated__/Withdrawals'; import type { Withdrawals_party_withdrawals } from './__generated__/Withdrawals';
@ -23,6 +21,7 @@ export interface WithdrawalsTableProps {
} }
export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => { export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => {
const { ETHERSCAN_URL } = useEnvironment();
const { transaction, submit } = useCompleteWithdraw(); const { transaction, submit } = useCompleteWithdraw();
return ( return (
@ -47,6 +46,7 @@ export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => {
headerName="Recipient" headerName="Recipient"
field="details.receiverAddress" field="details.receiverAddress"
cellRenderer="RecipientCell" cellRenderer="RecipientCell"
cellRendererParams={{ ethUrl: ETHERSCAN_URL }}
valueFormatter={({ value }: ValueFormatterParams) => { valueFormatter={({ value }: ValueFormatterParams) => {
return truncateByChars(value); return truncateByChars(value);
}} }}
@ -62,7 +62,7 @@ export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => {
headerName="Status" headerName="Status"
field="status" field="status"
cellRenderer="StatusCell" cellRenderer="StatusCell"
cellRendererParams={{ complete: submit }} cellRendererParams={{ complete: submit, ethUrl: ETHERSCAN_URL }}
/> />
</AgGrid> </AgGrid>
<TransactionDialog name="withdraw" {...transaction} /> <TransactionDialog name="withdraw" {...transaction} />
@ -71,16 +71,28 @@ export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => {
}; };
export interface StatusCellProps extends ICellRendererParams { export interface StatusCellProps extends ICellRendererParams {
ethUrl: string;
complete: (withdrawalId: string) => void; complete: (withdrawalId: string) => void;
} }
export const StatusCell = ({ value, data, complete }: StatusCellProps) => { export const StatusCell = ({
ethUrl,
value,
data,
complete,
}: StatusCellProps) => {
if (data.pendingOnForeignChain) { if (data.pendingOnForeignChain) {
return ( return (
<div className="flex justify-between gap-8"> <div className="flex justify-between gap-8">
{t('Pending')} {t('Pending')}
{data.txHash && ( {data.txHash && (
<EtherscanLink tx={data.txHash} text={t('View on Etherscan')} /> <Link
title={t('View transaction on Etherscan')}
href={`${ethUrl}/tx/${data.txHash}`}
data-testid="etherscan-link"
>
{t('View on Etherscan')}
</Link>
)} )}
</div> </div>
); );
@ -92,7 +104,13 @@ export const StatusCell = ({ value, data, complete }: StatusCellProps) => {
{data.txHash ? ( {data.txHash ? (
<> <>
{t('Finalized')} {t('Finalized')}
<EtherscanLink tx={data.txHash} text={t('View on Etherscan')} /> <Link
title={t('View transaction on Etherscan')}
href={`${ethUrl}/tx/${data.txHash}`}
data-testid="etherscan-link"
>
{t('View on Etherscan')}
</Link>
</> </>
) : ( ) : (
<> <>
@ -109,6 +127,22 @@ export const StatusCell = ({ value, data, complete }: StatusCellProps) => {
return value; return value;
}; };
const RecipientCell = ({ value, valueFormatted }: ICellRendererParams) => { export interface RecipientCellProps extends ICellRendererParams {
return <EtherscanLink address={value} text={valueFormatted} />; ethUrl: string;
}
const RecipientCell = ({
ethUrl,
value,
valueFormatted,
}: RecipientCellProps) => {
return (
<Link
title={t('View address on Etherscan')}
href={`${ethUrl}/address/${value}`}
data-testid="etherscan-link"
>
{valueFormatted}
</Link>
);
}; };

View File

@ -10,7 +10,6 @@
"noPropertyAccessFromIndexSignature": false, "noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"resolveJsonModule": true,
"esModuleInterop": true "esModuleInterop": true
}, },
"files": [], "files": [],

View File

@ -14,6 +14,7 @@
"skipLibCheck": true, "skipLibCheck": true,
"skipDefaultLibCheck": true, "skipDefaultLibCheck": true,
"baseUrl": ".", "baseUrl": ".",
"resolveJsonModule": true,
"paths": { "paths": {
"@vegaprotocol/accounts": ["libs/accounts/src/index.ts"], "@vegaprotocol/accounts": ["libs/accounts/src/index.ts"],
"@vegaprotocol/candles-chart": ["libs/candles-chart/src/index.ts"], "@vegaprotocol/candles-chart": ["libs/candles-chart/src/index.ts"],