ETH Price: $1,945.30 (-0.90%)
Gas: 0.03 Gwei

Contract Diff Checker

Contract Name:
Root

Contract Source Code:

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

import {Auth} from "src/Auth.sol";
import {MessagesLib} from "src/libraries/MessagesLib.sol";
import {BytesLib} from "src/libraries/BytesLib.sol";
import {IRoot, IRecoverable} from "src/interfaces/IRoot.sol";
import {IAuth} from "src/interfaces/IAuth.sol";

/// @title  Root
/// @notice Core contract that is a ward on all other deployed contracts.
/// @dev    Pausing can happen instantaneously, but relying on other contracts
///         is restricted to the timelock set by the delay.
contract Root is Auth, IRoot {
    using BytesLib for bytes;

    /// @dev To prevent filing a delay that would block any updates indefinitely
    uint256 internal constant MAX_DELAY = 4 weeks;

    address public immutable escrow;

    /// @inheritdoc IRoot
    bool public paused;
    /// @inheritdoc IRoot
    uint256 public delay;
    /// @inheritdoc IRoot
    mapping(address => uint256) public endorsements;
    /// @inheritdoc IRoot
    mapping(address relyTarget => uint256 timestamp) public schedule;

    constructor(address _escrow, uint256 _delay, address deployer) Auth(deployer) {
        require(_delay <= MAX_DELAY, "Root/delay-too-long");

        escrow = _escrow;
        delay = _delay;
    }

    // --- Administration ---
    /// @inheritdoc IRoot
    function file(bytes32 what, uint256 data) external auth {
        if (what == "delay") {
            require(data <= MAX_DELAY, "Root/delay-too-long");
            delay = data;
        } else {
            revert("Root/file-unrecognized-param");
        }
        emit File(what, data);
    }

    /// --- Endorsements ---
    /// @inheritdoc IRoot
    function endorse(address user) external auth {
        endorsements[user] = 1;
        emit Endorse(user);
    }

    /// @inheritdoc IRoot
    function veto(address user) external auth {
        endorsements[user] = 0;
        emit Veto(user);
    }

    /// @inheritdoc IRoot
    function endorsed(address user) public view returns (bool) {
        return endorsements[user] == 1;
    }

    // --- Pause management ---
    /// @inheritdoc IRoot
    function pause() external auth {
        paused = true;
        emit Pause();
    }

    /// @inheritdoc IRoot
    function unpause() external auth {
        paused = false;
        emit Unpause();
    }

    /// --- Timelocked ward management ---
    /// @inheritdoc IRoot
    function scheduleRely(address target) public auth {
        schedule[target] = block.timestamp + delay;
        emit ScheduleRely(target, schedule[target]);
    }

    /// @inheritdoc IRoot
    function cancelRely(address target) public auth {
        require(schedule[target] != 0, "Root/target-not-scheduled");
        schedule[target] = 0;
        emit CancelRely(target);
    }

    /// @inheritdoc IRoot
    function executeScheduledRely(address target) external {
        require(schedule[target] != 0, "Root/target-not-scheduled");
        require(schedule[target] <= block.timestamp, "Root/target-not-ready");

        wards[target] = 1;
        emit Rely(target);

        schedule[target] = 0;
    }

    /// --- Incoming message handling ---
    /// @inheritdoc IRoot
    function handle(bytes calldata message) public auth {
        MessagesLib.Call call = MessagesLib.messageType(message);

        if (call == MessagesLib.Call.ScheduleUpgrade) {
            scheduleRely(message.toAddress(1));
        } else if (call == MessagesLib.Call.CancelUpgrade) {
            cancelRely(message.toAddress(1));
        } else if (call == MessagesLib.Call.RecoverTokens) {
            (address target, address token, address to, uint256 amount) =
                (message.toAddress(1), message.toAddress(33), message.toAddress(65), message.toUint256(97));
            recoverTokens(target, token, to, amount);
        } else {
            revert("Root/invalid-message");
        }
    }

    /// --- External contract ward management ---
    /// @inheritdoc IRoot
    function relyContract(address target, address user) external auth {
        IAuth(target).rely(user);
        emit RelyContract(target, user);
    }

    /// @inheritdoc IRoot
    function denyContract(address target, address user) external auth {
        IAuth(target).deny(user);
        emit DenyContract(target, user);
    }

    /// --- Token recovery ---
    /// @inheritdoc IRoot
    function recoverTokens(address target, address token, address to, uint256 amount) public auth {
        IRecoverable(target).recoverTokens(token, to, amount);
        emit RecoverTokens(target, token, to, amount);
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

import {IAuth} from "src/interfaces/IAuth.sol";

/// @title  Auth
/// @notice Simple authentication pattern
/// @author Based on code from https://github.com/makerdao/dss
abstract contract Auth is IAuth {
    /// @inheritdoc IAuth
    mapping(address => uint256) public wards;

    constructor(address initialWard) {
        wards[initialWard] = 1;
        emit Rely(initialWard);
    }

    /// @dev Check if the msg.sender has permissions
    modifier auth() {
        require(wards[msg.sender] == 1, "Auth/not-authorized");
        _;
    }

    /// @inheritdoc IAuth
    function rely(address user) external auth {
        wards[user] = 1;
        emit Rely(user);
    }

    /// @inheritdoc IAuth
    function deny(address user) external auth {
        wards[user] = 0;
        emit Deny(user);
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

import {BytesLib} from "src/libraries/BytesLib.sol";

/// @title  MessagesLib
/// @dev    Library for encoding and decoding messages.
library MessagesLib {
    using BytesLib for bytes;

    enum Call {
        /// 0 - An invalid message
        Invalid,
        // --- Gateway ---
        /// 1 - Proof
        MessageProof,
        /// 2 - Initiate Message Recovery
        InitiateMessageRecovery,
        /// 3 - Dispute Message Recovery
        DisputeMessageRecovery,
        /// 4 - Batch Messages
        Batch,
        // --- Root ---
        /// 5 - Schedule an upgrade contract to be granted admin rights
        ScheduleUpgrade,
        /// 6 - Cancel a previously scheduled upgrade
        CancelUpgrade,
        /// 7 - Recover tokens sent to the wrong contract
        RecoverTokens,
        // --- Gas service ---
        /// 8 - Update Centrifuge gas price
        UpdateCentrifugeGasPrice,
        // --- Pool Manager ---
        /// 9 - Add an asset id -> EVM address mapping
        AddAsset,
        /// 10 - Add Pool
        AddPool,
        /// 11 - Add a Pool's Tranche Token
        AddTranche,
        /// 12 - Allow an asset to be used as an asset for investing in pools
        AllowAsset,
        /// 13 - Disallow an asset to be used as an asset for investing in pools
        DisallowAsset,
        /// 14 - Update the price of a Tranche Token
        UpdateTranchePrice,
        /// 15 - Update tranche token metadata
        UpdateTrancheMetadata,
        /// 16 - Update Tranche Hook
        UpdateTrancheHook,
        /// 17 - A transfer of assets
        TransferAssets,
        /// 18 - A transfer of tranche tokens
        TransferTrancheTokens,
        /// 19 - Update a user restriction
        UpdateRestriction,
        /// --- Investment Manager ---
        /// 20 - Increase an investment order by a given amount
        DepositRequest,
        /// 21 - Increase a Redeem order by a given amount
        RedeemRequest,
        /// 22 - Executed Collect Invest
        FulfilledDepositRequest,
        /// 23 - Executed Collect Redeem
        FulfilledRedeemRequest,
        /// 24 - Cancel an investment order
        CancelDepositRequest,
        /// 25 - Cancel a redeem order
        CancelRedeemRequest,
        /// 26 - Executed Decrease Invest Order
        FulfilledCancelDepositRequest,
        /// 27 - Executed Decrease Redeem Order
        FulfilledCancelRedeemRequest,
        /// 28 - Request redeem investor
        TriggerRedeemRequest
    }

    function messageType(bytes memory _msg) internal pure returns (Call _call) {
        _call = Call(_msg.toUint8(0));
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

/// @title  Bytes Lib
/// @dev    Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
///         The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
/// @author Modified from Solidity Bytes Arrays Utils v0.8.0
library BytesLib {
    function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } { mstore(mc, mload(cc)) }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
        require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function toBytes16(bytes memory _bytes, uint256 _start) internal pure returns (bytes16) {
        require(_bytes.length >= _start + 16, "toBytes16_outOfBounds");
        bytes16 tempBytes16;

        assembly {
            tempBytes16 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes16;
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

import {IMessageHandler} from "src/interfaces/gateway/IGateway.sol";

interface IRecoverable {
    /// @notice Used to recover any ERC-20 token.
    /// @dev    This method is called only by authorized entities
    /// @param  token It could be 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
    ///         to recover locked native ETH or any ERC20 compatible token.
    /// @param  to Receiver of the funds
    /// @param  amount Amount to send to the receiver.
    function recoverTokens(address token, address to, uint256 amount) external;
}

interface IRoot is IMessageHandler {
    // --- Events ---
    event File(bytes32 indexed what, uint256 data);
    event Pause();
    event Unpause();
    event ScheduleRely(address indexed target, uint256 indexed scheduledTime);
    event CancelRely(address indexed target);
    event RelyContract(address indexed target, address indexed user);
    event DenyContract(address indexed target, address indexed user);
    event RecoverTokens(address indexed target, address indexed token, address indexed to, uint256 amount);
    event Endorse(address indexed user);
    event Veto(address indexed user);

    /// @notice Returns whether the root is paused
    function paused() external view returns (bool);

    /// @notice Returns the current timelock for adding new wards
    function delay() external view returns (uint256);

    /// @notice Trusted contracts within the system
    function endorsements(address target) external view returns (uint256);

    /// @notice Returns when `relyTarget` has passed the timelock
    function schedule(address relyTarget) external view returns (uint256 timestamp);

    // --- Administration ---
    /// @notice Updates a contract parameter
    /// @param what Accepts a bytes32 representation of 'delay'
    function file(bytes32 what, uint256 data) external;

    /// --- Endorsements ---
    /// @notice Endorses the `user`
    /// @dev    Endorsed users are trusted contracts in the system. They are allowed to bypass
    ///         token restrictions (e.g. the Escrow can automatically receive tranche tokens by being endorsed), and
    ///         can automatically set operators in ERC-7540 vaults (e.g. the CentrifugeRouter) is always an operator.
    function endorse(address user) external;

    /// @notice Removes the endorsed user
    function veto(address user) external;

    /// @notice Returns whether the user is endorsed
    function endorsed(address user) external view returns (bool);

    // --- Pause management ---
    /// @notice Pause any contracts that depend on `Root.paused()`
    function pause() external;

    /// @notice Unpause any contracts that depend on `Root.paused()`
    function unpause() external;

    /// --- Timelocked ward management ---
    /// @notice Schedule relying a new ward after the delay has passed
    function scheduleRely(address target) external;

    /// @notice Cancel a pending scheduled rely
    function cancelRely(address target) external;

    /// @notice Execute a scheduled rely
    /// @dev    Can be triggered by anyone since the scheduling is protected
    function executeScheduledRely(address target) external;

    /// --- Incoming message handling ---
    function handle(bytes calldata message) external;

    /// --- External contract ward management ---
    /// @notice Make an address a ward on any contract that Root is a ward on
    function relyContract(address target, address user) external;

    /// @notice Removes an address as a ward on any contract that Root is a ward on
    function denyContract(address target, address user) external;

    /// --- Token Recovery ---
    /// @notice Allows Governance to recover tokens sent to the wrong contract by mistake
    function recoverTokens(address target, address token, address to, uint256 amount) external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

interface IAuth {
    event Rely(address indexed user);
    event Deny(address indexed user);

    /// @notice Returns whether the target is a ward (has admin access)
    function wards(address target) external view returns (uint256);

    /// @notice Make user a ward (give them admin access)
    function rely(address user) external;

    /// @notice Remove user as a ward (remove admin access)
    function deny(address user) external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

uint8 constant MAX_ADAPTER_COUNT = 8;

interface IGateway {
    /// @dev Each adapter struct is packed with the quorum to reduce SLOADs on handle
    struct Adapter {
        /// @notice Starts at 1 and maps to id - 1 as the index on the adapters array
        uint8 id;
        /// @notice Number of votes required for a message to be executed
        uint8 quorum;
        /// @notice Each time the quorum is decreased, a new session starts which invalidates old votes
        uint64 activeSessionId;
    }

    struct Message {
        /// @dev Counts are stored as integers (instead of boolean values) to accommodate duplicate
        ///      messages (e.g. two investments from the same user with the same amount) being
        ///      processed in parallel. The entire struct is packed in a single bytes32 slot.
        ///      Max uint16 = 65,535 so at most 65,535 duplicate messages can be processed in parallel.
        uint16[MAX_ADAPTER_COUNT] votes;
        /// @notice Each time adapters are updated, a new session starts which invalidates old votes
        uint64 sessionId;
        bytes pendingMessage;
    }

    // --- Events ---
    event ProcessMessage(bytes message, address adapter);
    event ProcessProof(bytes32 messageHash, address adapter);
    event ExecuteMessage(bytes message, address adapter);
    event SendMessage(bytes message);
    event RecoverMessage(address adapter, bytes message);
    event RecoverProof(address adapter, bytes32 messageHash);
    event InitiateMessageRecovery(bytes32 messageHash, address adapter);
    event DisputeMessageRecovery(bytes32 messageHash, address adapter);
    event ExecuteMessageRecovery(bytes message, address adapter);
    event File(bytes32 indexed what, address[] adapters);
    event File(bytes32 indexed what, address instance);
    event File(bytes32 indexed what, uint8 messageId, address manager);
    event File(bytes32 indexed what, address caller, bool isAllowed);
    event ReceiveNativeTokens(address indexed sender, uint256 amount);

    /// @notice Returns the address of the adapter at the given id.
    function adapters(uint256 id) external view returns (address);

    /// @notice Returns the address of the contract that handles the given message id.
    function messageHandlers(uint8 messageId) external view returns (address);

    /// @notice Returns the timestamp when the given recovery can be executed.
    function recoveries(address adapter, bytes32 messageHash) external view returns (uint256 timestamp);

    // --- Administration ---
    /// @notice Used to update an array of addresses ( state variable ) on very rare occasions.
    /// @dev    Currently it is used to update the supported adapters.
    /// @param  what The name of the variable to be updated.
    /// @param  value New addresses.
    function file(bytes32 what, address[] calldata value) external;

    /// @notice Used to update an address ( state variable ) on very rare occasions.
    /// @dev    Currently used to update addresses of contract instances.
    /// @param  what The name of the variable to be updated.
    /// @param  data New address.
    function file(bytes32 what, address data) external;

    /// @notice Used to update a mapping ( state variables ) on very rare occasions.
    /// @dev    Currently used to update any custom handlers for a specific message type.
    ///         data1 is the message id from MessagesLib.Call and data2 could be any
    ///         custom instance of a contract that will handle that call.
    /// @param  what The name of the variable to be updated.
    /// @param  data1 The key of the mapping.
    /// @param  data2 The value of the mapping
    function file(bytes32 what, uint8 data1, address data2) external;

    /// @notice Used to update a mapping ( state variables ) on very rare occasions.
    /// @dev    Manages who is allowed to call `this.topUp`
    ///
    /// @param what The name of the variable to be updated - `payers`
    /// @param caller Address of the payer allowed to top-up
    /// @param isAllower Whether the `caller` is allowed to top-up or not
    function file(bytes32 what, address caller, bool isAllower) external;

    // --- Incoming ---
    /// @notice Handles incoming messages, proofs, and recoveries.
    /// @dev    Assumes adapters ensure messages cannot be confirmed more than once.
    /// @param  payload Incoming message from the Centrifuge Chain passed through adapters.
    function handle(bytes calldata payload) external;

    /// @notice Governance on Centrifuge Chain can initiate message recovery. After the challenge period,
    ///         the recovery can be executed. If a malign adapter initiates message recovery, governance on
    ///         Centrifuge Chain can dispute and immediately cancel the recovery, using any other valid adapter.
    /// @param  adapter Adapter that the recovery was targeting
    /// @param  messageHash Hash of the message being disputed
    function disputeMessageRecovery(address adapter, bytes32 messageHash) external;

    /// @notice Governance on Centrifuge Chain can initiate message recovery. After the challenge period,
    ///         the recovery can be executed. If a malign adapter initiates message recovery, governance on
    ///         Centrifuge Chain can dispute and immediately cancel the recovery, using any other valid adapter.
    ///
    ///         Only 1 recovery can be outstanding per message hash. If multiple adapters fail at the same time,
    ///         these will need to be recovered serially (increasing the challenge period for each failed adapter).
    /// @param  adapter Adapter's address that the recovery is targeting
    /// @param  message Hash of the message to be recovered
    function executeMessageRecovery(address adapter, bytes calldata message) external;

    // --- Outgoing ---
    /// @notice Sends outgoing messages to the Centrifuge Chain.
    /// @dev    Sends 1 message to the first adapter with the full message,
    ///         and n-1 messages to the other adapters with proofs (hash of message).
    ///         This ensures message uniqueness (can only be executed on the destination once).
    ///         Source could be either Centrifuge router or EoA or any contract
    ///         that calls the ERC7540Vault contract directly.
    /// @param  message Message to be send. Either the message itself or a hash value of it ( proof ).
    /// @param  source Entry point of the transaction.
    ///         Used to determine whether it is eligible for TX cost payment.
    function send(bytes calldata message, address source) external payable;

    /// @notice Prepays for the TX cost for sending through the adapters
    ///         and Centrifuge Chain
    /// @dev    It can be called only through endorsed contracts.
    ///         Currently being called from Centrifuge Router only.
    ///         In order to prepay, the method MUST be called with `msg.value`.
    ///         Called is assumed to have called IGateway.estimate before calling this.
    function topUp() external payable;

    // --- Helpers ---
    /// @notice A view method of the current quorum.abi
    /// @dev    Quorum shows the amount of votes needed in order for a message to be dispatched further.
    ///         The quorum is taken from the first adapter.
    ///         Current quorum is the amount of all adapters.
    /// return  Needed amount
    function quorum() external view returns (uint8);

    /// @notice Gets the current active routers session id.
    /// @dev    When the adapters are updated with new ones,
    ///         each new set of adapters has their own sessionId.
    ///         Currently it uses sessionId of the previous set and
    ///         increments it by 1. The idea of an activeSessionId is
    ///         to invalidate any incoming messages from previously used adapters.
    function activeSessionId() external view returns (uint64);

    /// @notice Counts how many times each incoming messages has been received per adapter.
    /// @dev    It supports parallel messages ( duplicates ). That means that the incoming messages could be
    ///         the result of two or more independ request from the user of the same type.
    ///         i.e. Same user would like to deposit same underlying asset with the same amount more then once.
    /// @param  messageHash The hash value of the incoming message.
    function votes(bytes32 messageHash) external view returns (uint16[MAX_ADAPTER_COUNT] memory);

    /// @notice Used to calculate overall cost for bridging a payload on the first adapter and settling
    ///         on the destination chain and bridging its payload proofs on n-1 adapter
    ///         and settling on the destination chain.
    /// @param  payload Used in gas cost calculations.
    /// @dev    Currenly the payload is not taken into consideration.
    /// @return perAdapter An array of cost values per adapter. Each value is how much it's going to cost
    ///         for a message / proof to be passed through one router and executed on Centrifuge Chain
    /// @return total Total cost for sending one message and corresponding proofs on through all adapters
    function estimate(bytes calldata payload) external view returns (uint256[] memory perAdapter, uint256 total);

    /// @notice Used to check current state of the `caller` and whether they are allowed to call
    ///         `this.topUp` or not.
    /// @param  caller Address to check
    /// @return isAllowed Whether the `caller` `isAllowed to call `this.topUp()`
    function payers(address caller) external view returns (bool isAllowed);
}

interface IMessageHandler {
    /// @notice Handling incoming messages from Centrifuge Chain.
    /// @param  message Incoming message
    function handle(bytes memory message) external;
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):