From 25b5e6c98009cf39a73b556a2ac3488d736426fa Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Thu, 8 Feb 2018 13:12:20 -0500 Subject: [PATCH] move execution EventsDecoder to Remix-Lib --- remix-lib/index.js | 5 + remix-lib/src/execution/eventsDecoder.js | 123 +++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 remix-lib/src/execution/eventsDecoder.js diff --git a/remix-lib/index.js b/remix-lib/index.js index 26ef7b0e74..70b2c4e240 100644 --- a/remix-lib/index.js +++ b/remix-lib/index.js @@ -17,6 +17,8 @@ var styleGuideDark = require('./src/ui/styleGuideDark') var themeChooser = require('./src/ui/theme-chooser') var Storage = require('./src/storage') +var EventsDecoder = require('./src/execution/eventsDecoder') + if (typeof (module) !== 'undefined' && typeof (module.exports) !== 'undefined') { module.exports = modules() } @@ -49,6 +51,9 @@ function modules () { styleGuide: styleGuide, styleGuideDark: styleGuideDark, themeChooser: themeChooser + }, + execution: { + EventsDecoder: EventsDecoder } } } diff --git a/remix-lib/src/execution/eventsDecoder.js b/remix-lib/src/execution/eventsDecoder.js new file mode 100644 index 0000000000..2127f469b4 --- /dev/null +++ b/remix-lib/src/execution/eventsDecoder.js @@ -0,0 +1,123 @@ +'use strict' +var ethJSABI = require('ethereumjs-abi') +var txHelper = require('../execution/txHelper') + +/** + * Register to txListener and extract events + * + */ +class EventsDecoder { + constructor (opt = {}) { + this._api = opt.api + } + +/** + * use Transaction Receipt to decode logs. assume that the transaction as already been resolved by txListener. + * logs are decoded only if the contract if known by remix. + * + * @param {Object} tx - transaction object + * @param {Function} cb - callback + */ + parseLogs (tx, contractName, compiledContracts, cb) { + if (tx.isCall) return cb(null, { decoded: [], raw: [] }) + this._api.resolveReceipt(tx, (error, receipt) => { + if (error) return cb(error) + this._decodeLogs(tx, receipt, contractName, compiledContracts, cb) + }) + } + + _decodeLogs (tx, receipt, contract, contracts, cb) { + if (!contract || !receipt) { + return cb('cannot decode logs - contract or receipt not resolved ') + } + if (!receipt.logs) { + return cb(null, { decoded: [], raw: [] }) + } + this._decodeEvents(tx, receipt.logs, contract, contracts, cb) + } + + _eventABI (contract) { + var eventABI = {} + contract.abi.forEach(function (funABI, i) { + if (funABI.type !== 'event') { + return + } + var hash = ethJSABI.eventID(funABI.name, funABI.inputs.map(function (item) { return item.type })) + eventABI[hash.toString('hex')] = { event: funABI.name, inputs: funABI.inputs } + }) + return eventABI + } + + _eventsABI (compiledContracts) { + var eventsABI = {} + txHelper.visitContracts(compiledContracts, (contract) => { + eventsABI[contract.name] = this._eventABI(contract.object) + }) + return eventsABI + } + + _event (hash, eventsABI) { + for (var k in eventsABI) { + if (eventsABI[k][hash]) { + return eventsABI[k][hash] + } + } + return null + } + + _decodeEvents (tx, logs, contractName, compiledContracts, cb) { + var eventsABI = this._eventsABI(compiledContracts) + var events = [] + for (var i in logs) { + // [address, topics, mem] + var log = logs[i] + var topicId = log.topics[0] + var abi = this._event(topicId.replace('0x', ''), eventsABI) + if (abi) { + var event + try { + var decoded = new Array(abi.inputs.length) + event = abi.event + var indexed = 1 + var nonindexed = [] + // decode indexed param + abi.inputs.map(function (item, index) { + if (item.indexed) { + var encodedData = log.topics[indexed].replace('0x', '') + try { + decoded[index] = ethJSABI.rawDecode([item.type], new Buffer(encodedData, 'hex'))[0] + if (typeof decoded[index] !== 'string') { + decoded[index] = ethJSABI.stringify([item.type], decoded[index]) + } + } catch (e) { + decoded[index] = encodedData + } + indexed++ + } else { + nonindexed.push(item.type) + } + }) + // decode non indexed param + var nonindexededResult = ethJSABI.rawDecode(nonindexed, new Buffer(log.data.replace('0x', ''), 'hex')) + nonindexed = ethJSABI.stringify(nonindexed, nonindexededResult) + // ordering + var j = 0 + abi.inputs.map(function (item, index) { + if (!item.indexed) { + decoded[index] = nonindexed[j] + j++ + } + }) + } catch (e) { + decoded = log.data + } + events.push({ topic: topicId, event: event, args: decoded }) + } else { + events.push({ data: log.data, topics: log.topics }) + } + } + cb(null, { decoded: events, raw: logs }) + } +} + +module.exports = EventsDecoder