Source Code
Overview
AVAX Balance
More Info
ContractCreator
Multichain Info
N/A
Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Register Vault | 40372292 | 253 days ago | IN | 0 AVAX | 0 |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | Amount | ||
|---|---|---|---|---|---|---|
| 40624266 | 247 days ago | 0 AVAX | ||||
| 40624266 | 247 days ago | 0 AVAX | ||||
| 40623685 | 247 days ago | 0 AVAX | ||||
| 40623685 | 247 days ago | 0 AVAX | ||||
| 40623618 | 247 days ago | 0 AVAX | ||||
| 40623618 | 247 days ago | 0 AVAX | ||||
| 40623276 | 247 days ago | 0 AVAX | ||||
| 40623276 | 247 days ago | 0 AVAX | ||||
| 40623276 | 247 days ago | 0 AVAX | ||||
| 40623276 | 247 days ago | 0 AVAX | ||||
| 40623276 | 247 days ago | 0 AVAX | ||||
| 40623276 | 247 days ago | 0 AVAX | ||||
| 40621692 | 247 days ago | 0 AVAX | ||||
| 40621692 | 247 days ago | 0 AVAX | ||||
| 40516787 | 249 days ago | 0 AVAX | ||||
| 40516787 | 249 days ago | 0 AVAX | ||||
| 40516787 | 249 days ago | 0 AVAX | ||||
| 40515683 | 249 days ago | 0 AVAX | ||||
| 40515683 | 249 days ago | 0 AVAX | ||||
| 40515683 | 249 days ago | 0 AVAX | ||||
| 40515683 | 249 days ago | 0 AVAX | ||||
| 40515683 | 249 days ago | 0 AVAX | ||||
| 40515683 | 249 days ago | 0 AVAX | ||||
| 40442805 | 251 days ago | 0 AVAX | ||||
| 40442805 | 251 days ago | 0 AVAX |
Loading...
Loading
Contract Name:
MiddlewareVaultManager
Compiler Version
v0.8.25+commit.b61c2a91
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity 0.8.25;
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {IMiddlewareVaultManager} from "../../interfaces/middleware/IMiddlewareVaultManager.sol";
import {IAvalancheL1Middleware} from "../../interfaces/middleware/IAvalancheL1Middleware.sol";
import {IRegistry} from "../../interfaces/common/IRegistry.sol";
import {IEntity} from "../../interfaces/common/IEntity.sol";
import {IVaultTokenized} from "../../interfaces/vault/IVaultTokenized.sol";
import {BaseDelegator} from "../../contracts/delegator/BaseDelegator.sol";
import {ISlasher} from "../../interfaces/slasher/ISlasher.sol";
import {IVetoSlasher} from "../../interfaces/slasher/IVetoSlasher.sol";
import {MapWithTimeData} from "./libraries/MapWithTimeData.sol";
import {AvalancheL1Middleware} from "./AvalancheL1Middleware.sol";
contract MiddlewareVaultManager is IMiddlewareVaultManager, Ownable {
using EnumerableMap for EnumerableMap.AddressToUintMap;
using MapWithTimeData for EnumerableMap.AddressToUintMap;
mapping(address => uint96) public vaultToAssetClass;
EnumerableMap.AddressToUintMap private vaults;
address public immutable VAULT_REGISTRY;
AvalancheL1Middleware public immutable middleware;
uint48 private constant INSTANT_SLASHER_TYPE = 0;
uint48 private constant VETO_SLASHER_TYPE = 1;
constructor(address vaultRegistry, address owner, address middlewareAddress) Ownable(owner) {
if (vaultRegistry == address(0)) {
revert AvalancheL1Middleware__ZeroAddress("vaultRegistry");
}
if (middlewareAddress == address(0)) {
revert AvalancheL1Middleware__ZeroAddress("middlewareAddress");
}
VAULT_REGISTRY = vaultRegistry;
middleware = AvalancheL1Middleware(payable(middlewareAddress));
}
/**
* @notice Registers a vault to a specific asset class, sets the max stake.
* @param vault The vault address
* @param assetClassId The asset class ID for that vault
* @param vaultMaxL1Limit The maximum stake allowed for this vault
*/
function registerVault(address vault, uint96 assetClassId, uint256 vaultMaxL1Limit) external onlyOwner {
if (vaultMaxL1Limit == 0) {
revert AvalancheL1Middleware__ZeroVaultMaxL1Limit();
}
if (vaults.contains(vault)) {
revert AvalancheL1Middleware__VaultAlreadyRegistered();
}
uint48 vaultEpoch = IVaultTokenized(vault).epochDuration();
address slasher = IVaultTokenized(vault).slasher();
if (slasher != address(0) && IEntity(slasher).TYPE() == VETO_SLASHER_TYPE) {
vaultEpoch -= IVetoSlasher(slasher).vetoDuration();
}
if (vaultEpoch < middleware.SLASHING_WINDOW()) {
revert AvalancheL1Middleware__VaultEpochTooShort();
}
vaultToAssetClass[vault] = assetClassId;
_setVaultMaxL1Limit(vault, assetClassId, vaultMaxL1Limit);
vaults.add(vault);
vaults.enable(vault);
}
/**
* @notice Updates a vault's max L1 stake limit. Disables or enables the vault based on the new limit
* @param vault The vault address
* @param assetClassId The asset class ID
* @param vaultMaxL1Limit The new maximum stake
*/
function updateVaultMaxL1Limit(address vault, uint96 assetClassId, uint256 vaultMaxL1Limit) external onlyOwner {
if (!vaults.contains(vault)) {
revert AvalancheL1Middleware__NotVault(vault);
}
if (vaultToAssetClass[vault] != assetClassId) {
revert AvalancheL1Middleware__WrongVaultAssetClass();
}
_setVaultMaxL1Limit(vault, assetClassId, vaultMaxL1Limit);
if (vaultMaxL1Limit == 0) {
vaults.disable(vault);
} else {
vaults.enable(vault);
}
}
/**
* @notice Removes a vault if the grace period has passed
* @param vault The vault address
*/
function removeVault(
address vault
) external onlyOwner {
if (!vaults.contains(vault)) {
revert AvalancheL1Middleware__NotVault(vault);
}
(, uint48 disabledTime) = vaults.getTimes(vault);
if (disabledTime == 0) {
revert AvalancheL1Middleware__VaultNotDisabled();
}
if (disabledTime + middleware.SLASHING_WINDOW() > Time.timestamp()) {
revert AvalancheL1Middleware__VaultGracePeriodNotPassed();
}
// Remove from vaults and clear mapping
vaults.remove(vault);
delete vaultToAssetClass[vault];
}
/**
* @notice Sets a vault's max L1 stake limit
* @param vault The vault address
* @param assetClassId The asset class ID
* @param amount The new maximum stake
*/
function _setVaultMaxL1Limit(address vault, uint96 assetClassId, uint256 amount) internal onlyOwner {
if (!IRegistry(VAULT_REGISTRY).isEntity(vault)) {
revert AvalancheL1Middleware__NotVault(vault);
}
if (!middleware.isActiveAssetClass(assetClassId)) {
revert IAvalancheL1Middleware.AvalancheL1Middleware__AssetClassNotActive(assetClassId);
}
address vaultCollateral = IVaultTokenized(vault).collateral();
if (!middleware.isAssetInClass(assetClassId, vaultCollateral)) {
revert IAvalancheL1Middleware.AvalancheL1Middleware__CollateralNotInAssetClass(
vaultCollateral, assetClassId
);
}
address delegator = IVaultTokenized(vault).delegator();
BaseDelegator(delegator).setMaxL1Limit(middleware.L1_VALIDATOR_MANAGER(), assetClassId, amount);
}
function slashVault() external pure {
revert AvalancheL1Middleware__SlasherNotImplemented();
}
function getVaultCount() external view returns (uint256) {
return vaults.length();
}
function getVaultAtWithTimes(
uint256 index
) external view returns (address vault, uint48 enabledTime, uint48 disabledTime) {
return vaults.atWithTimes(index);
}
function getVaultAssetClass(
address vault
) external view returns (uint96) {
return vaultToAssetClass[vault];
}
function getVaults(
uint48 epoch
) external view returns (address[] memory) {
uint256 vaultCount = vaults.length();
uint48 epochStart = middleware.getEpochStartTs(epoch);
uint256 activeCount = 0;
for (uint256 i = 0; i < vaultCount; i++) {
(address vault, uint48 enabledTime, uint48 disabledTime) = vaults.atWithTimes(i);
if (_wasActiveAt(enabledTime, disabledTime, epochStart)) {
activeCount++;
}
}
address[] memory activeVaults = new address[](activeCount);
uint256 activeIndex = 0;
for (uint256 i = 0; i < vaultCount; i++) {
(address vault, uint48 enabledTime, uint48 disabledTime) = vaults.atWithTimes(i);
if (_wasActiveAt(enabledTime, disabledTime, epochStart)) {
activeVaults[activeIndex] = vault;
activeIndex++;
}
}
return activeVaults;
}
function _wasActiveAt(uint48 enabledTime, uint48 disabledTime, uint48 timestamp) private pure returns (bool) {
return enabledTime != 0 && enabledTime <= timestamp && (disabledTime == 0 || disabledTime >= timestamp);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/types/Time.sol)
pragma solidity ^0.8.20;
import {Math} from "../math/Math.sol";
import {SafeCast} from "../math/SafeCast.sol";
/**
* @dev This library provides helpers for manipulating time-related objects.
*
* It uses the following types:
* - `uint48` for timepoints
* - `uint32` for durations
*
* While the library doesn't provide specific types for timepoints and duration, it does provide:
* - a `Delay` type to represent duration that can be programmed to change value automatically at a given point
* - additional helper functions
*/
library Time {
using Time for *;
/**
* @dev Get the block timestamp as a Timepoint.
*/
function timestamp() internal view returns (uint48) {
return SafeCast.toUint48(block.timestamp);
}
/**
* @dev Get the block number as a Timepoint.
*/
function blockNumber() internal view returns (uint48) {
return SafeCast.toUint48(block.number);
}
// ==================================================== Delay =====================================================
/**
* @dev A `Delay` is a uint32 duration that can be programmed to change value automatically at a given point in the
* future. The "effect" timepoint describes when the transitions happens from the "old" value to the "new" value.
* This allows updating the delay applied to some operation while keeping some guarantees.
*
* In particular, the {update} function guarantees that if the delay is reduced, the old delay still applies for
* some time. For example if the delay is currently 7 days to do an upgrade, the admin should not be able to set
* the delay to 0 and upgrade immediately. If the admin wants to reduce the delay, the old delay (7 days) should
* still apply for some time.
*
*
* The `Delay` type is 112 bits long, and packs the following:
*
* ```
* | [uint48]: effect date (timepoint)
* | | [uint32]: value before (duration)
* ↓ ↓ ↓ [uint32]: value after (duration)
* 0xAAAAAAAAAAAABBBBBBBBCCCCCCCC
* ```
*
* NOTE: The {get} and {withUpdate} functions operate using timestamps. Block number based delays are not currently
* supported.
*/
type Delay is uint112;
/**
* @dev Wrap a duration into a Delay to add the one-step "update in the future" feature
*/
function toDelay(uint32 duration) internal pure returns (Delay) {
return Delay.wrap(duration);
}
/**
* @dev Get the value at a given timepoint plus the pending value and effect timepoint if there is a scheduled
* change after this timepoint. If the effect timepoint is 0, then the pending value should not be considered.
*/
function _getFullAt(Delay self, uint48 timepoint) private pure returns (uint32, uint32, uint48) {
(uint32 valueBefore, uint32 valueAfter, uint48 effect) = self.unpack();
return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect);
}
/**
* @dev Get the current value plus the pending value and effect timepoint if there is a scheduled change. If the
* effect timepoint is 0, then the pending value should not be considered.
*/
function getFull(Delay self) internal view returns (uint32, uint32, uint48) {
return _getFullAt(self, timestamp());
}
/**
* @dev Get the current value.
*/
function get(Delay self) internal view returns (uint32) {
(uint32 delay, , ) = self.getFull();
return delay;
}
/**
* @dev Update a Delay object so that it takes a new duration after a timepoint that is automatically computed to
* enforce the old delay at the moment of the update. Returns the updated Delay object and the timestamp when the
* new delay becomes effective.
*/
function withUpdate(
Delay self,
uint32 newValue,
uint32 minSetback
) internal view returns (Delay updatedDelay, uint48 effect) {
uint32 value = self.get();
uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0));
effect = timestamp() + setback;
return (pack(value, newValue, effect), effect);
}
/**
* @dev Split a delay into its components: valueBefore, valueAfter and effect (transition timepoint).
*/
function unpack(Delay self) internal pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {
uint112 raw = Delay.unwrap(self);
valueAfter = uint32(raw);
valueBefore = uint32(raw >> 32);
effect = uint48(raw >> 64);
return (valueBefore, valueAfter, effect);
}
/**
* @dev pack the components into a Delay object.
*/
function pack(uint32 valueBefore, uint32 valueAfter, uint48 effect) internal pure returns (Delay) {
return Delay.wrap((uint112(effect) << 64) | (uint112(valueBefore) << 32) | uint112(valueAfter));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.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.
*
* The initial owner is set to the address provided by the deployer. 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 Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_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);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.
pragma solidity ^0.8.20;
import {EnumerableSet} from "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
* - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
* - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
* - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
* ====
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code repetition as possible, we write it in
// terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
// and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit in bytes32.
/**
* @dev Query for a nonexistent map key.
*/
error EnumerableMapNonexistentKey(bytes32 key);
struct Bytes32ToBytes32Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 key => bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
bytes32 value = map._values[key];
if (value == 0 && !contains(map, key)) {
revert EnumerableMapNonexistentKey(key);
}
return value;
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
return map._keys.values();
}
// UintToUintMap
struct UintToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key)));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintToAddressMap
struct UintToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key)))));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressToUintMap
struct AddressToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (address(uint160(uint256(key))), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// Bytes32ToUintMap
struct Bytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
return set(map._inner, key, bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (key, uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, key);
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
return uint256(get(map._inner, key));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity 0.8.25;
import {IAvalancheL1Middleware} from "../../interfaces/middleware/IAvalancheL1Middleware.sol";
/**
* @title IMiddlewareVaultManager
* @notice Manages vault registration, maximum L1 stake limits, and slash routing.
*/
interface IMiddlewareVaultManager {
// -----------------------------------------------------------------------
// Errors
// -----------------------------------------------------------------------
error AvalancheL1Middleware__VaultAlreadyRegistered();
error AvalancheL1Middleware__VaultEpochTooShort();
error AvalancheL1Middleware__NotVault(address vault);
error AvalancheL1Middleware__WrongVaultAssetClass();
error AvalancheL1Middleware__ZeroVaultMaxL1Limit();
error AvalancheL1Middleware__VaultGracePeriodNotPassed();
error AvalancheL1Middleware__VaultNotDisabled();
error AvalancheL1Middleware__ZeroAddress(string name);
error AvalancheL1Middleware__SlasherNotImplemented();
// -----------------------------------------------------------------------
// Public state variable getters
// -----------------------------------------------------------------------
/**
* @notice Returns the VAULT_REGISTRY address.
*/
function VAULT_REGISTRY() external view returns (address);
//
// Functions
//
/**
* @notice Registers a vault to a specific asset class with a given maximum L1 stake limit.
* @param vault The vault address
* @param assetClassId The asset class ID for the vault
* @param vaultMaxL1Limit The maximum stake allowed for this vault
*/
function registerVault(address vault, uint96 assetClassId, uint256 vaultMaxL1Limit) external;
/**
* @notice Updates a vault's max L1 stake limit.
* @param vault The vault address
* @param assetClassId The asset class ID
* @param vaultMaxL1Limit The new maximum stake
*/
function updateVaultMaxL1Limit(address vault, uint96 assetClassId, uint256 vaultMaxL1Limit) external;
/**
* @notice Removes a vault if the grace period has passed.
* @param vault The vault address
*/
function removeVault(
address vault
) external;
/**
* @notice Slashes a vault based on the operator’s share of stake.
*/
function slashVault() external;
/**
* @notice Returns the number of vaults registered.
* @return The count of vaults
*/
function getVaultCount() external view returns (uint256);
/**
* @notice Returns the vault and its enable/disable times at the given index.
* @param index The vault index
* @return vault The vault address
* @return enabledTime The time the vault was enabled
* @return disabledTime The time the vault was disabled
*/
function getVaultAtWithTimes(
uint256 index
) external view returns (address vault, uint48 enabledTime, uint48 disabledTime);
/**
* @notice Returns the asset class ID for a given vault.
* @param vault The vault address
* @return The asset class ID
*/
function getVaultAssetClass(
address vault
) external view returns (uint96);
/**
* @notice Fetches the active vaults for a given epoch
* @param epoch The epoch for which vaults are fetched
* @return An array of active vault addresses
*/
function getVaults(
uint48 epoch
) external view returns (address[] memory);
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity 0.8.25;
import {
IValidatorManager,
Validator,
ValidatorStatus,
ValidatorRegistrationInput,
PChainOwner
} from "@avalabs/teleporter/validator-manager/interfaces/IValidatorManager.sol";
/**
* @title IAvalancheL1Middleware
* @notice Manages operator registration, asset classes, stake accounting, and slashing for Avalanche L1
*/
interface IAvalancheL1Middleware {
// Errors
error AvalancheL1Middleware__ActiveSecondaryAssetCLass(uint256 assetClassId);
error AvalancheL1Middleware__AssetClassNotActive(uint256 assetClassId);
error AvalancheL1Middleware__AssetStillInUse(uint256 assetClassId);
error AvalancheL1Middleware__AlreadyRebalanced(address operator, uint48 epoch);
error AvalancheL1Middleware__WeightUpdateNotPending(bytes32 validationId);
error AvalancheL1Middleware__CollateralNotInAssetClass(address collateral, uint96 assetClassId);
error AvalancheL1Middleware__EpochError(uint48 epochStartTs);
error AvalancheL1Middleware__MaxL1LimitZero();
error AvalancheL1Middleware__NoSlasher();
error AvalancheL1Middleware__NotEnoughFreeStakeSecondaryAssetClasses();
error AvalancheL1Middleware__NodeNotActive();
error AvalancheL1Middleware__NotEnoughFreeStake(uint256 newStake);
error AvalancheL1Middleware__StakeTooHigh(uint256 newStake, uint256 maxStake);
error AvalancheL1Middleware__StakeTooLow(uint256 newStake, uint256 minStake);
error AvalancheL1Middleware__OperatorGracePeriodNotPassed(uint48 disabledTime, uint48 slashingWindow);
error AvalancheL1Middleware__OperatorAlreadyRegistered(address operator);
error AvalancheL1Middleware__OperatorNotOptedIn(address operator, address l1ValidatorManager);
error AvalancheL1Middleware__OperatorNotRegistered(address operator);
error AvalancheL1Middleware__SlashingWindowTooShort(uint48 slashingWindow, uint48 epochDuration);
error AvalancheL1Middleware__TooBigSlashAmount();
error AvalancheL1Middleware__NodeNotFound(bytes32 nodeId);
error AvalancheL1Middleware__SecurityModuleCapacityNotEnough(uint256 securityModuleCapacity, uint256 minStake);
error AvalancheL1Middleware__WeightUpdatePending(bytes32 validationID);
error AvalancheL1Middleware__NodeStateNotUpdated(bytes32 validationID);
error AvalancheL1Middleware__NotEpochUpdatePeriod(uint48 timeNow, uint48 epochUpdatePeriod);
error AvalancheL1Middleware__NotImplemented();
error AvalancheL1Middleware__NodePendingRemoval(bytes32 nodeId);
error AvalancheL1Middleware__NodePendingUpdate(bytes32 nodeId);
error AvalancheL1Middleware__ZeroAddress(string name);
error AvalancheL1Middleware__InvalidScaleFactor();
error AvalancheL1Middleware__ManualEpochUpdateRequired(uint48 epochsPending, uint48 maxAutoUpdates);
error AvalancheL1Middleware__NoEpochsToProcess();
error AvalancheL1Middleware__TooManyEpochsRequested(uint48 requested, uint48 pending);
// Events
/**
* @notice Emitted when a node is added
* @param operator The operator who added the node
* @param nodeId The ID of the node
* @param stake The stake assigned to the node
* @param validationID The validation identifier from BalancerValidatorManager
*/
event NodeAdded(address indexed operator, bytes32 indexed nodeId, uint256 stake, bytes32 indexed validationID);
/**
* @notice Emitted when a node is removed
* @param operator The operator who removed the node
* @param nodeId The ID of the node
* @param validationID The validation identifier from BalancerValidatorManager
*/
event NodeRemoved(address indexed operator, bytes32 indexed nodeId, bytes32 indexed validationID);
/**
* @notice Emitted when a single node's stake is updated
* @param operator The operator who owns the node
* @param nodeId The ID of the node
* @param newStake The new stake of the node
* @param validationID The validation identifier from BalancerValidatorManager
*/
event NodeStakeUpdated(
address indexed operator, bytes32 indexed nodeId, uint256 newStake, bytes32 indexed validationID
);
/**
* @notice Emitted when the operator has leftover stake after rebalancing
* @param operator The operator who has leftover stake
* @param leftoverStake The amount of leftover stake
*/
event OperatorHasLeftoverStake(address indexed operator, uint256 leftoverStake);
/**
* @notice Emitted when all node stakes are updated for an operator
* @param operator The operator
* @param newStake The total new stake for the operator
*/
event AllNodeStakesUpdated(address indexed operator, uint256 newStake);
/**
* @notice Emitted when the Vault Manager is updated
* @param oldVaultManager The old Vault Manager address
* @param newVaultManager The new Vault Manager address
*/
event VaultManagerUpdated(address indexed oldVaultManager, address indexed newVaultManager);
/**
* @notice Emitted when the node stake cache is manually processed
* @param upToEpoch The epoch to process up to
* @param epochsProcessedCount The number of epochs processed
*/
event NodeStakeCacheManuallyProcessed(uint48 upToEpoch, uint48 epochsProcessedCount);
/**
* @dev Simple struct to return operator stake and key.
*/
struct OperatorData {
uint256 stake;
bytes32 key;
}
// External functions
/**
* @notice Activates a secondary asset class
* @param assetClassId The asset class ID to activate
*/
function activateSecondaryAssetClass(
uint256 assetClassId
) external;
/**
* @notice Deactivates a secondary asset class
* @param assetClassId The asset class ID to deactivate
*/
function deactivateSecondaryAssetClass(
uint256 assetClassId
) external;
/**
* @notice Registers a new operator and enables it
* @param operator The operator address
*/
function registerOperator(
address operator
) external;
/**
* @notice Disables an operator
* @param operator The operator address
*/
function disableOperator(
address operator
) external;
/**
* @notice Enables an operator
* @param operator The operator address
*/
function enableOperator(
address operator
) external;
/**
* @notice Removes an operator if grace period has passed
* @param operator The operator address
*/
function removeOperator(
address operator
) external;
/**
* @notice Add a new node => create a new validator.
* Check the new node stake also ensure security module capacity.
* @param nodeId The node ID
* @param blsKey The BLS key
* @param registrationExpiry The Unix timestamp after which the reigistration is no longer valid on the P-Chain
* @param remainingBalanceOwner The owner of a validator's remaining balance
* @param disableOwner The owner of a validator's disable owner on the P-Chain
* @param stakeAmount The initial stake of the node to be added(optional)
*/
function addNode(
bytes32 nodeId,
bytes calldata blsKey,
uint64 registrationExpiry,
PChainOwner calldata remainingBalanceOwner,
PChainOwner calldata disableOwner,
uint256 stakeAmount
) external;
/**
* @notice Remove a node => remove a validator.
* @param nodeId The node ID
*/
function removeNode(
bytes32 nodeId
) external;
/**
* @notice Rebalance node stakes once per epoch for an operator.
* @param operator The operator address
* @param limitStake The maximum stake adjustment (add or remove) allowed per node per call.
*/
function forceUpdateNodes(address operator, uint256 limitStake) external;
/**
* @notice Update the stake of a validator.
* @param nodeId The node ID.
* @param stakeAmount The new stake.
*/
function initializeValidatorStakeUpdate(bytes32 nodeId, uint256 stakeAmount) external;
/**
* @notice Finalize a pending validator registration
* @param operator The operator address
* @param nodeId The node ID
* @param messageIndex The message index
*/
function completeValidatorRegistration(address operator, bytes32 nodeId, uint32 messageIndex) external;
/**
* @notice Finalize a pending stake update
* @param nodeId The node ID
* @param messageIndex The message index
*/
function completeStakeUpdate(bytes32 nodeId, uint32 messageIndex) external;
/**
* @notice Finalize a pending validator removal
* @param messageIndex The message index
*/
function completeValidatorRemoval(
uint32 messageIndex
) external;
/**
* @notice Slashes an operator's stake
* @param epoch The epoch of the slash
* @param operator The operator being slashed
* @param amount The slash amount
* @param assetClassId The asset class ID
*/
function slash(uint48 epoch, address operator, uint256 amount, uint96 assetClassId) external;
/**
* @notice Calculates and caches total stake for an epoch
* @param epoch The epoch number
* @param assetClassId The asset class ID
* @return totalStake The total stake calculated and cached
*/
function calcAndCacheStakes(uint48 epoch, uint96 assetClassId) external returns (uint256);
/**
* @notice Calculates and caches node stakes for all operators retroactively for all epochs
*/
function calcAndCacheNodeStakeForAllOperators() external;
/**
* @notice Fetches the primary and secondary asset classes
* @return primary The primary asset class
* @return secondaries An array of secondary asset classes
*/
function getActiveAssetClasses() external view returns (uint256 primary, uint256[] memory secondaries);
/**
* @notice Checks if the classId is active
* @param assetClassId The asset class ID
* @return bool True if active
*/
function isActiveAssetClass(
uint96 assetClassId
) external view returns (bool);
/**
* @notice Gets the start timestamp for a given epoch
* @param epoch The epoch number
* @return timestamp The start time of that epoch
*/
function getEpochStartTs(
uint48 epoch
) external view returns (uint48);
/**
* @notice Gets the epoch number at a given timestamp
* @param timestamp The timestamp
* @return epoch The epoch at that time
*/
function getEpochAtTs(
uint48 timestamp
) external view returns (uint48);
/**
* @notice Gets the current epoch based on the current block time
* @return epoch The current epoch
*/
function getCurrentEpoch() external view returns (uint48);
/**
* @notice Returns an operator's stake at a given epoch for a specific asset class
* @param operator The operator address
* @param epoch The epoch number
* @param assetClassId The asset class ID
* @return stake The operator's stake
*/
function getOperatorStake(address operator, uint48 epoch, uint96 assetClassId) external view returns (uint256);
/**
* @notice Returns total stake across all operators in a specific epoch
* @param epoch The epoch number
* @param assetClassId The asset class ID
* @return The total stake in that epoch
*/
function getTotalStake(uint48 epoch, uint96 assetClassId) external view returns (uint256);
/**
* @notice Returns all operators
*/
function getAllOperators() external view returns (address[] memory);
/**
* @notice Returns the cached stake for a given node in the specified epoch, based on its Validation ID.
* @param epoch The target Not enough free stake to add nodeepoch.
* @param validationId The node ID.
* @return The node stake from the cache.
*/
function getNodeStake(uint48 epoch, bytes32 validationId) external view returns (uint256);
/**
* @notice Returns the current epoch number
* @param operator The operator address
* @param epoch The epoch number
* @return activeNodeIds The list of nodes
*/
function getActiveNodesForEpoch(address operator, uint48 epoch) external view returns (bytes32[] memory);
/**
* @notice Returns the available stake for an operator
* @param operator The operator address
* @return The available stake
*/
function getOperatorAvailableStake(
address operator
) external view returns (uint256);
/**
* @notice Summation of node stakes from the nodeStakeCache.
* @param operator The operator address.
* @return registeredStake The sum of node stakes.
*/
function getOperatorUsedStakeCached(
address operator
) external view returns (uint256);
/**
* @notice Returns the Vault Manager address associated to this middleware
* @return Address Vault Manager
*/
function getVaultManager() external view returns (address);
/**
* @notice Returns the true active stake for an operator in a given epoch and asset class
* @param epoch The epoch number
* @param operator The operator address
* @param assetClass The asset class ID
* @return The true active stake
*/
function getOperatorUsedStakeCachedPerEpoch(
uint48 epoch,
address operator,
uint96 assetClass
) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
interface IRegistry {
error EntityNotExist();
/**
* @notice Emitted when an entity is added.
* @param entity address of the added entity
*/
event AddEntity(address indexed entity);
/**
* @notice Get if a given address is an entity.
* @param account address to check
* @return if the given address is an entity
*/
function isEntity(
address account
) external view returns (bool);
/**
* @notice Get a total number of entities.
* @return total number of entities added
*/
function totalEntities() external view returns (uint256);
/**
* @notice Get an entity given its index.
* @param index index of the entity to get
* @return address of the entity
*/
function entity(
uint256 index
) external view returns (address);
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
interface IEntity is IERC165 {
error Entity__NotInitialized();
error Entity__ZeroAddress(string name);
/**
* @notice Get the factory's address.
* @return address of the factory
*/
function FACTORY() external view returns (address);
/**
* @notice Get the entity's type.
* @return type of the entity
*/
function TYPE() external view returns (uint64);
/**
* @notice Initialize this entity contract by using given data.
* @param data some data to use
*/
function initialize(
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
// import {IMigratableEntity} from "../common/IMigratableEntity.sol";
interface IVaultTokenized {
error Vault__AlreadyClaimed();
error Vault__AlreadySet();
error Vault__DelegatorAlreadyInitialized();
error Vault__DepositLimitReached();
error Vault__InsufficientClaim();
error Vault__InsufficientDeposit();
error Vault__InsufficientRedemption();
error Vault__InsufficientWithdrawal();
error Vault__InvalidAccount();
error Vault__InvalidCaptureEpoch();
error Vault__InvalidClaimer();
error Vault__InvalidCollateral();
error Vault__InvalidDelegator();
error Vault__InvalidEpoch();
error Vault__InvalidEpochDuration();
error Vault__InvalidLengthEpochs();
error Vault__InvalidOnBehalfOf();
error Vault__InvalidRecipient();
error Vault__InvalidSlasher();
error Vault__MissingRoles();
error Vault__InconsistentRoles();
error Vault__NotDelegator();
error Vault__NotSlasher();
error Vault__NotWhitelistedDepositor();
error Vault__SlasherAlreadyInitialized();
error Vault__TooMuchRedeem();
error Vault__TooMuchWithdraw();
error Vault__InvalidTimestamp();
error Vault__NoPreviousEpoch();
error Vault__MigrationNotImplemented();
error Vault__InvalidFactory();
// Structs
/**
* @notice Initial parameters needed for a vault deployment.
* @param collateral vault's underlying collateral
* @param burner vault's burner to issue debt to (e.g., 0xdEaD or some unwrapper contract)
* @param epochDuration duration of the vault epoch (it determines sync points for withdrawals)
* @param depositWhitelist if enabling deposit whitelist
* @param isDepositLimit if enabling deposit limit
* @param depositLimit deposit limit (maximum amount of the collateral that can be in the vault simultaneously)
* @param defaultAdminRoleHolder address of the initial DEFAULT_ADMIN_ROLE holder
* @param depositWhitelistSetRoleHolder address of the initial DEPOSIT_WHITELIST_SET_ROLE holder
* @param depositorWhitelistRoleHolder address of the initial DEPOSITOR_WHITELIST_ROLE holder
* @param isDepositLimitSetRoleHolder address of the initial IS_DEPOSIT_LIMIT_SET_ROLE holder
* @param depositLimitSetRoleHolder address of the initial DEPOSIT_LIMIT_SET_ROLE holder
* @param name name for the ERC20 tokenized vault
* @param symbol symbol for the ERC20 tokenized vault
*/
struct InitParams {
address collateral;
address burner;
uint48 epochDuration;
bool depositWhitelist;
bool isDepositLimit;
uint256 depositLimit;
address defaultAdminRoleHolder;
address depositWhitelistSetRoleHolder;
address depositorWhitelistRoleHolder;
address isDepositLimitSetRoleHolder;
address depositLimitSetRoleHolder;
string name;
string symbol;
}
/**
* @notice Get the factory's address.
* @return address of the factory
*/
function FACTORY() external view returns (address);
/**
* @notice Get the entity's version.
* @return version of the entity
* @dev Starts from 1.
*/
function version() external view returns (uint64);
/**
* @notice Initialize this entity contract by using a given data and setting a particular version and owner.
* @param initialVersion initial version of the entity
* @param owner initial owner of the entity
* @param data some data to use
* @param delegatorFactory Address of the delegator factory.
* @param slasherFactory Address of the slasher factory.
*
*/
function initialize(
uint64 initialVersion,
address owner,
bytes calldata data,
address delegatorFactory,
address slasherFactory
) external;
/**
* @notice Migrate this entity to a particular newer version using a given data.
* @param newVersion new version of the entity
* @param data some data to use
*/
function migrate(uint64 newVersion, bytes calldata data) external;
/**
* @notice Hints for an active balance.
* @param activeSharesOfHint hint for the active shares of checkpoint
* @param activeStakeHint hint for the active stake checkpoint
* @param activeSharesHint hint for the active shares checkpoint
*/
struct ActiveBalanceOfHints {
bytes activeSharesOfHint;
bytes activeStakeHint;
bytes activeSharesHint;
}
// Events
/**
* @notice Emitted when a deposit is made.
* @param depositor account that made the deposit
* @param onBehalfOf account the deposit was made on behalf of
* @param amount amount of the collateral deposited
* @param shares amount of the active shares minted
*/
event Deposit(address indexed depositor, address indexed onBehalfOf, uint256 amount, uint256 shares);
/**
* @notice Emitted when a withdrawal is made.
* @param withdrawer account that made the withdrawal
* @param claimer account that needs to claim the withdrawal
* @param amount amount of the collateral withdrawn
* @param burnedShares amount of the active shares burned
* @param mintedShares amount of the epoch withdrawal shares minted
*/
event Withdraw(
address indexed withdrawer, address indexed claimer, uint256 amount, uint256 burnedShares, uint256 mintedShares
);
/**
* @notice Emitted when a claim is made.
* @param claimer account that claimed
* @param recipient account that received the collateral
* @param epoch epoch the collateral was claimed for
* @param amount amount of the collateral claimed
*/
event Claim(address indexed claimer, address indexed recipient, uint256 epoch, uint256 amount);
/**
* @notice Emitted when a batch claim is made.
* @param claimer account that claimed
* @param recipient account that received the collateral
* @param epochs epochs the collateral was claimed for
* @param amount amount of the collateral claimed
*/
event ClaimBatch(address indexed claimer, address indexed recipient, uint256[] epochs, uint256 amount);
/**
* @notice Emitted when a slash happens.
* @param amount amount of the collateral to slash
* @param captureTimestamp time point when the stake was captured
* @param slashedAmount real amount of the collateral slashed
*/
event OnSlash(uint256 amount, uint48 captureTimestamp, uint256 slashedAmount);
/**
* @notice Emitted when a deposit whitelist status is enabled/disabled.
* @param status if enabled deposit whitelist
*/
event SetDepositWhitelist(bool status);
/**
* @notice Emitted when a depositor whitelist status is set.
* @param account account for which the whitelist status is set
* @param status if whitelisted the account
*/
event SetDepositorWhitelistStatus(address indexed account, bool status);
/**
* @notice Emitted when a deposit limit status is enabled/disabled.
* @param status if enabled deposit limit
*/
event SetIsDepositLimit(bool status);
/**
* @notice Emitted when a deposit limit is set.
* @param limit deposit limit (maximum amount of the collateral that can be in the vault simultaneously)
*/
event SetDepositLimit(uint256 limit);
/**
* @notice Emitted when a delegator is set.
* @param delegator vault's delegator to delegate the stake to networks and operators
* @dev Can be set only once.
*/
event SetDelegator(address indexed delegator);
/**
* @notice Emitted when a slasher is set.
* @param slasher vault's slasher to provide a slashing mechanism to networks
* @dev Can be set only once.
*/
event SetSlasher(address indexed slasher);
// Functions
// Ex IVaultStorage
/**
* @notice Get the delegator factory's address.
* @return address of the delegator factory
*/
function DELEGATOR_FACTORY() external view returns (address);
/**
* @notice Get the slasher factory's address.
* @return address of the slasher factory
*/
function SLASHER_FACTORY() external view returns (address);
/**
* @notice Get a vault collateral.
* @return address of the underlying collateral
*/
function collateral() external view returns (address);
/**
* @notice Get a burner to issue debt to (e.g., 0xdEaD or some unwrapper contract).
* @return address of the burner
*/
function burner() external view returns (address);
/**
* @notice Get a delegator (it delegates the vault's stake to networks and operators).
* @return address of the delegator
*/
function delegator() external view returns (address);
/**
* @notice Get if the delegator is initialized.
* @return if the delegator is initialized
*/
function isDelegatorInitialized() external view returns (bool);
/**
* @notice Get a slasher (it provides networks a slashing mechanism).
* @return address of the slasher
*/
function slasher() external view returns (address);
/**
* @notice Get if the slasher is initialized.
* @return if the slasher is initialized
*/
function isSlasherInitialized() external view returns (bool);
/**
* @notice Get a time point of the epoch duration set.
* @return time point of the epoch duration set
*/
function epochDurationInit() external view returns (uint48);
/**
* @notice Get a duration of the vault epoch.
* @return duration of the epoch
*/
function epochDuration() external view returns (uint48);
/**
* @notice Get an epoch at a given timestamp.
* @param timestamp time point to get the epoch at
* @return epoch at the timestamp
* @dev Reverts if the timestamp is less than the start of the epoch 0.
*/
function epochAt(
uint48 timestamp
) external view returns (uint256);
/**
* @notice Get a current vault epoch.
* @return current epoch
*/
function currentEpoch() external view returns (uint256);
/**
* @notice Get a start of the current vault epoch.
* @return start of the current epoch
*/
function currentEpochStart() external view returns (uint48);
/**
* @notice Get a start of the previous vault epoch.
* @return start of the previous epoch
* @dev Reverts if the current epoch is 0.
*/
function previousEpochStart() external view returns (uint48);
/**
* @notice Get a start of the next vault epoch.
* @return start of the next epoch
*/
function nextEpochStart() external view returns (uint48);
/**
* @notice Get if the deposit whitelist is enabled.
* @return if the deposit whitelist is enabled
*/
function depositWhitelist() external view returns (bool);
/**
* @notice Get if a given account is whitelisted as a depositor.
* @param account address to check
* @return if the account is whitelisted as a depositor
*/
function isDepositorWhitelisted(
address account
) external view returns (bool);
/**
* @notice Get if the deposit limit is set.
* @return if the deposit limit is set
*/
function isDepositLimit() external view returns (bool);
/**
* @notice Get a deposit limit (maximum amount of the active stake that can be in the vault simultaneously).
* @return deposit limit
*/
function depositLimit() external view returns (uint256);
/**
* @notice Get a total number of active shares in the vault at a given timestamp using a hint.
* @param timestamp time point to get the total number of active shares at
* @param hint hint for the checkpoint index
* @return total number of active shares at the timestamp
*/
function activeSharesAt(uint48 timestamp, bytes memory hint) external view returns (uint256);
/**
* @notice Get a total number of active shares in the vault.
* @return total number of active shares
*/
function activeShares() external view returns (uint256);
/**
* @notice Get a total amount of active stake in the vault at a given timestamp using a hint.
* @param timestamp time point to get the total active stake at
* @param hint hint for the checkpoint index
* @return total amount of active stake at the timestamp
*/
function activeStakeAt(uint48 timestamp, bytes memory hint) external view returns (uint256);
/**
* @notice Get a total amount of active stake in the vault.
* @return total amount of active stake
*/
function activeStake() external view returns (uint256);
/**
* @notice Get a total number of active shares for a particular account at a given timestamp using a hint.
* @param account account to get the number of active shares for
* @param timestamp time point to get the number of active shares for the account at
* @param hint hint for the checkpoint index
* @return number of active shares for the account at the timestamp
*/
function activeSharesOfAt(address account, uint48 timestamp, bytes memory hint) external view returns (uint256);
/**
* @notice Get a number of active shares for a particular account.
* @param account account to get the number of active shares for
* @return number of active shares for the account
*/
function activeSharesOf(
address account
) external view returns (uint256);
/**
* @notice Get a total amount of the withdrawals at a given epoch.
* @param epoch epoch to get the total amount of the withdrawals at
* @return total amount of the withdrawals at the epoch
*/
function withdrawals(
uint256 epoch
) external view returns (uint256);
/**
* @notice Get a total number of withdrawal shares at a given epoch.
* @param epoch epoch to get the total number of withdrawal shares at
* @return total number of withdrawal shares at the epoch
*/
function withdrawalShares(
uint256 epoch
) external view returns (uint256);
/**
* @notice Get a number of withdrawal shares for a particular account at a given epoch (zero if claimed).
* @param epoch epoch to get the number of withdrawal shares for the account at
* @param account account to get the number of withdrawal shares for
* @return number of withdrawal shares for the account at the epoch
*/
function withdrawalSharesOf(uint256 epoch, address account) external view returns (uint256);
/**
* @notice Get if the withdrawals are claimed for a particular account at a given epoch.
* @param epoch epoch to check the withdrawals for the account at
* @param account account to check the withdrawals for
* @return if the withdrawals are claimed for the account at the epoch
*/
function isWithdrawalsClaimed(uint256 epoch, address account) external view returns (bool);
//ex IVault
/**
* @notice Check if the vault is fully initialized (a delegator and a slasher are set).
* @return if the vault is fully initialized
*/
function isInitialized() external view returns (bool);
/**
* @notice Get a total amount of the collateral that can be slashed.
* @return total amount of the slashable collateral
*/
function totalStake() external view returns (uint256);
/**
* @notice Get an active balance for a particular account at a given timestamp using hints.
* @param account account to get the active balance for
* @param timestamp time point to get the active balance for the account at
* @param hints hints for checkpoints' indexes
* @return active balance for the account at the timestamp
*/
function activeBalanceOfAt(
address account,
uint48 timestamp,
bytes calldata hints
) external view returns (uint256);
/**
* @notice Get an active balance for a particular account.
* @param account account to get the active balance for
* @return active balance for the account
*/
function activeBalanceOf(
address account
) external view returns (uint256);
/**
* @notice Get withdrawals for a particular account at a given epoch (zero if claimed).
* @param epoch epoch to get the withdrawals for the account at
* @param account account to get the withdrawals for
* @return withdrawals for the account at the epoch
*/
function withdrawalsOf(uint256 epoch, address account) external view returns (uint256);
/**
* @notice Get a total amount of the collateral that can be slashed for a given account.
* @param account account to get the slashable collateral for
* @return total amount of the account's slashable collateral
*/
function slashableBalanceOf(
address account
) external view returns (uint256);
/**
* @notice Deposit collateral into the vault.
* @param onBehalfOf account the deposit is made on behalf of
* @param amount amount of the collateral to deposit
* @return depositedAmount real amount of the collateral deposited
* @return mintedShares amount of the active shares minted
*/
function deposit(
address onBehalfOf,
uint256 amount
) external returns (uint256 depositedAmount, uint256 mintedShares);
/**
* @notice Withdraw collateral from the vault (it will be claimable after the next epoch).
* @param claimer account that needs to claim the withdrawal
* @param amount amount of the collateral to withdraw
* @return burnedShares amount of the active shares burned
* @return mintedShares amount of the epoch withdrawal shares minted
*/
function withdraw(address claimer, uint256 amount) external returns (uint256 burnedShares, uint256 mintedShares);
/**
* @notice Redeem collateral from the vault (it will be claimable after the next epoch).
* @param claimer account that needs to claim the withdrawal
* @param shares amount of the active shares to redeem
* @return withdrawnAssets amount of the collateral withdrawn
* @return mintedShares amount of the epoch withdrawal shares minted
*/
function redeem(address claimer, uint256 shares) external returns (uint256 withdrawnAssets, uint256 mintedShares);
/**
* @notice Claim collateral from the vault.
* @param recipient account that receives the collateral
* @param epoch epoch to claim the collateral for
* @return amount amount of the collateral claimed
*/
function claim(address recipient, uint256 epoch) external returns (uint256 amount);
/**
* @notice Claim collateral from the vault for multiple epochs.
* @param recipient account that receives the collateral
* @param epochs epochs to claim the collateral for
* @return amount amount of the collateral claimed
*/
function claimBatch(address recipient, uint256[] calldata epochs) external returns (uint256 amount);
/**
* @notice Slash callback for burning collateral.
* @param amount amount to slash
* @param captureTimestamp time point when the stake was captured
* @return slashedAmount real amount of the collateral slashed
* @dev Only the slasher can call this function.
*/
function onSlash(uint256 amount, uint48 captureTimestamp) external returns (uint256 slashedAmount);
/**
* @notice Enable/disable deposit whitelist.
* @param status if enabling deposit whitelist
* @dev Only a DEPOSIT_WHITELIST_SET_ROLE holder can call this function.
*/
function setDepositWhitelist(
bool status
) external;
/**
* @notice Set a depositor whitelist status.
* @param account account for which the whitelist status is set
* @param status if whitelisting the account
* @dev Only a DEPOSITOR_WHITELIST_ROLE holder can call this function.
*/
function setDepositorWhitelistStatus(address account, bool status) external;
/**
* @notice Enable/disable deposit limit.
* @param status if enabling deposit limit
* @dev Only a IS_DEPOSIT_LIMIT_SET_ROLE holder can call this function.
*/
function setIsDepositLimit(
bool status
) external;
/**
* @notice Set a deposit limit.
* @param limit deposit limit (maximum amount of the collateral that can be in the vault simultaneously)
* @dev Only a DEPOSIT_LIMIT_SET_ROLE holder can call this function.
*/
function setDepositLimit(
uint256 limit
) external;
/**
* @notice Set a delegator.
* @param delegator vault's delegator to delegate the stake to networks and operators
* @dev Can be set only once.
*/
function setDelegator(
address delegator
) external;
/**
* @notice Set a slasher.
* @param slasher vault's slasher to provide a slashing mechanism to networks
* @dev Can be set only once.
*/
function setSlasher(
address slasher
) external;
/**
* @notice Make a delegatecall from this contract to a given target contract with a particular data (always reverts with a return data).
* @param target address of the contract to make a delegatecall to
* @param data data to make a delegatecall with
* @dev It allows to use this contract's storage on-chain.
*/
function staticDelegateCall(address target, bytes calldata data) external;
}// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity 0.8.25;
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IBaseDelegator} from "../../interfaces/delegator/IBaseDelegator.sol";
import {IDelegatorHook} from "../../interfaces/delegator/IDelegatorHook.sol";
import {IOptInService} from "../../interfaces/service/IOptInService.sol";
import {IRegistry} from "../../interfaces/common/IRegistry.sol";
import {IVaultTokenized} from "../../interfaces/vault/IVaultTokenized.sol";
import {IVaultFactory} from "../../interfaces/IVaultFactory.sol";
import {IL1Registry} from "../../interfaces/IL1Registry.sol";
import {IEntity} from "../../interfaces/common/IEntity.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
abstract contract BaseDelegator is AccessControlUpgradeable, ReentrancyGuardUpgradeable, IBaseDelegator, ERC165 {
using ERC165Checker for address;
/**
* @inheritdoc IBaseDelegator
*/
address public immutable FACTORY;
/**
* @inheritdoc IBaseDelegator
*/
uint64 public immutable TYPE;
/**
* @inheritdoc IBaseDelegator
*/
uint256 public constant HOOK_GAS_LIMIT = 250_000;
/**
* @inheritdoc IBaseDelegator
*/
uint256 public constant HOOK_RESERVE = 20_000;
/**
* @inheritdoc IBaseDelegator
*/
bytes32 public constant HOOK_SET_ROLE = keccak256("HOOK_SET_ROLE");
/**
* @inheritdoc IBaseDelegator
*/
address public immutable L1_REGISTRY;
/**
* @inheritdoc IBaseDelegator
*/
address public immutable VAULT_FACTORY;
/**
* @inheritdoc IBaseDelegator
*/
address public immutable OPERATOR_VAULT_OPT_IN_SERVICE;
/**
* @inheritdoc IBaseDelegator
*/
address public immutable OPERATOR_L1_OPT_IN_SERVICE;
/**
* @inheritdoc IBaseDelegator
*/
address public vault;
/**
* @inheritdoc IBaseDelegator
*/
address public hook;
/**
* @inheritdoc IBaseDelegator
*/
mapping(address => mapping(uint96 => uint256)) public maxL1Limit;
constructor(
address l1Registry,
address vaultFactory,
address operatorVaultOptInService,
address operatorL1OptInService,
address delegatorFactory,
uint64 entityType
) {
if (l1Registry == address(0)) {
revert BaseDelegator__ZeroAddress("l1Registry");
}
if (vaultFactory == address(0)) {
revert BaseDelegator__ZeroAddress("vaultFactory");
}
if (operatorVaultOptInService == address(0)) {
revert BaseDelegator__ZeroAddress("operatorVaultOptInService");
}
if (operatorL1OptInService == address(0)) {
revert BaseDelegator__ZeroAddress("operatorL1OptInService");
}
if (delegatorFactory == address(0)) {
revert BaseDelegator__ZeroAddress("delegatorFactory");
}
_disableInitializers();
L1_REGISTRY = l1Registry;
VAULT_FACTORY = vaultFactory;
OPERATOR_VAULT_OPT_IN_SERVICE = operatorVaultOptInService;
OPERATOR_L1_OPT_IN_SERVICE = operatorL1OptInService;
FACTORY = delegatorFactory;
TYPE = entityType;
}
/**
* @inheritdoc IBaseDelegator
*/
function VERSION() external pure returns (uint64) {
return 1;
}
/**
* @inheritdoc IBaseDelegator
*/
function stakeAt(
address l1,
uint96 assetClass,
address operator,
uint48 timestamp,
bytes memory hints
) public view returns (uint256) {
(uint256 stake_, bytes memory baseHints) = _stakeAt(l1, assetClass, operator, timestamp, hints);
StakeBaseHints memory stakeBaseHints;
if (baseHints.length > 0) {
stakeBaseHints = abi.decode(baseHints, (StakeBaseHints));
}
if (
stake_ == 0
|| !IOptInService(OPERATOR_VAULT_OPT_IN_SERVICE).isOptedInAt(
operator, vault, timestamp, stakeBaseHints.operatorVaultOptInHint
)
|| !IOptInService(OPERATOR_L1_OPT_IN_SERVICE).isOptedInAt(
operator, l1, timestamp, stakeBaseHints.operatorL1OptInHint
)
) {
return 0;
}
return stake_;
}
/**
* @inheritdoc IBaseDelegator
*/
function stake(address l1, uint96 assetClass, address operator) external view returns (uint256) {
if (
!IOptInService(OPERATOR_VAULT_OPT_IN_SERVICE).isOptedIn(operator, vault)
|| !IOptInService(OPERATOR_L1_OPT_IN_SERVICE).isOptedIn(operator, l1)
) {
return 0;
}
return _stake(l1, assetClass, operator);
}
/**
* @inheritdoc IBaseDelegator
*/
function setMaxL1Limit(address l1, uint96 assetClass, uint256 amount) external nonReentrant {
if (!IL1Registry(L1_REGISTRY).isRegistered(l1)) {
revert BaseDelegator__NotL1();
}
// Check if the middleware is registered and matches the caller
bool isRegisteredWithMiddleware = IL1Registry(L1_REGISTRY).isRegisteredWithMiddleware(l1, msg.sender);
if (!isRegisteredWithMiddleware) {
revert BaseDelegator__NotAuthorizedMiddleware();
}
uint256 currentLimit = maxL1Limit[l1][assetClass];
if (currentLimit == amount) {
revert BaseDelegator__AlreadySet();
}
maxL1Limit[l1][assetClass] = amount;
_setMaxL1Limit(l1, assetClass, amount);
emit SetMaxL1Limit(l1, assetClass, amount);
}
/**
* @inheritdoc IBaseDelegator
*/
function setHook(
address hook_
) external nonReentrant onlyRole(HOOK_SET_ROLE) {
if (hook == hook_) {
revert BaseDelegator__AlreadySet();
}
hook = hook_;
emit SetHook(hook_);
}
/**
* @inheritdoc IBaseDelegator
*/
function onSlash(
address l1,
uint96 assetClass,
address operator,
uint256 amount,
uint48 captureTimestamp,
bytes memory data
) external nonReentrant {
if (msg.sender != IVaultTokenized(vault).slasher()) {
revert IVaultTokenized.Vault__NotSlasher();
}
address hook_ = hook;
if (hook_ != address(0)) {
bytes memory calldata_ =
abi.encodeCall(IDelegatorHook.onSlash, (l1, assetClass, operator, amount, captureTimestamp, data));
if (gasleft() < HOOK_RESERVE + HOOK_GAS_LIMIT * 64 / 63) {
revert BaseDelegator__InsufficientHookGas();
}
assembly ("memory-safe") {
pop(call(HOOK_GAS_LIMIT, hook_, 0, add(calldata_, 0x20), mload(calldata_), 0, 0))
}
}
emit OnSlash(l1, assetClass, operator, amount, captureTimestamp);
}
/**
* @inheritdoc IBaseDelegator
*/
function initialize(
bytes calldata data
) external initializer {
_initialize(data);
}
function _initialize(
bytes calldata data
) internal {
(address vault_, bytes memory data_) = abi.decode(data, (address, bytes));
if (!IRegistry(VAULT_FACTORY).isEntity(vault_)) {
revert BaseDelegator__NotVault();
}
__AccessControl_init();
__ReentrancyGuard_init();
vault = vault_;
BaseParams memory baseParams = __initialize(vault_, data_);
hook = baseParams.hook;
if (baseParams.defaultAdminRoleHolder != address(0)) {
_grantRole(DEFAULT_ADMIN_ROLE, baseParams.defaultAdminRoleHolder);
}
if (baseParams.hookSetRoleHolder != address(0)) {
_grantRole(HOOK_SET_ROLE, baseParams.hookSetRoleHolder);
}
}
function _stakeAt(
address l1,
uint96 assetClass,
address operator,
uint48 timestamp,
bytes memory hints
) internal view virtual returns (uint256, bytes memory);
function _stake(address l1, uint96 assetClass, address operator) internal view virtual returns (uint256);
function _setMaxL1Limit(address l1, uint96 assetClass, uint256 amount) internal virtual;
function __initialize(address vault_, bytes memory data) internal virtual returns (BaseParams memory);
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC165, AccessControlUpgradeable) returns (bool) {
return interfaceId == type(IEntity).interfaceId || super.supportsInterface(interfaceId);
}
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
import {IBaseSlasher} from "./IBaseSlasher.sol";
interface ISlasher is IBaseSlasher {
error InsufficientSlash();
error InvalidCaptureTimestamp();
/**
* @notice Initial parameters needed for a slasher deployment.
* @param baseParams base parameters for slashers' deployment
*/
struct InitParams {
IBaseSlasher.BaseParams baseParams;
}
/**
* @notice Hints for a slash.
* @param slashableStakeHints hints for the slashable stake checkpoints
*/
struct SlashHints {
bytes slashableStakeHints;
}
/**
* @notice Extra data for the delegator.
* @param slashableStake amount of the slashable stake before the slash (cache)
* @param stakeAt amount of the stake at the capture time (cache)
*/
struct DelegatorData {
uint256 slashableStake;
uint256 stakeAt;
}
/**
* @notice Emitted when a slash is performed.
* @param l1 address of the l1.
* @param assetClass the uint96 assetClass.
* @param operator operator that is slashed
* @param slashedAmount virtual amount of the collateral slashed
* @param captureTimestamp time point when the stake was captured
*/
event Slash(
address indexed l1,
uint96 indexed assetClass,
address indexed operator,
uint256 slashedAmount,
uint48 captureTimestamp
);
/**
* @notice Perform a slash using a subnetwork for a particular operator by a given amount using hints.
* @param l1 address of the l1
* @param assetClass the uint96 assetClass
* @param operator address of the operator
* @param amount maximum amount of the collateral to be slashed
* @param captureTimestamp time point when the stake was captured
* @param hints hints for checkpoints' indexes
* @return slashedAmount virtual amount of the collateral slashed
* @dev Only a network middleware can call this function.
*/
function slash(
address l1,
uint96 assetClass,
address operator,
uint256 amount,
uint48 captureTimestamp,
bytes calldata hints
) external returns (uint256 slashedAmount);
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
import {IBaseSlasher} from "./IBaseSlasher.sol";
interface IVetoSlasher is IBaseSlasher {
error AlreadySet();
error InsufficientSlash();
error InvalidCaptureTimestamp();
error InvalidResolverSetEpochsDelay();
error InvalidVetoDuration();
error NoResolver();
error NotNetwork();
error NotResolver();
error SlashPeriodEnded();
error SlashRequestCompleted();
error SlashRequestNotExist();
error VetoPeriodEnded();
error VetoPeriodNotEnded();
/**
* @notice Initial parameters needed for a slasher deployment.
* @param baseParams base parameters for slashers' deployment
* @param vetoDuration duration of the veto period for a slash request
* @param resolverSetEpochsDelay delay in epochs for a network to update a resolver
*/
struct InitParams {
IBaseSlasher.BaseParams baseParams;
uint48 vetoDuration;
uint256 resolverSetEpochsDelay;
}
/**
* @notice Structure for a slash request.
* @param l1 address of the l1
* @param assetClass the uint96 assetClass
* @param operator operator that could be slashed (if the request is not vetoed)
* @param amount maximum amount of the collateral to be slashed
* @param captureTimestamp time point when the stake was captured
* @param vetoDeadline deadline for the resolver to veto the slash (exclusively)
* @param completed if the slash was vetoed/executed
*/
struct SlashRequest {
address l1;
uint96 assetClass;
address operator;
uint256 amount;
uint48 captureTimestamp;
uint48 vetoDeadline;
bool completed;
}
/**
* @notice Hints for a slash request.
* @param slashableStakeHints hints for the slashable stake checkpoints
*/
struct RequestSlashHints {
bytes slashableStakeHints;
}
/**
* @notice Hints for a slash execute.
* @param captureResolverHint hint for the resolver checkpoint at the capture time
* @param currentResolverHint hint for the resolver checkpoint at the current time
* @param slashableStakeHints hints for the slashable stake checkpoints
*/
struct ExecuteSlashHints {
bytes captureResolverHint;
bytes currentResolverHint;
bytes slashableStakeHints;
}
/**
* @notice Hints for a slash veto.
* @param captureResolverHint hint for the resolver checkpoint at the capture time
* @param currentResolverHint hint for the resolver checkpoint at the current time
*/
struct VetoSlashHints {
bytes captureResolverHint;
bytes currentResolverHint;
}
/**
* @notice Hints for a resolver set.
* @param resolverHint hint for the resolver checkpoint
*/
struct SetResolverHints {
bytes resolverHint;
}
/**
* @notice Extra data for the delegator.
* @param slashableStake amount of the slashable stake before the slash (cache)
* @param stakeAt amount of the stake at the capture time (cache)
* @param slashIndex index of the slash request
*/
struct DelegatorData {
uint256 slashableStake;
uint256 stakeAt;
uint256 slashIndex;
}
/**
* @notice Emitted when a slash request is created.
* @param slashIndex index of the slash request
* @param l1 address of the l1
* @param assetClass the uint96 assetClass
* @param operator operator that could be slashed (if the request is not vetoed)
* @param slashAmount maximum amount of the collateral to be slashed
* @param captureTimestamp time point when the stake was captured
* @param vetoDeadline deadline for the resolver to veto the slash (exclusively)
*/
event RequestSlash(
uint256 indexed slashIndex,
address l1,
uint96 assetClass,
address indexed operator,
uint256 slashAmount,
uint48 captureTimestamp,
uint48 vetoDeadline
);
/**
* @notice Emitted when a slash request is executed.
* @param slashIndex index of the slash request
* @param slashedAmount virtual amount of the collateral slashed
*/
event ExecuteSlash(uint256 indexed slashIndex, uint256 slashedAmount);
/**
* @notice Emitted when a slash request is vetoed.
* @param slashIndex index of the slash request
* @param resolver address of the resolver that vetoed the slash
*/
event VetoSlash(uint256 indexed slashIndex, address indexed resolver);
/**
* @notice Emitted when a resolver is set.
* @param l1 address of the l1
* @param assetClass the uint96 assetClass
* @param resolver address of the resolver
*/
event SetResolver(address indexed l1, uint96 indexed assetClass, address resolver);
/**
* @notice Get the network registry's address.
* @return address of the network registry
*/
function NETWORK_REGISTRY() external view returns (address);
/**
* @notice Get a duration during which resolvers can veto slash requests.
* @return duration of the veto period
*/
function vetoDuration() external view returns (uint48);
/**
* @notice Get a total number of slash requests.
* @return total number of slash requests
*/
function slashRequestsLength() external view returns (uint256);
/**
* @notice Get a particular slash request.
* @param slashIndex index of the slash request
* @return l1 address of the l1
* @return assetClass the uint96 assetClass
* @return operator operator that could be slashed (if the request is not vetoed)
* @return amount maximum amount of the collateral to be slashed
* @return captureTimestamp time point when the stake was captured
* @return vetoDeadline deadline for the resolver to veto the slash (exclusively)
* @return completed if the slash was vetoed/executed
*/
function slashRequests(
uint256 slashIndex
)
external
view
returns (
address l1,
uint96 assetClass,
address operator,
uint256 amount,
uint48 captureTimestamp,
uint48 vetoDeadline,
bool completed
);
/**
* @notice Get a delay for networks in epochs to update a resolver.
* @return updating resolver delay in epochs
*/
function resolverSetEpochsDelay() external view returns (uint256);
/**
* @notice Get a resolver for a given assset class at a particular timestamp using a hint.
* @param l1 address of the l1
* @param assetClass the uint96 assetClass
* @param timestamp timestamp to get the resolver at
* @param hint hint for the checkpoint index
* @return address of the resolver
*/
function resolverAt(
address l1,
uint96 assetClass,
uint48 timestamp,
bytes memory hint
) external view returns (address);
/**
* @notice Get a resolver for a given assset class using a hint.
* @param l1 address of the l1
* @param assetClass the uint96 assetClass
* @param hint hint for the checkpoint index
* @return address of the resolver
*/
function resolver(address l1, uint96 assetClass, bytes memory hint) external view returns (address);
/**
* @notice Request a slash using a assset class for a particular operator by a given amount using hints.
* @param l1 address of the l1
* @param assetClass the uint96 assetClass
* @param operator address of the operator
* @param amount maximum amount of the collateral to be slashed
* @param captureTimestamp time point when the stake was captured
* @param hints hints for checkpoints' indexes
* @return slashIndex index of the slash request
* @dev Only a network middleware can call this function.
*/
function requestSlash(
address l1,
uint96 assetClass,
address operator,
uint256 amount,
uint48 captureTimestamp,
bytes calldata hints
) external returns (uint256 slashIndex);
/**
* @notice Execute a slash with a given slash index using hints.
* @param slashIndex index of the slash request
* @param hints hints for checkpoints' indexes
* @return slashedAmount virtual amount of the collateral slashed
* @dev Only a network middleware can call this function.
*/
function executeSlash(uint256 slashIndex, bytes calldata hints) external returns (uint256 slashedAmount);
/**
* @notice Veto a slash with a given slash index using hints.
* @param slashIndex index of the slash request
* @param hints hints for checkpoints' indexes
* @dev Only a resolver can call this function.
*/
function vetoSlash(uint256 slashIndex, bytes calldata hints) external;
/**
* @notice Set a resolver for a assset class using hints.
* identifier identifier of the assset class
* @param resolver address of the resolver
* @param hints hints for checkpoints' indexes
* @dev Only a network can call this function.
*/
function setResolver(uint96 identifier, address resolver, bytes calldata hints) external;
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.25;
import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol";
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
library MapWithTimeData {
using EnumerableMap for EnumerableMap.AddressToUintMap;
error MapWithTimeData__AlreadyAdded();
error MapWithTimeData__NotEnabled();
error MapWithTimeData__AlreadyEnabled();
error MapWithTimeData__AlreadyDisabled();
function add(EnumerableMap.AddressToUintMap storage self, address addr) internal {
if (!self.set(addr, uint256(0))) {
revert MapWithTimeData__AlreadyAdded();
}
}
function disable(EnumerableMap.AddressToUintMap storage self, address addr) internal {
uint256 value = self.get(addr);
uint48 enabledTime = uint48(value);
uint48 disabledTime = uint48(value >> 48);
if (enabledTime == 0) {
revert MapWithTimeData__NotEnabled();
}
if (disabledTime != 0) {
revert MapWithTimeData__AlreadyDisabled();
}
value |= uint256(Time.timestamp()) << 48;
self.set(addr, value);
}
function enable(EnumerableMap.AddressToUintMap storage self, address addr) internal {
uint256 value = self.get(addr);
if (uint48(value) != 0 && uint48(value >> 48) == 0) {
revert MapWithTimeData__AlreadyEnabled();
}
value = uint256(Time.timestamp());
self.set(addr, value);
}
function atWithTimes(
EnumerableMap.AddressToUintMap storage self,
uint256 idx
) internal view returns (address key, uint48 enabledTime, uint48 disabledTime) {
uint256 value;
(key, value) = self.at(idx);
enabledTime = uint48(value);
disabledTime = uint48(value >> 48);
}
function getTimes(
EnumerableMap.AddressToUintMap storage self,
address addr
) internal view returns (uint48 enabledTime, uint48 disabledTime) {
uint256 value = self.get(addr);
enabledTime = uint48(value);
disabledTime = uint48(value >> 48);
}
}// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity 0.8.25;
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {
Validator,
ValidatorStatus,
ValidatorRegistrationInput,
PChainOwner
} from "@avalabs/teleporter/validator-manager/interfaces/IValidatorManager.sol";
import {BalancerValidatorManager} from
"@suzaku/contracts-library/contracts/ValidatorManager/BalancerValidatorManager.sol";
import {IOperatorRegistry} from "../../interfaces/IOperatorRegistry.sol";
import {IVaultTokenized} from "../../interfaces/vault/IVaultTokenized.sol";
import {IAvalancheL1Middleware} from "../../interfaces/middleware/IAvalancheL1Middleware.sol";
import {IOptInService} from "../../interfaces/service/IOptInService.sol";
import {AssetClassRegistry} from "./AssetClassRegistry.sol";
import {MiddlewareVaultManager} from "./MiddlewareVaultManager.sol";
import {MapWithTimeData} from "./libraries/MapWithTimeData.sol";
import {StakeConversion} from "./libraries/StakeConversion.sol";
import {BaseDelegator} from "../../contracts/delegator/BaseDelegator.sol";
struct AvalancheL1MiddlewareSettings {
address l1ValidatorManager;
address operatorRegistry;
address vaultRegistry;
address operatorL1Optin;
uint48 epochDuration;
uint48 slashingWindow;
uint48 stakeUpdateWindow;
}
/**
* @title AvalancheL1Middleware
* @notice Manages operator registration, vault registration, stake accounting, and slashing for Avalanche L1
*/
contract AvalancheL1Middleware is IAvalancheL1Middleware, AssetClassRegistry {
using EnumerableMap for EnumerableMap.AddressToUintMap;
using EnumerableSet for EnumerableSet.UintSet;
using MapWithTimeData for EnumerableMap.AddressToUintMap;
using EnumerableMap for EnumerableMap.Bytes32ToUintMap;
using EnumerableSet for EnumerableSet.Bytes32Set;
address public immutable L1_VALIDATOR_MANAGER;
address public immutable OPERATOR_REGISTRY;
address public immutable OPERATOR_L1_OPTIN;
address public immutable PRIMARY_ASSET;
uint48 public immutable EPOCH_DURATION;
uint48 public immutable SLASHING_WINDOW;
uint48 public immutable START_TIME;
uint48 public immutable UPDATE_WINDOW;
uint256 public immutable WEIGHT_SCALE_FACTOR;
uint48 public lastGlobalNodeStakeUpdateEpoch;
uint96 public constant PRIMARY_ASSET_CLASS = 1;
uint48 public constant MAX_AUTO_EPOCH_UPDATES = 1;
MiddlewareVaultManager private vaultManager;
EnumerableMap.AddressToUintMap private operators;
EnumerableSet.UintSet private secondaryAssetClasses;
BalancerValidatorManager public balancerValidatorManager;
mapping(address => mapping(uint48 => bool)) public rebalancedThisEpoch;
mapping(uint48 => mapping(uint96 => uint256)) public totalStakeCache;
mapping(address => bytes32[]) public operatorNodesArray;
mapping(uint48 => mapping(uint96 => mapping(address => uint256))) public operatorStakeCache;
mapping(uint48 => mapping(bytes32 => uint256)) public nodeStakeCache;
mapping(bytes32 => bool) public nodePendingUpdate;
mapping(bytes32 => bool) public nodePendingRemoval;
mapping(address => uint256) public operatorLockedStake;
mapping(uint48 => mapping(uint96 => bool)) public totalStakeCached;
// operatorNodesArray[operator] is used for iteration during certain
// rebalancing or node-update operations, and has nodes removed once
// they are effectively retired. This means a node can remain in
// operatorNodes while it is removed from operatorNodesArray.
// operatorNodes[operator] is intended as a permanent record of all nodes
// ever registered by the operator, used for historical/epoch-based queries.
// We do *not* remove nodes from this set when they are "retired" so
// getActiveNodesForEpoch(...) can still detect them for past epochs.
mapping(address => EnumerableSet.Bytes32Set) private operatorNodes;
/**
* @notice Initializes contract settings
* @param settings General contract parameters
* @param owner Owner address
* @param primaryAsset The primary asset address
* @param primaryAssetMaxStake Max stake for the primary asset class
* @param primaryAssetMinStake Min stake for the primary asset class
*/
constructor(
AvalancheL1MiddlewareSettings memory settings,
address owner,
address primaryAsset,
uint256 primaryAssetMaxStake,
uint256 primaryAssetMinStake,
uint256 primaryAssetWeightScaleFactor
) AssetClassRegistry(owner) {
if (settings.l1ValidatorManager == address(0)) {
revert AvalancheL1Middleware__ZeroAddress("l1ValidatorManager");
}
if (settings.operatorRegistry == address(0)) {
revert AvalancheL1Middleware__ZeroAddress("operatorRegistry");
}
if (settings.vaultRegistry == address(0)) {
revert AvalancheL1Middleware__ZeroAddress("vaultRegistry");
}
if (settings.operatorL1Optin == address(0)) {
revert AvalancheL1Middleware__ZeroAddress("operatorL1Optin");
}
if (owner == address(0)) {
revert AvalancheL1Middleware__ZeroAddress("owner");
}
if (primaryAsset == address(0)) {
revert AvalancheL1Middleware__ZeroAddress("primaryAsset");
}
if (settings.slashingWindow < settings.epochDuration) {
revert AvalancheL1Middleware__SlashingWindowTooShort(settings.slashingWindow, settings.epochDuration);
}
if (primaryAssetWeightScaleFactor == 0) {
revert AvalancheL1Middleware__InvalidScaleFactor();
}
START_TIME = Time.timestamp();
EPOCH_DURATION = settings.epochDuration;
L1_VALIDATOR_MANAGER = settings.l1ValidatorManager;
OPERATOR_REGISTRY = settings.operatorRegistry;
OPERATOR_L1_OPTIN = settings.operatorL1Optin;
SLASHING_WINDOW = settings.slashingWindow;
PRIMARY_ASSET = primaryAsset;
UPDATE_WINDOW = settings.stakeUpdateWindow;
WEIGHT_SCALE_FACTOR = primaryAssetWeightScaleFactor;
balancerValidatorManager = BalancerValidatorManager(settings.l1ValidatorManager);
_addAssetClass(PRIMARY_ASSET_CLASS, primaryAssetMinStake, primaryAssetMaxStake, PRIMARY_ASSET);
}
/**
* @notice Updates stake cache before function execution
* @param epoch The epoch to update
* @param assetClassId The asset class ID
*/
modifier updateStakeCache(uint48 epoch, uint96 assetClassId) {
if (!totalStakeCached[epoch][assetClassId]) {
calcAndCacheStakes(epoch, assetClassId);
}
_;
}
/**
* @notice Window where a node update can be done manually, before the force update can be applied
*/
modifier onlyDuringFinalWindowOfEpoch() {
uint48 currentEpoch = getCurrentEpoch();
uint48 epochStartTs = getEpochStartTs(currentEpoch);
uint48 timeNow = Time.timestamp();
uint48 epochUpdatePeriod = epochStartTs + UPDATE_WINDOW;
if (timeNow < epochUpdatePeriod || timeNow > epochStartTs + EPOCH_DURATION) {
revert AvalancheL1Middleware__NotEpochUpdatePeriod(timeNow, epochUpdatePeriod);
}
_;
}
modifier onlyRegisteredOperatorNode(address operator, bytes32 nodeId) {
if (!operators.contains(operator)) {
revert AvalancheL1Middleware__OperatorNotRegistered(operator);
}
if (!operatorNodes[operator].contains(nodeId)) {
revert AvalancheL1Middleware__NodeNotFound(nodeId);
}
_;
}
modifier updateGlobalNodeStakeOncePerEpoch() {
uint48 current = getCurrentEpoch();
if (current > lastGlobalNodeStakeUpdateEpoch) {
calcAndCacheNodeStakeForAllOperators();
lastGlobalNodeStakeUpdateEpoch = current;
}
_;
}
function setVaultManager(
address vaultManager_
) external onlyOwner {
if (vaultManager_ == address(0)) {
revert AvalancheL1Middleware__ZeroAddress("vaultManager");
}
emit VaultManagerUpdated(address(vaultManager), vaultManager_);
vaultManager = MiddlewareVaultManager(vaultManager_);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function activateSecondaryAssetClass(
uint256 assetClassId
) external onlyOwner updateGlobalNodeStakeOncePerEpoch {
if (!assetClassIds.contains(assetClassId)) {
revert AssetClassRegistry__AssetClassNotFound();
}
if (assetClassId == PRIMARY_ASSET_CLASS) {
revert AssetClassRegistry__AssetClassAlreadyExists();
}
bool added = secondaryAssetClasses.add(assetClassId);
if (!added) {
revert AssetClassRegistry__AssetClassAlreadyExists();
}
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function deactivateSecondaryAssetClass(
uint256 assetClassId
) external onlyOwner updateGlobalNodeStakeOncePerEpoch {
if (_isUsedAssetClass(assetClassId)) {
revert AvalancheL1Middleware__AssetStillInUse(assetClassId);
}
bool removed = secondaryAssetClasses.remove(assetClassId);
if (!removed) {
revert AssetClassRegistry__AssetClassNotFound();
}
}
/**
* @notice Removes an asset from an asset class, except primary asset
* @param assetClassId The ID of the asset class
* @param asset The address of the asset to remove
*/
function removeAssetFromClass(
uint256 assetClassId,
address asset
) public override onlyOwner updateGlobalNodeStakeOncePerEpoch {
if (assetClassId == 1 && asset == PRIMARY_ASSET) {
revert AssetClassRegistry__AssetIsPrimaryAssetClass(assetClassId);
}
if (_isUsedAsset(assetClassId, asset)) {
revert AvalancheL1Middleware__AssetStillInUse(assetClassId);
}
super.removeAssetFromClass(assetClassId, asset);
}
/**
* @notice Removes an asset class
* @param assetClassId The asset class ID
*/
function removeAssetClass(
uint256 assetClassId
) public override updateGlobalNodeStakeOncePerEpoch {
if (secondaryAssetClasses.contains(assetClassId)) {
revert AvalancheL1Middleware__ActiveSecondaryAssetCLass(assetClassId);
}
super.removeAssetClass(assetClassId);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function registerOperator(
address operator
) external onlyOwner updateGlobalNodeStakeOncePerEpoch {
if (operators.contains(operator)) {
revert AvalancheL1Middleware__OperatorAlreadyRegistered(operator);
}
if (!IOperatorRegistry(OPERATOR_REGISTRY).isRegistered(operator)) {
revert AvalancheL1Middleware__OperatorNotRegistered(operator);
}
if (!IOptInService(OPERATOR_L1_OPTIN).isOptedIn(operator, L1_VALIDATOR_MANAGER)) {
revert AvalancheL1Middleware__OperatorNotOptedIn(operator, L1_VALIDATOR_MANAGER);
}
operators.add(operator);
operators.enable(operator);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function disableOperator(
address operator
) external onlyOwner updateGlobalNodeStakeOncePerEpoch {
operators.disable(operator);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function enableOperator(
address operator
) external onlyOwner updateGlobalNodeStakeOncePerEpoch {
operators.enable(operator);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function removeOperator(
address operator
) external onlyOwner updateGlobalNodeStakeOncePerEpoch {
(, uint48 disabledTime) = operators.getTimes(operator);
if (disabledTime == 0 || disabledTime + SLASHING_WINDOW > Time.timestamp()) {
revert AvalancheL1Middleware__OperatorGracePeriodNotPassed(disabledTime, SLASHING_WINDOW);
}
operators.remove(operator);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function addNode(
bytes32 nodeId,
bytes calldata blsKey,
uint64 registrationExpiry,
PChainOwner calldata remainingBalanceOwner,
PChainOwner calldata disableOwner,
uint256 stakeAmount // optional
) external updateStakeCache(getCurrentEpoch(), PRIMARY_ASSET_CLASS) updateGlobalNodeStakeOncePerEpoch {
address operator = msg.sender;
if (!operators.contains(operator)) {
revert AvalancheL1Middleware__OperatorNotRegistered(operator);
}
if (!_requireMinSecondaryAssetClasses(1, operator)) {
revert AvalancheL1Middleware__NotEnoughFreeStakeSecondaryAssetClasses();
}
bytes32 valId = balancerValidatorManager.registeredValidators(abi.encodePacked(uint160(uint256(nodeId))));
uint256 available = _getOperatorAvailableStake(operator);
if (nodePendingRemoval[valId]) revert AvalancheL1Middleware__NodePendingRemoval(nodeId);
if (nodePendingUpdate[valId]) revert AvalancheL1Middleware__NodePendingUpdate(nodeId);
uint256 minStake = assetClasses[PRIMARY_ASSET_CLASS].minValidatorStake;
uint256 maxStake = assetClasses[PRIMARY_ASSET_CLASS].maxValidatorStake;
uint256 newStake = (stakeAmount != 0) ? stakeAmount : available;
newStake = (newStake > maxStake) ? maxStake : newStake;
if (newStake < minStake || newStake > available) {
revert AvalancheL1Middleware__NotEnoughFreeStake(newStake);
}
ValidatorRegistrationInput memory input = ValidatorRegistrationInput({
nodeID: abi.encodePacked(uint160(uint256(nodeId))),
blsPublicKey: blsKey,
registrationExpiry: registrationExpiry,
remainingBalanceOwner: remainingBalanceOwner,
disableOwner: disableOwner
});
// Track node in our time-based map and dynamic array.
operatorNodes[operator].add(nodeId);
operatorNodesArray[operator].push(nodeId);
uint48 epoch = getCurrentEpoch();
bytes32 validationID = balancerValidatorManager.initializeValidatorRegistration(
input, StakeConversion.stakeToWeight(newStake, WEIGHT_SCALE_FACTOR)
);
nodeStakeCache[epoch][validationID] = newStake;
nodeStakeCache[epoch + 1][validationID] = newStake;
nodePendingUpdate[validationID] = true;
emit NodeAdded(operator, nodeId, newStake, validationID);
}
function removeNode(
bytes32 nodeId
) external updateGlobalNodeStakeOncePerEpoch onlyRegisteredOperatorNode(msg.sender, nodeId) {
_removeNode(msg.sender, nodeId);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function forceUpdateNodes(
address operator,
uint256 limitStake
)
external
updateStakeCache(getCurrentEpoch(), PRIMARY_ASSET_CLASS)
onlyDuringFinalWindowOfEpoch
updateGlobalNodeStakeOncePerEpoch
{
uint48 currentEpoch = getCurrentEpoch();
if (rebalancedThisEpoch[operator][currentEpoch]) {
revert AvalancheL1Middleware__AlreadyRebalanced(operator, currentEpoch);
}
rebalancedThisEpoch[operator][currentEpoch] = true;
if (!operators.contains(operator)) {
revert AvalancheL1Middleware__OperatorNotRegistered(operator);
}
// Calculate the new total stake for the operator and compare it to the registered stake
uint256 newTotalStake = _getOperatorAvailableStake(operator);
uint256 registeredStake = getOperatorUsedStakeCached(operator);
uint256 leftoverStake;
bytes32[] storage nodesArr = operatorNodesArray[operator];
uint256 length = nodesArr.length;
// If nothing changed, do nothing
if (newTotalStake == registeredStake) {
return;
}
if (newTotalStake > registeredStake) {
leftoverStake = newTotalStake - registeredStake;
emit OperatorHasLeftoverStake(operator, leftoverStake);
emit AllNodeStakesUpdated(operator, newTotalStake);
return;
}
// We only handle the scenario newTotalStake < registeredStake, when removing stake
leftoverStake = registeredStake - newTotalStake;
for (uint256 i = length; i > 0 && leftoverStake > 0;) {
i--;
bytes32 nodeId = nodesArr[i];
bytes32 valID = balancerValidatorManager.registeredValidators(abi.encodePacked(uint160(uint256(nodeId))));
if (balancerValidatorManager.isValidatorPendingWeightUpdate(valID)) {
continue;
}
Validator memory validator = balancerValidatorManager.getValidator(valID);
if (validator.status != ValidatorStatus.Active) {
continue;
}
uint256 previousStake = getEffectiveNodeStake(currentEpoch, valID);
// Remove stake
if (previousStake == 0) {
continue;
}
uint256 stakeToRemove = leftoverStake < previousStake ? leftoverStake : previousStake;
if (limitStake > 0 && stakeToRemove > limitStake) {
stakeToRemove = limitStake;
}
uint256 newStake = previousStake - stakeToRemove;
leftoverStake -= stakeToRemove;
if (
(newStake < assetClasses[PRIMARY_ASSET_CLASS].minValidatorStake)
|| !_requireMinSecondaryAssetClasses(0, operator)
) {
newStake = 0;
_initializeEndValidationAndFlag(operator, valID, nodeId);
} else {
_initializeValidatorStakeUpdate(operator, valID, newStake);
emit NodeStakeUpdated(operator, nodeId, newStake, valID);
}
}
// Finally emit updated stake
emit AllNodeStakesUpdated(operator, newTotalStake);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function initializeValidatorStakeUpdate(
bytes32 nodeId,
uint256 stakeAmount
) external updateGlobalNodeStakeOncePerEpoch {
if (!operatorNodes[msg.sender].contains(nodeId)) {
revert AvalancheL1Middleware__NodeNotFound(nodeId);
}
uint256 minStake = assetClasses[PRIMARY_ASSET_CLASS].minValidatorStake;
uint256 maxStake = assetClasses[PRIMARY_ASSET_CLASS].maxValidatorStake;
if (stakeAmount > maxStake) {
revert AvalancheL1Middleware__StakeTooHigh(stakeAmount, maxStake);
}
if (stakeAmount < minStake) {
revert AvalancheL1Middleware__StakeTooLow(stakeAmount, minStake);
}
bytes32 validationID = balancerValidatorManager.registeredValidators(abi.encodePacked(uint160(uint256(nodeId))));
_initializeValidatorStakeUpdate(msg.sender, validationID, stakeAmount);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function completeValidatorRegistration(
address operator,
bytes32 nodeId,
uint32 messageIndex
) external updateGlobalNodeStakeOncePerEpoch {
_completeValidatorRegistration(operator, nodeId, messageIndex);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function completeStakeUpdate(
bytes32 nodeId,
uint32 messageIndex
) external onlyRegisteredOperatorNode(msg.sender, nodeId) updateGlobalNodeStakeOncePerEpoch {
_completeStakeUpdate(msg.sender, nodeId, messageIndex);
}
function completeValidatorRemoval(
uint32 messageIndex
) external updateGlobalNodeStakeOncePerEpoch {
_completeValidatorRemoval(messageIndex);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function slash(
uint48 epoch,
address, /* operator */
uint256, /* amount */
uint96 assetClassId
) public onlyOwner updateStakeCache(epoch, assetClassId) updateGlobalNodeStakeOncePerEpoch {
revert AvalancheL1Middleware__NotImplemented();
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function calcAndCacheStakes(uint48 epoch, uint96 assetClassId) public returns (uint256 totalStake) {
uint48 epochStartTs = getEpochStartTs(epoch);
// Check for too-old epoch: note that if Time.timestamp() < SLASHING_WINDOW this subtraction underflows.
if (epochStartTs > Time.timestamp() || epochStartTs < Time.timestamp() - SLASHING_WINDOW) {
revert AvalancheL1Middleware__EpochError(epochStartTs);
}
uint256 length = operators.length();
for (uint256 i; i < length; ++i) {
(address operator, uint48 enabledTime, uint48 disabledTime) = operators.atWithTimes(i);
if (!_wasActiveAt(enabledTime, disabledTime, epochStartTs)) {
continue;
}
uint256 operatorStake = getOperatorStake(operator, epoch, assetClassId);
operatorStakeCache[epoch][assetClassId][operator] = operatorStake;
totalStake += operatorStake;
}
totalStakeCache[epoch][assetClassId] = totalStake;
totalStakeCached[epoch][assetClassId] = true;
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function calcAndCacheNodeStakeForAllOperators() public {
uint48 current = getCurrentEpoch();
if (current <= lastGlobalNodeStakeUpdateEpoch) {
return; // Already up-to-date
}
uint48 epochsPending = current - lastGlobalNodeStakeUpdateEpoch;
if (epochsPending > MAX_AUTO_EPOCH_UPDATES) {
revert AvalancheL1Middleware__ManualEpochUpdateRequired(epochsPending, MAX_AUTO_EPOCH_UPDATES);
}
// Process pending epochs up to MAX_AUTO_EPOCH_UPDATES
for (uint48 i = 0; i < epochsPending; i++) {
bool processed = _processSingleEpochNodeStakeCacheUpdate();
if (!processed) break;
}
}
/**
* @notice Processes node stake cache updates for the next pending epoch.
* @dev Updates lastGlobalNodeStakeUpdateEpoch if an epoch is processed.
* @return processed True if an epoch was processed, false if already up-to-date.
*/
function _processSingleEpochNodeStakeCacheUpdate() internal returns (bool) {
uint48 current = getCurrentEpoch();
if (current <= lastGlobalNodeStakeUpdateEpoch) {
return false; // Already up-to-date
}
uint48 epochToProcess = lastGlobalNodeStakeUpdateEpoch + 1;
// Process this single epochToProcess
for (uint256 i = 0; i < operators.length(); i++) {
(address operator,,) = operators.atWithTimes(i);
// _calcAndCacheNodeStakeForOperatorAtEpoch itself handles carry-over from epochToProcess - 1
_calcAndCacheNodeStakeForOperatorAtEpoch(operator, epochToProcess);
}
lastGlobalNodeStakeUpdateEpoch = epochToProcess;
return true;
}
/**
* @notice Manually processes node stake cache updates for a specified number of epochs.
* @dev Useful if automatic updates via modifier fail due to too many pending epochs.
* @param numEpochsToProcess The number of pending epochs to process in this call.
*/
function manualProcessNodeStakeCache(uint48 numEpochsToProcess) external {
if (numEpochsToProcess == 0) {
revert AvalancheL1Middleware__NoEpochsToProcess();
}
uint48 currentEpoch = getCurrentEpoch();
uint48 epochsActuallyPending = 0;
if (currentEpoch > lastGlobalNodeStakeUpdateEpoch) {
epochsActuallyPending = currentEpoch - lastGlobalNodeStakeUpdateEpoch;
}
if (numEpochsToProcess > epochsActuallyPending) {
// Cap processing at what's actually pending to avoid processing non-existent future states.
if (epochsActuallyPending == 0) {
// Effectively, nothing to do, could emit an event or just succeed.
emit NodeStakeCacheManuallyProcessed(lastGlobalNodeStakeUpdateEpoch, 0);
return;
}
numEpochsToProcess = epochsActuallyPending;
}
uint48 epochsProcessedCount = 0;
for (uint48 i = 0; i < numEpochsToProcess; i++) {
if (lastGlobalNodeStakeUpdateEpoch >= currentEpoch) {
break; // Caught up
}
bool processed = _processSingleEpochNodeStakeCacheUpdate();
if (processed) {
epochsProcessedCount++;
} else {
// Should not happen if currentEpoch > lastGlobalNodeStakeUpdateEpoch initially
// and numEpochsToProcess is positive.
break;
}
}
emit NodeStakeCacheManuallyProcessed(lastGlobalNodeStakeUpdateEpoch, epochsProcessedCount);
}
/**
* @notice Caches manager-based stake for each node of `operator` in epoch `currentEpoch`.
* @param operator The operator address
*/
function _calcAndCacheNodeStakeForOperatorAtEpoch(address operator, uint48 epoch) internal {
uint48 prevEpoch = (epoch == 0) ? 0 : epoch - 1;
bytes32[] storage nodeArray = operatorNodesArray[operator];
for (uint256 i = nodeArray.length; i > 0;) {
i--;
bytes32 nodeId = nodeArray[i];
bytes32 valID = balancerValidatorManager.registeredValidators(abi.encodePacked(uint160(uint256(nodeId))));
// If no removal/update, just carry over from prevEpoch (only if we haven’t set it yet)
if (!nodePendingRemoval[valID] && !nodePendingUpdate[valID]) {
if (nodeStakeCache[epoch][valID] == 0) {
nodeStakeCache[epoch][valID] = nodeStakeCache[prevEpoch][valID];
}
continue;
}
if (nodePendingRemoval[valID] && nodeStakeCache[epoch][valID] == 0 && nodeStakeCache[prevEpoch][valID] != 0)
{
_removeNodeFromArray(operator, nodeId);
nodePendingRemoval[valID] = false;
}
// If there was a pending update, finalize and clear the pending markers
if (nodePendingUpdate[valID]) {
nodePendingUpdate[valID] = false;
}
}
// Reset operator locked stake once per epoch
if (operatorLockedStake[operator] > 0) {
operatorLockedStake[operator] = 0;
}
}
/**
* @notice Remove a node => end its validator. Checks still to be done.
* @param nodeId The node ID
*/
function _removeNode(address operator, bytes32 nodeId) internal {
bytes32 validationID = balancerValidatorManager.registeredValidators(abi.encodePacked(uint160(uint256(nodeId))));
_initializeEndValidationAndFlag(operator, validationID, nodeId);
}
function _initializeEndValidationAndFlag(address operator, bytes32 validationID, bytes32 nodeId) internal {
uint48 nextEpoch = getCurrentEpoch() + 1;
nodeStakeCache[nextEpoch][validationID] = 0;
nodePendingRemoval[validationID] = true;
balancerValidatorManager.initializeEndValidation(validationID);
emit NodeRemoved(operator, nodeId, validationID);
}
/**
* @notice Remove the node from the dynamic array (swap and pop).
* @param nodeId The node ID.
*/
function _removeNodeFromArray(address operator, bytes32 nodeId) internal {
bytes32[] storage nodesArr = operatorNodesArray[operator];
// Find the node index by looping (O(n)), then swap+pop
uint256 length = nodesArr.length;
for (uint256 i = 0; i < length; i++) {
if (nodesArr[i] == nodeId) {
uint256 lastIndex = length - 1;
if (i != lastIndex) {
nodesArr[i] = nodesArr[lastIndex];
}
nodesArr.pop();
break;
}
}
}
/**
* @notice Completes a validator's registration.
* @param operator The operator who owns the validator
* @param nodeId The unique ID of the validator whose registration is being finalized
* @param messageIndex The message index from the BalancerValidatorManager (used for ordering/verification)
*/
function _completeValidatorRegistration(
address operator,
bytes32 nodeId,
uint32 messageIndex
) internal onlyRegisteredOperatorNode(operator, nodeId) {
balancerValidatorManager.completeValidatorRegistration(messageIndex);
}
/**
* @notice Completes a validator's removal.
* @param messageIndex The message index from the BalancerValidatorManager (used for ordering/verification)
*/
function _completeValidatorRemoval(
uint32 messageIndex
) internal {
balancerValidatorManager.completeEndValidation(messageIndex);
}
/**
* @notice Completes a validator's stake update
* @param operator The operator who owns the validator
* @param nodeId The unique ID of the validator whose relative weight update is being finalized
* @param messageIndex The message index from the BalancerValidatorManager (used for ordering/verification)
*/
function _completeStakeUpdate(
address operator,
bytes32 nodeId,
uint32 messageIndex
) internal onlyRegisteredOperatorNode(operator, nodeId) {
bytes32 validationID = balancerValidatorManager.registeredValidators(abi.encodePacked(uint160(uint256(nodeId))));
if (!balancerValidatorManager.isValidatorPendingWeightUpdate(validationID)) {
revert AvalancheL1Middleware__WeightUpdateNotPending(validationID);
}
// if the completeValidatorWeightUpdate fails, not sure if the previous bool is secure.
balancerValidatorManager.completeValidatorWeightUpdate(validationID, messageIndex);
}
/**
* @notice Sets the stake of a validator and updates the operator's locked stake accordingly.
* @param operator The operator who owns the validator
* @param validationID The unique ID of the validator whose stake is being updated
* @param newStake The new stake for the validator
* @dev When updating the relative weight of a validator, the operator's locked stake is increased or decreased
*/
function _initializeValidatorStakeUpdate(address operator, bytes32 validationID, uint256 newStake) internal {
uint48 currentEpoch = getCurrentEpoch();
uint256 cachedStake = getEffectiveNodeStake(currentEpoch, validationID);
if (balancerValidatorManager.isValidatorPendingWeightUpdate(validationID)) {
revert AvalancheL1Middleware__WeightUpdatePending(validationID);
}
uint256 delta;
if (newStake > cachedStake) {
delta = newStake - cachedStake;
if (delta > _getOperatorAvailableStake(operator)) {
revert AvalancheL1Middleware__NotEnoughFreeStake(newStake);
}
}
operatorLockedStake[operator] += delta;
nodePendingUpdate[validationID] = true;
nodeStakeCache[currentEpoch + 1][validationID] = newStake;
// if newStake < cachedStake, no lock should happen, it's locked in the cache
uint64 scaledWeight = StakeConversion.stakeToWeight(newStake, WEIGHT_SCALE_FACTOR);
balancerValidatorManager.initializeValidatorWeightUpdate(validationID, scaledWeight);
}
function _requireMinSecondaryAssetClasses(uint256 extraNode, address operator) internal view returns (bool) {
uint48 epoch = getCurrentEpoch();
uint256 nodeCount = operatorNodesArray[operator].length; // existing nodes
uint256 secCount = secondaryAssetClasses.length();
if (secCount == 0) {
return true;
}
for (uint256 i = 0; i < secCount; i++) {
uint256 classId = secondaryAssetClasses.at(i);
uint256 stake = getOperatorStake(operator, epoch, uint96(classId));
// Check ratio vs. class's min stake, could add an emit here to debug
if (stake / (nodeCount + extraNode) < assetClasses[classId].minValidatorStake) {
return false;
}
}
return true;
}
/**
* @notice Checks if the classId is active
* @param assetClassId The asset class ID
* @return bool True if active
*/
function _isActiveAssetClass(
uint256 assetClassId
) internal view returns (bool) {
return (assetClassId == PRIMARY_ASSET_CLASS || secondaryAssetClasses.contains(assetClassId));
}
/**
* @notice Checks if the asset is still in use by a vault
* @param assetClassId The asset class ID
* @param asset The asset address
* @return bool True if in use by any vault
*/
function _isUsedAsset(uint256 assetClassId, address asset) internal view returns (bool) {
for (uint256 i; i < vaultManager.getVaultCount(); ++i) {
(address vault,,) = vaultManager.getVaultAtWithTimes(i);
if (vaultManager.vaultToAssetClass(vault) == assetClassId && IVaultTokenized(vault).collateral() == asset) {
return true;
}
}
return false;
}
/**
* @notice Checks if the asset class is still in use by a vault
* @param assetClassId The asset class ID
* @return bool True if in use by any vault
*/
function _isUsedAssetClass(
uint256 assetClassId
) internal view returns (bool) {
for (uint256 i; i < vaultManager.getVaultCount(); ++i) {
(address vault,,) = vaultManager.getVaultAtWithTimes(i);
if (vaultManager.vaultToAssetClass(vault) == assetClassId) {
return true;
}
}
return false;
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getActiveAssetClasses() external view returns (uint256 primary, uint256[] memory secondaries) {
primary = PRIMARY_ASSET_CLASS;
secondaries = secondaryAssetClasses.values();
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getEpochStartTs(
uint48 epoch
) public view returns (uint48 timestamp) {
return START_TIME + epoch * EPOCH_DURATION;
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getEpochAtTs(
uint48 timestamp
) public view returns (uint48 epoch) {
return (timestamp - START_TIME) / EPOCH_DURATION;
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getCurrentEpoch() public view returns (uint48 epoch) {
return getEpochAtTs(Time.timestamp());
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getOperatorStake(
address operator,
uint48 epoch,
uint96 assetClassId
) public view returns (uint256 stake) {
if (totalStakeCached[epoch][assetClassId]) {
uint256 cachedStake = operatorStakeCache[epoch][assetClassId][operator];
return cachedStake;
}
uint48 epochStartTs = getEpochStartTs(epoch);
uint256 totalVaults = vaultManager.getVaultCount();
for (uint256 i; i < totalVaults; ++i) {
(address vault, uint48 enabledTime, uint48 disabledTime) = vaultManager.getVaultAtWithTimes(i);
// Skip if vault not active in the target epoch
if (!_wasActiveAt(enabledTime, disabledTime, epochStartTs)) {
continue;
}
// Skip if vault asset not in AssetClassID
if (vaultManager.getVaultAssetClass(vault) != assetClassId) {
continue;
}
uint256 vaultStake = BaseDelegator(IVaultTokenized(vault).delegator()).stakeAt(
L1_VALIDATOR_MANAGER, assetClassId, operator, epochStartTs, new bytes(0)
);
stake += vaultStake;
}
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getTotalStake(uint48 epoch, uint96 assetClassId) public view returns (uint256) {
if (totalStakeCached[epoch][assetClassId]) {
return totalStakeCache[epoch][assetClassId];
}
return _calcTotalStake(epoch, assetClassId);
}
function getOperatorNodesLength(
address operator
) public view returns (uint256) {
return operatorNodesArray[operator].length;
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getAllOperators() external view returns (address[] memory) {
uint256 length = operators.length();
address[] memory result = new address[](length);
for (uint256 i; i < length; i++) {
(address operator,,) = operators.atWithTimes(i);
result[i] = operator;
}
return result;
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getNodeStake(uint48 epoch, bytes32 validationID) external view returns (uint256) {
return nodeStakeCache[epoch][validationID];
}
function isActiveAssetClass(
uint96 assetClassId
) external view returns (bool) {
return _isActiveAssetClass(assetClassId);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getActiveNodesForEpoch(
address operator,
uint48 epoch
) external view returns (bytes32[] memory activeNodeIds) {
uint48 epochStartTs = getEpochStartTs(epoch);
// Gather all nodes from the never-removed set
bytes32[] memory allNodeIds = operatorNodes[operator].values();
bytes32[] memory temp = new bytes32[](allNodeIds.length);
uint256 activeCount;
for (uint256 i = 0; i < allNodeIds.length; i++) {
bytes32 nodeId = allNodeIds[i];
bytes32 validationID =
balancerValidatorManager.registeredValidators(abi.encodePacked(uint160(uint256(nodeId))));
Validator memory validator = balancerValidatorManager.getValidator(validationID);
if (_wasActiveAt(uint48(validator.startedAt), uint48(validator.endedAt), epochStartTs)) {
temp[activeCount++] = nodeId;
}
}
activeNodeIds = new bytes32[](activeCount);
for (uint256 j = 0; j < activeCount; j++) {
activeNodeIds[j] = temp[j];
}
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getOperatorAvailableStake(
address operator
) external view returns (uint256) {
return _getOperatorAvailableStake(operator);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getVaultManager() external view returns (address) {
return address(vaultManager);
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getOperatorUsedStakeCached(
address operator
) public view returns (uint256 registeredStake) {
bytes32[] storage nodesArr = operatorNodesArray[operator];
for (uint256 i = 0; i < nodesArr.length; i++) {
bytes32 nodeId = nodesArr[i];
bytes32 validationID =
balancerValidatorManager.registeredValidators(abi.encodePacked(uint160(uint256(nodeId))));
registeredStake += getEffectiveNodeStake(getCurrentEpoch(), validationID);
}
}
/**
* @notice Gets the effective stake for a specific ValidationID.
* @param epoch The epoch number
* @param validationID The validation ID
*/
function getEffectiveNodeStake(uint48 epoch, bytes32 validationID) internal view returns (uint256) {
return nodeStakeCache[epoch][validationID];
}
/**
* @inheritdoc IAvalancheL1Middleware
*/
function getOperatorUsedStakeCachedPerEpoch(
uint48 epoch,
address operator,
uint96 assetClass
) external view returns (uint256) {
if (assetClass == PRIMARY_ASSET_CLASS) {
bytes32[] memory nodesArr = this.getActiveNodesForEpoch(operator, epoch);
uint256 operatorStake = 0;
for (uint256 i = 0; i < nodesArr.length; i++) {
bytes32 nodeId = nodesArr[i];
bytes32 validationID =
balancerValidatorManager.registeredValidators(abi.encodePacked(uint160(uint256(nodeId))));
operatorStake += getEffectiveNodeStake(epoch, validationID);
}
return operatorStake;
} else {
return getOperatorStake(operator, epoch, assetClass);
}
}
/**
* @notice Get the validator per ValidationID.
* @param validationID The validation ID.
*/
function _getValidator(
bytes32 validationID
) internal view returns (Validator memory) {
return balancerValidatorManager.getValidator(validationID);
}
/**
* @notice Returns the available stake for an operator
* @param operator The operator address
* @return The available stake
*/
function _getOperatorAvailableStake(
address operator
) internal view returns (uint256) {
uint48 epoch = getCurrentEpoch();
uint256 totalStake = getOperatorStake(operator, epoch, PRIMARY_ASSET_CLASS);
// Enforce max security module weight
(, uint64 securityModuleMaxWeight) = balancerValidatorManager.getSecurityModuleWeights(address(this));
uint256 convertedSecurityModuleMaxWeight =
StakeConversion.weightToStake(securityModuleMaxWeight, WEIGHT_SCALE_FACTOR);
if (totalStake > convertedSecurityModuleMaxWeight) {
totalStake = convertedSecurityModuleMaxWeight;
}
uint256 lockedStake = operatorLockedStake[operator];
if (totalStake <= lockedStake) {
return 0;
}
return totalStake - lockedStake;
}
/**
* @notice Helper to calculate total stake for an epoch
* @param epoch The epoch number
* @param assetClassId The asset class ID
* @return totalStake The total stake across all operators
*/
function _calcTotalStake(uint48 epoch, uint96 assetClassId) private view returns (uint256 totalStake) {
uint48 epochStartTs = getEpochStartTs(epoch);
// for epoch older than SLASHING_WINDOW total stake can be invalidated (use cache)
if (epochStartTs > Time.timestamp() || epochStartTs < Time.timestamp() - SLASHING_WINDOW) {
revert AvalancheL1Middleware__EpochError(epochStartTs);
}
uint256 length = operators.length();
for (uint256 i; i < length; ++i) {
(address operator, uint48 enabledTime, uint48 disabledTime) = operators.atWithTimes(i);
// just skip operator if it was added after the target epoch or paused
if (!_wasActiveAt(enabledTime, disabledTime, epochStartTs)) {
continue;
}
uint256 operatorStake = getOperatorStake(operator, epoch, assetClassId);
totalStake += operatorStake;
}
}
/**
* @notice Checks if an operator or vault was active at a specific timestamp
* @param enabledTime The time it was enabled
* @param disabledTime The time it was disabled
* @param timestamp The timestamp to check
* @return bool True if active
*/
function _wasActiveAt(uint48 enabledTime, uint48 disabledTime, uint48 timestamp) private pure returns (bool) {
return enabledTime != 0 && enabledTime <= timestamp && (disabledTime == 0 || disabledTime >= timestamp);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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 Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.25;
/**
* @dev Validator status
*/
enum ValidatorStatus {
Unknown,
PendingAdded,
Active,
PendingRemoved,
Completed,
Invalidated
}
/**
* @dev Specifies the owner of a validator's remaining balance or disable owner on the P-Chain.
* P-Chain addresses are also 20-bytes, so we use the address type to represent them.
*/
struct PChainOwner {
uint32 threshold;
address[] addresses;
}
/**
* @dev Contains the active state of a Validator
*/
struct Validator {
ValidatorStatus status;
bytes nodeID;
uint64 startingWeight;
uint64 messageNonce;
uint64 weight;
uint64 startedAt;
uint64 endedAt;
}
/**
* @dev Describes the current churn period
*/
struct ValidatorChurnPeriod {
uint256 startedAt;
uint64 initialWeight;
uint64 totalWeight;
uint64 churnAmount;
}
/**
* @notice Validator Manager settings, used to initialize the Validator Manager
* @notice The l1ID is the ID of the L1 that the Validator Manager is managing
* @notice The churnPeriodSeconds is the duration of the churn period in seconds
* @notice The maximumChurnPercentage is the maximum percentage of the total weight that can be added or removed in a single churn period
*/
struct ValidatorManagerSettings {
bytes32 l1ID;
uint64 churnPeriodSeconds;
uint8 maximumChurnPercentage;
}
/**
* @dev Description of the conversion data used to convert
* a subnet to an L1 on the P-Chain.
* This data is the pre-image of a hash that is authenticated by the P-Chain
* and verified by the Validator Manager.
*/
struct ConversionData {
bytes32 l1ID;
bytes32 validatorManagerBlockchainID;
address validatorManagerAddress;
InitialValidator[] initialValidators;
}
/**
* @dev Specifies an initial validator, used in the conversion data.
*/
struct InitialValidator {
bytes nodeID;
bytes blsPublicKey;
uint64 weight;
}
/**
* @dev Specifies a validator to register.
*/
struct ValidatorRegistrationInput {
bytes nodeID;
bytes blsPublicKey;
uint64 registrationExpiry;
PChainOwner remainingBalanceOwner;
PChainOwner disableOwner;
}
/**
* @notice Interface for Validator Manager contracts that implement Subnet-only Validator management.
*/
interface IValidatorManager {
/**
* @notice Emitted when a new validation period is created by locking stake in the manager contract.
* Note: This event does not mean that the validation period has been successfully registered on the P-Chain,
* and rewards for this validation period will not begin accruing until the {ValidationPeriodRegistered} event is
* emitted.
* @param validationID The ID of the validation period being created.
* @param nodeID The node ID of the validator being registered.
* @param registerValidationMessageID The ID of the ICM message that will be sent to the P-Chain to register the
* validation period.
* @param weight The weight of the validator being registered.
* @param registrationExpiry The Unix timestamp after which the reigistration is no longer valid on the P-Chain.
*/
event ValidationPeriodCreated(
bytes32 indexed validationID,
bytes indexed nodeID,
bytes32 indexed registerValidationMessageID,
uint64 weight,
uint64 registrationExpiry
);
event InitialValidatorCreated(
bytes32 indexed validationID, bytes indexed nodeID, uint64 weight
);
/**
* @notice Emitted when the staking manager learns that the validation period has been successfully registered
* on the P-Chain. Rewards for this validation period will begin accruing when this event is emitted.
* @param validationID The ID of the validation period being registered.
* @param weight The weight of the validator being registered.
* @param timestamp The time at which the validation period was registered with the contract.
*/
event ValidationPeriodRegistered(
bytes32 indexed validationID, uint64 weight, uint256 timestamp
);
/**
* @notice Emitted when the process of ending a registered validation period is started by calling
* {initializeEndValidation}.
* Note: The stake for this validation period remains locked until a {ValidationPeriodRemoved} event is emitted.
* @param validationID The ID of the validation period being removed.
* @param setWeightMessageID The ID of the ICM message that updates the validator's weight on the P-Chain.
* @param weight The weight of the validator being removed.
* @param endTime The time at which the removal was initiated.
*/
event ValidatorRemovalInitialized(
bytes32 indexed validationID,
bytes32 indexed setWeightMessageID,
uint64 weight,
uint256 endTime
);
/**
* @notice Emitted when the stake for a validation period is unlocked and returned to the staker.
* This is done by calling {completeEndValidation}, which provides proof from the P-Chain that the
* validation period is not active and will never be active in the future.
* @param validationID The ID of the validation period being removed.
*/
event ValidationPeriodEnded(bytes32 indexed validationID, ValidatorStatus indexed status);
/**
* @notice Event emitted when validator weight is updated.
* @param validationID The ID of the validation period being updated
* @param nonce The message nonce used to update the validator weight
* @param weight The updated validator weight that is sent to the P-Chain
* @param setWeightMessageID The ID of the ICM message that updates the validator's weight on the P-Chain
*/
event ValidatorWeightUpdate(
bytes32 indexed validationID,
uint64 indexed nonce,
uint64 weight,
bytes32 setWeightMessageID
);
/**
* @notice Verifies and sets the initial validator set for the chain through a P-Chain SubnetToL1ConversionMessage.
* @param conversionData The subnet conversion message data used to recompute and verify against the conversionID.
* @param messsageIndex The index that contains the SubnetToL1ConversionMessage ICM message containing the conversionID to be verified against the provided {ConversionData}
*/
function initializeValidatorSet(
ConversionData calldata conversionData,
uint32 messsageIndex
) external;
/**
* @notice Resubmits a validator registration message to be sent to the P-Chain.
* Only necessary if the original message can't be delivered due to validator churn.
* @param validationID The ID of the validation period being registered.
*/
function resendRegisterValidatorMessage(bytes32 validationID) external;
/**
* @notice Completes the validator registration process by returning an acknowledgement of the registration of a
* validationID from the P-Chain.
* @param messageIndex The index of the ICM message to be received providing the acknowledgement.
*/
function completeValidatorRegistration(uint32 messageIndex) external;
/**
* @notice Resubmits a validator end message to be sent to the P-Chain.
* Only necessary if the original message can't be delivered due to validator churn.
* @param validationID The ID of the validation period being ended.
*/
function resendEndValidatorMessage(bytes32 validationID) external;
/**
* @notice Completes the process of ending a validation period by receiving an acknowledgement from the P-Chain
* that the validation ID is not active and will never be active in the future. Returns the the stake associated
* with the validation.
* Note: This function can be used for successful validation periods that have been explicitly ended by calling
* {initializeEndValidation} or for validation periods that never began on the P-Chain due to the {registrationExpiry} being reached.
* @param messageIndex The index of the ICM message to be received providing the proof the validation is not active
* and never will be active on the P-Chain.
*/
function completeEndValidation(uint32 messageIndex) external;
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165Checker.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff;
/**
* @dev Returns true if `account` supports the {IERC165} interface.
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return
supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
!supportsERC165InterfaceUnchecked(account, INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
}
/**
* @dev Returns a boolean array where each value corresponds to the
* interfaces passed in and whether they're supported or not. This allows
* you to batch check interfaces for a contract where your expectation
* is that some interfaces may not be supported.
*
* See {IERC165-supportsInterface}.
*/
function getSupportedInterfaces(
address account,
bytes4[] memory interfaceIds
) internal view returns (bool[] memory) {
// an array of booleans corresponding to interfaceIds and whether they're supported or not
bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
// query support of ERC165 itself
if (supportsERC165(account)) {
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
}
}
return interfaceIdsSupported;
}
/**
* @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically.
*
* Batch-querying can lead to gas savings by skipping repeated checks for
* {IERC165} support.
*
* See {IERC165-supportsInterface}.
*/
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
// query support of ERC165 itself
if (!supportsERC165(account)) {
return false;
}
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
return false;
}
}
// all interfaces supported
return true;
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
*
* Some precompiled contracts will falsely indicate support for a given interface, so caution
* should be exercised when using this function.
*
* Interface identification is specified in ERC-165.
*/
function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
// prepare call
bytes memory encodedParams = abi.encodeCall(IERC165.supportsInterface, (interfaceId));
// perform static call
bool success;
uint256 returnSize;
uint256 returnValue;
assembly {
success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
returnSize := returndatasize()
returnValue := mload(0x00)
}
return success && returnSize >= 0x20 && returnValue > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.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 ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
interface IBaseDelegator {
error BaseDelegator__AlreadySet();
error BaseDelegator__InsufficientHookGas();
error BaseDelegator__NotL1();
error BaseDelegator__NotSlasher();
error BaseDelegator__NotVault();
error BaseDelegator__NotInitialized();
error BaseDelegator__ZeroAddress(string name);
error BaseDelegator__NotAuthorizedMiddleware();
/**
* @notice Base parameters needed for delegators' deployment.
* @param defaultAdminRoleHolder address of the initial DEFAULT_ADMIN_ROLE holder
* @param hook address of the hook contract
* @param hookSetRoleHolder address of the initial HOOK_SET_ROLE holder
*/
struct BaseParams {
address defaultAdminRoleHolder;
address hook;
address hookSetRoleHolder;
}
/**
* @notice Base hints for a stake.
* @param operatorVaultOptInHint hint for the operator-vault opt-in
* @param operatorL1OptInHint hint for the operator-l1 opt-in
*/
struct StakeBaseHints {
bytes operatorVaultOptInHint;
bytes operatorL1OptInHint;
}
/**
* @notice Emitted when a asset class maximum limit is set.
* @param l1 address of the l1.
* @param assetClass the uint96 assetClass.
* @param amount new maximum asset class limit (how much stake the asset class is ready to get)
*/
event SetMaxL1Limit(address indexed l1, uint96 indexed assetClass, uint256 amount);
/**
* @notice Emitted when a slash happens.
* @param l1 address of the l1.
* @param assetClass the uint96 assetClass.
* @param operator address of the operator
* @param amount amount of the collateral to be slashed
* @param captureTimestamp time point when the stake was captured
*/
event OnSlash(
address indexed l1, uint96 indexed assetClass, address indexed operator, uint256 amount, uint48 captureTimestamp
);
/**
* @notice Emitted when a hook is set.
* @param hook address of the hook
*/
event SetHook(address indexed hook);
/**
* @notice Get a version of the delegator (different versions mean different interfaces).
* @return version of the delegator
* @dev Must return 1 for this one.
*/
/**
* @notice Get the factory's address.
* @return address of the factory
*/
function FACTORY() external view returns (address);
/**
* @notice Get the entity's type.
* @return type of the entity
*/
function TYPE() external view returns (uint64);
/**
* @notice Initialize this entity contract by using a given data.
* @param data some data to use
*/
function initialize(
bytes calldata data
) external;
function VERSION() external view returns (uint64);
/**
* @notice Get the l1 registry's address.
* @return address of the l1 registry
*/
function L1_REGISTRY() external view returns (address);
/**
* @notice Get the vault factory's address.
* @return address of the vault factory
*/
function VAULT_FACTORY() external view returns (address);
/**
* @notice Get the operator-vault opt-in service's address.
* @return address of the operator-vault opt-in service
*/
function OPERATOR_VAULT_OPT_IN_SERVICE() external view returns (address);
/**
* @notice Get the operator-l1 opt-in service's address.
* @return address of the operator-l1 opt-in service
*/
function OPERATOR_L1_OPT_IN_SERVICE() external view returns (address);
/**
* @notice Get a gas limit for the hook.
* @return value of the hook gas limit
*/
function HOOK_GAS_LIMIT() external view returns (uint256);
/**
* @notice Get a reserve gas between the gas limit check and the hook's execution.
* @return value of the reserve gas
*/
function HOOK_RESERVE() external view returns (uint256);
/**
* @notice Get a hook setter's role.
* @return assetClass of the hook setter role
*/
function HOOK_SET_ROLE() external view returns (bytes32);
/**
* @notice Get the vault's address.
* @return address of the vault
*/
function vault() external view returns (address);
/**
* @notice Get the hook's address.
* @return address of the hook
* @dev The hook can have arbitrary logic under certain functions, however, it doesn't affect the stake guarantees.
*/
function hook() external view returns (address);
/**
* @notice Get a particular asset class maximum limit
* (meaning the asset class is not ready to get more as a stake).
* @param l1 address of the l1.
* @param assetClass the uint96 assetClass.
* @return maximum limit of the asset class
*/
function maxL1Limit(address l1, uint96 assetClass) external view returns (uint256);
/**
* @notice Get a stake that a given asset class could be able to slash for a certain operator at a given timestamp
* until the end of the consequent epoch using hints (if no cross-slashing and no slashings by the asset class).
* @param l1 address of the l1.
* @param assetClass the uint96 assetClass.
* @param operator address of the operator
* @param timestamp time point to capture the stake at
* @param hints hints for the checkpoints' indexes
* @return slashable stake at the given timestamp until the end of the consequent epoch
* @dev Warning: it is not safe to use timestamp >= current one for the stake capturing, as it can change later.
*/
function stakeAt(
address l1,
uint96 assetClass,
address operator,
uint48 timestamp,
bytes memory hints
) external view returns (uint256);
/**
* @notice Get a stake that a given asset class will be able to slash
* for a certain operator until the end of the next epoch (if no cross-slashing and no slashings by the asset class).
* @param l1 address of the l1.
* @param assetClass the uint96 assetClass.
* @param operator address of the operator
* @return slashable stake until the end of the next epoch
* @dev Warning: this function is not safe to use for stake capturing, as it can change by the end of the block.
*/
function stake(address l1, uint96 assetClass, address operator) external view returns (uint256);
/**
* @notice Set a maximum limit for a asset class (how much stake the asset class is ready to get).
* assetClass assetClass of the asset class
* @param l1 address of the l1
* @param amount new maximum asset class limit
* @dev Only a l1 can call this function.
*/
function setMaxL1Limit(address l1, uint96 assetClass, uint256 amount) external;
/**
* @notice Set a new hook.
* @param hook address of the hook
* @dev Only a HOOK_SET_ROLE holder can call this function.
* The hook can have arbitrary logic under certain functions, however, it doesn't affect the stake guarantees.
*/
function setHook(
address hook
) external;
/**
* @notice Called when a slash happens.
* @param l1 address of the l1.
* @param assetClass the uint96 assetClass.
* @param operator address of the operator
* @param amount amount of the collateral slashed
* @param captureTimestamp time point when the stake was captured
* @param data some additional data
* @dev Only the vault's slasher can call this function.
*/
function onSlash(
address l1,
uint96 assetClass,
address operator,
uint256 amount,
uint48 captureTimestamp,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
interface IDelegatorHook {
/**
* @notice Called when a slash happens.
* @param l1 address of the l1.
* @param assetClass the uint96 assetClass.
* @param operator address of the operator
* @param amount amount of the collateral to be slashed
* @param captureTimestamp time point when the stake was captured
* @param data some additional data
*/
function onSlash(
address l1,
uint96 assetClass,
address operator,
uint256 amount,
uint48 captureTimestamp,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
interface IOptInService {
error OptInService__AlreadyOptedIn();
error OptInService__ExpiredSignature();
error OptInService__InvalidSignature();
error OptInService__NotOptedIn();
error OptInService__NotWhereRegistered();
error OptInService__NotWho();
error OptInService__OptOutCooldown();
error OptInService__NotWhereEntity();
/**
* @notice Emitted when a "who" opts into a "where" entity.
* @param who address of the "who"
* @param where address of the "where" entity
*/
event OptIn(address indexed who, address indexed where);
/**
* @notice Emitted when a "who" opts out from a "where" entity.
* @param who address of the "who"
* @param where address of the "where" entity
*/
event OptOut(address indexed who, address indexed where);
/**
* @notice Emitted when the nonce of a "who" to a "where" entity is increased.
* @param who address of the "who"
* @param where address of the "where" entity
*/
event IncreaseNonce(address indexed who, address indexed where);
/**
* @notice Get the "who" registry's address.
* @return address of the "who" registry
*/
function WHO_REGISTRY() external view returns (address);
/**
* @notice Get the address of the registry where to opt-in.
* @return address of the "where" registry
*/
function WHERE_REGISTRY() external view returns (address);
/**
* @notice Get if a given "who" is opted-in to a particular "where" entity at a given timestamp using a hint.
* @param who address of the "who"
* @param where address of the "where" entity
* @param timestamp time point to get if the "who" is opted-in at
* @param hint hint for the checkpoint index
* @return if the "who" is opted-in at the given timestamp
*/
function isOptedInAt(
address who,
address where,
uint48 timestamp,
bytes calldata hint
) external view returns (bool);
/**
* @notice Check if a given "who" is opted-in to a particular "where" entity.
* @param who address of the "who"
* @param where address of the "where" entity
* @return if the "who" is opted-in
*/
function isOptedIn(address who, address where) external view returns (bool);
/**
* @notice Get the nonce of a given "who" to a particular "where" entity.
* @param who address of the "who"
* @param where address of the "where" entity
* @return nonce
*/
function nonces(address who, address where) external view returns (uint256);
/**
* @notice Opt-in a calling "who" to a particular "where" entity.
* @param where address of the "where" entity
*/
function optIn(
address where
) external;
/**
* @notice Opt-in a "who" to a particular "where" entity with a signature.
* @param who address of the "who"
* @param where address of the "where" entity
* @param deadline time point until the signature is valid (inclusively)
* @param signature signature of the "who"
*/
function optIn(address who, address where, uint48 deadline, bytes calldata signature) external;
/**
* @notice Opt-out a calling "who" from a particular "where" entity.
* @param where address of the "where" entity
*/
function optOut(
address where
) external;
/**
* @notice Opt-out a "who" from a particular "where" entity with a signature.
* @param who address of the "who"
* @param where address of the "where" entity
* @param deadline time point until the signature is valid (inclusively)
* @param signature signature of the "who"
*/
function optOut(address who, address where, uint48 deadline, bytes calldata signature) external;
/**
* @notice Increase the nonce of a given "who" to a particular "where" entity.
* @param where address of the "where" entity
* @dev It can be used to invalidate a given signature.
*/
function increaseNonce(
address where
) external;
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
import {IMigratablesFactory} from "./common/IMigratablesFactory.sol";
interface IVaultFactory is IMigratablesFactory {}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
interface IL1Registry {
event RegisterL1(address indexed l1);
event SetL1Middleware(address indexed l1, address indexed l1Middleware);
event SetMetadataURL(address indexed l1, string metadataURL);
error L1Registry__L1AlreadyRegistered();
error L1Registry__L1NotRegistered();
error L1Registry__InvalidValidatorManager(address l1);
error L1Registry__InvalidL1Middleware();
error L1Registry__NotValidatorManagerOwner(address caller, address expectedOwner);
error L1Registry__InsufficientFee();
error L1Registry__FeeTransferFailed();
error L1Registry__FeeExceedsMaximum(uint256 newFee, uint256 maxFee);
error L1Registry__ZeroAddress(string name);
error L1Registry__NotFeeCollector(address caller);
error L1Registry__NoFeesToWithdraw();
/**
* @notice Register an Avalanche L1
* @dev l1 must be the manager of the Avalanche L1
* @dev msg.sender must be a SecurityModule of the l1
* @dev l1Middleware must be a SecurityModule of the Avalanche L1
* @param l1 The Avalanche L1. Should be The ValidatorManager.
* @param l1Middleware The l1Middleware of the Avalanche L1
* @param metadataURL The metadata URL of the Avalanche L1
*/
function registerL1(
address l1,
address l1Middleware,
string calldata metadataURL
)
/*, uint32 messageIndex, SubnetConversionData subnetConversionData*/
external
payable;
/**
* @notice Check if an address is registered as an L1
* @param l1 The Avalanche L1. Should be The ValidatorManager.
* @return True if the address is registered as an L1, false otherwise
*/
function isRegistered(
address l1
) external view returns (bool);
/**
* @notice Check if an address is registered as an L1 and if the Middleware is correct
* @param l1 The Avalanche L1. Should be The ValidatorManager.
* @param l1middleware_ The l1Middleware to check
* @return True if the address is registered as an L1 and the middleware is correct, false otherwise
*/
function isRegisteredWithMiddleware(address l1, address l1middleware_) external view returns (bool);
/**
* @notice Get the L1 at a specific index
* @param index The index of the L1 to get
* @return The address of the L1 at the specified index
* @return The l1Middleware of the L1 at the specified index
* @return The metadata URL of the L1 at the specified index
*/
function getL1At(
uint256 index
) external view returns (address, address, string memory);
/**
* @notice Get the total number of L1s
* @return Total number of L1s
*/
function totalL1s() external view returns (uint256);
/**
* @notice Get all L1s
* @return Array of all L1s
* @return Array of all L1s' l1Middlewares
* @return Array of all L1s' metadata URLs
*/
function getAllL1s() external view returns (address[] memory, address[] memory, string[] memory);
/**
* @notice Set the l1Middleware of an L1
* @param l1 The Avalanche L1. Should be The ValidatorManager.
* @param l1Middleware_ The new l1Middleware
*/
function setL1Middleware(address l1, address l1Middleware_) external;
/**
* @notice Set the metadata URL of an L1
* @param l1 The Avalanche L1. Should be The ValidatorManager.
* @param metadataURL The new metadata URL
*/
function setMetadataURL(address l1, string calldata metadataURL) external;
}// 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;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
struct ReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
assembly {
$.slot := ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTERED
if ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function _nonReentrantAfter() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}// 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
}
}
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
interface IBaseSlasher {
error BaseSlasher__NoBurner();
error BaseSlasher__InsufficientBurnerGas();
error BaseSlasher__NotNetworkMiddleware();
error BaseSlasher__NotVault();
error BaseSlasher__NotInitialized();
/**
* @notice Base parameters needed for slashers' deployment.
* @param isBurnerHook if the burner is needed to be called on a slashing
*/
struct BaseParams {
bool isBurnerHook;
}
/**
* @notice Hints for a slashable stake.
* @param stakeHints hints for the stake checkpoints
* @param cumulativeSlashFromHint hint for the cumulative slash amount at a capture timestamp
*/
struct SlashableStakeHints {
bytes stakeHints;
bytes cumulativeSlashFromHint;
}
/**
* @notice General data for the delegator.
* @param slasherType type of the slasher
* @param data slasher-dependent data for the delegator
*/
struct GeneralDelegatorData {
uint64 slasherType;
bytes data;
}
/**
* @notice Get the factory's address.
* @return address of the factory
*/
function FACTORY() external view returns (address);
/**
* @notice Get the entity's type.
* @return type of the entity
*/
function TYPE() external view returns (uint64);
/**
* @notice Initialize this entity contract by using a given data.
* @param data some data to use
*/
function initialize(
bytes calldata data
) external;
/**
* @notice Get a gas limit for the burner.
* @return value of the burner gas limit
*/
function BURNER_GAS_LIMIT() external view returns (uint256);
/**
* @notice Get a reserve gas between the gas limit check and the burner's execution.
* @return value of the reserve gas
*/
function BURNER_RESERVE() external view returns (uint256);
/**
* @notice Get the vault factory's address.
* @return address of the vault factory
*/
function VAULT_FACTORY() external view returns (address);
/**
* @notice Get the network middleware service's address.
* @return address of the network middleware service
*/
function NETWORK_MIDDLEWARE_SERVICE() external view returns (address);
/**
* @notice Get the vault's address.
* @return address of the vault to perform slashings on
*/
function vault() external view returns (address);
/**
* @notice Get if the burner is needed to be called on a slashing.
* @return if the burner is a hook
*/
function isBurnerHook() external view returns (bool);
/**
* @notice Get the latest capture timestamp that was slashed on a subnetwork.
* @param subnetwork full identifier of the subnetwork (address of the network concatenated with the uint96 identifier)
* @param operator address of the operator
* @return latest capture timestamp that was slashed
*/
function latestSlashedCaptureTimestamp(bytes32 subnetwork, address operator) external view returns (uint48);
/**
* @notice Get a cumulative slash amount for an operator on a subnetwork until a given timestamp (inclusively) using a hint.
* @param subnetwork full identifier of the subnetwork (address of the network concatenated with the uint96 identifier)
* @param operator address of the operator
* @param timestamp time point to get the cumulative slash amount until (inclusively)
* @param hint hint for the checkpoint index
* @return cumulative slash amount until the given timestamp (inclusively)
*/
function cumulativeSlashAt(
bytes32 subnetwork,
address operator,
uint48 timestamp,
bytes memory hint
) external view returns (uint256);
/**
* @notice Get a cumulative slash amount for an operator on a subnetwork.
* @param subnetwork full identifier of the subnetwork (address of the network concatenated with the uint96 identifier)
* @param operator address of the operator
* @return cumulative slash amount
*/
function cumulativeSlash(bytes32 subnetwork, address operator) external view returns (uint256);
/**
* @notice Get a slashable amount of a stake got at a given capture timestamp using hints.
* @param subnetwork full identifier of the subnetwork (address of the network concatenated with the uint96 identifier)
* @param operator address of the operator
* @param captureTimestamp time point to get the stake amount at
* @param hints hints for the checkpoints' indexes
* @return slashable amount of the stake
*/
function slashableStake(
bytes32 subnetwork,
address operator,
uint48 captureTimestamp,
bytes memory hints
) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/Checkpoints.sol)
// This file was procedurally generated from scripts/generate/templates/Checkpoints.js.
pragma solidity ^0.8.20;
import {Math} from "../math/Math.sol";
/**
* @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in
* time, and later looking up past values by block number. See {Votes} as an example.
*
* To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new
* checkpoint for the current transaction block using the {push} function.
*/
library Checkpoints {
/**
* @dev A value was attempted to be inserted on a past checkpoint.
*/
error CheckpointUnorderedInsertion();
struct Trace224 {
Checkpoint224[] _checkpoints;
}
struct Checkpoint224 {
uint32 _key;
uint224 _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint32).max` key set will disable the
* library.
*/
function push(Trace224 storage self, uint32 key, uint224 value) internal returns (uint224, uint224) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(Trace224 storage self) internal view returns (uint224) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, 0);
} else {
Checkpoint224 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(Trace224 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(Trace224 storage self, uint32 pos) internal view returns (Checkpoint224 memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(Checkpoint224[] storage self, uint32 key, uint224 value) private returns (uint224, uint224) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
Checkpoint224 memory last = _unsafeAccess(self, pos - 1);
// Checkpoint keys must be non-decreasing.
if (last._key > key) {
revert CheckpointUnorderedInsertion();
}
// Update or push new checkpoint
if (last._key == key) {
_unsafeAccess(self, pos - 1)._value = value;
} else {
self.push(Checkpoint224({_key: key, _value: value}));
}
return (last._value, value);
} else {
self.push(Checkpoint224({_key: key, _value: value}));
return (0, value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
Checkpoint224[] storage self,
uint32 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
* `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
* exclusive `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
Checkpoint224[] storage self,
uint32 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(
Checkpoint224[] storage self,
uint256 pos
) private pure returns (Checkpoint224 storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
struct Trace208 {
Checkpoint208[] _checkpoints;
}
struct Checkpoint208 {
uint48 _key;
uint208 _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a Trace208 so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
* library.
*/
function push(Trace208 storage self, uint48 key, uint208 value) internal returns (uint208, uint208) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(Trace208 storage self, uint48 key) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(Trace208 storage self) internal view returns (uint208) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(Trace208 storage self) internal view returns (bool exists, uint48 _key, uint208 _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, 0);
} else {
Checkpoint208 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(Trace208 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(Trace208 storage self, uint32 pos) internal view returns (Checkpoint208 memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(Checkpoint208[] storage self, uint48 key, uint208 value) private returns (uint208, uint208) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
Checkpoint208 memory last = _unsafeAccess(self, pos - 1);
// Checkpoint keys must be non-decreasing.
if (last._key > key) {
revert CheckpointUnorderedInsertion();
}
// Update or push new checkpoint
if (last._key == key) {
_unsafeAccess(self, pos - 1)._value = value;
} else {
self.push(Checkpoint208({_key: key, _value: value}));
}
return (last._value, value);
} else {
self.push(Checkpoint208({_key: key, _value: value}));
return (0, value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
Checkpoint208[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
* `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
* exclusive `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
Checkpoint208[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(
Checkpoint208[] storage self,
uint256 pos
) private pure returns (Checkpoint208 storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
struct Trace160 {
Checkpoint160[] _checkpoints;
}
struct Checkpoint160 {
uint96 _key;
uint160 _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint96).max` key set will disable the
* library.
*/
function push(Trace160 storage self, uint96 key, uint160 value) internal returns (uint160, uint160) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(Trace160 storage self) internal view returns (uint160) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, 0);
} else {
Checkpoint160 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(Trace160 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(Trace160 storage self, uint32 pos) internal view returns (Checkpoint160 memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(Checkpoint160[] storage self, uint96 key, uint160 value) private returns (uint160, uint160) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
Checkpoint160 memory last = _unsafeAccess(self, pos - 1);
// Checkpoint keys must be non-decreasing.
if (last._key > key) {
revert CheckpointUnorderedInsertion();
}
// Update or push new checkpoint
if (last._key == key) {
_unsafeAccess(self, pos - 1)._value = value;
} else {
self.push(Checkpoint160({_key: key, _value: value}));
}
return (last._value, value);
} else {
self.push(Checkpoint160({_key: key, _value: value}));
return (0, value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
Checkpoint160[] storage self,
uint96 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
* `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
* exclusive `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
Checkpoint160[] storage self,
uint96 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(
Checkpoint160[] storage self,
uint256 pos
) private pure returns (Checkpoint160 storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
}// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity 0.8.25;
import {
BalancerValidatorManagerSettings,
IBalancerValidatorManager
} from "../../interfaces/ValidatorManager/IBalancerValidatorManager.sol";
import {ValidatorManager} from "@avalabs/icm-contracts/validator-manager/ValidatorManager.sol";
import {ValidatorMessages} from "@avalabs/icm-contracts/validator-manager/ValidatorMessages.sol";
import {
IValidatorManager,
Validator,
ValidatorChurnPeriod,
ValidatorManagerSettings,
ValidatorRegistrationInput,
ValidatorStatus
} from "@avalabs/icm-contracts/validator-manager/interfaces/IValidatorManager.sol";
import {OwnableUpgradeable} from
"@openzeppelin/[email protected]/access/OwnableUpgradeable.sol";
import {EnumerableMap} from "@openzeppelin/[email protected]/utils/structs/EnumerableMap.sol";
/**
* @title BalancerValidatorManager
* @author ADDPHO
* @notice The Balancer Validator Manager contract allows to balance the weight of an L1 between multiple security modules.
* @custom:oz-upgrades-unsafe-allow external-library-linking
* @custom:oz-upgrades-from PoAValidatorManager
*/
contract BalancerValidatorManager is
IBalancerValidatorManager,
ValidatorManager,
OwnableUpgradeable
{
using EnumerableMap for EnumerableMap.AddressToUintMap;
/// @custom:storage-location erc7201:suzaku.storage.BalancerValidatorManager
struct BalancerValidatorManagerStorage {
/// @notice The registered security modules along with their maximum weight
EnumerableMap.AddressToUintMap securityModules;
/// @notice The total weight of all validators for a given security module
mapping(address securityModule => uint64 weight) securityModuleWeight;
/// @notice The security module to which each validator belongs
mapping(bytes32 validationID => address securityModule) validatorSecurityModule;
/// @notice Validators pending weight updates
mapping(bytes32 validationID => bytes32 messageID) validatorPendingWeightUpdate;
}
// keccak256(abi.encode(uint256(keccak256("suzaku.storage.BalancerValidatorManager")) - 1)) & ~bytes32(uint256(0xff));
bytes32 public constant BALANCER_VALIDATOR_MANAGER_STORAGE_LOCATION =
0x9d2d7650aa35ca910e5b713f6b3de6524a06fbcb31ffc9811340c6f331a23400;
// solhint-disable func-name-mixedcase, ordering
function _getBalancerValidatorManagerStorage()
private
pure
returns (BalancerValidatorManagerStorage storage $)
{
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := BALANCER_VALIDATOR_MANAGER_STORAGE_LOCATION
}
}
modifier onlySecurityModule() {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
if (!$.securityModules.contains(msg.sender)) {
revert BalancerValidatorManager__SecurityModuleNotRegistered(msg.sender);
}
_;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(
BalancerValidatorManagerSettings calldata settings
) external reinitializer(2) {
__BalancerValidatorManager_init(settings);
}
function __BalancerValidatorManager_init(
BalancerValidatorManagerSettings calldata settings
) internal onlyInitializing {
__ValidatorManager_init(settings.baseSettings);
__Ownable_init(settings.initialOwner);
__BalancerValidatorManager_init_unchained(
settings.initialSecurityModule,
settings.initialSecurityModuleMaxWeight,
settings.migratedValidations
);
}
// solhint-disable-next-line no-empty-blocks
function __BalancerValidatorManager_init_unchained(
address initialSecurityModule,
uint64 initialSecurityModuleMaxWeight,
bytes32[] calldata migratedValidations
) internal onlyInitializing {
_setUpSecurityModule(initialSecurityModule, initialSecurityModuleMaxWeight);
_migrateValidators(migratedValidations);
}
// solhint-enable func-name-mixedcase
/// @inheritdoc IBalancerValidatorManager
function setUpSecurityModule(address securityModule, uint64 maxWeight) external onlyOwner {
_setUpSecurityModule(securityModule, maxWeight);
}
/// @inheritdoc IBalancerValidatorManager
function initializeValidatorRegistration(
ValidatorRegistrationInput calldata registrationInput,
uint64 weight
) external onlySecurityModule returns (bytes32 validationID) {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
validationID = _initializeValidatorRegistration(registrationInput, weight);
// Update the security module weight
uint64 newSecurityModuleWeight = $.securityModuleWeight[msg.sender] + weight;
_updateSecurityModuleWeight(msg.sender, newSecurityModuleWeight);
$.validatorSecurityModule[validationID] = msg.sender;
return validationID;
}
/// @inheritdoc IBalancerValidatorManager
function initializeEndValidation(
bytes32 validationID
) external onlySecurityModule returns (Validator memory) {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
Validator memory validator = getValidator(validationID);
// Ensure the validator weight is not being updated
if ($.validatorPendingWeightUpdate[validationID] != 0) {
revert BalancerValidatorManager__PendingWeightUpdate(validationID);
}
_checkValidatorSecurityModule(validationID, msg.sender);
validator = _initializeEndValidation(validationID);
// Update the security module weight
uint64 newSecurityModuleWeight = $.securityModuleWeight[msg.sender] - validator.weight;
_updateSecurityModuleWeight(msg.sender, newSecurityModuleWeight);
return validator;
}
/// @inheritdoc IValidatorManager
function completeEndValidation(
uint32 messageIndex
) external {
_completeEndValidation(messageIndex);
}
/// @inheritdoc IBalancerValidatorManager
function initializeValidatorWeightUpdate(
bytes32 validationID,
uint64 newWeight
) external onlySecurityModule returns (Validator memory validator) {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
// Check that the newWeight is greater than zero
if (newWeight == 0) {
revert BalancerValidatorManager__NewWeightIsZero();
}
// Ensure the validation period is active and that the validator is not already being updated
// The initial validator set must have been set already to have active validators.
validator = getValidator(validationID);
if (validator.status != ValidatorStatus.Active) {
revert InvalidValidatorStatus(validator.status);
}
if ($.validatorPendingWeightUpdate[validationID] != 0) {
revert BalancerValidatorManager__PendingWeightUpdate(validationID);
}
_checkValidatorSecurityModule(validationID, msg.sender);
uint64 oldWeight = getValidator(validationID).weight;
(, bytes32 messageID) = _setValidatorWeight(validationID, newWeight);
// Update the security module weight
uint64 newSecurityModuleWeight = $.securityModuleWeight[msg.sender] + newWeight - oldWeight;
_updateSecurityModuleWeight(msg.sender, newSecurityModuleWeight);
$.validatorPendingWeightUpdate[validationID] = messageID;
return validator;
}
/// @inheritdoc IBalancerValidatorManager
function completeValidatorWeightUpdate(bytes32 validationID, uint32 messageIndex) external {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
Validator memory validator = getValidator(validationID);
// Check that the validator is active and being updated
if (validator.status != ValidatorStatus.Active) {
revert InvalidValidatorStatus(validator.status);
}
if ($.validatorPendingWeightUpdate[validationID] == 0) {
revert BalancerValidatorManager__NoPendingWeightUpdate(validationID);
}
// Unpack the Warp message
(bytes32 messageValidationID, uint64 nonce, uint64 weight) = ValidatorMessages
.unpackL1ValidatorWeightMessage(_getPChainWarpMessage(messageIndex).payload);
if (validationID != messageValidationID) {
revert InvalidValidationID(validationID);
}
if (weight != validator.weight) {
revert BalancerValidatorManager__WeightUpdateMismatch(weight, validator.weight);
}
if (validator.messageNonce < nonce) {
revert BalancerValidatorManager__InvalidNonce(nonce);
}
delete $.validatorPendingWeightUpdate[validationID];
}
function resendValidatorWeightUpdate(
bytes32 validationID
) external {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
Validator memory validator = getValidator(validationID);
if (validator.status != ValidatorStatus.Active) {
revert InvalidValidatorStatus(validator.status);
}
if ($.validatorPendingWeightUpdate[validationID] == 0) {
revert BalancerValidatorManager__NoPendingWeightUpdate(validationID);
}
if (validator.messageNonce == 0) {
revert InvalidValidationID(validationID);
}
// Submit the message to the Warp precompile.
WARP_MESSENGER.sendWarpMessage(
ValidatorMessages.packL1ValidatorWeightMessage(
validationID, validator.messageNonce, validator.weight
)
);
}
/// @inheritdoc IBalancerValidatorManager
function getChurnPeriodSeconds() external view returns (uint64 churnPeriodSeconds) {
return _getChurnPeriodSeconds();
}
/// @inheritdoc IBalancerValidatorManager
function getMaximumChurnPercentage() external view returns (uint64 maximumChurnPercentage) {
ValidatorManager.ValidatorManagerStorage storage vms = _getValidatorManagerStorage();
return vms._maximumChurnPercentage;
}
/// @inheritdoc IBalancerValidatorManager
function getCurrentChurnPeriod()
external
view
returns (ValidatorChurnPeriod memory churnPeriod)
{
ValidatorManager.ValidatorManagerStorage storage vms = _getValidatorManagerStorage();
return vms._churnTracker;
}
/// @inheritdoc IBalancerValidatorManager
function getSecurityModules() external view returns (address[] memory securityModules) {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
return $.securityModules.keys();
}
/// @inheritdoc IBalancerValidatorManager
function getSecurityModuleWeights(
address securityModule
) external view returns (uint64 weight, uint64 maxWeight) {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
weight = $.securityModuleWeight[securityModule];
maxWeight = uint64($.securityModules.get(securityModule));
return (weight, maxWeight);
}
/// @inheritdoc IBalancerValidatorManager
function isValidatorPendingWeightUpdate(
bytes32 validationID
) external view returns (bool) {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
return $.validatorPendingWeightUpdate[validationID] != 0;
}
function _checkValidatorSecurityModule(
bytes32 validationID,
address securityModule
) internal view {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
// If the validator has no associated security module, it is an initial validator
// and can be managed by any security module
if ($.validatorSecurityModule[validationID] == address(0)) {
return;
} else if ($.validatorSecurityModule[validationID] != securityModule) {
revert BalancerValidatorManager__ValidatorNotBelongingToSecurityModule(
validationID, securityModule
);
}
}
function _setUpSecurityModule(address securityModule, uint64 maxWeight) internal {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
uint64 currentWeight = $.securityModuleWeight[securityModule];
if (maxWeight < currentWeight) {
revert BalancerValidatorManager__SecurityModuleNewMaxWeightLowerThanCurrentWeight(
securityModule, maxWeight, currentWeight
);
}
if (maxWeight == 0) {
if (!$.securityModules.remove(securityModule)) {
revert BalancerValidatorManager__SecurityModuleNotRegistered(securityModule);
}
} else {
$.securityModules.set(securityModule, uint256(maxWeight));
}
emit SetUpSecurityModule(securityModule, maxWeight);
}
function _updateSecurityModuleWeight(address securityModule, uint64 weight) internal {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
uint64 maxWeight = uint64($.securityModules.get(securityModule));
if (weight > maxWeight) {
revert BalancerValidatorManager__SecurityModuleMaxWeightExceeded(
securityModule, weight, maxWeight
);
}
$.securityModuleWeight[securityModule] = weight;
}
function _migrateValidators(
bytes32[] calldata migratedValidations
) internal {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
ValidatorManager.ValidatorManagerStorage storage vms = _getValidatorManagerStorage();
// Add the migrated validators to the initial security module
uint64 migratedValidationsTotalWeight = 0;
for (uint256 i = 0; i < migratedValidations.length; i++) {
Validator memory validator = getValidator(migratedValidations[i]);
$.validatorSecurityModule[migratedValidations[i]] = $.securityModules.keys()[0];
migratedValidationsTotalWeight += validator.weight;
}
// Check that the migrated validators total weight equals the current L1 total weight
if (migratedValidationsTotalWeight != vms._churnTracker.totalWeight) {
revert BalancerValidatorManager__MigratedValidationsTotalWeightMismatch(
migratedValidationsTotalWeight, vms._churnTracker.totalWeight
);
}
// Update the initial security module weight
_updateSecurityModuleWeight($.securityModules.keys()[0], migratedValidationsTotalWeight);
}
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
interface IOperatorRegistry {
event RegisterOperator(address indexed operator);
event SetMetadataURL(address indexed operator, string metadataURL);
error OperatorRegistry__OperatorAlreadyRegistered();
error OperatorRegistry__OperatorNotRegistered();
/// @notice Register an operator with its metadata URL
function registerOperator(
string calldata metadataURL
) external;
/**
* @notice Check if an address is registered as an operator
* @param operator The address to check
* @return True if the address is registered as an operator, false otherwise
*/
function isRegistered(
address operator
) external view returns (bool);
/**
* @notice Get the operator at a specific index
* @param index The index of the operator to get
* @return The address of the operator at the specified index
* @return The metadata URL of the operator at the specified index
*/
function getOperatorAt(
uint256 index
) external view returns (address, string memory);
/**
* @notice Get the total number of operators
* @return Total number of operators
*/
function totalOperators() external view returns (uint256);
/**
* @notice Get all operators
* @return Array of all operators
* @return Array of all operators' metadata URLs
*/
function getAllOperators() external view returns (address[] memory, string[] memory);
/**
* @notice Set the metadata URL of an operator
* @param metadataURL The new metadata URL
*/
function setMetadataURL(
string calldata metadataURL
) external;
}// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import {IAssetClassRegistry} from "../../interfaces/middleware/IAssetClassRegistry.sol";
abstract contract AssetClassRegistry is IAssetClassRegistry, Ownable {
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.UintSet;
struct AssetClass {
EnumerableSet.AddressSet assets;
uint256 minValidatorStake;
uint256 maxValidatorStake;
}
EnumerableSet.UintSet internal assetClassIds;
mapping(uint256 => AssetClass) internal assetClasses;
constructor(
address initialOwner
) Ownable(initialOwner) {}
/// @inheritdoc IAssetClassRegistry
function addAssetClass(
uint256 assetClassId,
uint256 minValidatorStake,
uint256 maxValidatorStake,
address initialAsset
) external onlyOwner {
_addAssetClass(assetClassId, minValidatorStake, maxValidatorStake, initialAsset);
}
/// @inheritdoc IAssetClassRegistry
function addAssetToClass(uint256 assetClassId, address asset) external onlyOwner {
if (!assetClassIds.contains(assetClassId)) {
revert AssetClassRegistry__AssetClassNotFound();
}
if (asset == address(0)) {
revert AssetClassRegistry__InvalidAsset();
}
_addAssetToClass(assetClassId, asset);
}
/// @inheritdoc IAssetClassRegistry
function removeAssetFromClass(uint256 assetClassId, address asset) public virtual onlyOwner {
_removeAssetFromClass(assetClassId, asset);
}
/// @inheritdoc IAssetClassRegistry
function removeAssetClass(
uint256 assetClassId
) public virtual onlyOwner {
_removeAssetClass(assetClassId);
}
/// @inheritdoc IAssetClassRegistry
function getClassAssets(
uint256 assetClassId
) external view returns (address[] memory) {
if (!assetClassIds.contains(assetClassId)) {
revert AssetClassRegistry__AssetClassNotFound();
}
return assetClasses[assetClassId].assets.values();
}
/// @inheritdoc IAssetClassRegistry
function getClassStakingRequirements(
uint256 assetClassId
) external view returns (uint256 minStake, uint256 maxStake) {
if (!assetClassIds.contains(assetClassId)) {
revert AssetClassRegistry__AssetClassNotFound();
}
AssetClass storage cls = assetClasses[assetClassId];
return (cls.minValidatorStake, cls.maxValidatorStake);
}
function _addAssetClass(
uint256 assetClassId,
uint256 minValidatorStake,
uint256 maxValidatorStake,
address initialAsset
) internal {
if (initialAsset == address(0)) {
revert AssetClassRegistry__InvalidAsset();
}
if (assetClassId == 1 && minValidatorStake > maxValidatorStake) {
revert AssetClassRegistry__InvalidStakingRequirements();
}
bool added = assetClassIds.add(assetClassId);
if (!added) {
revert AssetClassRegistry__AssetClassAlreadyExists();
}
AssetClass storage cls = assetClasses[assetClassId];
cls.minValidatorStake = minValidatorStake;
cls.maxValidatorStake = maxValidatorStake;
emit AssetClassAdded(assetClassId, minValidatorStake, maxValidatorStake);
_addAssetToClass(assetClassId, initialAsset);
}
function _addAssetToClass(uint256 assetClassId, address asset) internal {
AssetClass storage cls = assetClasses[assetClassId];
bool added = cls.assets.add(asset);
if (!added) {
revert AssetClassRegistry__AssetAlreadyRegistered();
}
emit AssetAdded(assetClassId, asset);
}
function _removeAssetFromClass(uint256 assetClassId, address asset) internal {
AssetClass storage cls = assetClasses[assetClassId];
bool assetFound = cls.assets.remove(asset);
if (!assetFound) {
if (!assetClassIds.contains(assetClassId)) {
revert AssetClassRegistry__AssetClassNotFound();
}
revert AssetClassRegistry__AssetNotFound();
}
emit AssetRemoved(assetClassId, asset);
}
function _removeAssetClass(
uint256 assetClassId
) internal {
if (assetClassId == 1) {
revert AssetClassRegistry__AssetIsPrimaryAssetClass(assetClassId);
}
if (assetClasses[assetClassId].assets.length() != 0) {
revert AssetClassRegistry__AssetsStillExist();
}
bool removed = assetClassIds.remove(assetClassId);
if (!removed) {
revert AssetClassRegistry__AssetClassNotFound();
}
delete assetClasses[assetClassId];
emit AssetClassRemoved(assetClassId);
}
function isAssetInClass(uint256 assetClassId, address asset) external view returns (bool) {
if (!assetClassIds.contains(assetClassId)) {
revert AssetClassRegistry__AssetClassNotFound();
}
return assetClasses[assetClassId].assets.contains(asset);
}
function getAssetClassIds() external view returns (uint96[] memory) {
uint256[] memory ids = assetClassIds.values();
uint96[] memory assetClassIDs = new uint96[](ids.length);
for (uint256 i = 0; i < ids.length; i++) {
assetClassIDs[i] = uint96(ids[i]);
}
return assetClassIDs;
}
receive() external payable {}
}// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity 0.8.25;
library StakeConversion {
error MiddlewareUtils__OverflowInStakeToWeight();
/**
* @notice Convert a full 256-bit stake amount into a 64-bit weight
* @dev Anything < WEIGHT_SCALE_FACTOR becomes 0
*/
function stakeToWeight(uint256 stakeAmount, uint256 scaleFactor) internal pure returns (uint64) {
uint256 weight = stakeAmount / scaleFactor;
if (weight > type(uint64).max) {
revert MiddlewareUtils__OverflowInStakeToWeight();
}
return uint64(weight);
}
/**
* @notice Convert a 64-bit weight back into its 256-bit stake amount
*/
function weightToStake(uint64 weight, uint256 scaleFactor) internal pure returns (uint256) {
return uint256(weight) * scaleFactor;
}
/**
* @notice Remove the node from the dynamic array (swap and pop).
* @dev Matches logic from _removeNodeFromArray() unchanged.
*/
function removeNodeFromArray(bytes32[] storage arr, bytes32 nodeId) internal {
uint256 arrLength = arr.length;
for (uint256 i = 0; i < arrLength; i++) {
if (arr[i] == nodeId) {
uint256 lastIndex;
unchecked {
lastIndex = arrLength - 1;
}
if (i != lastIndex) {
arr[i] = arr[lastIndex];
}
arr.pop();
break;
}
}
}
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
import {IRegistry} from "./IRegistry.sol";
interface IMigratablesFactory is IRegistry {
error MigratableFactory__AlreadyBlacklisted();
error MigratableFactory__AlreadyWhitelisted();
error MigratableFactory__InvalidImplementation();
error MigratableFactory__InvalidVersion();
error MigratableFactory__NotOwner();
error MigratableFactory__OldVersion();
error MigratableFactory__VersionBlacklisted();
/**
* @notice Emitted when a new implementation is whitelisted.
* @param implementation address of the new implementation
*/
event Whitelist(address indexed implementation);
/**
* @notice Emitted when a version is blacklisted (e.g., in case of invalid implementation).
* @param version version that was blacklisted
* @dev The given version is still deployable.
*/
event Blacklist(uint64 indexed version);
/**
* @notice Emitted when an entity is migrated to a new version.
* @param entity address of the entity
* @param newVersion new version of the entity
*/
event Migrate(address indexed entity, uint64 newVersion);
/**
* @notice Get the last available version.
* @return version of the last implementation
* @dev If zero, no implementations are whitelisted.
*/
function lastVersion() external view returns (uint64);
/**
* @notice Get the implementation for a given version.
* @param version version to get the implementation for
* @return address of the implementation
* @dev Reverts when an invalid version.
*/
function implementation(
uint64 version
) external view returns (address);
/**
* @notice Get if a version is blacklisted (e.g., in case of invalid implementation).
* @param version version to check
* @return whether the version is blacklisted
* @dev The given version is still deployable.
*/
function blacklisted(
uint64 version
) external view returns (bool);
/**
* @notice Whitelist a new implementation for entities.
* @param implementation address of the new implementation
*/
function whitelist(
address implementation
) external;
/**
* @notice Blacklist a version of entities.
* @param version version to blacklist
* @dev The given version will still be deployable.
*/
function blacklist(
uint64 version
) external;
/**
* @notice Create a new entity at the factory.
* @param version entity's version to use
* @param owner initial owner of the entity
* @param data initial data for the entity creation
* @param delegatorFactory address of the delegator factory
* @param slasherFactory address of the slashers factory
* @return address of the entity
* @dev CREATE2 salt is constructed from the given parameters.
*/
function create(
uint64 version,
address owner,
bytes calldata data,
address delegatorFactory,
address slasherFactory
) external returns (address);
/**
* @notice Migrate a given entity to a given newer version.
* @param entity address of the entity to migrate`
* @param newVersion new version to migrate to
* @param data some data to reinitialize the contract with
* @dev Only the entity's owner can call this function.
*/
function migrate(address entity, uint64 newVersion, bytes calldata data) external;
}// 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;
}// 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;
}
}// 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;
}
}// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity 0.8.25;
import {
IValidatorManager,
Validator,
ValidatorChurnPeriod,
ValidatorManagerSettings,
ValidatorRegistrationInput
} from "@avalabs/icm-contracts/validator-manager/interfaces/IValidatorManager.sol";
/**
* @dev Balancer Validator Manager settings, used to initialize the Balancer Validator Manager
*/
struct BalancerValidatorManagerSettings {
ValidatorManagerSettings baseSettings;
address initialOwner;
address initialSecurityModule;
uint64 initialSecurityModuleMaxWeight;
bytes32[] migratedValidations;
}
/**
* @title IBalancerValidatorManager
* @author ADDPHO
* @notice Interface for Balancer Validator Manager contracts
* @custom:security-contact [email protected]
*/
interface IBalancerValidatorManager is IValidatorManager {
/**
* @notice Emitted when a security module is registered, updated, or removed (maxWeight = 0)
* @param securityModule The address of the security module
* @param maxWeight The maximum total weight allowed for validators managed by this module
*/
event SetUpSecurityModule(address indexed securityModule, uint64 maxWeight);
error BalancerValidatorManager__MigratedValidationsTotalWeightMismatch(
uint64 migratedValidationsTotalWeight, uint64 currentL1TotalWeight
);
error BalancerValidatorManager__SecurityModuleAlreadyRegistered(address securityModule);
error BalancerValidatorManager__SecurityModuleNotRegistered(address securityModule);
error BalancerValidatorManager__SecurityModuleMaxWeightExceeded(
address securityModule, uint64 weight, uint64 maxWeight
);
error BalancerValidatorManager__SecurityModuleNewMaxWeightLowerThanCurrentWeight(
address securityModule, uint64 newMaxWeight, uint64 currentWeight
);
error BalancerValidatorManager__NewWeightIsZero();
error BalancerValidatorManager__ValidatorNotBelongingToSecurityModule(
bytes32 validationID, address securityModule
);
error BalancerValidatorManager__PendingWeightUpdate(bytes32 validationID);
error BalancerValidatorManager__NoPendingWeightUpdate(bytes32 validationID);
error BalancerValidatorManager__InvalidNonce(uint64 nonce);
error BalancerValidatorManager__WeightUpdateMismatch(uint64 newWeight, uint64 expectedWeight);
/**
* @notice Returns the ValidatorManager churn period in seconds
* @return churnPeriodSeconds The churn period in seconds
*/
function getChurnPeriodSeconds() external view returns (uint64 churnPeriodSeconds);
/**
* @notice Returns the maximum churn rate per churn period (in percentage)
* @return maximumChurnPercentage The maximum churn percentage
*/
function getMaximumChurnPercentage() external view returns (uint64 maximumChurnPercentage);
/**
* @notice Returns the current churn period
* @return churnPeriod The current churn period
*/
function getCurrentChurnPeriod()
external
view
returns (ValidatorChurnPeriod memory churnPeriod);
/**
* @notice Returns the list of registered security modules
* @return securityModules The list of registered security modules
*/
function getSecurityModules() external view returns (address[] memory securityModules);
/**
* @notice Returns the weight associated with a security module
* @param securityModule The address of the security module
* @return weight The weight of the security module
*/
function getSecurityModuleWeights(
address securityModule
) external view returns (uint64 weight, uint64 maxWeight);
/**
* @notice Returns whether a validator has a pending weight update
* @param validationID The ID of the validator
* @return Whether the validator has a pending weight update
*/
function isValidatorPendingWeightUpdate(
bytes32 validationID
) external view returns (bool);
/**
* @notice Registers a new security module with a maximum weight limit
* @param securityModule The address of the security module to register
* @param maxWeight The maximum total weight allowed for validators managed by this module
*/
function setUpSecurityModule(address securityModule, uint64 maxWeight) external;
/**
* @notice Begins the validator registration process, and sets the {weight} of the validator.
* @param registrationInput The inputs for a validator registration.
* @param weight The weight of the validator being registered.
* @return validationID The ID of the validator registration.
*/
/**
* @notice Begins the validator registration process, and sets the {weight} of the validator.
* @dev Can only be called by registered security modules
* @param registrationInput The inputs for a validator registration.
* @param weight The weight of the validator being registered.
* @return validationID The ID of the validator registration.
*/
function initializeValidatorRegistration(
ValidatorRegistrationInput calldata registrationInput,
uint64 weight
) external returns (bytes32 validationID);
/**
* @notice Begins the process of ending an active validation period. The validation period must have been previously
* started by a successful call to {completeValidatorRegistration} with the given validationID.
* @dev Can only be called by the security module that registered the validator
* @param validationID The ID of the validation period being ended.
* @return validator The validator that is being ended
*/
function initializeEndValidation(
bytes32 validationID
) external returns (Validator memory validator);
/**
* @notice Initiates a weight update for a validator
* @dev Can only be called by the security module that registered the validator
* @param validationID The ID of the validation period being updated
* @param newWeight The new weight to set for the validator
*/
function initializeValidatorWeightUpdate(
bytes32 validationID,
uint64 newWeight
) external returns (Validator memory validator);
/**
* @notice Completes a pending validator weight update after P-Chain confirmation
* @dev Can only be called by the security module that registered the validator
* @param validationID The ID of the validation period being updated
* @param messageIndex The index of the Warp message containing the weight update confirmation
*/
function completeValidatorWeightUpdate(bytes32 validationID, uint32 messageIndex) external;
/**
* @notice Resends a pending validator weight update message to the P-Chain
* @param validationID The ID of the validation period being updated
*/
function resendValidatorWeightUpdate(
bytes32 validationID
) external;
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.25;
import {ValidatorMessages} from "./ValidatorMessages.sol";
import {
InitialValidator,
IValidatorManager,
PChainOwner,
ConversionData,
Validator,
ValidatorChurnPeriod,
ValidatorManagerSettings,
ValidatorRegistrationInput,
ValidatorStatus
} from "./interfaces/IValidatorManager.sol";
import {
IWarpMessenger,
WarpMessage
} from "@avalabs/[email protected]/contracts/interfaces/IWarpMessenger.sol";
import {ContextUpgradeable} from
"@openzeppelin/[email protected]/utils/ContextUpgradeable.sol";
import {Initializable} from
"@openzeppelin/[email protected]/proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IValidatorManager} interface.
*
* @custom:security-contact https://github.com/ava-labs/icm-contracts/blob/main/SECURITY.md
*/
abstract contract ValidatorManager is Initializable, ContextUpgradeable, IValidatorManager {
// solhint-disable private-vars-leading-underscore
/// @custom:storage-location erc7201:avalanche-icm.storage.ValidatorManager
struct ValidatorManagerStorage {
/// @notice The l1ID associated with this validator manager.
bytes32 _l1ID;
/// @notice The number of seconds after which to reset the churn tracker.
uint64 _churnPeriodSeconds;
/// @notice The maximum churn rate allowed per churn period.
uint8 _maximumChurnPercentage;
/// @notice The churn tracker used to track the amount of stake added or removed in the churn period.
ValidatorChurnPeriod _churnTracker;
/// @notice Maps the validationID to the registration message such that the message can be re-sent if needed.
mapping(bytes32 => bytes) _pendingRegisterValidationMessages;
/// @notice Maps the validationID to the validator information.
mapping(bytes32 => Validator) _validationPeriods;
/// @notice Maps the nodeID to the validationID for validation periods that have not ended.
mapping(bytes => bytes32) _registeredValidators;
/// @notice Boolean that indicates if the initial validator set has been set.
bool _initializedValidatorSet;
}
// solhint-enable private-vars-leading-underscore
// keccak256(abi.encode(uint256(keccak256("avalanche-icm.storage.ValidatorManager")) - 1)) & ~bytes32(uint256(0xff));
bytes32 public constant VALIDATOR_MANAGER_STORAGE_LOCATION =
0xe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb00;
uint8 public constant MAXIMUM_CHURN_PERCENTAGE_LIMIT = 20;
uint64 public constant MAXIMUM_REGISTRATION_EXPIRY_LENGTH = 2 days;
uint32 public constant ADDRESS_LENGTH = 20; // This is only used as a packed uint32
uint8 public constant BLS_PUBLIC_KEY_LENGTH = 48;
bytes32 public constant P_CHAIN_BLOCKCHAIN_ID = bytes32(0);
error InvalidValidatorManagerAddress(address validatorManagerAddress);
error InvalidWarpOriginSenderAddress(address senderAddress);
error InvalidValidatorManagerBlockchainID(bytes32 blockchainID);
error InvalidWarpSourceChainID(bytes32 sourceChainID);
error InvalidRegistrationExpiry(uint64 registrationExpiry);
error InvalidInitializationStatus();
error InvalidMaximumChurnPercentage(uint8 maximumChurnPercentage);
error InvalidBLSKeyLength(uint256 length);
error InvalidNodeID(bytes nodeID);
error InvalidConversionID(bytes32 encodedConversionID, bytes32 expectedConversionID);
error InvalidTotalWeight(uint64 weight);
error InvalidValidationID(bytes32 validationID);
error InvalidValidatorStatus(ValidatorStatus status);
error InvalidWarpMessage();
error MaxChurnRateExceeded(uint64 churnAmount);
error NodeAlreadyRegistered(bytes nodeID);
error UnexpectedRegistrationStatus(bool validRegistration);
error InvalidPChainOwnerThreshold(uint256 threshold, uint256 addressesLength);
error PChainOwnerAddressesNotSorted();
// solhint-disable ordering
/**
* @dev This storage is visible to child contracts for convenience.
* External getters would be better practice, but code size limitations are preventing this.
* Child contracts should probably never write to this storage.
*/
function _getValidatorManagerStorage()
internal
pure
returns (ValidatorManagerStorage storage $)
{
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := VALIDATOR_MANAGER_STORAGE_LOCATION
}
}
/**
* @notice Warp precompile used for sending and receiving Warp messages.
*/
IWarpMessenger public constant WARP_MESSENGER =
IWarpMessenger(0x0200000000000000000000000000000000000005);
// solhint-disable-next-line func-name-mixedcase
function __ValidatorManager_init(ValidatorManagerSettings calldata settings)
internal
onlyInitializing
{
__Context_init();
__ValidatorManager_init_unchained(settings);
}
// solhint-disable-next-line func-name-mixedcase
function __ValidatorManager_init_unchained(ValidatorManagerSettings calldata settings)
internal
onlyInitializing
{
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
$._l1ID = settings.l1ID;
if (
settings.maximumChurnPercentage > MAXIMUM_CHURN_PERCENTAGE_LIMIT
|| settings.maximumChurnPercentage == 0
) {
revert InvalidMaximumChurnPercentage(settings.maximumChurnPercentage);
}
$._maximumChurnPercentage = settings.maximumChurnPercentage;
$._churnPeriodSeconds = settings.churnPeriodSeconds;
}
modifier initializedValidatorSet() {
if (!_getValidatorManagerStorage()._initializedValidatorSet) {
revert InvalidInitializationStatus();
}
_;
}
/**
* @notice See {IValidatorManager-initializeValidatorSet}.
*/
function initializeValidatorSet(
ConversionData calldata conversionData,
uint32 messageIndex
) external {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
if ($._initializedValidatorSet) {
revert InvalidInitializationStatus();
}
// Check that the blockchainID and validator manager address in the ConversionData correspond to this contract.
// Other validation checks are done by the P-Chain when converting the L1, so are not required here.
if (conversionData.validatorManagerBlockchainID != WARP_MESSENGER.getBlockchainID()) {
revert InvalidValidatorManagerBlockchainID(conversionData.validatorManagerBlockchainID);
}
if (address(conversionData.validatorManagerAddress) != address(this)) {
revert InvalidValidatorManagerAddress(address(conversionData.validatorManagerAddress));
}
uint256 numInitialValidators = conversionData.initialValidators.length;
uint64 totalWeight;
for (uint32 i; i < numInitialValidators; ++i) {
InitialValidator memory initialValidator = conversionData.initialValidators[i];
if ($._registeredValidators[initialValidator.nodeID] != bytes32(0)) {
revert NodeAlreadyRegistered(initialValidator.nodeID);
}
// Validation ID of the initial validators is the sha256 hash of the
// convert subnet to L1 tx ID and the index of the initial validator.
bytes32 validationID = sha256(abi.encodePacked(conversionData.l1ID, i));
// Save the initial validator as an active validator.
$._registeredValidators[initialValidator.nodeID] = validationID;
$._validationPeriods[validationID].status = ValidatorStatus.Active;
$._validationPeriods[validationID].nodeID = initialValidator.nodeID;
$._validationPeriods[validationID].startingWeight = initialValidator.weight;
$._validationPeriods[validationID].messageNonce = 0;
$._validationPeriods[validationID].weight = initialValidator.weight;
$._validationPeriods[validationID].startedAt = uint64(block.timestamp);
$._validationPeriods[validationID].endedAt = 0;
totalWeight += initialValidator.weight;
emit InitialValidatorCreated(
validationID, initialValidator.nodeID, initialValidator.weight
);
}
$._churnTracker.totalWeight = totalWeight;
// Rearranged equation for totalWeight < (100 / $._maximumChurnPercentage)
// Total weight must be above this value in order to not trigger churn limits with an added/removed weight of 1.
if (totalWeight * $._maximumChurnPercentage < 100) {
revert InvalidTotalWeight(totalWeight);
}
// Verify that the sha256 hash of the L1 conversion data matches with the Warp message's conversionID.
bytes32 conversionID = ValidatorMessages.unpackSubnetToL1ConversionMessage(
_getPChainWarpMessage(messageIndex).payload
);
bytes memory encodedConversion = ValidatorMessages.packConversionData(conversionData);
bytes32 encodedConversionID = sha256(encodedConversion);
if (encodedConversionID != conversionID) {
revert InvalidConversionID(encodedConversionID, conversionID);
}
$._initializedValidatorSet = true;
}
function _validatePChainOwner(PChainOwner calldata pChainOwner) internal pure {
// If threshold is 0, addresses must be empty.
if (pChainOwner.threshold == 0 && pChainOwner.addresses.length != 0) {
revert InvalidPChainOwnerThreshold(pChainOwner.threshold, pChainOwner.addresses.length);
}
// Threshold must be less than or equal to the number of addresses.
if (pChainOwner.threshold > pChainOwner.addresses.length) {
revert InvalidPChainOwnerThreshold(pChainOwner.threshold, pChainOwner.addresses.length);
}
// Addresses must be sorted in ascending order
for (uint256 i = 1; i < pChainOwner.addresses.length; i++) {
// Compare current address with the previous one
if (pChainOwner.addresses[i] < pChainOwner.addresses[i - 1]) {
revert PChainOwnerAddressesNotSorted();
}
}
}
/**
* @notice Begins the validator registration process, and sets the initial weight for the validator.
* This is the only method related to validator registration and removal that needs the initializedValidatorSet
* modifier. All others are guarded by checking the validator status changes initialized in this function.
* @param input The inputs for a validator registration.
* @param weight The weight of the validator being registered.
*/
function _initializeValidatorRegistration(
ValidatorRegistrationInput calldata input,
uint64 weight
) internal virtual initializedValidatorSet returns (bytes32) {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
if (
input.registrationExpiry <= block.timestamp
|| input.registrationExpiry >= block.timestamp + MAXIMUM_REGISTRATION_EXPIRY_LENGTH
) {
revert InvalidRegistrationExpiry(input.registrationExpiry);
}
// Ensure the new validator doesn't overflow the total weight
if (uint256(weight) + uint256($._churnTracker.totalWeight) > type(uint64).max) {
revert InvalidTotalWeight(weight);
}
_validatePChainOwner(input.remainingBalanceOwner);
_validatePChainOwner(input.disableOwner);
// Ensure the nodeID is not the zero address, and is not already an active validator.
if (input.blsPublicKey.length != BLS_PUBLIC_KEY_LENGTH) {
revert InvalidBLSKeyLength(input.blsPublicKey.length);
}
if (input.nodeID.length == 0) {
revert InvalidNodeID(input.nodeID);
}
if ($._registeredValidators[input.nodeID] != bytes32(0)) {
revert NodeAlreadyRegistered(input.nodeID);
}
// Check that adding this validator would not exceed the maximum churn rate.
_checkAndUpdateChurnTracker(weight, 0);
(bytes32 validationID, bytes memory registerL1ValidatorMessage) = ValidatorMessages
.packRegisterL1ValidatorMessage(
ValidatorMessages.ValidationPeriod({
l1ID: $._l1ID,
nodeID: input.nodeID,
blsPublicKey: input.blsPublicKey,
remainingBalanceOwner: input.remainingBalanceOwner,
disableOwner: input.disableOwner,
registrationExpiry: input.registrationExpiry,
weight: weight
})
);
$._pendingRegisterValidationMessages[validationID] = registerL1ValidatorMessage;
$._registeredValidators[input.nodeID] = validationID;
// Submit the message to the Warp precompile.
bytes32 messageID = WARP_MESSENGER.sendWarpMessage(registerL1ValidatorMessage);
$._validationPeriods[validationID].status = ValidatorStatus.PendingAdded;
$._validationPeriods[validationID].nodeID = input.nodeID;
$._validationPeriods[validationID].startingWeight = weight;
$._validationPeriods[validationID].messageNonce = 0;
$._validationPeriods[validationID].weight = weight;
$._validationPeriods[validationID].startedAt = 0; // The validation period only starts once the registration is acknowledged.
$._validationPeriods[validationID].endedAt = 0;
emit ValidationPeriodCreated(
validationID, input.nodeID, messageID, weight, input.registrationExpiry
);
return validationID;
}
/**
* @notice See {IValidatorManager-resendRegisterValidatorMessage}.
*/
function resendRegisterValidatorMessage(bytes32 validationID) external {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
// The initial validator set must have been set already to have pending register validation messages.
if ($._pendingRegisterValidationMessages[validationID].length == 0) {
revert InvalidValidationID(validationID);
}
if ($._validationPeriods[validationID].status != ValidatorStatus.PendingAdded) {
revert InvalidValidatorStatus($._validationPeriods[validationID].status);
}
// Submit the message to the Warp precompile.
WARP_MESSENGER.sendWarpMessage($._pendingRegisterValidationMessages[validationID]);
}
/**
* @notice See {IValidatorManager-completeValidatorRegistration}.
*/
function completeValidatorRegistration(uint32 messageIndex) external {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
(bytes32 validationID, bool validRegistration) = ValidatorMessages
.unpackL1ValidatorRegistrationMessage(_getPChainWarpMessage(messageIndex).payload);
if (!validRegistration) {
revert UnexpectedRegistrationStatus(validRegistration);
}
// The initial validator set must have been set already to have pending register validation messages.
if ($._pendingRegisterValidationMessages[validationID].length == 0) {
revert InvalidValidationID(validationID);
}
if ($._validationPeriods[validationID].status != ValidatorStatus.PendingAdded) {
revert InvalidValidatorStatus($._validationPeriods[validationID].status);
}
delete $._pendingRegisterValidationMessages[validationID];
$._validationPeriods[validationID].status = ValidatorStatus.Active;
$._validationPeriods[validationID].startedAt = uint64(block.timestamp);
emit ValidationPeriodRegistered(
validationID, $._validationPeriods[validationID].weight, block.timestamp
);
}
/**
* @notice Returns a validation ID registered to the given nodeID
* @param nodeID ID of the node associated with the validation ID
*/
function registeredValidators(bytes calldata nodeID) public view returns (bytes32) {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
return $._registeredValidators[nodeID];
}
/**
* @notice Returns a validator registered to the given validationID
* @param validationID ID of the validation period associated with the validator
*/
function getValidator(bytes32 validationID) public view returns (Validator memory) {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
return $._validationPeriods[validationID];
}
/**
* @notice Begins the process of ending an active validation period. The validation period must have been previously
* started by a successful call to {completeValidatorRegistration} with the given validationID.
* Any rewards for this validation period will stop accruing when this function is called.
* @param validationID The ID of the validation period being ended.
*/
function _initializeEndValidation(bytes32 validationID)
internal
virtual
returns (Validator memory)
{
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
// Ensure the validation period is active.
// The initial validator set must have been set already to have active validators.
Validator memory validator = $._validationPeriods[validationID];
if (validator.status != ValidatorStatus.Active) {
revert InvalidValidatorStatus($._validationPeriods[validationID].status);
}
// Update the validator status to pending removal.
// They are not removed from the active validators mapping until the P-Chain acknowledges the removal.
validator.status = ValidatorStatus.PendingRemoved;
// Set the end time of the validation period, since it is no longer known to be an active validator
// on the P-Chain.
validator.endedAt = uint64(block.timestamp);
// Save the validator updates.
$._validationPeriods[validationID] = validator;
(, bytes32 messageID) = _setValidatorWeight(validationID, 0);
// Emit the event to signal the start of the validator removal process.
emit ValidatorRemovalInitialized(validationID, messageID, validator.weight, block.timestamp);
return validator;
}
/**
* @notice See {IValidatorManager-resendEndValidatorMessage}.
*/
function resendEndValidatorMessage(bytes32 validationID) external {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
Validator memory validator = $._validationPeriods[validationID];
// The initial validator set must have been set already to have pending end validation messages.
if (validator.status != ValidatorStatus.PendingRemoved) {
revert InvalidValidatorStatus($._validationPeriods[validationID].status);
}
WARP_MESSENGER.sendWarpMessage(
ValidatorMessages.packL1ValidatorWeightMessage(validationID, validator.messageNonce, 0)
);
}
/**
* @notice Completes the process of ending a validation period by receiving an acknowledgement from the P-Chain
* that the validation ID is not active and will never be active in the future.
* Note: that this function can be used for successful validation periods that have been explicitly
* ended by calling {initializeEndValidation} or for validation periods that never began on the P-Chain due to the
* {registrationExpiry} being reached.
* @return (Validation ID, Validator instance) representing the completed validation period.
*/
function _completeEndValidation(uint32 messageIndex)
internal
returns (bytes32, Validator memory)
{
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
// Get the Warp message.
(bytes32 validationID, bool validRegistration) = ValidatorMessages
.unpackL1ValidatorRegistrationMessage(_getPChainWarpMessage(messageIndex).payload);
if (validRegistration) {
revert UnexpectedRegistrationStatus(validRegistration);
}
Validator memory validator = $._validationPeriods[validationID];
// The validation status is PendingRemoved if validator removal was initiated with a call to {initiateEndValidation}.
// The validation status is PendingAdded if the validator was never registered on the P-Chain.
// The initial validator set must have been set already to have pending validation messages.
if (
validator.status != ValidatorStatus.PendingRemoved
&& validator.status != ValidatorStatus.PendingAdded
) {
revert InvalidValidatorStatus(validator.status);
}
if (validator.status == ValidatorStatus.PendingRemoved) {
validator.status = ValidatorStatus.Completed;
} else {
validator.status = ValidatorStatus.Invalidated;
}
// Remove the validator from the registered validators mapping.
delete $._registeredValidators[validator.nodeID];
// Update the validator.
$._validationPeriods[validationID] = validator;
// Emit event.
emit ValidationPeriodEnded(validationID, validator.status);
return (validationID, validator);
}
function _incrementAndGetNonce(bytes32 validationID) internal returns (uint64) {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
return ++$._validationPeriods[validationID].messageNonce;
}
function _getPChainWarpMessage(uint32 messageIndex)
internal
view
returns (WarpMessage memory)
{
(WarpMessage memory warpMessage, bool valid) =
WARP_MESSENGER.getVerifiedWarpMessage(messageIndex);
if (!valid) {
revert InvalidWarpMessage();
}
// Must match to P-Chain blockchain id, which is 0.
if (warpMessage.sourceChainID != P_CHAIN_BLOCKCHAIN_ID) {
revert InvalidWarpSourceChainID(warpMessage.sourceChainID);
}
if (warpMessage.originSenderAddress != address(0)) {
revert InvalidWarpOriginSenderAddress(warpMessage.originSenderAddress);
}
return warpMessage;
}
function _setValidatorWeight(
bytes32 validationID,
uint64 newWeight
) internal returns (uint64, bytes32) {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
uint64 validatorWeight = $._validationPeriods[validationID].weight;
// Check that changing the validator weight would not exceed the maximum churn rate.
_checkAndUpdateChurnTracker(newWeight, validatorWeight);
uint64 nonce = _incrementAndGetNonce(validationID);
$._validationPeriods[validationID].weight = newWeight;
// Submit the message to the Warp precompile.
bytes32 messageID = WARP_MESSENGER.sendWarpMessage(
ValidatorMessages.packL1ValidatorWeightMessage(validationID, nonce, newWeight)
);
emit ValidatorWeightUpdate({
validationID: validationID,
nonce: nonce,
weight: newWeight,
setWeightMessageID: messageID
});
return (nonce, messageID);
}
function _getChurnPeriodSeconds() internal view returns (uint64) {
return _getValidatorManagerStorage()._churnPeriodSeconds;
}
/**
* @dev Helper function to check if the stake weight to be added or removed would exceed the maximum stake churn
* rate for the past churn period. If the churn rate is exceeded, the function will revert. If the churn rate is
* not exceeded, the function will update the churn tracker with the new weight.
*/
function _checkAndUpdateChurnTracker(
uint64 newValidatorWeight,
uint64 oldValidatorWeight
) private {
ValidatorManagerStorage storage $ = _getValidatorManagerStorage();
uint64 weightChange;
if (newValidatorWeight > oldValidatorWeight) {
weightChange = newValidatorWeight - oldValidatorWeight;
} else {
weightChange = oldValidatorWeight - newValidatorWeight;
}
uint256 currentTime = block.timestamp;
ValidatorChurnPeriod memory churnTracker = $._churnTracker;
if (
churnTracker.startedAt == 0
|| currentTime >= churnTracker.startedAt + $._churnPeriodSeconds
) {
churnTracker.churnAmount = weightChange;
churnTracker.startedAt = currentTime;
churnTracker.initialWeight = churnTracker.totalWeight;
} else {
// Churn is always additive whether the weight is being added or removed.
churnTracker.churnAmount += weightChange;
}
// Rearranged equation of maximumChurnPercentage >= currentChurnPercentage to avoid integer division truncation.
if ($._maximumChurnPercentage * churnTracker.initialWeight < churnTracker.churnAmount * 100)
{
revert MaxChurnRateExceeded(churnTracker.churnAmount);
}
// Two separate calculations because we're using uints and (newValidatorWeight - oldValidatorWeight) could underflow.
churnTracker.totalWeight += newValidatorWeight;
churnTracker.totalWeight -= oldValidatorWeight;
// Rearranged equation for totalWeight < (100 / $._maximumChurnPercentage)
// Total weight must be above this value in order to not trigger churn limits with an added/removed weight of 1.
if (churnTracker.totalWeight * $._maximumChurnPercentage < 100) {
revert InvalidTotalWeight(churnTracker.totalWeight);
}
$._churnTracker = churnTracker;
}
}// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.25;
import {PChainOwner, ConversionData} from "./interfaces/IValidatorManager.sol";
/**
* @dev Packing utilities for the ICM message types used by the Validator Manager contracts, as specified in ACP-77:
* https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/77-reinventing-subnets
*/
library ValidatorMessages {
// The information that uniquely identifies an L1 validation period.
// The validationID is the SHA-256 hash of the concatenation of the CODEC_ID,
// REGISTER_L1_VALIDATOR_MESSAGE_TYPE_ID, and the concatenated ValidationPeriod fields.
struct ValidationPeriod {
bytes32 l1ID;
bytes nodeID;
bytes blsPublicKey;
uint64 registrationExpiry;
PChainOwner remainingBalanceOwner;
PChainOwner disableOwner;
uint64 weight;
}
// The P-Chain uses a hardcoded codecID of 0 for all messages.
uint16 internal constant CODEC_ID = 0;
// The P-Chain signs a SubnetToL1ConversionMessage that is used to verify the L1's initial validators.
uint32 internal constant SUBNET_TO_L1_CONVERSION_MESSAGE_TYPE_ID = 0;
// L1s send a RegisterL1ValidatorMessage to the P-Chain to register a validator.
uint32 internal constant REGISTER_L1_VALIDATOR_MESSAGE_TYPE_ID = 1;
// The P-Chain responds with a RegisterL1ValidatorMessage indicating whether the registration was successful
// for the given validation ID.
uint32 internal constant L1_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID = 2;
// L1s can send a L1ValidatorWeightMessage to the P-Chain to update a validator's weight.
// The P-Chain responds with another L1ValidatorWeightMessage acknowledging the weight update.
uint32 internal constant L1_VALIDATOR_WEIGHT_MESSAGE_TYPE_ID = 3;
// The L1 will self-sign a ValidationUptimeMessage to be provided when a validator is initiating
// the end of their validation period.
uint32 internal constant VALIDATION_UPTIME_MESSAGE_TYPE_ID = 0;
error InvalidMessageLength(uint32 actual, uint32 expected);
error InvalidCodecID(uint32 id);
error InvalidMessageType();
error InvalidBLSPublicKey();
/**
* @notice Packs a SubnetToL1ConversionMessage message into a byte array.
* The message format specification is:
* +--------------------+----------+----------+
* | codecID : uint16 | 2 bytes |
* +--------------------+----------+----------+
* | typeID : uint32 | 4 bytes |
* +--------------------+----------+----------+
* | conversionID : [32]byte | 32 bytes |
* +--------------------+----------+----------+
* | 38 bytes |
* +----------+
*
* @param conversionID The subnet conversion ID to pack into the message.
* @return The packed message.
*/
function packSubnetToL1ConversionMessage(bytes32 conversionID)
external
pure
returns (bytes memory)
{
return abi.encodePacked(CODEC_ID, SUBNET_TO_L1_CONVERSION_MESSAGE_TYPE_ID, conversionID);
}
/**
* @notice Unpacks a byte array as a SubnetToL1ConversionMessage message.
* The message format specification is the same as the one used in above for packing.
*
* @param input The byte array to unpack.
* @return The unpacked conversionID.
*/
function unpackSubnetToL1ConversionMessage(bytes memory input)
external
pure
returns (bytes32)
{
if (input.length != 38) {
revert InvalidMessageLength(uint32(input.length), 38);
}
// Unpack the codec ID
uint16 codecID;
for (uint256 i; i < 2; ++i) {
codecID |= uint16(uint8(input[i])) << uint16((8 * (1 - i)));
}
if (codecID != CODEC_ID) {
revert InvalidCodecID(codecID);
}
// Unpack the type ID
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i + 2])) << uint32((8 * (3 - i)));
}
if (typeID != SUBNET_TO_L1_CONVERSION_MESSAGE_TYPE_ID) {
revert InvalidMessageType();
}
// Unpack the conversionID
bytes32 conversionID;
for (uint256 i; i < 32; ++i) {
conversionID |= bytes32(uint256(uint8(input[i + 6])) << (8 * (31 - i)));
}
return conversionID;
}
/**
* @notice Packs ConversionData into a byte array.
* This byte array is the SHA256 pre-image of the conversionID hash
* The message format specification is:
*
* ConversionData:
* +----------------+-----------------+--------------------------------------------------------+
* | codecID : uint16 | 2 bytes |
* +----------------+-----------------+--------------------------------------------------------+
* | l1ID : [32]byte | 32 bytes |
* +----------------+-----------------+--------------------------------------------------------+
* | managerChainID : [32]byte | 32 bytes |
* +----------------+-----------------+--------------------------------------------------------+
* | managerAddress : []byte | 4 + len(managerAddress) bytes |
* +----------------+-----------------+--------------------------------------------------------+
* | validators : []ValidatorData | 4 + sum(validatorLengths) bytes |
* +----------------+-----------------+--------------------------------------------------------+
* | 74 + len(managerAddress) + len(validatorLengths) bytes |
* +--------------------------------------------------------+
* ValidatorData:
* +--------------+----------+------------------------+
* | nodeID : []byte | 4 + len(nodeID) bytes |
* +--------------+----------+------------------------+
* | blsPublicKey : [48]byte | 48 bytes |
* +--------------+----------+------------------------+
* | weight : uint64 | 8 bytes |
* +--------------+----------+------------------------+
* | 60 + len(nodeID) bytes |
* +------------------------+
*
* @dev Input validation is skipped, since the returned value is intended to be compared
* directly with an authenticated ICM message.
* @param conversionData The struct representing data to pack into the message.
* @return The packed message.
*/
function packConversionData(ConversionData memory conversionData)
external
pure
returns (bytes memory)
{
// Hardcoded 20 is for length of the managerAddress on EVM chains
// solhint-disable-next-line func-named-parameters
bytes memory res = abi.encodePacked(
CODEC_ID,
conversionData.l1ID,
conversionData.validatorManagerBlockchainID,
uint32(20),
conversionData.validatorManagerAddress,
uint32(conversionData.initialValidators.length)
);
// The approach below of encoding initialValidators using `abi.encodePacked` in a loop
// was tested against pre-allocating the array and doing manual byte by byte packing and
// it was found to be more gas efficient.
for (uint256 i; i < conversionData.initialValidators.length; ++i) {
res = abi.encodePacked(
res,
uint32(conversionData.initialValidators[i].nodeID.length),
conversionData.initialValidators[i].nodeID,
conversionData.initialValidators[i].blsPublicKey,
conversionData.initialValidators[i].weight
);
}
return res;
}
/**
* @notice Packs a RegisterL1ValidatorMessage message into a byte array.
* The message format specification is:
*
* RegisterL1ValidatorMessage:
* +-----------------------+-------------+--------------------------------------------------------------------+
* | codecID : uint16 | 2 bytes |
* +-----------------------+-------------+--------------------------------------------------------------------+
* | typeID : uint32 | 4 bytes |
* +-----------------------+-------------+-------------------------------------------------------------------+
* | l1ID : [32]byte | 32 bytes |
* +-----------------------+-------------+--------------------------------------------------------------------+
* | nodeID : []byte | 4 + len(nodeID) bytes |
* +-----------------------+-------------+--------------------------------------------------------------------+
* | blsPublicKey : [48]byte | 48 bytes |
* +-----------------------+-------------+--------------------------------------------------------------------+
* | expiry : uint64 | 8 bytes |
* +-----------------------+-------------+--------------------------------------------------------------------+
* | remainingBalanceOwner : PChainOwner | 8 + len(addresses) * 20 bytes |
* +-----------------------+-------------+--------------------------------------------------------------------+
* | disableOwner : PChainOwner | 8 + len(addresses) * 20 bytes |
* +-----------------------+-------------+--------------------------------------------------------------------+
* | weight : uint64 | 8 bytes |
* +-----------------------+-------------+--------------------------------------------------------------------+
* | 122 + len(nodeID) + (len(addresses1) + len(addresses2)) * 20 bytes |
* +--------------------------------------------------------------------+
*
* PChainOwner:
* +-----------+------------+-------------------------------+
* | threshold : uint32 | 4 bytes |
* +-----------+------------+-------------------------------+
* | addresses : [][20]byte | 4 + len(addresses) * 20 bytes |
* +-----------+------------+-------------------------------+
* | 8 + len(addresses) * 20 bytes |
* +-------------------------------+
*
* @param validationPeriod The information to pack into the message.
* @return The validationID and the packed message.
*/
function packRegisterL1ValidatorMessage(ValidationPeriod memory validationPeriod)
external
pure
returns (bytes32, bytes memory)
{
if (validationPeriod.blsPublicKey.length != 48) {
revert InvalidBLSPublicKey();
}
// solhint-disable-next-line func-named-parameters
bytes memory res = abi.encodePacked(
CODEC_ID,
REGISTER_L1_VALIDATOR_MESSAGE_TYPE_ID,
validationPeriod.l1ID,
uint32(validationPeriod.nodeID.length),
validationPeriod.nodeID,
validationPeriod.blsPublicKey,
validationPeriod.registrationExpiry,
validationPeriod.remainingBalanceOwner.threshold,
uint32(validationPeriod.remainingBalanceOwner.addresses.length)
);
for (uint256 i; i < validationPeriod.remainingBalanceOwner.addresses.length; ++i) {
res = abi.encodePacked(res, validationPeriod.remainingBalanceOwner.addresses[i]);
}
res = abi.encodePacked(
res,
validationPeriod.disableOwner.threshold,
uint32(validationPeriod.disableOwner.addresses.length)
);
for (uint256 i; i < validationPeriod.disableOwner.addresses.length; ++i) {
res = abi.encodePacked(res, validationPeriod.disableOwner.addresses[i]);
}
res = abi.encodePacked(res, validationPeriod.weight);
return (sha256(res), res);
}
/**
* @notice Unpacks a byte array as a RegisterL1ValidatorMessage message.
* The message format specification is the same as the one used in above for packing.
*
* @param input The byte array to unpack.
* @return The unpacked ValidationPeriod.
*/
function unpackRegisterL1ValidatorMessage(bytes memory input)
external
pure
returns (ValidationPeriod memory)
{
uint32 index = 0;
ValidationPeriod memory validation;
// Unpack the codec ID
// Individual fields are unpacked in their own scopes to avoid stack too deep errors.
{
uint16 codecID;
for (uint256 i; i < 2; ++i) {
codecID |= uint16(uint8(input[i + index])) << uint16((8 * (1 - i)));
}
if (codecID != CODEC_ID) {
revert InvalidCodecID(codecID);
}
index += 2;
}
// Unpack the type ID
{
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
if (typeID != REGISTER_L1_VALIDATOR_MESSAGE_TYPE_ID) {
revert InvalidMessageType();
}
index += 4;
}
// Unpack the l1ID
{
bytes32 l1ID;
for (uint256 i; i < 32; ++i) {
l1ID |= bytes32(uint256(uint8(input[i + index])) << (8 * (31 - i)));
}
validation.l1ID = l1ID;
index += 32;
}
// Unpack the nodeID length
uint32 nodeIDLength;
{
for (uint256 i; i < 4; ++i) {
nodeIDLength |= uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;
// Unpack the nodeID
bytes memory nodeID = new bytes(nodeIDLength);
for (uint256 i; i < nodeIDLength; ++i) {
nodeID[i] = input[i + index];
}
validation.nodeID = nodeID;
index += nodeIDLength;
}
// Unpack the blsPublicKey
{
bytes memory blsPublicKey = new bytes(48);
for (uint256 i; i < 48; ++i) {
blsPublicKey[i] = input[i + index];
}
validation.blsPublicKey = blsPublicKey;
index += 48;
}
// Unpack the registration expiry
{
uint64 expiry;
for (uint256 i; i < 8; ++i) {
expiry |= uint64(uint8(input[i + index])) << uint64((8 * (7 - i)));
}
validation.registrationExpiry = expiry;
index += 8;
}
// Unpack the remainingBalanceOwner threshold
uint32 remainingBalanceOwnerAddressesLength;
{
uint32 remainingBalanceOwnerThreshold;
for (uint256 i; i < 4; ++i) {
remainingBalanceOwnerThreshold |=
uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;
// Unpack the remainingBalanceOwner addresses length
for (uint256 i; i < 4; ++i) {
remainingBalanceOwnerAddressesLength |=
uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;
// Unpack the remainingBalanceOwner addresses
address[] memory remainingBalanceOwnerAddresses =
new address[](remainingBalanceOwnerAddressesLength);
for (uint256 i; i < remainingBalanceOwnerAddressesLength; ++i) {
bytes memory addrBytes = new bytes(20);
for (uint256 j; j < 20; ++j) {
addrBytes[j] = input[j + index];
}
address addr;
// solhint-disable-next-line no-inline-assembly
assembly {
addr := mload(add(addrBytes, 20))
}
remainingBalanceOwnerAddresses[i] = addr;
index += 20;
}
validation.remainingBalanceOwner = PChainOwner({
threshold: remainingBalanceOwnerThreshold,
addresses: remainingBalanceOwnerAddresses
});
}
// Unpack the disableOwner threshold
uint32 disableOwnerAddressesLength;
{
uint32 disableOwnerThreshold;
for (uint256 i; i < 4; ++i) {
disableOwnerThreshold |= uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;
// Unpack the disableOwner addresses length
for (uint256 i; i < 4; ++i) {
disableOwnerAddressesLength |=
uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;
// Unpack the disableOwner addresses
address[] memory disableOwnerAddresses = new address[](disableOwnerAddressesLength);
for (uint256 i; i < disableOwnerAddressesLength; ++i) {
bytes memory addrBytes = new bytes(20);
for (uint256 j; j < 20; ++j) {
addrBytes[j] = input[j + index];
}
address addr;
// solhint-disable-next-line no-inline-assembly
assembly {
addr := mload(add(addrBytes, 20))
}
disableOwnerAddresses[i] = addr;
index += 20;
}
validation.disableOwner =
PChainOwner({threshold: disableOwnerThreshold, addresses: disableOwnerAddresses});
}
// Now that we have all the variable lengths, validate the input length
uint32 expectedLength = 122 + nodeIDLength
+ (remainingBalanceOwnerAddressesLength + disableOwnerAddressesLength) * 20;
if (input.length != expectedLength) {
revert InvalidMessageLength(uint32(input.length), expectedLength);
}
// Unpack the weight
{
uint64 weight;
for (uint256 i; i < 8; ++i) {
weight |= uint64(uint8(input[i + index])) << uint64((8 * (7 - i)));
}
validation.weight = weight;
}
return validation;
}
/**
* @notice Packs a L1ValidatorRegistrationMessage into a byte array.
* The message format specification is:
* +--------------+----------+----------+
* | codecID : uint16 | 2 bytes |
* +--------------+----------+----------+
* | typeID : uint32 | 4 bytes |
* +--------------+----------+----------+
* | validationID : [32]byte | 32 bytes |
* +--------------+----------+----------+
* | valid : bool | 1 byte |
* +--------------+----------+----------+
* | 39 bytes |
* +----------+
*
* @param validationID The ID of the validation period.
* @param valid true if the validation period was registered, false if it was not and never will be.
* @return The packed message.
*
*/
function packL1ValidatorRegistrationMessage(
bytes32 validationID,
bool valid
) external pure returns (bytes memory) {
return abi.encodePacked(
CODEC_ID, L1_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID, validationID, valid
);
}
/**
* @notice Unpacks a byte array as a L1ValidatorRegistrationMessage message.
* The message format specification is the same as the one used in above for packing.
*
* @param input The byte array to unpack.
* @return The validationID and whether the validation period was registered or is not a
* validator and never will be a validator due to the expiry time passing.
*/
function unpackL1ValidatorRegistrationMessage(bytes memory input)
external
pure
returns (bytes32, bool)
{
if (input.length != 39) {
revert InvalidMessageLength(uint32(input.length), 39);
}
// Unpack the codec ID
uint16 codecID;
for (uint256 i; i < 2; ++i) {
codecID |= uint16(uint8(input[i])) << uint16((8 * (1 - i)));
}
if (codecID != CODEC_ID) {
revert InvalidCodecID(codecID);
}
// Unpack the type ID
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i + 2])) << uint32((8 * (3 - i)));
}
if (typeID != L1_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID) {
revert InvalidMessageType();
}
// Unpack the validation ID.
bytes32 validationID;
for (uint256 i; i < 32; ++i) {
validationID |= bytes32(uint256(uint8(input[i + 6])) << (8 * (31 - i)));
}
// Unpack the validity
bool valid = input[38] != 0;
return (validationID, valid);
}
/**
* @notice Packs a L1ValidatorWeightMessage message into a byte array.
* The message format specification is:
* +--------------+----------+----------+
* | codecID : uint16 | 2 bytes |
* +--------------+----------+----------+
* | typeID : uint32 | 4 bytes |
* +--------------+----------+----------+
* | validationID : [32]byte | 32 bytes |
* +--------------+----------+----------+
* | nonce : uint64 | 8 bytes |
* +--------------+----------+----------+
* | weight : uint64 | 8 bytes |
* +--------------+----------+----------+
* | 54 bytes |
* +----------+
*
* @param validationID The ID of the validation period.
* @param nonce The nonce of the validation ID.
* @param weight The new weight of the validator.
* @return The packed message.
*/
function packL1ValidatorWeightMessage(
bytes32 validationID,
uint64 nonce,
uint64 weight
) external pure returns (bytes memory) {
return abi.encodePacked(
CODEC_ID, L1_VALIDATOR_WEIGHT_MESSAGE_TYPE_ID, validationID, nonce, weight
);
}
/**
* @notice Unpacks a byte array as an L1ValidatorWeightMessage.
* The message format specification is the same as the one used in above for packing.
*
* @param input The byte array to unpack.
* @return The validationID, nonce, and weight.
*/
function unpackL1ValidatorWeightMessage(bytes memory input)
external
pure
returns (bytes32, uint64, uint64)
{
if (input.length != 54) {
revert InvalidMessageLength(uint32(input.length), 54);
}
// Unpack the codec ID.
uint16 codecID;
for (uint256 i; i < 2; ++i) {
codecID |= uint16(uint8(input[i])) << uint16((8 * (1 - i)));
}
if (codecID != CODEC_ID) {
revert InvalidCodecID(codecID);
}
// Unpack the type ID.
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i + 2])) << uint32((8 * (3 - i)));
}
if (typeID != L1_VALIDATOR_WEIGHT_MESSAGE_TYPE_ID) {
revert InvalidMessageType();
}
// Unpack the validation ID.
bytes32 validationID;
for (uint256 i; i < 32; ++i) {
validationID |= bytes32(uint256(uint8(input[i + 6])) << (8 * (31 - i)));
}
// Unpack the nonce.
uint64 nonce;
for (uint256 i; i < 8; ++i) {
nonce |= uint64(uint8(input[i + 38])) << uint64((8 * (7 - i)));
}
// Unpack the weight.
uint64 weight;
for (uint256 i; i < 8; ++i) {
weight |= uint64(uint8(input[i + 46])) << uint64((8 * (7 - i)));
}
return (validationID, nonce, weight);
}
/**
* @notice Packs a ValidationUptimeMessage into a byte array.
* The message format specification is:
* +--------------+----------+----------+
* | codecID : uint16 | 2 bytes |
* +--------------+----------+----------+
* | typeID : uint32 | 4 bytes |
* +--------------+----------+----------+
* | validationID : [32]byte | 32 bytes |
* +--------------+----------+----------+
* | uptime : uint64 | 8 bytes |
* +--------------+----------+----------+
* | 46 bytes |
* +----------+
*
* @param validationID The ID of the validation period.
* @param uptime The uptime of the validator.
* @return The packed message.
*/
function packValidationUptimeMessage(
bytes32 validationID,
uint64 uptime
) external pure returns (bytes memory) {
return abi.encodePacked(CODEC_ID, VALIDATION_UPTIME_MESSAGE_TYPE_ID, validationID, uptime);
}
/**
* @notice Unpacks a byte array as a ValidationUptimeMessage.
* The message format specification is the same as the one used in above for packing.
*
* @param input The byte array to unpack.
* @return The validationID and uptime.
*/
function unpackValidationUptimeMessage(bytes memory input)
external
pure
returns (bytes32, uint64)
{
if (input.length != 46) {
revert InvalidMessageLength(uint32(input.length), 46);
}
// Unpack the codec ID.
uint16 codecID;
for (uint256 i; i < 2; ++i) {
codecID |= uint16(uint8(input[i])) << uint16((8 * (1 - i)));
}
if (codecID != CODEC_ID) {
revert InvalidCodecID(codecID);
}
// Unpack the type ID.
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i + 2])) << uint32((8 * (3 - i)));
}
if (typeID != VALIDATION_UPTIME_MESSAGE_TYPE_ID) {
revert InvalidMessageType();
}
// Unpack the validation ID.
bytes32 validationID;
for (uint256 i; i < 32; ++i) {
validationID |= bytes32(uint256(uint8(input[i + 6])) << (8 * (31 - i)));
}
// Unpack the uptime.
uint64 uptime;
for (uint256 i; i < 8; ++i) {
uptime |= uint64(uint8(input[i + 38])) << uint64((8 * (7 - i)));
}
return (validationID, uptime);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../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.
*
* The initial owner is set to the address provided by the deployer. 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 {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.
pragma solidity ^0.8.20;
import {EnumerableSet} from "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
* - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
* - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
* - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
* ====
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code repetition as possible, we write it in
// terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
// and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit in bytes32.
/**
* @dev Query for a nonexistent map key.
*/
error EnumerableMapNonexistentKey(bytes32 key);
struct Bytes32ToBytes32Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 key => bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
bytes32 value = map._values[key];
if (value == 0 && !contains(map, key)) {
revert EnumerableMapNonexistentKey(key);
}
return value;
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
return map._keys.values();
}
// UintToUintMap
struct UintToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key)));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintToAddressMap
struct UintToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key)))));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressToUintMap
struct AddressToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (address(uint160(uint256(key))), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// Bytes32ToUintMap
struct Bytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
return set(map._inner, key, bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (key, uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, key);
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
return uint256(get(map._inner, key));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity ^0.8.0;
interface IAssetClassRegistry {
error AssetClassRegistry__InvalidAsset();
error AssetClassRegistry__AssetNotFound();
error AssetClassRegistry__AssetAlreadyRegistered();
error AssetClassRegistry__AssetClassAlreadyExists();
error AssetClassRegistry__AssetClassNotFound();
error AssetClassRegistry__AssetIsPrimaryAssetClass(uint256 assetClassId);
error AssetClassRegistry__AssetsStillExist();
error AssetClassRegistry__InvalidStakingRequirements();
event AssetClassAdded(uint256 indexed assetClassId, uint256 primaryAssetMinStake, uint256 primaryAssetMaxStake);
event AssetAdded(uint256 indexed assetClassId, address indexed asset);
event AssetRemoved(uint256 indexed assetClassId, address indexed asset);
event AssetClassRemoved(uint256 indexed assetClassId);
/**
* @notice Adds a new asset class
* @param assetClassId New asset class ID
* @param minValidatorStake Minimum validator stake
* @param maxValidatorStake Maximum validator stake
* @param initialAsset Initial asset to add to the asset class
*/
function addAssetClass(
uint256 assetClassId,
uint256 minValidatorStake,
uint256 maxValidatorStake,
address initialAsset
) external;
/**
* @notice Adds a asset to an asset class.
* @param assetClassId The ID of the asset class.
* @param asset The address of the asset to add.
*/
function addAssetToClass(uint256 assetClassId, address asset) external;
/**
* @notice Removes a asset from an asset class, except .
* @param assetClassId The ID of the asset class.
* @param asset The address of the asset to remove.
*/
function removeAssetFromClass(uint256 assetClassId, address asset) external;
/**
* @notice Removes an asset class.
* @param assetClassId The ID of the asset class.
*/
function removeAssetClass(
uint256 assetClassId
) external;
/**
* @notice Returns all the assets in a specific asset class.
* @param assetClassId The ID of the asset class.
* @return An array of asset addresses in the asset class.
*/
function getClassAssets(
uint256 assetClassId
) external view returns (address[] memory);
/**
* @notice Returns the minimum validator stake for a specific asset class.
* @param assetClassId The ID of the asset class.
* @return The minimum and maximum validator stake.
*/
function getClassStakingRequirements(
uint256 assetClassId
) external view returns (uint256, uint256);
/**
* @notice Fetches the active asset class IDs
* @return An array of asset class IDs
*/
function getAssetClassIds() external view returns (uint96[] memory);
}// (c) 2022-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
struct WarpMessage {
bytes32 sourceChainID;
address originSenderAddress;
bytes payload;
}
struct WarpBlockHash {
bytes32 sourceChainID;
bytes32 blockHash;
}
interface IWarpMessenger {
event SendWarpMessage(address indexed sender, bytes32 indexed messageID, bytes message);
// sendWarpMessage emits a request for the subnet to send a warp message from [msg.sender]
// with the specified parameters.
// This emits a SendWarpMessage log from the precompile. When the corresponding block is accepted
// the Accept hook of the Warp precompile is invoked with all accepted logs emitted by the Warp
// precompile.
// Each validator then adds the UnsignedWarpMessage encoded in the log to the set of messages
// it is willing to sign for an off-chain relayer to aggregate Warp signatures.
function sendWarpMessage(bytes calldata payload) external returns (bytes32 messageID);
// getVerifiedWarpMessage parses the pre-verified warp message in the
// predicate storage slots as a WarpMessage and returns it to the caller.
// If the message exists and passes verification, returns the verified message
// and true.
// Otherwise, returns false and the empty value for the message.
function getVerifiedWarpMessage(uint32 index) external view returns (WarpMessage calldata message, bool valid);
// getVerifiedWarpBlockHash parses the pre-verified WarpBlockHash message in the
// predicate storage slots as a WarpBlockHash message and returns it to the caller.
// If the message exists and passes verification, returns the verified message
// and true.
// Otherwise, returns false and the empty value for the message.
function getVerifiedWarpBlockHash(
uint32 index
) external view returns (WarpBlockHash calldata warpBlockHash, bool valid);
// getBlockchainID returns the snow.Context BlockchainID of this chain.
// This blockchainID is the hash of the transaction that created this blockchain on the P-Chain
// and is not related to the Ethereum ChainID.
function getBlockchainID() external view returns (bytes32 blockchainID);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// 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
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}{
"remappings": [
"@suzaku/collateral/=lib/collateral/src/",
"@suzaku/core/=lib/suzaku-core/",
"@suzaku/contracts-lib/=lib/suzaku-contracts-library/",
"@suzaku/contracts-library/=lib/suzaku-contracts-library/src/",
"@avalabs/teleporter/=lib/suzaku-contracts-library/lib/icm-contracts/contracts/",
"@avalabs/icm-contracts/=lib/suzaku-contracts-library/lib/icm-contracts/contracts/",
"@avalabs/[email protected]/=lib/suzaku-contracts-library/lib/icm-contracts/lib/subnet-evm/contracts/",
"@forge-std/=lib/suzaku-contracts-library/lib/icm-contracts/lib/forge-std/src/",
"@mocks/=lib/suzaku-contracts-library/lib/icm-contracts/contracts/mocks/",
"@openzeppelin/contracts-upgradeable/=lib/collateral/lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/[email protected]/=lib/suzaku-contracts-library/lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/collateral/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/[email protected]/=lib/suzaku-contracts-library/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/foundry-upgrades/=lib/suzaku-core/lib/openzeppelin-foundry-upgrades/src/",
"@symbiotic/core/=lib/suzaku-core/lib/core/src/",
"@teleporter/=lib/suzaku-contracts-library/lib/icm-contracts/contracts/teleporter/",
"@utilities/=lib/suzaku-contracts-library/lib/icm-contracts/contracts/utilities/",
"collateral/=lib/collateral/",
"core/=lib/suzaku-core/lib/core/",
"ds-test/=lib/collateral/lib/permit2/lib/solmate/lib/ds-test/src/",
"erc4626-tests/=lib/collateral/lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-gas-snapshot/=lib/collateral/lib/permit2/lib/forge-gas-snapshot/src/",
"forge-std/=lib/forge-std/src/",
"icm-contracts/=lib/suzaku-contracts-library/lib/icm-contracts/contracts/",
"openzeppelin-contracts-upgradeable/=lib/collateral/lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/collateral/lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/suzaku-core/lib/openzeppelin-foundry-upgrades/src/",
"permit2/=lib/collateral/lib/permit2/",
"solidity-stringutils/=lib/suzaku-core/lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
"solmate/=lib/collateral/lib/permit2/lib/solmate/",
"subnet-evm/=lib/suzaku-contracts-library/lib/icm-contracts/lib/subnet-evm/",
"suzaku-contracts-library/=lib/suzaku-contracts-library/",
"suzaku-core/=lib/suzaku-core/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true,
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"vaultRegistry","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"middlewareAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"AvalancheL1Middleware__AssetClassNotActive","type":"error"},{"inputs":[{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint96","name":"assetClassId","type":"uint96"}],"name":"AvalancheL1Middleware__CollateralNotInAssetClass","type":"error"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"AvalancheL1Middleware__NotVault","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__SlasherNotImplemented","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__VaultAlreadyRegistered","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__VaultEpochTooShort","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__VaultGracePeriodNotPassed","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__VaultNotDisabled","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__WrongVaultAssetClass","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"AvalancheL1Middleware__ZeroAddress","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__ZeroVaultMaxL1Limit","type":"error"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"EnumerableMapNonexistentKey","type":"error"},{"inputs":[],"name":"MapWithTimeData__AlreadyAdded","type":"error"},{"inputs":[],"name":"MapWithTimeData__AlreadyDisabled","type":"error"},{"inputs":[],"name":"MapWithTimeData__AlreadyEnabled","type":"error"},{"inputs":[],"name":"MapWithTimeData__NotEnabled","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"VAULT_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"getVaultAssetClass","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getVaultAtWithTimes","outputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint48","name":"enabledTime","type":"uint48"},{"internalType":"uint48","name":"disabledTime","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaultCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"epoch","type":"uint48"}],"name":"getVaults","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"middleware","outputs":[{"internalType":"contract AvalancheL1Middleware","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint96","name":"assetClassId","type":"uint96"},{"internalType":"uint256","name":"vaultMaxL1Limit","type":"uint256"}],"name":"registerVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"removeVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slashVault","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint96","name":"assetClassId","type":"uint96"},{"internalType":"uint256","name":"vaultMaxL1Limit","type":"uint256"}],"name":"updateVaultMaxL1Limit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"vaultToAssetClass","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60c03461019457601f61169538819003918201601f191683019291906001600160401b038411838510176101985781606092849260409687528339810103126101945761004b816101ac565b906100638361005c602084016101ac565b92016101ac565b6001600160a01b03918216801561017d575f80546001600160a01b031981168317825586519291908516907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a38284161561014a5750169081156101115760805260a052516114d490816101c182396080518181816104dd015281816109f4015261100a015260a05181818160cf0152818161022b0152818161044101528181610a2e0152610e860152f35b8251635d273a4360e11b81526020600482015260116024820152706d6964646c65776172654164647265737360781b6044820152606490fd5b635d273a4360e11b815260206004820152600d60248201526c7661756c74526567697374727960981b6044820152606490fd5b8451631e4fbdf760e01b81525f6004820152602490fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101945756fe6080604081815260049182361015610015575f80fd5b5f925f3560e01c91826302ace7fe14610ff85750816332a90e1b14610e385781633575764c14610df8578163651b87b214610db2578163715018a614610d5b57816374d4e49114610d3d578163845256e51461097257816388f50eb4146103995781638da5cb5b146103715781639374a51d1461032f578163ceb68c23146101b2578163ec8462131461018f578163f2fde38b14610102575063f4f20ac0146100bc575f80fd5b346100fe57816003193601126100fe57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5080fd5b90503461018b57602036600319011261018b5761011d611039565b906101266111d9565b6001600160a01b039182169283156101755750505f54826001600160601b0360a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b51631e4fbdf760e01b8152908101849052602490fd5b8280fd5b8284346101af57806003193601126101af5750516362eaaf0760e01b8152fd5b80fd5b90503461018b576020908160031936011261032b576101cf611039565b906101d86111d9565b6001600160a01b039182165f81815260036020526040902054909290156103155765ffffffffffff908161020b856112f9565b60301c16908115610305578584918851928380926322207cc760e01b82527f0000000000000000000000000000000000000000000000000000000000000000165afa80156102fb57839189916102ce575b501601908082116102bb5780610271426112c7565b169116116102ad57906001929181865282525f8486205561029181611371565b50845252812080546bffffffffffffffffffffffff1916905580f35b83516363808e2760e01b8152fd5b634e487b7160e01b875260118352602487fd5b6102ee9150873d89116102f4575b6102e681836110b1565b8101906110d3565b5f61025c565b503d6102dc565b87513d8a823e3d90fd5b8651634a65a5cf60e11b81528490fd5b50602491845191630fb8debf60e21b8352820152fd5b8380fd5b5050346100fe5760203660031901126100fe576020916001600160601b039082906001600160a01b03610360611039565b168152600185522054169051908152f35b5050346100fe57816003193601126100fe57905490516001600160a01b039091168152602090f35b90503461018b576103a93661104f565b6103b49492946111d9565b8015610963576001600160a01b038581165f81815260036020526040902054909290610953578451916327f843b560e11b835260209485848981885afa938415610949578a9461092a575b50865163b134427160e01b8152849087818b818a5afa908c821561091f578b93928a92879291610902575b501680151580610866575b6107e4575b50505050817f0000000000000000000000000000000000000000000000000000000000000000169387516322207cc760e01b815287818b81895afa90811561074b578c916107c7575b5065ffffffffffff8091169116106107b757848a52600186526001600160601b03878b20911690816001600160601b03198254161790556104c26111d9565b86516302910f8b60e31b8152888101869052602490878183817f000000000000000000000000000000000000000000000000000000000000000088165afa90811561074b578c9161079a575b5015610786578751630b80945b60e31b815289810183905287818381895afa90811561074b578c91610769575b501561075557875163d8dfeb4560e01b815287818b818a5afa90811561074b578c9161072e575b5083895191633c7b1d3760e01b8352848c8401521690818382015288816044818a5afa908115610724578d916106f7575b50156106dc57508751630ce9b79360e41b81528b95949392919088818c818b5afa9081156106b557848c93928b928a916106bf575b5016968b519384809263289c0bd760e21b82525afa9182156106b5578792610686575b50853b15610682578a95879560649487938d51998a988997634061d8a360e11b8952169087015285015260448401525af18015610678579086929161065d575b50928261063e9452528583812055611446565b15610650578361064d8461121c565b80f35b51635335595160e11b8152fd5b610668919250611089565b6106745783865f61062b565b8580fd5b85513d84823e3d90fd5b8680fd5b6106a7919250893d8b116106ae575b61069f81836110b1565b81019061112b565b905f6105eb565b503d610695565b8a513d89823e3d90fd5b6106d69150833d85116106ae5761069f81836110b1565b5f6105c8565b9189916044938a519363761e8a5160e01b8552840152820152fd5b6107179150893d8b1161071d575b61070f81836110b1565b810190611204565b5f610593565b503d610705565b8a513d8f823e3d90fd5b6107459150883d8a116106ae5761069f81836110b1565b5f610562565b89513d8e823e3d90fd5b87516305707c6760e01b8152808a01839052fd5b6107809150883d8a1161071d5761070f81836110b1565b5f61053b565b8751630fb8debf60e21b8152808a01879052fd5b6107b19150883d8a1161071d5761070f81836110b1565b5f61050e565b865163191ceafb60e31b81528890fd5b6107de9150883d8a116102f4576102e681836110b1565b5f610483565b90919293965089519384809263e054e08b60e01b82525afa91821561085c578b9261083d575b5065ffffffffffff8080931691160390811161082a5792875f878161043a565b634e487b7160e01b8a526011885260248afd5b610855919250873d89116102f4576102e681836110b1565b905f61080a565b88513d8d823e3d90fd5b50919250508851635d927f4560e11b815288818c81855afa908d82156108f757918c9493918b93916108a6575b5067ffffffffffffffff16600114610435565b92809295508391503d83116108f0575b6108c081836110b1565b810103126108ec575167ffffffffffffffff811681036108ec578a92899167ffffffffffffffff610893565b8c80fd5b503d6108b6565b8b51903d90823e3d90fd5b6109199150833d85116106ae5761069f81836110b1565b5f61042a565b8a51903d90823e3d90fd5b610942919450863d88116102f4576102e681836110b1565b925f6103ff565b87513d8c823e3d90fd5b845163163e3a3960e21b81528690fd5b505051630dd833b560e21b8152fd5b838334610c00576109823661104f565b92909161098d6111d9565b6001600160a01b038181165f818152600360205260409020549095919015610d2657855f52602094600186526001600160601b0380865f2054169116809103610d16576109d86111d9565b84516302910f8b60e31b815289810188905260249290878185817f000000000000000000000000000000000000000000000000000000000000000086165afa908115610d0c575f91610cef575b5015610cd957807f000000000000000000000000000000000000000000000000000000000000000016918651630b80945b60e31b8152818c82015288818681875afa908115610c21575f91610cbc575b5015610ca757865163d8dfeb4560e01b815288818d818d5afa908115610c21578c9184915f91610c8a575b50838a5193633c7b1d3760e01b8552840152169081868201528981604481885afa908115610c80575f91610c63575b5015610c4857508651630ce9b79360e41b815288818d818d5afa908115610c2157838d928b925f91610c2b575b50169489519283809263289c0bd760e21b82525afa908115610c21575f91610c04575b50833b15610c00575f80946064938e978b519889978896634061d8a360e11b885216908601528401528760448401525af18015610bf657610be3575b50610bd45750610b6a836112f9565b65ffffffffffff80821615610bc4578160301c16610bb45794610bb094956bffffffffffff000000000000610b9e426112c7565b60301b161792845f52525f2055611446565b5080f35b815163a4cf437160e01b81528690fd5b825163702a3be360e01b81528790fd5b9250505061064d91925061121c565b610bee919650611089565b5f9487610b5b565b84513d5f823e3d90fd5b5f80fd5b610c1b9150893d8b116106ae5761069f81836110b1565b8c610b1f565b88513d5f823e3d90fd5b610c429150833d85116106ae5761069f81836110b1565b8f610afc565b604491858d928a519363761e8a5160e01b8552840152820152fd5b610c7a91508a3d8c1161071d5761070f81836110b1565b8d610acf565b89513d5f823e3d90fd5b610ca191508b3d8d116106ae5761069f81836110b1565b8e610aa0565b8a84918851916305707c6760e01b8352820152fd5b610cd39150893d8b1161071d5761070f81836110b1565b8c610a75565b8551630fb8debf60e21b8152808b018990528390fd5b610d069150883d8a1161071d5761070f81836110b1565b8b610a25565b87513d5f823e3d90fd5b8451633134570b60e11b81528990fd5b8351630fb8debf60e21b8152808901879052602490fd5b8234610c00575f366003190112610c00576020906002549051908152f35b34610c00575f366003190112610c0057610d736111d9565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b8234610c00576020366003190112610c0057610dd06060923561114a565b83516001600160a01b03909316835265ffffffffffff91821660208401521691810191909152f35b8234610c00576020366003190112610c00576020906001600160a01b03610e1d611039565b165f52600182526001600160601b03815f2054169051908152f35b8234610c0057602080600319360112610c0057823565ffffffffffff8116809103610c005760025483516311369f0f60e01b8152808601929092526001600160a01b039460249391828486817f00000000000000000000000000000000000000000000000000000000000000008b165afa938415610fee575f94610fcf575b505f935f5b828110610f9a5750610ee5610ed086611113565b95610edd895197886110b1565b808752611113565b8585019690601f19013688375f935f5b848110610f3a57505050505050908351938285019183865251809252840192915f5b828110610f245785850386f35b8351871685529381019392810192600101610f17565b83610f50610f478361114a565b90939193611183565b610f5e575b50600101610ef5565b8896919651821015610f88576001918c610f819216898260051b8c0101526110f1565b9590610f55565b83603284634e487b7160e01b5f52525ffd5b610fae82610fa78361114a565b9150611183565b610fbb575b600101610ebc565b94610fc76001916110f1565b959050610fb3565b610fe7919450833d85116102f4576102e681836110b1565b9287610eb7565b86513d5f823e3d90fd5b34610c00575f366003190112610c00577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b600435906001600160a01b0382168203610c0057565b6060906003190112610c00576004356001600160a01b0381168103610c0057906024356001600160601b0381168103610c00579060443590565b67ffffffffffffffff811161109d57604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff82111761109d57604052565b90816020910312610c00575165ffffffffffff81168103610c005790565b5f1981146110ff5760010190565b634e487b7160e01b5f52601160045260245ffd5b67ffffffffffffffff811161109d5760051b60200190565b90816020910312610c0057516001600160a01b0381168103610c005790565b61115390611328565b90549060031b1c805f52600460205260405f20549060018060a01b03169165ffffffffffff8083169260301c1690565b65ffffffffffff908116801515939290846111cc575b50836111a6575b50505090565b8116801593509183156111bf575b5050505f80806111a0565b16111590505f80806111b4565b838316101593505f611199565b5f546001600160a01b031633036111ec57565b60405163118cdaa760e01b8152336004820152602490fd5b90816020910312610c0057518015158103610c005790565b6001600160a01b03165f81815260046020526040902054908115806112b6575b61129e5765ffffffffffff918280821615159182611290575b505061127e5761127b91611268426112c7565b16815f52600460205260405f2055611446565b50565b6040516389faef9d60e01b8152600490fd5b60301c16159050825f611255565b6024906040519063015ab34360e11b82526004820152fd5b50600360205260405f20541561123c565b65ffffffffffff908181116112da571690565b604490604051906306dfcc6560e41b8252603060048301526024820152fd5b805f52600460205260405f205490811580611317575b61129e575090565b50600360205260405f20541561130f565b60025481101561135d5760025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b634e487b7160e01b5f52603260045260245ffd5b5f818152600360205260409020548015611440575f19908082018181116110ff57600254908382019182116110ff578181036113f6575b50505060025480156113e2578101906113c082611328565b909182549160031b1b191690556002555f5260036020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b61142a61140561141493611328565b90549060031b1c928392611328565b819391549060031b91821b915f19901b19161790565b90555f52600360205260405f20555f80806113a8565b50505f90565b805f52600360205260405f2054155f14611499576002546801000000000000000081101561109d57611482611414826001859401600255611328565b9055600254905f52600360205260405f2055600190565b505f9056fea2646970667358221220b2efc5ba5171a10381f726953e626e87a95dde155806b81c656fe86a6657cc8b64736f6c63430008190033000000000000000000000000c3b09a650c78dafb79e3c5b782402c8dc523fe88000000000000000000000000c15adc0c87db428b9fc0bb3246e7deb2a991aa6e0000000000000000000000001c1b9f55bba4c0d4e695459a0340130c6eae4074
Deployed Bytecode
0x6080604081815260049182361015610015575f80fd5b5f925f3560e01c91826302ace7fe14610ff85750816332a90e1b14610e385781633575764c14610df8578163651b87b214610db2578163715018a614610d5b57816374d4e49114610d3d578163845256e51461097257816388f50eb4146103995781638da5cb5b146103715781639374a51d1461032f578163ceb68c23146101b2578163ec8462131461018f578163f2fde38b14610102575063f4f20ac0146100bc575f80fd5b346100fe57816003193601126100fe57517f0000000000000000000000001c1b9f55bba4c0d4e695459a0340130c6eae40746001600160a01b03168152602090f35b5080fd5b90503461018b57602036600319011261018b5761011d611039565b906101266111d9565b6001600160a01b039182169283156101755750505f54826001600160601b0360a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b51631e4fbdf760e01b8152908101849052602490fd5b8280fd5b8284346101af57806003193601126101af5750516362eaaf0760e01b8152fd5b80fd5b90503461018b576020908160031936011261032b576101cf611039565b906101d86111d9565b6001600160a01b039182165f81815260036020526040902054909290156103155765ffffffffffff908161020b856112f9565b60301c16908115610305578584918851928380926322207cc760e01b82527f0000000000000000000000001c1b9f55bba4c0d4e695459a0340130c6eae4074165afa80156102fb57839189916102ce575b501601908082116102bb5780610271426112c7565b169116116102ad57906001929181865282525f8486205561029181611371565b50845252812080546bffffffffffffffffffffffff1916905580f35b83516363808e2760e01b8152fd5b634e487b7160e01b875260118352602487fd5b6102ee9150873d89116102f4575b6102e681836110b1565b8101906110d3565b5f61025c565b503d6102dc565b87513d8a823e3d90fd5b8651634a65a5cf60e11b81528490fd5b50602491845191630fb8debf60e21b8352820152fd5b8380fd5b5050346100fe5760203660031901126100fe576020916001600160601b039082906001600160a01b03610360611039565b168152600185522054169051908152f35b5050346100fe57816003193601126100fe57905490516001600160a01b039091168152602090f35b90503461018b576103a93661104f565b6103b49492946111d9565b8015610963576001600160a01b038581165f81815260036020526040902054909290610953578451916327f843b560e11b835260209485848981885afa938415610949578a9461092a575b50865163b134427160e01b8152849087818b818a5afa908c821561091f578b93928a92879291610902575b501680151580610866575b6107e4575b50505050817f0000000000000000000000001c1b9f55bba4c0d4e695459a0340130c6eae4074169387516322207cc760e01b815287818b81895afa90811561074b578c916107c7575b5065ffffffffffff8091169116106107b757848a52600186526001600160601b03878b20911690816001600160601b03198254161790556104c26111d9565b86516302910f8b60e31b8152888101869052602490878183817f000000000000000000000000c3b09a650c78dafb79e3c5b782402c8dc523fe8888165afa90811561074b578c9161079a575b5015610786578751630b80945b60e31b815289810183905287818381895afa90811561074b578c91610769575b501561075557875163d8dfeb4560e01b815287818b818a5afa90811561074b578c9161072e575b5083895191633c7b1d3760e01b8352848c8401521690818382015288816044818a5afa908115610724578d916106f7575b50156106dc57508751630ce9b79360e41b81528b95949392919088818c818b5afa9081156106b557848c93928b928a916106bf575b5016968b519384809263289c0bd760e21b82525afa9182156106b5578792610686575b50853b15610682578a95879560649487938d51998a988997634061d8a360e11b8952169087015285015260448401525af18015610678579086929161065d575b50928261063e9452528583812055611446565b15610650578361064d8461121c565b80f35b51635335595160e11b8152fd5b610668919250611089565b6106745783865f61062b565b8580fd5b85513d84823e3d90fd5b8680fd5b6106a7919250893d8b116106ae575b61069f81836110b1565b81019061112b565b905f6105eb565b503d610695565b8a513d89823e3d90fd5b6106d69150833d85116106ae5761069f81836110b1565b5f6105c8565b9189916044938a519363761e8a5160e01b8552840152820152fd5b6107179150893d8b1161071d575b61070f81836110b1565b810190611204565b5f610593565b503d610705565b8a513d8f823e3d90fd5b6107459150883d8a116106ae5761069f81836110b1565b5f610562565b89513d8e823e3d90fd5b87516305707c6760e01b8152808a01839052fd5b6107809150883d8a1161071d5761070f81836110b1565b5f61053b565b8751630fb8debf60e21b8152808a01879052fd5b6107b19150883d8a1161071d5761070f81836110b1565b5f61050e565b865163191ceafb60e31b81528890fd5b6107de9150883d8a116102f4576102e681836110b1565b5f610483565b90919293965089519384809263e054e08b60e01b82525afa91821561085c578b9261083d575b5065ffffffffffff8080931691160390811161082a5792875f878161043a565b634e487b7160e01b8a526011885260248afd5b610855919250873d89116102f4576102e681836110b1565b905f61080a565b88513d8d823e3d90fd5b50919250508851635d927f4560e11b815288818c81855afa908d82156108f757918c9493918b93916108a6575b5067ffffffffffffffff16600114610435565b92809295508391503d83116108f0575b6108c081836110b1565b810103126108ec575167ffffffffffffffff811681036108ec578a92899167ffffffffffffffff610893565b8c80fd5b503d6108b6565b8b51903d90823e3d90fd5b6109199150833d85116106ae5761069f81836110b1565b5f61042a565b8a51903d90823e3d90fd5b610942919450863d88116102f4576102e681836110b1565b925f6103ff565b87513d8c823e3d90fd5b845163163e3a3960e21b81528690fd5b505051630dd833b560e21b8152fd5b838334610c00576109823661104f565b92909161098d6111d9565b6001600160a01b038181165f818152600360205260409020549095919015610d2657855f52602094600186526001600160601b0380865f2054169116809103610d16576109d86111d9565b84516302910f8b60e31b815289810188905260249290878185817f000000000000000000000000c3b09a650c78dafb79e3c5b782402c8dc523fe8886165afa908115610d0c575f91610cef575b5015610cd957807f0000000000000000000000001c1b9f55bba4c0d4e695459a0340130c6eae407416918651630b80945b60e31b8152818c82015288818681875afa908115610c21575f91610cbc575b5015610ca757865163d8dfeb4560e01b815288818d818d5afa908115610c21578c9184915f91610c8a575b50838a5193633c7b1d3760e01b8552840152169081868201528981604481885afa908115610c80575f91610c63575b5015610c4857508651630ce9b79360e41b815288818d818d5afa908115610c2157838d928b925f91610c2b575b50169489519283809263289c0bd760e21b82525afa908115610c21575f91610c04575b50833b15610c00575f80946064938e978b519889978896634061d8a360e11b885216908601528401528760448401525af18015610bf657610be3575b50610bd45750610b6a836112f9565b65ffffffffffff80821615610bc4578160301c16610bb45794610bb094956bffffffffffff000000000000610b9e426112c7565b60301b161792845f52525f2055611446565b5080f35b815163a4cf437160e01b81528690fd5b825163702a3be360e01b81528790fd5b9250505061064d91925061121c565b610bee919650611089565b5f9487610b5b565b84513d5f823e3d90fd5b5f80fd5b610c1b9150893d8b116106ae5761069f81836110b1565b8c610b1f565b88513d5f823e3d90fd5b610c429150833d85116106ae5761069f81836110b1565b8f610afc565b604491858d928a519363761e8a5160e01b8552840152820152fd5b610c7a91508a3d8c1161071d5761070f81836110b1565b8d610acf565b89513d5f823e3d90fd5b610ca191508b3d8d116106ae5761069f81836110b1565b8e610aa0565b8a84918851916305707c6760e01b8352820152fd5b610cd39150893d8b1161071d5761070f81836110b1565b8c610a75565b8551630fb8debf60e21b8152808b018990528390fd5b610d069150883d8a1161071d5761070f81836110b1565b8b610a25565b87513d5f823e3d90fd5b8451633134570b60e11b81528990fd5b8351630fb8debf60e21b8152808901879052602490fd5b8234610c00575f366003190112610c00576020906002549051908152f35b34610c00575f366003190112610c0057610d736111d9565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b8234610c00576020366003190112610c0057610dd06060923561114a565b83516001600160a01b03909316835265ffffffffffff91821660208401521691810191909152f35b8234610c00576020366003190112610c00576020906001600160a01b03610e1d611039565b165f52600182526001600160601b03815f2054169051908152f35b8234610c0057602080600319360112610c0057823565ffffffffffff8116809103610c005760025483516311369f0f60e01b8152808601929092526001600160a01b039460249391828486817f0000000000000000000000001c1b9f55bba4c0d4e695459a0340130c6eae40748b165afa938415610fee575f94610fcf575b505f935f5b828110610f9a5750610ee5610ed086611113565b95610edd895197886110b1565b808752611113565b8585019690601f19013688375f935f5b848110610f3a57505050505050908351938285019183865251809252840192915f5b828110610f245785850386f35b8351871685529381019392810192600101610f17565b83610f50610f478361114a565b90939193611183565b610f5e575b50600101610ef5565b8896919651821015610f88576001918c610f819216898260051b8c0101526110f1565b9590610f55565b83603284634e487b7160e01b5f52525ffd5b610fae82610fa78361114a565b9150611183565b610fbb575b600101610ebc565b94610fc76001916110f1565b959050610fb3565b610fe7919450833d85116102f4576102e681836110b1565b9287610eb7565b86513d5f823e3d90fd5b34610c00575f366003190112610c00577f000000000000000000000000c3b09a650c78dafb79e3c5b782402c8dc523fe886001600160a01b03168152602090f35b600435906001600160a01b0382168203610c0057565b6060906003190112610c00576004356001600160a01b0381168103610c0057906024356001600160601b0381168103610c00579060443590565b67ffffffffffffffff811161109d57604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff82111761109d57604052565b90816020910312610c00575165ffffffffffff81168103610c005790565b5f1981146110ff5760010190565b634e487b7160e01b5f52601160045260245ffd5b67ffffffffffffffff811161109d5760051b60200190565b90816020910312610c0057516001600160a01b0381168103610c005790565b61115390611328565b90549060031b1c805f52600460205260405f20549060018060a01b03169165ffffffffffff8083169260301c1690565b65ffffffffffff908116801515939290846111cc575b50836111a6575b50505090565b8116801593509183156111bf575b5050505f80806111a0565b16111590505f80806111b4565b838316101593505f611199565b5f546001600160a01b031633036111ec57565b60405163118cdaa760e01b8152336004820152602490fd5b90816020910312610c0057518015158103610c005790565b6001600160a01b03165f81815260046020526040902054908115806112b6575b61129e5765ffffffffffff918280821615159182611290575b505061127e5761127b91611268426112c7565b16815f52600460205260405f2055611446565b50565b6040516389faef9d60e01b8152600490fd5b60301c16159050825f611255565b6024906040519063015ab34360e11b82526004820152fd5b50600360205260405f20541561123c565b65ffffffffffff908181116112da571690565b604490604051906306dfcc6560e41b8252603060048301526024820152fd5b805f52600460205260405f205490811580611317575b61129e575090565b50600360205260405f20541561130f565b60025481101561135d5760025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b634e487b7160e01b5f52603260045260245ffd5b5f818152600360205260409020548015611440575f19908082018181116110ff57600254908382019182116110ff578181036113f6575b50505060025480156113e2578101906113c082611328565b909182549160031b1b191690556002555f5260036020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b61142a61140561141493611328565b90549060031b1c928392611328565b819391549060031b91821b915f19901b19161790565b90555f52600360205260405f20555f80806113a8565b50505f90565b805f52600360205260405f2054155f14611499576002546801000000000000000081101561109d57611482611414826001859401600255611328565b9055600254905f52600360205260405f2055600190565b505f9056fea2646970667358221220b2efc5ba5171a10381f726953e626e87a95dde155806b81c656fe86a6657cc8b64736f6c63430008190033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c3b09a650c78dafb79e3c5b782402c8dc523fe88000000000000000000000000c15adc0c87db428b9fc0bb3246e7deb2a991aa6e0000000000000000000000001c1b9f55bba4c0d4e695459a0340130c6eae4074
-----Decoded View---------------
Arg [0] : vaultRegistry (address): 0xC3b09a650c78daFb79e3C5B782402C8dc523fE88
Arg [1] : owner (address): 0xC15adc0C87db428B9FC0Bb3246e7dEb2A991aa6E
Arg [2] : middlewareAddress (address): 0x1C1B9F55BBa4C0D4E695459a0340130c6eAe4074
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000c3b09a650c78dafb79e3c5b782402c8dc523fe88
Arg [1] : 000000000000000000000000c15adc0c87db428b9fc0bb3246e7deb2a991aa6e
Arg [2] : 0000000000000000000000001c1b9f55bba4c0d4e695459a0340130c6eae4074
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.