chore: 1638 deposit flow (#1928)

This commit is contained in:
Art 2022-11-04 11:47:53 +01:00 committed by GitHub
parent 28b38c3ebf
commit 7e52f25e27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 224 additions and 124 deletions

View File

@ -1,40 +1,22 @@
import { useMemo } from 'react'; import { useState } from 'react';
import { DepositManager } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { enabledAssetsProvider } from '@vegaprotocol/assets'; import { Button } from '@vegaprotocol/ui-toolkit';
import { useDataProvider } from '@vegaprotocol/react-helpers'; import { DepositDialog } from '@vegaprotocol/deposits';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { Web3Container } from '@vegaprotocol/web3';
/** /**
* Fetches data required for the Deposit page * Fetches data required for the Deposit page
*/ */
export const DepositContainer = () => { export const DepositContainer = () => {
const { VEGA_ENV } = useEnvironment(); const [depositDialog, setDepositDialog] = useState(false);
const { pubKey } = useVegaWallet();
const variables = useMemo(() => ({ partyId: pubKey }), [pubKey]);
const { data, loading, error } = useDataProvider({
dataProvider: enabledAssetsProvider,
variables,
skip: !pubKey,
});
return ( return (
<AsyncRenderer data={data} loading={loading} error={error}> <div>
{data && data.length ? ( <DepositDialog
<Web3Container> depositDialog={depositDialog}
<DepositManager setDepositDialog={setDepositDialog}
assets={data}
isFaucetable={VEGA_ENV !== Networks.MAINNET}
/> />
</Web3Container> <Button size="sm" onClick={() => setDepositDialog(true)}>
) : ( {t('Make deposit')}
<Splash> </Button>
<p>{t('No assets on this network')}</p> </div>
</Splash>
)}
</AsyncRenderer>
); );
}; };

View File

@ -7,7 +7,7 @@ import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { AccountManager } from '@vegaprotocol/accounts'; import { AccountManager } from '@vegaprotocol/accounts';
import { DepositDialog } from './deposits-container'; import { DepositDialog } from '@vegaprotocol/deposits';
export const AccountsContainer = () => { export const AccountsContainer = () => {
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();

View File

@ -1,38 +0,0 @@
import { DepositManager } from '@vegaprotocol/deposits';
import { useDataProvider, t } from '@vegaprotocol/react-helpers';
import { enabledAssetsProvider } from '@vegaprotocol/assets';
import { useEnvironment } from '@vegaprotocol/environment';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
/**
* Fetches data required for the Deposit page
*/
export const DepositContainer = () => {
const { VEGA_ENV } = useEnvironment();
const { data, error, loading } = useDataProvider({
dataProvider: enabledAssetsProvider,
});
return (
<AsyncRenderer
data={data}
error={error}
loading={loading}
render={(assets) => {
if (!assets || !assets.length) {
return (
<Splash>
<p>{t('No assets on this network')}</p>
</Splash>
);
}
return (
<DepositManager
assets={assets}
isFaucetable={VEGA_ENV !== 'MAINNET'}
/>
);
}}
/>
);
};

View File

@ -1,8 +1,11 @@
import { t, titlefy } from '@vegaprotocol/react-helpers'; import { enabledAssetsProvider } from '@vegaprotocol/assets';
import { DepositManager } from '@vegaprotocol/deposits';
import { useEnvironment } from '@vegaprotocol/environment';
import { t, titlefy, useDataProvider } from '@vegaprotocol/react-helpers';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import { Web3Container } from '@vegaprotocol/web3'; import { Web3Container } from '@vegaprotocol/web3';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { usePageTitleStore } from '../../../stores'; import { usePageTitleStore } from '../../../stores';
import { DepositContainer } from './deposit-container';
const Deposit = () => { const Deposit = () => {
const { updateTitle } = usePageTitleStore((store) => ({ const { updateTitle } = usePageTitleStore((store) => ({
@ -13,11 +16,36 @@ const Deposit = () => {
updateTitle(titlefy([t('Deposits')])); updateTitle(titlefy([t('Deposits')]));
}, [updateTitle]); }, [updateTitle]);
const { VEGA_ENV } = useEnvironment();
const { data, error, loading } = useDataProvider({
dataProvider: enabledAssetsProvider,
});
return ( return (
<Web3Container> <Web3Container>
<div className="max-w-[420px] p-8 mx-auto"> <div className="max-w-[420px] p-8 mx-auto">
<h1 className="text-2xl mb-4">{t('Deposit')}</h1> <h1 className="text-2xl mb-4">{t('Deposit')}</h1>
<DepositContainer /> <AsyncRenderer
data={data}
error={error}
loading={loading}
render={(assets) => {
if (!assets || !assets.length) {
return (
<Splash>
<p>{t('No assets on this network')}</p>
</Splash>
);
}
return (
<DepositManager
assets={assets}
isFaucetable={VEGA_ENV !== 'MAINNET'}
/>
);
}}
/>
</div> </div>
</Web3Container> </Web3Container>
); );

View File

@ -1,5 +1,5 @@
import { AsyncRenderer, Button, Dialog } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { DepositContainer, DepositsTable } from '@vegaprotocol/deposits'; import { DepositDialog, DepositsTable } from '@vegaprotocol/deposits';
import { useDeposits } from '@vegaprotocol/deposits'; import { useDeposits } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { useState } from 'react'; import { useState } from 'react';
@ -26,28 +26,9 @@ export const DepositsContainer = () => {
/> />
<div className="w-full dark:bg-black bg-white absolute bottom-0 h-auto flex justify-end px-[11px] py-2"> <div className="w-full dark:bg-black bg-white absolute bottom-0 h-auto flex justify-end px-[11px] py-2">
<Button size="sm" onClick={() => setDepositDialog(true)}> <Button size="sm" onClick={() => setDepositDialog(true)}>
Deposit {t('Deposit')}
</Button> </Button>
</div> </div>
</div> </div>
); );
}; };
export interface DepositDialogProps {
assetId?: string;
depositDialog: boolean;
setDepositDialog: (open: boolean) => void;
}
export const DepositDialog = ({
assetId,
depositDialog,
setDepositDialog,
}: DepositDialogProps) => {
return (
<Dialog open={depositDialog} onChange={setDepositDialog}>
<h1 className="text-2xl mb-4">{t('Deposit')}</h1>
<DepositContainer assetId={assetId} />
</Dialog>
);
};

View File

@ -1,8 +1,8 @@
import { DepositContainer } from '@vegaprotocol/deposits';
import { normalizeFormatNumber, t } from '@vegaprotocol/react-helpers'; import { normalizeFormatNumber, t } from '@vegaprotocol/react-helpers';
import { ButtonLink, Dialog } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import React from 'react'; import React from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { DepositDialog } from '@vegaprotocol/deposits';
interface Props { interface Props {
margin: string; margin: string;
@ -43,10 +43,11 @@ export const ValidateMargin = ({
)}`} )}`}
</p> </p>
</div> </div>
<Dialog open={depositDialog} onChange={setDepositDialog}> <DepositDialog
<h1 className="text-2xl mb-4">{t('Deposit')}</h1> depositDialog={depositDialog}
<DepositContainer assetId={id} /> setDepositDialog={setDepositDialog}
</Dialog> assetId={id}
/>
</> </>
); );
}; };

View File

@ -4,11 +4,18 @@ import { Web3Container } from '@vegaprotocol/web3';
import { DepositManager } from './deposit-manager'; import { DepositManager } from './deposit-manager';
import { t, useDataProvider } from '@vegaprotocol/react-helpers'; import { t, useDataProvider } from '@vegaprotocol/react-helpers';
import { enabledAssetsProvider } from '@vegaprotocol/assets'; import { enabledAssetsProvider } from '@vegaprotocol/assets';
import type { DepositDialogStylePropsSetter } from './deposit-dialog';
/** /**
* Fetches data required for the Deposit page * Fetches data required for the Deposit page
*/ */
export const DepositContainer = ({ assetId }: { assetId?: string }) => { export const DepositContainer = ({
assetId,
setDialogStyleProps,
}: {
assetId?: string;
setDialogStyleProps?: DepositDialogStylePropsSetter;
}) => {
const { VEGA_ENV } = useEnvironment(); const { VEGA_ENV } = useEnvironment();
const { data, loading, error } = useDataProvider({ const { data, loading, error } = useDataProvider({
dataProvider: enabledAssetsProvider, dataProvider: enabledAssetsProvider,
@ -22,6 +29,7 @@ export const DepositContainer = ({ assetId }: { assetId?: string }) => {
assetId={assetId} assetId={assetId}
assets={data} assets={data}
isFaucetable={VEGA_ENV !== Networks.MAINNET} isFaucetable={VEGA_ENV !== Networks.MAINNET}
setDialogStyleProps={setDialogStyleProps}
/> />
</Web3Container> </Web3Container>
) : ( ) : (

View File

@ -0,0 +1,54 @@
import { t } from '@vegaprotocol/react-helpers';
import type { Intent } from '@vegaprotocol/ui-toolkit';
import { Dialog } from '@vegaprotocol/ui-toolkit';
import { useCallback, useState } from 'react';
import { DepositContainer } from './deposit-container';
export interface DepositDialogProps {
assetId?: string;
depositDialog: boolean;
setDepositDialog: (open: boolean) => void;
}
export type DepositDialogStyleProps = {
title: string;
icon?: JSX.Element;
intent?: Intent;
};
export type DepositDialogStylePropsSetter = (
props?: DepositDialogStyleProps
) => void;
const DEFAULT_STYLE: DepositDialogStyleProps = {
title: t('Deposit'),
intent: undefined,
icon: undefined,
};
export const DepositDialog = ({
assetId,
depositDialog,
setDepositDialog,
}: DepositDialogProps) => {
const [dialogStyleProps, _setDialogStyleProps] = useState(DEFAULT_STYLE);
const setDialogStyleProps: DepositDialogStylePropsSetter =
useCallback<DepositDialogStylePropsSetter>(
(props) =>
props
? _setDialogStyleProps(props)
: _setDialogStyleProps(DEFAULT_STYLE),
[_setDialogStyleProps]
);
return (
<Dialog
open={depositDialog}
onChange={setDepositDialog}
{...dialogStyleProps}
>
<DepositContainer
assetId={assetId}
setDialogStyleProps={setDialogStyleProps}
/>
</Dialog>
);
};

View File

@ -7,11 +7,17 @@ import { useDepositStore } from './deposit-store';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect } from 'react';
import { useDepositBalances } from './use-deposit-balances'; import { useDepositBalances } from './use-deposit-balances';
import type { Asset } from '@vegaprotocol/assets'; import type { Asset } from '@vegaprotocol/assets';
import type { DepositDialogStylePropsSetter } from './deposit-dialog';
import pick from 'lodash/pick';
import type { EthTransaction } from '@vegaprotocol/web3';
import { EthTxStatus } from '@vegaprotocol/web3';
import { t } from '@vegaprotocol/react-helpers';
interface DepositManagerProps { interface DepositManagerProps {
assetId?: string; assetId?: string;
assets: Asset[]; assets: Asset[];
isFaucetable: boolean; isFaucetable: boolean;
setDialogStyleProps?: DepositDialogStylePropsSetter;
} }
const useDepositAsset = (assets: Asset[], assetId?: string) => { const useDepositAsset = (assets: Asset[], assetId?: string) => {
@ -33,10 +39,14 @@ const useDepositAsset = (assets: Asset[], assetId?: string) => {
return { asset, balance, allowance, deposited, max, handleSelectAsset }; return { asset, balance, allowance, deposited, max, handleSelectAsset };
}; };
const getProps = (txContent?: EthTransaction['TxContent']) =>
txContent ? pick(txContent, ['title', 'icon', 'intent']) : undefined;
export const DepositManager = ({ export const DepositManager = ({
assetId, assetId,
assets, assets,
isFaucetable, isFaucetable,
setDialogStyleProps,
}: DepositManagerProps) => { }: DepositManagerProps) => {
const { asset, balance, allowance, deposited, max, handleSelectAsset } = const { asset, balance, allowance, deposited, max, handleSelectAsset } =
useDepositAsset(assets, assetId); useDepositAsset(assets, assetId);
@ -52,8 +62,21 @@ export const DepositManager = ({
// Set up faucet transaction // Set up faucet transaction
const faucet = useSubmitFaucet(); const faucet = useSubmitFaucet();
const transactionInProgress = [
approve.TxContent,
deposit.TxContent,
faucet.TxContent,
].filter((t) => t.status !== EthTxStatus.Default)[0];
useEffect(() => {
setDialogStyleProps?.(getProps(transactionInProgress));
}, [setDialogStyleProps, transactionInProgress]);
const returnLabel = t('Return to deposit');
return ( return (
<> <>
{!transactionInProgress && (
<DepositForm <DepositForm
balance={balance} balance={balance}
selectedAsset={asset} selectedAsset={asset}
@ -67,9 +90,11 @@ export const DepositManager = ({
allowance={allowance} allowance={allowance}
isFaucetable={isFaucetable} isFaucetable={isFaucetable}
/> />
<approve.Dialog /> )}
<faucet.Dialog />
<deposit.Dialog /> <approve.TxContent.Content returnLabel={returnLabel} />
<faucet.TxContent.Content returnLabel={returnLabel} />
<deposit.TxContent.Content returnLabel={returnLabel} />
</> </>
); );
}; };

View File

@ -14,3 +14,4 @@ export * from './use-get-deposited-amount';
export * from './use-submit-approval'; export * from './use-submit-approval';
export * from './use-submit-deposit'; export * from './use-submit-deposit';
export * from './use-submit-faucet'; export * from './use-submit-faucet';
export * from './deposit-dialog';

View File

@ -1,5 +1,5 @@
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit'; import { Button, Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
import { isEthereumError } from '../ethereum-error'; import { isEthereumError } from '../ethereum-error';
import type { EthTxState, TxError } from '../use-ethereum-transaction'; import type { EthTxState, TxError } from '../use-ethereum-transaction';
import { EthTxStatus } from '../use-ethereum-transaction'; import { EthTxStatus } from '../use-ethereum-transaction';
@ -39,7 +39,44 @@ export const EthereumTransactionDialog = ({
); );
}; };
export const TransactionContent = ({ export const getTransactionContent = ({
title,
transaction,
requiredConfirmations,
reset,
}: {
title: string;
transaction: EthTxState;
requiredConfirmations?: number;
reset: () => void;
}) => {
const { status, error, confirmations, txHash } = transaction;
const content = ({ returnLabel }: { returnLabel?: string }) => (
<>
{status !== EthTxStatus.Default && (
<TransactionContent
status={status}
error={error}
txHash={txHash}
confirmations={confirmations}
requiredConfirmations={requiredConfirmations}
/>
)}
{(status === EthTxStatus.Confirmed || status === EthTxStatus.Error) && (
<Button size="sm" className="mt-2" onClick={reset}>
{returnLabel ? returnLabel : t('Return')}
</Button>
)}
</>
);
return {
...getWrapperProps(title, status),
status,
Content: content,
};
};
const TransactionContent = ({
status, status,
error, error,
txHash, txHash,
@ -83,11 +120,15 @@ export const TransactionContent = ({
); );
}; };
export const getWrapperProps = (title: string, status: EthTxStatus) => { type WrapperProps = { title: string; icon?: JSX.Element; intent?: Intent };
export const getWrapperProps = (
title: string,
status: EthTxStatus
): WrapperProps => {
const propsMap = { const propsMap = {
[EthTxStatus.Default]: { [EthTxStatus.Default]: {
title: '', title: '',
icon: null, icon: undefined,
intent: undefined, intent: undefined,
}, },
[EthTxStatus.Error]: { [EthTxStatus.Error]: {

View File

@ -70,6 +70,7 @@ it('Ethereum transaction flow', async () => {
dialogOpen: false, dialogOpen: false,
}, },
Dialog: expect.any(Function), Dialog: expect.any(Function),
TxContent: expect.any(Object),
setConfirmed: expect.any(Function), setConfirmed: expect.any(Function),
perform: expect.any(Function), perform: expect.any(Function),
reset: expect.any(Function), reset: expect.any(Function),

View File

@ -4,7 +4,10 @@ import { useCallback, useMemo, useState } from 'react';
import type { EthereumError } from './ethereum-error'; import type { EthereumError } from './ethereum-error';
import { isExpectedEthereumError } from './ethereum-error'; import { isExpectedEthereumError } from './ethereum-error';
import { isEthereumError } from './ethereum-error'; import { isEthereumError } from './ethereum-error';
import { EthereumTransactionDialog } from './ethereum-transaction-dialog'; import {
EthereumTransactionDialog,
getTransactionContent,
} from './ethereum-transaction-dialog';
export enum EthTxStatus { export enum EthTxStatus {
Default = 'Default', Default = 'Default',
@ -163,5 +166,18 @@ export const useEthereumTransaction = <
); );
}, [methodName, transaction, requiredConfirmations, reset]); }, [methodName, transaction, requiredConfirmations, reset]);
return { perform, transaction, reset, setConfirmed, Dialog }; const TxContent = useMemo(
() =>
getTransactionContent({
title: formatLabel(methodName as string),
transaction,
requiredConfirmations,
reset,
}),
[methodName, requiredConfirmations, reset, transaction]
);
return { perform, transaction, reset, setConfirmed, Dialog, TxContent };
}; };
export type EthTransaction = ReturnType<typeof useEthereumTransaction>;