From 714df393b406c515b0bb3abd4fcd8bde4ad53fde Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Sep 2017 14:27:21 -0700 Subject: [PATCH 01/37] 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, + }) +} From f9a052deed8749a1c6dd544df561967a568749d9 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Sep 2017 14:36:15 -0700 Subject: [PATCH 02/37] 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, }) } From 74f7fc4613d136b57a4395d273ce4bf52d6685db Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Sep 2017 14:37:46 -0700 Subject: [PATCH 03/37] 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 } From b6e8791bc2bc912d874edcc92fcf3c4ce5a9b72a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 11:59:15 -0700 Subject: [PATCH 04/37] 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') { From 40585744365c128d1f64c5bf93ee8cedc9e91dae Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:30:25 -0700 Subject: [PATCH 05/37] 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) From 7b92268428cc2de4374bc669c524bb61959801f1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:43:10 -0700 Subject: [PATCH 06/37] 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 From 74c6de7d23c979c091028d8bd599f389e2090bc1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:45:00 -0700 Subject: [PATCH 07/37] 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 From a95a3c7e4f4d1331394a7bf92a77678fe8087c04 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:47:27 -0700 Subject: [PATCH 08/37] 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 () { From c616581001a7413a289b108b347005d53fb14732 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:47:52 -0700 Subject: [PATCH 09/37] 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 }) From 532a4040de191d455053aa11432dcb9b407c9bf4 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:52:25 -0700 Subject: [PATCH 10/37] 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 () { From fadc0617df016ad1fa3d1c92e220a8e9ede6379d Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:52:49 -0700 Subject: [PATCH 11/37] 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) { From 65c00e9fbbb4496db4adee13c227e47ba85837c6 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:53:30 -0700 Subject: [PATCH 12/37] 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 From d4d7c6d89eeddbe865e32b0f3636cc9de2a17cc1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Sep 2017 12:54:28 -0700 Subject: [PATCH 13/37] 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) { From 53a467cd1e2ab50168b06d36a98effcfd3db3a49 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 12 Sep 2017 15:06:19 -0700 Subject: [PATCH 14/37] 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, }) } + From e4d7fb244790d547b03d18763aa1d8e501d88b89 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 11:39:39 -0700 Subject: [PATCH 15/37] 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) } From 86cd4e4fedbea9639de33827733b4b85ef988bee Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 14:20:19 -0700 Subject: [PATCH 16/37] 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(), From 0ba649317592efced339980a729213876bc25a81 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 14:31:48 -0700 Subject: [PATCH 17/37] 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), From a01921758b25d151cfb1c47d7235f59291c29945 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 15:02:05 -0700 Subject: [PATCH 18/37] 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 }) From c9585f166f3aebea29e7214026a942eb18e4884a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 13 Sep 2017 15:21:18 -0700 Subject: [PATCH 19/37] 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) From 977405fc7d89256a911e73b83a6678235fa1cfb8 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 13:33:53 -0700 Subject: [PATCH 20/37] 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({ From 11c8c07bfc6677e347873f02ae8c401f8d6c4dcf Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 13:59:25 -0700 Subject: [PATCH 21/37] 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. --- .../{balances.js => computed-balances.js} | 4 +-- app/scripts/controllers/transactions.js | 6 ++--- .../lib/{eth-store.js => account-tracker.js} | 2 +- app/scripts/metamask-controller.js | 26 ++++++++++++------- 4 files changed, 22 insertions(+), 16 deletions(-) rename app/scripts/controllers/{balances.js => computed-balances.js} (95%) rename app/scripts/lib/{eth-store.js => account-tracker.js} (99%) diff --git a/app/scripts/controllers/balances.js b/app/scripts/controllers/computed-balances.js similarity index 95% rename from app/scripts/controllers/balances.js rename to app/scripts/controllers/computed-balances.js index 89c2ca95d..a85eb5590 100644 --- a/app/scripts/controllers/balances.js +++ b/app/scripts/controllers/computed-balances.js @@ -2,7 +2,7 @@ const ObservableStore = require('obs-store') const extend = require('xtend') const BalanceController = require('./balance') -class BalancesController { +class ComputedbalancesController { constructor (opts = {}) { const { ethStore, txController } = opts @@ -61,4 +61,4 @@ class BalancesController { } } -module.exports = BalancesController +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/eth-store.js b/app/scripts/lib/account-tracker.js similarity index 99% rename from app/scripts/lib/eth-store.js rename to app/scripts/lib/account-tracker.js index ff22eca4a..bf949597b 100644 --- a/app/scripts/lib/eth-store.js +++ b/app/scripts/lib/account-tracker.js @@ -1,4 +1,4 @@ -/* Ethereum Store +/* Account Tracker * * This module is responsible for tracking any number of accounts * and caching their current balances & transaction counts. 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(), From d2a747e57e591af5beb2e7cef7fc73a363d9c742 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 14:06:54 -0700 Subject: [PATCH 22/37] 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') From f01b0a818ba67c2549f14382056534768a255e5b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 14:13:56 -0700 Subject: [PATCH 23/37] 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 From 128cf40f91df6d78e2d5ca87608fe16e91a510fa Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 14:16:19 -0700 Subject: [PATCH 24/37] 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({ From f128240e7f877280fa59bf22f2ea8285bb467022 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 14:19:14 -0700 Subject: [PATCH 25/37] 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 +}) From 40f1d0868401662c42f6a031549c9b023427ccef Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 25 Sep 2017 11:42:08 -0700 Subject: [PATCH 26/37] Made some requested changes --- app/scripts/controllers/balance.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index ddeb06cf9..840b7abc3 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -16,7 +16,7 @@ class BalanceController { this.store = new ObservableStore(initState) this.balanceCalc = new PendingBalanceCalculator({ - getBalance: () => Promise.resolve(this._getBalance()), + getBalance: () => this._getBalance(), getPendingTransactions: this._getPendingTransactions.bind(this), }) @@ -35,24 +35,24 @@ class BalanceController { this.txController.on('submitted', update) this.txController.on('confirmed', update) this.txController.on('failed', update) + this.accountTracker.subscribe(update) this.txController.blockTracker.on('block', update) } - _getBalance () { - const store = this.accountTracker.getState() - const balances = store.accounts - const entry = balances[this.address] + async _getBalance () { + const { accounts } = this.accountTracker.getState() + const entry = accounts[this.address] const balance = entry.balance return balance ? new BN(balance.substring(2), 16) : undefined } - _getPendingTransactions () { + async _getPendingTransactions () { const pending = this.txController.getFilteredTxList({ from: this.address, status: 'submitted', err: undefined, }) - return Promise.resolve(pending) + return pending } } From 8cd7329c91b047ef15c81b164075ea6c1d15b0df Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 25 Sep 2017 14:36:49 -0700 Subject: [PATCH 27/37] Implemented feedback --- app/scripts/controllers/balance.js | 4 ++-- app/scripts/lib/pending-balance-calculator.js | 8 +++----- test/unit/pending-balance-test.js | 20 +++++++------------ 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index 840b7abc3..9b2566852 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -20,7 +20,7 @@ class BalanceController { getPendingTransactions: this._getPendingTransactions.bind(this), }) - this.registerUpdates() + this._registerUpdates() } async updateBalance () { @@ -30,7 +30,7 @@ class BalanceController { }) } - registerUpdates () { + _registerUpdates () { const update = this.updateBalance.bind(this) this.txController.on('submitted', update) this.txController.on('confirmed', update) diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index c66bffbbb..cea642f1a 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -19,19 +19,17 @@ class PendingBalanceCalculator { this.getPendingTransactions(), ]) - const balance = results[0] - const pending = results[1] - + const [ balance, pending ] = results if (!balance) return undefined const pendingValue = pending.reduce((total, tx) => { - return total.add(this.valueFor(tx)) + return total.add(this.calculateMaxCost(tx)) }, new BN(0)) return `0x${balance.sub(pendingValue).toString(16)}` } - valueFor (tx) { + calculateMaxCost (tx) { const txValue = tx.txParams.value const value = this.hexToBn(txValue) const gasPrice = this.hexToBn(tx.txParams.gasPrice) diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js index dde30fecc..5048d487b 100644 --- a/test/unit/pending-balance-test.js +++ b/test/unit/pending-balance-test.js @@ -11,7 +11,7 @@ const ether = '0x' + etherBn.toString(16) describe('PendingBalanceCalculator', function () { let balanceCalculator - describe('#valueFor(tx)', function () { + describe('#calculateMaxCost(tx)', function () { it('returns a BN for a given tx value', function () { const txGen = new MockTxGen() pendingTxs = txGen.generate({ @@ -24,7 +24,7 @@ describe('PendingBalanceCalculator', function () { }, { count: 1 }) const balanceCalculator = generateBalanceCalcWith([], zeroBn) - const result = balanceCalculator.valueFor(pendingTxs[0]) + const result = balanceCalculator.calculateMaxCost(pendingTxs[0]) assert.equal(result.toString(), etherBn.toString(), 'computes one ether') }) @@ -40,8 +40,8 @@ describe('PendingBalanceCalculator', function () { }, { count: 1 }) const balanceCalculator = generateBalanceCalcWith([], zeroBn) - const result = balanceCalculator.valueFor(pendingTxs[0]) - assert.equal(result.toString(), '6', 'computes one ether') + const result = balanceCalculator.calculateMaxCost(pendingTxs[0]) + assert.equal(result.toString(), '6', 'computes 6 wei of gas') }) }) @@ -82,15 +82,9 @@ describe('PendingBalanceCalculator', function () { }) function generateBalanceCalcWith (transactions, providerStub = zeroBn) { - const getPendingTransactions = () => Promise.resolve(transactions) - const getBalance = () => Promise.resolve(providerStub) - providerResultStub.result = providerStub - const provider = { - sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, - _blockTracker: { - getCurrentBlock: () => '0x11b568', - }, - } + const getPendingTransactions = async () => transactions + const getBalance = async () => providerStub + return new PendingBalanceCalculator({ getBalance, getPendingTransactions, From 674aac83ce49d21606be7be7afdf1b6a8ceb386f Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 25 Sep 2017 14:39:22 -0700 Subject: [PATCH 28/37] Make blockTracker an independent param --- app/scripts/controllers/balance.js | 5 +++-- app/scripts/controllers/computed-balances.js | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index 9b2566852..ab0cfe907 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -5,10 +5,11 @@ const BN = require('ethereumjs-util').BN class BalanceController { constructor (opts = {}) { - const { address, accountTracker, txController } = opts + const { address, accountTracker, txController, blockTracker } = opts this.address = address this.accountTracker = accountTracker this.txController = txController + this.blockTracker = blockTracker const initState = { ethBalance: undefined, @@ -36,7 +37,7 @@ class BalanceController { this.txController.on('confirmed', update) this.txController.on('failed', update) this.accountTracker.subscribe(update) - this.txController.blockTracker.on('block', update) + this.blockTracker.on('block', update) } async _getBalance () { diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js index 576746164..2b27d128d 100644 --- a/app/scripts/controllers/computed-balances.js +++ b/app/scripts/controllers/computed-balances.js @@ -5,9 +5,10 @@ const BalanceController = require('./balance') class ComputedbalancesController { constructor (opts = {}) { - const { accountTracker, txController } = opts + const { accountTracker, txController, blockTracker } = opts this.accountTracker = accountTracker this.txController = txController + this.blockTracker = blockTracker const initState = extend({ computedBalances: {}, @@ -50,6 +51,7 @@ class ComputedbalancesController { address, accountTracker: this.accountTracker, txController: this.txController, + blockTracker: this.blockTracker, }) updater.store.subscribe((accountBalance) => { let newState = this.store.getState() From feed9a5a17f89ee319dc5558634e8c5c07b2ce65 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 25 Sep 2017 14:45:28 -0700 Subject: [PATCH 29/37] Add mock random value generator --- test/lib/mock-encryptor.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/lib/mock-encryptor.js b/test/lib/mock-encryptor.js index cdf13c507..ef229a82f 100644 --- a/test/lib/mock-encryptor.js +++ b/test/lib/mock-encryptor.js @@ -29,4 +29,8 @@ module.exports = { return 'WHADDASALT!' }, + getRandomValues () { + return 'SOO RANDO!!!1' + } + } From 1968d61431183300298f3ea17b5092865cb915bf Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 25 Sep 2017 15:23:37 -0700 Subject: [PATCH 30/37] Make encryptor configurable for keyring-controller --- app/scripts/keyring-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index 2a1af6e29..34e008ec4 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -37,7 +37,7 @@ class KeyringController extends EventEmitter { }) this.accountTracker = opts.accountTracker - this.encryptor = encryptor + this.encryptor = opts.encryptor || encryptor this.keyrings = [] this.getNetwork = opts.getNetwork } From e52d52b22ed00b1a761bf57cff54c7276c6450a7 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 26 Sep 2017 08:22:48 +0000 Subject: [PATCH 31/37] chore(package): update sinon to version 4.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bcfb6c1ac..09490096b 100644 --- a/package.json +++ b/package.json @@ -196,7 +196,7 @@ "react-addons-test-utils": "^15.5.1", "react-test-renderer": "^15.5.4", "react-testutils-additions": "^15.2.0", - "sinon": "^3.2.0", + "sinon": "^4.0.0", "tape": "^4.5.1", "testem": "^1.10.3", "uglifyify": "^4.0.2", From b46cb3ecb5a3a1b4a197f69960f932d69287aa62 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 26 Sep 2017 09:23:46 -0700 Subject: [PATCH 32/37] Fix token precision bug Had fixed this before in the dependency, but hadn't merged in that version bump yet :( Fixes #2162 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e692c58dc..f04136d78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Fix bug that could mis-render token balances when very small. (Not actually included in 3.9.9) + ## 3.10.3 2017-9-21 - Fix bug where metamask-dapp connections are lost on rpc error diff --git a/package.json b/package.json index bcfb6c1ac..5599f2c20 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "eth-query": "^2.1.2", "eth-sig-util": "^1.2.2", "eth-simple-keyring": "^1.1.1", - "eth-token-tracker": "^1.1.3", + "eth-token-tracker": "^1.1.4", "ethereumjs-tx": "^1.3.0", "ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9", "ethereumjs-wallet": "^0.6.0", From 5d300f146a679ba1a639ee9a568e8452c886c736 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 26 Sep 2017 09:38:43 -0700 Subject: [PATCH 33/37] Add computed balance to mock state --- development/states/first-time.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/development/states/first-time.json b/development/states/first-time.json index 683a61fdf..b2cc8ef8f 100644 --- a/development/states/first-time.json +++ b/development/states/first-time.json @@ -4,6 +4,7 @@ "isUnlocked": false, "rpcTarget": "https://rawtestrpc.metamask.io/", "identities": {}, + "computedBalances": {}, "frequentRpcList": [], "unapprovedTxs": {}, "currentCurrency": "USD", @@ -48,5 +49,6 @@ "isLoading": false, "warning": null }, - "identities": {} + "identities": {}, + "computedBalances": {} } From 9e3648c668aed1f3e632efe1693d6a2e0aa76617 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 26 Sep 2017 11:33:36 -0700 Subject: [PATCH 34/37] Pass blocktracker to balances controller --- app/scripts/metamask-controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 30e511e19..cca796678 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -136,6 +136,7 @@ module.exports = class MetamaskController extends EventEmitter { this.balancesController = new BalancesController({ accountTracker: this.accountTracker, txController: this.txController, + blockTracker: this.blockTracker, }) this.networkController.on('networkDidChange', () => { this.balancesController.updateAllBalances() From 3bedcd3582519c7afbb8164b40acca4b96eab4bf Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 26 Sep 2017 13:36:41 -0700 Subject: [PATCH 35/37] Restore blockGasLimit to account-tracker --- app/scripts/lib/account-tracker.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index bf949597b..3df5fbc9d 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -18,6 +18,7 @@ class EthereumStore extends ObservableStore { constructor (opts = {}) { super({ accounts: {}, + currentBlockGasLimit: '', }) this._provider = opts.provider this._query = new EthQuery(this._provider) @@ -54,6 +55,8 @@ class EthereumStore extends ObservableStore { const blockNumber = '0x' + block.number.toString('hex') this._currentBlockNumber = blockNumber + this.updateState({ currentBlockGasLimit: `0x${block.gasLimit.toString('hex')}` }) + async.parallel([ this._updateAccounts.bind(this), ], (err) => { From 2eca5455c0c80d99b10c7d56858f84e605494fba Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 26 Sep 2017 14:15:16 -0700 Subject: [PATCH 36/37] Move obs store into account-tracker instead of inheriting --- app/scripts/controllers/balance.js | 4 +-- app/scripts/controllers/computed-balances.js | 6 ++-- app/scripts/lib/account-tracker.js | 31 ++++++++++++-------- app/scripts/metamask-controller.js | 4 +-- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index ab0cfe907..964dff0df 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -36,12 +36,12 @@ class BalanceController { this.txController.on('submitted', update) this.txController.on('confirmed', update) this.txController.on('failed', update) - this.accountTracker.subscribe(update) + this.accountTracker.store.subscribe(update) this.blockTracker.on('block', update) } async _getBalance () { - const { accounts } = this.accountTracker.getState() + const { accounts } = this.accountTracker.store.getState() const entry = accounts[this.address] const balance = entry.balance return balance ? new BN(balance.substring(2), 16) : undefined diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js index 2b27d128d..2479e1b3a 100644 --- a/app/scripts/controllers/computed-balances.js +++ b/app/scripts/controllers/computed-balances.js @@ -20,15 +20,15 @@ class ComputedbalancesController { } updateAllBalances () { - for (let address in this.balances) { + for (let address in this.accountTracker.store.getState().accounts) { this.balances[address].updateBalance() } } _initBalanceUpdating () { - const store = this.accountTracker.getState() + const store = this.accountTracker.store.getState() this.addAnyAccountsFromStore(store) - this.accountTracker.subscribe(this.addAnyAccountsFromStore.bind(this)) + this.accountTracker.store.subscribe(this.addAnyAccountsFromStore.bind(this)) } addAnyAccountsFromStore(store) { diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index 3df5fbc9d..e2892b1ce 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -10,16 +10,21 @@ const async = require('async') const EthQuery = require('eth-query') const ObservableStore = require('obs-store') +const EventEmitter = require('events').EventEmitter function noop () {} -class EthereumStore extends ObservableStore { +class AccountTracker extends EventEmitter { constructor (opts = {}) { - super({ + super() + + const initState = { accounts: {}, currentBlockGasLimit: '', - }) + } + this.store = new ObservableStore(initState) + this._provider = opts.provider this._query = new EthQuery(this._provider) this._blockTracker = opts.blockTracker @@ -34,17 +39,17 @@ class EthereumStore extends ObservableStore { // addAccount (address) { - const accounts = this.getState().accounts + const accounts = this.store.getState().accounts accounts[address] = {} - this.updateState({ accounts }) + this.store.updateState({ accounts }) if (!this._currentBlockNumber) return this._updateAccount(address) } removeAccount (address) { - const accounts = this.getState().accounts + const accounts = this.store.getState().accounts delete accounts[address] - this.updateState({ accounts }) + this.store.updateState({ accounts }) } // @@ -55,31 +60,31 @@ class EthereumStore extends ObservableStore { const blockNumber = '0x' + block.number.toString('hex') this._currentBlockNumber = blockNumber - this.updateState({ currentBlockGasLimit: `0x${block.gasLimit.toString('hex')}` }) + this.store.updateState({ currentBlockGasLimit: `0x${block.gasLimit.toString('hex')}` }) async.parallel([ this._updateAccounts.bind(this), ], (err) => { if (err) return console.error(err) - this.emit('block', this.getState()) + this.emit('block', this.store.getState()) }) } _updateAccounts (cb = noop) { - const accounts = this.getState().accounts + const accounts = this.store.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 + const accounts = this.store.getState().accounts // only populate if the entry is still present if (accounts[address]) { accounts[address] = result - this.updateState({ accounts }) + this.store.updateState({ accounts }) } cb(null, result) }) @@ -96,4 +101,4 @@ class EthereumStore extends ObservableStore { } -module.exports = EthereumStore +module.exports = AccountTracker diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index cca796678..a86d8d37b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -194,7 +194,7 @@ module.exports = class MetamaskController extends EventEmitter { // manual mem state subscriptions this.networkController.store.subscribe(this.sendUpdate.bind(this)) - this.accountTracker.subscribe(this.sendUpdate.bind(this)) + this.accountTracker.store.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)) @@ -277,7 +277,7 @@ module.exports = class MetamaskController extends EventEmitter { isInitialized, }, this.networkController.store.getState(), - this.accountTracker.getState(), + this.accountTracker.store.getState(), this.txController.memStore.getState(), this.messageManager.memStore.getState(), this.personalMessageManager.memStore.getState(), From 651098c70d21edbca98a96ef2a8800d164035638 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 26 Sep 2017 14:30:29 -0700 Subject: [PATCH 37/37] Remove duplicate instantiation of account-tracker --- app/scripts/metamask-controller.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a86d8d37b..0f850b7f5 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -86,6 +86,7 @@ module.exports = class MetamaskController extends EventEmitter { // eth data query tools this.ethQuery = new EthQuery(this.provider) + // account tracker watches balances, nonces, and any code at their address. this.accountTracker = new AccountTracker({ provider: this.provider, blockTracker: this.blockTracker, @@ -99,11 +100,6 @@ module.exports = class MetamaskController extends EventEmitter { encryptor: opts.encryptor || undefined, }) - // 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)