fix: withdrawals and depsits (#754)
* feat: handle withdrawal limits * feat: add withdraw limit ui to withdraw form * chore: lint error * fix: mock network param query for e2e tests * fix: wrong translation in tests * fix: withdrawals test and revert change in text for trade grid elements * fix: add check for signature length before progressing withdraw
This commit is contained in:
parent
b078fc9aad
commit
ab77e99f96
@ -1,10 +1,12 @@
|
|||||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||||
import { generateDepositPage } from '../support/mocks/generate-deposit-page';
|
import { generateDepositPage } from '../support/mocks/generate-deposit-page';
|
||||||
|
import { generateNetworkParameters } from '../support/mocks/generate-network-parameters';
|
||||||
|
|
||||||
describe('deposit form validation', () => {
|
describe('deposit form validation', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.mockWeb3Provider();
|
cy.mockWeb3Provider();
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
|
aliasQuery(req, 'NetworkParamsQuery', generateNetworkParameters());
|
||||||
aliasQuery(req, 'DepositPage', generateDepositPage());
|
aliasQuery(req, 'DepositPage', generateDepositPage());
|
||||||
});
|
});
|
||||||
cy.visit('/portfolio/deposit');
|
cy.visit('/portfolio/deposit');
|
||||||
|
@ -2,6 +2,7 @@ import { aliasQuery } from '@vegaprotocol/cypress';
|
|||||||
import { generateFill, generateFills } from '../support/mocks/generate-fills';
|
import { generateFill, generateFills } from '../support/mocks/generate-fills';
|
||||||
import { Side } from '@vegaprotocol/types';
|
import { Side } from '@vegaprotocol/types';
|
||||||
import { connectVegaWallet } from '../support/vega-wallet';
|
import { connectVegaWallet } from '../support/vega-wallet';
|
||||||
|
import { generateNetworkParameters } from '../support/mocks/generate-network-parameters';
|
||||||
|
|
||||||
describe('fills', () => {
|
describe('fills', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
@ -58,6 +59,7 @@ describe('fills', () => {
|
|||||||
});
|
});
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasQuery(req, 'Fills', result);
|
aliasQuery(req, 'Fills', result);
|
||||||
|
aliasQuery(req, 'NetworkParamsQuery', generateNetworkParameters());
|
||||||
});
|
});
|
||||||
cy.visit('/portfolio');
|
cy.visit('/portfolio');
|
||||||
cy.get('main[data-testid="portfolio"]').should('exist');
|
cy.get('main[data-testid="portfolio"]').should('exist');
|
||||||
@ -65,7 +67,7 @@ describe('fills', () => {
|
|||||||
|
|
||||||
it('renders fills', () => {
|
it('renders fills', () => {
|
||||||
cy.getByTestId('Fills').click();
|
cy.getByTestId('Fills').click();
|
||||||
cy.getByTestId('tab-fills').contains('Please connect Vega wallet');
|
cy.getByTestId('tab-fills').contains('Connect your Vega wallet');
|
||||||
|
|
||||||
connectVegaWallet();
|
connectVegaWallet();
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
||||||
|
import { generateNetworkParameters } from '../support/mocks/generate-network-parameters';
|
||||||
import { generateWithdrawPageQuery } from '../support/mocks/generate-withdraw-page-query';
|
import { generateWithdrawPageQuery } from '../support/mocks/generate-withdraw-page-query';
|
||||||
import { connectVegaWallet } from '../support/vega-wallet';
|
import { connectVegaWallet } from '../support/vega-wallet';
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ describe('withdraw', () => {
|
|||||||
cy.mockWeb3Provider();
|
cy.mockWeb3Provider();
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasQuery(req, 'WithdrawPageQuery', generateWithdrawPageQuery());
|
aliasQuery(req, 'WithdrawPageQuery', generateWithdrawPageQuery());
|
||||||
|
aliasQuery(req, 'NetworkParamsQuery', generateNetworkParameters());
|
||||||
});
|
});
|
||||||
cy.visit('/portfolio/withdraw');
|
cy.visit('/portfolio/withdraw');
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
||||||
|
import { generateNetworkParameters } from '../support/mocks/generate-network-parameters';
|
||||||
import { generateWithdrawals } from '../support/mocks/generate-withdrawals';
|
import { generateWithdrawals } from '../support/mocks/generate-withdrawals';
|
||||||
import { connectVegaWallet } from '../support/vega-wallet';
|
import { connectVegaWallet } from '../support/vega-wallet';
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ describe('withdrawals', () => {
|
|||||||
cy.mockWeb3Provider();
|
cy.mockWeb3Provider();
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasQuery(req, 'Withdrawals', generateWithdrawals());
|
aliasQuery(req, 'Withdrawals', generateWithdrawals());
|
||||||
|
aliasQuery(req, 'NetworkParamsQuery', generateNetworkParameters());
|
||||||
});
|
});
|
||||||
cy.visit('/portfolio/withdrawals');
|
cy.visit('/portfolio/withdrawals');
|
||||||
|
|
||||||
@ -16,8 +18,6 @@ describe('withdrawals', () => {
|
|||||||
|
|
||||||
// It also requires connection Ethereum wallet
|
// It also requires connection Ethereum wallet
|
||||||
connectEthereumWallet();
|
connectEthereumWallet();
|
||||||
|
|
||||||
cy.contains('Withdrawals');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders history of withdrawals', () => {
|
it('renders history of withdrawals', () => {
|
||||||
@ -27,7 +27,6 @@ describe('withdrawals', () => {
|
|||||||
const etherScanLink = `${Cypress.env(
|
const etherScanLink = `${Cypress.env(
|
||||||
'ETHERSCAN_URL'
|
'ETHERSCAN_URL'
|
||||||
)}/tx/0x5d7b1a35ba6bd23be17bb7a159c13cdbb3121fceb94e9c6c510f5503dce48d03`;
|
)}/tx/0x5d7b1a35ba6bd23be17bb7a159c13cdbb3121fceb94e9c6c510f5503dce48d03`;
|
||||||
cy.contains('Withdrawals');
|
|
||||||
|
|
||||||
const row = '.ag-center-cols-container[role="rowgroup"] > [role="row"]';
|
const row = '.ag-center-cols-container[role="rowgroup"] > [role="row"]';
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
import merge from 'lodash/merge';
|
||||||
|
import type { NetworkParamsQuery } from '@vegaprotocol/web3';
|
||||||
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
|
||||||
|
export const generateNetworkParameters = (
|
||||||
|
override?: PartialDeep<NetworkParamsQuery>
|
||||||
|
): NetworkParamsQuery => {
|
||||||
|
const defaultResult: NetworkParamsQuery = {
|
||||||
|
networkParameters: [
|
||||||
|
{
|
||||||
|
__typename: 'NetworkParameter',
|
||||||
|
key: 'blockchains.ethereumConfig',
|
||||||
|
value: JSON.stringify({
|
||||||
|
network_id: '3',
|
||||||
|
chain_id: '3',
|
||||||
|
collateral_bridge_contract: {
|
||||||
|
address: '0x947893AaA0A7b55f66990b3B4781514b691Fdd4a',
|
||||||
|
},
|
||||||
|
multisig_control_contract: {
|
||||||
|
address: '0xaE15126d2d1fAbF7cfA7cAD3cbD4921DfC87F620',
|
||||||
|
deployment_block_height: 12341882,
|
||||||
|
},
|
||||||
|
staking_bridge_contract: {
|
||||||
|
address: '0x7896C9491962D5839783CB6e0492ECebd34Bb35F',
|
||||||
|
deployment_block_height: 11177313,
|
||||||
|
},
|
||||||
|
token_vesting_contract: {
|
||||||
|
address: '0x9F10cBeEf03A564Fb914c2010c0Cd55E9BB11406',
|
||||||
|
deployment_block_height: 11177353,
|
||||||
|
},
|
||||||
|
confirmations: 3,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
return merge(defaultResult, override);
|
||||||
|
};
|
15
apps/trading/pages/portfolio/deposits-container.tsx
Normal file
15
apps/trading/pages/portfolio/deposits-container.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { AnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
export const DepositsContainer = () => {
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-[1fr_min-content] gap-12 h-full">
|
||||||
|
<div />
|
||||||
|
<div className="p-12">
|
||||||
|
<AnchorButton data-testid="deposit" href="/portfolio/deposit">
|
||||||
|
{t('Deposit')}
|
||||||
|
</AnchorButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -3,11 +3,13 @@ import { t } from '@vegaprotocol/react-helpers';
|
|||||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
import { PositionsContainer } from '@vegaprotocol/positions';
|
||||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||||
import { AccountsContainer } from '@vegaprotocol/accounts';
|
import { AccountsContainer } from '@vegaprotocol/accounts';
|
||||||
import { AnchorButton, Tab, Tabs } from '@vegaprotocol/ui-toolkit';
|
import { Tab, Tabs } from '@vegaprotocol/ui-toolkit';
|
||||||
import { WithdrawalsContainer } from './withdrawals/withdrawals-container';
|
import { WithdrawalsContainer } from './withdrawals-container';
|
||||||
import { FillsContainer } from '@vegaprotocol/fills';
|
import { FillsContainer } from '@vegaprotocol/fills';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||||
|
import { DepositsContainer } from './deposits-container';
|
||||||
|
|
||||||
const Portfolio = () => {
|
const Portfolio = () => {
|
||||||
const wrapperClasses = classNames(
|
const wrapperClasses = classNames(
|
||||||
@ -22,54 +24,58 @@ const Portfolio = () => {
|
|||||||
<PortfolioGridChild>
|
<PortfolioGridChild>
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab id="positions" name={t('Positions')}>
|
<Tab id="positions" name={t('Positions')}>
|
||||||
<div className={tabContentClassName}>
|
<VegaWalletContainer>
|
||||||
<h4 className="text-h4 text-black dark:text-white p-8">
|
<div className={tabContentClassName}>
|
||||||
{t('Positions')}
|
<h4 className="text-h4 text-black dark:text-white p-8">
|
||||||
</h4>
|
{t('Positions')}
|
||||||
<div>
|
</h4>
|
||||||
<PositionsContainer />
|
<div>
|
||||||
|
<PositionsContainer />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="orders" name={t('Orders')}>
|
<Tab id="orders" name={t('Orders')}>
|
||||||
<div className={tabContentClassName}>
|
<VegaWalletContainer>
|
||||||
<h4 className="text-h4 text-black dark:text-white p-8">
|
<div className={tabContentClassName}>
|
||||||
{t('Orders')}
|
<h4 className="text-h4 text-black dark:text-white p-8">
|
||||||
</h4>
|
{t('Orders')}
|
||||||
<div>
|
</h4>
|
||||||
<OrderListContainer />
|
<div>
|
||||||
|
<OrderListContainer />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
<div className={tabContentClassName}>
|
<VegaWalletContainer>
|
||||||
<h4 className="text-h4 text-black dark:text-white p-8">
|
<div className={tabContentClassName}>
|
||||||
{t('Fills')}
|
<h4 className="text-h4 text-black dark:text-white p-8">
|
||||||
</h4>
|
{t('Fills')}
|
||||||
<div>
|
</h4>
|
||||||
<FillsContainer />
|
<div>
|
||||||
|
<FillsContainer />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</PortfolioGridChild>
|
</PortfolioGridChild>
|
||||||
<PortfolioGridChild>
|
<PortfolioGridChild>
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab id="collateral" name={t('Collateral')}>
|
<Tab id="collateral" name={t('Collateral')}>
|
||||||
<AccountsContainer />
|
<VegaWalletContainer>
|
||||||
|
<AccountsContainer />
|
||||||
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="deposits" name={t('Deposits')}>
|
<Tab id="deposits" name={t('Deposits')}>
|
||||||
<div className={tabContentClassName}>
|
<DepositsContainer />
|
||||||
<div className="p-8">
|
|
||||||
<AnchorButton data-testid="deposit" href="/portfolio/deposit">
|
|
||||||
{t('Deposit')}
|
|
||||||
</AnchorButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="withdrawals" name={t('Withdrawals')}>
|
<Tab id="withdrawals" name={t('Withdrawals')}>
|
||||||
<Web3Container>
|
<Web3Container>
|
||||||
<WithdrawalsContainer />
|
<VegaWalletContainer>
|
||||||
|
<WithdrawalsContainer />
|
||||||
|
</VegaWalletContainer>
|
||||||
</Web3Container>
|
</Web3Container>
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import orderBy from 'lodash/orderBy';
|
import orderBy from 'lodash/orderBy';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useWithdrawals, WithdrawalsTable } from '@vegaprotocol/withdraws';
|
import { useWithdrawals, WithdrawalsTable } from '@vegaprotocol/withdraws';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
export const WithdrawalsContainer = () => {
|
export const WithdrawalsContainer = () => {
|
||||||
const { data, loading, error } = useWithdrawals();
|
const { data, loading, error } = useWithdrawals();
|
||||||
@ -16,7 +17,16 @@ export const WithdrawalsContainer = () => {
|
|||||||
(w) => new Date(w.createdTimestamp).getTime(),
|
(w) => new Date(w.createdTimestamp).getTime(),
|
||||||
'desc'
|
'desc'
|
||||||
);
|
);
|
||||||
return <WithdrawalsTable withdrawals={withdrawals} />;
|
return (
|
||||||
|
<div className="grid grid-cols-[1fr_min-content] gap-12 h-full">
|
||||||
|
<WithdrawalsTable withdrawals={withdrawals} />
|
||||||
|
<div className="p-12">
|
||||||
|
<Link href="/portfolio/withdraw" passHref={true}>
|
||||||
|
<Button data-testid="start-withdrawal">Withdraw</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
@ -1,25 +1,12 @@
|
|||||||
import { t } from '@vegaprotocol/react-helpers';
|
|
||||||
import { AnchorButton } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { VegaWalletContainer } from '../../../components/vega-wallet-container';
|
import { VegaWalletContainer } from '../../../components/vega-wallet-container';
|
||||||
import { Web3Container } from '../../../components/web3-container';
|
import { Web3Container } from '../../../components/web3-container';
|
||||||
import { WithdrawalsContainer } from './withdrawals-container';
|
import { WithdrawalsContainer } from '../withdrawals-container';
|
||||||
|
|
||||||
const Withdrawals = () => {
|
const Withdrawals = () => {
|
||||||
return (
|
return (
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<Web3Container>
|
<Web3Container>
|
||||||
<div className="h-full grid grid grid-rows-[min-content,1fr]">
|
<WithdrawalsContainer />
|
||||||
<header className="flex justify-between p-24">
|
|
||||||
<h1 className="text-h3">{t('Withdrawals')}</h1>
|
|
||||||
<AnchorButton
|
|
||||||
href="/portfolio/withdraw"
|
|
||||||
data-testid="start-withdrawal"
|
|
||||||
>
|
|
||||||
{t('Start withdrawal')}
|
|
||||||
</AnchorButton>
|
|
||||||
</header>
|
|
||||||
<WithdrawalsContainer />
|
|
||||||
</div>
|
|
||||||
</Web3Container>
|
</Web3Container>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
);
|
);
|
||||||
|
@ -8,12 +8,19 @@ interface DepositLimitsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DepositLimits = ({ limits }: DepositLimitsProps) => {
|
export const DepositLimits = ({ limits }: DepositLimitsProps) => {
|
||||||
const maxLimit = limits.max.isEqualTo(Infinity)
|
let maxLimit = '';
|
||||||
? t('No limit')
|
|
||||||
: limits.max.toString();
|
if (limits.max.isEqualTo(Infinity)) {
|
||||||
|
maxLimit = t('No limit');
|
||||||
|
} else if (limits.max.isGreaterThan(1_000_000)) {
|
||||||
|
maxLimit = t('1m+');
|
||||||
|
} else {
|
||||||
|
maxLimit = limits.max.toString();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="text-ui font-bold">{t('Temporary deposit limits')}</p>
|
<p className="text-ui font-bold">{t('Deposit limits')}</p>
|
||||||
<table className="w-full text-ui">
|
<table className="w-full text-ui">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -77,7 +77,7 @@ export const DepositManager = ({
|
|||||||
const allowance = useGetAllowance(tokenContract, decimals);
|
const allowance = useGetAllowance(tokenContract, decimals);
|
||||||
|
|
||||||
// Set up approve transaction
|
// Set up approve transaction
|
||||||
const approve = useSubmitApproval(tokenContract);
|
const approve = useSubmitApproval(tokenContract, decimals);
|
||||||
|
|
||||||
// Set up deposit transaction
|
// Set up deposit transaction
|
||||||
const { confirmationEvent, ...deposit } = useSubmitDeposit();
|
const { confirmationEvent, ...deposit } = useSubmitDeposit();
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
|
import { removeDecimal } from '@vegaprotocol/react-helpers';
|
||||||
import type { Token } from '@vegaprotocol/smart-contracts';
|
import type { Token } from '@vegaprotocol/smart-contracts';
|
||||||
import { useEthereumConfig, useEthereumTransaction } from '@vegaprotocol/web3';
|
import { useEthereumConfig, useEthereumTransaction } from '@vegaprotocol/web3';
|
||||||
|
|
||||||
export const useSubmitApproval = (contract: Token | null) => {
|
export const useSubmitApproval = (
|
||||||
|
contract: Token | null,
|
||||||
|
decimals: number | undefined
|
||||||
|
) => {
|
||||||
const { config } = useEthereumConfig();
|
const { config } = useEthereumConfig();
|
||||||
|
|
||||||
const transaction = useEthereumTransaction(() => {
|
const transaction = useEthereumTransaction(() => {
|
||||||
if (!contract || !config) {
|
if (!contract || !config || decimals === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return contract.approve(
|
|
||||||
config.collateral_bridge_contract.address,
|
const amount = removeDecimal('1000000', decimals);
|
||||||
Number.MAX_SAFE_INTEGER.toString()
|
|
||||||
);
|
return contract.approve(config.collateral_bridge_contract.address, amount);
|
||||||
});
|
});
|
||||||
|
|
||||||
return transaction;
|
return transaction;
|
||||||
|
@ -36,6 +36,9 @@ export class CollateralBridgeNew {
|
|||||||
isAssetListed(address: string) {
|
isAssetListed(address: string) {
|
||||||
return this.contract.is_asset_listed(address);
|
return this.contract.is_asset_listed(address);
|
||||||
}
|
}
|
||||||
|
getWithdrawThreshold(assetSource: string) {
|
||||||
|
return this.contract.get_withdraw_threshold(assetSource);
|
||||||
|
}
|
||||||
withdrawAsset(
|
withdrawAsset(
|
||||||
assetSource: string,
|
assetSource: string,
|
||||||
amount: string,
|
amount: string,
|
||||||
|
@ -41,6 +41,9 @@ export class CollateralBridge {
|
|||||||
isAssetListed(address: string) {
|
isAssetListed(address: string) {
|
||||||
return this.contract.is_asset_listed(address);
|
return this.contract.is_asset_listed(address);
|
||||||
}
|
}
|
||||||
|
getWithdrawThreshold(assetSource: string) {
|
||||||
|
return this.contract.get_withdraw_threshold(assetSource);
|
||||||
|
}
|
||||||
withdrawAsset(
|
withdrawAsset(
|
||||||
assetSource: string,
|
assetSource: string,
|
||||||
amount: string,
|
amount: string,
|
||||||
|
28
libs/withdraws/src/lib/use-get-withdraw-limits.tsx
Normal file
28
libs/withdraws/src/lib/use-get-withdraw-limits.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import type { Asset } from './types';
|
||||||
|
import { useBridgeContract, useEthereumReadContract } from '@vegaprotocol/web3';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { addDecimal } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
|
export const useGetWithdrawLimits = (asset?: Asset) => {
|
||||||
|
const contract = useBridgeContract(true);
|
||||||
|
const getLimits = useCallback(async () => {
|
||||||
|
if (!contract || !asset || asset.source.__typename !== 'ERC20') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contract.getWithdrawThreshold(asset.source.contractAddress);
|
||||||
|
}, [asset, contract]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: { data },
|
||||||
|
} = useEthereumReadContract(getLimits);
|
||||||
|
|
||||||
|
if (!data || !asset) return null;
|
||||||
|
|
||||||
|
const max = new BigNumber(addDecimal(data.toString(), asset.decimals));
|
||||||
|
|
||||||
|
return {
|
||||||
|
max: max.isEqualTo(0) ? new BigNumber(Infinity) : max,
|
||||||
|
};
|
||||||
|
};
|
@ -104,7 +104,10 @@ export const useWithdraw = (cancelled: boolean, isNewContract: boolean) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data?.erc20WithdrawalApproval) {
|
if (
|
||||||
|
data?.erc20WithdrawalApproval &&
|
||||||
|
data.erc20WithdrawalApproval.signatures.length > 2
|
||||||
|
) {
|
||||||
stopPolling();
|
stopPolling();
|
||||||
setApproval(data.erc20WithdrawalApproval);
|
setApproval(data.erc20WithdrawalApproval);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@ beforeEach(() => {
|
|||||||
assets,
|
assets,
|
||||||
min: new BigNumber(0.00001),
|
min: new BigNumber(0.00001),
|
||||||
max: new BigNumber(100),
|
max: new BigNumber(100),
|
||||||
|
limits: {
|
||||||
|
max: new BigNumber(200),
|
||||||
|
},
|
||||||
ethereumAccount: undefined,
|
ethereumAccount: undefined,
|
||||||
selectedAsset: undefined,
|
selectedAsset: undefined,
|
||||||
onSelectAsset: jest.fn(),
|
onSelectAsset: jest.fn(),
|
||||||
|
@ -18,6 +18,7 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
|||||||
import { useForm, Controller } from 'react-hook-form';
|
import { useForm, Controller } from 'react-hook-form';
|
||||||
import type { WithdrawalFields } from './use-withdraw';
|
import type { WithdrawalFields } from './use-withdraw';
|
||||||
import type { Asset } from './types';
|
import type { Asset } from './types';
|
||||||
|
import { WithdrawLimits } from './withdraw-limits';
|
||||||
|
|
||||||
interface FormFields {
|
interface FormFields {
|
||||||
asset: string;
|
asset: string;
|
||||||
@ -31,6 +32,9 @@ export interface WithdrawFormProps {
|
|||||||
min: BigNumber;
|
min: BigNumber;
|
||||||
selectedAsset?: Asset;
|
selectedAsset?: Asset;
|
||||||
ethereumAccount?: string;
|
ethereumAccount?: string;
|
||||||
|
limits: {
|
||||||
|
max: BigNumber;
|
||||||
|
} | null;
|
||||||
onSelectAsset: (assetId: string) => void;
|
onSelectAsset: (assetId: string) => void;
|
||||||
submitWithdraw: (withdrawal: WithdrawalFields) => void;
|
submitWithdraw: (withdrawal: WithdrawalFields) => void;
|
||||||
}
|
}
|
||||||
@ -41,6 +45,7 @@ export const WithdrawForm = ({
|
|||||||
min,
|
min,
|
||||||
selectedAsset,
|
selectedAsset,
|
||||||
ethereumAccount,
|
ethereumAccount,
|
||||||
|
limits,
|
||||||
onSelectAsset,
|
onSelectAsset,
|
||||||
submitWithdraw,
|
submitWithdraw,
|
||||||
}: WithdrawFormProps) => {
|
}: WithdrawFormProps) => {
|
||||||
@ -134,7 +139,11 @@ export const WithdrawForm = ({
|
|||||||
</UseButton>
|
</UseButton>
|
||||||
)}
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{selectedAsset && limits && (
|
||||||
|
<div className="mb-20">
|
||||||
|
<WithdrawLimits limits={limits} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<FormGroup label={t('Amount')} labelFor="amount" className="relative">
|
<FormGroup label={t('Amount')} labelFor="amount" className="relative">
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
|
34
libs/withdraws/src/lib/withdraw-limits.tsx
Normal file
34
libs/withdraws/src/lib/withdraw-limits.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import type BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
|
interface WithdrawLimitsProps {
|
||||||
|
limits: {
|
||||||
|
max: BigNumber;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithdrawLimits = ({ limits }: WithdrawLimitsProps) => {
|
||||||
|
let maxLimit = '';
|
||||||
|
|
||||||
|
if (limits.max.isEqualTo(Infinity)) {
|
||||||
|
maxLimit = t('No limit');
|
||||||
|
} else if (limits.max.isGreaterThan(1_000_000)) {
|
||||||
|
maxLimit = t('1m+');
|
||||||
|
} else {
|
||||||
|
maxLimit = limits.max.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-ui font-bold">{t('Withdraw limits')}</p>
|
||||||
|
<table className="w-full text-ui">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th className="text-left font-normal">{t('Maximum')}</th>
|
||||||
|
<td className="text-right">{maxLimit}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -9,6 +9,7 @@ import {
|
|||||||
EthTxStatus,
|
EthTxStatus,
|
||||||
initialState as ethTxInitialState,
|
initialState as ethTxInitialState,
|
||||||
} from '@vegaprotocol/web3';
|
} from '@vegaprotocol/web3';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
const ethereumAddress = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853';
|
const ethereumAddress = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853';
|
||||||
|
|
||||||
@ -16,6 +17,12 @@ jest.mock('@web3-react/core', () => ({
|
|||||||
useWeb3React: () => ({ account: ethereumAddress }),
|
useWeb3React: () => ({ account: ethereumAddress }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('./use-get-withdraw-limits', () => ({
|
||||||
|
useGetWithdrawLimits: () => {
|
||||||
|
return { max: new BigNumber(1000000) };
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
let props: WithdrawManagerProps;
|
let props: WithdrawManagerProps;
|
||||||
let useWithdrawValue: ReturnType<typeof withdrawHook.useWithdraw>;
|
let useWithdrawValue: ReturnType<typeof withdrawHook.useWithdraw>;
|
||||||
let useWithdraw: jest.SpyInstance;
|
let useWithdraw: jest.SpyInstance;
|
||||||
|
@ -10,6 +10,7 @@ import { AccountType } from '@vegaprotocol/types';
|
|||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import type { Account, Asset } from './types';
|
import type { Account, Asset } from './types';
|
||||||
import { useWeb3React } from '@web3-react/core';
|
import { useWeb3React } from '@web3-react/core';
|
||||||
|
import { useGetWithdrawLimits } from './use-get-withdraw-limits';
|
||||||
|
|
||||||
export interface WithdrawManagerProps {
|
export interface WithdrawManagerProps {
|
||||||
assets: Asset[];
|
assets: Asset[];
|
||||||
@ -39,6 +40,8 @@ export const WithdrawManager = ({
|
|||||||
return assets?.find((a) => a.id === assetId);
|
return assets?.find((a) => a.id === assetId);
|
||||||
}, [assets, assetId]);
|
}, [assets, assetId]);
|
||||||
|
|
||||||
|
const limits = useGetWithdrawLimits(asset);
|
||||||
|
|
||||||
const max = useMemo(() => {
|
const max = useMemo(() => {
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
return new BigNumber(0);
|
return new BigNumber(0);
|
||||||
@ -48,13 +51,11 @@ export const WithdrawManager = ({
|
|||||||
(a) => a.type === AccountType.General && a.asset.id === asset.id
|
(a) => a.type === AccountType.General && a.asset.id === asset.id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!account) {
|
const v = account
|
||||||
return new BigNumber(0);
|
? new BigNumber(addDecimal(account.balance, asset.decimals))
|
||||||
}
|
: new BigNumber(0);
|
||||||
|
return BigNumber.minimum(v, limits ? limits.max : new BigNumber(Infinity));
|
||||||
const v = new BigNumber(addDecimal(account.balance, asset.decimals));
|
}, [asset, accounts, limits]);
|
||||||
return v;
|
|
||||||
}, [asset, accounts]);
|
|
||||||
|
|
||||||
const min = useMemo(() => {
|
const min = useMemo(() => {
|
||||||
return asset
|
return asset
|
||||||
@ -92,6 +93,7 @@ export const WithdrawManager = ({
|
|||||||
max={max}
|
max={max}
|
||||||
min={min}
|
min={min}
|
||||||
submitWithdraw={handleSubmit}
|
submitWithdraw={handleSubmit}
|
||||||
|
limits={limits}
|
||||||
/>
|
/>
|
||||||
<WithdrawDialog
|
<WithdrawDialog
|
||||||
vegaTx={vegaTx}
|
vegaTx={vegaTx}
|
||||||
|
Loading…
Reference in New Issue
Block a user