# Event

{% embed url="<https://youtu.be/GYwKDSSpzjQ>" %}
Event
{% endembed %}

## Setup

Target contract:

```solidity
pragma solidity 0.8.18;

contract Event {
    event Transfer(address indexed from, address indexed to, uint256 amount);

    function transfer(address from, address to, uint256 amount) external {
        emit Transfer(from, to, amount);
    }

    function transferMany(address from, address[] calldata to, uint256[] calldata amounts) external {
        for (uint256 i = 0; i < to.length; i++) {
            emit Transfer(from, to[i], amounts[i]);
        }
    }
}
```

Test file:

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "forge-std/Test.sol";
import {Event} from "../src/Event.sol";

// forge test --match-path test/Event.t.sol -vvvv

contract EventTest is Test {
    Event public e;

    event Transfer(address indexed from, address indexed to, uint256 amount);

    function setUp() public {
        e = new Event();
    }

    function testEmitTransferEvent() public {
        // function expectEmit(
        //     bool checkTopic1,
        //     bool checkTopic2,
        //     bool checkTopic3,
        //     bool checkData
        // ) external;

        // 1. Tell Foundry which data to check
        // Check index 1, index 2 and data
        vm.expectEmit(true, true, false, true);
        // 2. Emit the expected event
        emit Transfer(address(this), address(123), 456);
        // 3. Call the function that should emit the event
        e.transfer(address(this), address(123), 456);

        // Check only index 1
        vm.expectEmit(true, false, false, false);
        emit Transfer(address(this), address(123), 456);
        // NOTE: index 2 and data (amount) doesn't match
        //       but the test will still pass
        e.transfer(address(this), address(111), 222);
    }

    function testEmitManyTransferEvent() public {
        address[] memory to = new address[](2);
        to[0] = address(111);
        to[1] = address(222);

        uint256[] memory amounts = new uint[](2);
        amounts[0] = 1;
        amounts[1] = 2;

        for (uint256 i = 0; i < to.length; i++) {
            // 1. Tell Foundry which data to check
            vm.expectEmit(true, true, false, true);
            // 2. Emit the expected event
            emit Transfer(address(this), to[i], amounts[i]);
        }

        // 3. Call the function that should emit the event
        e.transferMany(address(this), to, amounts);
    }
}
```

## Test event example 1

To test event, follow these 3 steps:

1. Tell Foundry which data to check
2. Emit the expected event
3. Call the function that should emit the event

In step 1, we call `vm.expectEmit()`. Here is its definition:

```solidity
function expectEmit(
    bool checkTopic1, // check index1?
    bool checkTopic2, // check index2?
    bool checkTopic3, // check index3?
    bool checkData    // check data?
) external;
```

The checked event must be indexed. For example, our `Transfer` event has two indexed parameters:

```solidity
event Transfer(address indexed from, address indexed to, uint256 amount);
```

Writing a test case:

```solidity
function testEmitTransferEvent() public {
    // function expectEmit(
    //     bool checkTopic1,
    //     bool checkTopic2,
    //     bool checkTopic3,
    //     bool checkData
    // ) external;

    // 1. Tell Foundry which data to check
    // Check index 1, index 2 and data
    vm.expectEmit(true, true, false, true);
    // 2. Emit the expected event
    emit Transfer(address(this), address(123), 456);
    // 3. Call the function that should emit the event
    e.transfer(address(this), address(123), 456);
}
```

## Test event example 2

We can choose to only test some of the parameters:

```solidity
function testEmitTransferEvent() public {
    // Check only index 1
    vm.expectEmit(true, false, false, false);
    emit Transfer(address(this), address(123), 456);
    // NOTE: index 2 and data (amount) doesn't match
    //       but the test will still pass
    e.transfer(address(this), address(111), 222);
}
```

In this case, if index 1 matches then the test will pass.

## Test multiple events

To test multiple events emitted by a single function, we put step 1 and step 2 into a for loop, and then call the function outside the for loop:

```solidity
function testEmitManyTransferEvent() public {
    address[] memory to = new address[](2);
    to[0] = address(111);
    to[1] = address(222);

    uint256[] memory amounts = new uint[](2);
    amounts[0] = 1;
    amounts[1] = 2;

    for (uint256 i = 0; i < to.length; i++) {
        // 1. Tell Foundry which data to check
        vm.expectEmit(true, true, false, true);
        // 2. Emit the expected event
        emit Transfer(address(this), to[i], amounts[i]);
    }

    // 3. Call the function that should emit the event
    e.transferMany(address(this), to, amounts);
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ret2basic.gitbook.io/ctfnote/web3-security-research/foundry/event.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
