Introduction

The log mechanism is one of the most important features of a virtual machine, which is used to output specific binary data and record it in transaction-receipt while the virtual machine is running a contract. Based on the Log mechanism, the Event feature is implemented to help developers confirm, check, and quickly retrieve specific states of a smart contract. This article introduces the basics of the Event mechanism and how to decode the Event Log.

Solidity Event

Events in Solidity are defined by the event keyword. Triggering and logging an Event is implemented through the emit keyword. An Event is not just a specific event name, but can contain several parameters.

Solidity Event Example:

contract EventExampleContract {
    event Transfer(address indexed toAddress, uint256 amount);
    constructor() payable public{}
    function contractTransfer(address toAddress, uint256 amount){
        toAddress.transfer(amount);
        emit Transfer(toAddress, amount);
    }
}
  • event Transfer defines a Transfer event containing two parameters, the first being toAddress which Validators the destination address of the transfer, and the second being amount which represents the amount of the transfer.
  • emit Transfer(toAddress, amount) triggers the corresponding Event when the contract completes the transfer function.

The specification generally requires that the Event name be capitalized to distinguish it from the corresponding function. For example, the event Transfer and transfer functions.

Event Structure

Solidity uses LOG instruction to record event information in a transaction. The structure in protobuf is as follows:

message Log {
    bytes address = 1;
    repeated bytes topics = 2;
    bytes data = 3;
}

message TransactionInfo {
  // ...
  // A list of LOG represent list of events in a transaction
  repeated Log log = 8;
  // ...
}

Where the topics field indicates the topic of the event, e.g. the topic Transfer(...). Also, all parameters marked as indexed are listed in the topics field in that order.
The data field represents other non-indexed parameters of the event, such as amount.

Decoding Events Example

The ABI for the above Transfer event is defined as:

{
  "anonymous": false,
  "inputs": [
    {
      "indexed": true,
      "name": "toAddress",
      "type": "address"
    },
    {
      "indexed": false,
      "name": "amount",
      "type": "uint256"
    }
  ],
  "name": "Transfer",
  "type": "event"
}

Use gettransactioninfobyid API to get the transaction result:

log:
  address:
    289C4D540B32C7BC56953E55631F8B141EB86434 # contract address in 20-byte format
  data:
    0000000000000000000000000000000000000000000000000000000000000001
  topics:
    69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2  # topics[0]
    000000000000000000000000E552F6487585C2B58BC2C9BB4492BC1F17132CD0  # topics[1]

Check along with ABI:

  • topics[0] is 69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2. It is the result of keccak("event(address,uint256)").

  • topics[1] is 000000000000000000000000E552F6487585C2B58BC2C9BB4492BC1F17132CD0. It is the first indexed param (toAddress) with type address.

  • data is 0000000000000000000000000000000000000000000000000000000000000001. It is the param without being indexed (amount) with type uint256.