diff --git a/apps/trading-e2e/src/integration/capsule.cy.ts b/apps/trading-e2e/src/integration/capsule.cy.ts index a9d8735e9..456ff3e1a 100644 --- a/apps/trading-e2e/src/integration/capsule.cy.ts +++ b/apps/trading-e2e/src/integration/capsule.cy.ts @@ -72,7 +72,11 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => { cy.getByTestId('deposit-button').click(); connectEthereumWallet('Unknown'); 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.get('[data-testid="Return to deposit"]').click(); cy.get(amountField).clear().type('10'); @@ -407,7 +411,7 @@ describe('capsule', { tags: '@slow' }, () => { cy.getByTestId('deposit-button').click(); connectEthereumWallet('Unknown'); 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.get('[data-testid="Return to deposit"]').click(); cy.get(amountField).clear().type('10000'); diff --git a/apps/trading-e2e/src/integration/deposit.cy.ts b/apps/trading-e2e/src/integration/deposit.cy.ts index eb20a6630..ec53bfe9f 100644 --- a/apps/trading-e2e/src/integration/deposit.cy.ts +++ b/apps/trading-e2e/src/integration/deposit.cy.ts @@ -1,3 +1,5 @@ +import { removeDecimal } from '@vegaprotocol/cypress'; +import { ethers } from 'ethers'; import { connectEthereumWallet } from '../support/ethereum-wallet'; import { selectAsset } from '../support/helpers'; @@ -9,7 +11,7 @@ const formFieldError = 'input-error-text'; const ASSET_EURO = 1; describe('deposit form validation', { tags: '@smoke' }, () => { - before(() => { + function openDepositForm() { cy.mockWeb3Provider(); cy.mockSubscription(); cy.mockTradingPage(); @@ -20,10 +22,14 @@ describe('deposit form validation', { tags: '@smoke' }, () => { cy.getByTestId('deposit-button').click(); cy.wait('@Assets'); connectEthereumWallet('MetaMask'); - cy.getByTestId('deposit-submit').click(); + } + + before(() => { + openDepositForm(); }); it('handles empty fields', () => { + cy.getByTestId('deposit-submit').click(); cy.getByTestId(formFieldError).should('contain.text', 'Required'); cy.getByTestId(formFieldError).should('have.length', 2); }); @@ -44,6 +50,13 @@ describe('deposit form validation', { tags: '@smoke' }, () => { }); it('invalid amount', () => { + mockWeb3DepositCalls({ + allowance: '1000', + depositLifetimeLimit: '1000', + balance: '800', + deposited: '0', + dps: 5, + }); // Deposit amount smaller than minimum viable for selected asset // Select an amount so that we have a known decimal places value to work with selectAsset(ASSET_EURO); @@ -56,12 +69,16 @@ describe('deposit form validation', { tags: '@smoke' }, () => { }); it('insufficient funds', () => { - // 1001-DEPO-005 - // Deposit amount is valid, but less than approved. This will always be the case because our - // CI wallet wont have approved any assets + mockWeb3DepositCalls({ + allowance: '1000', + depositLifetimeLimit: '1000', + balance: '800', + deposited: '0', + dps: 5, + }); cy.get(amountField) .clear() - .type('100') + .type('850') .next(`[data-testid="${formFieldError}"]`) .should('have.text', 'Insufficient amount in Ethereum wallet'); }); @@ -88,3 +105,90 @@ describe('deposit actions', { tags: '@smoke' }, () => { 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), + }); + } + } + }); +} diff --git a/apps/trading-e2e/src/integration/wallets.cy.ts b/apps/trading-e2e/src/integration/wallets.cy.ts index 1798a406d..ee02b8873 100644 --- a/apps/trading-e2e/src/integration/wallets.cy.ts +++ b/apps/trading-e2e/src/integration/wallets.cy.ts @@ -17,19 +17,50 @@ describe('connect hosted wallet', { tags: '@smoke' }, () => { }); 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(); - mockConnectWallet(); cy.contains('Connect Vega wallet'); cy.contains('Hosted Fairground wallet'); cy.getByTestId('connectors-list') - .find('[data-testid="connector-jsonRpc"]') + .find('[data-testid="connector-hosted"]') .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'); }); 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('connectors-list') .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('#passphrase').click().type('invalid password'); 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('connectors-list') .find('[data-testid="connector-hosted"]') diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx index f8c3c6226..ac7848783 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx @@ -318,33 +318,37 @@ const SummaryMessage = memo( } if (!pubKey) { return ( - - You need a{' '} - - Vega wallet - {' '} - with {assetSymbol} to start trading in this market. -

- } - buttonProps={{ - text: t('Connect wallet'), - action: openVegaWalletDialog, - dataTestId: 'order-connect-wallet', - size: 'md', - }} - /> +
+ + You need a{' '} + + Vega wallet + {' '} + with {assetSymbol} to start trading in this market. +

+ } + buttonProps={{ + text: t('Connect wallet'), + action: openVegaWalletDialog, + dataTestId: 'order-connect-wallet', + size: 'md', + }} + /> +
); } if (errorMessage === SummaryValidationType.NoCollateral) { return ( - +
+ +
); } @@ -363,7 +367,11 @@ const SummaryMessage = memo( // If there is no blocking error but user doesn't have enough // balance render the margin warning, but still allow submission if (balanceError) { - return ; + return ( +
+ ; +
+ ); } // Show auction mode warning @@ -375,13 +383,15 @@ const SummaryMessage = memo( ].includes(marketData.marketTradingMode) ) { return ( - +
+ +
); } diff --git a/libs/deposits/src/lib/deposit-form.spec.tsx b/libs/deposits/src/lib/deposit-form.spec.tsx index 40598feae..b0e918377 100644 --- a/libs/deposits/src/lib/deposit-form.spec.tsx +++ b/libs/deposits/src/lib/deposit-form.spec.tsx @@ -4,10 +4,12 @@ import type { DepositFormProps } from './deposit-form'; import { DepositForm } from './deposit-form'; import * as Schema from '@vegaprotocol/types'; import { useVegaWallet } from '@vegaprotocol/wallet'; +import { useWeb3ConnectStore } from '@vegaprotocol/web3'; import { useWeb3React } from '@web3-react/core'; import type { AssetFieldsFragment } from '@vegaprotocol/assets'; jest.mock('@vegaprotocol/wallet'); +jest.mock('@vegaprotocol/web3'); jest.mock('@web3-react/core'); const mockConnector = { deactivate: jest.fn() }; @@ -37,6 +39,8 @@ function generateAsset(): AssetFieldsFragment { let asset: AssetFieldsFragment; let props: DepositFormProps; const MOCK_ETH_ADDRESS = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853'; +const MOCK_VEGA_KEY = + '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680'; beforeEach(() => { asset = generateAsset(); @@ -89,14 +93,17 @@ describe('Deposit form', () => { }); }); - it('fails when submitted with invalid ethereum address', async () => { - (useWeb3React as jest.Mock).mockReturnValue({ account: '123' }); + it('fails when Ethereum wallet not connected', async () => { + (useWeb3React as jest.Mock).mockReturnValue({ + isActive: false, + account: '', + }); render(); fireEvent.submit(screen.getByTestId('deposit-form')); expect( - await screen.findByText('Invalid Ethereum address') + await screen.findByText('Connect Ethereum wallet') ).toBeInTheDocument(); }); @@ -138,7 +145,7 @@ describe('Deposit form', () => { fireEvent.submit(screen.getByTestId('deposit-form')); expect( - await screen.findByText('Insufficient amount in Ethereum wallet') + await screen.findByText('Amount is above deposit limit') ).toBeInTheDocument(); }); @@ -159,7 +166,7 @@ describe('Deposit form', () => { fireEvent.submit(screen.getByTestId('deposit-form')); expect( - await screen.findByText('Amount is above approved amount') + await screen.findByText('Amount is above approved amount.') ).toBeInTheDocument(); }); @@ -193,9 +200,9 @@ describe('Deposit form', () => { }); }); - it('handles deposit approvals', () => { + it('handles deposit approvals', async () => { const mockUseVegaWallet = useVegaWallet as jest.Mock; - mockUseVegaWallet.mockReturnValue({ pubKey: null }); + mockUseVegaWallet.mockReturnValue({ pubKey: MOCK_VEGA_KEY }); const mockUseWeb3React = useWeb3React as jest.Mock; mockUseWeb3React.mockReturnValue({ @@ -212,13 +219,18 @@ describe('Deposit form', () => { /> ); - fireEvent.click( - screen.getByText(`Approve ${asset.symbol}`, { - selector: '[type="button"]', - }) + expect(screen.queryByLabelText('Amount')).not.toBeInTheDocument(); + expect(screen.getByTestId('approve-warning')).toHaveTextContent( + `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 () => { @@ -284,4 +296,55 @@ describe('Deposit form', () => { render(); 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(); + + 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(); + + 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) => any) => { + return selector({ + desiredChainId: 11155111, + open: jest.fn(), + foo: 'asdf', + }); + } + ); + render(); + expect(screen.getByTestId('chain-error')).toHaveTextContent( + /this app only works on/i + ); + }); }); diff --git a/libs/deposits/src/lib/deposit-form.tsx b/libs/deposits/src/lib/deposit-form.tsx index 6fc88a51d..2df087e12 100644 --- a/libs/deposits/src/lib/deposit-form.tsx +++ b/libs/deposits/src/lib/deposit-form.tsx @@ -1,8 +1,8 @@ import type { Asset } from '@vegaprotocol/assets'; import { AssetOption } from '@vegaprotocol/assets'; import { - ethereumAddress, t, + ethereumAddress, required, vegaPublicKey, minSafe, @@ -14,17 +14,19 @@ import { import { Button, FormGroup, - Icon, Input, InputError, RichSelect, + Notification, + Intent, } from '@vegaprotocol/ui-toolkit'; import { useVegaWallet } from '@vegaprotocol/wallet'; import { useWeb3React } from '@web3-react/core'; import BigNumber from 'bignumber.js'; -import type { ButtonHTMLAttributes, ReactNode } from 'react'; +import type { ButtonHTMLAttributes } 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 { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; import { @@ -72,7 +74,8 @@ export const DepositForm = ({ isFaucetable, }: DepositFormProps) => { const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); - const { account } = useWeb3React(); + const openDialog = useWeb3ConnectStore((store) => store.open); + const { isActive, account } = useWeb3React(); const { pubKey } = useVegaWallet(); const { register, @@ -83,26 +86,27 @@ export const DepositForm = ({ formState: { errors }, } = useForm({ defaultValues: { - from: account, to: pubKey ? pubKey : undefined, asset: selectedAsset?.id || '', }, }); - const onDeposit = async (fields: FormFields) => { + const onSubmit = async (fields: FormFields) => { if (!selectedAsset || selectedAsset.source.__typename !== 'ERC20') { throw new Error('Invalid asset'); } - submitDeposit({ - assetSource: selectedAsset.source.contractAddress, - amount: fields.amount, - vegaPublicKey: fields.to, - }); + if (approved) { + submitDeposit({ + assetSource: selectedAsset.source.contractAddress, + amount: fields.amount, + vegaPublicKey: fields.to, + }); + } else { + submitApprove(); + } }; - const amount = useWatch({ name: 'amount', control }); - const maxAmount = useMemo(() => { const maxApproved = allowance ? allowance : new BigNumber(0); const maxAvailable = balance ? balance : new BigNumber(0); @@ -133,9 +137,12 @@ export const DepositForm = ({ return minViableAmount; }, [selectedAsset]); + const approved = allowance && allowance.isGreaterThan(0) ? true : false; + const formState = getFormState(selectedAsset, isActive, approved); + return (
@@ -143,19 +150,54 @@ export const DepositForm = ({ label={t('From (Ethereum address)')} labelFor="ethereum-address" > - { + if (!value) return t('Connect Ethereum wallet'); + return true; + }, ethereumAddress, }, - })} - /> - { - setValue('from', ''); - clearErrors('from'); + }} + defaultValue={account} + render={() => { + if (isActive && account) { + return ( + <> + + { + setValue('from', ''); // clear from value so required ethereum connection validation works + }} + /> + + ); + } + return ( + + ); }} /> {errors.from?.message && ( @@ -241,148 +283,136 @@ export const DepositForm = ({ deposited={deposited} balance={balance} asset={selectedAsset} + allowance={allowance} /> )} - - minSafe(new BigNumber(min))(value), - maxSafe: (v) => { - const value = new BigNumber(v); - if (value.isGreaterThan(maxAmount.available)) { - return t('Insufficient amount in Ethereum wallet'); - } else if (value.isGreaterThan(maxAmount.limit)) { - return t('Amount is above temporary deposit limit'); - } else if (value.isGreaterThan(maxAmount.approved)) { - return t('Amount is above approved amount'); - } - return maxSafe(maxAmount.amount)(v); + {formState === 'deposit' && ( + + minSafe(new BigNumber(min))(value), + approved: (v) => { + const value = new BigNumber(v); + if (value.isGreaterThan(maxAmount.approved)) { + return t('Amount is above approved amount'); + } + return true; + }, + limit: (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} - - )} - {selectedAsset && balance && ( - { - setValue('amount', balance.toFixed(selectedAsset.decimals)); - clearErrors('amount'); - }} - > - {t('Use maximum')} - - )} - - + })} + /> + {errors.amount?.message && ( + + )} + {selectedAsset && balance && ( + { + setValue('amount', balance.toFixed(selectedAsset.decimals)); + clearErrors('amount'); + }} + > + {t('Use maximum')} + + )} + + )} + ); }; +const AmountError = ({ + error, + submitApprove, +}: { + error: FieldError; + submitApprove: () => void; +}) => { + if (error.type === 'approved') { + return ( + + {error.message}. + + + ); + } + return ( + + {error.message} + + ); +}; + interface FormButtonProps { selectedAsset?: Asset; - amount: BigNumber; - allowance: BigNumber | undefined; - onApproveClick: () => void; + formState: ReturnType; } -const FormButton = ({ - selectedAsset, - amount, - allowance, - onApproveClick, -}: FormButtonProps) => { - const { open, desiredChainId } = useWeb3ConnectStore((store) => ({ - open: store.open, - desiredChainId: store.desiredChainId, - })); +const FormButton = ({ selectedAsset, formState }: FormButtonProps) => { const { isActive, chainId } = useWeb3React(); - const approved = - allowance && allowance.isGreaterThan(0) && amount.isLessThan(allowance); - let button = null; - let message: ReactNode = ''; - - if (!isActive) { - button = ( - - ); - } else if (chainId !== desiredChainId) { - const chainName = getChainName(desiredChainId); - message = t(`This app only works on ${chainName}.`); - button = ( - - ); - } else if (!selectedAsset) { - button = ( - - ); - } else if (approved) { - message = ( - <> - - {t('Approved')} - - ); - button = ( - - ); - } else { - message = t(`Deposits of ${selectedAsset.symbol} not approved`); - button = ( - - ); - } - + const desiredChainId = useWeb3ConnectStore((store) => store.desiredChainId); + const submitText = + formState === 'approve' + ? t(`Approve ${selectedAsset ? selectedAsset.symbol : ''}`) + : t('Deposit'); + const invalidChain = isActive && chainId !== desiredChainId; return ( -
- {message &&

{message}

} - {button} -
+ <> + {formState === 'approve' && ( +
+ +
+ )} + {invalidChain && ( +
+ +
+ )} + + ); }; @@ -398,21 +428,20 @@ const UseButton = (props: UseButtonProps) => { ); }; -const EthereumButton = ({ clearAddress }: { clearAddress: () => void }) => { - const openDialog = useWeb3ConnectStore((state) => state.open); - const { isActive, connector } = useWeb3React(); +const DisconnectEthereumButton = ({ + onDisconnect, +}: { + onDisconnect: () => void; +}) => { + const { connector } = useWeb3React(); const [, , removeEagerConnector] = useLocalStorage(ETHEREUM_EAGER_CONNECT); - if (!isActive) { - return {t('Connect')}; - } - return ( { connector.deactivate(); - clearAddress(); removeEagerConnector(); + onDisconnect(); }} data-testid="disconnect-ethereum-wallet" > @@ -420,3 +449,14 @@ const EthereumButton = ({ clearAddress }: { clearAddress: () => void }) => { ); }; + +const getFormState = ( + selectedAsset: Asset | undefined, + isActive: boolean, + approved: boolean +) => { + if (!selectedAsset) return 'deposit'; + if (!isActive) return 'deposit'; + if (approved) return 'deposit'; + return 'approve'; +}; diff --git a/libs/deposits/src/lib/deposit-limits.tsx b/libs/deposits/src/lib/deposit-limits.tsx index 2977a784c..cb2a3ea62 100644 --- a/libs/deposits/src/lib/deposit-limits.tsx +++ b/libs/deposits/src/lib/deposit-limits.tsx @@ -11,6 +11,7 @@ interface DepositLimitsProps { deposited: BigNumber; asset: Asset; balance?: BigNumber; + allowance?: BigNumber; } export const DepositLimits = ({ @@ -18,6 +19,7 @@ export const DepositLimits = ({ deposited, asset, balance, + allowance, }: DepositLimitsProps) => { const limits = [ { @@ -44,6 +46,12 @@ export const DepositLimits = ({ rawValue: max.minus(deposited), value: compactNumber(max.minus(deposited), asset.decimals), }, + { + key: 'ALLOWANCE', + label: t('Approved'), + rawValue: allowance, + value: allowance ? compactNumber(allowance, asset.decimals) : '-', + }, ]; return ( diff --git a/libs/governance/src/components/asset-proposal-notification.tsx b/libs/governance/src/components/asset-proposal-notification.tsx index e1080dc5c..1ed187b21 100644 --- a/libs/governance/src/components/asset-proposal-notification.tsx +++ b/libs/governance/src/components/asset-proposal-notification.tsx @@ -27,12 +27,13 @@ export const AssetProposalNotification = ({ ); return ( - +
+ +
); } diff --git a/libs/governance/src/components/market-proposal-notification.tsx b/libs/governance/src/components/market-proposal-notification.tsx index 4a0ff39c0..f1ebb5b43 100644 --- a/libs/governance/src/components/market-proposal-notification.tsx +++ b/libs/governance/src/components/market-proposal-notification.tsx @@ -34,7 +34,6 @@ export const MarketProposalNotification = ({ intent={Intent.Warning} message={message} testId="market-proposal-notification" - className="px-2 py-1" /> ); diff --git a/libs/ui-toolkit/src/components/notification/notification.tsx b/libs/ui-toolkit/src/components/notification/notification.tsx index 5a6b0750c..07bf8353b 100644 --- a/libs/ui-toolkit/src/components/notification/notification.tsx +++ b/libs/ui-toolkit/src/components/notification/notification.tsx @@ -19,7 +19,6 @@ type NotificationProps = { size?: ButtonSize; }; testId?: string; - className?: string; }; const getIcon = (intent: Intent): IconName => { @@ -39,7 +38,6 @@ export const Notification = ({ title, testId, buttonProps, - className, }: NotificationProps) => { return (