Merge pull request #645 from ethereum/eventListener

Event Decoder
pull/1/head
yann300 7 years ago committed by GitHub
commit 9f48efd8af
  1. 91
      src/app.js
  2. 77
      src/app/eventsDecoder.js
  3. 40
      src/app/txListener.js

@ -7,7 +7,8 @@ var base64 = require('js-base64').Base64
var swarmgw = require('swarmgw')
var csjs = require('csjs-inject')
var yo = require('yo-yo')
var EventManager = require('ethereum-remix').lib.EventManager
var remix = require('ethereum-remix')
var EventManager = remix.lib.EventManager
var UniversalDApp = require('./universal-dapp.js')
var Remixd = require('./lib/remixd')
@ -30,7 +31,9 @@ var EditorPanel = require('./app/editor-panel')
var RighthandPanel = require('./app/righthand-panel')
var examples = require('./app/example-contracts')
var modalDialogCustom = require('./app/modal-dialog-custom')
// var Txlistener = require('./app/txListener')
var Txlistener = require('./app/txListener')
var EventsDecoder = require('./app/eventsDecoder')
var Web3VMProvider = remix.web3.web3VMProvider
var css = csjs`
html { box-sizing: border-box; }
@ -748,26 +751,62 @@ function run () {
node.insertBefore(staticanalysis.render(), node.childNodes[0])
// ----------------- Tx listener -----------------
// Commented for now. will be used later.
/*
// not used right now
// TODO the following should be put in execution context
var web3VM = new Web3VMProvider()
web3VM.setVM(executionContext.vm())
var currentWeb3 = function () {
return executionContext.isVM() ? web3VM : executionContext.web3()
}
var transactionReceiptResolver = {
_transactionReceipts: {},
resolve: function (tx, cb) {
if (this._transactionReceipts[tx.hash]) {
return cb(null, this._transactionReceipts[tx.hash])
}
currentWeb3().eth.getTransactionReceipt(tx.hash, (error, receipt) => {
if (!error) {
this._transactionReceipts[tx.hash] = receipt
cb(null, receipt)
} else {
cb(error)
}
})
}
}
var compiledContracts = function () {
if (compiler.lastCompilationResult && compiler.lastCompilationResult.data) {
return compiler.lastCompilationResult.data.contracts
}
return null
}
var txlistener = new Txlistener({
api: {
web3: function () { return executionContext.web3() },
web3: function () { return currentWeb3() },
isVM: function () { return executionContext.isVM() },
vm: function () { return executionContext.vm() },
contracts: function () {
if (compiler.lastCompilationResult && compiler.lastCompilationResult.data) {
return compiler.lastCompilationResult.data.contracts
}
return null
},
contracts: compiledContracts,
context: function () {
return executionContext.getProvider()
},
resolveReceipt: function (tx, cb) {
transactionReceiptResolver.resolve(tx, cb)
}
},
event: {
executionContext: executionContext.event,
udapp: udapp.event
}})
var eventsDecoder = new EventsDecoder({
api: {
resolveReceipt: function (tx, cb) {
transactionReceiptResolver.resolve(tx, cb)
}
}
})
@ -777,15 +816,31 @@ function run () {
var resolvedTransaction = txlistener.resolvedTransaction(tx.hash)
var address = null
if (resolvedTransaction) {
var resolvedContract
address = resolvedTransaction.contractAddress ? resolvedTransaction.contractAddress : tx.to
resolvedContract = txlistener.resolvedContract(address)
if (resolvedContract) {
eventsDecoder.parseLogs(tx, resolvedContract, compiledContracts(), (error, log) => {
console.log({
tx: tx,
resolvedTransaction: resolvedTransaction,
resolvedContract: resolvedContract,
resolvedEvents: (!error ? log : error)
})
})
} else {
console.log({
tx: tx,
resolvedTransaction: resolvedTransaction
})
}
} else {
// contract unknown - just displaying raw tx.
console.log({
tx: tx
})
}
console.log({
tx: tx,
resolvedContract: txlistener.resolvedContract(address),
resolvedTransaction: resolvedTransaction
})
})
*/
// ----------------- autoCompile -----------------
var autoCompile = document.querySelector('#autoCompile').checked

@ -0,0 +1,77 @@
'use strict'
var ethJSABI = require('ethereumjs-abi')
/**
* 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) {
this._api.resolveReceipt(tx, (error, receipt) => {
if (error) 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, [])
}
this._decodeEvents(tx, receipt.logs, contract, contracts, cb)
}
_eventABI (contractName, compiledContracts) {
var contractabi = JSON.parse(compiledContracts[contractName].interface)
var eventABI = {}
contractabi.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
}
_decodeEvents (tx, logs, contractName, compiledContracts, cb) {
var eventABI = this._eventABI(contractName, compiledContracts)
// FIXME: support indexed events
var events = []
for (var i in logs) {
// [address, topics, mem]
var log = logs[i]
var event
var decoded
try {
var abi = eventABI[log.topics[0].replace('0x', '')]
event = abi.event
var types = abi.inputs.map(function (item) {
return item.type
})
decoded = ethJSABI.rawDecode(types, new Buffer(log.data.replace('0x', ''), 'hex'))
decoded = ethJSABI.stringify(types, decoded)
} catch (e) {
decoded = log.data
}
events.push({ event: event, args: decoded })
}
cb(null, events)
}
}
module.exports = EventsDecoder

@ -5,7 +5,6 @@ var ethJSUtil = require('ethereumjs-util')
var EventManager = require('ethereum-remix').lib.EventManager
var remix = require('ethereum-remix')
var codeUtil = remix.util.code
var Web3VMProvider = remix.web3.web3VMProvider
/**
* poll web3 each 2s if web3
@ -18,8 +17,6 @@ class TxListener {
constructor (opt) {
this.event = new EventManager()
this._api = opt.api
this._web3VMProvider = new Web3VMProvider() // TODO this should maybe be put in app.js
this._web3VMProvider.setVM(opt.api.vm())
this._resolvedTransactions = {}
this._resolvedContracts = {}
this.init()
@ -30,7 +27,7 @@ class TxListener {
})
opt.event.udapp.register('transactionExecuted', (to, data, lookupOnly, txResult) => {
if (this.loopId && this._api.isVM()) {
this._web3VMProvider.getTransaction(txResult.transactionHash, (error, tx) => {
this._api.web3().eth.getTransaction(txResult.transactionHash, (error, tx) => {
if (error) return console.log(error)
this._newBlock({
type: 'VM',
@ -64,6 +61,7 @@ class TxListener {
} else {
this.loopId = setInterval(() => {
this._api.web3().eth.getBlockNumber((error, blockNumber) => {
if (this.loopId === null || this.loopId === 'vm-listener') return
if (error) return console.log(error)
if (!this.lastBlock || blockNumber > this.lastBlock) {
this.lastBlock = blockNumber
@ -78,10 +76,6 @@ class TxListener {
}
}
currentWeb3 () { // TODO this should maybe be put in app.js
return this._api.isVM() ? this._web3VMProvider : this._api.web3()
}
/**
* stop listening for incoming transactions. do not reset the recorded pool.
*
@ -124,7 +118,9 @@ class TxListener {
_resolve (block, callback) {
async.each(block.transactions, (tx, cb) => {
this._resolveTx(tx, () => {
this._resolveTx(tx, (error, resolvedData) => {
if (error) cb(error)
if (resolvedData) this.event.trigger('txResolved', [tx, resolvedData])
this.event.trigger('newTransaction', [tx])
cb()
})
@ -144,14 +140,15 @@ class TxListener {
var code = tx.input
contractName = this._tryResolveContract(code, contracts, 'bytecode')
if (contractName) {
this._resolveCreationAddress(tx, (error, address) => {
this._api.resolveReceipt(tx, (error, receipt) => {
if (error) return cb(error)
var address = receipt.contractAddress
this._resolvedContracts[address] = contractName
this._resolveFunction(contractName, contracts, tx, true)
var fun = this._resolveFunction(contractName, contracts, tx, true)
if (this._resolvedTransactions[tx.hash]) {
this._resolvedTransactions[tx.hash].contractAddress = address
}
return cb()
return cb(null, {to: null, contractName: contractName, function: fun, creationAddress: address})
})
return
}
@ -160,13 +157,14 @@ class TxListener {
// first check known contract, resolve against the `runtimeBytecode` if not known
contractName = this._resolvedContracts[tx.to]
if (!contractName) {
this.currentWeb3().eth.getCode(tx.to, (error, code) => {
this._api.web3().eth.getCode(tx.to, (error, code) => {
if (error) return cb(error)
if (code) {
var contractName = this._tryResolveContract(code, contracts, 'runtimeBytecode')
if (contractName) {
this._resolvedContracts[tx.to] = contractName
this._resolveFunction(contractName, contracts, tx, false)
var fun = this._resolveFunction(contractName, contracts, tx, false)
return cb(null, {to: tx.to, contractName: contractName, function: fun})
}
}
return cb()
@ -174,22 +172,13 @@ class TxListener {
return
}
if (contractName) {
this._resolveFunction(contractName, contracts, tx, false)
var fun = this._resolveFunction(contractName, contracts, tx, false)
return cb(null, {to: tx.to, contractName: contractName, function: fun})
}
return cb()
}
}
_resolveCreationAddress (tx, cb) {
this.currentWeb3().eth.getTransactionReceipt(tx.hash, (error, receipt) => {
if (!error) {
cb(null, receipt.contractAddress)
} else {
cb(error)
}
})
}
_resolveFunction (contractName, compiledContracts, tx, isCtor) {
var abi = JSON.parse(compiledContracts[contractName].interface)
var inputData = tx.input.replace('0x', '')
@ -222,6 +211,7 @@ class TxListener {
params: params
}
}
return this._resolvedTransactions[tx.hash]
}
_tryResolveContract (codeToResolve, compiledContracts, type) {

Loading…
Cancel
Save