# 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);
}
```
