Event Logging

Vyper can log events to be caught and displayed by user interfaces.

Example of Logging

This example is taken from the sample ERC20 contract and shows the basic flow of event logging:

# Events of the token.
event Transfer:
    sender: indexed(address)
    receiver: indexed(address)
    value: uint256

event Approval:
    owner: indexed(address)
    spender: indexed(address)
    value: uint256

# Transfer some tokens from message sender to another address
def transfer(_to : address, _value : uint256) -> bool:

   ... Logic here to do the real work ...

   # All done, log the event for listeners
   log Transfer(msg.sender, _to, _value)

Let’s look at what this is doing.

  1. We declare two event types to log. The two events are similar in that they contain two indexed address fields. Indexed fields do not make up part of the event data itself, but can be searched by clients that want to catch the event. Also, each event contains one single data field, in each case called value. Events can contain several arguments with any names desired.

  2. In the transfer function, after we do whatever work is necessary, we log the event. We pass three arguments, corresponding with the three arguments of the Transfer event declaration.

Clients listening to the events will declare and handle the events they are interested in using a library such as web3.js:

var abi = /* abi as generated by the compiler */;
var MyToken = web3.eth.contract(abi);
var myToken = MyToken.at("0x1234...ab67" /* address */);

// watch for changes in the callback
var event = myToken.Transfer(function(error, result) {
    if (!error) {
        var args = result.returnValues;
        console.log('value transferred = ', args._amount);
    }
});

In this example, the listening client declares the event to listen for. Any time the contract sends this log event, the callback will be invoked.

Declaring Events

Let’s look at an event declaration in more detail.

event Transfer:
    sender: indexed(address)
    receiver: indexed(address)
    value: uint256

Event declarations look similar to struct declarations, containing one or more arguments that are passed to the event. Typical events will contain two kinds of arguments:

  • Indexed arguments, which can be searched for by listeners. Each indexed argument is identified by the indexed keyword. Here, each indexed argument is an address. You can have any number of indexed arguments, but indexed arguments are not passed directly to listeners, although some of this information (such as the sender) may be available in the listener’s results object.

  • Value arguments, which are passed through to listeners. You can have any number of value arguments and they can have arbitrary names, but each is limited by the EVM to be no more than 32 bytes.

It is also possible to create an event with no arguments. In this case, use the pass statement:

event Foo: pass

Logging Events

Once an event is declared, you can log (send) events. You can send events as many times as you want to. Please note that events sent do not take state storage and thus do not cost gas: this makes events a good way to save some information. However, the drawback is that events are not available to contracts, only to clients.

Logging events is done using the log statement:

log Transfer(msg.sender, _to, _amount)

The order and types of arguments given must match the order of arguments used when declaring the event.

Listening for Events

In the example listener above, the result arg actually passes a large amount of information. Here we’re most interested in result.returnValues. This is an object with properties that match the properties declared in the event. Note that this object does not contain the indexed properties, which can only be searched in the original myToken.Transfer that created the callback.