|
|
|
@ -7,65 +7,56 @@ |
|
|
|
|
* on each new block. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
const EventEmitter = require('events').EventEmitter |
|
|
|
|
const inherits = require('util').inherits |
|
|
|
|
const async = require('async') |
|
|
|
|
const clone = require('clone') |
|
|
|
|
const EthQuery = require('eth-query') |
|
|
|
|
const ObservableStore = require('obs-store') |
|
|
|
|
function noop() {} |
|
|
|
|
|
|
|
|
|
module.exports = EthereumStore |
|
|
|
|
|
|
|
|
|
class EthereumStore extends ObservableStore { |
|
|
|
|
|
|
|
|
|
inherits(EthereumStore, EventEmitter) |
|
|
|
|
function EthereumStore(engine) { |
|
|
|
|
const self = this |
|
|
|
|
EventEmitter.call(self) |
|
|
|
|
self._currentState = { |
|
|
|
|
constructor (opts = {}) { |
|
|
|
|
super({ |
|
|
|
|
accounts: {}, |
|
|
|
|
transactions: {}, |
|
|
|
|
} |
|
|
|
|
self._query = new EthQuery(engine) |
|
|
|
|
|
|
|
|
|
engine.on('block', self._updateForBlock.bind(self)) |
|
|
|
|
}) |
|
|
|
|
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)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// public
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
EthereumStore.prototype.getState = function () { |
|
|
|
|
const self = this |
|
|
|
|
return clone(self._currentState) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype.addAccount = function (address) { |
|
|
|
|
const self = this |
|
|
|
|
self._currentState.accounts[address] = {} |
|
|
|
|
self._didUpdate() |
|
|
|
|
if (!self.currentBlockNumber) return |
|
|
|
|
self._updateAccount(address, () => { |
|
|
|
|
self._didUpdate() |
|
|
|
|
}) |
|
|
|
|
addAccount (address) { |
|
|
|
|
const accounts = this.getState().accounts |
|
|
|
|
accounts[address] = {} |
|
|
|
|
this.updateState({ accounts }) |
|
|
|
|
if (!this._currentBlockNumber) return |
|
|
|
|
this._updateAccount(address) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype.removeAccount = function (address) { |
|
|
|
|
const self = this |
|
|
|
|
delete self._currentState.accounts[address] |
|
|
|
|
self._didUpdate() |
|
|
|
|
removeAccount (address) { |
|
|
|
|
const accounts = this.getState().accounts |
|
|
|
|
delete accounts[address] |
|
|
|
|
this.updateState({ accounts }) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype.addTransaction = function (txHash) { |
|
|
|
|
const self = this |
|
|
|
|
self._currentState.transactions[txHash] = {} |
|
|
|
|
self._didUpdate() |
|
|
|
|
if (!self.currentBlockNumber) return |
|
|
|
|
self._updateTransaction(self.currentBlockNumber, txHash, noop) |
|
|
|
|
addTransaction (txHash) { |
|
|
|
|
const transactions = this.getState().transactions |
|
|
|
|
transactions[txHash] = {} |
|
|
|
|
this.updateState({ transactions }) |
|
|
|
|
if (!this._currentBlockNumber) return |
|
|
|
|
this._updateTransaction(this._currentBlockNumber, txHash, noop) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype.removeTransaction = function (address) { |
|
|
|
|
const self = this |
|
|
|
|
delete self._currentState.transactions[address] |
|
|
|
|
self._didUpdate() |
|
|
|
|
removeTransaction (txHash) { |
|
|
|
|
const transactions = this.getState().transactions |
|
|
|
|
delete transactions[txHash] |
|
|
|
|
this.updateState({ transactions }) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -73,74 +64,65 @@ EthereumStore.prototype.removeTransaction = function (address) { |
|
|
|
|
// private
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
EthereumStore.prototype._didUpdate = function () { |
|
|
|
|
const self = this |
|
|
|
|
var state = self.getState() |
|
|
|
|
self.emit('update', state) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype._updateForBlock = function (block) { |
|
|
|
|
const self = this |
|
|
|
|
var blockNumber = '0x' + block.number.toString('hex') |
|
|
|
|
self.currentBlockNumber = blockNumber |
|
|
|
|
_updateForBlock (block) { |
|
|
|
|
const blockNumber = '0x' + block.number.toString('hex') |
|
|
|
|
this._currentBlockNumber = blockNumber |
|
|
|
|
async.parallel([ |
|
|
|
|
self._updateAccounts.bind(self), |
|
|
|
|
self._updateTransactions.bind(self, blockNumber), |
|
|
|
|
], function (err) { |
|
|
|
|
this._updateAccounts.bind(this), |
|
|
|
|
this._updateTransactions.bind(this, blockNumber), |
|
|
|
|
], (err) => { |
|
|
|
|
if (err) return console.error(err) |
|
|
|
|
self.emit('block', self.getState()) |
|
|
|
|
self._didUpdate() |
|
|
|
|
this.emit('block', this.getState()) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype._updateAccounts = function (cb) { |
|
|
|
|
var accountsState = this._currentState.accounts |
|
|
|
|
var addresses = Object.keys(accountsState) |
|
|
|
|
_updateAccounts (cb) { |
|
|
|
|
const accounts = this.getState().accounts |
|
|
|
|
const addresses = Object.keys(accounts) |
|
|
|
|
async.each(addresses, this._updateAccount.bind(this), cb) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype._updateAccount = function (address, cb) { |
|
|
|
|
var accountsState = this._currentState.accounts |
|
|
|
|
this.getAccount(address, function (err, result) { |
|
|
|
|
_updateAccount (address, cb) { |
|
|
|
|
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 (accountsState[address]) { |
|
|
|
|
accountsState[address] = result |
|
|
|
|
if (accounts[address]) { |
|
|
|
|
accounts[address] = result |
|
|
|
|
} |
|
|
|
|
cb(null, result) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype.getAccount = function (address, cb) { |
|
|
|
|
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) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype._updateTransactions = function (block, cb) { |
|
|
|
|
const self = this |
|
|
|
|
var transactionsState = self._currentState.transactions |
|
|
|
|
var txHashes = Object.keys(transactionsState) |
|
|
|
|
async.each(txHashes, self._updateTransaction.bind(self, block), cb) |
|
|
|
|
_updateTransactions (block, cb) { |
|
|
|
|
const transactions = this.getState().transactions |
|
|
|
|
const txHashes = Object.keys(transactions) |
|
|
|
|
async.each(txHashes, this._updateTransaction.bind(this, block), cb) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EthereumStore.prototype._updateTransaction = function (block, txHash, cb) { |
|
|
|
|
const self = this |
|
|
|
|
_updateTransaction (block, txHash, cb) { |
|
|
|
|
// would use the block here to determine how many confirmations the tx has
|
|
|
|
|
var transactionsState = self._currentState.transactions |
|
|
|
|
self._query.getTransaction(txHash, function (err, result) { |
|
|
|
|
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 (transactionsState[txHash]) { |
|
|
|
|
transactionsState[txHash] = result |
|
|
|
|
self._didUpdate() |
|
|
|
|
if (transactions[txHash]) { |
|
|
|
|
transactions[txHash] = result |
|
|
|
|
} |
|
|
|
|
cb(null, result) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function noop() {} |
|
|
|
|
_getAccount (address, cb) { |
|
|
|
|
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 |