Как разобрать журнал транзакций с помощью web3.js?

Анализатор событий в web3 обеспечивает удобную функциональность анализа событий, и я использую его для записи всех событий в файл, но его очень сложно использовать для просмотра отдельных событий для конкретной транзакции для автоматического тестирования, поскольку он вызывает ненужные и сложные для управления аспектом параллелизма, когда он не нужен.

Когда я получаю результат транзакции с помощью web3, у меня на руках есть квитанция о транзакции, и это идеальное время для синхронной проверки результата и применения критериев проверки «пройдено/не пройдено».

Я хотел бы проанализировать раздел журнала квитанции, но не могу найти функцию в web3 для этого. Он существует?

Поскольку более свежие ответы не получили достаточного количества голосов, здесь следует четко указать, что версии web3 1.0 и более поздние версии предоставляют API, чтобы сделать это за вас, вам больше не нужно взламывать его самостоятельно.

Ответы (6)

Сделайте это: вам нужно будет получить код из web3, и это работает лучше всего, если ваш интерфейс связан с помощью чего-то вроде webpack или browserify:

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

В этом случае мы декодируем данные журнала, которые содержат две переменные: одну строкового типа и одну типа uint.

РЕДАКТИРОВАТЬ:

Если у вас есть доступный ABI, вы можете определить, какое событие связано с этим 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.
}
Разве это не должно использовать сгенерированный компилятором ABI для его декодирования? Вот как это работает с событиями...
Данные журнала кодируются ABI. Указанные типы («строка», затем «uint») сообщают декодеру, как его декодировать. Вам не нужен полный ABI контракта — только типы, относящиеся к конкретному журналу. Позвольте мне отредактировать свой ответ, чтобы лучше описать, как это работает.
Ответ отредактирован. Должен дать вам больше информации о том, как обрабатывать данные журнала.
Проблема в том, что мне не нужно указывать типы для события, объект JSON создается автоматически. Создание созданных вручную типов JSON на основе изучения кода Solidity утомительно и чревато ошибками. Возможно, мне следует изменить вопрос на «автоматический анализ», поскольку предлагаемая вами процедура выполняется вручную.
Я не уверен, что вы имеете в виду между «автоматическим» и «ручным» в этом контексте. Если вы хотите преобразовать данные журнала (шестнадцатеричную строку) в объекты Javascript, сделайте это следующим образом. Web3 не предоставляет функции, позволяющей сделать это за вас. Если вам нужна эта функция, я рекомендую вам отправить этот код в Web3 и попытаться включить его. Ваше здоровье!
Я в порядке с созданием кода, но я хотел бы получить несколько советов о том, с чего начать. как код обработчика событий определяет имя события, ищет его в ABI и создает объект JSON для события? Я не могу добраться до конца марта, у меня другие сроки.
Хм, есть проблема с кэшированием в stackexchange, почему-то я не видел добавленного кода. Теперь я вижу, что это полностью управляется данными, а не вручную. Все, что мне нужно, это ABI и журнал событий, и все в порядке. Я назначу награду. Теперь вы хотите проверить это в Web3 или хотите, чтобы я? В конце марта я собираюсь добавить первоклассный объект транзакции в свои расширения web3, вопрос в том, хочет это сообщество или нет...
Не стесняйтесь представить. Не уверен относительно вашего объекта транзакции. Но я использую эту абстракцию контракта последние пару месяцев (PS: я ее написал), и она хорошо работает для меня. Буду рад вашим мыслям. github.com/ConsenSys/эфирный пудинг
Я пинговал тебя на Gitter
Я пытаюсь сделать то же самое, но не могу найти coder.js, может ли кто-нибудь сказать мне местоположение на web3/lib в ОС Windows
@Tim Coulter @Paul S Не могли бы вы упомянуть, как изменить SolidityCoder.decodeParams(["string", "uint"], log.data.replace("0x", ""));параметры, когда тип ввода нашей функции представляет собой массив, подобный этому: function newObject(bytes32 _id, uint256 number_of_sub_states, bytes32[10] sub_states_types, bytes32[10] sub_states_values, address _owner)? Спасибо.
@Tim Coulter @Paul S Когда я использую , var SolidityCoder = require("web3/lib/solidity/coder.js");я получаю эту ошибку: Error: Cannot find module 'web3/lib/solidity/coder.js'Вы знаете, в чем причина? Спасибо.
вероятно, более новая версия web3 изменила внутренности

Теперь вы можете использовать эту web3.eth.abi.decodeLogфункцию (web3 1.0).

Пример из документации:

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'
}
копипаст хм
@MH, ты, должно быть, новичок в SE? Идея состоит в том, чтобы дать ответ, а не просто ссылку. Копипаста — идеальный способ сделать это.

Тим:

Большое спасибо за указатель. Вы заставили меня, наконец, понять некоторые внутренности web3.js.

Я нашел более чистый способ сделать это, который охватывает все крайние случаи на самом деле довольно сложного формата сообщений журнала (например, индексация). Я только что использовал SolidityEvent из web3, чтобы сделать за меня уже проверенную работу.

Ниже приведен код. У меня есть этот код на github , включая тестовый код .

// 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);
    })
}
обратите внимание, что если у вас несколько контрактов, вам нужны ABI для всех контрактов. Я не собираюсь поддерживать этот пример в актуальном состоянии. Если вы хотите быть в курсе, перейдите по ссылке github. Я уже исправил несколько ошибок. Каскадные функции массива очень плохи, так как пустой массив вызывает ошибку, выдаваемую javascript...
Это очень хороший ответ, если кто-то работает с web v0.20.x.
Да, комментарий скрыт выше, но в последних версиях web3.js квитанция о транзакции анализируется для вас.

Для web3.js 1.0 используйте следующее:

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

выход

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

Получив квитанцию ​​о транзакции ( tr), вы знаете номер блока транзакции ( tr.blockNumber). Итак, вы можете сделать следующее:

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

Это вернет массив всех событий типа MyEvent, сгенерированного контрактом myContractв транзакции, на которую ссылается квитанция транзакции tr.

С 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' } } ]