¿Cómo analizo el registro de recepción de transacciones con web3.js?

El analizador de eventos en web3 proporciona una buena funcionalidad de análisis de eventos, y lo uso para registrar todos los eventos en un archivo, pero es muy difícil de usar para buscar eventos individuales para una transacción en particular para pruebas automatizadas porque induce un error innecesario y difícil. para administrar el aspecto de concurrencia cuando no es necesario que haya uno.

Cuando obtengo el resultado de una transacción con web3, tengo el recibo de la transacción a mano y ese es el momento perfecto para examinar el resultado sincrónicamente y aplicar los criterios de prueba de aprobación/rechazo.

Me gustaría analizar la sección de registro del recibo pero no puedo encontrar una función en web3 para hacerlo. ¿Existe?

Dado que las respuestas más recientes no han sido lo suficientemente votadas, debe quedar claro aquí que las versiones 1.0 y posteriores de web3 proporcionan una API para hacer esto por usted, ya no necesita hackearlo usted mismo.

Respuestas (6)

Haga esto: necesitará extraer el código de web3, y funciona mejor si su interfaz está empaquetada usando algo como webpack o browserify:

var SolidityCoder = require("web3/lib/solidity/coder.js");
var log = receipt.logs[0];
var data = SolidityCoder.decodeParams(["string", "uint"], log.data.replace("0x", ""));

En este caso, estamos decodificando datos de registro que contienen dos variables, una de tipo cadena y otra de tipo uint.

EDITAR:

Si tiene el ABI disponible, puede detectar qué evento está relacionado con ese ABI:

var SolidityCoder = require("web3/lib/solidity/coder.js");

// You might want to put the following in a loop to handle all logs in this receipt.
var log = receipt.logs[0];
var event = null;

for (var i = 0; i < abi.length; i++) {
  var item = abi[i];
  if (item.type != "event") continue;
  var signature = item.name + "(" + item.inputs.map(function(input) {return input.type;}).join(",") + ")";
  var hash = web3.sha3(signature);
  if (hash == log.topics[0]) {
    event = item;
    break;
  }
}

if (event != null) {
  var inputs = event.inputs.map(function(input) {return input.type;});
  var data = SolidityCoder.decodeParams(inputs, log.data.replace("0x", ""));
  // Do something with the data. Depends on the log and what you're using the data for.
}
¿No debería esto usar el ABI generado por el compilador para decodificarlo? Así es como funciona con los eventos...
Los datos de registro están codificados con ABI. Los tipos especificados ("cadena, luego" uint "), le dicen al decodificador cómo decodificarlo. No necesita el contrato ABI completo, solo los tipos relacionados con el registro específico. Permítanme editar mi respuesta para describir mejor cómo esto obras.
Respuesta editada. Debería darle más de una idea de cómo manejar los datos de registro.
El problema es que no tengo que especificar los tipos para un evento, un objeto JSON se crea automáticamente. La creación de tipos JSON creados manualmente a partir del examen del código de solidez es tediosa y propensa a errores. Quizás debería cambiar la pregunta a "analizar automáticamente", ya que el procedimiento que propone es manual.
No estoy seguro de lo que quiere decir entre "automático" y "manual" en este contexto. Si desea convertir los datos de registro (una cadena hexadecimal) en objetos Javascript, así es como lo hace. Web3 no proporciona una función para hacer esto por usted. Si desea esta función, le recomiendo que envíe este código a Web3 e intente incluirlo. ¡Salud!
Estoy bien con la creación del código, pero me gustaría algunos consejos sobre por dónde empezar. ¿Cómo averigua el código del controlador de eventos el nombre del evento, lo busca en la ABI y crea el objeto JSON para el evento? No puedo llegar hasta finales de marzo, tengo otros plazos.
Hmm, hay un problema de almacenamiento en caché con stackexchange, no vi su código agregado por alguna razón. Ahora veo que es completamente basado en datos, no manual. Todo lo que necesito es el ABI y el registro de eventos y todo está bien. Voy a otorgar la recompensa. Ahora, ¿quieres registrar esto en Web3 o quieres que lo haga yo? A fines de marzo agregaré un objeto de transacción de primera clase a mis extensiones de web3, la pregunta es si la comunidad lo quiere o no...
Siéntase libre de enviar. No estoy seguro con respecto a su objeto de transacción. Pero he estado usando esta abstracción de contrato durante los últimos dos meses (PD: lo escribí) y funciona bien para mí. Me encantaría tus pensamientos. github.com/ConsenSys/ether-pudín
Te hice ping en gitter
Estoy intentando lo mismo, pero no puedo encontrar el codificador.js, ¿alguien puede decirme la ubicación en web3/lib en el sistema operativo Windows?
@Tim Coulter @Paul S ¿Podría mencionar cómo cambiar SolidityCoder.decodeParams(["string", "uint"], log.data.replace("0x", ""));los parámetros cuando el tipo de entrada de nuestra función es una matriz como esta function newObject(bytes32 _id, uint256 number_of_sub_states, bytes32[10] sub_states_types, bytes32[10] sub_states_values, address _owner):? Gracias.
@Tim Coulter @Paul S Cuando lo uso var SolidityCoder = require("web3/lib/solidity/coder.js");, recibo este error: Error: Cannot find module 'web3/lib/solidity/coder.js'¿Sabe cuál es el motivo? Gracias.
probablemente la versión más nueva de web3 ha reorganizado las partes internas

Ahora puede utilizar la web3.eth.abi.decodeLogfunción (web3 1.0).

Ejemplo de la documentación:

web3.eth.abi.decodeLog([{
    type: 'string',
    name: 'myString'
},{
    type: 'uint256',
    name: 'myNumber',
    indexed: true
},{
    type: 'uint8',
    name: 'mySmallNumber',
    indexed: true
}],
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000748656c6c6f252100000000000000000000000000000000000000000000000000',
['0x000000000000000000000000000000000000000000000000000000000000f310', '0x0000000000000000000000000000000000000000000000000000000000000010']);
> Result {
    '0': 'Hello%!',
    '1': '62224',
    '2': '16',
    myString: 'Hello%!',
    myNumber: '62224',
    mySmallNumber: '16'
}
copia y pega mmm
@MH, ¿debes ser nuevo aquí en SE? La idea es proporcionar una respuesta, no solo un enlace. Copiar y pegar es una forma perfecta de hacerlo.

Tim:

Muchas gracias por el puntero. Me obligaste a comprender finalmente algunas de las partes internas de web3.js.

Encontré una forma más limpia de hacer esto que cubre todos los casos de esquina del formato de mensaje de registro bastante complicado (por ejemplo, indexación). Acabo de usar SolidityEvent de web3 para hacer el trabajo ya probado por mí.

A continuación se muestra el código. Tengo este código en github , incluido el código de prueba .

// XXX move this to a hook function
var SolidityEvent = require("web3/lib/web3/event.js");
Pudding.logParser = function (logs, abi) {

    // pattern similar to lib/web3/contract.js:  addEventsToContract()
    var decoders = abi.filter(function (json) {
        return json.type === 'event';
    }).map(function(json) {
        // note first and third params required only by enocde and execute;
        // so don't call those!
        return new SolidityEvent(null, json, null);
    });

    return logs.map(function (log) {
        return decoders.find(function(decoder) {
            return (decoder.signature() == log.topics[0].replace("0x",""));
        }).decode(log);
    })
}
tenga en cuenta que si tiene varios contratos, necesita los ABI para todos los contratos. No voy a mantener este ejemplo actualizado. Si quieres estar actualizado, sigue el enlace de github. Ya he corregido algunos errores. Las funciones de matriz en cascada son muy malas, ya que una matriz vacía hace que javascript arroje un error ...
Esta es una muy buena respuesta si alguien trabaja con web v0.20.x.
Sí, el comentario está oculto arriba, pero las versiones recientes de web3.js analizan el recibo de la transacción.

Para web3.js 1.0 use lo siguiente:

contractInstance.inputs = [{"indexed": false, "name": "_id", "type": "uint256"}]; //event abi
contractInstance._decodeEventABI({data: '0x0'}); //event raw data

producción

{
  returnValues: 
   Result {
     '0': '1',
     _id: '1',
  },
  raw: {
    data: '0x0'
  }
}

Una vez que tenga el recibo de la transacción ( tr), sabrá el número de bloque de la transacción ( tr.blockNumber). Por lo tanto, puede hacer lo siguiente:

myContract.MyEvent (
  {},
  {fromBlock: tr.blockNumber, toBlock: tr.blockNumber}).
    get ().
      filter (function (e) {
        return e.transactionHash == tr.transactionHash
      });

Esto devolverá una matriz de todos los eventos de tipo MyEventgenerados por contrato myContracten transacción referidos por recibo de transacción tr.

Con web3.js 0.20.6.

$ node
> var AllEvents = require('web3/lib/web3/allevents')
undefined
> var decodeEventsForContract = (C, tr) => {
    const ae = new AllEvents(C._web3, C.abi, C.address);

    // ae.decode mutates the args, so we deep copy
    return JSON.parse(JSON.stringify(tr)) 
      .logs
      .filter(l => l.address === C.address)
      .map(l => ae.decode(l));
  }
undefined
> decodeEventsForContract(MyTokenContract, txreceipt);
[ { logIndex: 0,
    transactionIndex: 0,
    transactionHash: '0xbc68d5ddc391fab84cd633a77dbc815cbc42546a13de9d123f7a5b820faa3cb4',
    blockHash: '0xb604998aa0b6bece492530c9cbab494349a31b18a34067f225e1b59613952051',
    blockNumber: 21,
    address: '0x345ca3e014aaf5dca488057592ee47305d9b3e10',
    type: 'mined',
    event: 'Transfer',
    args: 
     { from: '0x0000000000000000000000000000000000000000',
       to: '0x627306090abab3a6e1400e9345bc60c78a8bef57',
       value: [BigNumber] } },
  { logIndex: 1,
    transactionIndex: 0,
    transactionHash: '0xbc68d5ddc391fab84cd633a77dbc815cbc42546a13de9d123f7a5b820faa3cb4',
    blockHash: '0xb604998aa0b6bece492530c9cbab494349a31b18a34067f225e1b59613952051',
    blockNumber: 21,
    address: '0x345ca3e014aaf5dca488057592ee47305d9b3e10',
    type: 'mined',
    event: 'Mint',
    args: 
     { to: '0x627306090abab3a6e1400e9345bc60c78a8bef57',
       value: [BigNumber],
       minter: '0x627306090abab3a6e1400e9345bc60c78a8bef57' } } ]