AgglayerGateway

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "@openzeppelin/contracts-upgradeable5/access/AccessControlUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;


    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
    struct AccessControlStorage {
        mapping(bytes32 role => RoleData) _roles;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;

    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
        assembly {
            $.slot := AccessControlStorageLocation
        }
    }

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        AccessControlStorage storage $ = _getAccessControlStorage();
        bytes32 previousAdminRole = getRoleAdmin(role);
        $._roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (!hasRole(role, account)) {
            $._roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (hasRole(role, account)) {
            $._roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
"
    },
    "@openzeppelin/contracts-upgradeable5/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
"
    },
    "@openzeppelin/contracts-upgradeable5/utils/ContextUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
"
    },
    "@openzeppelin/contracts-upgradeable5/utils/introspection/ERC165Upgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165Upgradeable is Initializable, IERC165 {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
"
    },
    "@openzeppelin/contracts/access/IAccessControl.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}
"
    },
    "@openzeppelin/contracts/utils/introspection/IERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
    },
    "contracts/AgglayerGateway.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {ISP1Verifier} from "./interfaces/ISP1Verifier.sol";
import {IAgglayerGateway} from "./interfaces/IAgglayerGateway.sol";
import {IVersion} from "./interfaces/IVersion.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable5/proxy/utils/Initializable.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable5/access/AccessControlUpgradeable.sol";
// Based on https://github.com/succinctlabs/sp1-contracts/blob/main/contracts/src/SP1VerifierGateway.sol

/**
 * @title AgglayerGateway
 * @notice Contract to handle the verification keys for the pessimistic proof.
 * It supports adding and freezing PP verification keys and verifying the PP.
 * Also maintains the default verification keys of aggchains
 */
contract AgglayerGateway is
    Initializable,
    AccessControlUpgradeable,
    IAgglayerGateway,
    IVersion
{
    ////////////////////////////////////////////////////////////
    //                  Constants & Immutables                //
    ////////////////////////////////////////////////////////////
    // Roles
    // Default admin role, can grant roles to addresses
    // @dev value: 0x131410eab1236cee2db19035b0e825c94e5ab705dffe23321dd53856da531617
    bytes32 internal constant AGGCHAIN_DEFAULT_VKEY_ROLE =
        keccak256("AGGCHAIN_DEFAULT_VKEY_ROLE");

    // Can add a route to a pessimistic verification key.
    // @dev value 0x0fdc2a718b96bc741c7544001e3dd7c26730802c54781668fa78a120e622629b
    bytes32 internal constant AL_ADD_PP_ROUTE_ROLE =
        keccak256("AL_ADD_PP_ROUTE_ROLE");

    // Can freeze a route to a pessimistic verification key.
    // @dev value 0xca75ae4228cde6195f9fa3dbde8dc352fb30aa63780717a378ccfc50274355dd
    bytes32 internal constant AL_FREEZE_PP_ROUTE_ROLE =
        keccak256("AL_FREEZE_PP_ROUTE_ROLE");

    // Can manage multisig signers and threshold
    // @dev value 0x0c3038a1ecdf82843b70709289ff1703351ad391e3e27df7f6fa7d913601e15e
    bytes32 internal constant AL_MULTISIG_ROLE = keccak256("AL_MULTISIG_ROLE");

    // Current AgglayerGateway version
    string public constant AGGLAYER_GATEWAY_VERSION = "v1.1.0";

    // Maximum number of aggchain signers supported
    uint256 public constant MAX_AGGCHAIN_SIGNERS = 255;

    ////////////////////////////////////////////////////////////
    //                  Transient Storage                     //
    ////////////////////////////////////////////////////////////

    /// @notice Value to detect if the contract has been initialized previously.
    uint64 private transient _initializerVersion;

    ////////////////////////////////////////////////////////////
    //                       Mappings                         //
    ////////////////////////////////////////////////////////////
    // Mapping with the default aggchain verification keys
    mapping(bytes4 defaultAggchainSelector => bytes32 defaultAggchainVKey)
        public defaultAggchainVKeys;

    // Mapping with the pessimistic verification key routes
    mapping(bytes4 pessimisticVKeySelector => AggLayerVerifierRoute)
        public pessimisticVKeyRoutes;

    ////////////////////////////////////////////////////////////
    //                      Multisig                          //
    ////////////////////////////////////////////////////////////

    /// @notice Array of multisig aggchainSigners
    address[] public aggchainSigners;

    /// @notice Mapping that stores the URL of each signer
    /// It's used as well to check if an address is a signer
    mapping(address => string) public signerToURLs;

    /// @notice Threshold required for multisig operations
    uint256 internal threshold;

    /// @notice Hash of the current aggchainSigners array
    bytes32 public aggchainMultisigHash;

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * Updated to account for new multisig storage variables (4 slots used)
     */
    uint256[46] private __gap;

    ////////////////////////////////////////////////////////////
    //                       Constructor                      //
    ////////////////////////////////////////////////////////////
    /**
     * @dev Disable initializers on the implementation following the best practices.
     */
    constructor() {
        // disable initializers for implementation contract
        _disableInitializers();
    }

    ////////////////////////////////////////////////////////////
    //                        Modifiers                       //
    ////////////////////////////////////////////////////////////

    /// @dev Modifier to retrieve initializer version value previous on using the reinitializer modifier, its used in the initialize function.
    modifier getInitializedVersion() {
        _initializerVersion = _getInitializedVersion();
        _;
    }

    ////////////////////////////////////////////////////////////
    //                  Initialization                        //
    ////////////////////////////////////////////////////////////
    /**
     * @notice  Initializer function to set up the AgglayerGateway contract.
     * @param defaultAdmin The address of the default admin. Can grant role to addresses.
     * @dev This address is the highest privileged address so it's recommended to use a timelock
     * @param aggchainDefaultVKeyRole The address that can manage the aggchain verification keys.
     * @param addRouteRole The address that can add a route to a pessimistic verification key.
     * @param freezeRouteRole The address that can freeze a route to a pessimistic verification key.
     * @param pessimisticVKeySelector The 4 bytes selector to add to the pessimistic verification keys.
     * @param verifier The address of the verifier contract.
     * @param pessimisticVKey New pessimistic program verification key.
     * @param multisigRole The address that can manage multisig signers and threshold.
     * @param signersToAdd Array of signers to add with their URLs
     * @param newThreshold New threshold value
     */
    function initialize(
        address defaultAdmin,
        address aggchainDefaultVKeyRole,
        address addRouteRole,
        address freezeRouteRole,
        bytes4 pessimisticVKeySelector,
        address verifier,
        bytes32 pessimisticVKey,
        address multisigRole,
        SignerInfo[] memory signersToAdd,
        uint256 newThreshold
    ) external getInitializedVersion reinitializer(2) {
        if (_initializerVersion != 0) {
            revert InvalidInitializer();
        }

        if (
            multisigRole == address(0) ||
            defaultAdmin == address(0) ||
            aggchainDefaultVKeyRole == address(0) ||
            addRouteRole == address(0) ||
            freezeRouteRole == address(0)
        ) {
            revert InvalidZeroAddress();
        }

        _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
        _grantRole(AGGCHAIN_DEFAULT_VKEY_ROLE, aggchainDefaultVKeyRole);
        _grantRole(AL_ADD_PP_ROUTE_ROLE, addRouteRole);
        _grantRole(AL_FREEZE_PP_ROUTE_ROLE, freezeRouteRole);
        _grantRole(AL_MULTISIG_ROLE, multisigRole);

        _addPessimisticVKeyRoute(
            pessimisticVKeySelector,
            verifier,
            pessimisticVKey
        );

        // Add the signers to the contract
        _updateSignersAndThreshold(
            new RemoveSignerInfo[](0), // No signers to remove
            signersToAdd,
            newThreshold
        );
    }

    /**
     * @notice Upgrade initializer to add multisig functionality to existing deployment.
     * @param multisigRole The address of the multisig role. Can manage multisig signers and threshold.
     * @param signersToAdd Array of signers to add with their URLs
     * @param newThreshold New threshold value
     */
    function initialize(
        address multisigRole,
        SignerInfo[] memory signersToAdd,
        uint256 newThreshold
    ) external getInitializedVersion reinitializer(2) {
        if (_initializerVersion != 1) {
            revert InvalidInitializer();
        }

        if (multisigRole == address(0)) {
            revert InvalidZeroAddress();
        }

        _grantRole(AL_MULTISIG_ROLE, multisigRole);

        // Add the signers to the contract
        _updateSignersAndThreshold(
            new RemoveSignerInfo[](0), // No signers to remove
            signersToAdd,
            newThreshold
        );
    }

    ////////////////////////////////////////////////////////////
    //        Functions: AgglayerGateway (pessimistic)        //
    ////////////////////////////////////////////////////////////
    /**
     * @notice Function to verify the pessimistic proof.
     * @param publicValues Public values of the proof.
     * @param proofBytes Proof for the pessimistic verification.
     * @dev First 4 bytes of the pessimistic proof are the pp selector.
     * proof[0:4]: 4 bytes selector pp
     * proof[4:8]: 4 bytes selector SP1 verifier
     * proof[8:]: proof
     */
    function verifyPessimisticProof(
        bytes calldata publicValues,
        bytes calldata proofBytes
    ) external view {
        /// @dev By protocol the proof should at least have the 4 bytes selector, the other bytes are not part of our protocol
        if (proofBytes.length < 4) {
            revert InvalidProofBytesLength();
        }

        bytes4 ppSelector = bytes4(proofBytes[:4]);

        AggLayerVerifierRoute memory route = pessimisticVKeyRoutes[ppSelector];
        if (route.verifier == address(0)) {
            revert RouteNotFound(ppSelector);
        } else if (route.frozen) {
            revert RouteIsFrozen(ppSelector);
        }

        ISP1Verifier(route.verifier).verifyProof(
            route.pessimisticVKey,
            publicValues,
            proofBytes[4:]
        );
    }

    /**
     * @notice Internal function to add a pessimistic verification key route
     * @param pessimisticVKeySelector The 4 bytes selector to add to the pessimistic verification keys.
     * @param verifier The address of the verifier contract.
     * @param pessimisticVKey New pessimistic program verification key
     */
    function _addPessimisticVKeyRoute(
        bytes4 pessimisticVKeySelector,
        address verifier,
        bytes32 pessimisticVKey
    ) internal {
        if (verifier == address(0)) {
            revert InvalidZeroAddress();
        }

        if (pessimisticVKeySelector == bytes4(0)) {
            revert PPSelectorCannotBeZero();
        }
        if (pessimisticVKey == bytes32(0)) {
            revert VKeyCannotBeZero();
        }

        AggLayerVerifierRoute storage route = pessimisticVKeyRoutes[
            pessimisticVKeySelector
        ];
        if (route.verifier != address(0)) {
            revert RouteAlreadyExists(pessimisticVKeySelector, route.verifier);
        }

        route.verifier = verifier;
        route.pessimisticVKey = pessimisticVKey;
        emit RouteAdded(pessimisticVKeySelector, verifier, pessimisticVKey);
    }

    /**
     * @notice Function to add a pessimistic verification key route
     * @param pessimisticVKeySelector The 4 bytes selector to add to the pessimistic verification keys.
     * @param verifier The address of the verifier contract.
     * @param pessimisticVKey New pessimistic program verification key
     */
    function addPessimisticVKeyRoute(
        bytes4 pessimisticVKeySelector,
        address verifier,
        bytes32 pessimisticVKey
    ) external onlyRole(AL_ADD_PP_ROUTE_ROLE) {
        _addPessimisticVKeyRoute(
            pessimisticVKeySelector,
            verifier,
            pessimisticVKey
        );
    }

    /**
     * @notice Function to freeze a pessimistic verification key route
     * @param pessimisticVKeySelector The 4 bytes selector to freeze the pessimistic verification key route.
     */
    function freezePessimisticVKeyRoute(
        bytes4 pessimisticVKeySelector
    ) external onlyRole(AL_FREEZE_PP_ROUTE_ROLE) {
        AggLayerVerifierRoute storage route = pessimisticVKeyRoutes[
            pessimisticVKeySelector
        ];
        if (route.verifier == address(0)) {
            revert RouteNotFound(pessimisticVKeySelector);
        }
        if (route.frozen) {
            revert RouteIsAlreadyFrozen(pessimisticVKeySelector);
        }

        route.frozen = true;

        emit RouteFrozen(
            pessimisticVKeySelector,
            route.verifier,
            route.pessimisticVKey
        );
    }

    ////////////////////////////////////////////////////////////
    //            Functions: defaultAggchainVkey              //
    ////////////////////////////////////////////////////////////
    /**
     * @notice Function to add an aggchain verification key
     * @param defaultAggchainSelector The 4 bytes selector to add to the default aggchain verification keys.
     * @dev First 2 bytes of the selector  are the 'verification key identifier', the last 2 bytes are the aggchain type (ex: FEP, ECDSA)
     * @param newAggchainVKey New default aggchain verification key to be added
     */
    function addDefaultAggchainVKey(
        bytes4 defaultAggchainSelector,
        bytes32 newAggchainVKey
    ) external onlyRole(AGGCHAIN_DEFAULT_VKEY_ROLE) {
        // Check already exists
        if (defaultAggchainVKeys[defaultAggchainSelector] != bytes32(0)) {
            revert AggchainVKeyAlreadyExists();
        }

        // Check new key is non-zero
        if (newAggchainVKey == bytes32(0)) {
            revert VKeyCannotBeZero();
        }

        // Add the new VKey to the mapping
        defaultAggchainVKeys[defaultAggchainSelector] = newAggchainVKey;

        emit AddDefaultAggchainVKey(defaultAggchainSelector, newAggchainVKey);
    }

    /**
     * @notice Function to update a default aggchain verification key from the mapping
     * @param defaultAggchainSelector The 4 bytes selector to update the default aggchain verification keys.
     * @param newDefaultAggchainVKey Updated default aggchain verification key value
     */
    function updateDefaultAggchainVKey(
        bytes4 defaultAggchainSelector,
        bytes32 newDefaultAggchainVKey
    ) external onlyRole(AGGCHAIN_DEFAULT_VKEY_ROLE) {
        // Check if the key exists
        if (defaultAggchainVKeys[defaultAggchainSelector] == bytes32(0)) {
            revert AggchainVKeyNotFound();
        }

        // Check new key is non-zero
        if (newDefaultAggchainVKey == bytes32(0)) {
            revert VKeyCannotBeZero();
        }

        // Update the VKey
        bytes32 previousVKey = defaultAggchainVKeys[defaultAggchainSelector];
        defaultAggchainVKeys[defaultAggchainSelector] = newDefaultAggchainVKey;

        emit UpdateDefaultAggchainVKey(
            defaultAggchainSelector,
            previousVKey,
            newDefaultAggchainVKey
        );
    }

    /**
     * @notice Function to unset a default aggchain verification key from the mapping
     * @param defaultAggchainSelector The 4 bytes selector to update the default aggchain verification keys.
     */
    function unsetDefaultAggchainVKey(
        bytes4 defaultAggchainSelector
    ) external onlyRole(AGGCHAIN_DEFAULT_VKEY_ROLE) {
        // Check if the key exists
        if (defaultAggchainVKeys[defaultAggchainSelector] == bytes32(0)) {
            revert AggchainVKeyNotFound();
        }

        // Set key to zero
        defaultAggchainVKeys[defaultAggchainSelector] = bytes32(0);

        emit UnsetDefaultAggchainVKey(defaultAggchainSelector);
    }

    /**
     * @notice function to retrieve the default aggchain verification key.
     * @param defaultAggchainSelector The default aggchain selector for the verification key.
     * @dev First 2 bytes of the selector  are the 'verification key identifier', the last 2 bytes are the aggchain type (ex: FEP, ECDSA)
     */
    function getDefaultAggchainVKey(
        bytes4 defaultAggchainSelector
    ) external view returns (bytes32) {
        if (defaultAggchainVKeys[defaultAggchainSelector] == bytes32(0)) {
            revert AggchainVKeyNotFound();
        }

        return defaultAggchainVKeys[defaultAggchainSelector];
    }

    /**
     * @notice Function to retrieve the current version of the contract.
     * @return version of the contract.
     */
    function version() external pure returns (string memory) {
        return AGGLAYER_GATEWAY_VERSION;
    }

    ////////////////////////////////////////////////////////////
    //                  Multisig Functions                    //
    ////////////////////////////////////////////////////////////

    /**
     * @notice Updates signers and threshold for multisig operations
     * @dev Removes signers first (in descending index order), then adds new signers, then updates threshold
     * @param _signersToRemove Array of signers to remove with their indices (MUST be in descending index order)
     * @param _signersToAdd Array of new signers to add with their URLs
     * @param _newThreshold New threshold value
     */
    function updateSignersAndThreshold(
        RemoveSignerInfo[] memory _signersToRemove,
        SignerInfo[] memory _signersToAdd,
        uint256 _newThreshold
    ) external onlyRole(AL_MULTISIG_ROLE) {
        _updateSignersAndThreshold(
            _signersToRemove,
            _signersToAdd,
            _newThreshold
        );
    }

    /**
     * @notice Batch update signers and threshold in a single transaction
     * @dev Internal function that handles the actual logic
     * @param _signersToRemove Array of signers to remove with their indices (MUST be in descending index order)
     * @param _signersToAdd Array of new signers to add with their URLs
     * @param _newThreshold New threshold value
     */
    function _updateSignersAndThreshold(
        RemoveSignerInfo[] memory _signersToRemove,
        SignerInfo[] memory _signersToAdd,
        uint256 _newThreshold
    ) internal {
        // Validate descending order of indices for removal to avoid index shifting issues
        // When removing multiple signers, we must process them from highest index to lowest
        if (_signersToRemove.length > 1) {
            for (uint256 i = 0; i < _signersToRemove.length - 1; i++) {
                if (
                    _signersToRemove[i].index <= _signersToRemove[i + 1].index
                ) {
                    revert IndicesNotInDescendingOrder();
                }
            }
        }

        // Remove signers (in descending index order to avoid index shifting issues)
        for (uint256 i = 0; i < _signersToRemove.length; i++) {
            _removeSignerInternal(
                _signersToRemove[i].addr,
                _signersToRemove[i].index
            );
        }

        // Add new signers
        for (uint256 i = 0; i < _signersToAdd.length; i++) {
            _addSignerInternal(_signersToAdd[i].addr, _signersToAdd[i].url);
        }

        if (aggchainSigners.length > MAX_AGGCHAIN_SIGNERS) {
            revert AggchainSignersTooHigh();
        }

        if (
            _newThreshold > aggchainSigners.length ||
            (aggchainSigners.length != 0 && _newThreshold == 0)
        ) {
            revert InvalidThreshold();
        }

        threshold = _newThreshold;

        // Update the signers hash once after all operations
        _updateAggchainMultisigHash();
    }

    /**
     * @notice Internal function to add a signer with validation
     * @param _signer Address of the signer to add
     * @param url URL associated with the signer
     */
    function _addSignerInternal(address _signer, string memory url) internal {
        if (_signer == address(0)) {
            revert SignerCannotBeZero();
        }

        if (bytes(url).length == 0) {
            revert SignerURLCannotBeEmpty();
        }

        if (isSigner(_signer)) {
            revert SignerAlreadyExists();
        }

        aggchainSigners.push(_signer);
        signerToURLs[_signer] = url;
    }

    /**
     * @notice Internal function to remove a signer with validation
     * @param _signer Address of the signer to remove
     * @param _signerIndex Index of the signer in the aggchainSigners array
     */
    function _removeSignerInternal(
        address _signer,
        uint256 _signerIndex
    ) internal {
        // Cache array length
        uint256 signersLength = aggchainSigners.length;

        // Validate input parameters
        if (_signerIndex >= signersLength) {
            revert SignerDoesNotExist();
        }

        if (aggchainSigners[_signerIndex] != _signer) {
            revert SignerDoesNotExist();
        }

        // Remove from mapping
        delete signerToURLs[_signer];

        // Move the last element to the deleted spot and remove the last element
        aggchainSigners[_signerIndex] = aggchainSigners[signersLength - 1];

        aggchainSigners.pop();
    }

    /**
     * @notice Update the hash of the aggchainSigners array
     * @dev Combines threshold and signers array into a single hash for efficient verification
     */
    function _updateAggchainMultisigHash() internal {
        aggchainMultisigHash = keccak256(
            abi.encodePacked(threshold, aggchainSigners)
        );

        emit SignersAndThresholdUpdated(
            aggchainSigners,
            threshold,
            aggchainMultisigHash
        );
    }

    /**
     * @notice Get the threshold for the multisig
     * @return threshold for the multisig
     */
    function getThreshold() external view returns (uint256) {
        return threshold;
    }

    /**
     * @notice Check if an address is a signer
     * @param _signer Address to check
     * @return True if the address is a signer
     */
    function isSigner(address _signer) public view returns (bool) {
        return bytes(signerToURLs[_signer]).length > 0;
    }

    /**
     * @notice Get the number of aggchainSigners
     * @return Number of aggchainSigners in the multisig
     */
    function getAggchainSignersCount() external view returns (uint256) {
        return aggchainSigners.length;
    }

    /**
     * @notice Get all aggchainSigners
     * @return Array of signer addresses
     */
    function getAggchainSigners() external view returns (address[] memory) {
        return aggchainSigners;
    }

    /**
     * @notice Returns the aggchain signers hash for verification
     * @dev Used by aggchain contracts to include in their hash computation
     * @return The current aggchainMultisigHash
     */
    function getAggchainMultisigHash() external view returns (bytes32) {
        // Sanity check to realize earlier that the aggchainMultisigHash has not been set given
        // that the proof cannot be computed since there is no hash reconstruction to be 0
        if (aggchainMultisigHash == bytes32(0)) {
            revert AggchainSignersHashNotInitialized();
        }
        return aggchainMultisigHash;
    }

    /**
     * @notice Get all aggchainSigners with their URLs
     * @return Array of SignerInfo structs containing signer addresses and URLs
     */
    function getAggchainSignerInfos()
        external
        view
        returns (SignerInfo[] memory)
    {
        SignerInfo[] memory signerInfos = new SignerInfo[](
            aggchainSigners.length
        );
        for (uint256 i = 0; i < aggchainSigners.length; i++) {
            signerInfos[i] = SignerInfo({
                addr: aggchainSigners[i],
                url: signerToURLs[aggchainSigners[i]]
            });
        }
        return signerInfos;
    }
}
"
    },
    "contracts/interfaces/IAggchainSigners.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;

/**
 * @title IAggchainSigners
 * @notice Interface for multisig signer management functionality
 * @dev This interface is implemented by both AggchainBase contracts and AgglayerGateway,
 *      providing a unified way to manage signers for consensus verification.
 *      Implementations may use local storage or delegate to a gateway contract.
 */
interface IAggchainSigners {
    ////////////////////////////////////////////////////////////
    //                       Structs                          //
    ////////////////////////////////////////////////////////////

    /**
     * @notice Struct to hold signer information
     * @param addr The address of the signer
     * @param url The URL associated with the signer
     */
    struct SignerInfo {
        address addr;
        string url;
    }

    /**
     * @notice Struct to hold information for removing a signer
     * @param addr The address of the signer to remove
     * @param index The index of the signer in the aggchainSigners array
     */
    struct RemoveSignerInfo {
        address addr;
        uint256 index;
    }

    /**
     * @notice Emitted when signers and threshold are updated in a batch operation.
     * @param aggchainSigners The updated array of signer addresses.
     * @param newThreshold The new threshold value.
     * @param newAggchainMultisigHash The new hash of the aggchainMultisig configuration.
     */
    event SignersAndThresholdUpdated(
        address[] aggchainSigners,
        uint256 newThreshold,
        bytes32 newAggchainMultisigHash
    );

    ////////////////////////////////////////////////////////////
    //                    View Functions                      //
    ////////////////////////////////////////////////////////////

    /**
     * @notice Check if an address is a signer
     * @param _signer Address to check
     * @return True if the address is a signer
     */
    function isSigner(address _signer) external view returns (bool);

    /**
     * @notice Get the minimum number of signatures required for consensus
     * @dev Returns the threshold value for multisig validation
     * @return threshold Minimum number of signatures required
     */
    function getThreshold() external view returns (uint256);

    /**
     * @notice Get the total number of registered signers
     * @dev Returns the count of active signers in the multisig
     * @return count Total number of aggchainSigners currently registered
     */
    function getAggchainSignersCount() external view returns (uint256);

    /**
     * @notice Get all registered signer addresses
     * @dev Returns the complete list of active signers
     * @return signers Array containing all signer addresses
     */
    function getAggchainSigners() external view returns (address[] memory);

    /**
     * @notice Returns the hash of current multisig configuration
     * @dev Computed as keccak256(abi.encodePacked(threshold, aggchainSigners)).
     *      Used by aggchain contracts for efficient consensus verification.
     * @return multisigHash The current aggchainMultisigHash for validation
     */
    function getAggchainMultisigHash() external view returns (bytes32);

    /**
     * @notice Get detailed information for all registered signers
     * @dev Returns both addresses and associated URLs/endpoints for each signer
     * @return signerInfos Array of SignerInfo structs containing complete signer details
     */
    function getAggchainSignerInfos()
        external
        view
        returns (SignerInfo[] memory);
}
"
    },
    "contracts/interfaces/IAgglayerGateway.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import "./IAggchainSigners.sol";

// based on: https://github.com/succinctlabs/sp1-contracts/blob/main/contracts/src/ISP1VerifierGateway.sol

interface IAgglayerGatewayEvents {
    /// @notice Emitted when a verifier route is added.
    /// @param selector The verifier selector that was added.
    /// @param verifier The address of the verifier contract.
    /// @param pessimisticVKey The verification key
    event RouteAdded(
        bytes4 selector,
        address verifier,
        bytes32 pessimisticVKey
    );

    /// @notice Emitted when a verifier route is frozen.
    /// @param selector The verifier selector that was frozen.
    /// @param verifier The address of the verifier contract.
    event RouteFrozen(
        bytes4 selector,
        address verifier,
        bytes32 pessimisticVKey
    );

    /**
     * Emitted when a new default aggchain verification key is added
     * @param selector The 4 bytes selector of the added default aggchain verification key.
     * @param newVKey New aggchain verification key to be added
     */
    event AddDefaultAggchainVKey(bytes4 selector, bytes32 newVKey);

    /**
     * Emitted when a default aggchain verification key is update
     * @param selector The 4 bytes selector of the updated default aggchain verification key.
     * @param previousVKey Aggchain verification key previous value
     * @param newVKey Aggchain verification key updated value
     */
    event UpdateDefaultAggchainVKey(
        bytes4 selector,
        bytes32 previousVKey,
        bytes32 newVKey
    );

    /**
     * Emitted when a default aggchain verification key is set to zero
     * @param selector The 4 bytes selector of the updated default aggchain verification key.
     */
    event UnsetDefaultAggchainVKey(bytes4 selector);
}

/// @dev Extended error events from https://github.com/succinctlabs/sp1-contracts/blob/main/contracts/src/ISP1VerifierGateway.sol
interface IAgglayerGatewayErrors {
    /// @notice Thrown when the verifier route is not found.
    /// @param selector The verifier selector that was specified.
    error RouteNotFound(bytes4 selector);

    /// @notice Thrown when the verifier route is found, but is frozen.
    /// @param selector The verifier selector that was specified.
    error RouteIsFrozen(bytes4 selector);

    /// @notice Thrown when trying to freeze a route that is already frozen.
    /// @param selector The pessimistic verification key selector that was specified.
    error RouteIsAlreadyFrozen(bytes4 selector);

    /// @notice Thrown when adding a verifier route and the selector already contains a route.
    /// @param selector The pessimistic verification key selector that was specified.
    /// @param verifier The address of the verifier contract in the existing route.
    error RouteAlreadyExists(bytes4 selector, address verifier);

    /// @notice Thrown when adding a verifier route and the selector returned by the verifier is
    /// zero.
    error PPSelectorCannotBeZero();

    /// @notice Thrown when adding a verifier key with value zero
    error VKeyCannotBeZero();

    /// @notice Thrown when the caller is not the AggLayerAdmin
    error OnlyAggLayerAdmin();

    //// @notice Thrown when the caller is not the pending AggLayerAdmin
    error OnlyPendingAggLayerAdmin();

    /// @notice Thrown when trying to add an aggchain verification key that already exists
    error AggchainVKeyAlreadyExists();

    /// @notice Thrown when trying to retrieve an aggchain verification key from the mapping that doesn't exists
    error AggchainVKeyNotFound();

    /// @notice Thrown when trying to call a function with an input zero address
    error InvalidZeroAddress();

    /// @notice Thrown when trying to call a function with an invalid initializer version
    error InvalidInitializer();

    /// @notice Thrown when the input proof bytes are invalid.
    error InvalidProofBytesLength();

    /// @notice Thrown when the aggchain signers hash has not been initialized
    error AggchainSignersHashNotInitialized();

    /// @notice Thrown when indices for signer removal are not in descending order
    error IndicesNotInDescendingOrder();

    /// @notice Thrown when trying to set more than 255 signers
    error AggchainSignersTooHigh();

    /// @notice Thrown when the threshold exceeds the number of signers
    error InvalidThreshold();

    /// @notice Thrown when trying to add a zero address as signer
    error SignerCannotBeZero();

    /// @notice Thrown when trying to add a signer with empty URL
    error SignerURLCannotBeEmpty();

    /// @notice Thrown when trying to add a signer that already exists
    error SignerAlreadyExists();

    /// @notice Thrown when trying to remove a signer that doesn't exist
    error SignerDoesNotExist();
}

/// @title IAgglayerGateway
/// @notice This contract is the interface for the AgglayerGateway.
/// @notice Based on https://github.com/succinctlabs/sp1-contracts/blob/main/contracts/src/ISP1VerifierGateway.sol
interface IAgglayerGateway is
    IAgglayerGatewayEvents,
    IAgglayerGatewayErrors,
    IAggchainSigners
{
    /**
     * Struct that defines a verifier route
     * @param verifier The address of the verifier contract.
     * @param pessimisticVKey The verification key to be used for verifying pessimistic proofs.
     * @param frozen Whether the route is frozen.
     */
    struct AggLayerVerifierRoute {
        address verifier; // SP1 Verifier. It contains sanity check SP1 version with the 4 first bytes of the proof. proof[4:]
        bytes32 pessimisticVKey;
        bool frozen;
    }

    /**
     * @notice returns the current aggchain verification key, used to verify chain's FEP.
     * @dev This function is necessary to query the map from an external function. In solidity maps are not
     * directly accessible from external functions like other state variables.
     */
    function getDefaultAggchainVKey(
        bytes4 defaultAggchainSelector
    ) external view returns (bytes32);

    /// @notice Verifies a pessimistic proof with given public values and proof.
    /// @dev It is expected that the first 4 bytes of proofBytes must match the first 4 bytes of
    /// target verifier's VERIFIER_HASH.
    /// @param publicValues The public values encoded as bytes.
    /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes.
    function verifyPessimisticProof(
        bytes calldata publicValues,
        bytes calldata proofBytes
    ) external view;

    /// @notice Adds a verifier route. This enable proofs to be routed to this verifier.
    /// @dev Only callable by the owner. The owner is responsible for ensuring that the specified
    /// verifier is correct with a valid VERIFIER_HASH. Once a route to a verifier is added, it
    /// cannot be removed.
    /// @param pessimisticVKeySelector The verifier selector to add.
    /// @param verifier The address of the verifier contract. This verifier MUST implement the
    /// ISP1VerifierWithHash interface.
    /// @param pessimisticVKey The verification key to be used for verifying pessimistic proofs.
    function addPessimisticVKeyRoute(
        bytes4 pessimisticVKeySelector,
        address verifier,
        bytes32 pessimisticVKey
    ) external;

    /// @notice Freezes a verifier route. This prevents proofs from being routed to this verifier.
    /// @dev Only callable by the owner. Once a route to a verifier is frozen, it cannot be
    /// unfrozen.
    /// @param pessimisticVKeySelector The verifier selector to freeze.
    function freezePessimisticVKeyRoute(
        bytes4 pessimisticVKeySelector
    ) external;

    ////////////////////////////////////////////////////////////
    //                  Multisig Functions                    //
    ////////////////////////////////////////////////////////////

    /**
     * @notice Updates signers and threshold for multisig operations
     * @dev Removes signers first (in descending index order), then adds new signers, then updates threshold
     * @param _signersToRemove Array of signers to remove with their indices (MUST be in descending index order)
     * @param _signersToAdd Array of new signers to add with their URLs
     * @param _newThreshold New threshold value
     */
    function updateSignersAndThreshold(
        RemoveSignerInfo[] memory _signersToRemove,
        SignerInfo[] memory _signersToAdd,
        uint256 _newThreshold
    ) external;
}
"
    },
    "contracts/interfaces/ISP1Verifier.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

// imported from: https://github.com/succinctlabs/sp1-contracts/blob/main/contracts/src/ISP1Verifier.sol

/// @title SP1 Verifier Interface
/// @author Succinct Labs
/// @notice This contract is the interface for the SP1 Verifier.
interface ISP1Verifier {
    /// @notice Verifies a proof with given public values and vkey.
    /// @dev It is expected that the first 4 bytes of proofBytes must match the first 4 bytes of
    /// target verifier's VERIFIER_HASH.
    /// @param programVKey The verification key for the RISC-V program.
    /// @param publicValues The public values encoded as bytes.
    /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes.
    function verifyProof(
        bytes32 programVKey,
        bytes calldata publicValues,
        bytes calldata proofBytes
    ) external view;
}

interface ISP1VerifierWithHash is ISP1Verifier {
    /// @notice Returns the hash of the verifier.
    function VERIFIER_HASH() external pure returns (bytes32);
}
"
    },
    "contracts/interfaces/IVersion.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

interface IVersion {
    function version() external pure returns (string memory);
}
"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 999999
    },
    "evmVersion": "cancun",
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    }
  }
}}

Tags:
ERC165, Multisig, Upgradeable, Multi-Signature, Factory|addr:0xd062b7f9fbb89bda59262e77015c34a27dc9ab49|verified:true|block:23497969|tx:0x6d75087cdc038ff6e782240d1925cc38130d2f19338f0a95d8626134ea31a282|first_check:1759506771

Submitted on: 2025-10-03 17:52:53

Comments

Log in to comment.

No comments yet.