diff --git a/apps/browser-extension-wallet/src/hooks/__tests__/useMaxAda.test.ts b/apps/browser-extension-wallet/src/hooks/__tests__/useMaxAda.test.ts index 51cd3e934..37b9b1533 100644 --- a/apps/browser-extension-wallet/src/hooks/__tests__/useMaxAda.test.ts +++ b/apps/browser-extension-wallet/src/hooks/__tests__/useMaxAda.test.ts @@ -7,14 +7,17 @@ import { Subject, of } from 'rxjs'; import { Wallet } from '@lace/cardano'; import { waitFor } from '@testing-library/react'; import { act } from 'react-dom/test-utils'; -const mockInitializeTx = jest.fn(); -const inspect = jest.fn().mockReturnThis(); + +const MIN_COINS_FOR_TOKENS = 1_155_080; +const TX_FEE = 155_381; + +const inspect = jest.fn(); const mockCreateTxBuilder = jest.fn().mockReturnValue({ inspect, build: jest.fn().mockReturnThis(), - addOutput: jest.fn().mockReturnThis() + addOutput: jest.fn().mockReturnThis(), + removeOutput: jest.fn().mockReturnThis() }); -const TX_FEE = 155_381; const inMemoryWallet = { balance: { utxo: { @@ -25,7 +28,6 @@ const inMemoryWallet = { } }, protocolParameters$: of({ coinsPerUtxoByte: 4310, maxValueSize: 5000 }), - initializeTx: mockInitializeTx, createTxBuilder: mockCreateTxBuilder }; @@ -38,14 +40,23 @@ jest.mock('../../stores', () => ({ }) })); +const outputMap = new Map(); + +jest.mock('../../views/browser-view/features/send-transaction', () => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...jest.requireActual('../../views/browser-view/features/send-transaction'), + useTransactionProps: () => ({ + outputMap + }) +})); + describe('Testing useMaxAda hook', () => { beforeEach(() => { - mockInitializeTx.mockImplementationOnce(() => ({ + inspect.mockResolvedValue({ inputSelection: { - // eslint-disable-next-line no-magic-numbers fee: BigInt(TX_FEE) } - })); + }); }); afterEach(() => { jest.clearAllMocks(); @@ -68,8 +79,8 @@ describe('Testing useMaxAda hook', () => { }); test('should return 0 in case there is an error', async () => { - mockInitializeTx.mockReset(); - mockInitializeTx.mockImplementation(async () => { + inspect.mockReset(); + inspect.mockImplementation(async () => { throw new Error('init tx error'); }); const { result } = renderHook(() => useMaxAda()); @@ -84,10 +95,9 @@ describe('Testing useMaxAda hook', () => { test('should return 0 if balance is minimum for coins', async () => { const { result } = renderHook(() => useMaxAda()); - act(() => { inMemoryWallet.balance.utxo.available$.next({ - coins: BigInt('1155080') + BigInt(TX_FEE), + coins: BigInt(MIN_COINS_FOR_TOKENS) + BigInt(TX_FEE), assets: new Map([ [ Wallet.Cardano.AssetId('659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41'), @@ -102,26 +112,31 @@ describe('Testing useMaxAda hook', () => { }); }); - test('should return 8874869', async () => { + test('should return balance minus fee', async () => { const { result } = renderHook(() => useMaxAda()); act(() => { inMemoryWallet.balance.utxo.available$.next({ coins: BigInt('10000000') }); }); await waitFor(() => { - expect(result.current.toString()).toBe('8874869'); + expect(result.current).toBe(BigInt('10000000') - BigInt(TX_FEE)); }); }); - test.each([[1], [3], [7]])('should return 8689539 minus adaErrorBuffer*%i', async (errorCount) => { - const { result } = renderHook(() => useMaxAda()); - + test.each([[1]])('should return balance minus fee and adaErrorBuffer times %i', async (errorCount) => { + inspect.mockResolvedValueOnce({ + inputSelection: { + fee: BigInt(TX_FEE) + } + }); Array.from({ length: errorCount }).forEach(() => { inspect.mockImplementationOnce(() => { throw new Error('Error'); }); }); + const { result } = renderHook(() => useMaxAda()); + act(() => { inMemoryWallet.balance.utxo.available$.next({ coins: BigInt('10000000'), @@ -135,7 +150,12 @@ describe('Testing useMaxAda hook', () => { }); await waitFor(() => { - expect(result.current).toBe(BigInt('8689539') - BigInt(UTXO_DEPLETED_ADA_BUFFER * errorCount)); + expect(result.current).toBe( + BigInt('10000000') - + BigInt(MIN_COINS_FOR_TOKENS) - + BigInt(TX_FEE) - + BigInt(UTXO_DEPLETED_ADA_BUFFER * errorCount) + ); }); }); }); diff --git a/apps/browser-extension-wallet/src/hooks/useMaxAda.ts b/apps/browser-extension-wallet/src/hooks/useMaxAda.ts index b5ea46d83..7603f6bc2 100644 --- a/apps/browser-extension-wallet/src/hooks/useMaxAda.ts +++ b/apps/browser-extension-wallet/src/hooks/useMaxAda.ts @@ -61,7 +61,7 @@ const getMinimunCoinsAndFee = async ({ address, balance, txBuilder, validateOutp const tx = await txBuilder.build().inspect(); props.outputs.forEach((output) => txBuilder.removeOutput(output)); - return { fee: tx.inputSelection.fee, minimumCoins: totalMinimumCoins.coinMissing }; + return { fee: tx.inputSelection.fee, minimumCoins: balance.assets ? totalMinimumCoins.coinMissing : BigInt(0) }; }; interface CreateTestOutputs { @@ -170,7 +170,7 @@ const calculateMaxAda = async ({ export const dCalculateMaxAda = pDebounce.promise(calculateMaxAda); export const useMaxAda = (): bigint => { - const [maxADA, setMaxADA] = useState(); + const [maxADA, setMaxADA] = useState(BigInt(0)); const { walletInfo, inMemoryWallet } = useWalletStore(); const balance = useObservable(inMemoryWallet?.balance?.utxo.available$); const availableRewards = useObservable(inMemoryWallet?.balance?.rewardAccounts?.rewards$); @@ -207,7 +207,6 @@ export const useMaxAda = (): bigint => { signal: abortController.signal, outputMap }); - if (!abortController.signal.aborted) { setMaxADA(result); } diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/send-transaction/components/Form/CoinInput/__tests__/useSelectedCoins.test.ts b/apps/browser-extension-wallet/src/views/browser-view/features/send-transaction/components/Form/CoinInput/__tests__/useSelectedCoins.test.ts index 2cbc16e24..9aee39a5b 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/send-transaction/components/Form/CoinInput/__tests__/useSelectedCoins.test.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/send-transaction/components/Form/CoinInput/__tests__/useSelectedCoins.test.ts @@ -10,7 +10,6 @@ const mockUseCurrencyStore = jest.fn().mockReturnValue({ fiatCurrency: { code: ' const mockUseWalletStore = jest.fn().mockReturnValue({ walletUI: { cardanoCoin: { id: '1', name: 'Cardano', decimals: 6, symbol: 'ADA' }, appMode: 'popup' } }); -const mockUseMaxAda = jest.fn().mockReturnValue(BigInt(100)); const mockUseCoinStateSelector = jest.fn().mockReturnValue(mockCoinStateSelector); const mockUseBuiltTxState = jest.fn().mockReturnValue({ builtTxData: { error: undefined } }); const mockUseAddressState = jest.fn().mockReturnValue({ address: undefined }); @@ -220,14 +219,13 @@ describe('useSelectedCoin', () => { ...mockCoinStateSelector, uiOutputs: [{ id: '1', value: '0' }] }); - mockUseMaxAda.mockReturnValueOnce(BigInt(10_000_000)); mockUseSpentBalances.mockReturnValueOnce({}); const props: UseSelectedCoinsProps = { assetBalances: new Map(), assets: new Map(), bundleId: 'bundleId', coinBalance: '12000000', - spendableCoin: BigInt(100) + spendableCoin: BigInt(10_000_000) }; const { result } = renderUseSelectedCoins(props); @@ -242,14 +240,13 @@ describe('useSelectedCoin', () => { ...mockCoinStateSelector, uiOutputs: [{ id: '1', value: '2' }] }); - mockUseMaxAda.mockReturnValueOnce(BigInt(10_000_000)); mockUseSpentBalances.mockReturnValueOnce({ '1': '5' }); const props: UseSelectedCoinsProps = { assetBalances: new Map(), assets: new Map(), bundleId: 'bundleId', coinBalance: '12000000', - spendableCoin: BigInt(100) + spendableCoin: BigInt(10_000_000) }; const { result } = renderUseSelectedCoins(props); @@ -264,14 +261,13 @@ describe('useSelectedCoin', () => { ...mockCoinStateSelector, uiOutputs: [{ id: '1', value: '3' }] }); - mockUseMaxAda.mockReturnValueOnce(BigInt(10_000_000)); mockUseSpentBalances.mockReturnValueOnce({ '1': '13' }); const props: UseSelectedCoinsProps = { assetBalances: new Map(), assets: new Map(), bundleId: 'bundleId', coinBalance: '12000000', - spendableCoin: BigInt(100) + spendableCoin: BigInt(10_000_000) }; const { result } = renderUseSelectedCoins(props);