diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js deleted file mode 100644 index ae14fca30..000000000 --- a/app/scripts/controllers/balance.js +++ /dev/null @@ -1,134 +0,0 @@ -import ObservableStore from 'obs-store' -import PendingBalanceCalculator from '../lib/pending-balance-calculator' -import { BN } from 'ethereumjs-util' - -export default class BalanceController { - - /** - * Controller responsible for storing and updating an account's balance. - * - * @typedef {Object} BalanceController - * @param {Object} opts - Initialize various properties of the class. - * @property {string} address A base 16 hex string. The account address which has the balance managed by this - * BalanceController. - * @property {AccountTracker} accountTracker Stores and updates the users accounts - * for which this BalanceController manages balance. - * @property {TransactionController} txController Stores, tracks and manages transactions. Here used to create a listener for - * transaction updates. - * @property {BlockTracker} blockTracker Tracks updates to blocks. On new blocks, this BalanceController updates its balance - * @property {Object} store The store for the ethBalance - * @property {string} store.ethBalance A base 16 hex string. The balance for the current account. - * @property {PendingBalanceCalculator} balanceCalc Used to calculate the accounts balance with possible pending - * transaction costs taken into account. - * - */ - constructor (opts = {}) { - this._validateParams(opts) - const { address, accountTracker, txController, blockTracker } = opts - - this.address = address - this.accountTracker = accountTracker - this.txController = txController - this.blockTracker = blockTracker - - const initState = { - ethBalance: undefined, - } - this.store = new ObservableStore(initState) - - this.balanceCalc = new PendingBalanceCalculator({ - getBalance: () => this._getBalance(), - getPendingTransactions: this._getPendingTransactions.bind(this), - }) - - this._registerUpdates() - } - - /** - * Updates the ethBalance property to the current pending balance - * - * @returns {Promise} - Promises undefined - */ - async updateBalance () { - const balance = await this.balanceCalc.getBalance() - this.store.updateState({ - ethBalance: balance, - }) - } - - /** - * Sets up listeners and subscriptions which should trigger an update of ethBalance. These updates include: - * - when a transaction changes state to 'submitted', 'confirmed' or 'failed' - * - when the current account changes (i.e. a new account is selected) - * - when there is a block update - * - * @private - * - */ - _registerUpdates () { - const update = this.updateBalance.bind(this) - - this.txController.on('tx:status-update', (_, status) => { - switch (status) { - case 'submitted': - case 'confirmed': - case 'failed': - update() - return - default: - return - } - }) - this.accountTracker.store.subscribe(update) - this.blockTracker.on('latest', update) - } - - /** - * Gets the balance, as a base 16 hex string, of the account at this BalanceController's current address. - * If the current account has no balance, returns undefined. - * - * @returns {Promise} - Promises a BN with a value equal to the balance of the current account, or undefined - * if the current account has no balance - * - */ - async _getBalance () { - const { accounts } = this.accountTracker.store.getState() - const entry = accounts[this.address] - const balance = entry.balance - return balance ? new BN(balance.substring(2), 16) : undefined - } - - /** - * Gets the pending transactions (i.e. those with a 'submitted' status). These are accessed from the - * TransactionController passed to this BalanceController during construction. - * - * @private - * @returns {Promise} - Promises an array of transaction objects. - * - */ - async _getPendingTransactions () { - const pending = this.txController.getFilteredTxList({ - from: this.address, - status: 'submitted', - err: undefined, - }) - return pending - } - - /** - * Validates that the passed options have all required properties. - * - * @param {Object} opts - The options object to validate - * @throws {string} Throw a custom error indicating that address, accountTracker, txController and blockTracker are - * missing and at least one is required - * - */ - _validateParams (opts) { - const { address, accountTracker, txController, blockTracker } = opts - if (!address || !accountTracker || !txController || !blockTracker) { - const error = 'Cannot construct a balance checker without address, accountTracker, txController, and blockTracker.' - throw new Error(error) - } - } - -} diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js deleted file mode 100644 index 083353041..000000000 --- a/app/scripts/lib/pending-balance-calculator.js +++ /dev/null @@ -1,79 +0,0 @@ -import { BN } from 'ethereumjs-util' -import { normalize } from 'eth-sig-util' - -export default class PendingBalanceCalculator { - - /** - * Used for calculating a users "pending balance": their current balance minus the total possible cost of all their - * pending transactions. - * - * @typedef {Object} PendingBalanceCalculator - * @param {Function} getBalance - Returns a promise of a BN of the current balance in Wei - * @param {Function} getPendingTransactions - Returns an array of TxMeta Objects, which have txParams properties, - * which include value, gasPrice, and gas, all in a base=16 hex format. - * - */ - constructor ({ getBalance, getPendingTransactions }) { - this.getPendingTransactions = getPendingTransactions - this.getNetworkBalance = getBalance - } - - /** - * Returns the users "pending balance": their current balance minus the total possible cost of all their - * pending transactions. - * - * @returns {Promise} - Promises a base 16 hex string that contains the user's "pending balance" - * - */ - async getBalance () { - const results = await Promise.all([ - this.getNetworkBalance(), - this.getPendingTransactions(), - ]) - - const [ balance, pending ] = results - if (!balance) { - return undefined - } - - const pendingValue = pending.reduce((total, tx) => { - return total.add(this.calculateMaxCost(tx)) - }, new BN(0)) - - return `0x${balance.sub(pendingValue).toString(16)}` - } - - /** - * Calculates the maximum possible cost of a single transaction, based on the value, gas price and gas limit. - * - * @param {Object} tx - Contains all that data about a transaction. - * @property {object} tx.txParams Contains data needed to calculate the maximum cost of the transaction: gas, - * gasLimit and value. - * - * @returns {string} - Returns a base 16 hex string that contains the maximum possible cost of the transaction. - */ - calculateMaxCost (tx) { - const txValue = tx.txParams.value - const value = this.hexToBn(txValue) - const gasPrice = this.hexToBn(tx.txParams.gasPrice) - - const gas = tx.txParams.gas - const gasLimit = tx.txParams.gasLimit - const gasLimitBn = this.hexToBn(gas || gasLimit) - - const gasCost = gasPrice.mul(gasLimitBn) - return value.add(gasCost) - } - - /** - * Converts a hex string to a BN object - * - * @param {string} hex - A number represented as a hex string - * @returns {Object} - A BN object - * - */ - hexToBn (hex) { - return new BN(normalize(hex).substring(2), 16) - } - -} diff --git a/test/lib/mock-tx-gen.js b/test/lib/mock-tx-gen.js deleted file mode 100644 index 720797d20..000000000 --- a/test/lib/mock-tx-gen.js +++ /dev/null @@ -1,41 +0,0 @@ -import { BN } from 'ethereumjs-util' - -const template = { - 'status': 'submitted', - 'history': [{}], - 'txParams': { - 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', - 'gas': '0x30d40', - 'value': '0x0', - 'nonce': '0x3', - }, -} - -class TxGenerator { - - constructor () { - this.txs = [] - } - - generate (tx = {}, opts = {}) { - const { count, fromNonce } = opts - let nonce = fromNonce || this.txs.length - const txs = [] - for (let i = 0; i < count; i++) { - txs.push(Object.assign({}, template, { - txParams: { - nonce: hexify(nonce++), - }, - }, tx)) - } - this.txs = this.txs.concat(txs) - return txs - } - -} - -function hexify (number) { - return '0x' + (new BN(number)).toString(16) -} - -export default TxGenerator diff --git a/test/unit/app/controllers/balance-controller.spec.js b/test/unit/app/controllers/balance-controller.spec.js deleted file mode 100644 index b912b23de..000000000 --- a/test/unit/app/controllers/balance-controller.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -import assert from 'assert' -import ObservableStore from 'obs-store' -import PollingBlockTracker from 'eth-block-tracker' -import BalanceController from '../../../../app/scripts/controllers/balance' -import AccountTracker from '../../../../app/scripts/lib/account-tracker' -import TransactionController from '../../../../app/scripts/controllers/transactions' -import { createTestProviderTools } from '../../../stub/provider' - -const provider = createTestProviderTools({ scaffold: {} }).provider - -const TEST_ADDRESS = '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc' - -const accounts = { - '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': { - balance: '0x5e942b06dc24c4d50', - address: TEST_ADDRESS, - }, -} - -describe('Balance Controller', function () { - - let balanceController - - it('errors when address, accountTracker, txController, or blockTracker', function () { - try { - balanceController = new BalanceController() - } catch (error) { - assert.equal(error.message, 'Cannot construct a balance checker without address, accountTracker, txController, and blockTracker.') - } - }) - - beforeEach(function () { - balanceController = new BalanceController({ - address: TEST_ADDRESS, - accountTracker: new AccountTracker({ - provider, - blockTracker: new PollingBlockTracker({ provider }), - }), - txController: new TransactionController({ - provider, - networkStore: new ObservableStore(), - blockTracker: new PollingBlockTracker({ provider }), - }), - blockTracker: new PollingBlockTracker({ provider }), - }) - - balanceController.accountTracker.store.updateState({ accounts }) - }) - - it('updates balance controller ethBalance from account tracker', async function () { - await balanceController.updateBalance() - const balanceControllerState = balanceController.store.getState() - assert.equal(balanceControllerState.ethBalance, '0x5e942b06dc24c4d50') - }) -}) diff --git a/test/unit/app/pending-balance-test.js b/test/unit/app/pending-balance-test.js deleted file mode 100644 index 2fe405eb5..000000000 --- a/test/unit/app/pending-balance-test.js +++ /dev/null @@ -1,85 +0,0 @@ -import assert from 'assert' -import PendingBalanceCalculator from '../../../app/scripts/lib/pending-balance-calculator' -import MockTxGen from '../../lib/mock-tx-gen' -import { BN } from 'ethereumjs-util' - -const zeroBn = new BN(0) -const etherBn = new BN(String(1e18)) -const ether = '0x' + etherBn.toString(16) - -describe('PendingBalanceCalculator', function () { - let balanceCalculator, pendingTxs - - describe('#calculateMaxCost(tx)', function () { - it('returns a BN for a given tx value', function () { - const txGen = new MockTxGen() - pendingTxs = txGen.generate({ - status: 'submitted', - txParams: { - value: ether, - gasPrice: '0x0', - gas: '0x0', - }, - }, { count: 1 }) - - const balanceCalculator = generateBalanceCalcWith([], zeroBn) - const result = balanceCalculator.calculateMaxCost(pendingTxs[0]) - assert.equal(result.toString(), etherBn.toString(), 'computes one ether') - }) - - it('calculates gas costs as well', function () { - const txGen = new MockTxGen() - pendingTxs = txGen.generate({ - status: 'submitted', - txParams: { - value: '0x0', - gasPrice: '0x2', - gas: '0x3', - }, - }, { count: 1 }) - - const balanceCalculator = generateBalanceCalcWith([], zeroBn) - const result = balanceCalculator.calculateMaxCost(pendingTxs[0]) - assert.equal(result.toString(), '6', 'computes 6 wei of gas') - }) - }) - - describe('if you have no pending txs and one ether', function () { - it('returns the network balance', async function () { - balanceCalculator = generateBalanceCalcWith([], etherBn) - const result = await balanceCalculator.getBalance() - assert.equal(result, ether, `gave ${result} needed ${ether}`) - }) - }) - - describe('if you have a one ether pending tx and one ether', function () { - it('returns the subtracted result', async function () { - const txGen = new MockTxGen() - pendingTxs = txGen.generate({ - status: 'submitted', - txParams: { - value: ether, - gasPrice: '0x0', - gas: '0x0', - }, - }, { count: 1 }) - - balanceCalculator = generateBalanceCalcWith(pendingTxs, etherBn) - - const result = await balanceCalculator.getBalance() - assert.equal(result, '0x0', `gave ${result} needed '0x0'`) - return true - }) - }) -}) - -function generateBalanceCalcWith (transactions, providerStub = zeroBn) { - const getPendingTransactions = async () => transactions - const getBalance = async () => providerStub - - return new PendingBalanceCalculator({ - getBalance, - getPendingTransactions, - }) -} -