From 1ddb8b9aec02e5e38a6eed8eeea1733ee3cd99c0 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 21 Oct 2016 13:41:33 -0700 Subject: Added tx & msg managing functionality to new KeyringController --- app/scripts/keyring-controller.js | 148 +++++++++++++++++++++++++++++++++++++ app/scripts/metamask-controller.js | 3 +- 2 files changed, 150 insertions(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index 807752a94..45deb40c8 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -1,9 +1,13 @@ +const async = require('async') const EventEmitter = require('events').EventEmitter const encryptor = require('./lib/encryptor') const messageManager = require('./lib/message-manager') const ethUtil = require('ethereumjs-util') +const ethBinToOps = require('eth-bin-to-ops') +const EthQuery = require('eth-query') const BN = ethUtil.BN const Transaction = require('ethereumjs-tx') +const createId = require('web3-provider-engine/util/random-id') // Keyrings: const SimpleKeyring = require('./keyrings/simple') @@ -15,6 +19,7 @@ module.exports = class KeyringController extends EventEmitter { constructor (opts) { super() + this.web3 = opts.web3 this.configManager = opts.configManager this.ethStore = opts.ethStore this.encryptor = encryptor @@ -22,6 +27,11 @@ module.exports = class KeyringController extends EventEmitter { this.keyrings = [] this.identities = {} // Essentially a name hash + + this._unconfTxCbs = {} + this._unconfMsgCbs = {} + + this.network = 'loading' } getState() { @@ -41,6 +51,7 @@ module.exports = class KeyringController extends EventEmitter { conversionDate: this.configManager.getConversionDate(), keyringTypes: this.keyringTypes.map((krt) => krt.type()), identities: this.identities, + network: this.network, } } @@ -193,11 +204,121 @@ module.exports = class KeyringController extends EventEmitter { cb(null, address) } + addUnconfirmedTransaction(txParams, onTxDoneCb, cb) { + var self = this + const configManager = this.configManager + + // create txData obj with parameters and meta data + var time = (new Date()).getTime() + var txId = createId() + txParams.metamaskId = txId + txParams.metamaskNetworkId = this.network + var txData = { + id: txId, + txParams: txParams, + time: time, + status: 'unconfirmed', + gasMultiplier: configManager.getGasMultiplier() || 1, + } + + console.log('addUnconfirmedTransaction:', txData) + + // keep the onTxDoneCb around for after approval/denial (requires user interaction) + // This onTxDoneCb fires completion to the Dapp's write operation. + this._unconfTxCbs[txId] = onTxDoneCb + + var provider = this.ethStore._query.currentProvider + var query = new EthQuery(provider) + + // calculate metadata for tx + async.parallel([ + analyzeForDelegateCall, + estimateGas, + ], didComplete) + + // perform static analyis on the target contract code + function analyzeForDelegateCall(cb){ + if (txParams.to) { + query.getCode(txParams.to, function (err, result) { + if (err) return cb(err) + var code = ethUtil.toBuffer(result) + if (code !== '0x') { + var ops = ethBinToOps(code) + var containsDelegateCall = ops.some((op) => op.name === 'DELEGATECALL') + txData.containsDelegateCall = containsDelegateCall + cb() + } else { + cb() + } + }) + } else { + cb() + } + } + + function estimateGas(cb){ + query.estimateGas(txParams, function(err, result){ + if (err) return cb(err) + txData.estimatedGas = self.addGasBuffer(result) + cb() + }) + } + + function didComplete (err) { + if (err) return cb(err) + configManager.addTx(txData) + // signal update + self.emit('update') + // signal completion of add tx + cb(null, txData) + } + } + + addUnconfirmedMessage(msgParams, cb) { + // create txData obj with parameters and meta data + var time = (new Date()).getTime() + var msgId = createId() + var msgData = { + id: msgId, + msgParams: msgParams, + time: time, + status: 'unconfirmed', + } + messageManager.addMsg(msgData) + console.log('addUnconfirmedMessage:', msgData) + + // keep the cb around for after approval (requires user interaction) + // This cb fires completion to the Dapp's write operation. + this._unconfMsgCbs[msgId] = cb + + // signal update + this.emit('update') + return msgId + } + approveTransaction(txId, cb) { + const configManager = this.configManager + var approvalCb = this._unconfTxCbs[txId] || noop + + // accept tx cb() + approvalCb(null, true) + // clean up + configManager.confirmTx(txId) + delete this._unconfTxCbs[txId] + this.emit('update') } cancelTransaction(txId, cb) { + const configManager = this.configManager + var approvalCb = this._unconfTxCbs[txId] || noop + + // reject tx + approvalCb(null, false) + // clean up + configManager.rejectTx(txId) + delete this._unconfTxCbs[txId] + if (cb && typeof cb === 'function') { cb() } @@ -279,5 +400,32 @@ module.exports = class KeyringController extends EventEmitter { cb() } + getNetwork(err) { + if (err) { + this.network = 'loading' + this.emit('update') + } + + this.web3.version.getNetwork((err, network) => { + if (err) { + this.network = 'loading' + return this.emit('update') + } + if (global.METAMASK_DEBUG) { + console.log('web3.getNetwork returned ' + network) + } + this.network = network + this.emit('update') + }) + } + + addGasBuffer(gasHex) { + var gas = new BN(gasHex, 16) + var buffer = new BN('100000', 10) + var result = gas.add(buffer) + return ethUtil.addHexPrefix(result.toString(16)) + } + } +function noop () {} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 96bd42513..c0da7cdaa 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -182,7 +182,8 @@ module.exports = class MetamaskController { this.configManager.subscribe(function (state) { storeSetFromObj(publicConfigStore, configToPublic(state)) }) - this.keyringController.on('update', function (state) { + this.keyringController.on('update', () => { + const state = this.keyringController.getState() storeSetFromObj(publicConfigStore, keyringControllerToPublic(state)) this.sendUpdate() }) -- cgit v1.2.3