import EventEmitter from 'events'; import { SINGLE_CALL_BALANCES_ADDRESS } from '../constants/contracts'; import { createTestProviderTools } from '../../../test/stub/provider'; import AccountTracker from './account-tracker'; const noop = () => true; const currentNetworkId = '5'; const VALID_ADDRESS = '0x0000000000000000000000000000000000000000'; const VALID_ADDRESS_TWO = '0x0000000000000000000000000000000000000001'; const INITIAL_BALANCE_1 = '0x1'; const INITIAL_BALANCE_2 = '0x2'; const UPDATE_BALANCE = '0xabc'; // The below three values were generated by running MetaMask in the browser // The response to eth_call, which is called via `ethContract.balances` // in `_updateAccountsViaBalanceChecker` of account-tracker.js, needs to be properly // formatted or else ethers will throw an error. const ETHERS_CONTRACT_BALANCES_ETH_CALL_RETURN = '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000038d7ea4c6800600000000000000000000000000000000000000000000000000000000000186a0'; const EXPECTED_CONTRACT_BALANCE_1 = '0x038d7ea4c68006'; const EXPECTED_CONTRACT_BALANCE_2 = '0x0186a0'; const mockAccounts = { [VALID_ADDRESS]: { address: VALID_ADDRESS, balance: INITIAL_BALANCE_1 }, [VALID_ADDRESS_TWO]: { address: VALID_ADDRESS_TWO, balance: INITIAL_BALANCE_2, }, }; describe('Account Tracker', () => { let provider, blockTrackerStub, providerResultStub, useMultiAccountBalanceChecker, accountTracker; beforeEach(() => { providerResultStub = { eth_getBalance: UPDATE_BALANCE, eth_call: ETHERS_CONTRACT_BALANCES_ETH_CALL_RETURN, }; provider = createTestProviderTools({ scaffold: providerResultStub, networkId: currentNetworkId, chainId: currentNetworkId, }).provider; blockTrackerStub = new EventEmitter(); blockTrackerStub.getCurrentBlock = noop; blockTrackerStub.getLatestBlock = noop; accountTracker = new AccountTracker({ provider, blockTracker: blockTrackerStub, preferencesController: { store: { getState: () => ({ useMultiAccountBalanceChecker, }), subscribe: noop, }, }, onboardingController: { store: { subscribe: noop, getState: noop, }, }, }); }); describe('_updateAccount', () => { it('should update the passed address account balance, and leave other balances unchanged, if useMultiAccountBalanceChecker is true', async () => { useMultiAccountBalanceChecker = true; accountTracker.store.updateState({ accounts: { ...mockAccounts } }); await accountTracker._updateAccount(VALID_ADDRESS); const newAccounts = accountTracker.store.getState(); const expectedAccounts = { accounts: { [VALID_ADDRESS]: { address: VALID_ADDRESS, balance: UPDATE_BALANCE }, [VALID_ADDRESS_TWO]: { address: VALID_ADDRESS_TWO, balance: INITIAL_BALANCE_2, }, }, currentBlockGasLimit: '', }; expect(newAccounts).toStrictEqual(expectedAccounts); }); it('should not change accounts if the passed address is not in accounts', async () => { accountTracker.store.updateState({ accounts: { ...mockAccounts } }); await accountTracker._updateAccount('fake address'); const newAccounts = accountTracker.store.getState(); const expectedAccounts = { accounts: { [VALID_ADDRESS]: { address: VALID_ADDRESS, balance: INITIAL_BALANCE_1, }, [VALID_ADDRESS_TWO]: { address: VALID_ADDRESS_TWO, balance: INITIAL_BALANCE_2, }, }, currentBlockGasLimit: '', }; expect(newAccounts).toStrictEqual(expectedAccounts); }); it('should update the passed address account balance, and set other balances to null, if useMultiAccountBalanceChecker is false', async () => { useMultiAccountBalanceChecker = false; accountTracker.store.updateState({ accounts: { ...mockAccounts } }); await accountTracker._updateAccount(VALID_ADDRESS); const newAccounts = accountTracker.store.getState(); const expectedAccounts = { accounts: { [VALID_ADDRESS]: { address: VALID_ADDRESS, balance: UPDATE_BALANCE }, [VALID_ADDRESS_TWO]: { address: VALID_ADDRESS_TWO, balance: null }, }, currentBlockGasLimit: '', }; expect(newAccounts).toStrictEqual(expectedAccounts); }); }); describe('_updateAccountsViaBalanceChecker', () => { it('should update the passed address account balance, and set other balances to null, if useMultiAccountBalanceChecker is false', async () => { useMultiAccountBalanceChecker = true; accountTracker.store.updateState({ accounts: { ...mockAccounts } }); await accountTracker._updateAccountsViaBalanceChecker( [VALID_ADDRESS], SINGLE_CALL_BALANCES_ADDRESS, ); const newAccounts = accountTracker.store.getState(); const expectedAccounts = { accounts: { [VALID_ADDRESS]: { address: VALID_ADDRESS, balance: EXPECTED_CONTRACT_BALANCE_1, }, [VALID_ADDRESS_TWO]: { address: VALID_ADDRESS_TWO, balance: null }, }, currentBlockGasLimit: '', }; expect(newAccounts).toStrictEqual(expectedAccounts); }); it('should update all balances if useMultiAccountBalanceChecker is true', async () => { useMultiAccountBalanceChecker = true; accountTracker.store.updateState({ accounts: { ...mockAccounts } }); await accountTracker._updateAccountsViaBalanceChecker( [VALID_ADDRESS, VALID_ADDRESS_TWO], SINGLE_CALL_BALANCES_ADDRESS, ); const newAccounts = accountTracker.store.getState(); const expectedAccounts = { accounts: { [VALID_ADDRESS]: { address: VALID_ADDRESS, balance: EXPECTED_CONTRACT_BALANCE_1, }, [VALID_ADDRESS_TWO]: { address: VALID_ADDRESS_TWO, balance: EXPECTED_CONTRACT_BALANCE_2, }, }, currentBlockGasLimit: '', }; expect(newAccounts).toStrictEqual(expectedAccounts); }); }); });