feat(trading,deposits): improve ethereum connection and approve step (#2926)
This commit is contained in:
parent
58d8f7857e
commit
63698fbb80
@ -72,7 +72,11 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
|||||||
cy.getByTestId('deposit-button').click();
|
cy.getByTestId('deposit-button').click();
|
||||||
connectEthereumWallet('Unknown');
|
connectEthereumWallet('Unknown');
|
||||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||||
cy.getByTestId('deposit-approve-submit').click();
|
cy.getByTestId('approve-warning').should(
|
||||||
|
'contain.text',
|
||||||
|
`Deposits of ${btcSymbol} not approved`
|
||||||
|
);
|
||||||
|
cy.getByTestId('deposit-submit').click();
|
||||||
cy.getByTestId('dialog-title').should('contain.text', 'Approve complete');
|
cy.getByTestId('dialog-title').should('contain.text', 'Approve complete');
|
||||||
cy.get('[data-testid="Return to deposit"]').click();
|
cy.get('[data-testid="Return to deposit"]').click();
|
||||||
cy.get(amountField).clear().type('10');
|
cy.get(amountField).clear().type('10');
|
||||||
@ -407,7 +411,7 @@ describe('capsule', { tags: '@slow' }, () => {
|
|||||||
cy.getByTestId('deposit-button').click();
|
cy.getByTestId('deposit-button').click();
|
||||||
connectEthereumWallet('Unknown');
|
connectEthereumWallet('Unknown');
|
||||||
cy.get(assetSelectField, txTimeout).select(vegaName, { force: true });
|
cy.get(assetSelectField, txTimeout).select(vegaName, { force: true });
|
||||||
cy.getByTestId('deposit-approve-submit').click();
|
cy.getByTestId('deposit-submit').click();
|
||||||
cy.getByTestId('dialog-title').should('contain.text', 'Approve complete');
|
cy.getByTestId('dialog-title').should('contain.text', 'Approve complete');
|
||||||
cy.get('[data-testid="Return to deposit"]').click();
|
cy.get('[data-testid="Return to deposit"]').click();
|
||||||
cy.get(amountField).clear().type('10000');
|
cy.get(amountField).clear().type('10000');
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { removeDecimal } from '@vegaprotocol/cypress';
|
||||||
|
import { ethers } from 'ethers';
|
||||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
||||||
import { selectAsset } from '../support/helpers';
|
import { selectAsset } from '../support/helpers';
|
||||||
|
|
||||||
@ -9,7 +11,7 @@ const formFieldError = 'input-error-text';
|
|||||||
const ASSET_EURO = 1;
|
const ASSET_EURO = 1;
|
||||||
|
|
||||||
describe('deposit form validation', { tags: '@smoke' }, () => {
|
describe('deposit form validation', { tags: '@smoke' }, () => {
|
||||||
before(() => {
|
function openDepositForm() {
|
||||||
cy.mockWeb3Provider();
|
cy.mockWeb3Provider();
|
||||||
cy.mockSubscription();
|
cy.mockSubscription();
|
||||||
cy.mockTradingPage();
|
cy.mockTradingPage();
|
||||||
@ -20,10 +22,14 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
|
|||||||
cy.getByTestId('deposit-button').click();
|
cy.getByTestId('deposit-button').click();
|
||||||
cy.wait('@Assets');
|
cy.wait('@Assets');
|
||||||
connectEthereumWallet('MetaMask');
|
connectEthereumWallet('MetaMask');
|
||||||
cy.getByTestId('deposit-submit').click();
|
}
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
openDepositForm();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles empty fields', () => {
|
it('handles empty fields', () => {
|
||||||
|
cy.getByTestId('deposit-submit').click();
|
||||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
||||||
cy.getByTestId(formFieldError).should('have.length', 2);
|
cy.getByTestId(formFieldError).should('have.length', 2);
|
||||||
});
|
});
|
||||||
@ -44,6 +50,13 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('invalid amount', () => {
|
it('invalid amount', () => {
|
||||||
|
mockWeb3DepositCalls({
|
||||||
|
allowance: '1000',
|
||||||
|
depositLifetimeLimit: '1000',
|
||||||
|
balance: '800',
|
||||||
|
deposited: '0',
|
||||||
|
dps: 5,
|
||||||
|
});
|
||||||
// Deposit amount smaller than minimum viable for selected asset
|
// Deposit amount smaller than minimum viable for selected asset
|
||||||
// Select an amount so that we have a known decimal places value to work with
|
// Select an amount so that we have a known decimal places value to work with
|
||||||
selectAsset(ASSET_EURO);
|
selectAsset(ASSET_EURO);
|
||||||
@ -56,12 +69,16 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('insufficient funds', () => {
|
it('insufficient funds', () => {
|
||||||
// 1001-DEPO-005
|
mockWeb3DepositCalls({
|
||||||
// Deposit amount is valid, but less than approved. This will always be the case because our
|
allowance: '1000',
|
||||||
// CI wallet wont have approved any assets
|
depositLifetimeLimit: '1000',
|
||||||
|
balance: '800',
|
||||||
|
deposited: '0',
|
||||||
|
dps: 5,
|
||||||
|
});
|
||||||
cy.get(amountField)
|
cy.get(amountField)
|
||||||
.clear()
|
.clear()
|
||||||
.type('100')
|
.type('850')
|
||||||
.next(`[data-testid="${formFieldError}"]`)
|
.next(`[data-testid="${formFieldError}"]`)
|
||||||
.should('have.text', 'Insufficient amount in Ethereum wallet');
|
.should('have.text', 'Insufficient amount in Ethereum wallet');
|
||||||
});
|
});
|
||||||
@ -88,3 +105,90 @@ describe('deposit actions', { tags: '@smoke' }, () => {
|
|||||||
cy.getByTestId('deposit-submit').should('be.visible');
|
cy.getByTestId('deposit-submit').should('be.visible');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function mockWeb3DepositCalls({
|
||||||
|
allowance,
|
||||||
|
depositLifetimeLimit,
|
||||||
|
balance,
|
||||||
|
deposited,
|
||||||
|
dps,
|
||||||
|
}: {
|
||||||
|
allowance: string;
|
||||||
|
depositLifetimeLimit: string;
|
||||||
|
balance: string;
|
||||||
|
deposited: string;
|
||||||
|
dps: number;
|
||||||
|
}) {
|
||||||
|
const assetContractAddress = '0x0158031158bb4df2ad02eaa31e8963e84ea978a4';
|
||||||
|
const collateralBridgeAddress = '0x7fe27d970bc8afc3b11cc8d9737bfb66b1efd799';
|
||||||
|
const toResult = (value: string, dps: number) => {
|
||||||
|
const rawValue = removeDecimal(value, dps);
|
||||||
|
return ethers.utils.hexZeroPad(
|
||||||
|
ethers.utils.hexlify(parseInt(rawValue)),
|
||||||
|
32
|
||||||
|
);
|
||||||
|
};
|
||||||
|
cy.intercept('POST', 'http://localhost:8545', (req) => {
|
||||||
|
// Mock chainId call
|
||||||
|
if (req.body.method === 'eth_chainId') {
|
||||||
|
req.alias = 'eth_chainId';
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: '0xaa36a7', // 11155111 for sepolia chain id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock deposited amount
|
||||||
|
if (req.body.method === 'eth_getStorageAt') {
|
||||||
|
req.alias = 'eth_getStorageAt';
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: toResult(deposited, dps),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.body.method === 'eth_call') {
|
||||||
|
// Mock approved amount for asset on collateral bridge
|
||||||
|
if (
|
||||||
|
req.body.params[0].to === assetContractAddress &&
|
||||||
|
req.body.params[0].data ===
|
||||||
|
'0xdd62ed3e000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f0000000000000000000000007fe27d970bc8afc3b11cc8d9737bfb66b1efd799'
|
||||||
|
) {
|
||||||
|
req.alias = 'eth_call_allowance';
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: toResult(allowance, dps),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Mock balance of asset in Ethereum wallet
|
||||||
|
else if (
|
||||||
|
req.body.params[0].to === assetContractAddress &&
|
||||||
|
req.body.params[0].data ===
|
||||||
|
'0x70a08231000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f'
|
||||||
|
) {
|
||||||
|
req.alias = 'eth_call_balanceOf';
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: toResult(balance, dps),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Mock deposit lifetime limit
|
||||||
|
else if (
|
||||||
|
req.body.params[0].to === collateralBridgeAddress &&
|
||||||
|
req.body.params[0].data ===
|
||||||
|
'0x354a897a0000000000000000000000000158031158bb4df2ad02eaa31e8963e84ea978a4'
|
||||||
|
) {
|
||||||
|
req.alias = 'eth_call_get_deposit_maximum'; // deposit lifetime limit
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: toResult(depositLifetimeLimit, dps),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -17,19 +17,50 @@ describe('connect hosted wallet', { tags: '@smoke' }, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('can connect', () => {
|
it('can connect', () => {
|
||||||
|
// Mock authentication
|
||||||
|
cy.intercept('POST', 'https://wallet.testnet.vega.xyz/api/v1/auth/token', {
|
||||||
|
body: {
|
||||||
|
token: 'test-token',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Mock getting keys from wallet
|
||||||
|
cy.intercept('GET', 'https://wallet.testnet.vega.xyz/api/v1/keys', {
|
||||||
|
body: {
|
||||||
|
keys: [
|
||||||
|
{
|
||||||
|
algorithm: {
|
||||||
|
name: 'algo',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
index: 0,
|
||||||
|
meta: [],
|
||||||
|
pub: 'HOSTED_PUBKEY',
|
||||||
|
tainted: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
cy.getByTestId(connectVegaBtn).click();
|
cy.getByTestId(connectVegaBtn).click();
|
||||||
mockConnectWallet();
|
|
||||||
cy.contains('Connect Vega wallet');
|
cy.contains('Connect Vega wallet');
|
||||||
cy.contains('Hosted Fairground wallet');
|
cy.contains('Hosted Fairground wallet');
|
||||||
|
|
||||||
cy.getByTestId('connectors-list')
|
cy.getByTestId('connectors-list')
|
||||||
.find('[data-testid="connector-jsonRpc"]')
|
.find('[data-testid="connector-hosted"]')
|
||||||
.click();
|
.click();
|
||||||
cy.wait('@walletReq');
|
cy.getByTestId(form).find('#wallet').click().type('user');
|
||||||
|
cy.getByTestId(form).find('#passphrase').click().type('pass');
|
||||||
|
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
|
||||||
cy.getByTestId(manageVegaBtn).should('exist');
|
cy.getByTestId(manageVegaBtn).should('exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('doesnt connect with invalid credentials', () => {
|
it('doesnt connect with invalid credentials', () => {
|
||||||
|
// Mock incorrect username/password
|
||||||
|
cy.intercept('POST', 'https://wallet.testnet.vega.xyz/api/v1/auth/token', {
|
||||||
|
body: {
|
||||||
|
error: 'No wallet',
|
||||||
|
},
|
||||||
|
statusCode: 403, // 403 forbidden invalid crednetials
|
||||||
|
});
|
||||||
cy.getByTestId(connectVegaBtn).click();
|
cy.getByTestId(connectVegaBtn).click();
|
||||||
cy.getByTestId('connectors-list')
|
cy.getByTestId('connectors-list')
|
||||||
.find('[data-testid="connector-hosted"]')
|
.find('[data-testid="connector-hosted"]')
|
||||||
@ -37,10 +68,10 @@ describe('connect hosted wallet', { tags: '@smoke' }, () => {
|
|||||||
cy.getByTestId(form).find('#wallet').click().type('invalid name');
|
cy.getByTestId(form).find('#wallet').click().type('invalid name');
|
||||||
cy.getByTestId(form).find('#passphrase').click().type('invalid password');
|
cy.getByTestId(form).find('#passphrase').click().type('invalid password');
|
||||||
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
|
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
|
||||||
cy.getByTestId('form-error').should('have.text', 'No wallet detected');
|
cy.getByTestId('form-error').should('have.text', 'Invalid credentials');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('doesnt connect with invalid fields', () => {
|
it('doesnt connect with empty fields', () => {
|
||||||
cy.getByTestId(connectVegaBtn).click();
|
cy.getByTestId(connectVegaBtn).click();
|
||||||
cy.getByTestId('connectors-list')
|
cy.getByTestId('connectors-list')
|
||||||
.find('[data-testid="connector-hosted"]')
|
.find('[data-testid="connector-hosted"]')
|
||||||
|
@ -318,33 +318,37 @@ const SummaryMessage = memo(
|
|||||||
}
|
}
|
||||||
if (!pubKey) {
|
if (!pubKey) {
|
||||||
return (
|
return (
|
||||||
<Notification
|
<div className="mb-4">
|
||||||
testId={'deal-ticket-connect-wallet'}
|
<Notification
|
||||||
intent={Intent.Warning}
|
testId={'deal-ticket-connect-wallet'}
|
||||||
message={
|
intent={Intent.Warning}
|
||||||
<p className="text-sm pb-2">
|
message={
|
||||||
You need a{' '}
|
<p className="text-sm pb-2">
|
||||||
<ExternalLink href="https://vega.xyz/wallet">
|
You need a{' '}
|
||||||
Vega wallet
|
<ExternalLink href="https://vega.xyz/wallet">
|
||||||
</ExternalLink>{' '}
|
Vega wallet
|
||||||
with {assetSymbol} to start trading in this market.
|
</ExternalLink>{' '}
|
||||||
</p>
|
with {assetSymbol} to start trading in this market.
|
||||||
}
|
</p>
|
||||||
buttonProps={{
|
}
|
||||||
text: t('Connect wallet'),
|
buttonProps={{
|
||||||
action: openVegaWalletDialog,
|
text: t('Connect wallet'),
|
||||||
dataTestId: 'order-connect-wallet',
|
action: openVegaWalletDialog,
|
||||||
size: 'md',
|
dataTestId: 'order-connect-wallet',
|
||||||
}}
|
size: 'md',
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (errorMessage === SummaryValidationType.NoCollateral) {
|
if (errorMessage === SummaryValidationType.NoCollateral) {
|
||||||
return (
|
return (
|
||||||
<ZeroBalanceError
|
<div className="mb-4">
|
||||||
asset={market.tradableInstrument.instrument.product.settlementAsset}
|
<ZeroBalanceError
|
||||||
onClickCollateral={onClickCollateral}
|
asset={market.tradableInstrument.instrument.product.settlementAsset}
|
||||||
/>
|
onClickCollateral={onClickCollateral}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,7 +367,11 @@ const SummaryMessage = memo(
|
|||||||
// If there is no blocking error but user doesn't have enough
|
// If there is no blocking error but user doesn't have enough
|
||||||
// balance render the margin warning, but still allow submission
|
// balance render the margin warning, but still allow submission
|
||||||
if (balanceError) {
|
if (balanceError) {
|
||||||
return <MarginWarning balance={balance} margin={margin} asset={asset} />;
|
return (
|
||||||
|
<div className="mb-4">
|
||||||
|
<MarginWarning balance={balance} margin={margin} asset={asset} />;
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show auction mode warning
|
// Show auction mode warning
|
||||||
@ -375,13 +383,15 @@ const SummaryMessage = memo(
|
|||||||
].includes(marketData.marketTradingMode)
|
].includes(marketData.marketTradingMode)
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<Notification
|
<div className="mb-4">
|
||||||
intent={Intent.Warning}
|
<Notification
|
||||||
testId={'dealticket-warning-auction'}
|
intent={Intent.Warning}
|
||||||
message={t(
|
testId={'dealticket-warning-auction'}
|
||||||
'Any orders placed now will not trade until the auction ends'
|
message={t(
|
||||||
)}
|
'Any orders placed now will not trade until the auction ends'
|
||||||
/>
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,12 @@ import type { DepositFormProps } from './deposit-form';
|
|||||||
import { DepositForm } from './deposit-form';
|
import { DepositForm } from './deposit-form';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
import { useWeb3ConnectStore } from '@vegaprotocol/web3';
|
||||||
import { useWeb3React } from '@web3-react/core';
|
import { useWeb3React } from '@web3-react/core';
|
||||||
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/wallet');
|
jest.mock('@vegaprotocol/wallet');
|
||||||
|
jest.mock('@vegaprotocol/web3');
|
||||||
jest.mock('@web3-react/core');
|
jest.mock('@web3-react/core');
|
||||||
|
|
||||||
const mockConnector = { deactivate: jest.fn() };
|
const mockConnector = { deactivate: jest.fn() };
|
||||||
@ -37,6 +39,8 @@ function generateAsset(): AssetFieldsFragment {
|
|||||||
let asset: AssetFieldsFragment;
|
let asset: AssetFieldsFragment;
|
||||||
let props: DepositFormProps;
|
let props: DepositFormProps;
|
||||||
const MOCK_ETH_ADDRESS = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853';
|
const MOCK_ETH_ADDRESS = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853';
|
||||||
|
const MOCK_VEGA_KEY =
|
||||||
|
'70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
asset = generateAsset();
|
asset = generateAsset();
|
||||||
@ -89,14 +93,17 @@ describe('Deposit form', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails when submitted with invalid ethereum address', async () => {
|
it('fails when Ethereum wallet not connected', async () => {
|
||||||
(useWeb3React as jest.Mock).mockReturnValue({ account: '123' });
|
(useWeb3React as jest.Mock).mockReturnValue({
|
||||||
|
isActive: false,
|
||||||
|
account: '',
|
||||||
|
});
|
||||||
render(<DepositForm {...props} />);
|
render(<DepositForm {...props} />);
|
||||||
|
|
||||||
fireEvent.submit(screen.getByTestId('deposit-form'));
|
fireEvent.submit(screen.getByTestId('deposit-form'));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText('Invalid Ethereum address')
|
await screen.findByText('Connect Ethereum wallet')
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -138,7 +145,7 @@ describe('Deposit form', () => {
|
|||||||
fireEvent.submit(screen.getByTestId('deposit-form'));
|
fireEvent.submit(screen.getByTestId('deposit-form'));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText('Insufficient amount in Ethereum wallet')
|
await screen.findByText('Amount is above deposit limit')
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -159,7 +166,7 @@ describe('Deposit form', () => {
|
|||||||
fireEvent.submit(screen.getByTestId('deposit-form'));
|
fireEvent.submit(screen.getByTestId('deposit-form'));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText('Amount is above approved amount')
|
await screen.findByText('Amount is above approved amount.')
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -193,9 +200,9 @@ describe('Deposit form', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles deposit approvals', () => {
|
it('handles deposit approvals', async () => {
|
||||||
const mockUseVegaWallet = useVegaWallet as jest.Mock;
|
const mockUseVegaWallet = useVegaWallet as jest.Mock;
|
||||||
mockUseVegaWallet.mockReturnValue({ pubKey: null });
|
mockUseVegaWallet.mockReturnValue({ pubKey: MOCK_VEGA_KEY });
|
||||||
|
|
||||||
const mockUseWeb3React = useWeb3React as jest.Mock;
|
const mockUseWeb3React = useWeb3React as jest.Mock;
|
||||||
mockUseWeb3React.mockReturnValue({
|
mockUseWeb3React.mockReturnValue({
|
||||||
@ -212,13 +219,18 @@ describe('Deposit form', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(
|
expect(screen.queryByLabelText('Amount')).not.toBeInTheDocument();
|
||||||
screen.getByText(`Approve ${asset.symbol}`, {
|
expect(screen.getByTestId('approve-warning')).toHaveTextContent(
|
||||||
selector: '[type="button"]',
|
`Deposits of ${asset.symbol} not approved`
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(props.submitApprove).toHaveBeenCalled();
|
fireEvent.click(
|
||||||
|
screen.getByRole('button', { name: `Approve ${asset.symbol}` })
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(props.submitApprove).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles submitting a deposit', async () => {
|
it('handles submitting a deposit', async () => {
|
||||||
@ -284,4 +296,55 @@ describe('Deposit form', () => {
|
|||||||
render(<DepositForm {...props} />);
|
render(<DepositForm {...props} />);
|
||||||
expect(await screen.queryAllByTestId('view-asset-details')).toHaveLength(0);
|
expect(await screen.queryAllByTestId('view-asset-details')).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders a connect button if Ethereum wallet is not connected', () => {
|
||||||
|
(useWeb3React as jest.Mock).mockReturnValue({
|
||||||
|
isActive: false,
|
||||||
|
account: '',
|
||||||
|
});
|
||||||
|
render(<DepositForm {...props} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', { name: 'Connect' })).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByLabelText('From (Ethereum address)')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders a disabled input if Ethereum wallet is connected', () => {
|
||||||
|
(useWeb3React as jest.Mock).mockReturnValue({
|
||||||
|
isActive: true,
|
||||||
|
account: MOCK_ETH_ADDRESS,
|
||||||
|
});
|
||||||
|
render(<DepositForm {...props} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('button', { name: 'Connect' })
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
const fromInput = screen.getByLabelText('From (Ethereum address)');
|
||||||
|
expect(fromInput).toHaveValue(MOCK_ETH_ADDRESS);
|
||||||
|
expect(fromInput).toBeDisabled();
|
||||||
|
expect(fromInput).toHaveAttribute('readonly');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prevents submission if you are on the wrong chain', () => {
|
||||||
|
(useWeb3React as jest.Mock).mockReturnValue({
|
||||||
|
isActive: true,
|
||||||
|
account: MOCK_ETH_ADDRESS,
|
||||||
|
chainId: 1,
|
||||||
|
});
|
||||||
|
(useWeb3ConnectStore as unknown as jest.Mock).mockImplementation(
|
||||||
|
// eslint-disable-next-line
|
||||||
|
(selector: (result: ReturnType<typeof useWeb3ConnectStore>) => any) => {
|
||||||
|
return selector({
|
||||||
|
desiredChainId: 11155111,
|
||||||
|
open: jest.fn(),
|
||||||
|
foo: 'asdf',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
render(<DepositForm {...props} />);
|
||||||
|
expect(screen.getByTestId('chain-error')).toHaveTextContent(
|
||||||
|
/this app only works on/i
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import type { Asset } from '@vegaprotocol/assets';
|
import type { Asset } from '@vegaprotocol/assets';
|
||||||
import { AssetOption } from '@vegaprotocol/assets';
|
import { AssetOption } from '@vegaprotocol/assets';
|
||||||
import {
|
import {
|
||||||
ethereumAddress,
|
|
||||||
t,
|
t,
|
||||||
|
ethereumAddress,
|
||||||
required,
|
required,
|
||||||
vegaPublicKey,
|
vegaPublicKey,
|
||||||
minSafe,
|
minSafe,
|
||||||
@ -14,17 +14,19 @@ import {
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
Icon,
|
|
||||||
Input,
|
Input,
|
||||||
InputError,
|
InputError,
|
||||||
RichSelect,
|
RichSelect,
|
||||||
|
Notification,
|
||||||
|
Intent,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useWeb3React } from '@web3-react/core';
|
import { useWeb3React } from '@web3-react/core';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
import type { ButtonHTMLAttributes } from 'react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { Controller, useForm, useWatch } from 'react-hook-form';
|
import type { FieldError } from 'react-hook-form';
|
||||||
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import { DepositLimits } from './deposit-limits';
|
import { DepositLimits } from './deposit-limits';
|
||||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
import {
|
import {
|
||||||
@ -72,7 +74,8 @@ export const DepositForm = ({
|
|||||||
isFaucetable,
|
isFaucetable,
|
||||||
}: DepositFormProps) => {
|
}: DepositFormProps) => {
|
||||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||||
const { account } = useWeb3React();
|
const openDialog = useWeb3ConnectStore((store) => store.open);
|
||||||
|
const { isActive, account } = useWeb3React();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@ -83,26 +86,27 @@ export const DepositForm = ({
|
|||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useForm<FormFields>({
|
} = useForm<FormFields>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
from: account,
|
|
||||||
to: pubKey ? pubKey : undefined,
|
to: pubKey ? pubKey : undefined,
|
||||||
asset: selectedAsset?.id || '',
|
asset: selectedAsset?.id || '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onDeposit = async (fields: FormFields) => {
|
const onSubmit = async (fields: FormFields) => {
|
||||||
if (!selectedAsset || selectedAsset.source.__typename !== 'ERC20') {
|
if (!selectedAsset || selectedAsset.source.__typename !== 'ERC20') {
|
||||||
throw new Error('Invalid asset');
|
throw new Error('Invalid asset');
|
||||||
}
|
}
|
||||||
|
|
||||||
submitDeposit({
|
if (approved) {
|
||||||
assetSource: selectedAsset.source.contractAddress,
|
submitDeposit({
|
||||||
amount: fields.amount,
|
assetSource: selectedAsset.source.contractAddress,
|
||||||
vegaPublicKey: fields.to,
|
amount: fields.amount,
|
||||||
});
|
vegaPublicKey: fields.to,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
submitApprove();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const amount = useWatch({ name: 'amount', control });
|
|
||||||
|
|
||||||
const maxAmount = useMemo(() => {
|
const maxAmount = useMemo(() => {
|
||||||
const maxApproved = allowance ? allowance : new BigNumber(0);
|
const maxApproved = allowance ? allowance : new BigNumber(0);
|
||||||
const maxAvailable = balance ? balance : new BigNumber(0);
|
const maxAvailable = balance ? balance : new BigNumber(0);
|
||||||
@ -133,9 +137,12 @@ export const DepositForm = ({
|
|||||||
return minViableAmount;
|
return minViableAmount;
|
||||||
}, [selectedAsset]);
|
}, [selectedAsset]);
|
||||||
|
|
||||||
|
const approved = allowance && allowance.isGreaterThan(0) ? true : false;
|
||||||
|
const formState = getFormState(selectedAsset, isActive, approved);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(onDeposit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
noValidate={true}
|
noValidate={true}
|
||||||
data-testid="deposit-form"
|
data-testid="deposit-form"
|
||||||
>
|
>
|
||||||
@ -143,19 +150,54 @@ export const DepositForm = ({
|
|||||||
label={t('From (Ethereum address)')}
|
label={t('From (Ethereum address)')}
|
||||||
labelFor="ethereum-address"
|
labelFor="ethereum-address"
|
||||||
>
|
>
|
||||||
<Input
|
<Controller
|
||||||
id="ethereum-address"
|
name="from"
|
||||||
{...register('from', {
|
control={control}
|
||||||
|
rules={{
|
||||||
validate: {
|
validate: {
|
||||||
required,
|
required: (value) => {
|
||||||
|
if (!value) return t('Connect Ethereum wallet');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
ethereumAddress,
|
ethereumAddress,
|
||||||
},
|
},
|
||||||
})}
|
}}
|
||||||
/>
|
defaultValue={account}
|
||||||
<EthereumButton
|
render={() => {
|
||||||
clearAddress={() => {
|
if (isActive && account) {
|
||||||
setValue('from', '');
|
return (
|
||||||
clearErrors('from');
|
<>
|
||||||
|
<Input
|
||||||
|
id="ethereum-address"
|
||||||
|
value={account}
|
||||||
|
readOnly={true}
|
||||||
|
disabled={true}
|
||||||
|
{...register('from', {
|
||||||
|
validate: {
|
||||||
|
required,
|
||||||
|
ethereumAddress,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<DisconnectEthereumButton
|
||||||
|
onDisconnect={() => {
|
||||||
|
setValue('from', ''); // clear from value so required ethereum connection validation works
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={openDialog}
|
||||||
|
variant="primary"
|
||||||
|
fill={true}
|
||||||
|
type="button"
|
||||||
|
data-testid="connect-eth-wallet-btn"
|
||||||
|
>
|
||||||
|
{t('Connect')}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{errors.from?.message && (
|
{errors.from?.message && (
|
||||||
@ -241,148 +283,136 @@ export const DepositForm = ({
|
|||||||
deposited={deposited}
|
deposited={deposited}
|
||||||
balance={balance}
|
balance={balance}
|
||||||
asset={selectedAsset}
|
asset={selectedAsset}
|
||||||
|
allowance={allowance}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<FormGroup label={t('Amount')} labelFor="amount">
|
{formState === 'deposit' && (
|
||||||
<Input
|
<FormGroup label={t('Amount')} labelFor="amount">
|
||||||
type="number"
|
<Input
|
||||||
autoComplete="off"
|
type="number"
|
||||||
id="amount"
|
autoComplete="off"
|
||||||
{...register('amount', {
|
id="amount"
|
||||||
validate: {
|
{...register('amount', {
|
||||||
required,
|
validate: {
|
||||||
minSafe: (value) => minSafe(new BigNumber(min))(value),
|
required,
|
||||||
maxSafe: (v) => {
|
minSafe: (value) => minSafe(new BigNumber(min))(value),
|
||||||
const value = new BigNumber(v);
|
approved: (v) => {
|
||||||
if (value.isGreaterThan(maxAmount.available)) {
|
const value = new BigNumber(v);
|
||||||
return t('Insufficient amount in Ethereum wallet');
|
if (value.isGreaterThan(maxAmount.approved)) {
|
||||||
} else if (value.isGreaterThan(maxAmount.limit)) {
|
return t('Amount is above approved amount');
|
||||||
return t('Amount is above temporary deposit limit');
|
}
|
||||||
} else if (value.isGreaterThan(maxAmount.approved)) {
|
return true;
|
||||||
return t('Amount is above approved amount');
|
},
|
||||||
}
|
limit: (v) => {
|
||||||
return maxSafe(maxAmount.amount)(v);
|
const value = new BigNumber(v);
|
||||||
|
if (value.isGreaterThan(maxAmount.limit)) {
|
||||||
|
return t('Amount is above deposit limit');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
balance: (v) => {
|
||||||
|
const value = new BigNumber(v);
|
||||||
|
if (value.isGreaterThan(maxAmount.available)) {
|
||||||
|
return t('Insufficient amount in Ethereum wallet');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
maxSafe: (v) => {
|
||||||
|
return maxSafe(maxAmount.amount)(v);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})}
|
||||||
})}
|
/>
|
||||||
/>
|
{errors.amount?.message && (
|
||||||
{errors.amount?.message && (
|
<AmountError error={errors.amount} submitApprove={submitApprove} />
|
||||||
<InputError intent="danger" forInput="amount">
|
)}
|
||||||
{errors.amount.message}
|
{selectedAsset && balance && (
|
||||||
</InputError>
|
<UseButton
|
||||||
)}
|
onClick={() => {
|
||||||
{selectedAsset && balance && (
|
setValue('amount', balance.toFixed(selectedAsset.decimals));
|
||||||
<UseButton
|
clearErrors('amount');
|
||||||
onClick={() => {
|
}}
|
||||||
setValue('amount', balance.toFixed(selectedAsset.decimals));
|
>
|
||||||
clearErrors('amount');
|
{t('Use maximum')}
|
||||||
}}
|
</UseButton>
|
||||||
>
|
)}
|
||||||
{t('Use maximum')}
|
</FormGroup>
|
||||||
</UseButton>
|
)}
|
||||||
)}
|
<FormButton selectedAsset={selectedAsset} formState={formState} />
|
||||||
</FormGroup>
|
|
||||||
<FormButton
|
|
||||||
selectedAsset={selectedAsset}
|
|
||||||
amount={new BigNumber(amount || 0)}
|
|
||||||
allowance={allowance}
|
|
||||||
onApproveClick={submitApprove}
|
|
||||||
/>
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AmountError = ({
|
||||||
|
error,
|
||||||
|
submitApprove,
|
||||||
|
}: {
|
||||||
|
error: FieldError;
|
||||||
|
submitApprove: () => void;
|
||||||
|
}) => {
|
||||||
|
if (error.type === 'approved') {
|
||||||
|
return (
|
||||||
|
<InputError intent="danger" forInput="amount">
|
||||||
|
{error.message}.
|
||||||
|
<button onClick={submitApprove} className="underline ml-2">
|
||||||
|
{t('Update approve amount')}
|
||||||
|
</button>
|
||||||
|
</InputError>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<InputError intent="danger" forInput="amount">
|
||||||
|
{error.message}
|
||||||
|
</InputError>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
interface FormButtonProps {
|
interface FormButtonProps {
|
||||||
selectedAsset?: Asset;
|
selectedAsset?: Asset;
|
||||||
amount: BigNumber;
|
formState: ReturnType<typeof getFormState>;
|
||||||
allowance: BigNumber | undefined;
|
|
||||||
onApproveClick: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FormButton = ({
|
const FormButton = ({ selectedAsset, formState }: FormButtonProps) => {
|
||||||
selectedAsset,
|
|
||||||
amount,
|
|
||||||
allowance,
|
|
||||||
onApproveClick,
|
|
||||||
}: FormButtonProps) => {
|
|
||||||
const { open, desiredChainId } = useWeb3ConnectStore((store) => ({
|
|
||||||
open: store.open,
|
|
||||||
desiredChainId: store.desiredChainId,
|
|
||||||
}));
|
|
||||||
const { isActive, chainId } = useWeb3React();
|
const { isActive, chainId } = useWeb3React();
|
||||||
const approved =
|
const desiredChainId = useWeb3ConnectStore((store) => store.desiredChainId);
|
||||||
allowance && allowance.isGreaterThan(0) && amount.isLessThan(allowance);
|
const submitText =
|
||||||
let button = null;
|
formState === 'approve'
|
||||||
let message: ReactNode = '';
|
? t(`Approve ${selectedAsset ? selectedAsset.symbol : ''}`)
|
||||||
|
: t('Deposit');
|
||||||
if (!isActive) {
|
const invalidChain = isActive && chainId !== desiredChainId;
|
||||||
button = (
|
|
||||||
<Button onClick={open} data-testid="connect-eth-wallet-btn">
|
|
||||||
{t('Connect Ethereum wallet')}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
} else if (chainId !== desiredChainId) {
|
|
||||||
const chainName = getChainName(desiredChainId);
|
|
||||||
message = t(`This app only works on ${chainName}.`);
|
|
||||||
button = (
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
data-testid="deposit-submit"
|
|
||||||
variant="primary"
|
|
||||||
fill={true}
|
|
||||||
disabled={true}
|
|
||||||
>
|
|
||||||
{t('Deposit')}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
} else if (!selectedAsset) {
|
|
||||||
button = (
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
data-testid="deposit-submit"
|
|
||||||
variant="primary"
|
|
||||||
fill={true}
|
|
||||||
>
|
|
||||||
{t('Deposit')}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
} else if (approved) {
|
|
||||||
message = (
|
|
||||||
<>
|
|
||||||
<Icon name="tick" className="mr-2" />
|
|
||||||
<span>{t('Approved')}</span>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
button = (
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
data-testid="deposit-submit"
|
|
||||||
variant="primary"
|
|
||||||
fill={true}
|
|
||||||
>
|
|
||||||
{t('Deposit')}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
message = t(`Deposits of ${selectedAsset.symbol} not approved`);
|
|
||||||
button = (
|
|
||||||
<Button
|
|
||||||
onClick={onApproveClick}
|
|
||||||
data-testid="deposit-approve-submit"
|
|
||||||
variant="primary"
|
|
||||||
fill={true}
|
|
||||||
>
|
|
||||||
{t(`Approve ${selectedAsset.symbol}`)}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<>
|
||||||
{message && <p className="text-center">{message}</p>}
|
{formState === 'approve' && (
|
||||||
{button}
|
<div className="mb-2">
|
||||||
</div>
|
<Notification
|
||||||
|
intent={Intent.Warning}
|
||||||
|
testId="approve-warning"
|
||||||
|
message={t(`Deposits of ${selectedAsset?.symbol} not approved`)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{invalidChain && (
|
||||||
|
<div className="mb-2">
|
||||||
|
<Notification
|
||||||
|
intent={Intent.Danger}
|
||||||
|
testId="chain-error"
|
||||||
|
message={t(
|
||||||
|
`This app only works on ${getChainName(desiredChainId)}.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
data-testid="deposit-submit"
|
||||||
|
variant={isActive ? 'primary' : 'default'}
|
||||||
|
fill={true}
|
||||||
|
disabled={invalidChain}
|
||||||
|
>
|
||||||
|
{submitText}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -398,21 +428,20 @@ const UseButton = (props: UseButtonProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const EthereumButton = ({ clearAddress }: { clearAddress: () => void }) => {
|
const DisconnectEthereumButton = ({
|
||||||
const openDialog = useWeb3ConnectStore((state) => state.open);
|
onDisconnect,
|
||||||
const { isActive, connector } = useWeb3React();
|
}: {
|
||||||
|
onDisconnect: () => void;
|
||||||
|
}) => {
|
||||||
|
const { connector } = useWeb3React();
|
||||||
const [, , removeEagerConnector] = useLocalStorage(ETHEREUM_EAGER_CONNECT);
|
const [, , removeEagerConnector] = useLocalStorage(ETHEREUM_EAGER_CONNECT);
|
||||||
|
|
||||||
if (!isActive) {
|
|
||||||
return <UseButton onClick={openDialog}>{t('Connect')}</UseButton>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UseButton
|
<UseButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
connector.deactivate();
|
connector.deactivate();
|
||||||
clearAddress();
|
|
||||||
removeEagerConnector();
|
removeEagerConnector();
|
||||||
|
onDisconnect();
|
||||||
}}
|
}}
|
||||||
data-testid="disconnect-ethereum-wallet"
|
data-testid="disconnect-ethereum-wallet"
|
||||||
>
|
>
|
||||||
@ -420,3 +449,14 @@ const EthereumButton = ({ clearAddress }: { clearAddress: () => void }) => {
|
|||||||
</UseButton>
|
</UseButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getFormState = (
|
||||||
|
selectedAsset: Asset | undefined,
|
||||||
|
isActive: boolean,
|
||||||
|
approved: boolean
|
||||||
|
) => {
|
||||||
|
if (!selectedAsset) return 'deposit';
|
||||||
|
if (!isActive) return 'deposit';
|
||||||
|
if (approved) return 'deposit';
|
||||||
|
return 'approve';
|
||||||
|
};
|
||||||
|
@ -11,6 +11,7 @@ interface DepositLimitsProps {
|
|||||||
deposited: BigNumber;
|
deposited: BigNumber;
|
||||||
asset: Asset;
|
asset: Asset;
|
||||||
balance?: BigNumber;
|
balance?: BigNumber;
|
||||||
|
allowance?: BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DepositLimits = ({
|
export const DepositLimits = ({
|
||||||
@ -18,6 +19,7 @@ export const DepositLimits = ({
|
|||||||
deposited,
|
deposited,
|
||||||
asset,
|
asset,
|
||||||
balance,
|
balance,
|
||||||
|
allowance,
|
||||||
}: DepositLimitsProps) => {
|
}: DepositLimitsProps) => {
|
||||||
const limits = [
|
const limits = [
|
||||||
{
|
{
|
||||||
@ -44,6 +46,12 @@ export const DepositLimits = ({
|
|||||||
rawValue: max.minus(deposited),
|
rawValue: max.minus(deposited),
|
||||||
value: compactNumber(max.minus(deposited), asset.decimals),
|
value: compactNumber(max.minus(deposited), asset.decimals),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'ALLOWANCE',
|
||||||
|
label: t('Approved'),
|
||||||
|
rawValue: allowance,
|
||||||
|
value: allowance ? compactNumber(allowance, asset.decimals) : '-',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -27,12 +27,13 @@ export const AssetProposalNotification = ({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Notification
|
<div className="mb-2">
|
||||||
intent={Intent.Warning}
|
<Notification
|
||||||
message={message}
|
intent={Intent.Warning}
|
||||||
testId="asset-proposal-notification"
|
message={message}
|
||||||
className="mb-2"
|
testId="asset-proposal-notification"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ export const MarketProposalNotification = ({
|
|||||||
intent={Intent.Warning}
|
intent={Intent.Warning}
|
||||||
message={message}
|
message={message}
|
||||||
testId="market-proposal-notification"
|
testId="market-proposal-notification"
|
||||||
className="px-2 py-1"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,6 @@ type NotificationProps = {
|
|||||||
size?: ButtonSize;
|
size?: ButtonSize;
|
||||||
};
|
};
|
||||||
testId?: string;
|
testId?: string;
|
||||||
className?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIcon = (intent: Intent): IconName => {
|
const getIcon = (intent: Intent): IconName => {
|
||||||
@ -39,7 +38,6 @@ export const Notification = ({
|
|||||||
title,
|
title,
|
||||||
testId,
|
testId,
|
||||||
buttonProps,
|
buttonProps,
|
||||||
className,
|
|
||||||
}: NotificationProps) => {
|
}: NotificationProps) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -61,8 +59,7 @@ export const Notification = ({
|
|||||||
intent === Intent.Warning,
|
intent === Intent.Warning,
|
||||||
'bg-vega-pink-300 dark:bg-vega-pink-650': intent === Intent.Danger,
|
'bg-vega-pink-300 dark:bg-vega-pink-650': intent === Intent.Danger,
|
||||||
},
|
},
|
||||||
'border rounded p-2 flex items-start gap-2.5 my-4',
|
'border rounded p-2 flex items-start gap-2.5'
|
||||||
className
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
Loading…
Reference in New Issue
Block a user