0

I built a guessing game using an web3 v1.3.0 and it worked great, then I found out that version was deprecated so I upgraded to v4.7.0.
The new code is expected to listen for an event:
event NumberGuessed(address indexed player, uint guessedNumber, uint numGuesses, bool isCorrect); emitted by a Solidity contract https://sepolia.etherscan.io/address/0xB5B3F727ab43bECbC61CD983BA60869774A54640

The web3 javascript is imported using CDN: <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/web3.min.js"></script>
Then I have

<script>
  const web3 = new Web3(window.ethereum);
  const address = "0xB5B3F727ab43bECbC61CD983BA60869774A54640";
  const ABI = [...];
  const instance = new web3.eth.Contract(ABI, address);
...

The code that is expected to listen to the event:

/*
  The event listener that is expected to capture the returned event.
  It does nothing, no error.
*/
  instance.events.NumberGuessed({
    fromBlock: 'latest'
  }, function(error, event) {
    console.log('@196');
    if (error) {
      console.error('Error:', error);
    } else {
      console.log('NumberGuessed event:', event.returnValues);
    }
  });
/*
This code captures some data.
But I don't get the returned guessedNumber
*/
web3.currentProvider.on('message', (message) => {
   console.log("@208");
   console.log("message.type:", message.type);
   console.log("message:", message);
}).on('error', console.error);

This is what I find in the log.

@208
message.type: eth_subscription
message: 
{
    "type": "eth_subscription",
    "data": {
        "subscription": "0x8a847a2a780d3c3ed6f58a2d51f865a6",
        "result": {
            "address": "0xb5b3f727ab43becbc61cd983ba60869774a54640",
            "blockHash": "0xc5a2e0c457bed08d9708bb6be2cf0932cc845a24012aed7dee553f0d9c0dcfde",
            "blockNumber": "0x557ddf",
            "data": "0x000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000",
            "logIndex": "0x3",
            "removed": false,
            "topics": [
                "0x00ca972d572a1573ce61f99cb22d78e85c775628c176b13591bc5d474cb770ec",
                "0x000000000000000000000000b5b3f727ab43becbc61cd983ba60869774a54640"
            ],
            "transactionHash": "0x091af4e5c4611bf172626c5914804a2afb050c912882ef18efeb30fbb72719fc",
            "transactionIndex": "0x4"
        }
    }
}

Now I have two questions.

  1. Given that instance.events.NumberGuessed runs but doesn't do anything or even throw an error, what is the proper way of capturing an emitted event using an explicit function reference?

  2. It has been suggested that the log needs to be parsed, how can I parse the log and turn the binary values into human readable values.

Any ideas?

1 Answer 1

1
  1. In your event listener, you've to specify fromBlock and toBlock values. As, you've only specified the fromBlock as latest without any info regarding the toBlock, therefore it's not capturing anything. Instead, you should specify toBlock as latest and fromBlock as something of the past. Also, you need to add the callback function inside .on(), instead of adding it directly. Here's the code snippet for the same:

    web3.eth.getBlockNumber()
        .then(latestBlockNumber => {
    
            const fromBlockNumber = parseInt(latestBlockNumber) - 100000; // Adjust as needed
    
            const event = instance.events.NumberGuessed({
                fromBlock: fromBlockNumber,
                toBlock: 'latest' // 'latest' to get events until the latest block
            });
    
            event.on('data', function (event) {
                console.log('NumberGuessed event return values:', event.returnValues);
            });
    
            event.on('error', function (error) {
                console.error('Error:', error);
            });
        }) 
    

    Edit: There would be two separate event listener callback functions for data and error attached with event as subscription using .on().

  2. Here's how you can parse/decode the log that you're recieving as message inside web3.currentProvider.on():

    const contractAbi = [{ "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "player", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "timestamp", "type": "uint256" }], "name": "NewGame", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "player", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "guessedNumber", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "numGuesses", "type": "uint256" }, { "indexed": false, "internalType": "bool", "name": "isCorrect", "type": "bool" }], "name": "NumberGuessed", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "player", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "maxNumber", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "timestamp", "type": "uint256" }], "name": "UpdatedMaxNumber", "type": "event" }, { "inputs": [], "name": "actualNumber", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getMaxNumber", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getNumGuesses", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getNumWins", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getSender", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "_guessedNumber", "type": "uint256" }], "name": "guess", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "newGame", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "_maxNumber", "type": "uint256" }], "name": "setMaxNumber", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "thisAddress", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }]
    
    const message = {
        "type": "eth_subscription",
        "data": {
            "subscription": "0x8a847a2a780d3c3ed6f58a2d51f865a6",
            "result": {
                "address": "0xb5b3f727ab43becbc61cd983ba60869774a54640",
                "blockHash": "0xc5a2e0c457bed08d9708bb6be2cf0932cc845a24012aed7dee553f0d9c0dcfde",
                "blockNumber": "0x557ddf",
                "data": "0x000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000",
                "logIndex": "0x3",
                "removed": false,
                "topics": [
                    "0x00ca972d572a1573ce61f99cb22d78e85c775628c176b13591bc5d474cb770ec",
                    "0x000000000000000000000000b5b3f727ab43becbc61cd983ba60869774a54640"
                ],
                "transactionHash": "0x091af4e5c4611bf172626c5914804a2afb050c912882ef18efeb30fbb72719fc",
                "transactionIndex": "0x4"
            }
        } 
    }
    
    const decodedLog = web3.eth.abi.decodeLog(
                          contractAbi.find(e => e.type === 'event' && e.name === 'NumberGuessed').inputs,
                          message.data.result.data,
                          message.data.result.topics.slice(1) 
                       );
    
    console.log("NumberGuessed Event: ", decodedLog); 
    
    console.log("Guessed Number: ", parseInt(decodedLog.guessedNumber))
    
    console.log("Number Of Guesses: ", parseInt(decodedLog.numGuesses))
    

    Output:

    NumberGuessed Event:  {
      '0': '0xB5B3F727ab43bECbC61CD983BA60869774A54640',
      '1': 123n,
      '2': 14n,
      '3': false,
      __length__: 4,
      player: '0xB5B3F727ab43bECbC61CD983BA60869774A54640',
      guessedNumber: 123n,
      numGuesses: 14n,
      isCorrect: false
    }
    Guessed Number:  123
    Number Of Guesses:  14
    

    P.S. The values like 123n, 14n can be typecasted to integer using parseInt() function.

3
  • Why the "n" in the return values like numGuesses: 14n? I have noticed that in my code too.
    – pigfox
    Commented Apr 1 at 17:33
  • 1
    It’s BigNumber notation. You can typecast it to integer using parseInt(). Commented Apr 1 at 17:47
  • @pigfox, I've corrected the event listener callback function, and typecasted the number values to integer while printing. Please check. Commented Apr 2 at 9:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.