Skip to content

Handlers

Handlers are contracts that contain handler functions, which are responsible for executing a specific set of actions in response to predefined conditions or events. Each handler function is tailored to address a particular type of task or event.

Writing a Handler Contract

Handlers are written in a similar way to regular Foundry scripts. They live right within your Foundry project and can directly import any other contract or dependency directly.

Kyn Library

Kyn provides a Solidity library that exposes the Handler base contract which handlers must inherit from. It provides access to the register function and other utilities needed to work with handler functions.

Kyn handlers can also make use of Vulcan, our open-source development framework for Foundry projects, which contains high-level features such as a module for easily working with JSON objects and a module for performing HTTP requests.

setUp function

All handler contracts must contain a setUp function (similar to Foundry tests and scripts).

The setUp function is called upon registering a handler. It is used to register handler functions and bootstrap the initial handler configuration and functionality.

Individual handler functions can be registered using the provided register function. It is important to note that register is just an internal Solidity function that can be used within the setUp function but also within other handler functions.

The following example illustrates how a real setUp function would look:

/// @dev This handler monitors all `Transfer` events emitted by the specified address
function setUp(
    address addr,
    NotificationType notificationType,
    string memory notificationUrl
) public {
    Config memory config = Config(notificationType, notificationUrl);

    // Arbitrary metadata that the handler will have access to
    bytes memory metadata = abi.encode(config);

    // Filter used to specify that we are registering a transaction handler
    TransactionFilter memory transferEventFilter = filter()
        .metadata(metadata)
        .log(Transfer.selector, addr);

    register(transferEventFilter, this.handle);
}

Handler functions

Handler functions are external functions that will be executed depending on the different conditions that are specified when registering them. The signature of each handler function depends on the type of handler function being used, so each one receives a different kind of event data struct as an argument.

Handler functions can register and remove other handler functions as well.

Initially, we plan to support the following handler function types:

Complete example

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {println, format} from "vulcan/utils.sol";
import {strings} from "vulcan/script/strings.sol";

import {discord, DiscordClient} from "kyn-contracts/Discord.sol";

import {Handler, TransactionEvent, TransactionFilter, filter, gte} from "kyn-contracts/HandlerBase.sol";

contract Monitor is Handler {
    using strings for *;

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

    enum NotificationType {
        Discord,
        Telegram
    }

    struct Config {
        NotificationType notificationType;
        string notificationUrl;
    }

    /// @dev This is a sample handler contract
    /// @param addr The address to monitor
    /// @param notificationType{Discord,Telegram} The notification type
    /// @param notificationUrl The notification url (telegram chat id or discord webhook url)
    function setUp(
        address addr,
        NotificationType notificationType,
        string memory notificationUrl
    ) public {
        Config memory config = Config(notificationType, notificationUrl);

        TransactionFilter memory transferEventFilter = filter()
            .metadata(abi.encode(config))
            .log(Transfer.selector, addr);

        register(transferEventFilter, this.handleTransferEvent);
    }

    /// @dev This function handles transfer events for the configured address
    function handleTransferEvent(TransactionEvent calldata ev) external {
        Config memory config = abi.decode(ev.metadata, (Config));

        string memory message = string.concat(
            "EVENT MATCHED! https://etherscan.io/tx/",
            ev.hash.toString()
        );

        notify(config, message);
    }

    function notify(Config memory config, string memory message) internal {
        if (config.notificationType == NotificationType.Discord) {
            discord.create(config.notificationUrl).content(message).send();
        } else if (config.notificationType == NotificationType.Telegram) {
            revert("Not implemented");
        }
    }
}