Arbitrum Stylus logo

Stylus by Example

Events

Events allow for data to be logged publicly to the blockchain. Log entries provide the contract's address, a series of up to four topics, and some arbitrary length binary data. The Stylus Rust SDK provides a few ways to publish event logs described below.

Learn More

Log

Using the evm::log function in the Stylus SDK is the preferred way to log events. It ensures that an event will be logged in a Solidity ABI-compatible format. The log function takes any type that implements Alloy SolEvent trait. It's not recommended to attempt to implement this trait on your own. Instead, make use of the provided sol! macro to declare your Events and their schema using Solidity-style syntax to declare the parameter types. Alloy will create ABI-compatible Rust types which you can instantiate and pass to the evm::log function.

Log Usage

1// sol! macro event declaration
2// Up to 3 parameters can be indexed.
3// Indexed parameters helps you filter the logs efficiently
4sol! {
5    event Log(address indexed sender, string message);
6    event AnotherLog();
7}
8
9#[entrypoint]
10fn user_main(_input: Vec<u8>) -> ArbResult {
11    // emits a 'Log' event, defined above in the sol! macro
12    evm::log(Log {
13        sender: Address::from([0x11; 20]),
14        message: "Hello world!".to_string(),
15    });
16
17    // no data, but 'AnotherLog' event will still emit to the chain
18    evm::log(AnotherLog {});
19
20    Ok(vec![])
21}
1// sol! macro event declaration
2// Up to 3 parameters can be indexed.
3// Indexed parameters helps you filter the logs efficiently
4sol! {
5    event Log(address indexed sender, string message);
6    event AnotherLog();
7}
8
9#[entrypoint]
10fn user_main(_input: Vec<u8>) -> ArbResult {
11    // emits a 'Log' event, defined above in the sol! macro
12    evm::log(Log {
13        sender: Address::from([0x11; 20]),
14        message: "Hello world!".to_string(),
15    });
16
17    // no data, but 'AnotherLog' event will still emit to the chain
18    evm::log(AnotherLog {});
19
20    Ok(vec![])
21}

Raw Log

The evm::raw_log affordance offers the ability to send anonymous events that do not necessarily conform to the Solidity ABI. Instead, up to four raw 32-byte indexed topics are published along with any arbitrary bytes appended as data.

NOTE: It's still possible to achieve Solidity ABI compatibility using this construct. To do so you'll have to manually compute the ABI signature for the event, following the equation set in the Solidity docs. The result of that should be assigned to TOPIC_0, the first topic in the slice passed to raw_log.

Raw Log Usage

1// set up local variables
2let user = Address::from([0x22; 20]);
3let balance = U256::from(10_000_000);
4
5// declare up to 4 topics
6// topics must be of type FixedBytes<32>
7let topics = &[user.into_word()];
8
9// store non-indexed data in a byte Vec
10let mut data: Vec<u8> = vec![];
11// to_be_bytes means 'to big endian bytes'
12data.extend_from_slice(balance.to_be_bytes::<32>().to_vec().as_slice());
13
14// unwrap() here 'consumes' the Result
15evm::raw_log(topics.as_slice(), data.as_ref()).unwrap();
1// set up local variables
2let user = Address::from([0x22; 20]);
3let balance = U256::from(10_000_000);
4
5// declare up to 4 topics
6// topics must be of type FixedBytes<32>
7let topics = &[user.into_word()];
8
9// store non-indexed data in a byte Vec
10let mut data: Vec<u8> = vec![];
11// to_be_bytes means 'to big endian bytes'
12data.extend_from_slice(balance.to_be_bytes::<32>().to_vec().as_slice());
13
14// unwrap() here 'consumes' the Result
15evm::raw_log(topics.as_slice(), data.as_ref()).unwrap();

Result

Combining the above examples into the boiler plate provided below this section, deploying to a Stylus chain and then invoking the deployed contract will result in the following three events logged to the chain:

logs

1[
2  {
3    "address": "0x6cf4a18ac8efd6b0b99d3200c4fb9609dd60d4b3",
4    "topics": [
5      "0x0738f4da267a110d810e6e89fc59e46be6de0c37b1d5cd559b267dc3688e74e0",
6      "0x0000000000000000000000001111111111111111111111111111111111111111"
7    ],
8    "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f20776f726c64210000000000000000000000000000000000000000",
9    "blockHash": "0xfef880025dc87b5ab4695a0e1a6955dd7603166ecba79ce0f503a568b2ec8940",
10    "blockNumber": "0x94",
11    "transactionHash": "0xc7318dae2164eb441fb80f5b869f844e3e97ae83c24a4639d46ec4d915a30818",
12    "transactionIndex": "0x1",
13    "logIndex": "0x0",
14    "removed": false
15  },
16  {
17    "address": "0x6cf4a18ac8efd6b0b99d3200c4fb9609dd60d4b3",
18    "topics": [
19      "0xfe1a3ad11e425db4b8e6af35d11c50118826a496df73006fc724cb27f2b99946"
20    ],
21    "data": "0x",
22    "blockHash": "0xfef880025dc87b5ab4695a0e1a6955dd7603166ecba79ce0f503a568b2ec8940",
23    "blockNumber": "0x94",
24    "transactionHash": "0xc7318dae2164eb441fb80f5b869f844e3e97ae83c24a4639d46ec4d915a30818",
25    "transactionIndex": "0x1",
26    "logIndex": "0x1",
27    "removed": false
28  },
29  {
30    "address": "0x6cf4a18ac8efd6b0b99d3200c4fb9609dd60d4b3",
31    "topics": [
32      "0x0000000000000000000000002222222222222222222222222222222222222222"
33    ],
34    "data": "0x0000000000000000000000000000000000000000000000000000000000989680",
35    "blockHash": "0xfef880025dc87b5ab4695a0e1a6955dd7603166ecba79ce0f503a568b2ec8940",
36    "blockNumber": "0x94",
37    "transactionHash": "0xc7318dae2164eb441fb80f5b869f844e3e97ae83c24a4639d46ec4d915a30818",
38    "transactionIndex": "0x1",
39    "logIndex": "0x2",
40    "removed": false
41  }
42]
1[
2  {
3    "address": "0x6cf4a18ac8efd6b0b99d3200c4fb9609dd60d4b3",
4    "topics": [
5      "0x0738f4da267a110d810e6e89fc59e46be6de0c37b1d5cd559b267dc3688e74e0",
6      "0x0000000000000000000000001111111111111111111111111111111111111111"
7    ],
8    "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f20776f726c64210000000000000000000000000000000000000000",
9    "blockHash": "0xfef880025dc87b5ab4695a0e1a6955dd7603166ecba79ce0f503a568b2ec8940",
10    "blockNumber": "0x94",
11    "transactionHash": "0xc7318dae2164eb441fb80f5b869f844e3e97ae83c24a4639d46ec4d915a30818",
12    "transactionIndex": "0x1",
13    "logIndex": "0x0",
14    "removed": false
15  },
16  {
17    "address": "0x6cf4a18ac8efd6b0b99d3200c4fb9609dd60d4b3",
18    "topics": [
19      "0xfe1a3ad11e425db4b8e6af35d11c50118826a496df73006fc724cb27f2b99946"
20    ],
21    "data": "0x",
22    "blockHash": "0xfef880025dc87b5ab4695a0e1a6955dd7603166ecba79ce0f503a568b2ec8940",
23    "blockNumber": "0x94",
24    "transactionHash": "0xc7318dae2164eb441fb80f5b869f844e3e97ae83c24a4639d46ec4d915a30818",
25    "transactionIndex": "0x1",
26    "logIndex": "0x1",
27    "removed": false
28  },
29  {
30    "address": "0x6cf4a18ac8efd6b0b99d3200c4fb9609dd60d4b3",
31    "topics": [
32      "0x0000000000000000000000002222222222222222222222222222222222222222"
33    ],
34    "data": "0x0000000000000000000000000000000000000000000000000000000000989680",
35    "blockHash": "0xfef880025dc87b5ab4695a0e1a6955dd7603166ecba79ce0f503a568b2ec8940",
36    "blockNumber": "0x94",
37    "transactionHash": "0xc7318dae2164eb441fb80f5b869f844e3e97ae83c24a4639d46ec4d915a30818",
38    "transactionIndex": "0x1",
39    "logIndex": "0x2",
40    "removed": false
41  }
42]

Boilerplate

src/main.rs

1#![no_main]
2#![no_std]
3extern crate alloc;
4
5#[global_allocator]
6static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
7use alloc::vec::Vec;
8use alloc::{string::ToString, vec};
9
10use stylus_sdk::alloy_primitives::{U256, Address};
11use stylus_sdk::alloy_sol_types::sol;
12use stylus_sdk::{evm, prelude::*, ArbResult};
13
14
15#[entrypoint]
16fn user_main(_input: Vec<u8>) -> ArbResult {
17    // Insert logic from above usages here
18
19    Ok(Vec::new())
20}
1#![no_main]
2#![no_std]
3extern crate alloc;
4
5#[global_allocator]
6static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
7use alloc::vec::Vec;
8use alloc::{string::ToString, vec};
9
10use stylus_sdk::alloy_primitives::{U256, Address};
11use stylus_sdk::alloy_sol_types::sol;
12use stylus_sdk::{evm, prelude::*, ArbResult};
13
14
15#[entrypoint]
16fn user_main(_input: Vec<u8>) -> ArbResult {
17    // Insert logic from above usages here
18
19    Ok(Vec::new())
20}

Cargo.toml

1[package]
2name = "events"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7# Note: Do not ship to prod with 'debug' flag set
8stylus-sdk = { version = "0.4.2", features = ["debug"] }
9wee_alloc = "0.4.5"
10alloy-sol-types = "0.3.1"
11
12[features]
13export-abi = ["stylus-sdk/export-abi"]
14
15[profile.release]
16codegen-units = 1
17strip = true
18lto = true
19panic = "abort"
20opt-level = "s"
21
22[workspace]
1[package]
2name = "events"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7# Note: Do not ship to prod with 'debug' flag set
8stylus-sdk = { version = "0.4.2", features = ["debug"] }
9wee_alloc = "0.4.5"
10alloy-sol-types = "0.3.1"
11
12[features]
13export-abi = ["stylus-sdk/export-abi"]
14
15[profile.release]
16codegen-units = 1
17strip = true
18lto = true
19panic = "abort"
20opt-level = "s"
21
22[workspace]