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": {
"src/contracts/multichain/CrossChainRegistry.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import "../mixins/PermissionControllerMixin.sol";
import "../mixins/SemVerMixin.sol";
import "../permissions/Pausable.sol";
import "../interfaces/IKeyRegistrar.sol";
import "./CrossChainRegistryStorage.sol";
/**
* @title CrossChainRegistry
* @author Layr Labs, Inc.
* @notice Implementation contract for managing cross-chain operator set configurations and generation reservations
* @dev Manages operator table calculations, transport destinations, and operator set configurations for cross-chain operations
*/
contract CrossChainRegistry is
Initializable,
OwnableUpgradeable,
Pausable,
CrossChainRegistryStorage,
PermissionControllerMixin,
SemVerMixin
{
using EnumerableMap for EnumerableMap.UintToAddressMap;
using EnumerableSet for EnumerableSet.Bytes32Set;
using EnumerableSet for EnumerableSet.UintSet;
using OperatorSetLib for OperatorSet;
/**
*
* MODIFIERS
*
*/
/**
* @dev Validates that the operator set exists in the AllocationManager
* @param operatorSet The operator set to validate
*/
modifier isValidOperatorSet(
OperatorSet calldata operatorSet
) {
require(allocationManager.isOperatorSet(operatorSet), InvalidOperatorSet());
_;
}
modifier checkHasActiveGenerationReservation(
OperatorSet calldata operatorSet
) {
require(hasActiveGenerationReservation(operatorSet), GenerationReservationDoesNotExist());
_;
}
/**
*
* INITIALIZING FUNCTIONS
*
*/
/**
* @dev Initializes the CrossChainRegistry with immutable dependencies
* @param _allocationManager The allocation manager for operator set validation
* @param _keyRegistrar The key registrar for operator set curve type validation
* @param _permissionController The permission controller for access control
* @param _pauserRegistry The pauser registry for pause functionality
* @param _version The semantic version of the contract
*/
constructor(
IAllocationManager _allocationManager,
IKeyRegistrar _keyRegistrar,
IPermissionController _permissionController,
IPauserRegistry _pauserRegistry,
string memory _version
)
CrossChainRegistryStorage(_allocationManager, _keyRegistrar)
PermissionControllerMixin(_permissionController)
Pausable(_pauserRegistry)
SemVerMixin(_version)
{
_disableInitializers();
}
/**
* @notice Initializes the contract with the initial paused status and owner
* @param initialOwner The initial owner of the contract
* @param initialTableUpdateCadence The initial table update cadence
* @param initialPausedStatus The initial paused status bitmap
*/
function initialize(
address initialOwner,
uint32 initialTableUpdateCadence,
uint256 initialPausedStatus
) external initializer {
_transferOwnership(initialOwner);
_setTableUpdateCadence(initialTableUpdateCadence);
_setPausedStatus(initialPausedStatus);
}
/**
*
* EXTERNAL FUNCTIONS
*
*/
/// @inheritdoc ICrossChainRegistry
function createGenerationReservation(
OperatorSet calldata operatorSet,
IOperatorTableCalculator operatorTableCalculator,
OperatorSetConfig calldata config
)
external
onlyWhenNotPaused(PAUSED_GENERATION_RESERVATIONS)
checkCanCall(operatorSet.avs)
isValidOperatorSet(operatorSet)
{
// Validate the the KeyType has been set in the KeyRegistrar for the OperatorSet
require(keyRegistrar.getOperatorSetCurveType(operatorSet) != IKeyRegistrarTypes.CurveType.NONE, KeyTypeNotSet());
// Add to active generation reservations
require(_activeGenerationReservations.add(operatorSet.key()), GenerationReservationAlreadyExists());
emit GenerationReservationCreated(operatorSet);
// Set the operator table calculator
_setOperatorTableCalculator(operatorSet, operatorTableCalculator);
// Set the operator set config
_setOperatorSetConfig(operatorSet, config);
}
/// @inheritdoc ICrossChainRegistry
function removeGenerationReservation(
OperatorSet calldata operatorSet
)
external
onlyWhenNotPaused(PAUSED_GENERATION_RESERVATIONS)
checkCanCall(operatorSet.avs)
isValidOperatorSet(operatorSet)
checkHasActiveGenerationReservation(operatorSet)
{
bytes32 operatorSetKey = operatorSet.key();
// Clear all storage for the operator set
// 1. Remove the operator table calculator
delete _operatorTableCalculators[operatorSetKey];
emit OperatorTableCalculatorRemoved(operatorSet);
// 2. Remove the operator set config
delete _operatorSetConfigs[operatorSetKey];
emit OperatorSetConfigRemoved(operatorSet);
// 3. Remove from active generation reservations
_activeGenerationReservations.remove(operatorSetKey);
emit GenerationReservationRemoved(operatorSet);
}
/// @inheritdoc ICrossChainRegistry
function setOperatorTableCalculator(
OperatorSet calldata operatorSet,
IOperatorTableCalculator operatorTableCalculator
)
external
onlyWhenNotPaused(PAUSED_OPERATOR_TABLE_CALCULATOR)
checkCanCall(operatorSet.avs)
isValidOperatorSet(operatorSet)
checkHasActiveGenerationReservation(operatorSet)
{
// Set the operator table calculator
_setOperatorTableCalculator(operatorSet, operatorTableCalculator);
}
/// @inheritdoc ICrossChainRegistry
function setOperatorSetConfig(
OperatorSet calldata operatorSet,
OperatorSetConfig calldata config
)
external
onlyWhenNotPaused(PAUSED_OPERATOR_SET_CONFIG)
checkCanCall(operatorSet.avs)
isValidOperatorSet(operatorSet)
checkHasActiveGenerationReservation(operatorSet)
{
// Set the operator set config
_setOperatorSetConfig(operatorSet, config);
}
/// @inheritdoc ICrossChainRegistry
function addChainIDsToWhitelist(
uint256[] calldata chainIDs,
address[] calldata operatorTableUpdaters
) external onlyOwner onlyWhenNotPaused(PAUSED_CHAIN_WHITELIST) {
require(chainIDs.length == operatorTableUpdaters.length, ArrayLengthMismatch());
for (uint256 i = 0; i < chainIDs.length; i++) {
uint256 chainID = chainIDs[i];
// Validate chainID
require(chainID != 0, InvalidChainId());
// Add to whitelist
require(_whitelistedChainIDs.set(chainID, operatorTableUpdaters[i]), ChainIDAlreadyWhitelisted());
emit ChainIDAddedToWhitelist(chainID, operatorTableUpdaters[i]);
}
}
/// @inheritdoc ICrossChainRegistry
function removeChainIDsFromWhitelist(
uint256[] calldata chainIDs
) external onlyOwner onlyWhenNotPaused(PAUSED_CHAIN_WHITELIST) {
for (uint256 i = 0; i < chainIDs.length; i++) {
uint256 chainID = chainIDs[i];
// Remove from whitelist
require(_whitelistedChainIDs.remove(chainID), ChainIDNotWhitelisted());
emit ChainIDRemovedFromWhitelist(chainID);
}
}
/// @inheritdoc ICrossChainRegistry
function setTableUpdateCadence(
uint32 tableUpdateCadence
) external onlyOwner {
_setTableUpdateCadence(tableUpdateCadence);
}
/**
*
* INTERNAL FUNCTIONS
*
*/
/**
* @dev Internal function to set the operator table calculator for an operator set
* @param operatorSet The operator set to set the calculator for
* @param operatorTableCalculator The operator table calculator contract
*/
function _setOperatorTableCalculator(
OperatorSet memory operatorSet,
IOperatorTableCalculator operatorTableCalculator
) internal {
_operatorTableCalculators[operatorSet.key()] = operatorTableCalculator;
emit OperatorTableCalculatorSet(operatorSet, operatorTableCalculator);
}
/**
* @dev Internal function to set the operator set config for an operator set
* @param operatorSet The operator set to set the config for
* @param config The operator set config
* @dev The 0 staleness period is special case and is allowed, since it allows for certificates to ALWAYS be valid
*/
function _setOperatorSetConfig(OperatorSet memory operatorSet, OperatorSetConfig memory config) internal {
require(
config.maxStalenessPeriod == 0 || config.maxStalenessPeriod >= _tableUpdateCadence, InvalidStalenessPeriod()
);
_operatorSetConfigs[operatorSet.key()] = config;
emit OperatorSetConfigSet(operatorSet, config);
}
/**
* @dev Internal function to set the table update cadence
* @param tableUpdateCadence the table update cadence
* @dev The table update cadence cannot be 0 as that is special-cased to allow for certificates to ALWAYS be valid
*/
function _setTableUpdateCadence(
uint32 tableUpdateCadence
) internal {
require(tableUpdateCadence > 0, InvalidTableUpdateCadence());
_tableUpdateCadence = tableUpdateCadence;
emit TableUpdateCadenceSet(tableUpdateCadence);
}
/**
*
* VIEW FUNCTIONS
*
*/
/// @inheritdoc ICrossChainRegistry
function getActiveGenerationReservations() external view returns (OperatorSet[] memory) {
uint256 length = _activeGenerationReservations.length();
OperatorSet[] memory operatorSets = new OperatorSet[](length);
for (uint256 i = 0; i < length; i++) {
bytes32 operatorSetKey = _activeGenerationReservations.at(i);
OperatorSet memory operatorSet = OperatorSetLib.decode(operatorSetKey);
operatorSets[i] = operatorSet;
}
return operatorSets;
}
/// @inheritdoc ICrossChainRegistry
function hasActiveGenerationReservation(
OperatorSet memory operatorSet
) public view returns (bool) {
return _activeGenerationReservations.contains(operatorSet.key());
}
/// @inheritdoc ICrossChainRegistry
function getActiveGenerationReservationsByRange(
uint256 startIndex,
uint256 endIndex
) external view returns (OperatorSet[] memory) {
require(startIndex <= endIndex, InvalidRange());
require(endIndex <= _activeGenerationReservations.length(), InvalidEndIndex());
uint256 length = endIndex - startIndex;
OperatorSet[] memory operatorSets = new OperatorSet[](length);
for (uint256 i = 0; i < length; i++) {
bytes32 operatorSetKey = _activeGenerationReservations.at(startIndex + i);
OperatorSet memory operatorSet = OperatorSetLib.decode(operatorSetKey);
operatorSets[i] = operatorSet;
}
return operatorSets;
}
/// @inheritdoc ICrossChainRegistry
function getOperatorTableCalculator(
OperatorSet memory operatorSet
) public view returns (IOperatorTableCalculator) {
return _operatorTableCalculators[operatorSet.key()];
}
/// @inheritdoc ICrossChainRegistry
function getOperatorSetConfig(
OperatorSet memory operatorSet
) public view returns (OperatorSetConfig memory) {
return _operatorSetConfigs[operatorSet.key()];
}
/// @inheritdoc ICrossChainRegistry
function calculateOperatorTableBytes(
OperatorSet calldata operatorSet
) external view returns (bytes memory) {
return abi.encode(
operatorSet,
keyRegistrar.getOperatorSetCurveType(operatorSet),
getOperatorSetConfig(operatorSet),
getOperatorTableCalculator(operatorSet).calculateOperatorTableBytes(operatorSet)
);
}
/// @inheritdoc ICrossChainRegistry
function getSupportedChains() external view returns (uint256[] memory, address[] memory) {
uint256 length = _whitelistedChainIDs.length();
uint256[] memory chainIDs = new uint256[](length);
address[] memory operatorTableUpdaters = new address[](length);
for (uint256 i = 0; i < length; i++) {
(uint256 chainID, address operatorTableUpdater) = _whitelistedChainIDs.at(i);
chainIDs[i] = chainID;
operatorTableUpdaters[i] = operatorTableUpdater;
}
return (chainIDs, operatorTableUpdaters);
}
/// @inheritdoc ICrossChainRegistry
function getTableUpdateCadence() external view returns (uint32) {
return _tableUpdateCadence;
}
/// @inheritdoc ICrossChainRegistry
function getActiveGenerationReservationCount() external view returns (uint256) {
return _activeGenerationReservations.length();
}
}
"
},
"lib/openzeppelin-contracts-upgradeable-v4.9.0/contracts/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @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 Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 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 functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_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 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_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() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @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 {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
"
},
"lib/openzeppelin-contracts-upgradeable-v4.9.0/contracts/access/OwnableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @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.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
"
},
"src/contracts/mixins/PermissionControllerMixin.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import "../interfaces/IPermissionController.sol";
abstract contract PermissionControllerMixin {
/// @dev Thrown when the caller is not allowed to call a function on behalf of an account.
error InvalidPermissions();
/// @notice Pointer to the permission controller contract.
IPermissionController public immutable permissionController;
constructor(
IPermissionController _permissionController
) {
permissionController = _permissionController;
}
/// @notice Checks if the caller (msg.sender) can call on behalf of an account.
modifier checkCanCall(
address account
) {
require(_checkCanCall(account), InvalidPermissions());
_;
}
/**
* @notice Checks if the caller is allowed to call a function on behalf of an account.
* @param account the account to check
* @dev `msg.sender` is the caller to check that can call the function on behalf of `account`.
* @dev Returns a bool, instead of reverting
*/
function _checkCanCall(
address account
) internal returns (bool) {
return permissionController.canCall(account, msg.sender, address(this), msg.sig);
}
}
"
},
"src/contracts/mixins/SemVerMixin.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import "../interfaces/ISemVerMixin.sol";
import "@openzeppelin-upgrades/contracts/utils/ShortStringsUpgradeable.sol";
/// @title SemVerMixin
/// @notice A mixin contract that provides semantic versioning functionality.
/// @dev Follows SemVer 2.0.0 specification (https://semver.org/).
abstract contract SemVerMixin is ISemVerMixin {
using ShortStringsUpgradeable for *;
/// @notice The semantic version string for this contract, stored as a ShortString for gas efficiency.
/// @dev Follows SemVer 2.0.0 specification (https://semver.org/).
ShortString internal immutable _VERSION;
/// @notice Initializes the contract with a semantic version string.
/// @param _version The SemVer-formatted version string (e.g., "1.2.3")
/// @dev Version should follow SemVer 2.0.0 format: MAJOR.MINOR.PATCH
constructor(
string memory _version
) {
_VERSION = _version.toShortString();
}
/// @inheritdoc ISemVerMixin
function version() public view virtual returns (string memory) {
return _VERSION.toString();
}
/// @notice Returns the major version of the contract.
/// @dev Supports single digit major versions (e.g., "1" for version "1.2.3")
/// @return The major version string (e.g., "1" for version "1.2.3")
function _majorVersion() internal view returns (string memory) {
bytes memory v = bytes(_VERSION.toString());
return string(abi.encodePacked(v[0]));
}
}
"
},
"src/contracts/permissions/Pausable.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import "../interfaces/IPausable.sol";
/**
* @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions.
* These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control.
* @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality.
* Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code.
* For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause,
* you can only flip (any number of) switches to off/0 (aka "paused").
* If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will:
* 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256)
* 2) update the paused state to this new value
* @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3`
* indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused
*/
abstract contract Pausable is IPausable {
/// Constants
uint256 internal constant _UNPAUSE_ALL = 0;
uint256 internal constant _PAUSE_ALL = type(uint256).max;
/// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
IPauserRegistry public immutable pauserRegistry;
/// Storage
/// @dev Do not remove, deprecated storage.
IPauserRegistry private __deprecated_pauserRegistry;
/// @dev Returns a bitmap representing the paused status of the contract.
uint256 private _paused;
/// Modifiers
/// @dev Thrown if the caller is not a valid pauser according to the pauser registry.
modifier onlyPauser() {
_checkOnlyPauser();
_;
}
/// @dev Thrown if the caller is not a valid unpauser according to the pauser registry.
modifier onlyUnpauser() {
_checkOnlyUnpauser();
_;
}
/// @dev Thrown if the contract is paused, i.e. if any of the bits in `_paused` is flipped to 1.
modifier whenNotPaused() {
_checkOnlyWhenNotPaused();
_;
}
/// @dev Thrown if the `indexed`th bit of `_paused` is 1, i.e. if the `index`th pause switch is flipped.
modifier onlyWhenNotPaused(
uint8 index
) {
_checkOnlyWhenNotPaused(index);
_;
}
function _checkOnlyPauser() internal view {
require(pauserRegistry.isPauser(msg.sender), OnlyPauser());
}
function _checkOnlyUnpauser() internal view {
require(msg.sender == pauserRegistry.unpauser(), OnlyUnpauser());
}
function _checkOnlyWhenNotPaused() internal view {
require(_paused == 0, CurrentlyPaused());
}
function _checkOnlyWhenNotPaused(
uint8 index
) internal view {
require(!paused(index), CurrentlyPaused());
}
/// Construction
constructor(
IPauserRegistry _pauserRegistry
) {
require(address(_pauserRegistry) != address(0), InputAddressZero());
pauserRegistry = _pauserRegistry;
}
/// @inheritdoc IPausable
function pause(
uint256 newPausedStatus
) external onlyPauser {
uint256 currentPausedStatus = _paused;
// verify that the `newPausedStatus` does not *unflip* any bits (i.e. doesn't unpause anything, all 1 bits remain)
require((currentPausedStatus & newPausedStatus) == currentPausedStatus, InvalidNewPausedStatus());
_setPausedStatus(newPausedStatus);
}
/// @inheritdoc IPausable
function pauseAll() external onlyPauser {
_setPausedStatus(_PAUSE_ALL);
}
/// @inheritdoc IPausable
function unpause(
uint256 newPausedStatus
) external onlyUnpauser {
uint256 currentPausedStatus = _paused;
// verify that the `newPausedStatus` does not *flip* any bits (i.e. doesn't pause anything, all 0 bits remain)
require(((~currentPausedStatus) & (~newPausedStatus)) == (~currentPausedStatus), InvalidNewPausedStatus());
_paused = newPausedStatus;
emit Unpaused(msg.sender, newPausedStatus);
}
/// @inheritdoc IPausable
function paused() public view virtual returns (uint256) {
return _paused;
}
/// @inheritdoc IPausable
function paused(
uint8 index
) public view virtual returns (bool) {
uint256 mask = 1 << index;
return ((_paused & mask) == mask);
}
/// @dev Internal helper for setting the paused status, and emitting the corresponding event.
function _setPausedStatus(
uint256 pausedStatus
) internal {
_paused = pausedStatus;
emit Paused(msg.sender, pausedStatus);
}
/**
* @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.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[48] private __gap;
}
"
},
"src/contracts/interfaces/IKeyRegistrar.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import {OperatorSet} from "../libraries/OperatorSetLib.sol";
import {BN254} from "../libraries/BN254.sol";
import "./ISemVerMixin.sol";
interface IKeyRegistrarErrors {
/// @notice Error thrown when a key is already registered
/// @dev Error code: 0x18f78402
/// @dev We prevent duplicate key registrations to maintain global key uniqueness and avoid conflicting operator-key mappings
error KeyAlreadyRegistered();
/// @notice Error thrown when an operator is already registered
/// @dev Error code: 0x42ee68b5
/// @dev We prevent duplicate operator registrations to prevent re-registrations with a new key
error OperatorAlreadyRegistered();
/// @notice Error thrown when the key format is invalid
/// @dev Error code: 0xd1091181
/// @dev We enforce proper key formats (20 bytes for ECDSA, valid G1/G2 points for BN254) to ensure cryptographic validity and prevent malformed key data
error InvalidKeyFormat();
/// @notice Error thrown when the address is zero
/// @dev Error code: 0xd92e233d
error ZeroAddress();
/// @notice Error thrown when the public key is zero
/// @dev Error code: 0x4935505f
error ZeroPubkey();
/// @notice Error thrown when the curve type is invalid
/// @dev Error code: 0xfdea7c09
/// @dev We require valid curve types (ECDSA or BN254)
error InvalidCurveType();
/// @notice Error thrown when the keypair is invalid
/// @dev Error code: 0x1b56a68b
error InvalidKeypair();
/// @notice Error thrown when the configuration is already set
/// @dev Error code: 0x0081f09f
/// @dev We prevent reconfiguration of operator sets to maintain consistency and avoid conflicting curve type settings
error ConfigurationAlreadySet();
/// @notice Error thrown when the operator set is not configured
/// @dev Error code: 0xb9a620da
/// @dev We require operator sets to be configured before key operations to ensure proper curve type validation and prevent operations on unconfigured sets
error OperatorSetNotConfigured();
/// @notice Error thrown when the key is not found
/// @dev Error code: 0x2e40e187
/// @dev We require existing key registrations for deregistration operations to ensure meaningful state changes and prevent operations on non-existent keys
error KeyNotFound(OperatorSet operatorSet, address operator);
/// @notice Error thrown when the operator is still slashable when trying to deregister a key
/// @dev Error code: 0x10702879
/// @dev We prevent key deregistration while operators are slashable to avoid race conditions and ensure operators cannot escape slashing by deregistering keys
error OperatorStillSlashable(OperatorSet operatorSet, address operator);
}
interface IKeyRegistrarTypes {
/// @dev Enum defining supported curve types
enum CurveType {
NONE,
ECDSA,
BN254
}
/// @dev Structure to store key information
struct KeyInfo {
bool isRegistered;
bytes keyData; // Flexible storage for different curve types
}
}
interface IKeyRegistrarEvents is IKeyRegistrarTypes {
/// @notice Emitted when a key is registered
event KeyRegistered(OperatorSet operatorSet, address indexed operator, CurveType curveType, bytes pubkey);
/// @notice Emitted when a key is deregistered
event KeyDeregistered(OperatorSet operatorSet, address indexed operator, CurveType curveType);
/// @notice Emitted when the aggregate BN254 key is updated
event AggregateBN254KeyUpdated(OperatorSet operatorSet, BN254.G1Point newAggregateKey);
/// @notice Emitted when an operator set is configured
event OperatorSetConfigured(OperatorSet operatorSet, CurveType curveType);
}
/// @notice The `KeyRegistrar` is used by AVSs to set their key type and by operators to register and deregister keys to operatorSets
/// @notice The integration pattern is as follows:
/// 1. The AVS calls `configureOperatorSet` to set the key type for their operatorSet
/// 2. Operators call `registerKey` to register their keys to the operatorSet
/// @dev This contract requires that keys are unique across all operatorSets, globally
/// @dev For the multichain protocol, the key type of the operatorSet must be set in the `KeyRegistrar`, but the
/// AVS is not required to use the KeyRegistrar for operator key management and can implement its own registry
interface IKeyRegistrar is IKeyRegistrarErrors, IKeyRegistrarEvents, ISemVerMixin {
/**
* @notice Configures an operator set with curve type
* @param operatorSet The operator set to configure
* @param curveType Type of curve (ECDSA, BN254)
* @dev Only authorized callers for the AVS can configure operator sets
* @dev Reverts for:
* - InvalidPermissions: Caller is not authorized for the AVS (via the PermissionController)
* - InvalidCurveType: The curve type is not ECDSA or BN254
* - ConfigurationAlreadySet: The operator set is already configured
* @dev Emits the following events:
* - OperatorSetConfigured: When the operator set is successfully configured with a curve type
*/
function configureOperatorSet(OperatorSet memory operatorSet, CurveType curveType) external;
/**
* @notice Registers a cryptographic key for an operator with a specific operator set
* @param operator Address of the operator to register key for
* @param operatorSet The operator set to register the key for
* @param pubkey Public key bytes. For ECDSA, this is the address of the key. For BN254, this is the G1 and G2 key combined (see `encodeBN254KeyData`)
* @param signature Signature proving ownership. For ECDSA this is a signature of the `getECDSAKeyRegistrationMessageHash`. For BN254 this is a signature of the `getBN254KeyRegistrationMessageHash`.
* @dev Can be called by operator directly or by addresses they've authorized via the `PermissionController`
* @dev There exist no restriction on the state of the operator with respect to the operatorSet. That is, an operator
* does not have to be registered for the operator in the `AllocationManager` to register a key for it
* @dev For ECDSA, we allow a smart contract to be the pubkey (via ERC1271 signatures), but note that the multichain protocol DOES NOT support smart contract signatures
* @dev Reverts for:
* - InvalidPermissions: Caller is not the operator or authorized via the PermissionController
* - OperatorSetNotConfigured: The operator set is not configured
* - OperatorAlreadyRegistered: The operator is already registered for the operatorSet in the KeyRegistrar
* - InvalidKeyFormat: For ECDSA: The key is not exactly 20 bytes
* - ZeroAddress: For ECDSA: The key is the zero address
* - KeyAlreadyRegistered: For ECDSA: The key is already registered globally by hash
* - InvalidSignature: For ECDSA: The signature is not valid
* - InvalidKeyFormat: For BN254: The key data is not exactly 192 bytes
* - InvalidSignature: For BN254: The signature is not exactly 64 bytes
* - ZeroPubkey: For BN254: The G1 point is the zero point
* - InvalidSignature: For BN254: The signature is not valid
* - KeyAlreadyRegistered: For BN254: The key is already registered globally by hash
* @dev Emits the following events:
* - KeyRegistered: When the key is successfully registered for the operator and operatorSet
*/
function registerKey(
address operator,
OperatorSet memory operatorSet,
bytes calldata pubkey,
bytes calldata signature
) external;
/**
* @notice Deregisters a cryptographic key for an operator with a specific operator set
* @param operator Address of the operator to deregister key for
* @param operatorSet The operator set to deregister the key from
* @dev Can be called by the operator directly or by addresses they've authorized via the `PermissionController`
* @dev Keys remain in global key registry to prevent reuse
* @dev Reverts for:
* - InvalidPermissions: Caller is not authorized for the operator (via the PermissionController)
* - OperatorStillSlashable: The operator is still slashable for the AVS
* - OperatorSetNotConfigured: The operator set is not configured
* - KeyNotFound: The operator does not have a registered key for this operator set
* @dev Emits the following events:
* - KeyDeregistered: When the key is successfully deregistered for the operator and operatorSet
*/
function deregisterKey(address operator, OperatorSet memory operatorSet) external;
/**
* @notice Checks if a key is registered for an operator with a specific operator set
* @param operatorSet The operator set to check
* @param operator Address of the operator
* @return True if the key is registered, false otherwise
* @dev If the operatorSet is not configured, this function will return false
*/
function isRegistered(OperatorSet memory operatorSet, address operator) external view returns (bool);
/**
* @notice Gets the curve type for an operator set
* @param operatorSet The operator set to get the curve type for
* @return The curve type, either ECDSA, BN254, or NONE
*/
function getOperatorSetCurveType(
OperatorSet memory operatorSet
) external view returns (CurveType);
/**
* @notice Gets the BN254 public key for an operator with a specific operator set
* @param operatorSet The operator set to get the key for
* @param operator Address of the operator
* @return g1Point The BN254 G1 public key
* @return g2Point The BN254 G2 public key
* @dev Reverts for:
* - InvalidCurveType: The operatorSet is not configured for BN254
* @dev Returns empty points if the operator has not registered a key for the operatorSet. We
* recommend calling `isRegistered` first to check if the operator has a key registered
*/
function getBN254Key(
OperatorSet memory operatorSet,
address operator
) external view returns (BN254.G1Point memory g1Point, BN254.G2Point memory g2Point);
/**
* @notice Gets the ECDSA public key for an operator with a specific operator set as bytes
* @param operatorSet The operator set to get the key for
* @param operator Address of the operator
* @return pubkey The ECDSA public key in bytes format
* @dev Reverts for:
* - InvalidCurveType: The operatorSet is not configured for ECDSA
* @dev Returns 0x0 if the operator has not registered a key for the operatorSet. We
* recommend calling `isRegistered` first to check if the operator has a key registered
*/
function getECDSAKey(OperatorSet memory operatorSet, address operator) external view returns (bytes memory);
/**
* @notice Gets the ECDSA public key for an operator with a specific operator set
* @param operatorSet The operator set to get the key for
* @param operator Address of the operator
* @return pubkey The ECDSA public key in address format
* @dev Reverts for:
* - InvalidCurveType: The operatorSet is not configured for ECDSA
* @dev Returns 0x0 if the operator has not registered a key for the operatorSet. We
* recommend calling `isRegistered` first to check if the operator has a key registered
*/
function getECDSAAddress(OperatorSet memory operatorSet, address operator) external view returns (address);
/**
* @notice Checks if a key hash is globally registered
* @param keyHash Hash of the key
* @return True if the key is globally registered
*/
function isKeyGloballyRegistered(
bytes32 keyHash
) external view returns (bool);
/**
* @notice Gets the key hash for an operator with a specific operator set
* @param operatorSet The operator set to get the key hash for
* @param operator Address of the operator
* @return keyHash The key hash
*/
function getKeyHash(OperatorSet memory operatorSet, address operator) external view returns (bytes32);
/**
* @notice Gets the operator from signing key
* @param operatorSet The operator set to get the operator for
* @param keyData The key data. For ECDSA, this is the signing key address. For BN254, this can be either the G1 key or the G1 and G2 key combined.
* @return operator. Returns 0x0 if the key is not registered
* @return status registration status. Returns false if the key is not registered
* @dev This function decodes the key data based on the curve type of the operator set
* @dev This function will return the operator address even if the operator is not registered for the operator set
* @dev Reverts for:
* - InvalidCurveType: The CurveType is not configured
*/
function getOperatorFromSigningKey(
OperatorSet memory operatorSet,
bytes memory keyData
) external view returns (address, bool);
/**
* @notice Returns the message hash for ECDSA key registration, which must be signed by the operator when registering an ECDSA key
* @param operator The operator address
* @param operatorSet The operator set
* @param keyAddress The address of the key
* @return The message hash for signing
*/
function getECDSAKeyRegistrationMessageHash(
address operator,
OperatorSet memory operatorSet,
address keyAddress
) external view returns (bytes32);
/**
* @notice Returns the message hash for BN254 key registration, which must be signed by the operator when registering a BN254 key
* @param operator The operator address
* @param operatorSet The operator set
* @param keyData The BN254 key data
* @return The message hash for signing
*/
function getBN254KeyRegistrationMessageHash(
address operator,
OperatorSet memory operatorSet,
bytes calldata keyData
) external view returns (bytes32);
/**
* @notice Encodes the BN254 key data into a bytes array
* @param g1Point The BN254 G1 public key
* @param g2Point The BN254 G2 public key
* @return The encoded key data
*/
function encodeBN254KeyData(
BN254.G1Point memory g1Point,
BN254.G2Point memory g2Point
) external pure returns (bytes memory);
}
"
},
"src/contracts/multichain/CrossChainRegistryStorage.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import "../interfaces/ICrossChainRegistry.sol";
import "../interfaces/IOperatorTableCalculator.sol";
import "../interfaces/IAllocationManager.sol";
import "../interfaces/IKeyRegistrar.sol";
import "../libraries/OperatorSetLib.sol";
/**
* @title CrossChainRegistryStorage
* @author Layr Labs, Inc.
* @notice Storage contract for the CrossChainRegistry, containing all storage variables and immutables
* @dev This abstract contract is designed to be inherited by the CrossChainRegistry implementation
*/
abstract contract CrossChainRegistryStorage is ICrossChainRegistry {
using EnumerableMap for EnumerableMap.UintToAddressMap;
using EnumerableSet for EnumerableSet.Bytes32Set;
using EnumerableSet for EnumerableSet.UintSet;
using OperatorSetLib for OperatorSet;
// Constants
/// @dev Index for flag that pauses generation reservations when set
uint8 internal constant PAUSED_GENERATION_RESERVATIONS = 0;
/// @dev Index for flag that pauses operator table calculator modifications when set
uint8 internal constant PAUSED_OPERATOR_TABLE_CALCULATOR = 1;
/// @dev Index for flag that pauses operator set config modifications when set
uint8 internal constant PAUSED_OPERATOR_SET_CONFIG = 2;
/// @dev Index for flag that pauses chain whitelist modifications when set
uint8 internal constant PAUSED_CHAIN_WHITELIST = 3;
// Immutables
/// @notice The AllocationManager contract for EigenLayer
IAllocationManager public immutable allocationManager;
/// @notice The KeyRegistrar contract for EigenLayer
IKeyRegistrar public immutable keyRegistrar;
// Mutatables
/// GENERATION RESERVATIONS
/// @notice Mapping of generation reservations for operator sets
EnumerableSet.Bytes32Set internal _activeGenerationReservations;
/// @dev Mapping from operator set key to operator table calculator for active reservations
mapping(bytes32 operatorSetKey => IOperatorTableCalculator) internal _operatorTableCalculators;
/// @dev Mapping from operator set key to operator set configuration
mapping(bytes32 operatorSetKey => OperatorSetConfig) internal _operatorSetConfigs;
/// CHAIN WHITELISTING
/// @dev Map of whitelisted chain IDs to operator table updaters
EnumerableMap.UintToAddressMap internal _whitelistedChainIDs;
/// @notice Table update cadence for all chains
uint32 internal _tableUpdateCadence;
// Construction
constructor(IAllocationManager _allocationManager, IKeyRegistrar _keyRegistrar) {
allocationManager = _allocationManager;
keyRegistrar = _keyRegistrar;
}
/**
* @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.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[42] private __gap;
}
"
},
"lib/openzeppelin-contracts-upgradeable-v4.9.0/contracts/utils/AddressUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable-v4.9.0/contracts/utils/ContextUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../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;
}
/**
* @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.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
"
},
"src/contracts/interfaces/IPermissionController.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import "./ISemVerMixin.sol";
interface IPermissionControllerErrors {
/// @notice Thrown when a non-admin caller attempts to perform an admin-only action.
error NotAdmin();
/// @notice Thrown when attempting to remove an admin that does not exist.
error AdminNotSet();
/// @notice Thrown when attempting to set an appointee for a function that already has one.
error AppointeeAlreadySet();
/// @notice Thrown when attempting to interact with a non-existent appointee.
error AppointeeNotSet();
/// @notice Thrown when attempting to remove the last remaining admin.
error CannotHaveZeroAdmins();
/// @notice Thrown when attempting to set an admin that is already registered.
error AdminAlreadySet();
/// @notice Thrown when attempting to interact with an admin that is not in pending status.
error AdminNotPending();
/// @notice Thrown when attempting to add an admin that is already pending.
error AdminAlreadyPending();
}
interface IPermissionControllerEvents {
/// @notice Emitted when an appointee is set for an account to handle specific function calls.
event AppointeeSet(address indexed account, address indexed appointee, address target, bytes4 selector);
/// @notice Emitted when an appointee's permission to handle function calls for an account is revoked.
event AppointeeRemoved(address indexed account, address indexed appointee, address target, bytes4 selector);
/// @notice Emitted when an address is set as a pending admin for an account, requiring acceptance.
event PendingAdminAdded(address indexed account, address admin);
/// @notice Emitted when a pending admin status is removed for an account before acceptance.
event PendingAdminRemoved(address indexed account, address admin);
/// @notice Emitted when an address accepts and becomes an active admin for an account.
event AdminSet(address indexed account, address admin);
/// @notice Emitted when an admin's permissions are removed from an account.
event AdminRemoved(address indexed account, address admin);
}
interface IPermissionController is IPermissionControllerErrors, IPermissionControllerEvents, ISemVerMixin {
/**
* @notice Sets a pending admin for an account.
* @param account The account to set the pending admin for.
* @param admin The address to set as pending admin.
* @dev The pending admin must accept the role before becoming an active admin.
* @dev Multiple admins can be set for a single account.
*/
function addPendingAdmin(address account, address admin) external;
/**
* @notice Removes a pending admin from an account before they have a
Submitted on: 2025-09-25 10:04:19
Comments
Log in to comment.
No comments yet.