Повторяющиеся события, запускаемые в прослушивателе Web3

Я экспериментирую с подключением Ethereum к веб-приложению React, используя в качестве основы это хорошее учебное репозиторий (спасибо uzyn): https://github.com/uzyn/ethereum-webpack-example-dapp

Я успешно подключаюсь к локальному экземпляру TestRPC 3.0.3, у меня развернут контракт Solidity 0.4.8 «MyToken», и я успешно использую функцию контракта, называемую «передача». Я знаю, что перевод выполняется только один раз из обновленных балансов учетной записи, но я получаю два экземпляра события «Перевод» в консоли браузера. Если я уберу строку, генерирующую событие, я ничего не получу в консоли.

Кто-нибудь может подсказать, почему так? Спасибо!

Я вызываю эту контрактную функцию, которая генерирует событие:

/* Send coins */
function transfer(address _to, uint256 _value) {

    if (balanceOf[msg.sender] < _value) throw;           // Check if the sender has enough
    if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows
    balanceOf[msg.sender] -= _value;                     // Subtract from the sender
    balanceOf[_to] += _value;                            // Add the same to the recipient

    Transfer(msg.sender, _to, _value);                   // Notify anyone listening that this transfer took place
}

С этим блоком кода:

  MyTokenContract.transfer.sendTransaction(
    web3.eth.accounts[1],
    100,
    (error, result) => {
      if (error) {
        console.log(`Error occurred: ${error}`);
        return;
      }

      const accountOneBalance = MyTokenContract.balanceOf(web3.eth.accounts[0]);
      const accountTwoBalance = MyTokenContract.balanceOf(web3.eth.accounts[1]);

      console.log(`accountOneBalance: ${accountOneBalance}`);
      console.log(`accountTwoBalance: ${accountTwoBalance}`);
    }
  );

И у меня есть этот код Web3, прослушивающий события контракта:

MyTokenContract.allEvents((error, event) => {
if (error) {
  console.log(`Event error: ${error}`);
  return;
}

const eventName = event.event;
const accountFrom = event.args.from;
const accountTo = event.args.to;
const amount = event.args.value;
console.log(`Event '${eventName}' from account ${accountFrom} to ${accountTo} of ${amount}`);

});

Когда он запускается, я получаю два события, зарегистрированные в моей консоли Chrome:

accountOneBalance: 249900
accountTwoBalance: 100
Event 'Transfer' from account 0xdd0efd8df206cb598bda146ae567f5d398436226 to 0x7497ac386ae2e1eb58210d08e6c401b56417e04d of 100

Event 'Transfer' from account 0xdd0efd8df206cb598bda146ae567f5d398436226 to 0x7497ac386ae2e1eb58210d08e6c401b56417e04d of 100
Если вы удвоите строку в том transferместе, где он создает событие (таким образом, создавая два события), сколько событий он теперь регистрирует? Три? Четыре? У меня есть подозрение, что это может помочь вам отладить.
Спасибо за предложение, это хорошая идея. Я все еще надеюсь, что это не глупая логическая ошибка :) Итак, когда я добавляю дополнительное событие и делаю зарегистрированные значения «1» и «2» соответственно, он печатает 1,2,1,2. Возможно, это как-то связано с тем, как React загружает страницу. Я буду продолжать расследование, когда у меня будет время, и опубликую обновление, если не будет другого предложения. Спасибо!
должно быть ошибка web3, вы когда-нибудь находили способ обойти это?

Ответы (6)

У меня была та же проблема, и приведенное выше решение не сработало в случае, если testrpc запущен и уже запустил некоторые события, а затем при запуске кода Web3 часы возвращают последнее инициированное событие. Что решило проблему и получило только новые события:

var latestBlock = web3.eth.blockNumber; //get the latest blocknumber
contractInstance.MyEvent({fromBlock: latestBlock}, (error, event) => { 
    if (error) {
     console.log(error); return; 
    } else {
        if(event.blockNumber != latestBlock) {   //accept only new events
        console.log(event);
        latestBlock = latestBlock + 1;   //update the latest blockNumber
        }
    }

Надеюсь, это поможет!

это сработало для меня

MyTokenContract.allEvents({fromBlock:'latest'}, (ошибка, событие) => { if (ошибка) { console.log( Event error: ${error}); return; }

Несмотря на то, что в документах указано, что «последний» используется по умолчанию, это исправило мои повторяющиеся события. Я не использовал allEvents. Я смотрел только одно мероприятие по контракту.

Надеюсь, поможет.

У меня все еще было это в марте 2019 года, я решил это, создав Setв своем javascript и добавив к нему blockNumbers в своем обратном вызове, чтобы гарантировать отсутствие дубликатов.

var eventBlocks = new Set()

listenCallback = async (error, event, type) => {
  if (error) { console.log(error); }
  else {
    let blockNumber = event.blockNumber;
    if (eventBlocks.has(blockNumber)) return;
    eventBlocks.add(blockNumber);
    //do everything else you want to do
  }
}
Я тестирую с помощью ropsten testnet, и blockNumber не совпадает с двумя повторяющимися событиями: 5777243и5777244

Я сделал именно то, что было сделано выше. Я ловил свое исходное событие следующим образом:

  this.buyEvent = this.$store.state.web3contract.BuyTransaction({},{fromBlock: 0, toBlock: 'latest'});
          this.buyEvent.watch(function(err,res){
console.log(res);
console.log(err);
})

Чтобы исправить двойное срабатывание, я добавил этот дополнительный фрагмент кода, и он решил мою проблему.

  this.$store.state.web3contract.allEvents({fromBlock:'latest'}, function (error,event){
    if (error) {
      console.log(error)
    } else {
      console.log("All Events")
      console.log(event);
    }
  })

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

Наконец-то я понял это, братья по блокчейну.

Я создавал бота, чтобы наблюдать за покупками/продажами на Uniswap, и продолжал получать повторяющиеся события Transfer для одного и того же Tx Hash. Не уверен, что это связано с web3 или с моим локальным узлом Eth, но это сводило меня с ума. Я пытался ограничиться блоками (например, если был найден следующий блок, игнорировать), но я получал несколько событий с одним и тем же хэшем в одном блоке, так что это не сработало.

Наконец, я нашел способ сравнить последний Tx Hash с текущим и, если они совпадают, игнорировать остальную часть процесса; если не продолжить обработку транзакции:



    async function savetxhash(lasthash) {   //async function to save last tx
        return new Promise((resolve) => {

        resolve(lasthash)

       });
      }

      var lasthash = ""                     //declare empty at first

     //Here you will have your web3.eth.Transfer() or .allEvents() listener code
     //Then below that you will compare saved hash to new one

       console.log('This hash is '+event.transactionHash+'')
       console.log('Last hash was '+lasthash+'')
                      
       if (lasthash.toLowerCase() == event.transactionHash.toLowerCase()) {
           console.log('Found duplicate tx hash, skipping...')
           } else {
           console.log('No duplicate tx hash found, doing something important...')
           }
                      
       lasthash = await savetxhash(event.transactionHash)   //lastly call the function


Я столкнулся с той же проблемой здесь, используя web3 ^1.3.6.

Судя по этой проблеме в репозитории web3.js , это похоже на проблему с самой библиотекой.

Мое решение заключалось в том, чтобы хранить хэш транзакции каждого события и сравнивать его перед выполнением какой-либо логики.