diff options
introduce nonce-tracker
-rw-r--r-- | app/scripts/lib/nonce-tracker.js | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js new file mode 100644 index 000000000..6e9d094bc --- /dev/null +++ b/app/scripts/lib/nonce-tracker.js @@ -0,0 +1,49 @@ +const EthQuery = require('ethjs-query') + +class NonceTracker { + + constructor({ blockTracker, provider, getPendingTransactions }) { + this.blockTracker = blockTracker + this.ethQuery = new EthQuery(provider) + this.getPendingTransactions = getPendingTransactions + this.lockMap = {} + } + + // releaseLock must be called + // releaseLock must be called after adding signed tx to pending transactions (or discarding) + async getNonceLock(address) { + // await lock free + await this.lockMap[address] + // take lock + const releaseLock = this._takeLock(address) + // calculate next nonce + const currentBlock = await this._getCurrentBlock() + const blockNumber = currentBlock.number + const pendingTransactions = this.getPendingTransactions(address) + const baseCount = await this.ethQuery.getTransactionCount(address, blockNumber) + const nextNonce = baseCount + pendingTransactions + // return next nonce and release cb + return { nextNonce, releaseLock } + } + + async _getCurrentBlock() { + const currentBlock = this.blockTracker.getCurrentBlock() + if (currentBlock) return currentBlock + return await Promise((reject, resolve) => { + this.blockTracker.once('latest', resolve) + }) + } + + _takeLock(lockId) { + let releaseLock = null + // create and store lock + const lock = new Promise((reject, resolve) => { releaseLock = resolve }) + this.lockMap[lockId] = lock + // setup lock teardown + lock.then(() => delete this.lockMap[lockId]) + return releaseLock + } + +} + +module.exports = NonceTracker |