diff options
Diffstat (limited to 'docs/jsdocs/controllers_transactions.js.html')
-rw-r--r-- | docs/jsdocs/controllers_transactions.js.html | 471 |
1 files changed, 0 insertions, 471 deletions
diff --git a/docs/jsdocs/controllers_transactions.js.html b/docs/jsdocs/controllers_transactions.js.html deleted file mode 100644 index f86d3aa48..000000000 --- a/docs/jsdocs/controllers_transactions.js.html +++ /dev/null @@ -1,471 +0,0 @@ -<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <meta name="google" content="notranslate">
- <meta http-equiv="Content-Language" content="en">
- <title>controllers/transactions.js - Documentation</title>
-
- <script src="scripts/prettify/prettify.js"></script>
- <script src="scripts/prettify/lang-css.js"></script>
- <script
- src="https://code.jquery.com/jquery-3.1.1.min.js"
- integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
- crossorigin="anonymous"></script>
- <script src="scripts/semantic.min.js"></script>
- <!--[if lt IE 9]>
- <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
- <![endif]-->
- <link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
- <link type="text/css" rel="stylesheet" href="styles/prettify.css">
- <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
- <link type="text/css" rel="stylesheet" href="styles/semantic.min.css">
- <link type="text/css" rel="stylesheet" href="styles/override.css">
-</head>
-<body>
-
-<input type="checkbox" id="nav-trigger" class="nav-trigger" />
-<label for="nav-trigger" class="navicon-button x">
- <div class="navicon"></div>
-</label>
-
-<label for="nav-trigger" class="overlay"></label>
-
-<nav>
- <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><div class="ui vertical accordion"><div class="title"><div class="ui list"><div class="item"><i class="inverted dropdown icon"></i><a href="module.exports_module.exports.html">exports</a></div></div></div></li></div></ul><h3><a href="global.html">Global</a></h3>
-</nav>
-
-<div id="main">
-
- <h1 class="page-title">controllers/transactions.js</h1>
-
-
-
-
-
-
-
- <section>
- <article>
- <pre class="prettyprint source linenums"><code>const EventEmitter = require('events') -const ObservableStore = require('obs-store') -const ethUtil = require('ethereumjs-util') -/** - * @file The transaction controller. Receives incoming transactions, and emits events for various states of their processing. - * @copyright Copyright (c) 2018 MetaMask - * @license MIT - */ - - -const Transaction = require('ethereumjs-tx') -const EthQuery = require('ethjs-query') -const TransactionStateManager = require('../lib/tx-state-manager') -const TxGasUtil = require('../lib/tx-gas-utils') -const PendingTransactionTracker = require('../lib/pending-tx-tracker') -const NonceTracker = require('../lib/nonce-tracker') - -/* - Transaction Controller is an aggregate of sub-controllers and trackers - composing them in a way to be exposed to the metamask controller - - txStateManager - responsible for the state of a transaction and - storing the transaction - - pendingTxTracker - watching blocks for transactions to be include - and emitting confirmed events - - txGasUtil - gas calculations and safety buffering - - nonceTracker - calculating nonces -*/ - -module.exports = class TransactionController extends EventEmitter { - constructor (opts) { - super() - this.networkStore = opts.networkStore || new ObservableStore({}) - this.preferencesStore = opts.preferencesStore || new ObservableStore({}) - this.provider = opts.provider - this.blockTracker = opts.blockTracker - this.signEthTx = opts.signTransaction - this.getGasPrice = opts.getGasPrice - - this.memStore = new ObservableStore({}) - this.query = new EthQuery(this.provider) - this.txGasUtil = new TxGasUtil(this.provider) - - this.txStateManager = new TransactionStateManager({ - initState: opts.initState, - txHistoryLimit: opts.txHistoryLimit, - getNetwork: this.getNetwork.bind(this), - }) - - this.txStateManager.getFilteredTxList({ - status: 'unapproved', - loadingDefaults: true, - }).forEach((tx) => { - this.addTxDefaults(tx) - .then((txMeta) => { - txMeta.loadingDefaults = false - this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot') - }).catch((error) => { - this.txStateManager.setTxStatusFailed(tx.id, error) - }) - }) - - this.txStateManager.getFilteredTxList({ - status: 'approved', - }).forEach((txMeta) => { - const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing') - this.txStateManager.setTxStatusFailed(txMeta.id, txSignError) - }) - - - this.store = this.txStateManager.store - this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) - this.nonceTracker = new NonceTracker({ - provider: this.provider, - getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager), - getConfirmedTransactions: (address) => { - return this.txStateManager.getFilteredTxList({ - from: address, - status: 'confirmed', - err: undefined, - }) - }, - }) - - this.pendingTxTracker = new PendingTransactionTracker({ - provider: this.provider, - nonceTracker: this.nonceTracker, - publishTransaction: (rawTx) => this.query.sendRawTransaction(rawTx), - getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager), - getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager), - }) - - this.txStateManager.store.subscribe(() => this.emit('update:badge')) - - this.pendingTxTracker.on('tx:warning', (txMeta) => { - this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning') - }) - this.pendingTxTracker.on('tx:confirmed', (txId) => this._markNonceDuplicatesDropped(txId)) - this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager)) - this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => { - if (!txMeta.firstRetryBlockNumber) { - txMeta.firstRetryBlockNumber = latestBlockNumber - this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:block-update') - } - }) - this.pendingTxTracker.on('tx:retry', (txMeta) => { - if (!('retryCount' in txMeta)) txMeta.retryCount = 0 - txMeta.retryCount++ - this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:retry') - }) - - this.blockTracker.on('block', 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 - this.blockTracker.on('latest', this.pendingTxTracker.resubmitPendingTxs.bind(this.pendingTxTracker)) - this.blockTracker.on('sync', this.pendingTxTracker.queryPendingTxs.bind(this.pendingTxTracker)) - // memstore is computed from a few different stores - this._updateMemstore() - this.txStateManager.store.subscribe(() => this._updateMemstore()) - this.networkStore.subscribe(() => this._updateMemstore()) - this.preferencesStore.subscribe(() => this._updateMemstore()) - } - - getState () { - return this.memStore.getState() - } - - getNetwork () { - return this.networkStore.getState() - } - - getSelectedAddress () { - return this.preferencesStore.getState().selectedAddress - } - - getUnapprovedTxCount () { - return Object.keys(this.txStateManager.getUnapprovedTxList()).length - } - - getPendingTxCount (account) { - return this.txStateManager.getPendingTransactions(account).length - } - - getFilteredTxList (opts) { - return this.txStateManager.getFilteredTxList(opts) - } - - getChainId () { - const networkState = this.networkStore.getState() - const getChainId = parseInt(networkState) - if (Number.isNaN(getChainId)) { - return 0 - } else { - return getChainId - } - } - - wipeTransactions (address) { - this.txStateManager.wipeTransactions(address) - } - - // Adds a tx to the txlist - addTx (txMeta) { - this.txStateManager.addTx(txMeta) - this.emit(`${txMeta.id}:unapproved`, txMeta) - } - - async newUnapprovedTransaction (txParams, opts = {}) { - log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`) - const initialTxMeta = await this.addUnapprovedTransaction(txParams) - initialTxMeta.origin = opts.origin - this.txStateManager.updateTx(initialTxMeta, '#newUnapprovedTransaction - adding the origin') - // listen for tx completion (success, fail) - return new Promise((resolve, reject) => { - this.txStateManager.once(`${initialTxMeta.id}:finished`, (finishedTxMeta) => { - switch (finishedTxMeta.status) { - case 'submitted': - return resolve(finishedTxMeta.hash) - case 'rejected': - return reject(new Error('MetaMask Tx Signature: User denied transaction signature.')) - case 'failed': - return reject(new Error(finishedTxMeta.err.message)) - default: - return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(finishedTxMeta.txParams)}`)) - } - }) - }) - } - - async addUnapprovedTransaction (txParams) { - // validate - const normalizedTxParams = this._normalizeTxParams(txParams) - this._validateTxParams(normalizedTxParams) - // construct txMeta - let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams }) - this.addTx(txMeta) - this.emit('newUnapprovedTx', txMeta) - // add default tx params - try { - txMeta = await this.addTxDefaults(txMeta) - } catch (error) { - console.log(error) - this.txStateManager.setTxStatusFailed(txMeta.id, error) - throw error - } - txMeta.loadingDefaults = false - // save txMeta - this.txStateManager.updateTx(txMeta) - - return txMeta - } - - async addTxDefaults (txMeta) { - const txParams = txMeta.txParams - // ensure value - txMeta.gasPriceSpecified = Boolean(txParams.gasPrice) - let gasPrice = txParams.gasPrice - if (!gasPrice) { - gasPrice = this.getGasPrice ? this.getGasPrice() : await this.query.gasPrice() - } - txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16)) - txParams.value = txParams.value || '0x0' - // set gasLimit - return await this.txGasUtil.analyzeGasUsage(txMeta) - } - - async retryTransaction (originalTxId) { - const originalTxMeta = this.txStateManager.getTx(originalTxId) - const lastGasPrice = originalTxMeta.txParams.gasPrice - const txMeta = this.txStateManager.generateTxMeta({ - txParams: originalTxMeta.txParams, - lastGasPrice, - loadingDefaults: false, - }) - this.addTx(txMeta) - this.emit('newUnapprovedTx', txMeta) - return txMeta - } - - async updateTransaction (txMeta) { - this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction') - } - - async updateAndApproveTransaction (txMeta) { - this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction') - await this.approveTransaction(txMeta.id) - } - - async approveTransaction (txId) { - let nonceLock - try { - // approve - this.txStateManager.setTxStatusApproved(txId) - // get next nonce - const txMeta = this.txStateManager.getTx(txId) - const fromAddress = txMeta.txParams.from - // wait for a nonce - nonceLock = await this.nonceTracker.getNonceLock(fromAddress) - // add nonce to txParams - // if txMeta has lastGasPrice then it is a retry at same nonce with higher - // gas price transaction and their for the nonce should not be calculated - const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce - txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16)) - // add nonce debugging information to txMeta - txMeta.nonceDetails = nonceLock.nonceDetails - this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction') - // sign transaction - const rawTx = await this.signTransaction(txId) - await this.publishTransaction(txId, rawTx) - // must set transaction to submitted/failed before releasing lock - nonceLock.releaseLock() - } catch (err) { - this.txStateManager.setTxStatusFailed(txId, err) - // must set transaction to submitted/failed before releasing lock - if (nonceLock) nonceLock.releaseLock() - // continue with error chain - throw err - } - } - - async signTransaction (txId) { - const txMeta = this.txStateManager.getTx(txId) - // add network/chain id - const chainId = this.getChainId() - const txParams = Object.assign({}, txMeta.txParams, { chainId }) - // sign tx - const fromAddress = txParams.from - const ethTx = new Transaction(txParams) - await this.signEthTx(ethTx, fromAddress) - // set state to signed - this.txStateManager.setTxStatusSigned(txMeta.id) - const rawTx = ethUtil.bufferToHex(ethTx.serialize()) - return rawTx - } - - async publishTransaction (txId, rawTx) { - const txMeta = this.txStateManager.getTx(txId) - txMeta.rawTx = rawTx - this.txStateManager.updateTx(txMeta, 'transactions#publishTransaction') - const txHash = await this.query.sendRawTransaction(rawTx) - this.setTxHash(txId, txHash) - this.txStateManager.setTxStatusSubmitted(txId) - } - - async cancelTransaction (txId) { - this.txStateManager.setTxStatusRejected(txId) - } - - // receives a txHash records the tx as signed - setTxHash (txId, txHash) { - // Add the tx hash to the persisted meta-tx object - const txMeta = this.txStateManager.getTx(txId) - txMeta.hash = txHash - this.txStateManager.updateTx(txMeta, 'transactions#setTxHash') - } - -// -// PRIVATE METHODS -// - - _normalizeTxParams (txParams) { - // functions that handle normalizing of that key in txParams - const whiteList = { - from: from => ethUtil.addHexPrefix(from).toLowerCase(), - to: to => ethUtil.addHexPrefix(txParams.to).toLowerCase(), - nonce: nonce => ethUtil.addHexPrefix(nonce), - value: value => ethUtil.addHexPrefix(value), - data: data => ethUtil.addHexPrefix(data), - gas: gas => ethUtil.addHexPrefix(gas), - gasPrice: gasPrice => ethUtil.addHexPrefix(gasPrice), - } - - // apply only keys in the whiteList - const normalizedTxParams = {} - Object.keys(whiteList).forEach((key) => { - if (txParams[key]) normalizedTxParams[key] = whiteList[key](txParams[key]) - }) - - return normalizedTxParams - } - - _validateTxParams (txParams) { - this._validateFrom(txParams) - this._validateRecipient(txParams) - if ('value' in txParams) { - const value = txParams.value.toString() - if (value.includes('-')) { - throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`) - } - - if (value.includes('.')) { - throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`) - } - } - } - - _validateFrom (txParams) { - if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`) - if (!ethUtil.isValidAddress(txParams.from)) throw new Error('Invalid from address') - } - - _validateRecipient (txParams) { - if (txParams.to === '0x' || txParams.to === null ) { - if (txParams.data) { - delete txParams.to - } else { - throw new Error('Invalid recipient address') - } - } else if ( txParams.to !== undefined && !ethUtil.isValidAddress(txParams.to) ) { - throw new Error('Invalid recipient address') - } - return txParams - } - - _markNonceDuplicatesDropped (txId) { - this.txStateManager.setTxStatusConfirmed(txId) - // get the confirmed transactions nonce and from address - const txMeta = this.txStateManager.getTx(txId) - const { nonce, from } = txMeta.txParams - const sameNonceTxs = this.txStateManager.getFilteredTxList({nonce, from}) - if (!sameNonceTxs.length) return - // mark all same nonce transactions as dropped and give i a replacedBy hash - sameNonceTxs.forEach((otherTxMeta) => { - if (otherTxMeta.id === txId) return - otherTxMeta.replacedBy = txMeta.hash - this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce') - this.txStateManager.setTxStatusDropped(otherTxMeta.id) - }) - } - - _updateMemstore () { - const unapprovedTxs = this.txStateManager.getUnapprovedTxList() - const selectedAddressTxList = this.txStateManager.getFilteredTxList({ - from: this.getSelectedAddress(), - metamaskNetworkId: this.getNetwork(), - }) - this.memStore.updateState({ unapprovedTxs, selectedAddressTxList }) - } -} -</code></pre>
- </article>
- </section>
-
-
-
-
-</div>
-
-<br class="clear">
-
-<footer>
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash.
-</footer>
-
-<script>prettyPrint();</script>
-<script src="scripts/linenumber.js"></script>
-<script>$('.ui.accordion').accordion();</script>
-</body>
-</html>
|