Source Code
Overview
AVAX Balance
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 25 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Complete Validat... | 41403217 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Remove Node | 41402872 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Manual Process N... | 41402865 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Complete Validat... | 41371531 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Manual Process N... | 41370987 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Add Node | 41366547 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Complete Validat... | 41366223 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Remove Node | 41366155 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Complete Validat... | 41365457 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Add Node | 41364820 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Complete Validat... | 41364115 | 226 days ago | IN | 0 AVAX | 0 | ||||
| Manual Process N... | 41362319 | 227 days ago | IN | 0 AVAX | 0.00017758 | ||||
| Remove Node | 41354393 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Manual Process N... | 41354387 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Complete Validat... | 41351392 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Add Node | 41351164 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Manual Process N... | 41351156 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Manual Process N... | 41323456 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Calc And Cache S... | 41323335 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Calc And Cache S... | 41323328 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Calc And Cache S... | 41323319 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Calc And Cache N... | 41323303 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Manual Process N... | 41323255 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Manual Process N... | 41323102 | 227 days ago | IN | 0 AVAX | 0 | ||||
| Manual Process N... | 41321680 | 227 days ago | IN | 0 AVAX | 0 |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | Amount | ||
|---|---|---|---|---|---|---|
| 43596839 | 184 days ago | 0 AVAX | ||||
| 43596839 | 184 days ago | 0 AVAX | ||||
| 43596839 | 184 days ago | 0 AVAX | ||||
| 43596839 | 184 days ago | 0 AVAX | ||||
| 41403217 | 226 days ago | 0 AVAX | ||||
| 41402872 | 226 days ago | 0 AVAX | ||||
| 41402872 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX | ||||
| 41402865 | 226 days ago | 0 AVAX |
Loading...
Loading
Contract Name:
AvalancheL1Middleware
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 {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);
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/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) (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
// 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: 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:security-contact [email protected]
* @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();
}
/**
* @notice Initialize the Balancer Validator Manager
* @dev This function is reinitializer(2) because it is upgrated from PoAValidatorManager
* https://github.com/ava-labs/icm-contracts/blob/validator-manager-v1.0.0/contracts/validator-manager/PoAValidatorManager.sol
* @param settings The settings for the Balancer Validator Manager
*/
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.migratedValidators
);
}
function __BalancerValidatorManager_init_unchained(
address initialSecurityModule,
uint64 initialSecurityModuleMaxWeight,
bytes[] calldata migratedValidators
) internal onlyInitializing {
ValidatorManager.ValidatorManagerStorage storage vms = _getValidatorManagerStorage();
// Ensure initial security module max weight is sufficient
if (initialSecurityModuleMaxWeight < vms._churnTracker.totalWeight) {
revert BalancerValidatorManager__InitialSecurityModuleMaxWeightLowerThanTotalWeight(
initialSecurityModule, initialSecurityModuleMaxWeight, vms._churnTracker.totalWeight
);
}
_setUpSecurityModule(initialSecurityModule, initialSecurityModuleMaxWeight);
_migrateValidators(initialSecurityModule, migratedValidators);
}
// 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 validator) {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
// 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 {
(bytes32 validationID, Validator memory validator) = _completeEndValidation(messageIndex);
// Update the security module weight only if the validation was invalidated
if (validator.status == ValidatorStatus.Invalidated) {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
address securityModule = $.validatorSecurityModule[validationID];
uint64 newSecurityModuleWeight =
$.securityModuleWeight[securityModule] - validator.weight;
_updateSecurityModuleWeight(securityModule, newSecurityModuleWeight);
}
}
/// @inheritdoc IBalancerValidatorManager
function initializeValidatorWeightUpdate(
bytes32 validationID,
uint64 newWeight
) external onlySecurityModule returns (Validator memory) {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
ValidatorManager.ValidatorManagerStorage storage vms = _getValidatorManagerStorage();
// 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 storage validator = vms._validationPeriods[validationID];
if (validator.status != ValidatorStatus.Active) {
revert InvalidValidatorStatus(validator.status);
}
if ($.validatorPendingWeightUpdate[validationID] != 0) {
revert BalancerValidatorManager__PendingWeightUpdate(validationID);
}
_checkValidatorSecurityModule(validationID, msg.sender);
uint64 oldWeight = validator.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();
ValidatorManager.ValidatorManagerStorage storage vms = _getValidatorManagerStorage();
Validator storage validator = vms._validationPeriods[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,) = ValidatorMessages
.unpackL1ValidatorWeightMessage(_getPChainWarpMessage(messageIndex).payload);
if (validationID != messageValidationID) {
revert InvalidValidationID(validationID);
}
if (validator.messageNonce < nonce) {
revert BalancerValidatorManager__InvalidNonce(nonce);
}
delete $.validatorPendingWeightUpdate[validationID];
}
function resendValidatorWeightUpdate(
bytes32 validationID
) external {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
ValidatorManager.ValidatorManagerStorage storage vms = _getValidatorManagerStorage();
Validator storage validator = vms._validationPeriods[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 getL1ID() external view returns (bytes32 l1ID) {
ValidatorManager.ValidatorManagerStorage storage vms = _getValidatorManagerStorage();
return vms._l1ID;
}
/// @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(
address initialSecurityModule,
bytes[] calldata migratedValidators
) internal {
BalancerValidatorManagerStorage storage $ = _getBalancerValidatorManagerStorage();
ValidatorManager.ValidatorManagerStorage storage vms = _getValidatorManagerStorage();
// Add the migrated validators to the initial security module
uint64 migratedValidatorsTotalWeight = 0;
for (uint256 i = 0; i < migratedValidators.length; i++) {
bytes32 validationID = registeredValidators(migratedValidators[i]);
// Ensure validator hasn't already been migrated
if ($.validatorSecurityModule[validationID] != address(0)) {
revert BalancerValidatorManager__ValidatorAlreadyMigrated(validationID);
}
Validator storage validator = vms._validationPeriods[validationID];
$.validatorSecurityModule[validationID] = initialSecurityModule;
migratedValidatorsTotalWeight += validator.weight;
}
// Check that the migrated validators total weight equals the current L1 total weight
if (migratedValidatorsTotalWeight != vms._churnTracker.totalWeight) {
revert BalancerValidatorManager__MigratedValidatorsTotalWeightMismatch(
migratedValidatorsTotalWeight, vms._churnTracker.totalWeight
);
}
// Update the initial security module weight directly since we've already validated the max weight
$.securityModuleWeight[initialSecurityModule] = migratedValidatorsTotalWeight;
}
}// 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: 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: 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 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: 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;
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
// 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;
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: 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
// 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: 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;
bytes[] migratedValidators;
}
/**
* @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__MigratedValidatorsTotalWeightMismatch(
uint64 migratedValidatorsTotalWeight, 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__InitialSecurityModuleMaxWeightLowerThanTotalWeight(
address securityModule, uint64 initialMaxWeight, uint64 totalWeight
);
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__ValidatorAlreadyMigrated(bytes32 validationID);
/**
* @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 Returns the L1ID associated with this validator manager
* @return l1ID The L1ID
*/
function getL1ID() external view returns (bytes32 l1ID);
/**
* @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
// 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
// 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);
}// 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.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 {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
// 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: 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;
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
}
}
}// (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;
}
}// 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/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
// 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
// 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;
}
}{
"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":[{"components":[{"internalType":"address","name":"l1ValidatorManager","type":"address"},{"internalType":"address","name":"operatorRegistry","type":"address"},{"internalType":"address","name":"vaultRegistry","type":"address"},{"internalType":"address","name":"operatorL1Optin","type":"address"},{"internalType":"uint48","name":"epochDuration","type":"uint48"},{"internalType":"uint48","name":"slashingWindow","type":"uint48"},{"internalType":"uint48","name":"stakeUpdateWindow","type":"uint48"}],"internalType":"struct AvalancheL1MiddlewareSettings","name":"settings","type":"tuple"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"primaryAsset","type":"address"},{"internalType":"uint256","name":"primaryAssetMaxStake","type":"uint256"},{"internalType":"uint256","name":"primaryAssetMinStake","type":"uint256"},{"internalType":"uint256","name":"primaryAssetWeightScaleFactor","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AssetClassRegistry__AssetAlreadyRegistered","type":"error"},{"inputs":[],"name":"AssetClassRegistry__AssetClassAlreadyExists","type":"error"},{"inputs":[],"name":"AssetClassRegistry__AssetClassNotFound","type":"error"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"AssetClassRegistry__AssetIsPrimaryAssetClass","type":"error"},{"inputs":[],"name":"AssetClassRegistry__AssetNotFound","type":"error"},{"inputs":[],"name":"AssetClassRegistry__AssetsStillExist","type":"error"},{"inputs":[],"name":"AssetClassRegistry__InvalidAsset","type":"error"},{"inputs":[],"name":"AssetClassRegistry__InvalidStakingRequirements","type":"error"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"AvalancheL1Middleware__ActiveSecondaryAssetCLass","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint48","name":"epoch","type":"uint48"}],"name":"AvalancheL1Middleware__AlreadyRebalanced","type":"error"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"AvalancheL1Middleware__AssetClassNotActive","type":"error"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"AvalancheL1Middleware__AssetStillInUse","type":"error"},{"inputs":[{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint96","name":"assetClassId","type":"uint96"}],"name":"AvalancheL1Middleware__CollateralNotInAssetClass","type":"error"},{"inputs":[{"internalType":"uint48","name":"epochStartTs","type":"uint48"}],"name":"AvalancheL1Middleware__EpochError","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__InvalidScaleFactor","type":"error"},{"inputs":[{"internalType":"uint48","name":"epochsPending","type":"uint48"},{"internalType":"uint48","name":"maxAutoUpdates","type":"uint48"}],"name":"AvalancheL1Middleware__ManualEpochUpdateRequired","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__MaxL1LimitZero","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__NoEpochsToProcess","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__NoSlasher","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__NodeNotActive","type":"error"},{"inputs":[{"internalType":"bytes32","name":"nodeId","type":"bytes32"}],"name":"AvalancheL1Middleware__NodeNotFound","type":"error"},{"inputs":[{"internalType":"bytes32","name":"nodeId","type":"bytes32"}],"name":"AvalancheL1Middleware__NodePendingRemoval","type":"error"},{"inputs":[{"internalType":"bytes32","name":"nodeId","type":"bytes32"}],"name":"AvalancheL1Middleware__NodePendingUpdate","type":"error"},{"inputs":[{"internalType":"bytes32","name":"validationID","type":"bytes32"}],"name":"AvalancheL1Middleware__NodeStateNotUpdated","type":"error"},{"inputs":[{"internalType":"uint256","name":"newStake","type":"uint256"}],"name":"AvalancheL1Middleware__NotEnoughFreeStake","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__NotEnoughFreeStakeSecondaryAssetClasses","type":"error"},{"inputs":[{"internalType":"uint48","name":"timeNow","type":"uint48"},{"internalType":"uint48","name":"epochUpdatePeriod","type":"uint48"}],"name":"AvalancheL1Middleware__NotEpochUpdatePeriod","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__NotImplemented","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"AvalancheL1Middleware__OperatorAlreadyRegistered","type":"error"},{"inputs":[{"internalType":"uint48","name":"disabledTime","type":"uint48"},{"internalType":"uint48","name":"slashingWindow","type":"uint48"}],"name":"AvalancheL1Middleware__OperatorGracePeriodNotPassed","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"l1ValidatorManager","type":"address"}],"name":"AvalancheL1Middleware__OperatorNotOptedIn","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"AvalancheL1Middleware__OperatorNotRegistered","type":"error"},{"inputs":[{"internalType":"uint256","name":"securityModuleCapacity","type":"uint256"},{"internalType":"uint256","name":"minStake","type":"uint256"}],"name":"AvalancheL1Middleware__SecurityModuleCapacityNotEnough","type":"error"},{"inputs":[{"internalType":"uint48","name":"slashingWindow","type":"uint48"},{"internalType":"uint48","name":"epochDuration","type":"uint48"}],"name":"AvalancheL1Middleware__SlashingWindowTooShort","type":"error"},{"inputs":[{"internalType":"uint256","name":"newStake","type":"uint256"},{"internalType":"uint256","name":"maxStake","type":"uint256"}],"name":"AvalancheL1Middleware__StakeTooHigh","type":"error"},{"inputs":[{"internalType":"uint256","name":"newStake","type":"uint256"},{"internalType":"uint256","name":"minStake","type":"uint256"}],"name":"AvalancheL1Middleware__StakeTooLow","type":"error"},{"inputs":[],"name":"AvalancheL1Middleware__TooBigSlashAmount","type":"error"},{"inputs":[{"internalType":"uint48","name":"requested","type":"uint48"},{"internalType":"uint48","name":"pending","type":"uint48"}],"name":"AvalancheL1Middleware__TooManyEpochsRequested","type":"error"},{"inputs":[{"internalType":"bytes32","name":"validationId","type":"bytes32"}],"name":"AvalancheL1Middleware__WeightUpdateNotPending","type":"error"},{"inputs":[{"internalType":"bytes32","name":"validationID","type":"bytes32"}],"name":"AvalancheL1Middleware__WeightUpdatePending","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"AvalancheL1Middleware__ZeroAddress","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":[],"name":"MiddlewareUtils__OverflowInStakeToWeight","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":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"newStake","type":"uint256"}],"name":"AllNodeStakesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"assetClassId","type":"uint256"},{"indexed":true,"internalType":"address","name":"asset","type":"address"}],"name":"AssetAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"assetClassId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"primaryAssetMinStake","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"primaryAssetMaxStake","type":"uint256"}],"name":"AssetClassAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"AssetClassRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"assetClassId","type":"uint256"},{"indexed":true,"internalType":"address","name":"asset","type":"address"}],"name":"AssetRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nodeId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"stake","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"validationID","type":"bytes32"}],"name":"NodeAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nodeId","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"validationID","type":"bytes32"}],"name":"NodeRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"upToEpoch","type":"uint48"},{"indexed":false,"internalType":"uint48","name":"epochsProcessedCount","type":"uint48"}],"name":"NodeStakeCacheManuallyProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nodeId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"newStake","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"validationID","type":"bytes32"}],"name":"NodeStakeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"leftoverStake","type":"uint256"}],"name":"OperatorHasLeftoverStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldVaultManager","type":"address"},{"indexed":true,"internalType":"address","name":"newVaultManager","type":"address"}],"name":"VaultManagerUpdated","type":"event"},{"inputs":[],"name":"EPOCH_DURATION","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"L1_VALIDATOR_MANAGER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_AUTO_EPOCH_UPDATES","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_L1_OPTIN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRIMARY_ASSET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRIMARY_ASSET_CLASS","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SLASHING_WINDOW","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"START_TIME","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPDATE_WINDOW","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WEIGHT_SCALE_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"activateSecondaryAssetClass","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"},{"internalType":"uint256","name":"minValidatorStake","type":"uint256"},{"internalType":"uint256","name":"maxValidatorStake","type":"uint256"},{"internalType":"address","name":"initialAsset","type":"address"}],"name":"addAssetClass","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"},{"internalType":"address","name":"asset","type":"address"}],"name":"addAssetToClass","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"nodeId","type":"bytes32"},{"internalType":"bytes","name":"blsKey","type":"bytes"},{"internalType":"uint64","name":"registrationExpiry","type":"uint64"},{"components":[{"internalType":"uint32","name":"threshold","type":"uint32"},{"internalType":"address[]","name":"addresses","type":"address[]"}],"internalType":"struct PChainOwner","name":"remainingBalanceOwner","type":"tuple"},{"components":[{"internalType":"uint32","name":"threshold","type":"uint32"},{"internalType":"address[]","name":"addresses","type":"address[]"}],"internalType":"struct PChainOwner","name":"disableOwner","type":"tuple"},{"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"addNode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"balancerValidatorManager","outputs":[{"internalType":"contract BalancerValidatorManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"calcAndCacheNodeStakeForAllOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"epoch","type":"uint48"},{"internalType":"uint96","name":"assetClassId","type":"uint96"}],"name":"calcAndCacheStakes","outputs":[{"internalType":"uint256","name":"totalStake","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"nodeId","type":"bytes32"},{"internalType":"uint32","name":"messageIndex","type":"uint32"}],"name":"completeStakeUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bytes32","name":"nodeId","type":"bytes32"},{"internalType":"uint32","name":"messageIndex","type":"uint32"}],"name":"completeValidatorRegistration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"messageIndex","type":"uint32"}],"name":"completeValidatorRemoval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"deactivateSecondaryAssetClass","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"disableOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"enableOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"limitStake","type":"uint256"}],"name":"forceUpdateNodes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getActiveAssetClasses","outputs":[{"internalType":"uint256","name":"primary","type":"uint256"},{"internalType":"uint256[]","name":"secondaries","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint48","name":"epoch","type":"uint48"}],"name":"getActiveNodesForEpoch","outputs":[{"internalType":"bytes32[]","name":"activeNodeIds","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllOperators","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAssetClassIds","outputs":[{"internalType":"uint96[]","name":"","type":"uint96[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"getClassAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"getClassStakingRequirements","outputs":[{"internalType":"uint256","name":"minStake","type":"uint256"},{"internalType":"uint256","name":"maxStake","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpoch","outputs":[{"internalType":"uint48","name":"epoch","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"timestamp","type":"uint48"}],"name":"getEpochAtTs","outputs":[{"internalType":"uint48","name":"epoch","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"epoch","type":"uint48"}],"name":"getEpochStartTs","outputs":[{"internalType":"uint48","name":"timestamp","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"epoch","type":"uint48"},{"internalType":"bytes32","name":"validationID","type":"bytes32"}],"name":"getNodeStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getOperatorAvailableStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getOperatorNodesLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint48","name":"epoch","type":"uint48"},{"internalType":"uint96","name":"assetClassId","type":"uint96"}],"name":"getOperatorStake","outputs":[{"internalType":"uint256","name":"stake","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getOperatorUsedStakeCached","outputs":[{"internalType":"uint256","name":"registeredStake","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"epoch","type":"uint48"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint96","name":"assetClass","type":"uint96"}],"name":"getOperatorUsedStakeCachedPerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"epoch","type":"uint48"},{"internalType":"uint96","name":"assetClassId","type":"uint96"}],"name":"getTotalStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaultManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"nodeId","type":"bytes32"},{"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"initializeValidatorStakeUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"assetClassId","type":"uint96"}],"name":"isActiveAssetClass","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"},{"internalType":"address","name":"asset","type":"address"}],"name":"isAssetInClass","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastGlobalNodeStakeUpdateEpoch","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"numEpochsToProcess","type":"uint48"}],"name":"manualProcessNodeStakeCache","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"nodePendingRemoval","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"nodePendingUpdate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"","type":"uint48"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"nodeStakeCache","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operatorLockedStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"operatorNodesArray","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"","type":"uint48"},{"internalType":"uint96","name":"","type":"uint96"},{"internalType":"address","name":"","type":"address"}],"name":"operatorStakeCache","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint48","name":"","type":"uint48"}],"name":"rebalancedThisEpoch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"registerOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"}],"name":"removeAssetClass","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetClassId","type":"uint256"},{"internalType":"address","name":"asset","type":"address"}],"name":"removeAssetFromClass","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"nodeId","type":"bytes32"}],"name":"removeNode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"removeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vaultManager_","type":"address"}],"name":"setVaultManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"epoch","type":"uint48"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint96","name":"assetClassId","type":"uint96"}],"name":"slash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"","type":"uint48"},{"internalType":"uint96","name":"","type":"uint96"}],"name":"totalStakeCache","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"","type":"uint48"},{"internalType":"uint96","name":"","type":"uint96"}],"name":"totalStakeCached","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6101a0346105d8576001600160401b039061615d38819003601f8101601f19168301848111848210176105c45783928291604052833981010361018081126105d85760e0136105d8576040519160e08301908111838210176105c457604052610067816105dc565b8252610075602082016105dc565b6020830152610086604082016105dc565b6040830152610097606082016105dc565b60608301526100a8608082016105f0565b60808301526100b960a082016105f0565b60a08301526100ca60c082016105f0565b60c08301526100db60e082016105dc565b906100e961010082016105dc565b92610120820151926101606101408401519301519060018060a01b031680156105ac575f80546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a382516001600160a01b031615610574575060208201516001600160a01b03161561053b5760408201516001600160a01b0316156105055760608201516001600160a01b0316156104cd576001600160a01b038516156104985765ffffffffffff60a08301511665ffffffffffff60808401511680821061047a57505080156104685765ffffffffffff4211610449574265ffffffffffff9081166101405260808381015182166101005283516001600160a01b039081169091526020840151811660a09081526060850151821660c09081529085015183166101205260e088905284015190911661016052610180919091529051600a80546001600160a01b031916919092161790558181116104375761026b610603565b156104255760407f8828c5448d14dfabfef1825a63323c740b9f79563a375c10bfff3215f754c10991600193845f526003602052806003845f20846002820155015582519182526020820152a260015f5260036020526102f46001600160a01b0382167fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c610687565b1561041357604051906001600160a01b031660017fc53536963369dbfa4c398238ebb9b09fce3943a140928bd25d3052a8a9cacdaf5f80a3615a7990816106e482396080518181816115dc015281816124730152818161323f015281816135ae015261387a015260a051818181611858015261241d015260c051818181611d9a01526124a6015260e05181818161042d0152611fe1015261010051818181610a200152818161154c01528181612d3e0152613f2501526101205181818161140d0152818161144001528181612741015261405501526101405181818161066d01528181612d700152613ef60152610160518181816109670152611b8d0152610180518181816110c40152818161162301528181614ac601526152230152f35b60405163ad2ac00f60e01b8152600490fd5b604051638e2262ff60e01b8152600490fd5b604051633631e2dd60e21b8152600490fd5b6040516306dfcc6560e41b815260306004820152426024820152604490fd5b6040516309ccb72f60e41b8152600490fd5b604492506040519163255fb47f60e21b835260048301526024820152fd5b604051635d273a4360e11b815260206004820152600c60248201526b1c1c9a5b585c9e505cdcd95d60a21b6044820152606490fd5b604051635d273a4360e11b815260206004820152600f60248201526e37b832b930ba37b92618a7b83a34b760891b6044820152606490fd5b604051635d273a4360e11b815260206004820152600d60248201526c7661756c74526567697374727960981b6044820152606490fd5b604051635d273a4360e11b815260206004820152601060248201526f6f70657261746f72526567697374727960801b6044820152606490fd5b635d273a4360e11b81526020600482015260126024820152713618ab30b634b230ba37b926b0b730b3b2b960711b6044820152606490fd5b604051631e4fbdf760e01b81525f6004820152602490fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b03821682036105d857565b519065ffffffffffff821682036105d857565b60015f81905260026020527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e054610682578054680100000000000000008110156105c45781810180835581101561066e578190815f5260205f2001558054600260205260405f205590565b634e487b7160e01b5f52603260045260245ffd5b505f90565b6001810190825f528160205260405f2054155f146106dc578054680100000000000000008110156105c4576001810180835581101561066e578390825f5260205f20015554915f5260205260405f2055600190565b5050505f9056fe608080604052600436101561001c575b50361561001a575f80fd5b005b5f905f3560e01c9081630e4e7bd514612b115750806311369f0f14612af357806316c58824146129d85780631af4b5af1461295e5780631b834ed3146129065780631d34a34f1461276557806322207cc71461272357806327eb17b2146125ee5780632b84d6b414610b025780632ee59f2d146125c85780633682a4501461239a5780633708d256146122e75780633b013969146122c55780633bc7daf11461207f5780633c7b1d371461201057806342cc179814611fcb5780634fe5b9af14611f5757806352c6f00c14611f22578063554bc85914611e6057806356e1c9e914611dc95780635c04a2d814611e0a5780635f3710d014611dc9578063642d5a2314611d84578063715018a614611d2b57806379b5c25e14611d125780637b426a3b14611cc05780637d3ef55d14611c095780637d7d7e4414611bb15780637ff0d8e514611b6e57806381b1d0be14611b1b578063822ca0461461188757806383ce0322146118425780638da5cb5b1461181b5780638fb4bfbd146117ec57806396523059146117835780639681d940146116be57806396f0bcd21461168957806399e3d630146116655780639d94a1e3146116465780639f4191251461160b578063a2702f5c146115c6578063a365bf8814611570578063a70b9f0c1461152d578063a73e2333146114f4578063a7949eca146114c7578063a7dab16714611498578063ac8a584a14611368578063aeb5803d14610da2578063b2834db714610c67578063b36248e814610c3e578063b543503e14610b7a578063b97dd9e214610b4b578063b9bbb6f514610b1e578063ba14c58c14610b02578063c1c2e42b14610a5f578063c84a1242146108f0578063cbbb83d1146108cc578063d911c63214610849578063dc625d92146106ff578063dd307b9914610691578063ddaa26ad1461064e578063e3839ba314610619578063f2fde38b14610591578063f56408ed146104af578063f944e5c4146104765763fa9952f80361000f573461047357604036600319011261047357600435610316612bcb565b9061031f614866565b61033061032b426158c4565b613ef0565b65ffffffffffff80600454169116908111610455575b50600181148061041f575b6104075761035f82826153fd565b6103ef5761036b614866565b8083526003602052604083206001600160a01b039092169161038e9083906157dd565b156103ba577f03c0a1eb08ee9b373b2eee0ff81874c2eecc632e962925ae0fa3cfac84c594f08380a380f35b825260026020526040822054156103dd57604051631595a2fb60e31b8152600490fd5b60405163366eedf560e01b8152600490fd5b6024906040519063c37ba64d60e01b82526004820152fd5b602490604051906318973d4360e11b82526004820152fd5b506001600160a01b038281167f000000000000000000000000000000000000000000000000000000000000000090911614610351565b61045d613b3a565b65ffffffffffff1960045416176004555f610346565b80fd5b5034610473576020366003190112610473576020906040906001600160a01b0361049e612bb5565b168152600d83522054604051908152f35b5034610473576020366003190112610473576104c9612bb5565b6104d1614866565b6104dd61032b426158c4565b60045465ffffffffffff929183169083168111610573575b506001600160a01b031661050881615a14565b9180831615610561578260301c1661054f5761054b916bffffffffffff000000000000610534426158c4565b60301b1617815f52600760205260405f20556158f6565b5080f35b60405163a4cf437160e01b8152600490fd5b60405163702a3be360e01b8152600490fd5b61057b613b3a565b65ffffffffffff1960045416176004555f6104f5565b5034610473576020366003190112610473576105ab612bb5565b6105b3614866565b6001600160a01b03908116908115610600575f54826001600160601b0360a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b604051631e4fbdf760e01b815260048101849052602490fd5b5034610473576040366003190112610473576020610646610638612b5f565b610640612b89565b90614783565b604051908152f35b5034610473578060031936011261047357602060405165ffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5034610473576020366003190112610473576106de6106ae612bb5565b6106b6614866565b6106c261032b426158c4565b65ffffffffffff806004541691169081116106e1575b50614ba2565b80f35b6106e9613b3a565b65ffffffffffff1960045416176004555f6106d8565b50346104735760203660031901126104735760043561072061032b426158c4565b65ffffffffffff8060045416911690811161082b575b50805f52600960205260405f205461081357610750614866565b60018082146107fa57818352600360205260408320546107e85761077382615738565b156103dd57818352600360205260408320908154848355806107c5575b50506003815f60028194015501557f72eb970ea8827c3fd1e7e48a5011ec25c35d5ca2cf450465fc92e06579a927a58280a280f35b82855260208520908101905b8181106107de5750610790565b5f815582016107d1565b6040516302db767160e01b8152600490fd5b6040516318973d4360e11b815260048101839052602490fd5b6024906040519063464cd05560e01b82526004820152fd5b610833613b3a565b65ffffffffffff1960045416176004555f610736565b503461047357806003193601126104735760055461086681612fb9565b916108746040519384612de0565b818352601f1961088383612fb9565b013660208501375b8181106108a457604051806108a08582612be1565b0390f35b806108b06001926153c4565b50506108bc8286613002565b90838060a01b031690520161088b565b50346104735760203660031901126104735760206106466108eb612bb5565b6151c4565b50346104735760403660031901126104735761090a612bb5565b9061091761032b426158c4565b9165ffffffffffff92838116835260136020526040832060015f5260205260ff60405f20541615610a4f575b5061095861095361032b426158c4565b612d32565b92610962426158c4565b61098c7f000000000000000000000000000000000000000000000000000000000000000086612d18565b94828216838088168210928315610a17575b5050506109ee5750906106de916109b761032b426158c4565b600454908216911681116109d0575b5060243590614201565b6109d8613b3a565b65ffffffffffff196004541617600455836109c6565b604051633566e89d60e21b815265ffffffffffff91821660048201529085166024820152604490fd5b610a45919293507f000000000000000000000000000000000000000000000000000000000000000090612d18565b16105f838161099e565b610a58906146ab565b505f610943565b503461047357602036600319011261047357600435610a7c614866565b610a8861032b426158c4565b65ffffffffffff80600454169116908111610ae4575b50610ab4815f52600260205260405f2054151590565b156103dd5760018114610ad257610aca90615992565b15610ad25780f35b604051638e2262ff60e01b8152600490fd5b610aec613b3a565b65ffffffffffff1960045416176004555f610a9e565b5034610473578060031936011261047357602060405160018152f35b503461047357806003193601126104735760045460405160309190911c6001600160a01b03168152602090f35b50346104735780600319360112610473576020610b6a61032b426158c4565b65ffffffffffff60405191168152f35b503461047357602036600319011261047357610b94612bb5565b610b9c614866565b6001600160a01b03908082168015610c0957600454928360301c167f8cfaacab0869ad5307d9175b1a62164d7c9630958cbbc7bc9918133cd6fbb02d8580a36601000000000000600160d01b031990911660309190911b6601000000000000600160d01b03161760045580f35b604051635d273a4360e11b815260206004820152600c60248201526b3b30bab63a26b0b730b3b2b960a11b6044820152606490fd5b5034610473578060031936011261047357600a546040516001600160a01b039091168152602090f35b503461047357806003193601126104735760405180916001908154808452602080940190835f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6905f5b818110610d8f5750505084610cc8910385612de0565b8351610cec610cd682612fb9565b91610ce46040519384612de0565b808352612fb9565b8185019590601f190136873782845b610d41575b50506040519380850191818652518092526040850195925b828110610d255785870386f35b83516001600160601b0316875295810195928101928401610d18565b9084969491819693949651811015610d8257806001600160601b03610d67859385613002565b5116610d738288613002565b52019091949695939295610cfb565b5095939594929194610d00565b8254845292860192918501918501610cb2565b50346104735760031960c036820112611360576001600160401b036024351161136057366023602435011215611360576001600160401b03602435600401351161136057366024803560040135813501011161136057604435906001600160401b0382168203611364576001600160401b03606435116113645760408160643536030112611364576001600160401b0360843511611364576040906084353603011261136057610e5461032b426158c4565b65ffffffffffff8116835260136020526040832060015f5260205260ff60405f20541615611350575b50610e8a61032b426158c4565b65ffffffffffff80600454169116908111611332575b50610eb6335f52600660205260405f2054151590565b1561131a57610ec433615080565b1561130857610f20602060018060a01b03600a5416604051906001600160601b031960043560601b168383015260148252610efe82612daa565b604051808095819463fd7ac5e760e01b83528660048401526024830190612e01565b03915afa9081156112fd5783916112cb575b50610f3c336151c4565b90808452601160205260ff6040852054166112b2578352601060205260ff6040842054166112995760018352600360205260408320906003600283015492015460a43515155f146112935760a4355b8181111561128b5750915b8210908115611281575b506112695760405160043560601b6001600160601b031916602082015260148152610fca81612daa565b604051928360a08101106001600160401b0360a086011117611255576001600160401b039160a08501604052845261100760243560040135613016565b6110146040519182612de0565b602480356004810135808452910160208301378560206024356004013583010152602085015216604083015261104f366064356004016140a1565b6060830152611063366084356004016140a1565b6080830152338352601460205261107f600435604085206159e0565b50338352600d60205261109760043560408520614158565b6110a361032b426158c4565b600a546020906001600160401b03906001600160a01b031661116d876110e97f0000000000000000000000000000000000000000000000000000000000000000886152d3565b9760405198899586948593634dd4b5c360e11b85526040600486015261111b815160a0604488015260e4870190612e01565b608061115b61113b8d8501519360431994858b83030160648c0152612e01565b8660408601511660848a01526060850151848a83030160a48b0152614194565b920151908683030160c4870152614194565b9116602483015203925af192831561124a578493611210575b506111b18165ffffffffffff8093168652600f60205260408620858752602052836040872055612ced565b168352600f60205260408320828452602052806040842055601060205260408320600160ff19825416179055604051908152600435907f517e1badda2dba32b4f95f89957307097e82788cb2e6da8dcddc14850406538a60203392a480f35b9092506020813d602011611242575b8161122c60209383612de0565b8101031261123e5751916111b1611186565b5f80fd5b3d915061121f565b6040513d86823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b60249060405190630ae3d83f60e21b82526004820152fd5b905081115f610fa0565b905091610f96565b81610f8b565b602460405163566c728360e11b81526004356004820152fd5b6024604051635e75d9fb60e11b81526004356004820152fd5b90506020813d6020116112f5575b816112e660209383612de0565b8101031261123e57515f610f32565b3d91506112d9565b6040513d85823e3d90fd5b604051630462cfe760e41b8152600490fd5b604051634c383c5160e01b8152336004820152602490fd5b61133a613b3a565b65ffffffffffff1960045416176004555f610ea0565b611359906146ab565b505f610e7d565b5080fd5b8280fd5b503461047357602036600319011261047357611382612bb5565b61138a614866565b61139661032b426158c4565b60045465ffffffffffff92918316908316811161147a575b506001600160a01b031690806113c383615a14565b60301c16908115908115611439575b506113ef57508061054b91835260076020525f6040842055615693565b60405163e223232360e01b815265ffffffffffff91821660048201527f00000000000000000000000000000000000000000000000000000000000000009091166024820152604490fd5b90506114657f000000000000000000000000000000000000000000000000000000000000000083612d18565b9080611470426158c4565b169116115f6113d2565b611482613b3a565b65ffffffffffff1960045416176004555f6113ae565b50346104735760203660031901126104735760ff60406020926004358152601184522054166040519015158152f35b50346104735760403660031901126104735760206106466114e6612b5f565b6114ee612b89565b90613f65565b5034610473576020366003190112610473576020906040906001600160a01b0361151c612bb5565b168152601283522054604051908152f35b5034610473578060031936011261047357602060405165ffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346104735760403660031901126104735761158a612bb5565b6001600160a01b03168152600d60205260408120805460243592908310156104735760206115b88484612cd8565b90546040519160031b1c8152f35b50346104735780600319360112610473576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461047357806003193601126104735760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b5034610473576020366003190112610473576020610b6a61032b612b5f565b5034610473576020366003190112610473576020610646611684612bb5565b613dd1565b50346104735760603660031901126104735760206106466116a8612b5f565b6116b0612bcb565b6116b8612b9f565b91613bc6565b50346104735760203660031901126104735760043563ffffffff811680910361123e5781906116ef61032b426158c4565b65ffffffffffff80600454169116908111611765575b50600a546001600160a01b031690813b1561176157829160248392604051948593849263467ef06f60e01b845260048401525af18015611756576117465750f35b61174f90612d97565b6104735780f35b6040513d84823e3d90fd5b5050fd5b61176d613b3a565b65ffffffffffff1960045416176004555f611705565b5034610473576040366003190112610473576004356117a0612bcb565b6117a8614866565b6117bd825f52600260205260405f2054151590565b156103dd576001600160a01b038116156117da576106de9161501a565b604051630a32281360e21b8152600490fd5b50346104735760203660031901126104735760ff60406020926004358152601084522054166040519015158152f35b5034610473578060031936011261047357546040516001600160a01b039091168152602090f35b50346104735780600319360112610473576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034610473576040366003190112610473576004359060249182359063ffffffff821680920361123e576118c6335f52600660205260405f2054151590565b15611b04573383526020601481526118ef82604086206001915f520160205260405f2054151590565b15611ab75761190061032b426158c4565b65ffffffffffff80600454169116908111611ae6575b5061192c335f52600660205260405f2054151590565b15611acf573384526014815261195382604086206001915f520160205260405f2054151590565b15611ab75790816119ad9260018060a01b03600a541692604051906001600160601b03199060601b16828201526014815261198d81612daa565b6040518095819263fd7ac5e760e01b83528460048401528a830190612e01565b0381855afa928315611aac578593611a7d575b50604051639b4d9d1360e01b81526004810184905281818881865afa918215611a72578692611a45575b505015611a2e57938484953b15611a2a5784928360449260405196879586946376c951cf60e01b865260048601528401525af18015611756576117465750f35b8480fd5b6040516259d43960e71b8152600481018390528590fd5b611a649250803d10611a6b575b611a5c8183612de0565b810190612e25565b5f806119ea565b503d611a52565b6040513d88823e3d90fd5b9080935081813d8311611aa5575b611a958183612de0565b8101031261123e5751915f6119c0565b503d611a8b565b6040513d87823e3d90fd5b60405163c50edab960e01b8152600481018390528590fd5b604051634c383c5160e01b81523360048201528590fd5b611aee613b3a565b65ffffffffffff1960045416176004555f611916565b604051634c383c5160e01b81523360048201528490fd5b50346104735760203660031901126104735760043590611b46825f52600260205260405f2054151590565b156103dd576040816108a093611b629352600360205220615874565b60405191829182612be1565b5034610473578060031936011261047357602060405165ffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346104735760403660031901126104735765ffffffffffff6040611bd4612bb5565b92611bdd612b74565b9360018060a01b03168152600b6020522091165f52602052602060ff60405f2054166040519015158152f35b503461047357806003193601126104735760405180600854928383526020809301809460085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3905f5b818110611cac5750505082611c6a910383612de0565b6040519260408401906001936001865260406020870152518092526060850195925b828110611c995785870386f35b8351875295810195928101928401611c8c565b825484529286019260019283019201611c54565b503461047357602036600319011261047357600435611cea815f52600260205260405f2054151590565b156103dd57604082819281526003602052206003600282015491015482519182526020820152f35b50346104735780600319360112610473576106de613b3a565b5034610473578060031936011261047357611d44614866565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346104735780600319360112610473576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461047357604036600319011261047357604060209165ffffffffffff611def612b5f565b168152600f8352818120602435825283522054604051908152f35b5034610473576020366003190112610473576004356001600160601b03811680910361123e5760209160018214918215611e4b575b50506040519015158152f35b60409250815260098352205415155f80611e3f565b5034610473576080366003190112610473576024356064356004356044356001600160a01b03831680840361123e57611e97614866565b156117da576001821480611f19575b611f0757611eb382615944565b15610ad2577f8828c5448d14dfabfef1825a63323c740b9f79563a375c10bfff3215f754c10960406106de9584938489526003602052806003848b20846002820155015582519182526020820152a261501a565b604051633631e2dd60e21b8152600490fd5b50808411611ea6565b5034610473576060366003190112610473576020610646611f41612bb5565b611f49612b74565b611f51612b9f565b916137ea565b503461047357606036600319011261047357611f71612b5f565b90611f7a612b89565b906044359260018060a01b03841680940361123e576001600160601b039165ffffffffffff604092168152600e6020522091165f5260205260405f20905f52602052602060405f2054604051908152f35b50346104735780600319360112610473576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034610473576040366003190112610473576004359061202e612bcb565b612043835f52600260205260405f2054151590565b156103dd57604082602094612075945260038552209060018060a01b0316906001915f520160205260405f2054151590565b6040519015158152f35b503461047357604036600319011261047357612099612bb5565b6120a4610953612b74565b60018060a01b038092168352601491602091601483526120c660408620615874565b936120d18551612fd0565b9286928791600a5416915b8751811015612243576120ef8189613002565b51604051906001600160601b03198160601b168983015285825261211282612daa565b60405163fd7ac5e760e01b815289818061213760249684600484015287830190612e01565b0381895afa908115612238578c9161220b575b5060405190636af907fb60e11b825260048201528b808285818a5afa80156121fe5761219592828893926121db575b505065ffffffffffff60c08160a0840151169201511690614fc4565b6121a4575b50506001016120dc565b9086905f1982146121c95750906121c16001809493019789613002565b52905f61219a565b634e487b7160e01b8c5260116004528bfd5b6121f792503d8091833e6121ef8183612de0565b810190613045565b5f8e612179565b50604051903d90823e3d90fd5b90508981813d8311612231575b6122228183612de0565b8101031261123e57515f61214a565b503d612218565b6040513d8e823e3d90fd5b8887878761225081612fd0565b91845b82811061229e5750505060405192828493840190808552835180925280604086019401925b82811061228757505050500390f35b835185528695509381019392810192600101612278565b806122af6001928498969798613002565b516122ba8288613002565b520194939294612253565b5034610473576020366003190112610473576106de6122e2612b5f565b612e55565b503461047357608036600319011261047357612301612b5f565b612309612bcb565b506064356001600160601b03811680820361123e57612326614866565b604065ffffffffffff948585168152601360205220905f5260205260ff60405f20541615612389575b505061235d61032b426158c4565b600454821691161161237c575b60405163c736d3b160e01b8152600490fd5b612384613b3a565b61236a565b61239291614783565b505f8061234f565b503461047357602080600319360112611360576123b5612bb5565b906123be614866565b6123ca61032b426158c4565b65ffffffffffff806004541691169081116125aa575b506001600160a01b038281165f81815260066020526040902054909291906125915760405163c3c5a54760e01b81526004810184905282816024817f000000000000000000000000000000000000000000000000000000000000000086165afa908115611a72578691612574575b501561255b576040516308834cb560e21b81526001600160a01b0385811660048301527f00000000000000000000000000000000000000000000000000000000000000009081166024830152918390829060449082907f0000000000000000000000000000000000000000000000000000000000000000165afa908115611a7257869161253e575b501561251357509060076124f392828652528360408120556158f6565b15612501576106de90614ba2565b604051635335595160e11b8152600490fd5b60405163a2cb00e760e01b81526001600160a01b038581166004830152919091166024820152604490fd5b6125559150833d8511611a6b57611a5c8183612de0565b5f6124d6565b604051634c383c5160e01b815260048101849052602490fd5b61258b9150833d8511611a6b57611a5c8183612de0565b5f61244e565b604051631da9d69960e01b815260048101849052602490fd5b6125b2613b3a565b65ffffffffffff1960045416176004555f6123e0565b5034610473578060031936011261047357602065ffffffffffff60045416604051908152f35b503461123e57606036600319011261123e57612608612bb5565b602435906044359163ffffffff831680930361123e5761262a61032b426158c4565b65ffffffffffff80600454169116908111612705575b506001600160a01b039182165f81815260066020526040902054156126ed575f5260146020526126818160405f206001915f520160205260405f2054151590565b156126d55750600a541690813b1561123e575f91602483926040519485938492631474cbc960e31b845260048401525af180156126ca576126c0575080f35b61001a9150612d97565b6040513d5f823e3d90fd5b6024906040519063c50edab960e01b82526004820152fd5b60249060405190634c383c5160e01b82526004820152fd5b61270d613b3a565b65ffffffffffff1960045416176004555f612640565b3461123e575f36600319011261123e57602060405165ffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461123e57604036600319011261123e5760043560243561278861032b426158c4565b65ffffffffffff806004541691169081116128e8575b50335f526020601481526127c38360405f206001915f520160205260405f2054151590565b156128cf5760015f526003815260405f20600360028201549101548084116128b157508083106128935750600a5460405160609490941b6001600160601b03191684830152601484526128469382916001600160a01b031661282482612daa565b604051808097819463fd7ac5e760e01b83528660048401526024830190612e01565b03915afa9081156126ca575f91612863575b61001a8383336149eb565b905082813d831161288c575b6128798183612de0565b8101031261123e5761001a915183612858565b503d61286f565b82604491604051916353fe4d4b60e11b835260048301526024820152fd5b83604491604051916369367fbf60e01b835260048301526024820152fd5b60405163c50edab960e01b815260048101849052602490fd5b6128f0613b3a565b65ffffffffffff1960045416176004558261279e565b3461123e57604036600319011261123e5761291f612b5f565b65ffffffffffff61292e612b89565b91165f5260136020526001600160601b0360405f2091165f52602052602060ff60405f2054166040519015158152f35b3461123e57602036600319011261123e5760043561297a614866565b61298661032b426158c4565b65ffffffffffff806004541691169081116129ba575b506129a681614891565b6103ef576129b3906155ee565b156103dd57005b6129c2613b3a565b65ffffffffffff1960045416176004558161299c565b3461123e5760208060031936011261123e576004356129f961032b426158c4565b65ffffffffffff80600454169116908111612ad5575b50612a25335f52600660205260405f2054151590565b1561131a57335f5260148252612a4c8160405f206001915f520160205260405f2054151590565b156126d557600a54604051606083901b6001600160601b0319168185015260148152612a8893918291906001600160a01b031661282482612daa565b03915afa9081156126ca575f91612aa5575b61001a838333615303565b905082813d8311612ace575b612abb8183612de0565b8101031261123e5761001a915183612a9a565b503d612ab1565b612add613b3a565b65ffffffffffff19600454161760045582612a0f565b3461123e57602036600319011261123e576020610b6a610953612b5f565b3461123e57604036600319011261123e57602090612b2d612b5f565b65ffffffffffff612b3c612b89565b91165f52600c83526001600160601b0360405f2091165f52825260405f20548152f35b6004359065ffffffffffff8216820361123e57565b6024359065ffffffffffff8216820361123e57565b602435906001600160601b038216820361123e57565b604435906001600160601b038216820361123e57565b600435906001600160a01b038216820361123e57565b602435906001600160a01b038216820361123e57565b60209060206040818301928281528551809452019301915f5b828110612c08575050505090565b83516001600160a01b031685529381019392810192600101612bfa565b600854811015612c5a5760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee301905f90565b634e487b7160e01b5f52603260045260245ffd5b600554811015612c5a5760055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001905f90565b600154811015612c5a5760015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601905f90565b8054821015612c5a575f5260205f2001905f90565b90600165ffffffffffff80931601918211612d0457565b634e487b7160e01b5f52601160045260245ffd5b91909165ffffffffffff80809416911601918211612d0457565b65ffffffffffff9081167f0000000000000000000000000000000000000000000000000000000000000000821602908116908103612d0457612d94907f0000000000000000000000000000000000000000000000000000000000000000612d18565b90565b6001600160401b03811161125557604052565b604081019081106001600160401b0382111761125557604052565b602081019081106001600160401b0382111761125557604052565b90601f801991011681019081106001600160401b0382111761125557604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9081602091031261123e5751801515810361123e5790565b65ffffffffffff9182169082160391908211612d0457565b9065ffffffffffff91828116908115612fa757612e7461032b426158c4565b915f60049186600454169087861695828711612f93575b50878316809111612f46575b50505091905f925f925b86831687851610612ef7575b50506004546040805165ffffffffffff97909216871682529390951660208601525091927f4d292688ca328e445b76fb7747c31983e6367477e08b40b6a30f7cf280ee80b69250a1565b9091929381878454161015612f4057612f0e614c4a565b15612f40578616868114612f2d57600190810194018616929190612ea1565b601183634e487b7160e01b5f525260245ffd5b93612ead565b909192935015612f5a5750905f8080612e97565b9150507f4d292688ca328e445b76fb7747c31983e6367477e08b40b6a30f7cf280ee80b69293506040809250519081525f6020820152a1565b612fa09193508290612e3d565b915f612e8b565b604051631752afd760e31b8152600490fd5b6001600160401b0381116112555760051b60200190565b90612fda82612fb9565b612fe76040519182612de0565b8281528092612ff8601f1991612fb9565b0190602036910137565b8051821015612c5a5760209160051b010190565b6001600160401b03811161125557601f01601f191660200190565b51906001600160401b038216820361123e57565b90602090818382031261123e5782516001600160401b039384821161123e570160e08183031261123e576040519360e0850185811082821117611255576040528151600681101561123e5785528382015190811161123e5781019180601f8401121561123e578251936130b785613016565b916130c56040519384612de0565b85835281868601011161123e575f8186613133978260c098018387015e840101528501526130f560408201613031565b604085015261310660608201613031565b606085015261311760808201613031565b608085015261312860a08201613031565b60a085015201613031565b60c082015290565b51906001600160a01b038216820361123e57565b519065ffffffffffff8216820361123e57565b9081606091031261123e576131768161313b565b91612d9460406131886020850161314f565b930161314f565b9081602091031261123e57516001600160601b038116810361123e5790565b91908201809211612d0457565b91905f9265ffffffffffff80831691825f5260209360138552604090815f209460019560015f52875260ff835f2054166134fb57506131f990612d32565b9460018060a01b039560049680885460301c16958451916374d4e49160e01b835284838b818b5afa9283156134f1575f936134c2575b50989796905f96959493929699807f000000000000000000000000000000000000000000000000000000000000000016918416925b888c1061327957505050505050505050505050565b909192939495969798999a9c8d88519063328dc3d960e11b82528d820152606060249181818481885afa801561347e5789905f925f905f92613488575b506132c2939450614fc4565b15613447578951639374a51d60e01b8152908516818f018190529089818481885afa90811561347e578e916001600160601b03915f91613451575b50160361344757888e918b5192838092630ce9b79360e41b82525afa90811561343d579189918f8f9489918f8f908b935f966133f0575b50908361337f9282519261334784612dc5565b5f8452519a8b998a988997633dc5038960e01b89528801528601521660448401528d606484015260a0608484015260a4830190612e01565b0392165afa9081156133e657908c915f916133b3575b506133a091926131ae565b9d5b019a99989796959493929190613264565b809250898092503d83116133df575b6133cc8183612de0565b8101031261123e57518b906133a0613395565b503d6133c2565b89513d5f823e3d90fd5b9796985050505050505081813d8311613436575b61340e8183612de0565b8101031261123e578c918b8f87899161337f8f8f9761342d859161313b565b96919250613334565b503d613404565b8a513d5f823e3d90fd5b50509c8a906133a2565b61347191508c8d3d10613477575b6134698183612de0565b81019061318f565b5f6132fd565b503d61345f565b8b513d5f823e3d90fd5b9150506132c29250836134b09294903d106134bb575b6134a88183612de0565b810190613162565b9093919384936132b6565b503d61349e565b9092508481813d83116134ea575b6134da8183612de0565b8101031261123e5751915f61322f565b503d6134d0565b86513d5f823e3d90fd5b5f908152600e87528281206001825287528281206001600160a01b039094168152929095529020549450505050565b91905f9265ffffffffffff80831691825f5260209360138552604090815f209460019560015f52875260ff835f2054166134fb575061356890612d32565b9460018060a01b039560049680885460301c16958451916374d4e49160e01b835284838b818b5afa9283156134f1575f936137bb575b50989796905f96959493929699807f000000000000000000000000000000000000000000000000000000000000000016918416925b888c106135e857505050505050505050505050565b909192939495969798999a9c8d88519063328dc3d960e11b82528d820152606060249181818481885afa801561347e5789905f925f905f92613791575b50613631939450614fc4565b1561376a578951639374a51d60e01b8152908516818f018190529089818481885afa90811561347e578e916001600160601b03915f91613774575b50160361376a57888e918b5192838092630ce9b79360e41b82525afa90811561343d579189918f8f9489918f8f908b935f9661371d575b5090836136b69282519261334784612dc5565b0392165afa9081156133e657908c915f916136ea575b506136d791926131ae565b9d5b019a999897969594939291906135d3565b809250898092503d8311613716575b6137038183612de0565b8101031261123e57518b906136d76136cc565b503d6136f9565b9796985050505050505081813d8311613763575b61373b8183612de0565b8101031261123e578c918b8f8789916136b68f8f9761375a859161313b565b969192506136a3565b503d613731565b50509c8a906136d9565b61378b91508c8d3d10613477576134698183612de0565b5f61366c565b9150506136319250836137b09294903d106134bb576134a88183612de0565b909391938493613625565b9092508481813d83116137e3575b6137d38183612de0565b8101031261123e5751915f61359e565b503d6137c9565b929190925f9365ffffffffffff9283821690815f5260209060138252604093845f20936001600160601b0380931694855f52845260ff865f205416613b0b575061383390612d32565b9060018060a01b039660049780895460301c16978751936374d4e49160e01b855286858c818d5afa9485156133e6575f95613adc575b5099989291905f9897969594989a827f000000000000000000000000000000000000000000000000000000000000000016938616945b8a8d106138b55750505050505050505050505050565b909192939495969798999a9b9d8c8f8c519163328dc3d960e11b8352820152606060249181818481885afa8015613aa8578b905f925f905f92613ab2575b506138ff939450614fc4565b15613a80578e90878e5191639374a51d60e01b83521680928201528b818481885afa908115613aa8578d9187918e5f92613a8b575b50501603613a80578a8f918e5192838092630ce9b79360e41b82525afa908115613a7657918f91898f8f95948f95918e8c8e945f97613a25575b50908483926139b994519361398285612dc5565b5f8552519b8c9a8b998a98633dc5038960e01b8a52890152870152166044850152606484015260a0608484015260a4830190612e01565b0392165afa908115613a1b575f916139ed575b506139d9906001926131ae565b9e5b019b9a9998979695949392919061389f565b90508981813d8311613a14575b613a048183612de0565b8101031261123e575160016139cc565b503d6139fa565b8c513d5f823e3d90fd5b989799505050505050505081813d8311613a6f575b613a448183612de0565b8101031261123e578b918f898f89928f958c928f84613a656139b99461313b565b979192935061396e565b503d613a3a565b8d513d5f823e3d90fd5b50509d6001906139db565b613aa19250803d10613477576134698183612de0565b5f8e613934565b8e513d5f823e3d90fd5b9150506138ff925083613ad19294903d106134bb576134a88183612de0565b9093919384936138f3565b9094508681813d8311613b04575b613af48183612de0565b8101031261123e5751935f613869565b503d613aea565b5f908152600e845285812094815293835250508282206001600160a01b03909416825292909252902054925050565b613b4661032b426158c4565b65ffffffffffff90816004541690818382161115613bc1578291613b6991612e3d565b1660019060018111613ba2575f5b8184821610613b87575b50505050565b613b8f614c4a565b15613b9d5782018316613b77565b613b81565b60449060405190633c90bbed60e21b8252600482015260016024820152fd5b505050565b9091600191906001600160601b0381168303613dc6575060408051633bc7daf160e01b81526001600160a01b039490941660048086019190915265ffffffffffff831660248601529391905f83604481305afa928315613dbc575f93613d2b575b50600a545f969587959490916001600160a01b031690855b613c4d575b50505050505050565b8051871015613d2657613cab98613c648883613002565b5185519060209182916001600160601b03199060601b168282015260148152613c8c81612daa565b8751809d819263fd7ac5e760e01b8352848a8401526024830190612e01565b0381875afa9081156134f15788999a9b5f92613cf3575b5091613cea91899365ffffffffffff8a165f52600f8152885f20915f5252865f2054906131ae565b99980196613c3f565b8180949a508193503d8311613d1f575b613d0d8183612de0565b8101031261123e575188979188613cc2565b503d613d03565b613c44565b9092503d805f833e613d3d8183612de0565b810190602090818184031261123e578051906001600160401b03821161123e57019180601f8401121561123e578251613d7581612fb9565b93613d8287519586612de0565b818552838086019260051b82010192831161123e578301905b828210613dad5750505050915f613c27565b81518152908301908301613d9b565b50513d5f823e3d90fd5b9150612d94926137ea565b905f9160018060a01b038091165f52602090600d8252604090815f20925f93805492600a5416905b838610613e0857505050505050565b909192939496613e6584613e1c8a85612cd8565b9054895160039290921b1c60601b6001600160601b0319168282015260148152613e4581612daa565b88518093819263fd7ac5e760e01b83528460048401526024830190612e01565b0381875afa908115613ee6575f91613eb8575b50600191613eac9165ffffffffffff613e9361032b426158c4565b165f52600f8752885f20905f528652875f2054906131ae565b97019493929190613df9565b90508481813d8311613edf575b613ecf8183612de0565b8101031261123e57516001613e78565b503d613ec5565b87513d5f823e3d90fd5b613f1b907f000000000000000000000000000000000000000000000000000000000000000090612e3d565b65ffffffffffff807f000000000000000000000000000000000000000000000000000000000000000016918215613f5157160490565b634e487b7160e01b5f52601260045260245ffd5b9065ffffffffffff91828116805f52601360205260405f20906001600160601b03841691825f5260205260ff60405f2054166140835750505f92613fa882612d32565b9080613fb3426158c4565b1690808316918211908115614045575b5061402d575090600554915f935b838510613fe057505050505090565b909192939482613ffb613ff2886153c4565b90939193614fc4565b15614023576001916140118487614017946137ea565b906131ae565b955b0193929190613fd1565b5094600190614019565b6024906040519063c2ef899360e01b82526004820152fd5b905061407a614053426158c4565b7f000000000000000000000000000000000000000000000000000000000000000090612e3d565b1681105f613fc3565b92509250505f52600c60205260405f20905f5260205260405f205490565b919060408382031261123e57604080516001600160401b039491810185811182821017611255576040528094823563ffffffff8116810361123e5782526020928381013591821161123e57019280601f8501121561123e57833561410481612fb9565b946141126040519687612de0565b818652848087019260051b82010192831161123e578401905b828210614139575050500152565b81356001600160a01b038116810361123e57815290840190840161412b565b805490600160401b821015611255578161417a91600161419094018155612cd8565b819391549060031b91821b915f19901b19161790565b9055565b6040820163ffffffff8251168352602060608180940151946040838201528551809452019301915f5b8281106141cb575050505090565b83516001600160a01b0316855293810193928101926001016141bd565b91908203918211612d0457565b8015612d04575f190190565b61420d61032b426158c4565b9160018060a01b0382165f52600b60205260405f2065ffffffffffff84165f5260205260ff60405f205416614677576001600160a01b0382165f818152600b6020908152604080832065ffffffffffff881684528252808320805460ff191660011790559282526006905220541561465657614288826151c4565b61429183613dd1565b9060018060a01b0384165f52600d60205260405f209586549280831461464c578083116145cc57826142c2916141e8565b925b801515806145c3575b15614582576142db906141f5565b916143416142e9848a612cd8565b91905460018060a01b03600a541660206040516001600160601b0319848760031b1c60601b16828201526014815261432081612daa565b6040518095819263fd7ac5e760e01b83528460048401526024830190612e01565b0381845afa9283156126ca575f9361454e575b50604051639b4d9d1360e01b815260048101849052602081602481855afa9081156126ca575f9161452f575b50614526575f60249160405192838092636af907fb60e11b82528760048301525afa9081156126ca575f9161450c575b50519660068810156144f85760028098036144ee5765ffffffffffff8b165f52600f60205260405f20835f5260205260405f205480156144e35761441b90808310156144dd5782905b8b15158c83826144d3575b50506144cb575b81614415916141e8565b926141e8565b60015f5260036020527fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c90980154811080156144bb575b1561446d57506144679260031b1c9088615303565b916142c4565b919261447a83858c6149eb565b60405192835260031b1c906001600160a01b038916907f48d83a0359ece1af3ea738ea6e14f7f30eb31b2c57f44b98dbc41469c83a525a90602090a4614467565b506144c58a615137565b15614452565b8b915061440b565b1190508c5f614404565b806143f9565b509650505050614467565b9650505050614467565b634e487b7160e01b5f52602160045260245ffd5b61452091503d805f833e6121ef8183612de0565b5f6143b0565b50505050614467565b614548915060203d602011611a6b57611a5c8183612de0565b5f614380565b9092506020813d60201161457a575b8161456a60209383612de0565b8101031261123e5751915f614354565b3d915061455d565b5060405191825295506001600160a01b0390931693507fac43c581689bab0b319a9fc8e659012b23ee3494f1bf17edad354d457ccba3eb92602092509050a2565b508315156142cd565b90965081614603929650602094507fac43c581689bab0b319a9fc8e659012b23ee3494f1bf17edad354d457ccba3eb9593506141e8565b6040519081526001600160a01b038216907fb066d744c0abef0595669b34ee833c2d24b4d89382dc63f616513f3024a09464908490a26040519384526001600160a01b031692a2565b5095505050505050565b604051634c383c5160e01b81526001600160a01b0383166004820152602490fd5b5060405163316d57cf60e11b81526001600160a01b0391909116600482015265ffffffffffff919091166024820152604490fd5b905f916146b781612d32565b6005545f915b81831061470b5750505065ffffffffffff16805f52600c60205260405f2060015f526020528260405f20555f52601360205260405f2060015f5260205260405f20600160ff19825416179055565b829561472461471b8394956153c4565b90949194614fc4565b15614779578161476f9161473a8760019561352a565b809265ffffffffffff89165f52602090600e8252604091825f20885f528152825f2091888060a01b03165f52525f20556131ae565b955b0191906146bd565b9560019150614771565b91905f9261479081612d32565b6005545f915b8183106147ed5750505065ffffffffffff16805f52600c6020526001600160601b0360405f20921691825f526020528360405f20555f52601360205260405f20905f5260205260405f20600160ff19825416179055565b82966147fd61471b8394956153c4565b1561485c57816148529161481488886001966137ea565b809265ffffffffffff89165f52602090600e8252604091825f206001600160601b038d165f528152825f2091888060a01b03165f52525f20556131ae565b965b019190614796565b9660019150614854565b5f546001600160a01b0316330361487957565b60405163118cdaa760e01b8152336004820152602490fd5b6004805460301c6001600160a01b0390811692915f5b604080516374d4e49160e01b8152602090818186818b5afa9081156149e1575f916149b4575b508310156149a957815163328dc3d960e11b8152848101849052606090602490828183818d5afa92831561499f57918493918995935f9361497e575b50508551948592630d5d5d9360e21b84521688830152818b5afa9283156149755750916001600160601b039187935f92614958575b5050161461494e576001016148a7565b5050505050600190565b61496e9250803d10613477576134698183612de0565b5f8061493e565b513d5f823e3d90fd5b614995929350803d106134bb576134a88183612de0565b5050905f80614909565b85513d5f823e3d90fd5b505050505050505f90565b90508181813d83116149da575b6149cb8183612de0565b8101031261123e57515f6148cd565b503d6149c1565b83513d5f823e3d90fd5b9291906149fa61032b426158c4565b9165ffffffffffff8084165f52602090600f8252604094855f20855f528352855f205460018060a01b03986024858b600a54168a5192838092639b4d9d1360e01b82528c60048301525afa9081156133e6575f91614b85575b50614b6d575f91808711614b33575b50946044945f9794614ab08995614aeb958e6001600160401b039e9f9b16885260128552614a948d89209182546131ae565b9055878752601084528b8720805460ff19166001179055612ced565b168452600f8152888420908585525280888420557f0000000000000000000000000000000000000000000000000000000000000000906152d3565b93600a541686519788958694633aa2795360e01b865260048601521660248401525af19081156149755750614b1d5750565b614b30903d805f833e6121ef8183612de0565b50565b614b3f919250866141e8565b90614b49816151c4565b8211614b55575f614a62565b8751630ae3d83f60e21b815260048101879052602490fd5b8751638239160d60e01b815260048101889052602490fd5b614b9c9150863d8811611a6b57611a5c8183612de0565b5f614a53565b6001600160a01b03165f8181526007602052604090205490811580614c39575b614c215765ffffffffffff918280821615159182614c13575b5050614c0157614b3091614bee426158c4565b16815f52600760205260405f20556158f6565b6040516389faef9d60e01b8152600490fd5b60301c16159050825f614bdb565b6024906040519063015ab34360e11b82526004820152fd5b50600660205260405f205415614bc2565b65ffffffffffff614c5d61032b426158c4565b8160045416918291161115614fbf57614c7590612ced565b5f915b600554831015614fa057614c8b836153c4565b50509265ffffffffffff8316155f14614f81575f935b60018060a01b0381165f52600d60205260405f20948554805b614cf1575050600180809495965060a01b03165f52601260205260405f208054614ce8575b50019190614c78565b5f90555f614cdf565b614cfa906141f5565b614d3f614d078289612cd8565b905490602060018060a01b03600a5416604051906001600160601b0319858560031b1c60601b16838301526014825261282482612daa565b03915afa9283156126ca575f93614f4d575b50825f5260116020528360ff60405f2054161580614f37575b614ec35750601160205260ff60405f20541680614e9d575b80614e76575b614db9575b50505060106020528060405f20805460ff8116614dac575b5050614cba565b60ff191690555f80614da5565b6001600160a01b0386165f908152600d6020526040812080549290915b838110614dfe575b50505050505f52601160205260405f2060ff1981541690555f8080614d8d565b614e088184612cd8565b868460031b1c91549060031b1c14614e2257600101614dd6565b5f1984019450919284119050612d045782614e45938203614e4f575b50506155b1565b5f80808080614dde565b61417a614e5f614e6d9285612cd8565b90549060031b1c9284612cd8565b90555f80614e3e565b5065ffffffffffff85165f52600f60205260405f20835f5260205260405f20541515614d88565b5065ffffffffffff89165f52600f60205260405f20835f5260205260405f205415614d82565b9291505065ffffffffffff88165f52600f908160205260405f20815f5260205260405f205415614ef4575050614cba565b65ffffffffffff85165f528160205260405f20815f5260205260405f20549165ffffffffffff8a165f5260205260405f20905f5260205260405f20555f80614da5565b505060106020528360ff60405f20541615614d6a565b9092506020813d602011614f79575b81614f6960209383612de0565b8101031261123e5751915f614d51565b3d9150614f5c565b5f1965ffffffffffff84160165ffffffffffff8111612d045793614ca1565b915065ffffffffffff1665ffffffffffff196004541617600455600190565b505f90565b65ffffffffffff9081168015159392908461500d575b5083614fe7575b50505090565b811680159350918315615000575b5050505f8080614fe1565b16111590505f8080614ff5565b838316101593505f614fda565b805f52600360205261503960405f209260018060a01b031680936159e0565b15615064577fc53536963369dbfa4c398238ebb9b09fce3943a140928bd25d3052a8a9cacdaf5f80a3565b60405163ad2ac00f60e01b8152600490fd5b8115613f51570490565b61508c61032b426158c4565b60018060a01b0382165f52602090600d6020526040805f20549060085494851561512c57600195948387019384105f5b8781106150d157505050505050505050600190565b6150da81612c25565b9054600391821b1c90836150f86001600160601b0384168b896137ea565b90612d04578861510791615076565b915f5284526002865f2001541161511f5788016150bc565b5050505050505050505f90565b505050505050600190565b61514361032b426158c4565b60018060a01b0382165f52602091600d6020526040805f20549160085494851561512c575f5b86811061517c5750505050505050600190565b61518581612c25565b9054600391821b1c906151ab876151a66001600160601b0385168b896137ea565b615076565b915f5283526002855f200154116149a957600101615169565b6151d96151d361032b426158c4565b826131bb565b600a546040805163f113073360e01b8152306004820152929390926001600160a01b039284908290602490829087165afa9081156152c9575f91615287575b506001600160401b037f00000000000000000000000000000000000000000000000000000000000000009116818102918183041490151715612d045780851161527f575b50165f5260126020525f20548082111561527957612d94916141e8565b50505f90565b93505f61525c565b90508381813d83116152c2575b61529e8183612de0565b8101031261123e576020816152b56152bc93613031565b5001613031565b5f615218565b503d615294565b84513d5f823e3d90fd5b906152dd91615076565b6001600160401b03908181116152f1571690565b604051632a6bc64560e01b8152600490fd5b91909165ffffffffffff61532161531c61032b426158c4565b612ced565b165f52600f60205260405f20835f526020525f6040812055601160205260405f20600160ff1982541617905560018060a01b03905f8083600a54166024604051809481936325fedc3560e21b83528a60048401525af180156126ca576153aa575b50167f52dc76c098767cf70c407df0346869d074f6a3aa927b6497938715a0188a6d755f80a4565b6153bd903d805f833e6121ef8183612de0565b505f615382565b6153cd90612c6e565b90549060031b1c805f52600760205260405f20549060018060a01b03169165ffffffffffff8083169260301c1690565b5f9060018060a01b036004938160045460301c16935b604080516374d4e49160e01b81526020919082818a818b5afa9081156155a7575f9161557a575b5083101561556e5780519163328dc3d960e11b835283898401526060602493818186818d5afa9182156152c95791839189935f9261554f575b50508451630d5d5d9360e21b815292168b8301819052948290818c5afa9081156149e1576001600160601b038b93928a925f91615532575b50161493846154c5575b5050505061512c57600101615413565b838293949550519384809263d8dfeb4560e01b82525afa92831561497557505f926154fc575b50508316828416145f8781806154b5565b90809250813d831161552b575b6155138183612de0565b8101031261123e576155249061313b565b5f806154eb565b503d615509565b6155499150843d8611613477576134698183612de0565b5f6154ab565b6155659250803d106134bb576134a88183612de0565b50505f80615473565b50505050505050505f90565b90508281813d83116155a0575b6155918183612de0565b8101031261123e57515f61543a565b503d615587565b82513d5f823e3d90fd5b80549081156155da575f19918201916155ca8383612cd8565b909182549160031b1b1916905555565b634e487b7160e01b5f52603160045260245ffd5b5f818152600960205260409020548015615279575f1990808201818111612d045760085490838201918211612d045781810361565f575b50505060085480156155da5781019061563d82612c25565b909182549160031b1b191690556008555f5260096020525f6040812055600190565b61567d61566e61417a93612c25565b90549060031b1c928392612c25565b90555f52600960205260405f20555f8080615625565b5f818152600660205260409020548015615279575f1990808201818111612d045760055490838201918211612d0457818103615704575b50505060055480156155da578101906156e282612c6e565b909182549160031b1b191690556005555f5260066020525f6040812055600190565b61572261571361417a93612c6e565b90549060031b1c928392612c6e565b90555f52600660205260405f20555f80806156ca565b5f818152600260205260409020548015615279575f1990808201818111612d045760015490838201918211612d04578181036157a9575b50505060015480156155da5781019061578782612ca3565b909182549160031b1b191690556001555f5260026020525f6040812055600190565b6157c76157b861417a93612ca3565b90549060031b1c928392612ca3565b90555f52600260205260405f20555f808061576f565b906001820191815f528260205260405f2054908115155f1461586c575f198281019290818411612d04578254908101908111612d045783816158279503615837575b5050506155b1565b5f526020525f6040812055600190565b61585761584761417a9386612cd8565b90549060031b1c92839286612cd8565b90555f528460205260405f20555f808061581f565b505050505f90565b90604051918281549182825260209260208301915f5260205f20935f905b8282106158aa575050506158a892500383612de0565b565b855484526001958601958895509381019390910190615892565b65ffffffffffff908181116158d7571690565b604490604051906306dfcc6560e41b8252603060048301526024820152fd5b805f52600660205260405f2054155f14614fbf57600554600160401b8110156112555761592d61417a826001859401600555612c6e565b9055600554905f52600660205260405f2055600190565b805f52600260205260405f2054155f14614fbf57600154600160401b8110156112555761597b61417a826001859401600155612ca3565b9055600154905f52600260205260405f2055600190565b805f52600960205260405f2054155f14614fbf57600854600160401b811015611255576159c961417a826001859401600855612c25565b9055600854905f52600960205260405f2055600190565b5f8281526001820160205260409020546152795780615a0183600193614158565b8054925f520160205260405f2055600190565b805f52600760205260405f205490811580615a32575b614c21575090565b50600660205260405f205415615a2a56fea26469706673582212202bc4a3b83242b07631b1b21ba90e919382b5d555006d1770b7ff96f18bc6e8dc64736f6c6343000819003300000000000000000000000084f2b4d4cf8da889701fbe83d127896880c0432500000000000000000000000046d45d6be6214f6bd8124187cad1a5302755d7a2000000000000000000000000c3b09a650c78dafb79e3c5b782402c8dc523fe880000000000000000000000000360c1cb32a20d97b358538d9db71339ce2c9592000000000000000000000000000000000000000000000000000000000000070800000000000000000000000000000000000000000000000000000000000008340000000000000000000000000000000000000000000000000000000000000384000000000000000000000000c15adc0c87db428b9fc0bb3246e7deb2a991aa6e00000000000000000000000059fbba1845690fb202d69a77cfec5564df12d3de00000000000000000000000000000000000000000000001b1ae4d6e2ef5000000000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000470de4df820000
Deployed Bytecode
0x608080604052600436101561001c575b50361561001a575f80fd5b005b5f905f3560e01c9081630e4e7bd514612b115750806311369f0f14612af357806316c58824146129d85780631af4b5af1461295e5780631b834ed3146129065780631d34a34f1461276557806322207cc71461272357806327eb17b2146125ee5780632b84d6b414610b025780632ee59f2d146125c85780633682a4501461239a5780633708d256146122e75780633b013969146122c55780633bc7daf11461207f5780633c7b1d371461201057806342cc179814611fcb5780634fe5b9af14611f5757806352c6f00c14611f22578063554bc85914611e6057806356e1c9e914611dc95780635c04a2d814611e0a5780635f3710d014611dc9578063642d5a2314611d84578063715018a614611d2b57806379b5c25e14611d125780637b426a3b14611cc05780637d3ef55d14611c095780637d7d7e4414611bb15780637ff0d8e514611b6e57806381b1d0be14611b1b578063822ca0461461188757806383ce0322146118425780638da5cb5b1461181b5780638fb4bfbd146117ec57806396523059146117835780639681d940146116be57806396f0bcd21461168957806399e3d630146116655780639d94a1e3146116465780639f4191251461160b578063a2702f5c146115c6578063a365bf8814611570578063a70b9f0c1461152d578063a73e2333146114f4578063a7949eca146114c7578063a7dab16714611498578063ac8a584a14611368578063aeb5803d14610da2578063b2834db714610c67578063b36248e814610c3e578063b543503e14610b7a578063b97dd9e214610b4b578063b9bbb6f514610b1e578063ba14c58c14610b02578063c1c2e42b14610a5f578063c84a1242146108f0578063cbbb83d1146108cc578063d911c63214610849578063dc625d92146106ff578063dd307b9914610691578063ddaa26ad1461064e578063e3839ba314610619578063f2fde38b14610591578063f56408ed146104af578063f944e5c4146104765763fa9952f80361000f573461047357604036600319011261047357600435610316612bcb565b9061031f614866565b61033061032b426158c4565b613ef0565b65ffffffffffff80600454169116908111610455575b50600181148061041f575b6104075761035f82826153fd565b6103ef5761036b614866565b8083526003602052604083206001600160a01b039092169161038e9083906157dd565b156103ba577f03c0a1eb08ee9b373b2eee0ff81874c2eecc632e962925ae0fa3cfac84c594f08380a380f35b825260026020526040822054156103dd57604051631595a2fb60e31b8152600490fd5b60405163366eedf560e01b8152600490fd5b6024906040519063c37ba64d60e01b82526004820152fd5b602490604051906318973d4360e11b82526004820152fd5b506001600160a01b038281167f00000000000000000000000059fbba1845690fb202d69a77cfec5564df12d3de90911614610351565b61045d613b3a565b65ffffffffffff1960045416176004555f610346565b80fd5b5034610473576020366003190112610473576020906040906001600160a01b0361049e612bb5565b168152600d83522054604051908152f35b5034610473576020366003190112610473576104c9612bb5565b6104d1614866565b6104dd61032b426158c4565b60045465ffffffffffff929183169083168111610573575b506001600160a01b031661050881615a14565b9180831615610561578260301c1661054f5761054b916bffffffffffff000000000000610534426158c4565b60301b1617815f52600760205260405f20556158f6565b5080f35b60405163a4cf437160e01b8152600490fd5b60405163702a3be360e01b8152600490fd5b61057b613b3a565b65ffffffffffff1960045416176004555f6104f5565b5034610473576020366003190112610473576105ab612bb5565b6105b3614866565b6001600160a01b03908116908115610600575f54826001600160601b0360a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b604051631e4fbdf760e01b815260048101849052602490fd5b5034610473576040366003190112610473576020610646610638612b5f565b610640612b89565b90614783565b604051908152f35b5034610473578060031936011261047357602060405165ffffffffffff7f0000000000000000000000000000000000000000000000000000000068272eb9168152f35b5034610473576020366003190112610473576106de6106ae612bb5565b6106b6614866565b6106c261032b426158c4565b65ffffffffffff806004541691169081116106e1575b50614ba2565b80f35b6106e9613b3a565b65ffffffffffff1960045416176004555f6106d8565b50346104735760203660031901126104735760043561072061032b426158c4565b65ffffffffffff8060045416911690811161082b575b50805f52600960205260405f205461081357610750614866565b60018082146107fa57818352600360205260408320546107e85761077382615738565b156103dd57818352600360205260408320908154848355806107c5575b50506003815f60028194015501557f72eb970ea8827c3fd1e7e48a5011ec25c35d5ca2cf450465fc92e06579a927a58280a280f35b82855260208520908101905b8181106107de5750610790565b5f815582016107d1565b6040516302db767160e01b8152600490fd5b6040516318973d4360e11b815260048101839052602490fd5b6024906040519063464cd05560e01b82526004820152fd5b610833613b3a565b65ffffffffffff1960045416176004555f610736565b503461047357806003193601126104735760055461086681612fb9565b916108746040519384612de0565b818352601f1961088383612fb9565b013660208501375b8181106108a457604051806108a08582612be1565b0390f35b806108b06001926153c4565b50506108bc8286613002565b90838060a01b031690520161088b565b50346104735760203660031901126104735760206106466108eb612bb5565b6151c4565b50346104735760403660031901126104735761090a612bb5565b9061091761032b426158c4565b9165ffffffffffff92838116835260136020526040832060015f5260205260ff60405f20541615610a4f575b5061095861095361032b426158c4565b612d32565b92610962426158c4565b61098c7f000000000000000000000000000000000000000000000000000000000000038486612d18565b94828216838088168210928315610a17575b5050506109ee5750906106de916109b761032b426158c4565b600454908216911681116109d0575b5060243590614201565b6109d8613b3a565b65ffffffffffff196004541617600455836109c6565b604051633566e89d60e21b815265ffffffffffff91821660048201529085166024820152604490fd5b610a45919293507f000000000000000000000000000000000000000000000000000000000000070890612d18565b16105f838161099e565b610a58906146ab565b505f610943565b503461047357602036600319011261047357600435610a7c614866565b610a8861032b426158c4565b65ffffffffffff80600454169116908111610ae4575b50610ab4815f52600260205260405f2054151590565b156103dd5760018114610ad257610aca90615992565b15610ad25780f35b604051638e2262ff60e01b8152600490fd5b610aec613b3a565b65ffffffffffff1960045416176004555f610a9e565b5034610473578060031936011261047357602060405160018152f35b503461047357806003193601126104735760045460405160309190911c6001600160a01b03168152602090f35b50346104735780600319360112610473576020610b6a61032b426158c4565b65ffffffffffff60405191168152f35b503461047357602036600319011261047357610b94612bb5565b610b9c614866565b6001600160a01b03908082168015610c0957600454928360301c167f8cfaacab0869ad5307d9175b1a62164d7c9630958cbbc7bc9918133cd6fbb02d8580a36601000000000000600160d01b031990911660309190911b6601000000000000600160d01b03161760045580f35b604051635d273a4360e11b815260206004820152600c60248201526b3b30bab63a26b0b730b3b2b960a11b6044820152606490fd5b5034610473578060031936011261047357600a546040516001600160a01b039091168152602090f35b503461047357806003193601126104735760405180916001908154808452602080940190835f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6905f5b818110610d8f5750505084610cc8910385612de0565b8351610cec610cd682612fb9565b91610ce46040519384612de0565b808352612fb9565b8185019590601f190136873782845b610d41575b50506040519380850191818652518092526040850195925b828110610d255785870386f35b83516001600160601b0316875295810195928101928401610d18565b9084969491819693949651811015610d8257806001600160601b03610d67859385613002565b5116610d738288613002565b52019091949695939295610cfb565b5095939594929194610d00565b8254845292860192918501918501610cb2565b50346104735760031960c036820112611360576001600160401b036024351161136057366023602435011215611360576001600160401b03602435600401351161136057366024803560040135813501011161136057604435906001600160401b0382168203611364576001600160401b03606435116113645760408160643536030112611364576001600160401b0360843511611364576040906084353603011261136057610e5461032b426158c4565b65ffffffffffff8116835260136020526040832060015f5260205260ff60405f20541615611350575b50610e8a61032b426158c4565b65ffffffffffff80600454169116908111611332575b50610eb6335f52600660205260405f2054151590565b1561131a57610ec433615080565b1561130857610f20602060018060a01b03600a5416604051906001600160601b031960043560601b168383015260148252610efe82612daa565b604051808095819463fd7ac5e760e01b83528660048401526024830190612e01565b03915afa9081156112fd5783916112cb575b50610f3c336151c4565b90808452601160205260ff6040852054166112b2578352601060205260ff6040842054166112995760018352600360205260408320906003600283015492015460a43515155f146112935760a4355b8181111561128b5750915b8210908115611281575b506112695760405160043560601b6001600160601b031916602082015260148152610fca81612daa565b604051928360a08101106001600160401b0360a086011117611255576001600160401b039160a08501604052845261100760243560040135613016565b6110146040519182612de0565b602480356004810135808452910160208301378560206024356004013583010152602085015216604083015261104f366064356004016140a1565b6060830152611063366084356004016140a1565b6080830152338352601460205261107f600435604085206159e0565b50338352600d60205261109760043560408520614158565b6110a361032b426158c4565b600a546020906001600160401b03906001600160a01b031661116d876110e97f00000000000000000000000000000000000000000000000000470de4df820000886152d3565b9760405198899586948593634dd4b5c360e11b85526040600486015261111b815160a0604488015260e4870190612e01565b608061115b61113b8d8501519360431994858b83030160648c0152612e01565b8660408601511660848a01526060850151848a83030160a48b0152614194565b920151908683030160c4870152614194565b9116602483015203925af192831561124a578493611210575b506111b18165ffffffffffff8093168652600f60205260408620858752602052836040872055612ced565b168352600f60205260408320828452602052806040842055601060205260408320600160ff19825416179055604051908152600435907f517e1badda2dba32b4f95f89957307097e82788cb2e6da8dcddc14850406538a60203392a480f35b9092506020813d602011611242575b8161122c60209383612de0565b8101031261123e5751916111b1611186565b5f80fd5b3d915061121f565b6040513d86823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b60249060405190630ae3d83f60e21b82526004820152fd5b905081115f610fa0565b905091610f96565b81610f8b565b602460405163566c728360e11b81526004356004820152fd5b6024604051635e75d9fb60e11b81526004356004820152fd5b90506020813d6020116112f5575b816112e660209383612de0565b8101031261123e57515f610f32565b3d91506112d9565b6040513d85823e3d90fd5b604051630462cfe760e41b8152600490fd5b604051634c383c5160e01b8152336004820152602490fd5b61133a613b3a565b65ffffffffffff1960045416176004555f610ea0565b611359906146ab565b505f610e7d565b5080fd5b8280fd5b503461047357602036600319011261047357611382612bb5565b61138a614866565b61139661032b426158c4565b60045465ffffffffffff92918316908316811161147a575b506001600160a01b031690806113c383615a14565b60301c16908115908115611439575b506113ef57508061054b91835260076020525f6040842055615693565b60405163e223232360e01b815265ffffffffffff91821660048201527f00000000000000000000000000000000000000000000000000000000000008349091166024820152604490fd5b90506114657f000000000000000000000000000000000000000000000000000000000000083483612d18565b9080611470426158c4565b169116115f6113d2565b611482613b3a565b65ffffffffffff1960045416176004555f6113ae565b50346104735760203660031901126104735760ff60406020926004358152601184522054166040519015158152f35b50346104735760403660031901126104735760206106466114e6612b5f565b6114ee612b89565b90613f65565b5034610473576020366003190112610473576020906040906001600160a01b0361151c612bb5565b168152601283522054604051908152f35b5034610473578060031936011261047357602060405165ffffffffffff7f0000000000000000000000000000000000000000000000000000000000000708168152f35b50346104735760403660031901126104735761158a612bb5565b6001600160a01b03168152600d60205260408120805460243592908310156104735760206115b88484612cd8565b90546040519160031b1c8152f35b50346104735780600319360112610473576040517f00000000000000000000000084f2b4d4cf8da889701fbe83d127896880c043256001600160a01b03168152602090f35b503461047357806003193601126104735760206040517f00000000000000000000000000000000000000000000000000470de4df8200008152f35b5034610473576020366003190112610473576020610b6a61032b612b5f565b5034610473576020366003190112610473576020610646611684612bb5565b613dd1565b50346104735760603660031901126104735760206106466116a8612b5f565b6116b0612bcb565b6116b8612b9f565b91613bc6565b50346104735760203660031901126104735760043563ffffffff811680910361123e5781906116ef61032b426158c4565b65ffffffffffff80600454169116908111611765575b50600a546001600160a01b031690813b1561176157829160248392604051948593849263467ef06f60e01b845260048401525af18015611756576117465750f35b61174f90612d97565b6104735780f35b6040513d84823e3d90fd5b5050fd5b61176d613b3a565b65ffffffffffff1960045416176004555f611705565b5034610473576040366003190112610473576004356117a0612bcb565b6117a8614866565b6117bd825f52600260205260405f2054151590565b156103dd576001600160a01b038116156117da576106de9161501a565b604051630a32281360e21b8152600490fd5b50346104735760203660031901126104735760ff60406020926004358152601084522054166040519015158152f35b5034610473578060031936011261047357546040516001600160a01b039091168152602090f35b50346104735780600319360112610473576040517f00000000000000000000000046d45d6be6214f6bd8124187cad1a5302755d7a26001600160a01b03168152602090f35b5034610473576040366003190112610473576004359060249182359063ffffffff821680920361123e576118c6335f52600660205260405f2054151590565b15611b04573383526020601481526118ef82604086206001915f520160205260405f2054151590565b15611ab75761190061032b426158c4565b65ffffffffffff80600454169116908111611ae6575b5061192c335f52600660205260405f2054151590565b15611acf573384526014815261195382604086206001915f520160205260405f2054151590565b15611ab75790816119ad9260018060a01b03600a541692604051906001600160601b03199060601b16828201526014815261198d81612daa565b6040518095819263fd7ac5e760e01b83528460048401528a830190612e01565b0381855afa928315611aac578593611a7d575b50604051639b4d9d1360e01b81526004810184905281818881865afa918215611a72578692611a45575b505015611a2e57938484953b15611a2a5784928360449260405196879586946376c951cf60e01b865260048601528401525af18015611756576117465750f35b8480fd5b6040516259d43960e71b8152600481018390528590fd5b611a649250803d10611a6b575b611a5c8183612de0565b810190612e25565b5f806119ea565b503d611a52565b6040513d88823e3d90fd5b9080935081813d8311611aa5575b611a958183612de0565b8101031261123e5751915f6119c0565b503d611a8b565b6040513d87823e3d90fd5b60405163c50edab960e01b8152600481018390528590fd5b604051634c383c5160e01b81523360048201528590fd5b611aee613b3a565b65ffffffffffff1960045416176004555f611916565b604051634c383c5160e01b81523360048201528490fd5b50346104735760203660031901126104735760043590611b46825f52600260205260405f2054151590565b156103dd576040816108a093611b629352600360205220615874565b60405191829182612be1565b5034610473578060031936011261047357602060405165ffffffffffff7f0000000000000000000000000000000000000000000000000000000000000384168152f35b50346104735760403660031901126104735765ffffffffffff6040611bd4612bb5565b92611bdd612b74565b9360018060a01b03168152600b6020522091165f52602052602060ff60405f2054166040519015158152f35b503461047357806003193601126104735760405180600854928383526020809301809460085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3905f5b818110611cac5750505082611c6a910383612de0565b6040519260408401906001936001865260406020870152518092526060850195925b828110611c995785870386f35b8351875295810195928101928401611c8c565b825484529286019260019283019201611c54565b503461047357602036600319011261047357600435611cea815f52600260205260405f2054151590565b156103dd57604082819281526003602052206003600282015491015482519182526020820152f35b50346104735780600319360112610473576106de613b3a565b5034610473578060031936011261047357611d44614866565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346104735780600319360112610473576040517f0000000000000000000000000360c1cb32a20d97b358538d9db71339ce2c95926001600160a01b03168152602090f35b503461047357604036600319011261047357604060209165ffffffffffff611def612b5f565b168152600f8352818120602435825283522054604051908152f35b5034610473576020366003190112610473576004356001600160601b03811680910361123e5760209160018214918215611e4b575b50506040519015158152f35b60409250815260098352205415155f80611e3f565b5034610473576080366003190112610473576024356064356004356044356001600160a01b03831680840361123e57611e97614866565b156117da576001821480611f19575b611f0757611eb382615944565b15610ad2577f8828c5448d14dfabfef1825a63323c740b9f79563a375c10bfff3215f754c10960406106de9584938489526003602052806003848b20846002820155015582519182526020820152a261501a565b604051633631e2dd60e21b8152600490fd5b50808411611ea6565b5034610473576060366003190112610473576020610646611f41612bb5565b611f49612b74565b611f51612b9f565b916137ea565b503461047357606036600319011261047357611f71612b5f565b90611f7a612b89565b906044359260018060a01b03841680940361123e576001600160601b039165ffffffffffff604092168152600e6020522091165f5260205260405f20905f52602052602060405f2054604051908152f35b50346104735780600319360112610473576040517f00000000000000000000000059fbba1845690fb202d69a77cfec5564df12d3de6001600160a01b03168152602090f35b5034610473576040366003190112610473576004359061202e612bcb565b612043835f52600260205260405f2054151590565b156103dd57604082602094612075945260038552209060018060a01b0316906001915f520160205260405f2054151590565b6040519015158152f35b503461047357604036600319011261047357612099612bb5565b6120a4610953612b74565b60018060a01b038092168352601491602091601483526120c660408620615874565b936120d18551612fd0565b9286928791600a5416915b8751811015612243576120ef8189613002565b51604051906001600160601b03198160601b168983015285825261211282612daa565b60405163fd7ac5e760e01b815289818061213760249684600484015287830190612e01565b0381895afa908115612238578c9161220b575b5060405190636af907fb60e11b825260048201528b808285818a5afa80156121fe5761219592828893926121db575b505065ffffffffffff60c08160a0840151169201511690614fc4565b6121a4575b50506001016120dc565b9086905f1982146121c95750906121c16001809493019789613002565b52905f61219a565b634e487b7160e01b8c5260116004528bfd5b6121f792503d8091833e6121ef8183612de0565b810190613045565b5f8e612179565b50604051903d90823e3d90fd5b90508981813d8311612231575b6122228183612de0565b8101031261123e57515f61214a565b503d612218565b6040513d8e823e3d90fd5b8887878761225081612fd0565b91845b82811061229e5750505060405192828493840190808552835180925280604086019401925b82811061228757505050500390f35b835185528695509381019392810192600101612278565b806122af6001928498969798613002565b516122ba8288613002565b520194939294612253565b5034610473576020366003190112610473576106de6122e2612b5f565b612e55565b503461047357608036600319011261047357612301612b5f565b612309612bcb565b506064356001600160601b03811680820361123e57612326614866565b604065ffffffffffff948585168152601360205220905f5260205260ff60405f20541615612389575b505061235d61032b426158c4565b600454821691161161237c575b60405163c736d3b160e01b8152600490fd5b612384613b3a565b61236a565b61239291614783565b505f8061234f565b503461047357602080600319360112611360576123b5612bb5565b906123be614866565b6123ca61032b426158c4565b65ffffffffffff806004541691169081116125aa575b506001600160a01b038281165f81815260066020526040902054909291906125915760405163c3c5a54760e01b81526004810184905282816024817f00000000000000000000000046d45d6be6214f6bd8124187cad1a5302755d7a286165afa908115611a72578691612574575b501561255b576040516308834cb560e21b81526001600160a01b0385811660048301527f00000000000000000000000084f2b4d4cf8da889701fbe83d127896880c043259081166024830152918390829060449082907f0000000000000000000000000360c1cb32a20d97b358538d9db71339ce2c9592165afa908115611a7257869161253e575b501561251357509060076124f392828652528360408120556158f6565b15612501576106de90614ba2565b604051635335595160e11b8152600490fd5b60405163a2cb00e760e01b81526001600160a01b038581166004830152919091166024820152604490fd5b6125559150833d8511611a6b57611a5c8183612de0565b5f6124d6565b604051634c383c5160e01b815260048101849052602490fd5b61258b9150833d8511611a6b57611a5c8183612de0565b5f61244e565b604051631da9d69960e01b815260048101849052602490fd5b6125b2613b3a565b65ffffffffffff1960045416176004555f6123e0565b5034610473578060031936011261047357602065ffffffffffff60045416604051908152f35b503461123e57606036600319011261123e57612608612bb5565b602435906044359163ffffffff831680930361123e5761262a61032b426158c4565b65ffffffffffff80600454169116908111612705575b506001600160a01b039182165f81815260066020526040902054156126ed575f5260146020526126818160405f206001915f520160205260405f2054151590565b156126d55750600a541690813b1561123e575f91602483926040519485938492631474cbc960e31b845260048401525af180156126ca576126c0575080f35b61001a9150612d97565b6040513d5f823e3d90fd5b6024906040519063c50edab960e01b82526004820152fd5b60249060405190634c383c5160e01b82526004820152fd5b61270d613b3a565b65ffffffffffff1960045416176004555f612640565b3461123e575f36600319011261123e57602060405165ffffffffffff7f0000000000000000000000000000000000000000000000000000000000000834168152f35b3461123e57604036600319011261123e5760043560243561278861032b426158c4565b65ffffffffffff806004541691169081116128e8575b50335f526020601481526127c38360405f206001915f520160205260405f2054151590565b156128cf5760015f526003815260405f20600360028201549101548084116128b157508083106128935750600a5460405160609490941b6001600160601b03191684830152601484526128469382916001600160a01b031661282482612daa565b604051808097819463fd7ac5e760e01b83528660048401526024830190612e01565b03915afa9081156126ca575f91612863575b61001a8383336149eb565b905082813d831161288c575b6128798183612de0565b8101031261123e5761001a915183612858565b503d61286f565b82604491604051916353fe4d4b60e11b835260048301526024820152fd5b83604491604051916369367fbf60e01b835260048301526024820152fd5b60405163c50edab960e01b815260048101849052602490fd5b6128f0613b3a565b65ffffffffffff1960045416176004558261279e565b3461123e57604036600319011261123e5761291f612b5f565b65ffffffffffff61292e612b89565b91165f5260136020526001600160601b0360405f2091165f52602052602060ff60405f2054166040519015158152f35b3461123e57602036600319011261123e5760043561297a614866565b61298661032b426158c4565b65ffffffffffff806004541691169081116129ba575b506129a681614891565b6103ef576129b3906155ee565b156103dd57005b6129c2613b3a565b65ffffffffffff1960045416176004558161299c565b3461123e5760208060031936011261123e576004356129f961032b426158c4565b65ffffffffffff80600454169116908111612ad5575b50612a25335f52600660205260405f2054151590565b1561131a57335f5260148252612a4c8160405f206001915f520160205260405f2054151590565b156126d557600a54604051606083901b6001600160601b0319168185015260148152612a8893918291906001600160a01b031661282482612daa565b03915afa9081156126ca575f91612aa5575b61001a838333615303565b905082813d8311612ace575b612abb8183612de0565b8101031261123e5761001a915183612a9a565b503d612ab1565b612add613b3a565b65ffffffffffff19600454161760045582612a0f565b3461123e57602036600319011261123e576020610b6a610953612b5f565b3461123e57604036600319011261123e57602090612b2d612b5f565b65ffffffffffff612b3c612b89565b91165f52600c83526001600160601b0360405f2091165f52825260405f20548152f35b6004359065ffffffffffff8216820361123e57565b6024359065ffffffffffff8216820361123e57565b602435906001600160601b038216820361123e57565b604435906001600160601b038216820361123e57565b600435906001600160a01b038216820361123e57565b602435906001600160a01b038216820361123e57565b60209060206040818301928281528551809452019301915f5b828110612c08575050505090565b83516001600160a01b031685529381019392810192600101612bfa565b600854811015612c5a5760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee301905f90565b634e487b7160e01b5f52603260045260245ffd5b600554811015612c5a5760055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001905f90565b600154811015612c5a5760015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601905f90565b8054821015612c5a575f5260205f2001905f90565b90600165ffffffffffff80931601918211612d0457565b634e487b7160e01b5f52601160045260245ffd5b91909165ffffffffffff80809416911601918211612d0457565b65ffffffffffff9081167f0000000000000000000000000000000000000000000000000000000000000708821602908116908103612d0457612d94907f0000000000000000000000000000000000000000000000000000000068272eb9612d18565b90565b6001600160401b03811161125557604052565b604081019081106001600160401b0382111761125557604052565b602081019081106001600160401b0382111761125557604052565b90601f801991011681019081106001600160401b0382111761125557604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9081602091031261123e5751801515810361123e5790565b65ffffffffffff9182169082160391908211612d0457565b9065ffffffffffff91828116908115612fa757612e7461032b426158c4565b915f60049186600454169087861695828711612f93575b50878316809111612f46575b50505091905f925f925b86831687851610612ef7575b50506004546040805165ffffffffffff97909216871682529390951660208601525091927f4d292688ca328e445b76fb7747c31983e6367477e08b40b6a30f7cf280ee80b69250a1565b9091929381878454161015612f4057612f0e614c4a565b15612f40578616868114612f2d57600190810194018616929190612ea1565b601183634e487b7160e01b5f525260245ffd5b93612ead565b909192935015612f5a5750905f8080612e97565b9150507f4d292688ca328e445b76fb7747c31983e6367477e08b40b6a30f7cf280ee80b69293506040809250519081525f6020820152a1565b612fa09193508290612e3d565b915f612e8b565b604051631752afd760e31b8152600490fd5b6001600160401b0381116112555760051b60200190565b90612fda82612fb9565b612fe76040519182612de0565b8281528092612ff8601f1991612fb9565b0190602036910137565b8051821015612c5a5760209160051b010190565b6001600160401b03811161125557601f01601f191660200190565b51906001600160401b038216820361123e57565b90602090818382031261123e5782516001600160401b039384821161123e570160e08183031261123e576040519360e0850185811082821117611255576040528151600681101561123e5785528382015190811161123e5781019180601f8401121561123e578251936130b785613016565b916130c56040519384612de0565b85835281868601011161123e575f8186613133978260c098018387015e840101528501526130f560408201613031565b604085015261310660608201613031565b606085015261311760808201613031565b608085015261312860a08201613031565b60a085015201613031565b60c082015290565b51906001600160a01b038216820361123e57565b519065ffffffffffff8216820361123e57565b9081606091031261123e576131768161313b565b91612d9460406131886020850161314f565b930161314f565b9081602091031261123e57516001600160601b038116810361123e5790565b91908201809211612d0457565b91905f9265ffffffffffff80831691825f5260209360138552604090815f209460019560015f52875260ff835f2054166134fb57506131f990612d32565b9460018060a01b039560049680885460301c16958451916374d4e49160e01b835284838b818b5afa9283156134f1575f936134c2575b50989796905f96959493929699807f00000000000000000000000084f2b4d4cf8da889701fbe83d127896880c0432516918416925b888c1061327957505050505050505050505050565b909192939495969798999a9c8d88519063328dc3d960e11b82528d820152606060249181818481885afa801561347e5789905f925f905f92613488575b506132c2939450614fc4565b15613447578951639374a51d60e01b8152908516818f018190529089818481885afa90811561347e578e916001600160601b03915f91613451575b50160361344757888e918b5192838092630ce9b79360e41b82525afa90811561343d579189918f8f9489918f8f908b935f966133f0575b50908361337f9282519261334784612dc5565b5f8452519a8b998a988997633dc5038960e01b89528801528601521660448401528d606484015260a0608484015260a4830190612e01565b0392165afa9081156133e657908c915f916133b3575b506133a091926131ae565b9d5b019a99989796959493929190613264565b809250898092503d83116133df575b6133cc8183612de0565b8101031261123e57518b906133a0613395565b503d6133c2565b89513d5f823e3d90fd5b9796985050505050505081813d8311613436575b61340e8183612de0565b8101031261123e578c918b8f87899161337f8f8f9761342d859161313b565b96919250613334565b503d613404565b8a513d5f823e3d90fd5b50509c8a906133a2565b61347191508c8d3d10613477575b6134698183612de0565b81019061318f565b5f6132fd565b503d61345f565b8b513d5f823e3d90fd5b9150506132c29250836134b09294903d106134bb575b6134a88183612de0565b810190613162565b9093919384936132b6565b503d61349e565b9092508481813d83116134ea575b6134da8183612de0565b8101031261123e5751915f61322f565b503d6134d0565b86513d5f823e3d90fd5b5f908152600e87528281206001825287528281206001600160a01b039094168152929095529020549450505050565b91905f9265ffffffffffff80831691825f5260209360138552604090815f209460019560015f52875260ff835f2054166134fb575061356890612d32565b9460018060a01b039560049680885460301c16958451916374d4e49160e01b835284838b818b5afa9283156134f1575f936137bb575b50989796905f96959493929699807f00000000000000000000000084f2b4d4cf8da889701fbe83d127896880c0432516918416925b888c106135e857505050505050505050505050565b909192939495969798999a9c8d88519063328dc3d960e11b82528d820152606060249181818481885afa801561347e5789905f925f905f92613791575b50613631939450614fc4565b1561376a578951639374a51d60e01b8152908516818f018190529089818481885afa90811561347e578e916001600160601b03915f91613774575b50160361376a57888e918b5192838092630ce9b79360e41b82525afa90811561343d579189918f8f9489918f8f908b935f9661371d575b5090836136b69282519261334784612dc5565b0392165afa9081156133e657908c915f916136ea575b506136d791926131ae565b9d5b019a999897969594939291906135d3565b809250898092503d8311613716575b6137038183612de0565b8101031261123e57518b906136d76136cc565b503d6136f9565b9796985050505050505081813d8311613763575b61373b8183612de0565b8101031261123e578c918b8f8789916136b68f8f9761375a859161313b565b969192506136a3565b503d613731565b50509c8a906136d9565b61378b91508c8d3d10613477576134698183612de0565b5f61366c565b9150506136319250836137b09294903d106134bb576134a88183612de0565b909391938493613625565b9092508481813d83116137e3575b6137d38183612de0565b8101031261123e5751915f61359e565b503d6137c9565b929190925f9365ffffffffffff9283821690815f5260209060138252604093845f20936001600160601b0380931694855f52845260ff865f205416613b0b575061383390612d32565b9060018060a01b039660049780895460301c16978751936374d4e49160e01b855286858c818d5afa9485156133e6575f95613adc575b5099989291905f9897969594989a827f00000000000000000000000084f2b4d4cf8da889701fbe83d127896880c0432516938616945b8a8d106138b55750505050505050505050505050565b909192939495969798999a9b9d8c8f8c519163328dc3d960e11b8352820152606060249181818481885afa8015613aa8578b905f925f905f92613ab2575b506138ff939450614fc4565b15613a80578e90878e5191639374a51d60e01b83521680928201528b818481885afa908115613aa8578d9187918e5f92613a8b575b50501603613a80578a8f918e5192838092630ce9b79360e41b82525afa908115613a7657918f91898f8f95948f95918e8c8e945f97613a25575b50908483926139b994519361398285612dc5565b5f8552519b8c9a8b998a98633dc5038960e01b8a52890152870152166044850152606484015260a0608484015260a4830190612e01565b0392165afa908115613a1b575f916139ed575b506139d9906001926131ae565b9e5b019b9a9998979695949392919061389f565b90508981813d8311613a14575b613a048183612de0565b8101031261123e575160016139cc565b503d6139fa565b8c513d5f823e3d90fd5b989799505050505050505081813d8311613a6f575b613a448183612de0565b8101031261123e578b918f898f89928f958c928f84613a656139b99461313b565b979192935061396e565b503d613a3a565b8d513d5f823e3d90fd5b50509d6001906139db565b613aa19250803d10613477576134698183612de0565b5f8e613934565b8e513d5f823e3d90fd5b9150506138ff925083613ad19294903d106134bb576134a88183612de0565b9093919384936138f3565b9094508681813d8311613b04575b613af48183612de0565b8101031261123e5751935f613869565b503d613aea565b5f908152600e845285812094815293835250508282206001600160a01b03909416825292909252902054925050565b613b4661032b426158c4565b65ffffffffffff90816004541690818382161115613bc1578291613b6991612e3d565b1660019060018111613ba2575f5b8184821610613b87575b50505050565b613b8f614c4a565b15613b9d5782018316613b77565b613b81565b60449060405190633c90bbed60e21b8252600482015260016024820152fd5b505050565b9091600191906001600160601b0381168303613dc6575060408051633bc7daf160e01b81526001600160a01b039490941660048086019190915265ffffffffffff831660248601529391905f83604481305afa928315613dbc575f93613d2b575b50600a545f969587959490916001600160a01b031690855b613c4d575b50505050505050565b8051871015613d2657613cab98613c648883613002565b5185519060209182916001600160601b03199060601b168282015260148152613c8c81612daa565b8751809d819263fd7ac5e760e01b8352848a8401526024830190612e01565b0381875afa9081156134f15788999a9b5f92613cf3575b5091613cea91899365ffffffffffff8a165f52600f8152885f20915f5252865f2054906131ae565b99980196613c3f565b8180949a508193503d8311613d1f575b613d0d8183612de0565b8101031261123e575188979188613cc2565b503d613d03565b613c44565b9092503d805f833e613d3d8183612de0565b810190602090818184031261123e578051906001600160401b03821161123e57019180601f8401121561123e578251613d7581612fb9565b93613d8287519586612de0565b818552838086019260051b82010192831161123e578301905b828210613dad5750505050915f613c27565b81518152908301908301613d9b565b50513d5f823e3d90fd5b9150612d94926137ea565b905f9160018060a01b038091165f52602090600d8252604090815f20925f93805492600a5416905b838610613e0857505050505050565b909192939496613e6584613e1c8a85612cd8565b9054895160039290921b1c60601b6001600160601b0319168282015260148152613e4581612daa565b88518093819263fd7ac5e760e01b83528460048401526024830190612e01565b0381875afa908115613ee6575f91613eb8575b50600191613eac9165ffffffffffff613e9361032b426158c4565b165f52600f8752885f20905f528652875f2054906131ae565b97019493929190613df9565b90508481813d8311613edf575b613ecf8183612de0565b8101031261123e57516001613e78565b503d613ec5565b87513d5f823e3d90fd5b613f1b907f0000000000000000000000000000000000000000000000000000000068272eb990612e3d565b65ffffffffffff807f000000000000000000000000000000000000000000000000000000000000070816918215613f5157160490565b634e487b7160e01b5f52601260045260245ffd5b9065ffffffffffff91828116805f52601360205260405f20906001600160601b03841691825f5260205260ff60405f2054166140835750505f92613fa882612d32565b9080613fb3426158c4565b1690808316918211908115614045575b5061402d575090600554915f935b838510613fe057505050505090565b909192939482613ffb613ff2886153c4565b90939193614fc4565b15614023576001916140118487614017946137ea565b906131ae565b955b0193929190613fd1565b5094600190614019565b6024906040519063c2ef899360e01b82526004820152fd5b905061407a614053426158c4565b7f000000000000000000000000000000000000000000000000000000000000083490612e3d565b1681105f613fc3565b92509250505f52600c60205260405f20905f5260205260405f205490565b919060408382031261123e57604080516001600160401b039491810185811182821017611255576040528094823563ffffffff8116810361123e5782526020928381013591821161123e57019280601f8501121561123e57833561410481612fb9565b946141126040519687612de0565b818652848087019260051b82010192831161123e578401905b828210614139575050500152565b81356001600160a01b038116810361123e57815290840190840161412b565b805490600160401b821015611255578161417a91600161419094018155612cd8565b819391549060031b91821b915f19901b19161790565b9055565b6040820163ffffffff8251168352602060608180940151946040838201528551809452019301915f5b8281106141cb575050505090565b83516001600160a01b0316855293810193928101926001016141bd565b91908203918211612d0457565b8015612d04575f190190565b61420d61032b426158c4565b9160018060a01b0382165f52600b60205260405f2065ffffffffffff84165f5260205260ff60405f205416614677576001600160a01b0382165f818152600b6020908152604080832065ffffffffffff881684528252808320805460ff191660011790559282526006905220541561465657614288826151c4565b61429183613dd1565b9060018060a01b0384165f52600d60205260405f209586549280831461464c578083116145cc57826142c2916141e8565b925b801515806145c3575b15614582576142db906141f5565b916143416142e9848a612cd8565b91905460018060a01b03600a541660206040516001600160601b0319848760031b1c60601b16828201526014815261432081612daa565b6040518095819263fd7ac5e760e01b83528460048401526024830190612e01565b0381845afa9283156126ca575f9361454e575b50604051639b4d9d1360e01b815260048101849052602081602481855afa9081156126ca575f9161452f575b50614526575f60249160405192838092636af907fb60e11b82528760048301525afa9081156126ca575f9161450c575b50519660068810156144f85760028098036144ee5765ffffffffffff8b165f52600f60205260405f20835f5260205260405f205480156144e35761441b90808310156144dd5782905b8b15158c83826144d3575b50506144cb575b81614415916141e8565b926141e8565b60015f5260036020527fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c90980154811080156144bb575b1561446d57506144679260031b1c9088615303565b916142c4565b919261447a83858c6149eb565b60405192835260031b1c906001600160a01b038916907f48d83a0359ece1af3ea738ea6e14f7f30eb31b2c57f44b98dbc41469c83a525a90602090a4614467565b506144c58a615137565b15614452565b8b915061440b565b1190508c5f614404565b806143f9565b509650505050614467565b9650505050614467565b634e487b7160e01b5f52602160045260245ffd5b61452091503d805f833e6121ef8183612de0565b5f6143b0565b50505050614467565b614548915060203d602011611a6b57611a5c8183612de0565b5f614380565b9092506020813d60201161457a575b8161456a60209383612de0565b8101031261123e5751915f614354565b3d915061455d565b5060405191825295506001600160a01b0390931693507fac43c581689bab0b319a9fc8e659012b23ee3494f1bf17edad354d457ccba3eb92602092509050a2565b508315156142cd565b90965081614603929650602094507fac43c581689bab0b319a9fc8e659012b23ee3494f1bf17edad354d457ccba3eb9593506141e8565b6040519081526001600160a01b038216907fb066d744c0abef0595669b34ee833c2d24b4d89382dc63f616513f3024a09464908490a26040519384526001600160a01b031692a2565b5095505050505050565b604051634c383c5160e01b81526001600160a01b0383166004820152602490fd5b5060405163316d57cf60e11b81526001600160a01b0391909116600482015265ffffffffffff919091166024820152604490fd5b905f916146b781612d32565b6005545f915b81831061470b5750505065ffffffffffff16805f52600c60205260405f2060015f526020528260405f20555f52601360205260405f2060015f5260205260405f20600160ff19825416179055565b829561472461471b8394956153c4565b90949194614fc4565b15614779578161476f9161473a8760019561352a565b809265ffffffffffff89165f52602090600e8252604091825f20885f528152825f2091888060a01b03165f52525f20556131ae565b955b0191906146bd565b9560019150614771565b91905f9261479081612d32565b6005545f915b8183106147ed5750505065ffffffffffff16805f52600c6020526001600160601b0360405f20921691825f526020528360405f20555f52601360205260405f20905f5260205260405f20600160ff19825416179055565b82966147fd61471b8394956153c4565b1561485c57816148529161481488886001966137ea565b809265ffffffffffff89165f52602090600e8252604091825f206001600160601b038d165f528152825f2091888060a01b03165f52525f20556131ae565b965b019190614796565b9660019150614854565b5f546001600160a01b0316330361487957565b60405163118cdaa760e01b8152336004820152602490fd5b6004805460301c6001600160a01b0390811692915f5b604080516374d4e49160e01b8152602090818186818b5afa9081156149e1575f916149b4575b508310156149a957815163328dc3d960e11b8152848101849052606090602490828183818d5afa92831561499f57918493918995935f9361497e575b50508551948592630d5d5d9360e21b84521688830152818b5afa9283156149755750916001600160601b039187935f92614958575b5050161461494e576001016148a7565b5050505050600190565b61496e9250803d10613477576134698183612de0565b5f8061493e565b513d5f823e3d90fd5b614995929350803d106134bb576134a88183612de0565b5050905f80614909565b85513d5f823e3d90fd5b505050505050505f90565b90508181813d83116149da575b6149cb8183612de0565b8101031261123e57515f6148cd565b503d6149c1565b83513d5f823e3d90fd5b9291906149fa61032b426158c4565b9165ffffffffffff8084165f52602090600f8252604094855f20855f528352855f205460018060a01b03986024858b600a54168a5192838092639b4d9d1360e01b82528c60048301525afa9081156133e6575f91614b85575b50614b6d575f91808711614b33575b50946044945f9794614ab08995614aeb958e6001600160401b039e9f9b16885260128552614a948d89209182546131ae565b9055878752601084528b8720805460ff19166001179055612ced565b168452600f8152888420908585525280888420557f00000000000000000000000000000000000000000000000000470de4df820000906152d3565b93600a541686519788958694633aa2795360e01b865260048601521660248401525af19081156149755750614b1d5750565b614b30903d805f833e6121ef8183612de0565b50565b614b3f919250866141e8565b90614b49816151c4565b8211614b55575f614a62565b8751630ae3d83f60e21b815260048101879052602490fd5b8751638239160d60e01b815260048101889052602490fd5b614b9c9150863d8811611a6b57611a5c8183612de0565b5f614a53565b6001600160a01b03165f8181526007602052604090205490811580614c39575b614c215765ffffffffffff918280821615159182614c13575b5050614c0157614b3091614bee426158c4565b16815f52600760205260405f20556158f6565b6040516389faef9d60e01b8152600490fd5b60301c16159050825f614bdb565b6024906040519063015ab34360e11b82526004820152fd5b50600660205260405f205415614bc2565b65ffffffffffff614c5d61032b426158c4565b8160045416918291161115614fbf57614c7590612ced565b5f915b600554831015614fa057614c8b836153c4565b50509265ffffffffffff8316155f14614f81575f935b60018060a01b0381165f52600d60205260405f20948554805b614cf1575050600180809495965060a01b03165f52601260205260405f208054614ce8575b50019190614c78565b5f90555f614cdf565b614cfa906141f5565b614d3f614d078289612cd8565b905490602060018060a01b03600a5416604051906001600160601b0319858560031b1c60601b16838301526014825261282482612daa565b03915afa9283156126ca575f93614f4d575b50825f5260116020528360ff60405f2054161580614f37575b614ec35750601160205260ff60405f20541680614e9d575b80614e76575b614db9575b50505060106020528060405f20805460ff8116614dac575b5050614cba565b60ff191690555f80614da5565b6001600160a01b0386165f908152600d6020526040812080549290915b838110614dfe575b50505050505f52601160205260405f2060ff1981541690555f8080614d8d565b614e088184612cd8565b868460031b1c91549060031b1c14614e2257600101614dd6565b5f1984019450919284119050612d045782614e45938203614e4f575b50506155b1565b5f80808080614dde565b61417a614e5f614e6d9285612cd8565b90549060031b1c9284612cd8565b90555f80614e3e565b5065ffffffffffff85165f52600f60205260405f20835f5260205260405f20541515614d88565b5065ffffffffffff89165f52600f60205260405f20835f5260205260405f205415614d82565b9291505065ffffffffffff88165f52600f908160205260405f20815f5260205260405f205415614ef4575050614cba565b65ffffffffffff85165f528160205260405f20815f5260205260405f20549165ffffffffffff8a165f5260205260405f20905f5260205260405f20555f80614da5565b505060106020528360ff60405f20541615614d6a565b9092506020813d602011614f79575b81614f6960209383612de0565b8101031261123e5751915f614d51565b3d9150614f5c565b5f1965ffffffffffff84160165ffffffffffff8111612d045793614ca1565b915065ffffffffffff1665ffffffffffff196004541617600455600190565b505f90565b65ffffffffffff9081168015159392908461500d575b5083614fe7575b50505090565b811680159350918315615000575b5050505f8080614fe1565b16111590505f8080614ff5565b838316101593505f614fda565b805f52600360205261503960405f209260018060a01b031680936159e0565b15615064577fc53536963369dbfa4c398238ebb9b09fce3943a140928bd25d3052a8a9cacdaf5f80a3565b60405163ad2ac00f60e01b8152600490fd5b8115613f51570490565b61508c61032b426158c4565b60018060a01b0382165f52602090600d6020526040805f20549060085494851561512c57600195948387019384105f5b8781106150d157505050505050505050600190565b6150da81612c25565b9054600391821b1c90836150f86001600160601b0384168b896137ea565b90612d04578861510791615076565b915f5284526002865f2001541161511f5788016150bc565b5050505050505050505f90565b505050505050600190565b61514361032b426158c4565b60018060a01b0382165f52602091600d6020526040805f20549160085494851561512c575f5b86811061517c5750505050505050600190565b61518581612c25565b9054600391821b1c906151ab876151a66001600160601b0385168b896137ea565b615076565b915f5283526002855f200154116149a957600101615169565b6151d96151d361032b426158c4565b826131bb565b600a546040805163f113073360e01b8152306004820152929390926001600160a01b039284908290602490829087165afa9081156152c9575f91615287575b506001600160401b037f00000000000000000000000000000000000000000000000000470de4df8200009116818102918183041490151715612d045780851161527f575b50165f5260126020525f20548082111561527957612d94916141e8565b50505f90565b93505f61525c565b90508381813d83116152c2575b61529e8183612de0565b8101031261123e576020816152b56152bc93613031565b5001613031565b5f615218565b503d615294565b84513d5f823e3d90fd5b906152dd91615076565b6001600160401b03908181116152f1571690565b604051632a6bc64560e01b8152600490fd5b91909165ffffffffffff61532161531c61032b426158c4565b612ced565b165f52600f60205260405f20835f526020525f6040812055601160205260405f20600160ff1982541617905560018060a01b03905f8083600a54166024604051809481936325fedc3560e21b83528a60048401525af180156126ca576153aa575b50167f52dc76c098767cf70c407df0346869d074f6a3aa927b6497938715a0188a6d755f80a4565b6153bd903d805f833e6121ef8183612de0565b505f615382565b6153cd90612c6e565b90549060031b1c805f52600760205260405f20549060018060a01b03169165ffffffffffff8083169260301c1690565b5f9060018060a01b036004938160045460301c16935b604080516374d4e49160e01b81526020919082818a818b5afa9081156155a7575f9161557a575b5083101561556e5780519163328dc3d960e11b835283898401526060602493818186818d5afa9182156152c95791839189935f9261554f575b50508451630d5d5d9360e21b815292168b8301819052948290818c5afa9081156149e1576001600160601b038b93928a925f91615532575b50161493846154c5575b5050505061512c57600101615413565b838293949550519384809263d8dfeb4560e01b82525afa92831561497557505f926154fc575b50508316828416145f8781806154b5565b90809250813d831161552b575b6155138183612de0565b8101031261123e576155249061313b565b5f806154eb565b503d615509565b6155499150843d8611613477576134698183612de0565b5f6154ab565b6155659250803d106134bb576134a88183612de0565b50505f80615473565b50505050505050505f90565b90508281813d83116155a0575b6155918183612de0565b8101031261123e57515f61543a565b503d615587565b82513d5f823e3d90fd5b80549081156155da575f19918201916155ca8383612cd8565b909182549160031b1b1916905555565b634e487b7160e01b5f52603160045260245ffd5b5f818152600960205260409020548015615279575f1990808201818111612d045760085490838201918211612d045781810361565f575b50505060085480156155da5781019061563d82612c25565b909182549160031b1b191690556008555f5260096020525f6040812055600190565b61567d61566e61417a93612c25565b90549060031b1c928392612c25565b90555f52600960205260405f20555f8080615625565b5f818152600660205260409020548015615279575f1990808201818111612d045760055490838201918211612d0457818103615704575b50505060055480156155da578101906156e282612c6e565b909182549160031b1b191690556005555f5260066020525f6040812055600190565b61572261571361417a93612c6e565b90549060031b1c928392612c6e565b90555f52600660205260405f20555f80806156ca565b5f818152600260205260409020548015615279575f1990808201818111612d045760015490838201918211612d04578181036157a9575b50505060015480156155da5781019061578782612ca3565b909182549160031b1b191690556001555f5260026020525f6040812055600190565b6157c76157b861417a93612ca3565b90549060031b1c928392612ca3565b90555f52600260205260405f20555f808061576f565b906001820191815f528260205260405f2054908115155f1461586c575f198281019290818411612d04578254908101908111612d045783816158279503615837575b5050506155b1565b5f526020525f6040812055600190565b61585761584761417a9386612cd8565b90549060031b1c92839286612cd8565b90555f528460205260405f20555f808061581f565b505050505f90565b90604051918281549182825260209260208301915f5260205f20935f905b8282106158aa575050506158a892500383612de0565b565b855484526001958601958895509381019390910190615892565b65ffffffffffff908181116158d7571690565b604490604051906306dfcc6560e41b8252603060048301526024820152fd5b805f52600660205260405f2054155f14614fbf57600554600160401b8110156112555761592d61417a826001859401600555612c6e565b9055600554905f52600660205260405f2055600190565b805f52600260205260405f2054155f14614fbf57600154600160401b8110156112555761597b61417a826001859401600155612ca3565b9055600154905f52600260205260405f2055600190565b805f52600960205260405f2054155f14614fbf57600854600160401b811015611255576159c961417a826001859401600855612c25565b9055600854905f52600960205260405f2055600190565b5f8281526001820160205260409020546152795780615a0183600193614158565b8054925f520160205260405f2055600190565b805f52600760205260405f205490811580615a32575b614c21575090565b50600660205260405f205415615a2a56fea26469706673582212202bc4a3b83242b07631b1b21ba90e919382b5d555006d1770b7ff96f18bc6e8dc64736f6c63430008190033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000084f2b4d4cf8da889701fbe83d127896880c0432500000000000000000000000046d45d6be6214f6bd8124187cad1a5302755d7a2000000000000000000000000c3b09a650c78dafb79e3c5b782402c8dc523fe880000000000000000000000000360c1cb32a20d97b358538d9db71339ce2c9592000000000000000000000000000000000000000000000000000000000000070800000000000000000000000000000000000000000000000000000000000008340000000000000000000000000000000000000000000000000000000000000384000000000000000000000000c15adc0c87db428b9fc0bb3246e7deb2a991aa6e00000000000000000000000059fbba1845690fb202d69a77cfec5564df12d3de00000000000000000000000000000000000000000000001b1ae4d6e2ef5000000000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000470de4df820000
-----Decoded View---------------
Arg [0] : settings (tuple):
Arg [1] : l1ValidatorManager (address): 0x84F2B4D4cF8DA889701fBe83d127896880c04325
Arg [2] : operatorRegistry (address): 0x46D45D6be6214F6bd8124187caD1a5302755d7A2
Arg [3] : vaultRegistry (address): 0xC3b09a650c78daFb79e3C5B782402C8dc523fE88
Arg [4] : operatorL1Optin (address): 0x0360C1cB32A20D97b358538D9Db71339ce2c9592
Arg [5] : epochDuration (uint48): 1800
Arg [6] : slashingWindow (uint48): 2100
Arg [7] : stakeUpdateWindow (uint48): 900
Arg [1] : owner (address): 0xC15adc0C87db428B9FC0Bb3246e7dEb2A991aa6E
Arg [2] : primaryAsset (address): 0x59fbBa1845690Fb202d69a77CFEc5564df12D3DE
Arg [3] : primaryAssetMaxStake (uint256): 500000000000000000000
Arg [4] : primaryAssetMinStake (uint256): 100000000000000000000
Arg [5] : primaryAssetWeightScaleFactor (uint256): 20000000000000000
-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 00000000000000000000000084f2b4d4cf8da889701fbe83d127896880c04325
Arg [1] : 00000000000000000000000046d45d6be6214f6bd8124187cad1a5302755d7a2
Arg [2] : 000000000000000000000000c3b09a650c78dafb79e3c5b782402c8dc523fe88
Arg [3] : 0000000000000000000000000360c1cb32a20d97b358538d9db71339ce2c9592
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000708
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000834
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000384
Arg [7] : 000000000000000000000000c15adc0c87db428b9fc0bb3246e7deb2a991aa6e
Arg [8] : 00000000000000000000000059fbba1845690fb202d69a77cfec5564df12d3de
Arg [9] : 00000000000000000000000000000000000000000000001b1ae4d6e2ef500000
Arg [10] : 0000000000000000000000000000000000000000000000056bc75e2d63100000
Arg [11] : 00000000000000000000000000000000000000000000000000470de4df820000
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.