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).
- 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
### Further help with Nx

View File

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

View File

@ -2,7 +2,10 @@ import { useState, useEffect, useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { ApolloProvider } from '@apollo/client';
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 { Nav } from './components/nav';
import { Header } from './components/header';
@ -23,27 +26,29 @@ function App() {
const client = useMemo(() => createClient(DATA_SOURCES.dataNodeUrl), []);
return (
<ThemeContext.Provider value={theme}>
<TendermintWebsocketProvider>
<ApolloProvider client={client}>
<div
className={`${
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
toggleTheme={toggleTheme}
menuOpen={menuOpen}
setMenuOpen={setMenuOpen}
/>
<Nav menuOpen={menuOpen} />
<Main />
<EnvironmentProvider>
<ThemeContext.Provider value={theme}>
<TendermintWebsocketProvider>
<ApolloProvider client={client}>
<div
className={`${
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
toggleTheme={toggleTheme}
menuOpen={menuOpen}
setMenuOpen={setMenuOpen}
/>
<Nav menuOpen={menuOpen} />
<Main />
</div>
</div>
</div>
</ApolloProvider>
</TendermintWebsocketProvider>
</ThemeContext.Provider>
</ApolloProvider>
</TendermintWebsocketProvider>
</ThemeContext.Provider>
</EnvironmentProvider>
);
}

View File

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

View File

@ -9,6 +9,7 @@ import {
VegaManageDialog,
VegaWalletProvider,
} from '@vegaprotocol/wallet';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
import { VegaWalletConnectButton } from './components/vega-wallet-connect-button';
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
import { Connectors } from './lib/vega-connectors';
@ -37,51 +38,53 @@ function App() {
}, [location]);
return (
<ThemeContext.Provider value={theme}>
<ApolloProvider client={client}>
<VegaWalletProvider>
<AppLoader>
<div className="h-full dark:bg-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
<div className="flex items-stretch border-b-[7px] border-vega-yellow">
<DrawerToggle
onToggle={onToggle}
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 }))
}
<EnvironmentProvider>
<ThemeContext.Provider value={theme}>
<ApolloProvider client={client}>
<VegaWalletProvider>
<AppLoader>
<div className="h-full dark:bg-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
<div className="flex items-stretch border-b-[7px] border-vega-yellow">
<DrawerToggle
onToggle={onToggle}
variant={DRAWER_TOGGLE_VARIANTS.OPEN}
className="xs:py-32 xs:px-16"
/>
<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>
<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>
<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>
</AppLoader>
</VegaWalletProvider>
</ApolloProvider>
</ThemeContext.Provider>
</AppLoader>
</VegaWalletProvider>
</ApolloProvider>
</ThemeContext.Provider>
</EnvironmentProvider>
);
}

View File

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

View File

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

View File

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

View File

@ -20,37 +20,40 @@ import { Web3Provider } from '@vegaprotocol/web3';
import { Connectors } from './lib/web3-connectors';
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
import { VegaWalletProvider } from '@vegaprotocol/wallet';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
function App() {
const sideBar = React.useMemo(() => [<EthWallet />, <VegaWallet />], []);
return (
<GraphQlProvider>
<Router>
<AppStateProvider>
<Web3Provider connectors={Connectors}>
<Web3Connector>
<VegaWalletProvider>
<ContractsProvider>
<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 />
<TemplateSidebar sidebar={sideBar}>
<AppRouter />
</TemplateSidebar>
<AppFooter />
</div>
<VegaWalletDialogs />
<TransactionModal />
</>
</BalanceManager>
</AppLoader>
</ContractsProvider>
</VegaWalletProvider>
</Web3Connector>
</Web3Provider>
</AppStateProvider>
<EnvironmentProvider>
<AppStateProvider>
<Web3Provider connectors={Connectors}>
<Web3Connector>
<VegaWalletProvider>
<ContractsProvider>
<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 />
<TemplateSidebar sidebar={sideBar}>
<AppRouter />
</TemplateSidebar>
<AppFooter />
</div>
<VegaWalletDialogs />
<TransactionModal />
</>
</BalanceManager>
</AppLoader>
</ContractsProvider>
</VegaWalletProvider>
</Web3Connector>
</Web3Provider>
</AppStateProvider>
</EnvironmentProvider>
</Router>
</GraphQlProvider>
);

View File

@ -1,12 +1,13 @@
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 vegaVesting from '../../images/vega_vesting.png';
import { AddTokenButtonLink } from '../add-token-button/add-token-button';
import { Callout } from '@vegaprotocol/ui-toolkit';
export const AddLockedTokenAddress = () => {
const { ADDRESSES } = useEnvironment();
const { t } = useTranslation();
const addSupported = useAddAssetSupported();
return (

View File

@ -2,7 +2,7 @@ import * as Sentry from '@sentry/react';
import { useWeb3React } from '@web3-react/core';
import React from 'react';
import { ADDRESSES } from '../../config';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import {
AppStateActionType,
useAppState,
@ -17,6 +17,7 @@ interface BalanceManagerProps {
}
export const BalanceManager = ({ children }: BalanceManagerProps) => {
const { ADDRESSES } = useEnvironment();
const contracts = useContracts();
const { account } = useWeb3React();
const { appDispatch } = useAppState();
@ -55,7 +56,13 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
};
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.
React.useEffect(() => {

View File

@ -3,7 +3,8 @@ import { useTranslation } from 'react-i18next';
import type { TransactionState } from '../../hooks/transaction-reducer';
import { TxState } from '../../hooks/transaction-reducer';
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 { Loader } from '../loader';
import { StatefulButton } from '../stateful-button';
@ -122,6 +123,7 @@ export const TransactionButtonFooter = ({
txHash,
message,
}: TransactionButtonFooterProps) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
if (message) {
@ -142,7 +144,12 @@ export const TransactionButtonFooter = ({
<div className="transaction-button__footer">
<p className="flex justify-between items-start m-0 text-ui">
<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>
</div>
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import * as Sentry from '@sentry/react';
import { useVegaWallet } from '@vegaprotocol/wallet';
import React from 'react';
import { ADDRESSES } from '../config';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import {
AppStateActionType,
useAppState,
@ -10,6 +10,7 @@ import {
import { useContracts } from '../contexts/contracts/contracts-context';
export const useRefreshBalances = (address: string) => {
const { ADDRESSES } = useEnvironment();
const { appDispatch } = useAppState();
const { keypair } = useVegaWallet();
const { token, staking, vesting } = useContracts();
@ -44,5 +45,13 @@ export const useRefreshBalances = (address: string) => {
} catch (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 type { Networks, Tranche } from '@vegaprotocol/smart-contracts';
import React, { useEffect } from 'react';
import { APP_ENV } from '../config';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { BigNumber } from '../lib/bignumber';
@ -15,8 +15,9 @@ const TRANCHES_URLS: { [N in Networks]: string } = {
};
export function useTranches() {
const { VEGA_ENV } = useEnvironment();
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 {
state: { data, loading, error },
} = useFetch<Tranche[] | null>(url);

View File

@ -1,11 +1,7 @@
import {
Callout,
Intent,
EtherscanLink,
Button,
} from '@vegaprotocol/ui-toolkit';
import { Callout, Intent, Link, Button } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
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 { formatNumber } from '../../lib/format-number';
@ -22,6 +18,7 @@ export const Complete = ({
commitTxHash: string | null;
claimTxHash: string | null;
}) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
return (
@ -38,18 +35,28 @@ export const Complete = ({
{commitTxHash && (
<p style={{ margin: 0 }}>
{t('Link transaction')}:{' '}
<EtherscanLink tx={commitTxHash} text={commitTxHash} />
<Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${commitTxHash}`}
>
{commitTxHash}
</Link>
</p>
)}
{claimTxHash && (
<p>
{t('Claim transaction')}:{' '}
<EtherscanLink tx={claimTxHash} text={claimTxHash} />
<Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${claimTxHash}`}
>
{claimTxHash}
</Link>
</p>
)}
<Link to={Routes.VESTING}>
<RouteLink to={Routes.VESTING}>
<Button className="fill">{t('Check your vesting VEGA tokens')}</Button>
</Link>
</RouteLink>
</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 { ADDRESSES } from '../../config';
const Contracts = () => {
const { ADDRESSES, ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
return (
<section>
<Heading title={'Contracts'} />
@ -10,7 +13,12 @@ const Contracts = () => {
{Object.entries(ADDRESSES).map(([key, value]) => (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<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>
))}
</section>

View File

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

View File

@ -1,12 +1,8 @@
import {
Button,
Callout,
EtherscanLink,
Intent,
} from '@vegaprotocol/ui-toolkit';
import { Button, Callout, Link, Intent } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import React from 'react';
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 type {
@ -35,6 +31,7 @@ export const AssociateTransaction = ({
requiredConfirmations: number;
linking: PartyStakeLinkings_party_stake_linkings | null;
}) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
const remainingConfirmations = React.useMemo(() => {
@ -71,7 +68,12 @@ export const AssociateTransaction = ({
})}
</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 data-testid="transaction-pending-footer">
{t('pendingAssociationText', {
@ -90,11 +92,11 @@ export const AssociateTransaction = ({
{ vegaKey }
)}
completeFooter={
<Link to={Routes.STAKING}>
<RouteLink to={Routes.STAKING}>
<Button className="fill">
{t('Nominate Stake to Validator Node')}
</Button>
</Link>
</RouteLink>
}
pendingHeading={t('Associating Tokens')}
pendingBody={t(

View File

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

View File

@ -1,10 +1,11 @@
import { Button, Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useWeb3React } from '@web3-react/core';
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 { EtherscanLink } from '@vegaprotocol/ui-toolkit';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { Links } from '../../config';
import {
AppStateActionType,
@ -29,14 +30,9 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
<p className="mb-12">{t('stakingDescription3')}</p>
<p className="mb-12">{t('stakingDescription4')}</p>
<p className="mb-12">
<a
className="underline"
href={Links.STAKING_GUIDE}
target="_blank"
rel="noreferrer"
>
<Link href={Links.STAKING_GUIDE} target="_blank">
{t('readMoreStaking')}
</a>
</Link>
</p>
</section>
@ -65,6 +61,7 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
};
export const StakingStepConnectWallets = () => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
const { account } = useWeb3React();
const { keypair } = useVegaWallet();
@ -75,7 +72,12 @@ export const StakingStepConnectWallets = () => {
<Callout intent={Intent.Success} iconName="tick" title={'Connected'}>
<p>
{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>
{t('stakingVegaWalletConnected', {
@ -94,7 +96,7 @@ export const StakingStepConnectWallets = () => {
components={{
vegaWalletLink: (
// 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) })}
>
<p>
<Link to="/staking/associate">
<RouteLink to="/staking/associate">
<Button data-testid="associate-more-tokens-btn">
{t('stakingAssociateMoreButton')}
</Button>
</Link>
</RouteLink>
</p>
<Link to="/staking/disassociate">
<RouteLink to="/staking/disassociate">
<Button data-testid="disassociate-tokens-btn">
{t('stakingDisassociateButton')}
</Button>
</Link>
</RouteLink>
</Callout>
);
}
@ -189,11 +191,11 @@ export const StakingStepAssociate = ({
return (
<>
<p>{t('stakingStep2Text')}</p>
<Link to="/staking/associate">
<RouteLink to="/staking/associate">
<Button data-testid="associate-tokens-btn">
{t('associateButton')}
</Button>
</Link>
</RouteLink>
</>
);
};

View File

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

View File

@ -1,6 +1,10 @@
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 { TrancheLabel } from './tranche-label';
@ -9,7 +13,7 @@ let props: TrancheLabelProps;
beforeEach(() => {
props = {
chainId: EthereumChainIds.Mainnet,
contract: ADDRESSES.vestingAddress,
contract: EnvironmentConfig[Networks.MAINNET].vestingAddress,
id: 5,
};
});

View File

@ -1,5 +1,6 @@
import { ADDRESSES, EthereumChainIds } from '../../config';
import type { EthereumChainId } from '../../config';
import type { EthereumChainId } from '@vegaprotocol/smart-contracts';
import { EthereumChainIds } from '@vegaprotocol/smart-contracts';
import { useEnvironment } from '@vegaprotocol/react-helpers';
const TRANCHE_LABELS: Record<number, string[]> = {
'5': ['Coinlist Option 1', 'Community Whitelist'],
@ -26,6 +27,7 @@ export interface TrancheLabelProps {
* @param id The tranche ID on this contract
*/
export const TrancheLabel = ({ contract, chainId, id }: TrancheLabelProps) => {
const { ADDRESSES } = useEnvironment();
// Only mainnet tranches on the known vesting contract have useful name
if (
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 React from 'react';
import { useTranslation } from 'react-i18next';
@ -6,9 +9,8 @@ import { useParams } from 'react-router';
import { Navigate } from 'react-router-dom';
import { useOutletContext } from 'react-router-dom';
import { EtherscanLink } from '@vegaprotocol/ui-toolkit';
import type { EthereumChainId } from '../../config';
import { ADDRESSES } from '../../config';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { BigNumber } from '../../lib/bignumber';
import { formatNumber } from '../../lib/format-number';
import { TrancheItem } from '../redemption/tranche-item';
@ -27,6 +29,7 @@ const TrancheProgressContents = ({
export const Tranche = () => {
const tranches = useOutletContext<ITranche[]>();
const { ADDRESSES, ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
const { trancheId } = useParams<{ trancheId: string }>();
const { chainId } = useWeb3React();
@ -81,7 +84,12 @@ export const Tranche = () => {
const locked = user.remaining_tokens.times(lockedData?.locked || 0);
return (
<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>
<span>{t('Locked')}</span>
<span>{t('Unlocked')}</span>

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import {
VegaManageDialog,
VegaWalletProvider,
} from '@vegaprotocol/wallet';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
import { Connectors } from '../lib/vega-connectors';
import { useMemo, useState } from 'react';
import { createClient } from '../lib/apollo-client';
@ -25,63 +26,68 @@ function VegaTradingApp({ Component, pageProps }: AppProps) {
const [theme, toggleTheme] = useThemeSwitcher();
return (
<ThemeContext.Provider value={theme}>
<ApolloProvider client={client}>
<VegaWalletProvider>
<AppLoader>
<Head>
<link
rel="preload"
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
<title>{t('Welcome to Vega trading!')}</title>
<link
rel="icon"
type="image/x-icon"
href="https://static.vega.xyz/favicon.ico"
/>
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
</Head>
<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]">
<div className="flex items-stretch border-b-[7px] border-vega-yellow">
<Navbar />
<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" />
<EnvironmentProvider>
<ThemeContext.Provider value={theme}>
<ApolloProvider client={client}>
<VegaWalletProvider>
<AppLoader>
<Head>
<link
rel="preload"
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
<title>{t('Welcome to Vega trading!')}</title>
<link
rel="icon"
type="image/x-icon"
href="https://static.vega.xyz/favicon.ico"
/>
<link
rel="stylesheet"
href="https://static.vega.xyz/fonts.css"
/>
</Head>
<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]">
<div className="flex items-stretch border-b-[7px] border-vega-yellow">
<Navbar />
<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>
<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>
<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>
</AppLoader>
</VegaWalletProvider>
</ApolloProvider>
</ThemeContext.Provider>
</AppLoader>
</VegaWalletProvider>
</ApolloProvider>
</ThemeContext.Provider>
</EnvironmentProvider>
);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": 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),
},
allowance: new BigNumber(30),
isFaucetable: true,
};
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,3 +2,4 @@ export * from './use-apply-grid-transaction';
export * from './use-data-provider';
export * from './use-theme-switcher';
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,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": 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 = {
displayName: 'smart-contracts',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
transform: {
'^.+\\.[tj]s$': 'ts-jest',
'^.+\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/libs/smart-contracts',

View File

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

View File

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

View File

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

View File

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

View File

@ -6,6 +6,5 @@ module.exports = {
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/ui-toolkit',
setupFiles: ['./src/setup-test-env.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 './dialog';
export * from './dropdown-menu';
export * from './etherscan-link';
export * from './link';
export * from './form-group';
export * from './icon';
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,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},

View File

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

View File

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

View File

@ -74,7 +74,7 @@ it('Dialog states', () => {
expect(
screen.getByText('Awaiting Ethereum transaction 0/1 confirmations...')
).toBeInTheDocument();
expect(screen.getByTestId('etherscan-link')).toBeInTheDocument();
expect(screen.getByTestId('link')).toBeInTheDocument();
rerender(generateJsx({ status: EthTxStatus.Complete, confirmations: 1 }));
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 { useWeb3React } from '@web3-react/core';
import { useMemo } from 'react';
import { useEnvironment } from '@vegaprotocol/react-helpers';
export const useBridgeContract = () => {
const { VEGA_ENV } = useEnvironment();
const { provider } = useWeb3React();
const contract = useMemo(() => {
if (!provider) {
return null;
}
return new VegaErc20Bridge(
process.env['NX_VEGA_ENV'] as Networks,
provider,
provider?.getSigner()
);
}, [provider]);
return new VegaErc20Bridge(VEGA_ENV, provider, provider?.getSigner());
}, [provider, VEGA_ENV]);
return contract;
};

View File

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

View File

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

View File

@ -10,10 +10,8 @@ import {
formatNumber,
} from '@vegaprotocol/react-helpers';
import { WithdrawalStatus } from '@vegaprotocol/types';
import {
EtherscanLink,
AgGridDynamic as AgGrid,
} from '@vegaprotocol/ui-toolkit';
import { Link, AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { TransactionDialog } from '@vegaprotocol/web3';
import { useCompleteWithdraw } from './use-complete-withdraw';
import type { Withdrawals_party_withdrawals } from './__generated__/Withdrawals';
@ -23,6 +21,7 @@ export interface WithdrawalsTableProps {
}
export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => {
const { ETHERSCAN_URL } = useEnvironment();
const { transaction, submit } = useCompleteWithdraw();
return (
@ -47,6 +46,7 @@ export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => {
headerName="Recipient"
field="details.receiverAddress"
cellRenderer="RecipientCell"
cellRendererParams={{ ethUrl: ETHERSCAN_URL }}
valueFormatter={({ value }: ValueFormatterParams) => {
return truncateByChars(value);
}}
@ -62,7 +62,7 @@ export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => {
headerName="Status"
field="status"
cellRenderer="StatusCell"
cellRendererParams={{ complete: submit }}
cellRendererParams={{ complete: submit, ethUrl: ETHERSCAN_URL }}
/>
</AgGrid>
<TransactionDialog name="withdraw" {...transaction} />
@ -71,16 +71,28 @@ export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => {
};
export interface StatusCellProps extends ICellRendererParams {
ethUrl: string;
complete: (withdrawalId: string) => void;
}
export const StatusCell = ({ value, data, complete }: StatusCellProps) => {
export const StatusCell = ({
ethUrl,
value,
data,
complete,
}: StatusCellProps) => {
if (data.pendingOnForeignChain) {
return (
<div className="flex justify-between gap-8">
{t('Pending')}
{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>
);
@ -92,7 +104,13 @@ export const StatusCell = ({ value, data, complete }: StatusCellProps) => {
{data.txHash ? (
<>
{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;
};
const RecipientCell = ({ value, valueFormatted }: ICellRendererParams) => {
return <EtherscanLink address={value} text={valueFormatted} />;
export interface RecipientCellProps extends ICellRendererParams {
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,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"resolveJsonModule": true,
"esModuleInterop": true
},
"files": [],

View File

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