From 714df393b406c515b0bb3abd4fcd8bde4ad53fde Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Sep 2017 14:27:21 -0700 Subject: Add test template --- test/unit/pending-balance-test.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/unit/pending-balance-test.js diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js new file mode 100644 index 000000000..845d6d552 --- /dev/null +++ b/test/unit/pending-balance-test.js @@ -0,0 +1,37 @@ +const assert = require('assert') +const PendingBalanceCalculator = require('../../app/scripts/lib/pending-balance-calculator') +const MockTxGen = require('../lib/mock-tx-gen') +const BN = require('ethereumjs-util').BN +let providerResultStub = {} + +describe('PendingBalanceCalculator', function () { + let nonceTracker + + describe('if you have no pending txs and one ether', function () { + const ether = '0x' + (new BN(1e18)).toString(16) + + beforeEach(function () { + nonceTracker = generateNonceTrackerWith([], ether) + }) + + it('returns the network balance', function () { + const result = nonceTracker.getBalance() + assert.equal(result, ether, 'returns one ether') + }) + }) +}) + +function generateBalaneCalcWith (transactions, providerStub = '0x0') { + const getPendingTransactions = () => transactions + providerResultStub.result = providerStub + const provider = { + sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, + _blockTracker: { + getCurrentBlock: () => '0x11b568', + }, + } + return new PendingBalanceCalculator({ + provider, + getPendingTransactions, + }) +} -- cgit v1.2.3 From f9a052deed8749a1c6dd544df561967a568749d9 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Sep 2017 14:36:15 -0700 Subject: Add first passing balance calc test --- app/scripts/lib/pending-balance-calculator.js | 18 ++++++++++++++++++ test/unit/pending-balance-test.js | 15 ++++++++------- 2 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 app/scripts/lib/pending-balance-calculator.js diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js new file mode 100644 index 000000000..5b30354a2 --- /dev/null +++ b/app/scripts/lib/pending-balance-calculator.js @@ -0,0 +1,18 @@ +const BN = require('ethereumjs-util').BN +const EthQuery = require('ethjs-query') + +class PendingBalanceCalculator { + + constructor ({ getBalance, getPendingTransactions }) { + this.getPendingTransactions = getPendingTransactions + this.getBalance = getBalance + } + + async getBalance() { + const balance = await this.getBalance + return balance + } + +} + +module.exports = PendingBalanceCalculator diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index 845d6d552..dcf1926f0 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -8,21 +8,22 @@ describe('PendingBalanceCalculator', function () { let nonceTracker describe('if you have no pending txs and one ether', function () { - const ether = '0x' + (new BN(1e18)).toString(16) + const ether = '0x' + (new BN(String(1e18))).toString(16) beforeEach(function () { - nonceTracker = generateNonceTrackerWith([], ether) + nonceTracker = generateBalaneCalcWith([], ether) }) - it('returns the network balance', function () { - const result = nonceTracker.getBalance() - assert.equal(result, ether, 'returns one ether') + it('returns the network balance', async function () { + const result = await nonceTracker.getBalance() + assert.equal(result, ether, `gave ${result} needed ${ether}`) }) }) }) function generateBalaneCalcWith (transactions, providerStub = '0x0') { - const getPendingTransactions = () => transactions + const getPendingTransactions = () => Promise.resolve(transactions) + const getBalance = () => Promise.resolve(providerStub) providerResultStub.result = providerStub const provider = { sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, @@ -31,7 +32,7 @@ function generateBalaneCalcWith (transactions, providerStub = '0x0') { }, } return new PendingBalanceCalculator({ - provider, + getBalance, getPendingTransactions, }) } -- cgit v1.2.3 From 74f7fc4613d136b57a4395d273ce4bf52d6685db Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Sep 2017 14:37:46 -0700 Subject: Check balances in parallel --- app/scripts/lib/pending-balance-calculator.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index 5b30354a2..4f6e03138 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -9,7 +9,14 @@ class PendingBalanceCalculator { } async getBalance() { - const balance = await this.getBalance + const results = await Promise.all([ + this.getBalance(), + this.getPendingTransactions(), + ]) + + const balance = results[0] + const pending = results[1] + return balance } -- cgit v1.2.3 From b6e8791bc2bc912d874edcc92fcf3c4ce5a9b72a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 11:59:15 -0700 Subject: test not passing --- app/scripts/lib/pending-balance-calculator.js | 18 +++++++++++++- test/unit/pending-balance-test.js | 35 ++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index 4f6e03138..9df87e34b 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -1,5 +1,6 @@ const BN = require('ethereumjs-util').BN const EthQuery = require('ethjs-query') +const normalize = require('eth-sig-util').normalize class PendingBalanceCalculator { @@ -9,15 +10,30 @@ class PendingBalanceCalculator { } async getBalance() { + console.log('getting balance') const results = await Promise.all([ this.getBalance(), this.getPendingTransactions(), ]) + console.dir(results) const balance = results[0] const pending = results[1] - return balance + console.dir({ balance, pending }) + + const pendingValue = pending.reduce(function (total, tx) { + return total.sub(this.valueFor(tx)) + }, new BN(0)) + + const balanceBn = new BN(normalize(balance)) + + return `0x${ balanceBn.sub(pendingValue).toString(16) }` + } + + valueFor (tx) { + const value = new BN(normalize(tx.txParams.value)) + return value } } diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index dcf1926f0..9077e8f14 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -5,20 +5,49 @@ const BN = require('ethereumjs-util').BN let providerResultStub = {} describe('PendingBalanceCalculator', function () { - let nonceTracker + let balanceCalculator describe('if you have no pending txs and one ether', function () { const ether = '0x' + (new BN(String(1e18))).toString(16) beforeEach(function () { - nonceTracker = generateBalaneCalcWith([], ether) + balanceCalculator = generateBalaneCalcWith([], ether) }) it('returns the network balance', async function () { - const result = await nonceTracker.getBalance() + 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 () { + const ether = '0x' + (new BN(String(1e18))).toString(16) + + beforeEach(function () { + const txGen = new MockTxGen() + pendingTxs = txGen.generate({ + status: 'submitted', + txParams: { + value: ether, + gasPrice: '0x0', + gas: '0x0', + } + }, { count: 1 }) + + balanceCalculator = generateBalaneCalcWith(pendingTxs, ether) + }) + + it('returns the network balance', async function () { + console.log('one') + console.dir(balanceCalculator) + const result = await balanceCalculator.getBalance() + console.log('two') + console.dir(result) + assert.equal(result, '0x0', `gave ${result} needed '0x0'`) + return true + }) + + }) }) function generateBalaneCalcWith (transactions, providerStub = '0x0') { -- cgit v1.2.3 From 40585744365c128d1f64c5bf93ee8cedc9e91dae Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:30:25 -0700 Subject: Add basic test for valueFor --- app/scripts/lib/pending-balance-calculator.js | 13 +++++++++---- test/unit/pending-balance-test.js | 25 ++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index 9df87e34b..f2c9ce379 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -6,13 +6,13 @@ class PendingBalanceCalculator { constructor ({ getBalance, getPendingTransactions }) { this.getPendingTransactions = getPendingTransactions - this.getBalance = getBalance + this.getNetworkBalance = getBalance } async getBalance() { console.log('getting balance') const results = await Promise.all([ - this.getBalance(), + this.getNetworkBalance(), this.getPendingTransactions(), ]) console.dir(results) @@ -21,18 +21,23 @@ class PendingBalanceCalculator { const pending = results[1] console.dir({ balance, pending }) + console.dir(pending) const pendingValue = pending.reduce(function (total, tx) { - return total.sub(this.valueFor(tx)) + return total.add(this.valueFor(tx)) }, new BN(0)) const balanceBn = new BN(normalize(balance)) + console.log(`subtracting ${pendingValue.toString()} from ${balanceBn.toString()}`) return `0x${ balanceBn.sub(pendingValue).toString(16) }` } valueFor (tx) { - const value = new BN(normalize(tx.txParams.value)) + const txValue = tx.txParams.value + const normalized = normalize(txValue).substring(2) + console.log({ txValue, normalized }) + const value = new BN(normalize(txValue).substring(2), 16) return value } diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index 9077e8f14..7f20270cb 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -4,11 +4,31 @@ const MockTxGen = require('../lib/mock-tx-gen') const BN = require('ethereumjs-util').BN let providerResultStub = {} +const etherBn = new BN(String(1e18)) +const ether = '0x' + etherBn.toString(16) + describe('PendingBalanceCalculator', function () { let balanceCalculator + describe('#valueFor(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 = generateBalaneCalcWith([], '0x0') + const result = balanceCalculator.valueFor(pendingTxs[0]) + assert.equal(result.toString(), etherBn.toString(), 'computes one ether') + }) + }) + describe('if you have no pending txs and one ether', function () { - const ether = '0x' + (new BN(String(1e18))).toString(16) beforeEach(function () { balanceCalculator = generateBalaneCalcWith([], ether) @@ -21,8 +41,6 @@ describe('PendingBalanceCalculator', function () { }) describe('if you have a one ether pending tx and one ether', function () { - const ether = '0x' + (new BN(String(1e18))).toString(16) - beforeEach(function () { const txGen = new MockTxGen() pendingTxs = txGen.generate({ @@ -40,6 +58,7 @@ describe('PendingBalanceCalculator', function () { it('returns the network balance', async function () { console.log('one') console.dir(balanceCalculator) + console.dir(balanceCalculator.getBalance.toString()) const result = await balanceCalculator.getBalance() console.log('two') console.dir(result) -- cgit v1.2.3 From 7b92268428cc2de4374bc669c524bb61959801f1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:43:10 -0700 Subject: Fix valueFor test --- app/scripts/lib/pending-balance-calculator.js | 15 ++++++++------- test/unit/pending-balance-test.js | 9 +++++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index f2c9ce379..e4ff1e050 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -15,32 +15,33 @@ class PendingBalanceCalculator { this.getNetworkBalance(), this.getPendingTransactions(), ]) - console.dir(results) const balance = results[0] const pending = results[1] - console.dir({ balance, pending }) console.dir(pending) - const pendingValue = pending.reduce(function (total, tx) { + const pendingValue = pending.reduce((total, tx) => { return total.add(this.valueFor(tx)) }, new BN(0)) - const balanceBn = new BN(normalize(balance)) - console.log(`subtracting ${pendingValue.toString()} from ${balanceBn.toString()}`) + console.log(`subtracting ${pendingValue.toString()} from ${balance.toString()}`) - return `0x${ balanceBn.sub(pendingValue).toString(16) }` + return `0x${ balance.sub(pendingValue).toString(16) }` } valueFor (tx) { const txValue = tx.txParams.value const normalized = normalize(txValue).substring(2) console.log({ txValue, normalized }) - const value = new BN(normalize(txValue).substring(2), 16) + const value = this.hexToBn(txValue) return value } + hexToBn (hex) { + return new BN(normalize(hex).substring(2), 16) + } + } module.exports = PendingBalanceCalculator diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index 7f20270cb..0937579e2 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -4,6 +4,7 @@ const MockTxGen = require('../lib/mock-tx-gen') const BN = require('ethereumjs-util').BN let providerResultStub = {} +const zeroBn = new BN(0) const etherBn = new BN(String(1e18)) const ether = '0x' + etherBn.toString(16) @@ -22,7 +23,7 @@ describe('PendingBalanceCalculator', function () { } }, { count: 1 }) - const balanceCalculator = generateBalaneCalcWith([], '0x0') + const balanceCalculator = generateBalanceCalcWith([], zeroBn) const result = balanceCalculator.valueFor(pendingTxs[0]) assert.equal(result.toString(), etherBn.toString(), 'computes one ether') }) @@ -31,7 +32,7 @@ describe('PendingBalanceCalculator', function () { describe('if you have no pending txs and one ether', function () { beforeEach(function () { - balanceCalculator = generateBalaneCalcWith([], ether) + balanceCalculator = generateBalanceCalcWith([], zeroBn) }) it('returns the network balance', async function () { @@ -52,7 +53,7 @@ describe('PendingBalanceCalculator', function () { } }, { count: 1 }) - balanceCalculator = generateBalaneCalcWith(pendingTxs, ether) + balanceCalculator = generateBalanceCalcWith(pendingTxs, etherBn) }) it('returns the network balance', async function () { @@ -69,7 +70,7 @@ describe('PendingBalanceCalculator', function () { }) }) -function generateBalaneCalcWith (transactions, providerStub = '0x0') { +function generateBalanceCalcWith (transactions, providerStub = zeroBn) { const getPendingTransactions = () => Promise.resolve(transactions) const getBalance = () => Promise.resolve(providerStub) providerResultStub.result = providerStub -- cgit v1.2.3 From 74c6de7d23c979c091028d8bd599f389e2090bc1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:45:00 -0700 Subject: Add constructor comment --- app/scripts/lib/pending-balance-calculator.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index e4ff1e050..d5e2e4c17 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -4,6 +4,11 @@ const normalize = require('eth-sig-util').normalize class PendingBalanceCalculator { + // Must be initialized with two functions: + // getBalance => Returns a promise of a BN of the current balance in Wei + // 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 -- cgit v1.2.3 From a95a3c7e4f4d1331394a7bf92a77678fe8087c04 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:47:27 -0700 Subject: Fix balance calc test --- app/scripts/lib/pending-balance-calculator.js | 2 ++ test/unit/pending-balance-test.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index d5e2e4c17..4e1189a65 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -25,6 +25,8 @@ class PendingBalanceCalculator { const pending = results[1] console.dir(pending) + console.dir(balance.toString()) + console.trace('but why') const pendingValue = pending.reduce((total, tx) => { return total.add(this.valueFor(tx)) diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index 0937579e2..a9b0f7b66 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -32,7 +32,7 @@ describe('PendingBalanceCalculator', function () { describe('if you have no pending txs and one ether', function () { beforeEach(function () { - balanceCalculator = generateBalanceCalcWith([], zeroBn) + balanceCalculator = generateBalanceCalcWith([], etherBn) }) it('returns the network balance', async function () { -- cgit v1.2.3 From c616581001a7413a289b108b347005d53fb14732 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:47:52 -0700 Subject: Remove logs --- app/scripts/lib/pending-balance-calculator.js | 8 -------- test/unit/pending-balance-test.js | 5 ----- 2 files changed, 13 deletions(-) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index 4e1189a65..8564f0134 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -15,7 +15,6 @@ class PendingBalanceCalculator { } async getBalance() { - console.log('getting balance') const results = await Promise.all([ this.getNetworkBalance(), this.getPendingTransactions(), @@ -24,23 +23,16 @@ class PendingBalanceCalculator { const balance = results[0] const pending = results[1] - console.dir(pending) - console.dir(balance.toString()) - console.trace('but why') - const pendingValue = pending.reduce((total, tx) => { return total.add(this.valueFor(tx)) }, new BN(0)) - console.log(`subtracting ${pendingValue.toString()} from ${balance.toString()}`) - return `0x${ balance.sub(pendingValue).toString(16) }` } valueFor (tx) { const txValue = tx.txParams.value const normalized = normalize(txValue).substring(2) - console.log({ txValue, normalized }) const value = this.hexToBn(txValue) return value } diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index a9b0f7b66..e1d5f9303 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -57,12 +57,7 @@ describe('PendingBalanceCalculator', function () { }) it('returns the network balance', async function () { - console.log('one') - console.dir(balanceCalculator) - console.dir(balanceCalculator.getBalance.toString()) const result = await balanceCalculator.getBalance() - console.log('two') - console.dir(result) assert.equal(result, '0x0', `gave ${result} needed '0x0'`) return true }) -- cgit v1.2.3 From 532a4040de191d455053aa11432dcb9b407c9bf4 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:52:25 -0700 Subject: Add test for computing gas price --- test/unit/pending-balance-test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index e1d5f9303..3029a849c 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -27,6 +27,22 @@ describe('PendingBalanceCalculator', function () { const result = balanceCalculator.valueFor(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.valueFor(pendingTxs[0]) + assert.equal(result.toString(), '6', 'computes one ether') + }) }) describe('if you have no pending txs and one ether', function () { -- cgit v1.2.3 From fadc0617df016ad1fa3d1c92e220a8e9ede6379d Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:52:49 -0700 Subject: Make tx calculations account for gas prices --- app/scripts/lib/pending-balance-calculator.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index 8564f0134..29f1fd63a 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -32,9 +32,15 @@ class PendingBalanceCalculator { valueFor (tx) { const txValue = tx.txParams.value - const normalized = normalize(txValue).substring(2) const value = this.hexToBn(txValue) - return value + 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) } hexToBn (hex) { -- cgit v1.2.3 From 65c00e9fbbb4496db4adee13c227e47ba85837c6 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:53:30 -0700 Subject: Improve test name --- test/unit/pending-balance-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index 3029a849c..17be0306b 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -72,7 +72,7 @@ describe('PendingBalanceCalculator', function () { balanceCalculator = generateBalanceCalcWith(pendingTxs, etherBn) }) - it('returns the network balance', async function () { + it('returns the subtracted result', async function () { const result = await balanceCalculator.getBalance() assert.equal(result, '0x0', `gave ${result} needed '0x0'`) return true -- cgit v1.2.3 From d4d7c6d89eeddbe865e32b0f3636cc9de2a17cc1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:54:28 -0700 Subject: Linted --- app/scripts/lib/pending-balance-calculator.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index 29f1fd63a..474ed3261 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -1,5 +1,4 @@ const BN = require('ethereumjs-util').BN -const EthQuery = require('ethjs-query') const normalize = require('eth-sig-util').normalize class PendingBalanceCalculator { @@ -27,7 +26,7 @@ class PendingBalanceCalculator { return total.add(this.valueFor(tx)) }, new BN(0)) - return `0x${ balance.sub(pendingValue).toString(16) }` + return `0x${balance.sub(pendingValue).toString(16)}` } valueFor (tx) { -- cgit v1.2.3 From 53a467cd1e2ab50168b06d36a98effcfd3db3a49 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 12 Sep 2017 15:06:19 -0700 Subject: Some progress --- app/scripts/controllers/balance.js | 28 ++++++++++ app/scripts/controllers/balances.js | 108 ++++++++++++++++++++++++++++++++++++ app/scripts/metamask-controller.js | 8 ++- test/unit/pending-balance-test.js | 1 + 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 app/scripts/controllers/balance.js create mode 100644 app/scripts/controllers/balances.js diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js new file mode 100644 index 000000000..5dfe266e3 --- /dev/null +++ b/app/scripts/controllers/balance.js @@ -0,0 +1,28 @@ +const ObservableStore = require('obs-store') +const normalizeAddress = require('eth-sig-util').normalize +const extend = require('xtend') +const PendingBalanceCalculator = require('../lib/pending-balance-calculator') + +class BalanceController { + + constructor (opts = {}) { + const { address, ethQuery, txController } = opts + this.ethQuery = ethQuery + this.txController = txController + + const initState = extend({ + ethBalance: undefined, + }, opts.initState) + this.store = new ObservableStore(initState) + + const { getBalance, getPendingTransactions } = opts + this.balanceCalc = new PendingBalanceCalculator({ + getBalance, + getPendingTransactions, + }) + this.updateBalance() + } + +} + +module.exports = BalanceController diff --git a/app/scripts/controllers/balances.js b/app/scripts/controllers/balances.js new file mode 100644 index 000000000..b0b366628 --- /dev/null +++ b/app/scripts/controllers/balances.js @@ -0,0 +1,108 @@ +const ObservableStore = require('obs-store') +const normalizeAddress = require('eth-sig-util').normalize +const extend = require('xtend') +const BalanceController = require('./balance') + +class BalancesController { + + constructor (opts = {}) { + const { ethStore, txController } = opts + this.ethStore = ethStore + this.txController = txController + + const initState = extend({ + balances: [], + }, opts.initState) + this.store = new ObservableStore(initState) + + this._initBalanceUpdating() + } + + // PUBLIC METHODS + + setSelectedAddress (_address) { + return new Promise((resolve, reject) => { + const address = normalizeAddress(_address) + this.store.updateState({ selectedAddress: address }) + resolve() + }) + } + + getSelectedAddress (_address) { + return this.store.getState().selectedAddress + } + + addToken (rawAddress, symbol, decimals) { + const address = normalizeAddress(rawAddress) + const newEntry = { address, symbol, decimals } + + const tokens = this.store.getState().tokens + const previousIndex = tokens.find((token, index) => { + return token.address === address + }) + + if (previousIndex) { + tokens[previousIndex] = newEntry + } else { + tokens.push(newEntry) + } + + this.store.updateState({ tokens }) + return Promise.resolve() + } + + getTokens () { + return this.store.getState().tokens + } + + updateFrequentRpcList (_url) { + return this.addToFrequentRpcList(_url) + .then((rpcList) => { + this.store.updateState({ frequentRpcList: rpcList }) + return Promise.resolve() + }) + } + + setCurrentAccountTab (currentAccountTab) { + return new Promise((resolve, reject) => { + this.store.updateState({ currentAccountTab }) + resolve() + }) + } + + addToFrequentRpcList (_url) { + const rpcList = this.getFrequentRpcList() + const index = rpcList.findIndex((element) => { return element === _url }) + if (index !== -1) { + rpcList.splice(index, 1) + } + if (_url !== 'http://localhost:8545') { + rpcList.push(_url) + } + if (rpcList.length > 2) { + rpcList.shift() + } + return Promise.resolve(rpcList) + } + + getFrequentRpcList () { + return this.store.getState().frequentRpcList + } + // + // PRIVATE METHODS + // + _initBalanceUpdating () { + const store = this.ethStore.getState() + const balances = store.accounts + + for (let address in balances) { + let updater = new BalancesController({ + address, + ethQuery: this.ethQuery, + txController: this.txController, + }) + } + } +} + +module.exports = BalancesController diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a007d6fc5..81e31a556 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -115,6 +115,12 @@ module.exports = class MetamaskController extends EventEmitter { }) this.txController.on('newUnaprovedTx', opts.showUnapprovedTx.bind(opts)) + // computed balances (accounting for pending transactions) + this.balancesController = new BalancesController({ + ethStore: this.ethStore, + txController: this.txController, + }) + // notices this.noticeController = new NoticeController({ initState: initState.NoticeController, @@ -647,4 +653,4 @@ module.exports = class MetamaskController extends EventEmitter { return Promise.resolve(rpcTarget) }) } -} \ No newline at end of file +} diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index 17be0306b..dde30fecc 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -96,3 +96,4 @@ function generateBalanceCalcWith (transactions, providerStub = zeroBn) { getPendingTransactions, }) } + -- cgit v1.2.3 From e4d7fb244790d547b03d18763aa1d8e501d88b89 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 11:39:39 -0700 Subject: Add state-labeled events to allow subscribing to any transaction's state change --- app/scripts/controllers/transactions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index fb3be6073..59a3f5329 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -434,6 +434,7 @@ module.exports = class TransactionController extends EventEmitter { const txMeta = this.getTx(txId) txMeta.status = status this.emit(`${txMeta.id}:${status}`, txId) + this.emit(`${status}`, txId) if (status === 'submitted' || status === 'rejected') { this.emit(`${txMeta.id}:finished`, txMeta) } -- cgit v1.2.3 From 86cd4e4fedbea9639de33827733b4b85ef988bee Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 14:20:19 -0700 Subject: Got pending balance updating correctly --- app/scripts/controllers/balance.js | 45 ++++++++++++++-- app/scripts/controllers/balances.js | 101 +++++++++--------------------------- app/scripts/metamask-controller.js | 4 ++ 3 files changed, 69 insertions(+), 81 deletions(-) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index 5dfe266e3..0d4ab7d4f 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -2,12 +2,14 @@ const ObservableStore = require('obs-store') const normalizeAddress = require('eth-sig-util').normalize const extend = require('xtend') const PendingBalanceCalculator = require('../lib/pending-balance-calculator') +const BN = require('ethereumjs-util').BN class BalanceController { constructor (opts = {}) { - const { address, ethQuery, txController } = opts - this.ethQuery = ethQuery + const { address, ethStore, txController } = opts + this.address = address + this.ethStore = ethStore this.txController = txController const initState = extend({ @@ -17,10 +19,43 @@ class BalanceController { const { getBalance, getPendingTransactions } = opts this.balanceCalc = new PendingBalanceCalculator({ - getBalance, - getPendingTransactions, + getBalance: () => Promise.resolve(this._getBalance()), + getPendingTransactions: this._getPendingTransactions.bind(this), }) - this.updateBalance() + + this.registerUpdates() + } + + async updateBalance () { + const balance = await this.balanceCalc.getBalance() + this.store.updateState({ + ethBalance: balance, + }) + } + + registerUpdates () { + const update = this.updateBalance.bind(this) + this.txController.on('submitted', update) + this.txController.on('confirmed', update) + this.txController.on('failed', update) + this.txController.blockTracker.on('block', update) + } + + _getBalance () { + const store = this.ethStore.getState() + const balances = store.accounts + const entry = balances[this.address] + const balance = entry.balance + return balance ? new BN(balance.substring(2), 16) : new BN(0) + } + + _getPendingTransactions () { + const pending = this.txController.getFilteredTxList({ + from: this.address, + status: 'submitted', + err: undefined, + }) + return Promise.resolve(pending) } } diff --git a/app/scripts/controllers/balances.js b/app/scripts/controllers/balances.js index b0b366628..cf3c8a757 100644 --- a/app/scripts/controllers/balances.js +++ b/app/scripts/controllers/balances.js @@ -11,97 +11,46 @@ class BalancesController { this.txController = txController const initState = extend({ - balances: [], + computedBalances: {}, }, opts.initState) this.store = new ObservableStore(initState) this._initBalanceUpdating() } - // PUBLIC METHODS - - setSelectedAddress (_address) { - return new Promise((resolve, reject) => { - const address = normalizeAddress(_address) - this.store.updateState({ selectedAddress: address }) - resolve() - }) - } - - getSelectedAddress (_address) { - return this.store.getState().selectedAddress + _initBalanceUpdating () { + const store = this.ethStore.getState() + this.addAnyAccountsFromStore(store) + this.ethStore.subscribe(this.addAnyAccountsFromStore.bind(this)) } - addToken (rawAddress, symbol, decimals) { - const address = normalizeAddress(rawAddress) - const newEntry = { address, symbol, decimals } - - const tokens = this.store.getState().tokens - const previousIndex = tokens.find((token, index) => { - return token.address === address - }) + addAnyAccountsFromStore(store) { + const balances = store.accounts - if (previousIndex) { - tokens[previousIndex] = newEntry - } else { - tokens.push(newEntry) + for (let address in balances) { + this.trackAddressIfNotAlready(address) } - - this.store.updateState({ tokens }) - return Promise.resolve() - } - - getTokens () { - return this.store.getState().tokens - } - - updateFrequentRpcList (_url) { - return this.addToFrequentRpcList(_url) - .then((rpcList) => { - this.store.updateState({ frequentRpcList: rpcList }) - return Promise.resolve() - }) } - setCurrentAccountTab (currentAccountTab) { - return new Promise((resolve, reject) => { - this.store.updateState({ currentAccountTab }) - resolve() - }) - } - - addToFrequentRpcList (_url) { - const rpcList = this.getFrequentRpcList() - const index = rpcList.findIndex((element) => { return element === _url }) - if (index !== -1) { - rpcList.splice(index, 1) - } - if (_url !== 'http://localhost:8545') { - rpcList.push(_url) + trackAddressIfNotAlready (address) { + const state = this.store.getState() + if (!(address in state.computedBalances)) { + this.trackAddress(address) } - if (rpcList.length > 2) { - rpcList.shift() - } - return Promise.resolve(rpcList) - } - - getFrequentRpcList () { - return this.store.getState().frequentRpcList } - // - // PRIVATE METHODS - // - _initBalanceUpdating () { - const store = this.ethStore.getState() - const balances = store.accounts - for (let address in balances) { - let updater = new BalancesController({ - address, - ethQuery: this.ethQuery, - txController: this.txController, - }) - } + trackAddress (address) { + let updater = new BalanceController({ + address, + ethStore: this.ethStore, + txController: this.txController, + }) + updater.store.subscribe((accountBalance) => { + let newState = this.store.getState() + newState.computedBalances[address] = accountBalance + this.store.updateState(newState) + }) + updater.updateBalance() } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 81e31a556..f2df45947 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -20,6 +20,7 @@ const BlacklistController = require('./controllers/blacklist') const MessageManager = require('./lib/message-manager') const PersonalMessageManager = require('./lib/personal-message-manager') const TransactionController = require('./controllers/transactions') +const BalancesController = require('./controllers/balances') const ConfigManager = require('./lib/config-manager') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') @@ -174,6 +175,7 @@ module.exports = class MetamaskController extends EventEmitter { this.networkController.store.subscribe(this.sendUpdate.bind(this)) this.ethStore.subscribe(this.sendUpdate.bind(this)) this.txController.memStore.subscribe(this.sendUpdate.bind(this)) + this.balancesController.store.subscribe(this.sendUpdate.bind(this)) this.messageManager.memStore.subscribe(this.sendUpdate.bind(this)) this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this)) this.keyringController.memStore.subscribe(this.sendUpdate.bind(this)) @@ -248,6 +250,7 @@ module.exports = class MetamaskController extends EventEmitter { const wallet = this.configManager.getWallet() const vault = this.keyringController.store.getState().vault const isInitialized = (!!wallet || !!vault) + return extend( { isInitialized, @@ -258,6 +261,7 @@ module.exports = class MetamaskController extends EventEmitter { this.messageManager.memStore.getState(), this.personalMessageManager.memStore.getState(), this.keyringController.memStore.getState(), + this.balancesController.store.getState(), this.preferencesController.store.getState(), this.addressBookController.store.getState(), this.currencyController.store.getState(), -- cgit v1.2.3 From 0ba649317592efced339980a729213876bc25a81 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 14:31:48 -0700 Subject: Use computed balance for tx confirmation --- ui/app/components/pending-tx.js | 6 +++--- ui/app/conf-tx.js | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 3e53d47f9..1cc8daebe 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -33,7 +33,7 @@ function PendingTx () { PendingTx.prototype.render = function () { const props = this.props - const { currentCurrency, blockGasLimit } = props + const { currentCurrency, blockGasLimit, computedBalances } = props const conversionRate = props.conversionRate const txMeta = this.gatherTxMeta() @@ -42,8 +42,8 @@ PendingTx.prototype.render = function () { // Account Details const address = txParams.from || props.selectedAddress const identity = props.identities[address] || { address: address } - const account = props.accounts[address] - const balance = account ? account.balance : '0x0' + const account = computedBalances[address] + const balance = account ? account.ethBalance : '0x0' // recipient check const isValidAddress = !txParams.to || util.isValidAddress(txParams.to) diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 1ee4166f7..8b93305eb 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -29,6 +29,7 @@ function mapStateToProps (state) { conversionRate: state.metamask.conversionRate, currentCurrency: state.metamask.currentCurrency, blockGasLimit: state.metamask.currentBlockGasLimit, + computedBalances: state.metamask.computedBalances, } } @@ -39,7 +40,7 @@ function ConfirmTxScreen () { ConfirmTxScreen.prototype.render = function () { const props = this.props - const { network, provider, unapprovedTxs, currentCurrency, + const { network, provider, unapprovedTxs, currentCurrency, computedBalances, unapprovedMsgs, unapprovedPersonalMsgs, conversionRate, blockGasLimit } = props var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, unapprovedPersonalMsgs, network) @@ -104,6 +105,7 @@ ConfirmTxScreen.prototype.render = function () { currentCurrency, blockGasLimit, unconfTxListLength, + computedBalances, // Actions buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress), sendTransaction: this.sendTransaction.bind(this), -- cgit v1.2.3 From a01921758b25d151cfb1c47d7235f59291c29945 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 15:02:05 -0700 Subject: Add computed balance to account detail view --- app/scripts/controllers/balance.js | 9 +++------ app/scripts/controllers/balances.js | 9 ++++++++- app/scripts/lib/pending-balance-calculator.js | 2 ++ app/scripts/metamask-controller.js | 4 ++++ ui/app/account-detail.js | 5 +++-- ui/app/conf-tx.js | 1 - 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index 0d4ab7d4f..b4e72e751 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -1,6 +1,4 @@ const ObservableStore = require('obs-store') -const normalizeAddress = require('eth-sig-util').normalize -const extend = require('xtend') const PendingBalanceCalculator = require('../lib/pending-balance-calculator') const BN = require('ethereumjs-util').BN @@ -12,12 +10,11 @@ class BalanceController { this.ethStore = ethStore this.txController = txController - const initState = extend({ + const initState = { ethBalance: undefined, - }, opts.initState) + } this.store = new ObservableStore(initState) - const { getBalance, getPendingTransactions } = opts this.balanceCalc = new PendingBalanceCalculator({ getBalance: () => Promise.resolve(this._getBalance()), getPendingTransactions: this._getPendingTransactions.bind(this), @@ -46,7 +43,7 @@ class BalanceController { const balances = store.accounts const entry = balances[this.address] const balance = entry.balance - return balance ? new BN(balance.substring(2), 16) : new BN(0) + return balance ? new BN(balance.substring(2), 16) : undefined } _getPendingTransactions () { diff --git a/app/scripts/controllers/balances.js b/app/scripts/controllers/balances.js index cf3c8a757..89c2ca95d 100644 --- a/app/scripts/controllers/balances.js +++ b/app/scripts/controllers/balances.js @@ -1,5 +1,4 @@ const ObservableStore = require('obs-store') -const normalizeAddress = require('eth-sig-util').normalize const extend = require('xtend') const BalanceController = require('./balance') @@ -14,10 +13,17 @@ class BalancesController { computedBalances: {}, }, opts.initState) this.store = new ObservableStore(initState) + this.balances = {} this._initBalanceUpdating() } + updateAllBalances () { + for (let address in this.balances) { + this.balances[address].updateBalance() + } + } + _initBalanceUpdating () { const store = this.ethStore.getState() this.addAnyAccountsFromStore(store) @@ -50,6 +56,7 @@ class BalancesController { newState.computedBalances[address] = accountBalance this.store.updateState(newState) }) + this.balances[address] = updater updater.updateBalance() } } diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index 474ed3261..c66bffbbb 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -22,6 +22,8 @@ class PendingBalanceCalculator { const balance = results[0] const pending = results[1] + if (!balance) return undefined + const pendingValue = pending.reduce((total, tx) => { return total.add(this.valueFor(tx)) }, new BN(0)) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index f2df45947..02c06ead2 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -121,6 +121,10 @@ module.exports = class MetamaskController extends EventEmitter { ethStore: this.ethStore, txController: this.txController, }) + this.networkController.on('networkDidChange', () => { + this.balancesController.updateAllBalances() + }) + this.balancesController.updateAllBalances() // notices this.noticeController = new NoticeController({ diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index 02089ecd0..90724dc3f 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -32,6 +32,7 @@ function mapStateToProps (state) { currentCurrency: state.metamask.currentCurrency, currentAccountTab: state.metamask.currentAccountTab, tokens: state.metamask.tokens, + computedBalances: state.metamask.computedBalances, } } @@ -45,7 +46,7 @@ AccountDetailScreen.prototype.render = function () { var selected = props.address || Object.keys(props.accounts)[0] var checksumAddress = selected && ethUtil.toChecksumAddress(selected) var identity = props.identities[selected] - var account = props.accounts[selected] + var account = props.computedBalances[selected] const { network, conversionRate, currentCurrency } = props return ( @@ -180,7 +181,7 @@ AccountDetailScreen.prototype.render = function () { }, [ h(EthBalance, { - value: account && account.balance, + value: account && account.ethBalance, conversionRate, currentCurrency, style: { diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 8b93305eb..15fb9a59f 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -49,7 +49,6 @@ ConfirmTxScreen.prototype.render = function () { var txParams = txData.params || {} var isNotification = isPopupOrNotification() === 'notification' - log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`) if (unconfTxList.length === 0) return h(Loading, { isLoading: true }) -- cgit v1.2.3 From c9585f166f3aebea29e7214026a942eb18e4884a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 15:21:18 -0700 Subject: Fix test --- test/unit/components/pending-tx-test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/unit/components/pending-tx-test.js b/test/unit/components/pending-tx-test.js index 22a98bc93..fdade1042 100644 --- a/test/unit/components/pending-tx-test.js +++ b/test/unit/components/pending-tx-test.js @@ -34,10 +34,15 @@ describe('PendingTx', function () { const renderer = ReactTestUtils.createRenderer() const newGasPrice = '0x77359400' + const computedBalances = {} + computedBalances[Object.keys(identities)[0]] = { + ethBalance: '0x00000000000000056bc75e2d63100000', + } const props = { identities, accounts: identities, txData, + computedBalances, sendTransaction: (txMeta, event) => { // Assert changes: const result = ethUtil.addHexPrefix(txMeta.txParams.gasPrice) -- cgit v1.2.3 From 977405fc7d89256a911e73b83a6678235fa1cfb8 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 13:33:53 -0700 Subject: Remove dead code from eth-store --- app/scripts/lib/eth-store.js | 44 +------------------------------------------- 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js index ebba98f5c..ff22eca4a 100644 --- a/app/scripts/lib/eth-store.js +++ b/app/scripts/lib/eth-store.js @@ -18,10 +18,6 @@ class EthereumStore extends ObservableStore { constructor (opts = {}) { super({ accounts: {}, - transactions: {}, - currentBlockNumber: '0', - currentBlockHash: '', - currentBlockGasLimit: '', }) this._provider = opts.provider this._query = new EthQuery(this._provider) @@ -50,21 +46,6 @@ class EthereumStore extends ObservableStore { this.updateState({ accounts }) } - addTransaction (txHash) { - const transactions = this.getState().transactions - transactions[txHash] = {} - this.updateState({ transactions }) - if (!this._currentBlockNumber) return - this._updateTransaction(this._currentBlockNumber, txHash, noop) - } - - removeTransaction (txHash) { - const transactions = this.getState().transactions - delete transactions[txHash] - this.updateState({ transactions }) - } - - // // private // @@ -72,12 +53,9 @@ class EthereumStore extends ObservableStore { _updateForBlock (block) { const blockNumber = '0x' + block.number.toString('hex') this._currentBlockNumber = blockNumber - this.updateState({ currentBlockNumber: parseInt(blockNumber) }) - this.updateState({ currentBlockHash: `0x${block.hash.toString('hex')}`}) - this.updateState({ currentBlockGasLimit: `0x${block.gasLimit.toString('hex')}` }) + async.parallel([ this._updateAccounts.bind(this), - this._updateTransactions.bind(this, blockNumber), ], (err) => { if (err) return console.error(err) this.emit('block', this.getState()) @@ -104,26 +82,6 @@ class EthereumStore extends ObservableStore { }) } - _updateTransactions (block, cb = noop) { - const transactions = this.getState().transactions - const txHashes = Object.keys(transactions) - async.each(txHashes, this._updateTransaction.bind(this, block), cb) - } - - _updateTransaction (block, txHash, cb = noop) { - // would use the block here to determine how many confirmations the tx has - const transactions = this.getState().transactions - this._query.getTransaction(txHash, (err, result) => { - if (err) return cb(err) - // only populate if the entry is still present - if (transactions[txHash]) { - transactions[txHash] = result - this.updateState({ transactions }) - } - cb(null, result) - }) - } - _getAccount (address, cb = noop) { const query = this._query async.parallel({ -- cgit v1.2.3 From 11c8c07bfc6677e347873f02ae8c401f8d6c4dcf Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 13:59:25 -0700 Subject: Refactor eth-store into account-tracker EthStore was only being used for tracking account balances and nonces now, so I removed its block-tracking duties, renamed it account-tracker, and removed it as a dependency from `KeyringController`, so that KRC can go live on without a hard dep on it. --- app/scripts/controllers/balances.js | 64 ------------------- app/scripts/controllers/computed-balances.js | 64 +++++++++++++++++++ app/scripts/controllers/transactions.js | 6 +- app/scripts/lib/account-tracker.js | 96 ++++++++++++++++++++++++++++ app/scripts/lib/eth-store.js | 96 ---------------------------- app/scripts/metamask-controller.js | 26 +++++--- 6 files changed, 179 insertions(+), 173 deletions(-) delete mode 100644 app/scripts/controllers/balances.js create mode 100644 app/scripts/controllers/computed-balances.js create mode 100644 app/scripts/lib/account-tracker.js delete mode 100644 app/scripts/lib/eth-store.js diff --git a/app/scripts/controllers/balances.js b/app/scripts/controllers/balances.js deleted file mode 100644 index 89c2ca95d..000000000 --- a/app/scripts/controllers/balances.js +++ /dev/null @@ -1,64 +0,0 @@ -const ObservableStore = require('obs-store') -const extend = require('xtend') -const BalanceController = require('./balance') - -class BalancesController { - - constructor (opts = {}) { - const { ethStore, txController } = opts - this.ethStore = ethStore - this.txController = txController - - const initState = extend({ - computedBalances: {}, - }, opts.initState) - this.store = new ObservableStore(initState) - this.balances = {} - - this._initBalanceUpdating() - } - - updateAllBalances () { - for (let address in this.balances) { - this.balances[address].updateBalance() - } - } - - _initBalanceUpdating () { - const store = this.ethStore.getState() - this.addAnyAccountsFromStore(store) - this.ethStore.subscribe(this.addAnyAccountsFromStore.bind(this)) - } - - addAnyAccountsFromStore(store) { - const balances = store.accounts - - for (let address in balances) { - this.trackAddressIfNotAlready(address) - } - } - - trackAddressIfNotAlready (address) { - const state = this.store.getState() - if (!(address in state.computedBalances)) { - this.trackAddress(address) - } - } - - trackAddress (address) { - let updater = new BalanceController({ - address, - ethStore: this.ethStore, - txController: this.txController, - }) - updater.store.subscribe((accountBalance) => { - let newState = this.store.getState() - newState.computedBalances[address] = accountBalance - this.store.updateState(newState) - }) - this.balances[address] = updater - updater.updateBalance() - } -} - -module.exports = BalancesController diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js new file mode 100644 index 000000000..a85eb5590 --- /dev/null +++ b/app/scripts/controllers/computed-balances.js @@ -0,0 +1,64 @@ +const ObservableStore = require('obs-store') +const extend = require('xtend') +const BalanceController = require('./balance') + +class ComputedbalancesController { + + constructor (opts = {}) { + const { ethStore, txController } = opts + this.ethStore = ethStore + this.txController = txController + + const initState = extend({ + computedBalances: {}, + }, opts.initState) + this.store = new ObservableStore(initState) + this.balances = {} + + this._initBalanceUpdating() + } + + updateAllBalances () { + for (let address in this.balances) { + this.balances[address].updateBalance() + } + } + + _initBalanceUpdating () { + const store = this.ethStore.getState() + this.addAnyAccountsFromStore(store) + this.ethStore.subscribe(this.addAnyAccountsFromStore.bind(this)) + } + + addAnyAccountsFromStore(store) { + const balances = store.accounts + + for (let address in balances) { + this.trackAddressIfNotAlready(address) + } + } + + trackAddressIfNotAlready (address) { + const state = this.store.getState() + if (!(address in state.computedBalances)) { + this.trackAddress(address) + } + } + + trackAddress (address) { + let updater = new BalanceController({ + address, + ethStore: this.ethStore, + txController: this.txController, + }) + updater.store.subscribe((accountBalance) => { + let newState = this.store.getState() + newState.computedBalances[address] = accountBalance + this.store.updateState(newState) + }) + this.balances[address] = updater + updater.updateBalance() + } +} + +module.exports = ComputedbalancesController diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 59a3f5329..2aff4e5ff 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -22,7 +22,7 @@ module.exports = class TransactionController extends EventEmitter { this.provider = opts.provider this.blockTracker = opts.blockTracker this.signEthTx = opts.signTransaction - this.ethStore = opts.ethStore + this.accountTracker = opts.accountTracker this.nonceTracker = new NonceTracker({ provider: this.provider, @@ -52,7 +52,7 @@ module.exports = class TransactionController extends EventEmitter { provider: this.provider, nonceTracker: this.nonceTracker, getBalance: (address) => { - const account = this.ethStore.getState().accounts[address] + const account = this.accountTracker.getState().accounts[address] if (!account) return return account.balance }, @@ -73,7 +73,7 @@ module.exports = class TransactionController extends EventEmitter { this.blockTracker.on('rawBlock', this.pendingTxTracker.checkForTxInBlock.bind(this.pendingTxTracker)) // this is a little messy but until ethstore has been either // removed or redone this is to guard against the race condition - // where ethStore hasent been populated by the results yet + // where accountTracker hasent been populated by the results yet this.blockTracker.once('latest', () => { this.blockTracker.on('latest', this.pendingTxTracker.resubmitPendingTxs.bind(this.pendingTxTracker)) }) diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js new file mode 100644 index 000000000..bf949597b --- /dev/null +++ b/app/scripts/lib/account-tracker.js @@ -0,0 +1,96 @@ +/* Account Tracker + * + * This module is responsible for tracking any number of accounts + * and caching their current balances & transaction counts. + * + * It also tracks transaction hashes, and checks their inclusion status + * on each new block. + */ + +const async = require('async') +const EthQuery = require('eth-query') +const ObservableStore = require('obs-store') +function noop () {} + + +class EthereumStore extends ObservableStore { + + constructor (opts = {}) { + super({ + accounts: {}, + }) + this._provider = opts.provider + this._query = new EthQuery(this._provider) + this._blockTracker = opts.blockTracker + // subscribe to latest block + this._blockTracker.on('block', this._updateForBlock.bind(this)) + // blockTracker.currentBlock may be null + this._currentBlockNumber = this._blockTracker.currentBlock + } + + // + // public + // + + addAccount (address) { + const accounts = this.getState().accounts + accounts[address] = {} + this.updateState({ accounts }) + if (!this._currentBlockNumber) return + this._updateAccount(address) + } + + removeAccount (address) { + const accounts = this.getState().accounts + delete accounts[address] + this.updateState({ accounts }) + } + + // + // private + // + + _updateForBlock (block) { + const blockNumber = '0x' + block.number.toString('hex') + this._currentBlockNumber = blockNumber + + async.parallel([ + this._updateAccounts.bind(this), + ], (err) => { + if (err) return console.error(err) + this.emit('block', this.getState()) + }) + } + + _updateAccounts (cb = noop) { + const accounts = this.getState().accounts + const addresses = Object.keys(accounts) + async.each(addresses, this._updateAccount.bind(this), cb) + } + + _updateAccount (address, cb = noop) { + const accounts = this.getState().accounts + this._getAccount(address, (err, result) => { + if (err) return cb(err) + result.address = address + // only populate if the entry is still present + if (accounts[address]) { + accounts[address] = result + this.updateState({ accounts }) + } + cb(null, result) + }) + } + + _getAccount (address, cb = noop) { + const query = this._query + async.parallel({ + balance: query.getBalance.bind(query, address), + nonce: query.getTransactionCount.bind(query, address), + code: query.getCode.bind(query, address), + }, cb) + } + +} + +module.exports = EthereumStore diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js deleted file mode 100644 index ff22eca4a..000000000 --- a/app/scripts/lib/eth-store.js +++ /dev/null @@ -1,96 +0,0 @@ -/* Ethereum Store - * - * This module is responsible for tracking any number of accounts - * and caching their current balances & transaction counts. - * - * It also tracks transaction hashes, and checks their inclusion status - * on each new block. - */ - -const async = require('async') -const EthQuery = require('eth-query') -const ObservableStore = require('obs-store') -function noop () {} - - -class EthereumStore extends ObservableStore { - - constructor (opts = {}) { - super({ - accounts: {}, - }) - this._provider = opts.provider - this._query = new EthQuery(this._provider) - this._blockTracker = opts.blockTracker - // subscribe to latest block - this._blockTracker.on('block', this._updateForBlock.bind(this)) - // blockTracker.currentBlock may be null - this._currentBlockNumber = this._blockTracker.currentBlock - } - - // - // public - // - - addAccount (address) { - const accounts = this.getState().accounts - accounts[address] = {} - this.updateState({ accounts }) - if (!this._currentBlockNumber) return - this._updateAccount(address) - } - - removeAccount (address) { - const accounts = this.getState().accounts - delete accounts[address] - this.updateState({ accounts }) - } - - // - // private - // - - _updateForBlock (block) { - const blockNumber = '0x' + block.number.toString('hex') - this._currentBlockNumber = blockNumber - - async.parallel([ - this._updateAccounts.bind(this), - ], (err) => { - if (err) return console.error(err) - this.emit('block', this.getState()) - }) - } - - _updateAccounts (cb = noop) { - const accounts = this.getState().accounts - const addresses = Object.keys(accounts) - async.each(addresses, this._updateAccount.bind(this), cb) - } - - _updateAccount (address, cb = noop) { - const accounts = this.getState().accounts - this._getAccount(address, (err, result) => { - if (err) return cb(err) - result.address = address - // only populate if the entry is still present - if (accounts[address]) { - accounts[address] = result - this.updateState({ accounts }) - } - cb(null, result) - }) - } - - _getAccount (address, cb = noop) { - const query = this._query - async.parallel({ - balance: query.getBalance.bind(query, address), - nonce: query.getTransactionCount.bind(query, address), - code: query.getCode.bind(query, address), - }, cb) - } - -} - -module.exports = EthereumStore diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 02c06ead2..b1cfe1a2d 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -4,7 +4,7 @@ const promiseToCallback = require('promise-to-callback') const pipe = require('pump') const Dnode = require('dnode') const ObservableStore = require('obs-store') -const EthStore = require('./lib/eth-store') +const AccountTracker = require('./lib/account-tracker') const EthQuery = require('eth-query') const streamIntoProvider = require('web3-stream-provider/handler') const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex @@ -81,19 +81,25 @@ module.exports = class MetamaskController extends EventEmitter { // eth data query tools this.ethQuery = new EthQuery(this.provider) - this.ethStore = new EthStore({ - provider: this.provider, - blockTracker: this.provider, - }) // key mgmt this.keyringController = new KeyringController({ initState: initState.KeyringController, - ethStore: this.ethStore, + accountTracker: this.accountTracker, getNetwork: this.networkController.getNetworkState.bind(this.networkController), }) + + // account tracker watches balances, nonces, and any code at their address. + this.accountTracker = new AccountTracker({ + provider: this.provider, + blockTracker: this.provider, + }) this.keyringController.on('newAccount', (address) => { this.preferencesController.setSelectedAddress(address) + this.accountTracker.addAccount(address) + }) + this.keyringController.on('removedAccount', (address) => { + this.accountTracker.removeAccount(address) }) // address book controller @@ -112,13 +118,13 @@ module.exports = class MetamaskController extends EventEmitter { provider: this.provider, blockTracker: this.provider, ethQuery: this.ethQuery, - ethStore: this.ethStore, + accountTracker: this.accountTracker, }) this.txController.on('newUnaprovedTx', opts.showUnapprovedTx.bind(opts)) // computed balances (accounting for pending transactions) this.balancesController = new BalancesController({ - ethStore: this.ethStore, + accountTracker: this.accountTracker, txController: this.txController, }) this.networkController.on('networkDidChange', () => { @@ -177,7 +183,7 @@ module.exports = class MetamaskController extends EventEmitter { // manual mem state subscriptions this.networkController.store.subscribe(this.sendUpdate.bind(this)) - this.ethStore.subscribe(this.sendUpdate.bind(this)) + this.accountTracker.subscribe(this.sendUpdate.bind(this)) this.txController.memStore.subscribe(this.sendUpdate.bind(this)) this.balancesController.store.subscribe(this.sendUpdate.bind(this)) this.messageManager.memStore.subscribe(this.sendUpdate.bind(this)) @@ -260,7 +266,7 @@ module.exports = class MetamaskController extends EventEmitter { isInitialized, }, this.networkController.store.getState(), - this.ethStore.getState(), + this.accountTracker.getState(), this.txController.memStore.getState(), this.messageManager.memStore.getState(), this.personalMessageManager.memStore.getState(), -- cgit v1.2.3 From d2a747e57e591af5beb2e7cef7fc73a363d9c742 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 14:06:54 -0700 Subject: Fix computed-balances controller reference --- app/scripts/metamask-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b1cfe1a2d..34d60d253 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -20,7 +20,7 @@ const BlacklistController = require('./controllers/blacklist') const MessageManager = require('./lib/message-manager') const PersonalMessageManager = require('./lib/personal-message-manager') const TransactionController = require('./controllers/transactions') -const BalancesController = require('./controllers/balances') +const BalancesController = require('./controllers/computed-balances') const ConfigManager = require('./lib/config-manager') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') -- cgit v1.2.3 From f01b0a818ba67c2549f14382056534768a255e5b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 14:13:56 -0700 Subject: Fix account-tracker references --- app/scripts/controllers/balance.js | 6 +++--- app/scripts/controllers/computed-balances.js | 10 +++++----- app/scripts/keyring-controller.js | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index b4e72e751..ddeb06cf9 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -5,9 +5,9 @@ const BN = require('ethereumjs-util').BN class BalanceController { constructor (opts = {}) { - const { address, ethStore, txController } = opts + const { address, accountTracker, txController } = opts this.address = address - this.ethStore = ethStore + this.accountTracker = accountTracker this.txController = txController const initState = { @@ -39,7 +39,7 @@ class BalanceController { } _getBalance () { - const store = this.ethStore.getState() + const store = this.accountTracker.getState() const balances = store.accounts const entry = balances[this.address] const balance = entry.balance diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js index a85eb5590..576746164 100644 --- a/app/scripts/controllers/computed-balances.js +++ b/app/scripts/controllers/computed-balances.js @@ -5,8 +5,8 @@ const BalanceController = require('./balance') class ComputedbalancesController { constructor (opts = {}) { - const { ethStore, txController } = opts - this.ethStore = ethStore + const { accountTracker, txController } = opts + this.accountTracker = accountTracker this.txController = txController const initState = extend({ @@ -25,9 +25,9 @@ class ComputedbalancesController { } _initBalanceUpdating () { - const store = this.ethStore.getState() + const store = this.accountTracker.getState() this.addAnyAccountsFromStore(store) - this.ethStore.subscribe(this.addAnyAccountsFromStore.bind(this)) + this.accountTracker.subscribe(this.addAnyAccountsFromStore.bind(this)) } addAnyAccountsFromStore(store) { @@ -48,7 +48,7 @@ class ComputedbalancesController { trackAddress (address) { let updater = new BalanceController({ address, - ethStore: this.ethStore, + accountTracker: this.accountTracker, txController: this.txController, }) updater.store.subscribe((accountBalance) => { diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index fd57fac70..fa470dd89 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -35,7 +35,7 @@ class KeyringController extends EventEmitter { keyrings: [], identities: {}, }) - this.ethStore = opts.ethStore + this.accountTracker = opts.accountTracker this.encryptor = encryptor this.keyrings = [] this.getNetwork = opts.getNetwork @@ -338,7 +338,7 @@ class KeyringController extends EventEmitter { // // Initializes the provided account array // Gives them numerically incremented nicknames, - // and adds them to the ethStore for regular balance checking. + // and adds them to the accountTracker for regular balance checking. setupAccounts (accounts) { return this.getAccounts() .then((loadedAccounts) => { @@ -361,7 +361,7 @@ class KeyringController extends EventEmitter { throw new Error('Problem loading account.') } const address = normalizeAddress(account) - this.ethStore.addAccount(address) + this.accountTracker.addAccount(address) return this.createNickname(address) } @@ -567,12 +567,12 @@ class KeyringController extends EventEmitter { clearKeyrings () { let accounts try { - accounts = Object.keys(this.ethStore.getState()) + accounts = Object.keys(this.accountTracker.getState()) } catch (e) { accounts = [] } accounts.forEach((address) => { - this.ethStore.removeAccount(address) + this.accountTracker.removeAccount(address) }) // clear keyrings from memory -- cgit v1.2.3 From 128cf40f91df6d78e2d5ca87608fe16e91a510fa Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 14:16:19 -0700 Subject: Fix accont-tracker merge bug --- app/scripts/metamask-controller.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 2fc5b4204..1dd09d0ad 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -86,6 +86,10 @@ module.exports = class MetamaskController extends EventEmitter { // eth data query tools this.ethQuery = new EthQuery(this.provider) + this.accountTracker = new AccountTracker({ + provider: this.provider, + blockTracker: this.blockTracker, + }) // key mgmt this.keyringController = new KeyringController({ -- cgit v1.2.3 From f128240e7f877280fa59bf22f2ea8285bb467022 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 14:19:14 -0700 Subject: Fix test references --- test/unit/keyring-controller-test.js | 2 +- test/unit/tx-controller-test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/keyring-controller-test.js b/test/unit/keyring-controller-test.js index 2d9a53723..34c314639 100644 --- a/test/unit/keyring-controller-test.js +++ b/test/unit/keyring-controller-test.js @@ -24,7 +24,7 @@ describe('KeyringController', function () { getTxList: () => [], getUnapprovedTxList: () => [], }, - ethStore: { + accountTracker: { addAccount (acct) { accounts.push(ethUtil.addHexPrefix(acct)) }, }, }) diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js index 7bb193242..7b875db66 100644 --- a/test/unit/tx-controller-test.js +++ b/test/unit/tx-controller-test.js @@ -27,7 +27,7 @@ describe('Transaction Controller', function () { networkStore: new ObservableStore(currentNetworkId), txHistoryLimit: 10, blockTracker: { getCurrentBlock: noop, on: noop, once: noop }, - ethStore: { getState: noop }, + accountTracker: { getState: noop }, signTransaction: (ethTx) => new Promise((resolve) => { ethTx.sign(privKey) resolve() @@ -431,4 +431,4 @@ describe('Transaction Controller', function () { }).catch(done) }) }) -}) \ No newline at end of file +}) -- cgit v1.2.3