Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
RiverV1
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 100 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "./interfaces/IAllowlist.1.sol";
import "./interfaces/IOperatorRegistry.1.sol";
import "./interfaces/IRiver.1.sol";
import "./interfaces/IWithdraw.1.sol";
import "./interfaces/IELFeeRecipient.1.sol";
import "./interfaces/ICoverageFund.1.sol";
import "./interfaces/IProtocolVersion.sol";
import "./components/ConsensusLayerDepositManager.1.sol";
import "./components/UserDepositManager.1.sol";
import "./components/SharesManager.1.sol";
import "./components/OracleManager.1.sol";
import "./Initializable.sol";
import "./Administrable.sol";
import "./libraries/LibAllowlistMasks.sol";
import "./state/river/AllowlistAddress.sol";
import "./state/river/RedeemManagerAddress.sol";
import "./state/river/OperatorsRegistryAddress.sol";
import "./state/river/CollectorAddress.sol";
import "./state/river/ELFeeRecipientAddress.sol";
import "./state/river/CoverageFundAddress.sol";
import "./state/river/BalanceToRedeem.sol";
import "./state/river/GlobalFee.sol";
import "./state/river/MetadataURI.sol";
import "./state/river/LastConsensusLayerReport.sol";
/// @title River (v1)
/// @author Alluvial Finance Inc.
/// @notice This contract merges all the manager contracts and implements all the virtual methods stitching all components together
contract RiverV1 is
ConsensusLayerDepositManagerV1,
UserDepositManagerV1,
SharesManagerV1,
OracleManagerV1,
Initializable,
Administrable,
IProtocolVersion,
IRiverV1
{
/// @inheritdoc IRiverV1
function initRiverV1(
address _depositContractAddress,
address _elFeeRecipientAddress,
bytes32 _withdrawalCredentials,
address _oracleAddress,
address _systemAdministratorAddress,
address _allowlistAddress,
address _operatorRegistryAddress,
address _collectorAddress,
uint256 _globalFee
) external init(0) {
_setAdmin(_systemAdministratorAddress);
CollectorAddress.set(_collectorAddress);
emit SetCollector(_collectorAddress);
GlobalFee.set(_globalFee);
emit SetGlobalFee(_globalFee);
ELFeeRecipientAddress.set(_elFeeRecipientAddress);
emit SetELFeeRecipient(_elFeeRecipientAddress);
AllowlistAddress.set(_allowlistAddress);
emit SetAllowlist(_allowlistAddress);
OperatorsRegistryAddress.set(_operatorRegistryAddress);
emit SetOperatorsRegistry(_operatorRegistryAddress);
ConsensusLayerDepositManagerV1.initConsensusLayerDepositManagerV1(
_depositContractAddress, _withdrawalCredentials
);
OracleManagerV1.initOracleManagerV1(_oracleAddress);
}
/// @inheritdoc IRiverV1
function initRiverV1_1(
address _redeemManager,
uint64 _epochsPerFrame,
uint64 _slotsPerEpoch,
uint64 _secondsPerSlot,
uint64 _genesisTime,
uint64 _epochsToAssumedFinality,
uint256 _annualAprUpperBound,
uint256 _relativeLowerBound,
uint128 _minDailyNetCommittableAmount_,
uint128 _maxDailyRelativeCommittableAmount_
) external init(1) {
RedeemManagerAddress.set(_redeemManager);
emit SetRedeemManager(_redeemManager);
_setDailyCommittableLimits(
DailyCommittableLimits.DailyCommittableLimitsStruct({
minDailyNetCommittableAmount: _minDailyNetCommittableAmount_,
maxDailyRelativeCommittableAmount: _maxDailyRelativeCommittableAmount_
})
);
initOracleManagerV1_1(
_epochsPerFrame,
_slotsPerEpoch,
_secondsPerSlot,
_genesisTime,
_epochsToAssumedFinality,
_annualAprUpperBound,
_relativeLowerBound
);
_approve(address(this), _redeemManager, type(uint256).max);
}
/// @inheritdoc IRiverV1
function initRiverV1_2() external init(2) {
// force committed balance to a multiple of 32 ETH and
// move extra funds back to the deposit buffer
uint256 dustToUncommit = CommittedBalance.get() % DEPOSIT_SIZE;
unchecked {
_setCommittedBalance(CommittedBalance.get() - dustToUncommit);
_setBalanceToDeposit(BalanceToDeposit.get() + dustToUncommit);
}
}
/// @inheritdoc IRiverV1
function getGlobalFee() external view returns (uint256) {
return GlobalFee.get();
}
/// @inheritdoc IRiverV1
function getAllowlist() external view returns (address) {
return AllowlistAddress.get();
}
/// @inheritdoc IRiverV1
function getCollector() external view returns (address) {
return CollectorAddress.get();
}
/// @inheritdoc IRiverV1
function getELFeeRecipient() external view returns (address) {
return ELFeeRecipientAddress.get();
}
/// @inheritdoc IRiverV1
function getCoverageFund() external view returns (address) {
return CoverageFundAddress.get();
}
/// @inheritdoc IRiverV1
function getRedeemManager() external view returns (address) {
return RedeemManagerAddress.get();
}
/// @inheritdoc IRiverV1
function getMetadataURI() external view returns (string memory) {
return MetadataURI.get();
}
/// @inheritdoc IRiverV1
function getDailyCommittableLimits()
external
view
returns (DailyCommittableLimits.DailyCommittableLimitsStruct memory)
{
return DailyCommittableLimits.get();
}
/// @inheritdoc IRiverV1
function setDailyCommittableLimits(DailyCommittableLimits.DailyCommittableLimitsStruct memory _dcl)
external
onlyAdmin
{
_setDailyCommittableLimits(_dcl);
}
function setKeeper(address _keeper) external onlyAdmin {
_setKeeper(_keeper);
}
/// @inheritdoc IRiverV1
function getBalanceToRedeem() external view returns (uint256) {
return BalanceToRedeem.get();
}
/// @inheritdoc IRiverV1
function resolveRedeemRequests(uint32[] calldata _redeemRequestIds)
external
view
returns (int64[] memory withdrawalEventIds)
{
return IRedeemManagerV1(RedeemManagerAddress.get()).resolveRedeemRequests(_redeemRequestIds);
}
/// @inheritdoc IRiverV1
function requestRedeem(uint256 _lsETHAmount, address _recipient) external returns (uint32 _redeemRequestId) {
IAllowlistV1(AllowlistAddress.get()).onlyAllowed(msg.sender, LibAllowlistMasks.REDEEM_MASK);
_transfer(msg.sender, address(this), _lsETHAmount);
return IRedeemManagerV1(RedeemManagerAddress.get()).requestRedeem(_lsETHAmount, _recipient);
}
/// @inheritdoc IRiverV1
function claimRedeemRequests(uint32[] calldata _redeemRequestIds, uint32[] calldata _withdrawalEventIds)
external
returns (uint8[] memory claimStatuses)
{
return IRedeemManagerV1(RedeemManagerAddress.get()).claimRedeemRequests(
_redeemRequestIds, _withdrawalEventIds, true, type(uint16).max
);
}
/// @inheritdoc IRiverV1
function setGlobalFee(uint256 _newFee) external onlyAdmin {
GlobalFee.set(_newFee);
emit SetGlobalFee(_newFee);
}
/// @inheritdoc IRiverV1
function setAllowlist(address _newAllowlist) external onlyAdmin {
AllowlistAddress.set(_newAllowlist);
emit SetAllowlist(_newAllowlist);
}
/// @inheritdoc IRiverV1
function setCollector(address _newCollector) external onlyAdmin {
CollectorAddress.set(_newCollector);
emit SetCollector(_newCollector);
}
/// @inheritdoc IRiverV1
function setELFeeRecipient(address _newELFeeRecipient) external onlyAdmin {
ELFeeRecipientAddress.set(_newELFeeRecipient);
emit SetELFeeRecipient(_newELFeeRecipient);
}
/// @inheritdoc IRiverV1
function setCoverageFund(address _newCoverageFund) external onlyAdmin {
CoverageFundAddress.set(_newCoverageFund);
emit SetCoverageFund(_newCoverageFund);
}
/// @inheritdoc IRiverV1
function setMetadataURI(string memory _metadataURI) external onlyAdmin {
LibSanitize._notEmptyString(_metadataURI);
MetadataURI.set(_metadataURI);
emit SetMetadataURI(_metadataURI);
}
/// @inheritdoc IRiverV1
function getOperatorsRegistry() external view returns (address) {
return OperatorsRegistryAddress.get();
}
/// @inheritdoc IRiverV1
function sendELFees() external payable {
if (msg.sender != ELFeeRecipientAddress.get()) {
revert LibErrors.Unauthorized(msg.sender);
}
}
/// @inheritdoc IRiverV1
function sendCLFunds() external payable {
if (msg.sender != WithdrawalCredentials.getAddress()) {
revert LibErrors.Unauthorized(msg.sender);
}
}
/// @inheritdoc IRiverV1
function sendCoverageFunds() external payable {
if (msg.sender != CoverageFundAddress.get()) {
revert LibErrors.Unauthorized(msg.sender);
}
}
/// @inheritdoc IRiverV1
function sendRedeemManagerExceedingFunds() external payable {
if (msg.sender != RedeemManagerAddress.get()) {
revert LibErrors.Unauthorized(msg.sender);
}
}
/// @notice Overridden handler to pass the system admin inside components
/// @return The address of the admin
function _getRiverAdmin()
internal
view
override(OracleManagerV1, ConsensusLayerDepositManagerV1)
returns (address)
{
return Administrable._getAdmin();
}
/// @notice Overridden handler called whenever a token transfer is triggered
/// @param _from Token sender
/// @param _to Token receiver
function _onTransfer(address _from, address _to) internal view override {
IAllowlistV1 allowlist = IAllowlistV1(AllowlistAddress.get());
if (allowlist.isDenied(_from)) {
revert Denied(_from);
}
if (allowlist.isDenied(_to)) {
revert Denied(_to);
}
}
/// @notice Overridden handler called whenever a user deposits ETH to the system. Mints the adequate amount of shares.
/// @param _depositor User address that made the deposit
/// @param _amount Amount of ETH deposited
function _onDeposit(address _depositor, address _recipient, uint256 _amount) internal override {
uint256 mintedShares = SharesManagerV1._mintShares(_depositor, _amount);
IAllowlistV1 allowlist = IAllowlistV1(AllowlistAddress.get());
allowlist.onlyAllowed(_depositor, LibAllowlistMasks.DEPOSIT_MASK); // this call reverts if unauthorized or denied
if (_depositor != _recipient) {
if (allowlist.isDenied(_recipient)) {
revert Denied(_recipient);
}
_transfer(_depositor, _recipient, mintedShares);
}
}
/// @notice Overridden handler called whenever a deposit to the consensus layer is made. Should retrieve _requestedAmount or lower keys
/// @param _requestedAmount Amount of keys required. Contract is expected to send _requestedAmount or lower.
/// @return publicKeys Array of fundable public keys
/// @return signatures Array of signatures linked to the public keys
function _getNextValidators(uint256 _requestedAmount)
internal
override
returns (bytes[] memory publicKeys, bytes[] memory signatures)
{
return IOperatorsRegistryV1(OperatorsRegistryAddress.get()).pickNextValidatorsToDeposit(_requestedAmount);
}
/// @notice Overridden handler to pull funds from the execution layer fee recipient to River and return the delta in the balance
/// @param _max The maximum amount to pull from the execution layer fee recipient
/// @return The amount pulled from the execution layer fee recipient
function _pullELFees(uint256 _max) internal override returns (uint256) {
address elFeeRecipient = ELFeeRecipientAddress.get();
uint256 initialBalance = address(this).balance;
IELFeeRecipientV1(payable(elFeeRecipient)).pullELFees(_max);
uint256 collectedELFees = address(this).balance - initialBalance;
if (collectedELFees > 0) {
_setBalanceToDeposit(BalanceToDeposit.get() + collectedELFees);
}
emit PulledELFees(collectedELFees);
return collectedELFees;
}
/// @notice Overridden handler to pull funds from the coverage fund to River and return the delta in the balance
/// @param _max The maximum amount to pull from the coverage fund
/// @return The amount pulled from the coverage fund
function _pullCoverageFunds(uint256 _max) internal override returns (uint256) {
address coverageFund = CoverageFundAddress.get();
if (coverageFund == address(0)) {
return 0;
}
uint256 initialBalance = address(this).balance;
ICoverageFundV1(payable(coverageFund)).pullCoverageFunds(_max);
uint256 collectedCoverageFunds = address(this).balance - initialBalance;
if (collectedCoverageFunds > 0) {
_setBalanceToDeposit(BalanceToDeposit.get() + collectedCoverageFunds);
}
emit PulledCoverageFunds(collectedCoverageFunds);
return collectedCoverageFunds;
}
/// @notice Overridden handler called whenever the balance of ETH handled by the system increases. Computes the fees paid to the collector
/// @param _amount Additional ETH received
function _onEarnings(uint256 _amount) internal override {
uint256 oldTotalSupply = _totalSupply();
if (oldTotalSupply == 0) {
revert ZeroMintedShares();
}
uint256 newTotalBalance = _assetBalance();
uint256 globalFee = GlobalFee.get();
uint256 numerator = _amount * oldTotalSupply * globalFee;
uint256 denominator = (newTotalBalance * LibBasisPoints.BASIS_POINTS_MAX) - (_amount * globalFee);
uint256 sharesToMint = denominator == 0 ? 0 : (numerator / denominator);
if (sharesToMint > 0) {
address collector = CollectorAddress.get();
_mintRawShares(collector, sharesToMint);
uint256 newTotalSupply = _totalSupply();
uint256 oldTotalBalance = newTotalBalance - _amount;
emit RewardsEarned(collector, oldTotalBalance, oldTotalSupply, newTotalBalance, newTotalSupply);
}
}
/// @notice Overridden handler called whenever the total balance of ETH is requested
/// @return The current total asset balance managed by River
function _assetBalance() internal view override(SharesManagerV1, OracleManagerV1) returns (uint256) {
IOracleManagerV1.StoredConsensusLayerReport storage storedReport = LastConsensusLayerReport.get();
uint256 clValidatorCount = storedReport.validatorsCount;
uint256 depositedValidatorCount = DepositedValidatorCount.get();
if (clValidatorCount < depositedValidatorCount) {
return storedReport.validatorsBalance + BalanceToDeposit.get() + CommittedBalance.get()
+ BalanceToRedeem.get()
+ (depositedValidatorCount - clValidatorCount) * ConsensusLayerDepositManagerV1.DEPOSIT_SIZE;
} else {
return
storedReport.validatorsBalance + BalanceToDeposit.get() + CommittedBalance.get() + BalanceToRedeem.get();
}
}
/// @notice Internal utility to set the daily committable limits
/// @param _dcl The new daily committable limits
function _setDailyCommittableLimits(DailyCommittableLimits.DailyCommittableLimitsStruct memory _dcl) internal {
DailyCommittableLimits.set(_dcl);
emit SetMaxDailyCommittableAmounts(_dcl.minDailyNetCommittableAmount, _dcl.maxDailyRelativeCommittableAmount);
}
/// @notice Sets the balance to deposit, but not yet committed
/// @param _newBalanceToDeposit The new balance to deposit value
function _setBalanceToDeposit(uint256 _newBalanceToDeposit) internal override(UserDepositManagerV1) {
emit SetBalanceToDeposit(BalanceToDeposit.get(), _newBalanceToDeposit);
BalanceToDeposit.set(_newBalanceToDeposit);
}
/// @notice Sets the balance to redeem, to be used to satisfy redeem requests on the redeem manager
/// @param _newBalanceToRedeem The new balance to redeem value
function _setBalanceToRedeem(uint256 _newBalanceToRedeem) internal {
emit SetBalanceToRedeem(BalanceToRedeem.get(), _newBalanceToRedeem);
BalanceToRedeem.set(_newBalanceToRedeem);
}
/// @notice Sets the committed balance, ready to be deposited to the consensus layer
/// @param _newCommittedBalance The new committed balance value
function _setCommittedBalance(uint256 _newCommittedBalance) internal override(ConsensusLayerDepositManagerV1) {
emit SetBalanceCommittedToDeposit(CommittedBalance.get(), _newCommittedBalance);
CommittedBalance.set(_newCommittedBalance);
}
/// @notice Pulls funds from the Withdraw contract, and adds funds to deposit and redeem balances
/// @param _skimmedEthAmount The new amount of skimmed eth to pull
/// @param _exitedEthAmount The new amount of exited eth to pull
function _pullCLFunds(uint256 _skimmedEthAmount, uint256 _exitedEthAmount) internal override {
uint256 currentBalance = address(this).balance;
uint256 totalAmountToPull = _skimmedEthAmount + _exitedEthAmount;
IWithdrawV1(WithdrawalCredentials.getAddress()).pullEth(totalAmountToPull);
uint256 collectedCLFunds = address(this).balance - currentBalance;
if (collectedCLFunds != _skimmedEthAmount + _exitedEthAmount) {
revert InvalidPulledClFundsAmount(_skimmedEthAmount + _exitedEthAmount, collectedCLFunds);
}
if (_skimmedEthAmount > 0) {
_setBalanceToDeposit(BalanceToDeposit.get() + _skimmedEthAmount);
}
if (_exitedEthAmount > 0) {
_setBalanceToRedeem(BalanceToRedeem.get() + _exitedEthAmount);
}
emit PulledCLFunds(_skimmedEthAmount, _exitedEthAmount);
}
/// @notice Pulls funds from the redeem manager exceeding eth buffer
/// @param _max The maximum amount to pull
function _pullRedeemManagerExceedingEth(uint256 _max) internal override returns (uint256) {
uint256 currentBalance = address(this).balance;
IRedeemManagerV1(RedeemManagerAddress.get()).pullExceedingEth(_max);
uint256 collectedExceedingEth = address(this).balance - currentBalance;
if (collectedExceedingEth > 0) {
_setBalanceToDeposit(BalanceToDeposit.get() + collectedExceedingEth);
}
emit PulledRedeemManagerExceedingEth(collectedExceedingEth);
return collectedExceedingEth;
}
/// @notice Use the balance to redeem to report a withdrawal event on the redeem manager
function _reportWithdrawToRedeemManager() internal override {
IRedeemManagerV1 redeemManager_ = IRedeemManagerV1(RedeemManagerAddress.get());
uint256 underlyingAssetBalance = _assetBalance();
uint256 totalSupply = _totalSupply();
if (underlyingAssetBalance > 0 && totalSupply > 0) {
// we compute the redeem manager demands in eth and lsEth based on current conversion rate
uint256 redeemManagerDemand = redeemManager_.getRedeemDemand();
uint256 suppliedRedeemManagerDemand = redeemManagerDemand;
uint256 suppliedRedeemManagerDemandInEth = _balanceFromShares(suppliedRedeemManagerDemand);
uint256 availableBalanceToRedeem = BalanceToRedeem.get();
// if demand is higher than available eth, we update demand values to use the available eth
if (suppliedRedeemManagerDemandInEth > availableBalanceToRedeem) {
suppliedRedeemManagerDemandInEth = availableBalanceToRedeem;
suppliedRedeemManagerDemand = _sharesFromBalance(suppliedRedeemManagerDemandInEth);
}
emit ReportedRedeemManager(
redeemManagerDemand, suppliedRedeemManagerDemand, suppliedRedeemManagerDemandInEth
);
if (suppliedRedeemManagerDemandInEth > 0) {
// the available balance to redeem is updated
unchecked {
_setBalanceToRedeem(availableBalanceToRedeem - suppliedRedeemManagerDemandInEth);
}
// we burn the shares of the redeem manager associated with the amount of eth provided
_burnRawShares(address(redeemManager_), suppliedRedeemManagerDemand);
// perform a report withdraw call to the redeem manager
redeemManager_.reportWithdraw{value: suppliedRedeemManagerDemandInEth}(suppliedRedeemManagerDemand);
}
}
}
/// @notice Requests exits of validators after possibly rebalancing deposit and redeem balances
/// @param _exitingBalance The currently exiting funds, soon to be received on the execution layer
/// @param _depositToRedeemRebalancingAllowed True if rebalancing from deposit to redeem is allowed
function _requestExitsBasedOnRedeemDemandAfterRebalancings(
uint256 _exitingBalance,
uint32[] memory _stoppedValidatorCounts,
bool _depositToRedeemRebalancingAllowed,
bool _slashingContainmentModeEnabled
) internal override {
IOperatorsRegistryV1(OperatorsRegistryAddress.get()).reportStoppedValidatorCounts(
_stoppedValidatorCounts, DepositedValidatorCount.get()
);
if (_slashingContainmentModeEnabled) {
return;
}
uint256 totalSupply = _totalSupply();
if (totalSupply > 0) {
uint256 availableBalanceToRedeem = BalanceToRedeem.get();
uint256 availableBalanceToDeposit = BalanceToDeposit.get();
uint256 redeemManagerDemandInEth =
_balanceFromShares(IRedeemManagerV1(RedeemManagerAddress.get()).getRedeemDemand());
// if after all rebalancings, the redeem manager demand is still higher than the balance to redeem and exiting eth, we compute
// the amount of validators to exit in order to cover the remaining demand
if (availableBalanceToRedeem + _exitingBalance < redeemManagerDemandInEth) {
// if reblancing is enabled and the redeem manager demand is higher than exiting eth, we add eth for deposit buffer to redeem buffer
if (_depositToRedeemRebalancingAllowed && availableBalanceToDeposit > 0) {
uint256 rebalancingAmount = LibUint256.min(
availableBalanceToDeposit, redeemManagerDemandInEth - _exitingBalance - availableBalanceToRedeem
);
if (rebalancingAmount > 0) {
availableBalanceToRedeem += rebalancingAmount;
_setBalanceToRedeem(availableBalanceToRedeem);
_setBalanceToDeposit(availableBalanceToDeposit - rebalancingAmount);
}
}
IOperatorsRegistryV1 or = IOperatorsRegistryV1(OperatorsRegistryAddress.get());
(uint256 totalStoppedValidatorCount, uint256 totalRequestedExitsCount) =
or.getStoppedAndRequestedExitCounts();
// what we are calling pre-exiting balance is the amount of eth that should soon enter the exiting balance
// because exit requests have been made and operators might have a lag to process them
// we take them into account to not exit too many validators
uint256 preExitingBalance = (
totalRequestedExitsCount > totalStoppedValidatorCount
? (totalRequestedExitsCount - totalStoppedValidatorCount)
: 0
) * DEPOSIT_SIZE;
if (availableBalanceToRedeem + _exitingBalance + preExitingBalance < redeemManagerDemandInEth) {
uint256 validatorCountToExit = LibUint256.ceil(
redeemManagerDemandInEth - (availableBalanceToRedeem + _exitingBalance + preExitingBalance),
DEPOSIT_SIZE
);
or.demandValidatorExits(validatorCountToExit, DepositedValidatorCount.get());
}
}
}
}
/// @notice Skims the redeem balance and sends remaining funds to the deposit balance
function _skimExcessBalanceToRedeem() internal override {
uint256 availableBalanceToRedeem = BalanceToRedeem.get();
// if the available balance to redeem is not 0, it means that all the redeem requests are fulfilled, we should redirect funds for deposits
if (availableBalanceToRedeem > 0) {
_setBalanceToDeposit(BalanceToDeposit.get() + availableBalanceToRedeem);
_setBalanceToRedeem(0);
}
}
/// @notice Commits the deposit balance up to the allowed daily limit in batches of 32 ETH.
/// @notice Committed funds are funds waiting to be deposited but that cannot be used to fund the redeem manager anymore
/// @notice This two step process is required to prevent possible out of gas issues we would have from actually funding the validators at this point
/// @param _period The period between current and last report
function _commitBalanceToDeposit(uint256 _period) internal override {
uint256 underlyingAssetBalance = _assetBalance();
uint256 currentBalanceToDeposit = BalanceToDeposit.get();
DailyCommittableLimits.DailyCommittableLimitsStruct memory dcl = DailyCommittableLimits.get();
// we compute the max daily committable amount by taking the asset balance without the balance to deposit into account
// this value is the daily maximum amount we can commit for deposits
// we take the maximum value between a net amount and an amount relative to the asset balance
// this ensures that the amount we can commit is not too low in the beginning and that it is not too high when volumes grow
// the relative amount is computed from the committed and activated funds (on the CL or committed to be on the CL soon) and not
// the deposit balance
// this value is computed by subtracting the current balance to deposit from the underlying asset balance
uint256 currentMaxDailyCommittableAmount = LibUint256.max(
dcl.minDailyNetCommittableAmount,
(uint256(dcl.maxDailyRelativeCommittableAmount) * (underlyingAssetBalance - currentBalanceToDeposit))
/ LibBasisPoints.BASIS_POINTS_MAX
);
// we adapt the value for the reporting period by using the asset balance as upper bound
uint256 currentMaxCommittableAmount =
LibUint256.min((currentMaxDailyCommittableAmount * _period) / 1 days, currentBalanceToDeposit);
// we only commit multiples of 32 ETH
currentMaxCommittableAmount = (currentMaxCommittableAmount / DEPOSIT_SIZE) * DEPOSIT_SIZE;
if (currentMaxCommittableAmount > 0) {
_setCommittedBalance(CommittedBalance.get() + currentMaxCommittableAmount);
_setBalanceToDeposit(currentBalanceToDeposit - currentMaxCommittableAmount);
}
}
function version() external pure returns (string memory) {
return "1.2.1";
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "./interfaces/IAdministrable.sol";
import "./libraries/LibAdministrable.sol";
import "./libraries/LibSanitize.sol";
/// @title Administrable
/// @author Alluvial Finance Inc.
/// @notice This contract handles the administration of the contracts
abstract contract Administrable is IAdministrable {
/// @notice Prevents unauthorized calls
modifier onlyAdmin() {
if (msg.sender != LibAdministrable._getAdmin()) {
revert LibErrors.Unauthorized(msg.sender);
}
_;
}
/// @notice Prevents unauthorized calls
modifier onlyPendingAdmin() {
if (msg.sender != LibAdministrable._getPendingAdmin()) {
revert LibErrors.Unauthorized(msg.sender);
}
_;
}
/// @inheritdoc IAdministrable
function getAdmin() external view returns (address) {
return LibAdministrable._getAdmin();
}
/// @inheritdoc IAdministrable
function getPendingAdmin() external view returns (address) {
return LibAdministrable._getPendingAdmin();
}
/// @inheritdoc IAdministrable
function proposeAdmin(address _newAdmin) external onlyAdmin {
_setPendingAdmin(_newAdmin);
}
/// @inheritdoc IAdministrable
function acceptAdmin() external onlyPendingAdmin {
_setAdmin(LibAdministrable._getPendingAdmin());
_setPendingAdmin(address(0));
}
/// @notice Internal utility to set the admin address
/// @param _admin Address to set as admin
function _setAdmin(address _admin) internal {
LibAdministrable._setAdmin(_admin);
emit SetAdmin(_admin);
}
/// @notice Internal utility to set the pending admin address
/// @param _pendingAdmin Address to set as pending admin
function _setPendingAdmin(address _pendingAdmin) internal {
LibAdministrable._setPendingAdmin(_pendingAdmin);
emit SetPendingAdmin(_pendingAdmin);
}
/// @notice Internal utility to retrieve the address of the current admin
/// @return The address of admin
function _getAdmin() internal view returns (address) {
return LibAdministrable._getAdmin();
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../interfaces/components/IConsensusLayerDepositManager.1.sol";
import "../interfaces/IDepositContract.sol";
import "../libraries/LibBytes.sol";
import "../libraries/LibUint256.sol";
import "../state/river/DepositContractAddress.sol";
import "../state/river/WithdrawalCredentials.sol";
import "../state/river/DepositedValidatorCount.sol";
import "../state/river/BalanceToDeposit.sol";
import "../state/river/CommittedBalance.sol";
import "../state/river/KeeperAddress.sol";
/// @title Consensus Layer Deposit Manager (v1)
/// @author Alluvial Finance Inc.
/// @notice This contract handles the interactions with the official deposit contract, funding all validators
/// @notice Whenever a deposit to the consensus layer is requested, this contract computed the amount of keys
/// @notice that could be deposited depending on the amount available in the contract. It then tries to retrieve
/// @notice validator keys by calling its internal virtual method _getNextValidators. This method should be
/// @notice overridden by the implementing contract to provide [0; _keyCount] keys when invoked.
abstract contract ConsensusLayerDepositManagerV1 is IConsensusLayerDepositManagerV1 {
/// @notice Size of a BLS Public key in bytes
uint256 public constant PUBLIC_KEY_LENGTH = 48;
/// @notice Size of a BLS Signature in bytes
uint256 public constant SIGNATURE_LENGTH = 96;
/// @notice Size of a deposit in ETH
uint256 public constant DEPOSIT_SIZE = 32 ether;
/// @notice Handler called to retrieve the internal River admin address
/// @dev Must be Overridden
function _getRiverAdmin() internal view virtual returns (address);
/// @notice Handler called to change the committed balance to deposit
/// @param newCommittedBalance The new committed balance value
function _setCommittedBalance(uint256 newCommittedBalance) internal virtual;
/// @notice Internal helper to retrieve validator keys ready to be funded
/// @dev Must be overridden
/// @param _keyCount The amount of keys (or less) to return.
function _getNextValidators(uint256 _keyCount)
internal
virtual
returns (bytes[] memory publicKeys, bytes[] memory signatures);
/// @notice Initializer to set the deposit contract address and the withdrawal credentials to use
/// @param _depositContractAddress The address of the deposit contract
/// @param _withdrawalCredentials The withdrawal credentials to apply to all deposits
function initConsensusLayerDepositManagerV1(address _depositContractAddress, bytes32 _withdrawalCredentials)
internal
{
DepositContractAddress.set(_depositContractAddress);
emit SetDepositContractAddress(_depositContractAddress);
WithdrawalCredentials.set(_withdrawalCredentials);
emit SetWithdrawalCredentials(_withdrawalCredentials);
}
function _setKeeper(address _keeper) internal {
KeeperAddress.set(_keeper);
}
/// @inheritdoc IConsensusLayerDepositManagerV1
function getCommittedBalance() external view returns (uint256) {
return CommittedBalance.get();
}
/// @inheritdoc IConsensusLayerDepositManagerV1
function getBalanceToDeposit() external view returns (uint256) {
return BalanceToDeposit.get();
}
/// @inheritdoc IConsensusLayerDepositManagerV1
function getWithdrawalCredentials() external view returns (bytes32) {
return WithdrawalCredentials.get();
}
/// @inheritdoc IConsensusLayerDepositManagerV1
function getDepositedValidatorCount() external view returns (uint256) {
return DepositedValidatorCount.get();
}
/// @inheritdoc IConsensusLayerDepositManagerV1
function getKeeper() external view returns (address) {
return KeeperAddress.get();
}
/// @inheritdoc IConsensusLayerDepositManagerV1
function depositToConsensusLayerWithDepositRoot(uint256 _maxCount, bytes32 _depositRoot) external {
if (msg.sender != KeeperAddress.get()) {
revert OnlyKeeper();
}
if (IDepositContract(DepositContractAddress.get()).get_deposit_root() != _depositRoot) {
revert InvalidDepositRoot();
}
uint256 committedBalance = CommittedBalance.get();
uint256 keyToDepositCount = LibUint256.min(committedBalance / DEPOSIT_SIZE, _maxCount);
if (keyToDepositCount == 0) {
revert NotEnoughFunds();
}
// it's up to the internal overriden _getNextValidators method to provide two array of the same
// size for the publicKeys and the signatures
(bytes[] memory publicKeys, bytes[] memory signatures) = _getNextValidators(keyToDepositCount);
uint256 receivedPublicKeyCount = publicKeys.length;
if (receivedPublicKeyCount == 0) {
revert NoAvailableValidatorKeys();
}
if (receivedPublicKeyCount > keyToDepositCount) {
revert InvalidPublicKeyCount();
}
bytes32 withdrawalCredentials = WithdrawalCredentials.get();
if (withdrawalCredentials == 0) {
revert InvalidWithdrawalCredentials();
}
for (uint256 idx = 0; idx < receivedPublicKeyCount;) {
_depositValidator(publicKeys[idx], signatures[idx], withdrawalCredentials);
unchecked {
++idx;
}
}
_setCommittedBalance(committedBalance - DEPOSIT_SIZE * receivedPublicKeyCount);
uint256 currentDepositedValidatorCount = DepositedValidatorCount.get();
DepositedValidatorCount.set(currentDepositedValidatorCount + receivedPublicKeyCount);
emit SetDepositedValidatorCount(
currentDepositedValidatorCount, currentDepositedValidatorCount + receivedPublicKeyCount
);
}
/// @notice Deposits 32 ETH to the official Deposit contract
/// @param _publicKey The public key of the validator
/// @param _signature The signature provided by the operator
/// @param _withdrawalCredentials The withdrawal credentials provided by River
function _depositValidator(bytes memory _publicKey, bytes memory _signature, bytes32 _withdrawalCredentials)
internal
{
if (_publicKey.length != PUBLIC_KEY_LENGTH) {
revert InconsistentPublicKeys();
}
if (_signature.length != SIGNATURE_LENGTH) {
revert InconsistentSignatures();
}
uint256 value = DEPOSIT_SIZE;
uint256 depositAmount = value / 1 gwei;
bytes32 pubkeyRoot = sha256(bytes.concat(_publicKey, bytes16(0)));
bytes32 signatureRoot = sha256(
bytes.concat(
sha256(LibBytes.slice(_signature, 0, 64)),
sha256(bytes.concat(LibBytes.slice(_signature, 64, SIGNATURE_LENGTH - 64), bytes32(0)))
)
);
bytes32 depositDataRoot = sha256(
bytes.concat(
sha256(bytes.concat(pubkeyRoot, _withdrawalCredentials)),
sha256(bytes.concat(bytes32(LibUint256.toLittleEndian64(depositAmount)), signatureRoot))
)
);
uint256 targetBalance = address(this).balance - value;
IDepositContract(DepositContractAddress.get()).deposit{value: value}(
_publicKey, abi.encodePacked(_withdrawalCredentials), _signature, depositDataRoot
);
if (address(this).balance != targetBalance) {
revert ErrorOnDeposit();
}
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../interfaces/components/IOracleManager.1.sol";
import "../interfaces/IRedeemManager.1.sol";
import "../libraries/LibUint256.sol";
import "../state/river/LastConsensusLayerReport.sol";
import "../state/river/OracleAddress.sol";
import "../state/river/CLValidatorTotalBalance.sol";
import "../state/river/CLValidatorCount.sol";
import "../state/river/DepositedValidatorCount.sol";
import "../state/river/LastOracleRoundId.sol";
/// @title Oracle Manager (v1)
/// @author Alluvial Finance Inc.
/// @notice This contract handles the inputs provided by the oracle
/// @notice The Oracle contract is plugged to this contract and is in charge of pushing
/// @notice data whenever a new report has been deemed valid. The report consists in two
/// @notice values: the sum of all balances of all deposited validators and the count of
/// @notice validators that have been activated on the consensus layer.
abstract contract OracleManagerV1 is IOracleManagerV1 {
uint256 internal constant ONE_YEAR = 365 days;
/// @notice Size of a deposit in ETH
uint256 public constant _DEPOSIT_SIZE = 32 ether;
/// @notice Handler called if the delta between the last and new validator balance sum is positive
/// @dev Must be overridden
/// @param _profits The positive increase in the validator balance sum (staking rewards)
function _onEarnings(uint256 _profits) internal virtual;
/// @notice Handler called to pull the Execution layer fees from the recipient
/// @dev Must be overridden
/// @param _max The maximum amount to pull inside the system
/// @return The amount pulled inside the system
function _pullELFees(uint256 _max) internal virtual returns (uint256);
/// @notice Handler called to pull the coverage funds
/// @dev Must be overridden
/// @param _max The maximum amount to pull inside the system
/// @return The amount pulled inside the system
function _pullCoverageFunds(uint256 _max) internal virtual returns (uint256);
/// @notice Handler called to retrieve the system administrator address
/// @dev Must be overridden
/// @return The system administrator address
function _getRiverAdmin() internal view virtual returns (address);
/// @notice Overridden handler called whenever the total balance of ETH is requested
/// @return The current total asset balance managed by River
function _assetBalance() internal view virtual returns (uint256);
/// @notice Pulls funds from the Withdraw contract, and adds funds to deposit and redeem balances
/// @param _skimmedEthAmount The new amount of skimmed eth to pull
/// @param _exitedEthAmount The new amount of exited eth to pull
function _pullCLFunds(uint256 _skimmedEthAmount, uint256 _exitedEthAmount) internal virtual;
/// @notice Pulls funds from the redeem manager exceeding eth buffer
/// @param _max The maximum amount to pull
/// @return The amount pulled
function _pullRedeemManagerExceedingEth(uint256 _max) internal virtual returns (uint256);
/// @notice Use the balance to redeem to report a withdrawal event on the redeem manager
function _reportWithdrawToRedeemManager() internal virtual;
/// @notice Requests exits of validators after possibly rebalancing deposit and redeem balances
/// @param _exitingBalance The currently exiting funds, soon to be received on the execution layer
/// @param _depositToRedeemRebalancingAllowed True if rebalancing from deposit to redeem is allowed
function _requestExitsBasedOnRedeemDemandAfterRebalancings(
uint256 _exitingBalance,
uint32[] memory _stoppedValidatorCounts,
bool _depositToRedeemRebalancingAllowed,
bool _slashingContainmentModeEnabled
) internal virtual;
/// @notice Skims the redeem balance and sends remaining funds to the deposit balance
function _skimExcessBalanceToRedeem() internal virtual;
/// @notice Commits the deposit balance up to the allowed daily limit
/// @param _period The period between current and last report
function _commitBalanceToDeposit(uint256 _period) internal virtual;
/// @notice Prevents unauthorized calls
modifier onlyAdmin_OMV1() {
if (msg.sender != _getRiverAdmin()) {
revert LibErrors.Unauthorized(msg.sender);
}
_;
}
/// @notice Set the initial oracle address
/// @param _oracle Address of the oracle
function initOracleManagerV1(address _oracle) internal {
OracleAddress.set(_oracle);
emit SetOracle(_oracle);
}
/// @notice Initializes version 1.1 of the oracle manager
/// @param _epochsPerFrame The amounts of epochs in a frame
/// @param _slotsPerEpoch The slots inside an epoch
/// @param _secondsPerSlot The seconds inside a slot
/// @param _genesisTime The genesis timestamp
/// @param _epochsToAssumedFinality The number of epochs before an epoch is considered final on-chain
/// @param _annualAprUpperBound The reporting upper bound
/// @param _relativeLowerBound The reporting lower bound
function initOracleManagerV1_1(
uint64 _epochsPerFrame,
uint64 _slotsPerEpoch,
uint64 _secondsPerSlot,
uint64 _genesisTime,
uint64 _epochsToAssumedFinality,
uint256 _annualAprUpperBound,
uint256 _relativeLowerBound
) internal {
CLSpec.set(
CLSpec.CLSpecStruct({
epochsPerFrame: _epochsPerFrame,
slotsPerEpoch: _slotsPerEpoch,
secondsPerSlot: _secondsPerSlot,
genesisTime: _genesisTime,
epochsToAssumedFinality: _epochsToAssumedFinality
})
);
emit SetSpec(_epochsPerFrame, _slotsPerEpoch, _secondsPerSlot, _genesisTime, _epochsToAssumedFinality);
ReportBounds.set(
ReportBounds.ReportBoundsStruct({
annualAprUpperBound: _annualAprUpperBound,
relativeLowerBound: _relativeLowerBound
})
);
emit SetBounds(_annualAprUpperBound, _relativeLowerBound);
IOracleManagerV1.StoredConsensusLayerReport memory storedReport;
storedReport.epoch = uint256(LastOracleRoundId.get());
storedReport.validatorsBalance = CLValidatorTotalBalance.get();
storedReport.validatorsSkimmedBalance = 0;
storedReport.validatorsExitedBalance = 0;
storedReport.validatorsExitingBalance = 0;
storedReport.validatorsCount = uint32(CLValidatorCount.get());
storedReport.rebalanceDepositToRedeemMode = false;
storedReport.slashingContainmentMode = false;
LastConsensusLayerReport.set(storedReport);
}
/// @inheritdoc IOracleManagerV1
function getOracle() external view returns (address) {
return OracleAddress.get();
}
/// @inheritdoc IOracleManagerV1
function getCLValidatorTotalBalance() external view returns (uint256) {
return LastConsensusLayerReport.get().validatorsBalance;
}
/// @inheritdoc IOracleManagerV1
function getCLValidatorCount() external view returns (uint256) {
return LastConsensusLayerReport.get().validatorsCount;
}
/// @inheritdoc IOracleManagerV1
function getExpectedEpochId() external view returns (uint256) {
CLSpec.CLSpecStruct memory cls = CLSpec.get();
uint256 currentEpoch = _currentEpoch(cls);
return LibUint256.max(
LastConsensusLayerReport.get().epoch + cls.epochsPerFrame,
currentEpoch - (currentEpoch % cls.epochsPerFrame)
);
}
/// @inheritdoc IOracleManagerV1
function isValidEpoch(uint256 _epoch) external view returns (bool) {
return _isValidEpoch(CLSpec.get(), _epoch);
}
/// @inheritdoc IOracleManagerV1
function getTime() external view returns (uint256) {
return block.timestamp;
}
/// @inheritdoc IOracleManagerV1
function getLastCompletedEpochId() external view returns (uint256) {
return LastConsensusLayerReport.get().epoch;
}
/// @inheritdoc IOracleManagerV1
function getCurrentEpochId() external view returns (uint256) {
return _currentEpoch(CLSpec.get());
}
/// @inheritdoc IOracleManagerV1
function getCLSpec() external view returns (CLSpec.CLSpecStruct memory) {
return CLSpec.get();
}
/// @inheritdoc IOracleManagerV1
function getCurrentFrame() external view returns (uint256 _startEpochId, uint256 _startTime, uint256 _endTime) {
CLSpec.CLSpecStruct memory cls = CLSpec.get();
uint256 currentEpoch = _currentEpoch(cls);
_startEpochId = currentEpoch - (currentEpoch % cls.epochsPerFrame);
_startTime = _startEpochId * cls.slotsPerEpoch * cls.secondsPerSlot;
_endTime = (_startEpochId + cls.epochsPerFrame) * cls.slotsPerEpoch * cls.secondsPerSlot - 1;
}
/// @inheritdoc IOracleManagerV1
function getFrameFirstEpochId(uint256 _epochId) external view returns (uint256) {
return _epochId - (_epochId % CLSpec.get().epochsPerFrame);
}
/// @inheritdoc IOracleManagerV1
function getReportBounds() external view returns (ReportBounds.ReportBoundsStruct memory) {
return ReportBounds.get();
}
/// @inheritdoc IOracleManagerV1
function getLastConsensusLayerReport() external view returns (IOracleManagerV1.StoredConsensusLayerReport memory) {
return LastConsensusLayerReport.get();
}
/// @inheritdoc IOracleManagerV1
function setOracle(address _oracleAddress) external onlyAdmin_OMV1 {
OracleAddress.set(_oracleAddress);
emit SetOracle(_oracleAddress);
}
/// @inheritdoc IOracleManagerV1
function setCLSpec(CLSpec.CLSpecStruct calldata _newValue) external onlyAdmin_OMV1 {
CLSpec.set(_newValue);
emit SetSpec(
_newValue.epochsPerFrame,
_newValue.slotsPerEpoch,
_newValue.secondsPerSlot,
_newValue.genesisTime,
_newValue.epochsToAssumedFinality
);
}
/// @inheritdoc IOracleManagerV1
function setReportBounds(ReportBounds.ReportBoundsStruct calldata _newValue) external onlyAdmin_OMV1 {
ReportBounds.set(_newValue);
emit SetBounds(_newValue.annualAprUpperBound, _newValue.relativeLowerBound);
}
/// @notice Structure holding internal variables used during reporting
struct ConsensusLayerDataReportingVariables {
uint256 preReportUnderlyingBalance;
uint256 postReportUnderlyingBalance;
uint256 lastReportExitedBalance;
uint256 lastReportSkimmedBalance;
uint256 exitedAmountIncrease;
uint256 skimmedAmountIncrease;
uint256 timeElapsedSinceLastReport;
uint256 availableAmountToUpperBound;
uint256 redeemManagerDemand;
ConsensusLayerDataReportingTrace trace;
}
/// @inheritdoc IOracleManagerV1
function setConsensusLayerData(IOracleManagerV1.ConsensusLayerReport calldata _report) external {
// only the oracle is allowed to call this endpoint
if (msg.sender != OracleAddress.get()) {
revert LibErrors.Unauthorized(msg.sender);
}
CLSpec.CLSpecStruct memory cls = CLSpec.get();
// we start by verifying that the reported epoch is valid based on the consensus layer spec
if (!_isValidEpoch(cls, _report.epoch)) {
revert InvalidEpoch(_report.epoch);
}
ConsensusLayerDataReportingVariables memory vars;
{
IOracleManagerV1.StoredConsensusLayerReport storage lastStoredReport = LastConsensusLayerReport.get();
vars.lastReportExitedBalance = lastStoredReport.validatorsExitedBalance;
// we ensure that the reported total exited balance is not decreasing
if (_report.validatorsExitedBalance < vars.lastReportExitedBalance) {
revert InvalidDecreasingValidatorsExitedBalance(
vars.lastReportExitedBalance, _report.validatorsExitedBalance
);
}
// we compute the exited amount increase by taking the delta between reports
vars.exitedAmountIncrease = _report.validatorsExitedBalance - vars.lastReportExitedBalance;
vars.lastReportSkimmedBalance = lastStoredReport.validatorsSkimmedBalance;
// we ensure that the reported total skimmed balance is not decreasing
if (_report.validatorsSkimmedBalance < vars.lastReportSkimmedBalance) {
revert InvalidDecreasingValidatorsSkimmedBalance(
vars.lastReportSkimmedBalance, _report.validatorsSkimmedBalance
);
}
// we ensure that the reported validator count is not decreasing
if (
_report.validatorsCount > DepositedValidatorCount.get()
|| _report.validatorsCount < lastStoredReport.validatorsCount
) {
revert InvalidValidatorCountReport(
_report.validatorsCount, DepositedValidatorCount.get(), lastStoredReport.validatorsCount
);
}
// we compute the new skimmed amount by taking the delta between reports
vars.skimmedAmountIncrease = _report.validatorsSkimmedBalance - vars.lastReportSkimmedBalance;
vars.timeElapsedSinceLastReport = _timeBetweenEpochs(cls, lastStoredReport.epoch, _report.epoch);
}
// we retrieve the current total underlying balance before any reporting data is applied to the system
vars.preReportUnderlyingBalance = _assetBalance();
// if we have new exited / skimmed eth available, we pull funds from the consensus layer recipient
if (vars.exitedAmountIncrease + vars.skimmedAmountIncrease > 0) {
// this method pulls and updates ethToDeposit / ethToRedeem accordingly
_pullCLFunds(vars.skimmedAmountIncrease, vars.exitedAmountIncrease);
}
{
// we update the system parameters, this will have an impact on how the total underlying balance is computed
IOracleManagerV1.StoredConsensusLayerReport memory storedReport;
storedReport.epoch = _report.epoch;
storedReport.validatorsBalance = _report.validatorsBalance;
storedReport.validatorsSkimmedBalance = _report.validatorsSkimmedBalance;
storedReport.validatorsExitedBalance = _report.validatorsExitedBalance;
storedReport.validatorsExitingBalance = _report.validatorsExitingBalance;
storedReport.validatorsCount = _report.validatorsCount;
storedReport.rebalanceDepositToRedeemMode = _report.rebalanceDepositToRedeemMode;
storedReport.slashingContainmentMode = _report.slashingContainmentMode;
LastConsensusLayerReport.set(storedReport);
}
ReportBounds.ReportBoundsStruct memory rb = ReportBounds.get();
// we compute the maximum allowed increase in balance based on the pre report value
uint256 maxIncrease = _maxIncrease(rb, vars.preReportUnderlyingBalance, vars.timeElapsedSinceLastReport);
// we retrieve the new total underlying balance after system parameters are changed
vars.postReportUnderlyingBalance = _assetBalance();
// we can now compute the earned rewards from the consensus layer balances
// in order to properly account for the balance increase, we compare the sums of current balances, skimmed balance and exited balances
// we also synthetically increase the current balance by 32 eth per new activated validator, this way we have no discrepency due
// to currently activating funds that were not yet accounted in the consensus layer balances
if (vars.postReportUnderlyingBalance >= vars.preReportUnderlyingBalance) {
// if this happens, we revert and the reporting process is cancelled
if (vars.postReportUnderlyingBalance > vars.preReportUnderlyingBalance + maxIncrease) {
revert TotalValidatorBalanceIncreaseOutOfBound(
vars.preReportUnderlyingBalance,
vars.postReportUnderlyingBalance,
vars.timeElapsedSinceLastReport,
rb.annualAprUpperBound
);
}
// we update the rewards based on the balance delta
vars.trace.rewards = vars.postReportUnderlyingBalance - vars.preReportUnderlyingBalance;
// we update the available amount to upper bound (the amount of eth we can still pull and stay below the upper reporting bound)
vars.availableAmountToUpperBound = maxIncrease - vars.trace.rewards;
} else {
// otherwise if the balance has decreased, we verify that we are not exceeding the lower reporting bound
// we compute the maximum allowed decrease in balance
uint256 maxDecrease = _maxDecrease(rb, vars.preReportUnderlyingBalance);
// we verify that the bound is not crossed
if (
vars.postReportUnderlyingBalance
< vars.preReportUnderlyingBalance - LibUint256.min(maxDecrease, vars.preReportUnderlyingBalance)
) {
revert TotalValidatorBalanceDecreaseOutOfBound(
vars.preReportUnderlyingBalance,
vars.postReportUnderlyingBalance,
vars.timeElapsedSinceLastReport,
rb.relativeLowerBound
);
}
// we update the available amount to upper bound to be equal to the maximum allowed increase plus the negative delta due to the loss
vars.availableAmountToUpperBound =
maxIncrease + (vars.preReportUnderlyingBalance - vars.postReportUnderlyingBalance);
}
// if we have available amount to upper bound after the reporting values are applied
if (vars.availableAmountToUpperBound > 0) {
// we pull the funds from the execution layer fee recipient
vars.trace.pulledELFees = _pullELFees(vars.availableAmountToUpperBound);
// we update the rewards
vars.trace.rewards += vars.trace.pulledELFees;
// we update the available amount accordingly
vars.availableAmountToUpperBound -= vars.trace.pulledELFees;
}
// if we have available amount to upper bound after the execution layer fees are pulled
if (vars.availableAmountToUpperBound > 0) {
// we pull the funds from the exceeding eth buffer of the redeem manager
vars.trace.pulledRedeemManagerExceedingEthBuffer =
_pullRedeemManagerExceedingEth(vars.availableAmountToUpperBound);
// we update the available amount accordingly
vars.availableAmountToUpperBound -= vars.trace.pulledRedeemManagerExceedingEthBuffer;
}
// if we have available amount to upper bound after pulling the exceeding eth buffer, we attempt to pull coverage funds
if (vars.availableAmountToUpperBound > 0) {
// we pull the funds from the coverage recipient
vars.trace.pulledCoverageFunds = _pullCoverageFunds(vars.availableAmountToUpperBound);
// we do not update the rewards as coverage is not considered rewards
// we do not update the available amount as there are no more pulling actions to perform afterwards
}
// if our rewards are not null, we dispatch the fee to the collector
if (vars.trace.rewards > 0) {
_onEarnings(vars.trace.rewards);
}
_requestExitsBasedOnRedeemDemandAfterRebalancings(
_report.validatorsExitingBalance,
_report.stoppedValidatorCountPerOperator,
_report.rebalanceDepositToRedeemMode,
_report.slashingContainmentMode
);
// we use the updated balanceToRedeem value to report a withdraw event on the redeem manager
_reportWithdrawToRedeemManager();
// if funds are left in the balance to redeem, we move them to the deposit balance
_skimExcessBalanceToRedeem();
// we update the committable amount based on daily maximum allowed
_commitBalanceToDeposit(vars.timeElapsedSinceLastReport);
// we emit a summary event with all the reporting details
emit ProcessedConsensusLayerReport(_report, vars.trace);
}
/// @notice Retrieve the current epoch based on the current timestamp
/// @param _cls The consensus layer spec struct
/// @return The current epoch
function _currentEpoch(CLSpec.CLSpecStruct memory _cls) internal view returns (uint256) {
return ((block.timestamp - _cls.genesisTime) / _cls.secondsPerSlot) / _cls.slotsPerEpoch;
}
/// @notice Verifies if the given epoch is valid
/// @param _cls The consensus layer spec struct
/// @param _epoch The epoch to verify
/// @return True if valid
function _isValidEpoch(CLSpec.CLSpecStruct memory _cls, uint256 _epoch) internal view returns (bool) {
return (
_currentEpoch(_cls) >= _epoch + _cls.epochsToAssumedFinality
&& _epoch > LastConsensusLayerReport.get().epoch && _epoch % _cls.epochsPerFrame == 0
);
}
/// @notice Retrieves the maximum increase in balance based on current total underlying supply and period since last report
/// @param _rb The report bounds struct
/// @param _prevTotalEth The total underlying supply during reporting
/// @param _timeElapsed The time since last report
/// @return The maximum allowed increase in balance
function _maxIncrease(ReportBounds.ReportBoundsStruct memory _rb, uint256 _prevTotalEth, uint256 _timeElapsed)
internal
pure
returns (uint256)
{
return (_prevTotalEth * _rb.annualAprUpperBound * _timeElapsed) / (LibBasisPoints.BASIS_POINTS_MAX * ONE_YEAR);
}
/// @notice Retrieves the maximum decrease in balance based on current total underlying supply
/// @param _rb The report bounds struct
/// @param _prevTotalEth The total underlying supply during reporting
/// @return The maximum allowed decrease in balance
function _maxDecrease(ReportBounds.ReportBoundsStruct memory _rb, uint256 _prevTotalEth)
internal
pure
returns (uint256)
{
return (_prevTotalEth * _rb.relativeLowerBound) / LibBasisPoints.BASIS_POINTS_MAX;
}
/// @notice Retrieve the number of seconds between two epochs
/// @param _cls The consensus layer spec struct
/// @param _epochPast The starting epoch
/// @param _epochNow The current epoch
/// @return The number of seconds between the two epochs
function _timeBetweenEpochs(CLSpec.CLSpecStruct memory _cls, uint256 _epochPast, uint256 _epochNow)
internal
pure
returns (uint256)
{
return (_epochNow - _epochPast) * (_cls.secondsPerSlot * _cls.slotsPerEpoch);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../interfaces/components/ISharesManager.1.sol";
import "../libraries/LibSanitize.sol";
import "../state/river/Shares.sol";
import "../state/river/SharesPerOwner.sol";
import "../state/shared/ApprovalsPerOwner.sol";
/// @title Shares Manager (v1)
/// @author Alluvial Finance Inc.
/// @notice This contract handles the shares of the depositor and the ERC20 interface
abstract contract SharesManagerV1 is ISharesManagerV1 {
/// @notice Internal hook triggered on the external transfer call
/// @dev Must be overridden
/// @param _from Address of the sender
/// @param _to Address of the recipient
function _onTransfer(address _from, address _to) internal view virtual;
/// @notice Internal method to override to provide the total underlying asset balance
/// @dev Must be overridden
/// @return The total asset balance of the system
function _assetBalance() internal view virtual returns (uint256);
/// @notice Modifier used to ensure that the transfer is allowed by using the internal hook to perform internal checks
/// @param _from Address of the sender
/// @param _to Address of the recipient
modifier transferAllowed(address _from, address _to) {
_onTransfer(_from, _to);
_;
}
/// @notice Modifier used to ensure the amount transferred is not 0
/// @param _value Amount to check
modifier isNotZero(uint256 _value) {
if (_value == 0) {
revert NullTransfer();
}
_;
}
/// @notice Modifier used to ensure that the sender has enough funds for the transfer
/// @param _owner Address of the sender
/// @param _value Value that is required to be sent
modifier hasFunds(address _owner, uint256 _value) {
if (_balanceOf(_owner) < _value) {
revert BalanceTooLow();
}
_;
}
/// @inheritdoc ISharesManagerV1
function name() external pure returns (string memory) {
return "Liquid Staked ETH";
}
/// @inheritdoc ISharesManagerV1
function symbol() external pure returns (string memory) {
return "LsETH";
}
/// @inheritdoc ISharesManagerV1
function decimals() external pure returns (uint8) {
return 18;
}
/// @inheritdoc ISharesManagerV1
function totalSupply() external view returns (uint256) {
return _totalSupply();
}
/// @inheritdoc ISharesManagerV1
function totalUnderlyingSupply() external view returns (uint256) {
return _assetBalance();
}
/// @inheritdoc ISharesManagerV1
function balanceOf(address _owner) external view returns (uint256) {
return _balanceOf(_owner);
}
/// @inheritdoc ISharesManagerV1
function balanceOfUnderlying(address _owner) public view returns (uint256) {
return _balanceFromShares(SharesPerOwner.get(_owner));
}
/// @inheritdoc ISharesManagerV1
function underlyingBalanceFromShares(uint256 _shares) external view returns (uint256) {
return _balanceFromShares(_shares);
}
/// @inheritdoc ISharesManagerV1
function sharesFromUnderlyingBalance(uint256 _underlyingAssetAmount) external view returns (uint256) {
return _sharesFromBalance(_underlyingAssetAmount);
}
/// @inheritdoc ISharesManagerV1
function allowance(address _owner, address _spender) external view returns (uint256) {
return ApprovalsPerOwner.get(_owner, _spender);
}
/// @inheritdoc ISharesManagerV1
function transfer(address _to, uint256 _value)
external
transferAllowed(msg.sender, _to)
isNotZero(_value)
hasFunds(msg.sender, _value)
returns (bool)
{
if (_to == address(0)) {
revert UnauthorizedTransfer(msg.sender, address(0));
}
return _transfer(msg.sender, _to, _value);
}
/// @inheritdoc ISharesManagerV1
function transferFrom(address _from, address _to, uint256 _value)
external
transferAllowed(_from, _to)
isNotZero(_value)
hasFunds(_from, _value)
returns (bool)
{
if (_to == address(0)) {
revert UnauthorizedTransfer(_from, address(0));
}
_spendAllowance(_from, _value);
return _transfer(_from, _to, _value);
}
/// @inheritdoc ISharesManagerV1
function approve(address _spender, uint256 _value) external returns (bool) {
_approve(msg.sender, _spender, _value);
return true;
}
/// @inheritdoc ISharesManagerV1
function increaseAllowance(address _spender, uint256 _additionalValue) external returns (bool) {
_approve(msg.sender, _spender, ApprovalsPerOwner.get(msg.sender, _spender) + _additionalValue);
return true;
}
/// @inheritdoc ISharesManagerV1
function decreaseAllowance(address _spender, uint256 _subtractableValue) external returns (bool) {
_approve(msg.sender, _spender, ApprovalsPerOwner.get(msg.sender, _spender) - _subtractableValue);
return true;
}
/// @notice Internal utility to spend the allowance of an account from the message sender
/// @param _from Address owning the allowance
/// @param _value Amount of allowance in shares to spend
function _spendAllowance(address _from, uint256 _value) internal {
uint256 currentAllowance = ApprovalsPerOwner.get(_from, msg.sender);
if (currentAllowance < _value) {
revert AllowanceTooLow(_from, msg.sender, currentAllowance, _value);
}
if (currentAllowance != type(uint256).max) {
unchecked {
_approve(_from, msg.sender, currentAllowance - _value);
}
}
}
/// @notice Internal utility to change the allowance of an owner to a spender
/// @param _owner The owner of the shares
/// @param _spender The allowed spender of the shares
/// @param _value The new allowance value
function _approve(address _owner, address _spender, uint256 _value) internal {
LibSanitize._notZeroAddress(_owner);
LibSanitize._notZeroAddress(_spender);
ApprovalsPerOwner.set(_owner, _spender, _value);
emit Approval(_owner, _spender, _value);
}
/// @notice Internal utility to retrieve the total supply of tokens
/// @return The total supply
function _totalSupply() internal view returns (uint256) {
return Shares.get();
}
/// @notice Internal utility to perform an unchecked transfer
/// @param _from Address sending the tokens
/// @param _to Address receiving the tokens
/// @param _value Amount of shares to be sent
/// @return True if success
function _transfer(address _from, address _to, uint256 _value) internal returns (bool) {
SharesPerOwner.set(_from, SharesPerOwner.get(_from) - _value);
SharesPerOwner.set(_to, SharesPerOwner.get(_to) + _value);
emit Transfer(_from, _to, _value);
return true;
}
/// @notice Internal utility to retrieve the underlying asset balance for the given shares
/// @param _shares Amount of shares to convert
/// @return The balance from the given shares
function _balanceFromShares(uint256 _shares) internal view returns (uint256) {
uint256 _totalSharesValue = Shares.get();
if (_totalSharesValue == 0) {
return 0;
}
return ((_shares * _assetBalance())) / _totalSharesValue;
}
/// @notice Internal utility to retrieve the shares count for a given underlying asset amount
/// @param _balance Amount of underlying asset balance to convert
/// @return The shares from the given balance
function _sharesFromBalance(uint256 _balance) internal view returns (uint256) {
uint256 _totalUnderlyingSupply = _assetBalance();
if (_totalUnderlyingSupply == 0) {
return 0;
}
return (_balance * Shares.get()) / _totalUnderlyingSupply;
}
/// @notice Internal utility to mint shares for the specified user
/// @dev This method assumes that funds received are now part of the _assetBalance()
/// @param _owner Account that should receive the new shares
/// @param _underlyingAssetValue Value of underlying asset received, to convert into shares
/// @return sharesToMint The amnount of minted shares
function _mintShares(address _owner, uint256 _underlyingAssetValue) internal returns (uint256 sharesToMint) {
uint256 oldTotalAssetBalance = _assetBalance() - _underlyingAssetValue;
uint256 tSupply = _totalSupply();
if (oldTotalAssetBalance == 0 || tSupply == 0) {
sharesToMint = _underlyingAssetValue;
_mintRawShares(_owner, _underlyingAssetValue);
} else {
sharesToMint = (_underlyingAssetValue * tSupply) / oldTotalAssetBalance;
_mintRawShares(_owner, sharesToMint);
}
}
/// @notice Internal utility to retrieve the amount of shares per owner
/// @param _owner Account to be checked
/// @return The balance of the account in shares
function _balanceOf(address _owner) internal view returns (uint256) {
return SharesPerOwner.get(_owner);
}
/// @notice Internal utility to mint shares without any conversion, and emits a mint Transfer event
/// @param _owner Account that should receive the new shares
/// @param _value Amount of shares to mint
function _mintRawShares(address _owner, uint256 _value) internal {
_setTotalSupply(Shares.get() + _value);
SharesPerOwner.set(_owner, SharesPerOwner.get(_owner) + _value);
emit Transfer(address(0), _owner, _value);
}
/// @notice Internal utility to burn shares without any conversion, and emits a burn Transfer event
/// @param _owner Account that should burn its shares
/// @param _value Amount of shares to burn
function _burnRawShares(address _owner, uint256 _value) internal {
_setTotalSupply(Shares.get() - _value);
SharesPerOwner.set(_owner, SharesPerOwner.get(_owner) - _value);
emit Transfer(_owner, address(0), _value);
}
/// @notice Internal utility to set the total supply and emit an event
/// @param newTotalSupply The new total supply value
function _setTotalSupply(uint256 newTotalSupply) internal {
Shares.set(newTotalSupply);
emit SetTotalSupply(newTotalSupply);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../interfaces/components/IUserDepositManager.1.sol";
import "../libraries/LibSanitize.sol";
import "../state/river/BalanceToDeposit.sol";
/// @title User Deposit Manager (v1)
/// @author Alluvial Finance Inc.
/// @notice This contract handles the inbound transfers cases or the explicit submissions
abstract contract UserDepositManagerV1 is IUserDepositManagerV1 {
/// @notice Handler called whenever a user has sent funds to the contract
/// @dev Must be overridden
/// @param _depositor Address that made the deposit
/// @param _recipient Address that receives the minted shares
/// @param _amount Amount deposited
function _onDeposit(address _depositor, address _recipient, uint256 _amount) internal virtual;
function _setBalanceToDeposit(uint256 newBalanceToDeposit) internal virtual;
/// @inheritdoc IUserDepositManagerV1
function deposit() external payable {
_deposit(msg.sender);
}
/// @inheritdoc IUserDepositManagerV1
function depositAndTransfer(address _recipient) external payable {
LibSanitize._notZeroAddress(_recipient);
_deposit(_recipient);
}
/// @inheritdoc IUserDepositManagerV1
receive() external payable {
_deposit(msg.sender);
}
/// @inheritdoc IUserDepositManagerV1
fallback() external payable {
revert LibErrors.InvalidCall();
}
/// @notice Internal utility calling the deposit handler and emitting the deposit details
/// @param _recipient The account receiving the minted shares
function _deposit(address _recipient) internal {
if (msg.value == 0) {
revert EmptyDeposit();
}
_setBalanceToDeposit(BalanceToDeposit.get() + msg.value);
_onDeposit(msg.sender, _recipient, msg.value);
emit UserDeposit(msg.sender, _recipient, msg.value);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "./state/shared/Version.sol";
/// @title Initializable
/// @author Alluvial Finance Inc.
/// @notice This contract ensures that initializers are called only once per version
contract Initializable {
/// @notice Disable initialization on implementations
constructor() {
Version.set(type(uint256).max);
emit Initialize(type(uint256).max, msg.data);
}
/// @notice An error occured during the initialization
/// @param version The version that was attempting to be initialized
/// @param expectedVersion The version that was expected
error InvalidInitialization(uint256 version, uint256 expectedVersion);
/// @notice Emitted when the contract is properly initialized
/// @param version New version of the contracts
/// @param cdata Complete calldata that was used during the initialization
event Initialize(uint256 version, bytes cdata);
/// @notice Use this modifier on initializers along with a hard-coded version number
/// @param _version Version to initialize
modifier init(uint256 _version) {
if (_version != Version.get()) {
revert InvalidInitialization(_version, Version.get());
}
Version.set(_version + 1); // prevents reentrency on the called method
_;
emit Initialize(_version, msg.data);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Consensys Layer Deposit Manager Interface (v1)
/// @author Alluvial Finance Inc.
/// @notice This interface exposes methods to handle the interactions with the official deposit contract
interface IConsensusLayerDepositManagerV1 {
/// @notice The stored deposit contract address changed
/// @param depositContract Address of the deposit contract
event SetDepositContractAddress(address indexed depositContract);
/// @notice The stored withdrawal credentials changed
/// @param withdrawalCredentials The withdrawal credentials to use for deposits
event SetWithdrawalCredentials(bytes32 withdrawalCredentials);
/// @notice Emitted when the deposited validator count is updated
/// @param oldDepositedValidatorCount The old deposited validator count value
/// @param newDepositedValidatorCount The new deposited validator count value
event SetDepositedValidatorCount(uint256 oldDepositedValidatorCount, uint256 newDepositedValidatorCount);
/// @notice Not enough funds to deposit one validator
error NotEnoughFunds();
/// @notice The length of the BLS Public key is invalid during deposit
error InconsistentPublicKeys();
/// @notice The length of the BLS Signature is invalid during deposit
error InconsistentSignatures();
/// @notice The internal key retrieval returned no keys
error NoAvailableValidatorKeys();
/// @notice The received count of public keys to deposit is invalid
error InvalidPublicKeyCount();
/// @notice The received count of signatures to deposit is invalid
error InvalidSignatureCount();
/// @notice The withdrawal credentials value is null
error InvalidWithdrawalCredentials();
/// @notice An error occured during the deposit
error ErrorOnDeposit();
/// @notice Invalid deposit root
error InvalidDepositRoot();
// @notice Not keeper
error OnlyKeeper();
/// @notice Returns the amount of ETH not yet committed for deposit
/// @return The amount of ETH not yet committed for deposit
function getBalanceToDeposit() external view returns (uint256);
/// @notice Returns the amount of ETH committed for deposit
/// @return The amount of ETH committed for deposit
function getCommittedBalance() external view returns (uint256);
/// @notice Retrieve the withdrawal credentials
/// @return The withdrawal credentials
function getWithdrawalCredentials() external view returns (bytes32);
/// @notice Get the deposited validator count (the count of deposits made by the contract)
/// @return The deposited validator count
function getDepositedValidatorCount() external view returns (uint256);
/// @notice Get the keeper address
/// @return The keeper address
function getKeeper() external view returns (address);
/// @notice Deposits current balance to the Consensus Layer by batches of 32 ETH
/// @param _maxCount The maximum amount of validator keys to fund
/// @param _depositRoot The root of the deposit tree
function depositToConsensusLayerWithDepositRoot(uint256 _maxCount, bytes32 _depositRoot) external;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../state/river/CLSpec.sol";
import "../../state/river/ReportBounds.sol";
/// @title Oracle Manager (v1)
/// @author Alluvial Finance Inc.
/// @notice This interface exposes methods to handle the inputs provided by the oracle
interface IOracleManagerV1 {
/// @notice The stored oracle address changed
/// @param oracleAddress The new oracle address
event SetOracle(address indexed oracleAddress);
/// @notice The consensus layer data provided by the oracle has been updated
/// @param validatorCount The new count of validators running on the consensus layer
/// @param validatorTotalBalance The new total balance sum of all validators
/// @param roundId Round identifier
event ConsensusLayerDataUpdate(uint256 validatorCount, uint256 validatorTotalBalance, bytes32 roundId);
/// @notice The Consensus Layer Spec is changed
/// @param epochsPerFrame The number of epochs inside a frame
/// @param slotsPerEpoch The number of slots inside an epoch
/// @param secondsPerSlot The number of seconds inside a slot
/// @param genesisTime The genesis timestamp
/// @param epochsToAssumedFinality The number of epochs before an epoch is considered final
event SetSpec(
uint64 epochsPerFrame,
uint64 slotsPerEpoch,
uint64 secondsPerSlot,
uint64 genesisTime,
uint64 epochsToAssumedFinality
);
/// @notice The Report Bounds are changed
/// @param annualAprUpperBound The reporting upper bound
/// @param relativeLowerBound The reporting lower bound
event SetBounds(uint256 annualAprUpperBound, uint256 relativeLowerBound);
/// @notice The provided report has beend processed
/// @param report The report that was provided
/// @param trace The trace structure providing more insights on internals
event ProcessedConsensusLayerReport(
IOracleManagerV1.ConsensusLayerReport report, ConsensusLayerDataReportingTrace trace
);
/// @notice The reported validator count is invalid
/// @param providedValidatorCount The received validator count value
/// @param depositedValidatorCount The number of deposits performed by the system
/// @param lastReportedValidatorCount The last reported validator count
error InvalidValidatorCountReport(
uint256 providedValidatorCount, uint256 depositedValidatorCount, uint256 lastReportedValidatorCount
);
/// @notice Thrown when an invalid epoch was reported
/// @param epoch Invalid epoch
error InvalidEpoch(uint256 epoch);
/// @notice The balance increase is higher than the maximum allowed by the upper bound
/// @param prevTotalEthIncludingExited The previous total balance, including all exited balance
/// @param postTotalEthIncludingExited The post-report total balance, including all exited balance
/// @param timeElapsed The time in seconds since last report
/// @param annualAprUpperBound The upper bound value that was used
error TotalValidatorBalanceIncreaseOutOfBound(
uint256 prevTotalEthIncludingExited,
uint256 postTotalEthIncludingExited,
uint256 timeElapsed,
uint256 annualAprUpperBound
);
/// @notice The balance decrease is higher than the maximum allowed by the lower bound
/// @param prevTotalEthIncludingExited The previous total balance, including all exited balance
/// @param postTotalEthIncludingExited The post-report total balance, including all exited balance
/// @param timeElapsed The time in seconds since last report
/// @param relativeLowerBound The lower bound value that was used
error TotalValidatorBalanceDecreaseOutOfBound(
uint256 prevTotalEthIncludingExited,
uint256 postTotalEthIncludingExited,
uint256 timeElapsed,
uint256 relativeLowerBound
);
/// @notice The total exited balance decreased
/// @param currentValidatorsExitedBalance The current exited balance
/// @param newValidatorsExitedBalance The new exited balance
error InvalidDecreasingValidatorsExitedBalance(
uint256 currentValidatorsExitedBalance, uint256 newValidatorsExitedBalance
);
/// @notice The total skimmed balance decreased
/// @param currentValidatorsSkimmedBalance The current exited balance
/// @param newValidatorsSkimmedBalance The new exited balance
error InvalidDecreasingValidatorsSkimmedBalance(
uint256 currentValidatorsSkimmedBalance, uint256 newValidatorsSkimmedBalance
);
/// @notice Trace structure emitted via logs during reporting
struct ConsensusLayerDataReportingTrace {
uint256 rewards;
uint256 pulledELFees;
uint256 pulledRedeemManagerExceedingEthBuffer;
uint256 pulledCoverageFunds;
}
/// @notice The format of the oracle report
struct ConsensusLayerReport {
// this is the epoch at which the report was performed
// data should be fetched up to the state of this epoch by the oracles
uint256 epoch;
// the sum of all the validator balances on the consensus layer
// when a validator enters the exit queue, the validator is considered stopped, its balance is accounted in both validatorsExitingBalance and validatorsBalance
// when a validator leaves the exit queue and the funds are sweeped onto the execution layer, the balance is only accounted in validatorsExitedBalance and not in validatorsBalance
// this value can decrease between reports
uint256 validatorsBalance;
// the sum of all the skimmings performed on the validators
// these values can be found in the execution layer block bodies under the withdrawals field
// a withdrawal is considered skimming if
// - the epoch at which it happened is < validator.withdrawableEpoch
// - the epoch at which it happened is >= validator.withdrawableEpoch and in that case we only account for what would be above 32 eth as skimming
// this value cannot decrease over reports
uint256 validatorsSkimmedBalance;
// the sum of all the exits performed on the validators
// these values can be found in the execution layer block bodies under the withdrawals field
// a withdrawal is considered exit if
// - the epoch at which it happened is >= validator.withdrawableEpoch and in that case we only account for what would be <= 32 eth as exit
// this value cannot decrease over reports
uint256 validatorsExitedBalance;
// the sum of all the exiting balance, which is all the validators on their way to get sweeped and exited
// this includes voluntary exits and slashings
// this value can decrease between reports
uint256 validatorsExitingBalance;
// the count of activated validators
// even validators that are exited are still accounted
// this value cannot decrease over reports
uint32 validatorsCount;
// an array containing the count of stopped validators per operator
// the first element of the array is the sum of all stopped validators
// then index 1 would be operator 0
// these values cannot decrease over reports
uint32[] stoppedValidatorCountPerOperator;
// flag enabled by the oracles when the buffer rebalancing is activated
// the activation logic is written in the oracle specification and all oracle members must agree on the activation
// when active, the eth in the deposit buffer can be used to pay for exits in the redeem manager
bool rebalanceDepositToRedeemMode;
// flag enabled by the oracles when the slashing containment is activated
// the activation logic is written in the oracle specification and all oracle members must agree on the activation
// This flag is activated when a pre-defined threshold of slashed validators in our set of validators is reached
// This flag is deactivated when a bottom threshold is met, this means that when we reach the upper threshold and activate the flag, we will deactivate it when we reach the bottom threshold and not before
// when active, no more validator exits can be requested by the protocol
bool slashingContainmentMode;
}
/// @notice The format of the oracle report in storage
/// @notice These fields have the exact same function as the ones in ConsensusLayerReport, but this struct is optimized for storage
struct StoredConsensusLayerReport {
uint256 epoch;
uint256 validatorsBalance;
uint256 validatorsSkimmedBalance;
uint256 validatorsExitedBalance;
uint256 validatorsExitingBalance;
uint32 validatorsCount;
bool rebalanceDepositToRedeemMode;
bool slashingContainmentMode;
}
/// @notice Get oracle address
/// @return The oracle address
function getOracle() external view returns (address);
/// @notice Get CL validator total balance
/// @return The CL Validator total balance
function getCLValidatorTotalBalance() external view returns (uint256);
/// @notice Get CL validator count (the amount of validator reported by the oracles)
/// @return The CL validator count
function getCLValidatorCount() external view returns (uint256);
/// @notice Verifies if the provided epoch is valid
/// @param epoch The epoch to lookup
/// @return True if valid
function isValidEpoch(uint256 epoch) external view returns (bool);
/// @notice Retrieve the block timestamp
/// @return The current timestamp from the EVM context
function getTime() external view returns (uint256);
/// @notice Retrieve expected epoch id
/// @return The current expected epoch id
function getExpectedEpochId() external view returns (uint256);
/// @notice Retrieve the last completed epoch id
/// @return The last completed epoch id
function getLastCompletedEpochId() external view returns (uint256);
/// @notice Retrieve the current epoch id based on block timestamp
/// @return The current epoch id
function getCurrentEpochId() external view returns (uint256);
/// @notice Retrieve the current cl spec
/// @return The Consensus Layer Specification
function getCLSpec() external view returns (CLSpec.CLSpecStruct memory);
/// @notice Retrieve the current frame details
/// @return _startEpochId The epoch at the beginning of the frame
/// @return _startTime The timestamp of the beginning of the frame in seconds
/// @return _endTime The timestamp of the end of the frame in seconds
function getCurrentFrame() external view returns (uint256 _startEpochId, uint256 _startTime, uint256 _endTime);
/// @notice Retrieve the first epoch id of the frame of the provided epoch id
/// @param _epochId Epoch id used to get the frame
/// @return The first epoch id of the frame containing the given epoch id
function getFrameFirstEpochId(uint256 _epochId) external view returns (uint256);
/// @notice Retrieve the report bounds
/// @return The report bounds
function getReportBounds() external view returns (ReportBounds.ReportBoundsStruct memory);
/// @notice Retrieve the last consensus layer report
/// @return The stored consensus layer report
function getLastConsensusLayerReport() external view returns (IOracleManagerV1.StoredConsensusLayerReport memory);
/// @notice Set the oracle address
/// @param _oracleAddress Address of the oracle
function setOracle(address _oracleAddress) external;
/// @notice Set the consensus layer spec
/// @param _newValue The new consensus layer spec value
function setCLSpec(CLSpec.CLSpecStruct calldata _newValue) external;
/// @notice Set the report bounds
/// @param _newValue The new report bounds value
function setReportBounds(ReportBounds.ReportBoundsStruct calldata _newValue) external;
/// @notice Performs all the reporting logics
/// @param _report The consensus layer report structure
function setConsensusLayerData(ConsensusLayerReport calldata _report) external;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
/// @title Shares Manager Interface (v1)
/// @author Alluvial Finance Inc.
/// @notice This interface exposes methods to handle the shares of the depositor and the ERC20 interface
interface ISharesManagerV1 is IERC20 {
/// @notice Emitted when the total supply is changed
event SetTotalSupply(uint256 totalSupply);
/// @notice Balance too low to perform operation
error BalanceTooLow();
/// @notice Allowance too low to perform operation
/// @param _from Account where funds are sent from
/// @param _operator Account attempting the transfer
/// @param _allowance Current allowance
/// @param _value Requested transfer value in shares
error AllowanceTooLow(address _from, address _operator, uint256 _allowance, uint256 _value);
/// @notice Invalid empty transfer
error NullTransfer();
/// @notice Invalid transfer recipients
/// @param _from Account sending the funds in the invalid transfer
/// @param _to Account receiving the funds in the invalid transfer
error UnauthorizedTransfer(address _from, address _to);
/// @notice Retrieve the token name
/// @return The token name
function name() external pure returns (string memory);
/// @notice Retrieve the token symbol
/// @return The token symbol
function symbol() external pure returns (string memory);
/// @notice Retrieve the decimal count
/// @return The decimal count
function decimals() external pure returns (uint8);
/// @notice Retrieve the total token supply
/// @return The total supply in shares
function totalSupply() external view returns (uint256);
/// @notice Retrieve the total underlying asset supply
/// @return The total underlying asset supply
function totalUnderlyingSupply() external view returns (uint256);
/// @notice Retrieve the balance of an account
/// @param _owner Address to be checked
/// @return The balance of the account in shares
function balanceOf(address _owner) external view returns (uint256);
/// @notice Retrieve the underlying asset balance of an account
/// @param _owner Address to be checked
/// @return The underlying balance of the account
function balanceOfUnderlying(address _owner) external view returns (uint256);
/// @notice Retrieve the underlying asset balance from an amount of shares
/// @param _shares Amount of shares to convert
/// @return The underlying asset balance represented by the shares
function underlyingBalanceFromShares(uint256 _shares) external view returns (uint256);
/// @notice Retrieve the shares count from an underlying asset amount
/// @param _underlyingAssetAmount Amount of underlying asset to convert
/// @return The amount of shares worth the underlying asset amopunt
function sharesFromUnderlyingBalance(uint256 _underlyingAssetAmount) external view returns (uint256);
/// @notice Retrieve the allowance value for a spender
/// @param _owner Address that issued the allowance
/// @param _spender Address that received the allowance
/// @return The allowance in shares for a given spender
function allowance(address _owner, address _spender) external view returns (uint256);
/// @notice Performs a transfer from the message sender to the provided account
/// @param _to Address receiving the tokens
/// @param _value Amount of shares to be sent
/// @return True if success
function transfer(address _to, uint256 _value) external returns (bool);
/// @notice Performs a transfer between two recipients
/// @param _from Address sending the tokens
/// @param _to Address receiving the tokens
/// @param _value Amount of shares to be sent
/// @return True if success
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
/// @notice Approves an account for future spendings
/// @dev An approved account can use transferFrom to transfer funds on behalf of the token owner
/// @param _spender Address that is allowed to spend the tokens
/// @param _value The allowed amount in shares, will override previous value
/// @return True if success
function approve(address _spender, uint256 _value) external returns (bool);
/// @notice Increase allowance to another account
/// @param _spender Spender that receives the allowance
/// @param _additionalValue Amount of shares to add
/// @return True if success
function increaseAllowance(address _spender, uint256 _additionalValue) external returns (bool);
/// @notice Decrease allowance to another account
/// @param _spender Spender that receives the allowance
/// @param _subtractableValue Amount of shares to subtract
/// @return True if success
function decreaseAllowance(address _spender, uint256 _subtractableValue) external returns (bool);
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title User Deposit Manager (v1)
/// @author Alluvial Finance Inc.
/// @notice This interface exposes methods to handle the inbound transfers cases or the explicit submissions
interface IUserDepositManagerV1 {
/// @notice User deposited ETH in the system
/// @param depositor Address performing the deposit
/// @param recipient Address receiving the minted shares
/// @param amount Amount in ETH deposited
event UserDeposit(address indexed depositor, address indexed recipient, uint256 amount);
/// @notice And empty deposit attempt was made
error EmptyDeposit();
/// @notice Explicit deposit method to mint on msg.sender
function deposit() external payable;
/// @notice Explicit deposit method to mint on msg.sender and transfer to _recipient
/// @param _recipient Address receiving the minted LsETH
function depositAndTransfer(address _recipient) external payable;
/// @notice Implicit deposit method, when the user performs a regular transfer to the contract
receive() external payable;
/// @notice Invalid call, when the user sends a transaction with a data payload but no method matched
fallback() external payable;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Administrable Interface
/// @author Alluvial Finance Inc.
/// @notice This interface exposes methods to handle the ownership of the contracts
interface IAdministrable {
/// @notice The pending admin address changed
/// @param pendingAdmin New pending admin address
event SetPendingAdmin(address indexed pendingAdmin);
/// @notice The admin address changed
/// @param admin New admin address
event SetAdmin(address indexed admin);
/// @notice Retrieves the current admin address
/// @return The admin address
function getAdmin() external view returns (address);
/// @notice Retrieve the current pending admin address
/// @return The pending admin address
function getPendingAdmin() external view returns (address);
/// @notice Proposes a new address as admin
/// @dev This security prevents setting an invalid address as an admin. The pending
/// @dev admin has to claim its ownership of the contract, and prove that the new
/// @dev address is able to perform regular transactions.
/// @param _newAdmin New admin address
function proposeAdmin(address _newAdmin) external;
/// @notice Accept the transfer of ownership
/// @dev Only callable by the pending admin. Resets the pending admin if succesful.
function acceptAdmin() external;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Allowlist Interface (v1)
/// @author Alluvial Finance Inc.
/// @notice This interface exposes methods to handle the list of allowed recipients.
interface IAllowlistV1 {
/// @notice The permissions of several accounts have changed
/// @param accounts List of accounts
/// @param permissions New permissions for each account at the same index
event SetAllowlistPermissions(address[] accounts, uint256[] permissions);
/// @notice The stored allower address has been changed
/// @param allower The new allower address
event SetAllower(address indexed allower);
/// @notice The stored denier address has been changed
/// @param denier The new denier address
event SetDenier(address indexed denier);
/// @notice The provided accounts list is empty
error InvalidCount();
/// @notice The account is denied access
/// @param _account The denied account
error Denied(address _account);
/// @notice The provided accounts and permissions list have different lengths
error MismatchedArrayLengths();
/// @notice Allower can't set deny permission
error AttemptToSetDenyPermission();
/// @notice Allower can't remove deny permission
error AttemptToRemoveDenyPermission();
/// @notice Initializes the allowlist
/// @param _admin Address of the Allowlist administrator
/// @param _allower Address of the allower
function initAllowlistV1(address _admin, address _allower) external;
/// @notice Initializes the allowlist denier
/// @param _denier Address of the denier
function initAllowlistV1_1(address _denier) external;
/// @notice Retrieves the allower address
/// @return The address of the allower
function getAllower() external view returns (address);
/// @notice Retrieves the denier address
/// @return The address of the denier
function getDenier() external view returns (address);
/// @notice This method returns true if the user has the expected permission and
/// is not in the deny list
/// @param _account Recipient to verify
/// @param _mask Combination of permissions to verify
/// @return True if mask is respected and user is allowed
function isAllowed(address _account, uint256 _mask) external view returns (bool);
/// @notice This method returns true if the user is in the deny list
/// @param _account Recipient to verify
/// @return True if user is denied access
function isDenied(address _account) external view returns (bool);
/// @notice This method returns true if the user has the expected permission
/// ignoring any deny list membership
/// @param _account Recipient to verify
/// @param _mask Combination of permissions to verify
/// @return True if mask is respected
function hasPermission(address _account, uint256 _mask) external view returns (bool);
/// @notice This method retrieves the raw permission value
/// @param _account Recipient to verify
/// @return The raw permissions value of the account
function getPermissions(address _account) external view returns (uint256);
/// @notice This method should be used as a modifier and is expected to revert
/// if the user hasn't got the required permission or if the user is
/// in the deny list.
/// @param _account Recipient to verify
/// @param _mask Combination of permissions to verify
function onlyAllowed(address _account, uint256 _mask) external view;
/// @notice Changes the allower address
/// @param _newAllowerAddress New address allowed to edit the allowlist
function setAllower(address _newAllowerAddress) external;
/// @notice Changes the denier address
/// @param _newDenierAddress New address allowed to edit the allowlist
function setDenier(address _newDenierAddress) external;
/// @notice Sets the allow permissions for one or more accounts
/// @dev This function is for allocating or removing deposit, redeem or donate permissions.
/// This function could be used to give any permissions that we come up with in the future.
/// An address which was denied has to be undenied first before they could be given any permission(s).
/// @param _accounts Accounts to update
/// @param _permissions New permission values
function setAllowPermissions(address[] calldata _accounts, uint256[] calldata _permissions) external;
/// @notice Sets the deny permissions for one or more accounts
/// @dev This function is for allocating or removing deny permissions.
/// An address which is undenied has to be given permissions again for them to be able to deposit, donate or redeem.
/// @param _accounts Accounts to update
/// @param _permissions New permission values
function setDenyPermissions(address[] calldata _accounts, uint256[] calldata _permissions) external;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Coverage Fund Interface (v1)
/// @author Alluvial Finance Inc.
/// @notice This interface exposes methods to receive donations for the slashing coverage fund and pull the funds into river
interface ICoverageFundV1 {
/// @notice The storage river address has changed
/// @param river The new river address
event SetRiver(address indexed river);
/// @notice A donation has been made to the coverage fund
/// @param donator Address that performed the donation
/// @param amount The amount donated
event Donate(address indexed donator, uint256 amount);
/// @notice The fallback or receive callback has been triggered
error InvalidCall();
/// @notice A donation with 0 ETH has been performed
error EmptyDonation();
/// @notice Initialize the coverage fund with the required arguments
/// @param _riverAddress Address of River
function initCoverageFundV1(address _riverAddress) external;
/// @notice Pulls ETH into the River contract
/// @dev Only callable by the River contract
/// @param _maxAmount The maximum amount to pull into the system
function pullCoverageFunds(uint256 _maxAmount) external;
/// @notice Donates ETH to the coverage fund contract
function donate() external payable;
/// @notice Ether receiver
receive() external payable;
/// @notice Invalid fallback detector
fallback() external payable;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Deposit Contract Interface
/// @notice This interface exposes methods to perform validator deposits
interface IDepositContract {
/// @notice Official deposit method to activate a validator on the consensus layer
/// @param pubkey The 48 bytes long BLS Public key representing the validator
/// @param withdrawalCredentials The 32 bytes long withdrawal credentials, configures the withdrawal recipient
/// @param signature The 96 bytes long BLS Signature performed by the pubkey's private key
/// @param depositDataRoot The root hash of the whole deposit data structure
function deposit(
bytes calldata pubkey,
bytes calldata withdrawalCredentials,
bytes calldata signature,
bytes32 depositDataRoot
) external payable;
/// @notice Query the current deposit root hash.
/// @return The deposit root hash.
function get_deposit_root() external view returns (bytes32);
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Execution Layer Fee Recipient Interface (v1)
/// @author Alluvial Finance Inc.
/// @notice This interface exposes methods to receive all the execution layer fees from the proposed blocks + bribes
interface IELFeeRecipientV1 {
/// @notice The storage river address has changed
/// @param river The new river address
event SetRiver(address indexed river);
/// @notice The fallback has been triggered
error InvalidCall();
/// @notice Initialize the fee recipient with the required arguments
/// @param _riverAddress Address of River
function initELFeeRecipientV1(address _riverAddress) external;
/// @notice Pulls ETH to the River contract
/// @dev Only callable by the River contract
/// @param _maxAmount The maximum amount to pull into the system
function pullELFees(uint256 _maxAmount) external;
/// @notice Ether receiver
receive() external payable;
/// @notice Invalid fallback detector
fallback() external payable;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../state/operatorsRegistry/Operators.2.sol";
/// @title Operators Registry Interface (v1)
/// @author Alluvial Finance Inc.
/// @notice This interface exposes methods to handle the list of operators and their keys
interface IOperatorsRegistryV1 {
/// @notice A new operator has been added to the registry
/// @param index The operator index
/// @param name The operator display name
/// @param operatorAddress The operator address
event AddedOperator(uint256 indexed index, string name, address indexed operatorAddress);
/// @notice The operator status has been changed
/// @param index The operator index
/// @param active True if the operator is active
event SetOperatorStatus(uint256 indexed index, bool active);
/// @notice The operator limit has been changed
/// @param index The operator index
/// @param newLimit The new operator staking limit
event SetOperatorLimit(uint256 indexed index, uint256 newLimit);
/// @notice The operator stopped validator count has been changed
/// @param index The operator index
/// @param newStoppedValidatorCount The new stopped validator count
event SetOperatorStoppedValidatorCount(uint256 indexed index, uint256 newStoppedValidatorCount);
/// @notice The operator address has been changed
/// @param index The operator index
/// @param newOperatorAddress The new operator address
event SetOperatorAddress(uint256 indexed index, address indexed newOperatorAddress);
/// @notice The operator display name has been changed
/// @param index The operator index
/// @param newName The new display name
event SetOperatorName(uint256 indexed index, string newName);
/// @notice The operator or the admin added new validator keys and signatures
/// @dev The public keys and signatures are concatenated
/// @dev A public key is 48 bytes long
/// @dev A signature is 96 bytes long
/// @dev [P1, S1, P2, S2, ..., PN, SN] where N is the bytes length divided by (96 + 48)
/// @param index The operator index
/// @param publicKeysAndSignatures The concatenated public keys and signatures
event AddedValidatorKeys(uint256 indexed index, bytes publicKeysAndSignatures);
/// @notice The operator or the admin removed a public key and its signature from the registry
/// @param index The operator index
/// @param publicKey The BLS public key that has been removed
event RemovedValidatorKey(uint256 indexed index, bytes publicKey);
/// @notice The stored river address has been changed
/// @param river The new river address
event SetRiver(address indexed river);
/// @notice The operator edited its keys after the snapshot block
/// @dev This means that we cannot assume that its key set is checked by the snapshot
/// @dev This happens only if the limit was meant to be increased
/// @param index The operator index
/// @param currentLimit The current operator limit
/// @param newLimit The new operator limit that was attempted to be set
/// @param latestKeysEditBlockNumber The last block number at which the operator changed its keys
/// @param snapshotBlock The block number of the snapshot
event OperatorEditsAfterSnapshot(
uint256 indexed index,
uint256 currentLimit,
uint256 newLimit,
uint256 indexed latestKeysEditBlockNumber,
uint256 indexed snapshotBlock
);
/// @notice The call didn't alter the limit of the operator
/// @param index The operator index
/// @param limit The limit of the operator
event OperatorLimitUnchanged(uint256 indexed index, uint256 limit);
/// @notice The stopped validator array has been changed
/// @notice A validator is considered stopped if exiting, exited or slashed
/// @notice This event is emitted when the oracle reports new stopped validators counts
/// @param stoppedValidatorCounts The new stopped validator counts
event UpdatedStoppedValidators(uint32[] stoppedValidatorCounts);
/// @notice The requested exit count has been updated
/// @param index The operator index
/// @param count The count of requested exits
event RequestedValidatorExits(uint256 indexed index, uint256 count);
/// @notice The exit request demand has been updated
/// @param previousValidatorExitsDemand The previous exit request demand
/// @param nextValidatorExitsDemand The new exit request demand
event SetCurrentValidatorExitsDemand(uint256 previousValidatorExitsDemand, uint256 nextValidatorExitsDemand);
/// @notice The total requested exit has been updated
/// @param previousTotalValidatorExitsRequested The previous total requested exit
/// @param newTotalValidatorExitsRequested The new total requested exit
event SetTotalValidatorExitsRequested(
uint256 previousTotalValidatorExitsRequested, uint256 newTotalValidatorExitsRequested
);
/// @notice A validator key got funded on the deposit contract
/// @notice This event was introduced during a contract upgrade, in order to cover all possible public keys, this event
/// @notice will be replayed for past funded keys in order to have a complete coverage of all the funded public keys.
/// @notice In this particuliar scenario, the deferred value will be set to true, to indicate that we are not going to have
/// @notice the expected additional events and side effects in the same transaction (deposit to official DepositContract etc ...) because
/// @notice the event was synthetically crafted.
/// @param index The operator index
/// @param publicKeys BLS Public key that got funded
/// @param deferred True if event has been replayed in the context of a migration
event FundedValidatorKeys(uint256 indexed index, bytes[] publicKeys, bool deferred);
/// @notice The requested exit count has been update to fill the gap with the reported stopped count
/// @param index The operator index
/// @param oldRequestedExits The old requested exit count
/// @param newRequestedExits The new requested exit count
event UpdatedRequestedValidatorExitsUponStopped(
uint256 indexed index, uint32 oldRequestedExits, uint32 newRequestedExits
);
/// @notice The calling operator is inactive
/// @param index The operator index
error InactiveOperator(uint256 index);
/// @notice A funded key deletion has been attempted
error InvalidFundedKeyDeletionAttempt();
/// @notice The index provided are not sorted properly (descending order)
error InvalidUnsortedIndexes();
/// @notice The provided operator and limits array have different lengths
error InvalidArrayLengths();
/// @notice The provided operator and limits array are empty
error InvalidEmptyArray();
/// @notice The provided key count is 0
error InvalidKeyCount();
/// @notice The provided concatenated keys do not have the expected length
error InvalidKeysLength();
/// @notice The index that is removed is out of bounds
error InvalidIndexOutOfBounds();
/// @notice The value for the operator limit is too high
/// @param index The operator index
/// @param limit The new limit provided
/// @param keyCount The operator key count
error OperatorLimitTooHigh(uint256 index, uint256 limit, uint256 keyCount);
/// @notice The value for the limit is too low
/// @param index The operator index
/// @param limit The new limit provided
/// @param fundedKeyCount The operator funded key count
error OperatorLimitTooLow(uint256 index, uint256 limit, uint256 fundedKeyCount);
/// @notice The provided list of operators is not in increasing order
error UnorderedOperatorList();
/// @notice Thrown when an invalid empty stopped validator array is provided
error InvalidEmptyStoppedValidatorCountsArray();
/// @notice Thrown when the sum of stopped validators is invalid
error InvalidStoppedValidatorCountsSum();
/// @notice Throw when an element in the stopped validator array is decreasing
error StoppedValidatorCountsDecreased();
/// @notice Thrown when the number of elements in the array is too high compared to operator count
error StoppedValidatorCountsTooHigh();
/// @notice Thrown when no exit requests can be performed
error NoExitRequestsToPerform();
/// @notice The provided stopped validator count array is shrinking
error StoppedValidatorCountArrayShrinking();
/// @notice The provided stopped validator count of an operator is above its funded validator count
error StoppedValidatorCountAboveFundedCount(uint256 operatorIndex, uint32 stoppedCount, uint32 fundedCount);
/// @notice Initializes the operators registry
/// @param _admin Admin in charge of managing operators
/// @param _river Address of River system
function initOperatorsRegistryV1(address _admin, address _river) external;
/// @notice Initializes the operators registry for V1_1
function initOperatorsRegistryV1_1() external;
/// @notice Retrieve the River address
/// @return The address of River
function getRiver() external view returns (address);
/// @notice Get operator details
/// @param _index The index of the operator
/// @return The details of the operator
function getOperator(uint256 _index) external view returns (OperatorsV2.Operator memory);
/// @notice Get operator count
/// @return The operator count
function getOperatorCount() external view returns (uint256);
/// @notice Retrieve the stopped validator count for an operator index
/// @param _idx The index of the operator
/// @return The stopped validator count of the operator
function getOperatorStoppedValidatorCount(uint256 _idx) external view returns (uint32);
/// @notice Retrieve the total stopped validator count
/// @return The total stopped validator count
function getTotalStoppedValidatorCount() external view returns (uint32);
/// @notice Retrieve the total requested exit count
/// @notice This value is the amount of exit requests that have been performed, emitting an event for operators to catch
/// @return The total requested exit count
function getTotalValidatorExitsRequested() external view returns (uint256);
/// @notice Get the current exit request demand waiting to be triggered
/// @notice This value is the amount of exit requests that are demanded and not yet performed by the contract
/// @return The current exit request demand
function getCurrentValidatorExitsDemand() external view returns (uint256);
/// @notice Retrieve the total stopped and requested exit count
/// @return The total stopped count
/// @return The total requested exit count
function getStoppedAndRequestedExitCounts() external view returns (uint32, uint256);
/// @notice Retrieve the raw stopped validators array from storage
/// @return The stopped validator array
function getStoppedValidatorCountPerOperator() external view returns (uint32[] memory);
/// @notice Get the details of a validator
/// @param _operatorIndex The index of the operator
/// @param _validatorIndex The index of the validator
/// @return publicKey The public key of the validator
/// @return signature The signature used during deposit
/// @return funded True if validator has been funded
function getValidator(uint256 _operatorIndex, uint256 _validatorIndex)
external
view
returns (bytes memory publicKey, bytes memory signature, bool funded);
/// @notice Get the next validators that would be funded
/// @param _count Count of validators that would be funded next
/// @return publicKeys An array of fundable public keys
/// @return signatures An array of signatures linked to the public keys
function getNextValidatorsToDepositFromActiveOperators(uint256 _count)
external
view
returns (bytes[] memory publicKeys, bytes[] memory signatures);
/// @notice Retrieve the active operator set
/// @return The list of active operators and their details
function listActiveOperators() external view returns (OperatorsV2.Operator[] memory);
/// @notice Allows river to override the stopped validators array
/// @notice This actions happens during the Oracle report processing
/// @param _stoppedValidatorCounts The new stopped validators array
/// @param _depositedValidatorCount The total deposited validator count
function reportStoppedValidatorCounts(uint32[] calldata _stoppedValidatorCounts, uint256 _depositedValidatorCount)
external;
/// @notice Adds an operator to the registry
/// @dev Only callable by the administrator
/// @param _name The name identifying the operator
/// @param _operator The address representing the operator, receiving the rewards
/// @return The index of the new operator
function addOperator(string calldata _name, address _operator) external returns (uint256);
/// @notice Changes the operator address of an operator
/// @dev Only callable by the administrator or the previous operator address
/// @param _index The operator index
/// @param _newOperatorAddress The new address of the operator
function setOperatorAddress(uint256 _index, address _newOperatorAddress) external;
/// @notice Changes the operator name
/// @dev Only callable by the administrator or the operator
/// @param _index The operator index
/// @param _newName The new operator name
function setOperatorName(uint256 _index, string calldata _newName) external;
/// @notice Changes the operator status
/// @dev Only callable by the administrator
/// @param _index The operator index
/// @param _newStatus The new status of the operator
function setOperatorStatus(uint256 _index, bool _newStatus) external;
/// @notice Changes the operator staking limit
/// @dev Only callable by the administrator
/// @dev The operator indexes must be in increasing order and contain no duplicate
/// @dev The limit cannot exceed the total key count of the operator
/// @dev The _indexes and _newLimits must have the same length.
/// @dev Each limit value is applied to the operator index at the same index in the _indexes array.
/// @param _operatorIndexes The operator indexes, in increasing order and duplicate free
/// @param _newLimits The new staking limit of the operators
/// @param _snapshotBlock The block number at which the snapshot was computed
function setOperatorLimits(
uint256[] calldata _operatorIndexes,
uint32[] calldata _newLimits,
uint256 _snapshotBlock
) external;
/// @notice Adds new keys for an operator
/// @dev Only callable by the administrator or the operator address
/// @param _index The operator index
/// @param _keyCount The amount of keys provided
/// @param _publicKeysAndSignatures Public keys of the validator, concatenated
function addValidators(uint256 _index, uint32 _keyCount, bytes calldata _publicKeysAndSignatures) external;
/// @notice Remove validator keys
/// @dev Only callable by the administrator or the operator address
/// @dev The indexes must be provided sorted in decreasing order and duplicate-free, otherwise the method will revert
/// @dev The operator limit will be set to the lowest deleted key index if the operator's limit wasn't equal to its total key count
/// @dev The operator or the admin cannot remove funded keys
/// @dev When removing validators, the indexes of specific unfunded keys can be changed in order to properly
/// @dev remove the keys from the storage array. Beware of this specific behavior when chaining calls as the
/// @dev targeted public key indexes can point to a different key after a first call was made and performed
/// @dev some swaps
/// @param _index The operator index
/// @param _indexes The indexes of the keys to remove
function removeValidators(uint256 _index, uint256[] calldata _indexes) external;
/// @notice Retrieve validator keys based on operator statuses
/// @param _count Max amount of keys requested
/// @return publicKeys An array of public keys
/// @return signatures An array of signatures linked to the public keys
function pickNextValidatorsToDeposit(uint256 _count)
external
returns (bytes[] memory publicKeys, bytes[] memory signatures);
/// @notice Public endpoint to consume the exit request demand and perform the actual exit requests
/// @notice The selection algorithm will pick validators based on their active validator counts
/// @notice This value is computed by using the count of funded keys and taking into account the stopped validator counts and exit requests
/// @param _count Max amount of exits to request
function requestValidatorExits(uint256 _count) external;
/// @notice Increases the exit request demand
/// @dev This method is only callable by the river contract, and to actually forward the information to the node operators via event emission, the unprotected requestValidatorExits method must be called
/// @param _count The amount of exit requests to add to the demand
/// @param _depositedValidatorCount The total deposited validator count
function demandValidatorExits(uint256 _count, uint256 _depositedValidatorCount) external;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
interface IProtocolVersion {
/// @notice Retrieves the version of the contract
/// @return Version of the contract
function version() external pure returns (string memory);
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../state/redeemManager/RedeemQueue.2.sol";
import "../state/redeemManager/WithdrawalStack.sol";
/// @title Redeem Manager Interface (v1)
/// @author Alluvial Finance Inc.
/// @notice This contract handles the redeem requests of all users
interface IRedeemManagerV1 {
/// @notice Emitted when a redeem request is created
/// @param recipient The recipient of the redeem request
/// @param height The height of the redeem request in LsETH
/// @param amount The amount of the redeem request in LsETH
/// @param maxRedeemableEth The maximum amount of eth that can be redeemed from this request
/// @param id The id of the new redeem request
event RequestedRedeem(
address indexed recipient, uint256 height, uint256 amount, uint256 maxRedeemableEth, uint32 id
);
/// @notice Emitted when a withdrawal event is created
/// @param height The height of the withdrawal event in LsETH
/// @param amount The amount of the withdrawal event in LsETH
/// @param ethAmount The amount of eth to distrubute to claimers
/// @param id The id of the withdrawal event
event ReportedWithdrawal(uint256 height, uint256 amount, uint256 ethAmount, uint32 id);
/// @notice Emitted when a redeem request has been satisfied and filled (even partially) from a withdrawal event
/// @param redeemRequestId The id of the redeem request
/// @param withdrawalEventId The id of the withdrawal event used to fill the request
/// @param lsEthAmountSatisfied The amount of LsETH filled
/// @param ethAmountSatisfied The amount of ETH filled
/// @param lsEthAmountRemaining The amount of LsETH remaining
/// @param ethAmountExceeding The amount of eth added to the exceeding buffer
event SatisfiedRedeemRequest(
uint32 indexed redeemRequestId,
uint32 indexed withdrawalEventId,
uint256 lsEthAmountSatisfied,
uint256 ethAmountSatisfied,
uint256 lsEthAmountRemaining,
uint256 ethAmountExceeding
);
/// @notice Emitted when a redeem request claim has been processed and matched at least once and funds are sent to the recipient
/// @param redeemRequestId The id of the redeem request
/// @param recipient The address receiving the redeem request funds
/// @param ethAmount The amount of eth retrieved
/// @param lsEthAmount The total amount of LsETH used to redeem the eth
/// @param remainingLsEthAmount The amount of LsETH remaining
event ClaimedRedeemRequest(
uint32 indexed redeemRequestId,
address indexed recipient,
uint256 ethAmount,
uint256 lsEthAmount,
uint256 remainingLsEthAmount
);
/// @notice Emitted when the redeem demand is set
/// @param oldRedeemDemand The old redeem demand
/// @param newRedeemDemand The new redeem demand
event SetRedeemDemand(uint256 oldRedeemDemand, uint256 newRedeemDemand);
/// @notice Emitted when the River address is set
/// @param river The new river address
event SetRiver(address river);
/// @notice Thrown When a zero value is provided
error InvalidZeroAmount();
/// @notice Thrown when a transfer error occured with LsETH
error TransferError();
/// @notice Thrown when the provided arrays don't have matching lengths
error IncompatibleArrayLengths();
/// @notice Thrown when the provided redeem request id is out of bounds
/// @param id The redeem request id
error RedeemRequestOutOfBounds(uint256 id);
/// @notice Thrown when the withdrawal request id if out of bounds
/// @param id The withdrawal event id
error WithdrawalEventOutOfBounds(uint256 id);
/// @notice Thrown when the redeem request id is already claimed
/// @param id The redeem request id
error RedeemRequestAlreadyClaimed(uint256 id);
/// @notice Thrown when the redeem request and withdrawal event are not matching during claim
/// @param redeemRequestId The provided redeem request id
/// @param withdrawalEventId The provided associated withdrawal event id
error DoesNotMatch(uint256 redeemRequestId, uint256 withdrawalEventId);
/// @notice Thrown when the provided withdrawal event exceeds the redeem demand
/// @param withdrawalAmount The amount of the withdrawal event
/// @param redeemDemand The current redeem demand
error WithdrawalExceedsRedeemDemand(uint256 withdrawalAmount, uint256 redeemDemand);
/// @notice Thrown when the payment after a claim failed
/// @param recipient The recipient of the payment
/// @param rdata The revert data
error ClaimRedeemFailed(address recipient, bytes rdata);
/// @notice Thrown when the claim recipient is denied
error ClaimRecipientIsDenied();
/// @notice Thrown when the claim initiator is denied
error ClaimInitiatorIsDenied();
/// @notice Thrown when the recipient of redeemRequest is denied
error RecipientIsDenied();
/// @param _river The address of the River contract
function initializeRedeemManagerV1(address _river) external;
function initializeRedeemManagerV1_2() external;
/// @notice Retrieve River address
/// @return The address of River
function getRiver() external view returns (address);
/// @notice Retrieve the global count of redeem requests
function getRedeemRequestCount() external view returns (uint256);
/// @notice Retrieve the details of a specific redeem request
/// @param _redeemRequestId The id of the request
/// @return The redeem request details
function getRedeemRequestDetails(uint32 _redeemRequestId)
external
view
returns (RedeemQueueV2.RedeemRequest memory);
/// @notice Retrieve the global count of withdrawal events
function getWithdrawalEventCount() external view returns (uint256);
/// @notice Retrieve the details of a specific withdrawal event
/// @param _withdrawalEventId The id of the withdrawal event
/// @return The withdrawal event details
function getWithdrawalEventDetails(uint32 _withdrawalEventId)
external
view
returns (WithdrawalStack.WithdrawalEvent memory);
/// @notice Retrieve the amount of redeemed LsETH pending to be supplied with withdrawn ETH
/// @return The amount of eth in the buffer
function getBufferedExceedingEth() external view returns (uint256);
/// @notice Retrieve the amount of LsETH waiting to be exited
/// @return The amount of LsETH waiting to be exited
function getRedeemDemand() external view returns (uint256);
/// @notice Resolves the provided list of redeem request ids
/// @dev The result is an array of equal length with ids or error code
/// @dev -1 means that the request is not satisfied yet
/// @dev -2 means that the request is out of bounds
/// @dev -3 means that the request has already been claimed
/// @dev This call was created to be called by an off-chain interface, the output could then be used to perform the claimRewards call in a regular transaction
/// @param _redeemRequestIds The list of redeem requests to resolve
/// @return withdrawalEventIds The list of withdrawal events matching every redeem request (or error codes)
function resolveRedeemRequests(uint32[] calldata _redeemRequestIds)
external
view
returns (int64[] memory withdrawalEventIds);
/// @notice Creates a redeem request
/// @param _lsETHAmount The amount of LsETH to redeem
/// @param _recipient The recipient owning the redeem request
/// @return redeemRequestId The id of the redeem request
function requestRedeem(uint256 _lsETHAmount, address _recipient) external returns (uint32 redeemRequestId);
/// @notice Creates a redeem request using msg.sender as recipient
/// @param _lsETHAmount The amount of LsETH to redeem
/// @return redeemRequestId The id of the redeem request
function requestRedeem(uint256 _lsETHAmount) external returns (uint32 redeemRequestId);
/// @notice Claims the rewards of the provided redeem request ids
/// @param _redeemRequestIds The list of redeem requests to claim
/// @param _withdrawalEventIds The list of withdrawal events to use for every redeem request claim
/// @param _skipAlreadyClaimed True if the call should not revert on claiming of already claimed requests
/// @param _depth The maximum recursive depth for the resolution of the redeem requests
/// @return claimStatuses The list of claim statuses. 0 for fully claimed, 1 for partially claimed, 2 for skipped
function claimRedeemRequests(
uint32[] calldata _redeemRequestIds,
uint32[] calldata _withdrawalEventIds,
bool _skipAlreadyClaimed,
uint16 _depth
) external returns (uint8[] memory claimStatuses);
/// @notice Claims the rewards of the provided redeem request ids
/// @param _redeemRequestIds The list of redeem requests to claim
/// @param _withdrawalEventIds The list of withdrawal events to use for every redeem request claim
/// @return claimStatuses The list of claim statuses. 0 for fully claimed, 1 for partially claimed, 2 for skipped
function claimRedeemRequests(uint32[] calldata _redeemRequestIds, uint32[] calldata _withdrawalEventIds)
external
returns (uint8[] memory claimStatuses);
/// @notice Reports a withdraw event from River
/// @param _lsETHWithdrawable The amount of LsETH that can be redeemed due to this new withdraw event
function reportWithdraw(uint256 _lsETHWithdrawable) external payable;
/// @notice Pulls exceeding buffer eth
/// @param _max The maximum amount that should be pulled
function pullExceedingEth(uint256 _max) external;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../state/river/DailyCommittableLimits.sol";
import "./components/IConsensusLayerDepositManager.1.sol";
import "./components/IOracleManager.1.sol";
import "./components/ISharesManager.1.sol";
import "./components/IUserDepositManager.1.sol";
/// @title River Interface (v1)
/// @author Alluvial Finance Inc.
/// @notice The main system interface
interface IRiverV1 is IConsensusLayerDepositManagerV1, IUserDepositManagerV1, ISharesManagerV1, IOracleManagerV1 {
/// @notice Funds have been pulled from the Execution Layer Fee Recipient
/// @param amount The amount pulled
event PulledELFees(uint256 amount);
/// @notice Funds have been pulled from the Coverage Fund
/// @param amount The amount pulled
event PulledCoverageFunds(uint256 amount);
/// @notice Emitted when funds are pulled from the redeem manager
/// @param amount The amount pulled
event PulledRedeemManagerExceedingEth(uint256 amount);
/// @notice Emitted when funds are pulled from the CL recipient
/// @param pulledSkimmedEthAmount The amount of skimmed ETH pulled
/// @param pullExitedEthAmount The amount of exited ETH pulled
event PulledCLFunds(uint256 pulledSkimmedEthAmount, uint256 pullExitedEthAmount);
/// @notice The stored Execution Layer Fee Recipient has been changed
/// @param elFeeRecipient The new Execution Layer Fee Recipient
event SetELFeeRecipient(address indexed elFeeRecipient);
/// @notice The stored Coverage Fund has been changed
/// @param coverageFund The new Coverage Fund
event SetCoverageFund(address indexed coverageFund);
/// @notice The stored Collector has been changed
/// @param collector The new Collector
event SetCollector(address indexed collector);
/// @notice The stored Allowlist has been changed
/// @param allowlist The new Allowlist
event SetAllowlist(address indexed allowlist);
/// @notice The stored Global Fee has been changed
/// @param fee The new Global Fee
event SetGlobalFee(uint256 fee);
/// @notice The stored Operators Registry has been changed
/// @param operatorRegistry The new Operators Registry
event SetOperatorsRegistry(address indexed operatorRegistry);
/// @notice The stored Metadata URI string has been changed
/// @param metadataURI The new Metadata URI string
event SetMetadataURI(string metadataURI);
/// @notice The system underlying supply increased. This is a snapshot of the balances for accounting purposes
/// @param _collector The address of the collector during this event
/// @param _oldTotalUnderlyingBalance Old total ETH balance under management by River
/// @param _oldTotalSupply Old total supply in shares
/// @param _newTotalUnderlyingBalance New total ETH balance under management by River
/// @param _newTotalSupply New total supply in shares
event RewardsEarned(
address indexed _collector,
uint256 _oldTotalUnderlyingBalance,
uint256 _oldTotalSupply,
uint256 _newTotalUnderlyingBalance,
uint256 _newTotalSupply
);
/// @notice Emitted when the daily committable limits are changed
/// @param minNetAmount The minimum amount that must be used as the daily committable amount
/// @param maxRelativeAmount The maximum amount that can be used as the daily committable amount, relative to the total underlying supply
event SetMaxDailyCommittableAmounts(uint256 minNetAmount, uint256 maxRelativeAmount);
/// @notice Emitted when the redeem manager address is changed
/// @param redeemManager The address of the redeem manager
event SetRedeemManager(address redeemManager);
/// @notice Emitted when the balance to deposit is updated
/// @param oldAmount The old balance to deposit
/// @param newAmount The new balance to deposit
event SetBalanceToDeposit(uint256 oldAmount, uint256 newAmount);
/// @notice Emitted when the balance to redeem is updated
/// @param oldAmount The old balance to redeem
/// @param newAmount The new balance to redeem
event SetBalanceToRedeem(uint256 oldAmount, uint256 newAmount);
/// @notice Emitted when the balance committed to deposit
/// @param oldAmount The old balance committed to deposit
/// @param newAmount The new balance committed to deposit
event SetBalanceCommittedToDeposit(uint256 oldAmount, uint256 newAmount);
/// @notice Emitted when the redeem manager received a withdraw event report
/// @param redeemManagerDemand The total demand in LsETH of the redeem manager
/// @param suppliedRedeemManagerDemand The amount of LsETH demand actually supplied
/// @param suppliedRedeemManagerDemandInEth The amount in ETH of the supplied demand
event ReportedRedeemManager(
uint256 redeemManagerDemand, uint256 suppliedRedeemManagerDemand, uint256 suppliedRedeemManagerDemandInEth
);
/// @notice Thrown when the amount received from the Withdraw contract doe not match the requested amount
/// @param requested The amount that was requested
/// @param received The amount that was received
error InvalidPulledClFundsAmount(uint256 requested, uint256 received);
/// @notice The computed amount of shares to mint is 0
error ZeroMintedShares();
/// @notice The access was denied
/// @param account The account that was denied
error Denied(address account);
/// @notice Initializes the River system
/// @param _depositContractAddress Address to make Consensus Layer deposits
/// @param _elFeeRecipientAddress Address that receives the execution layer fees
/// @param _withdrawalCredentials Credentials to use for every validator deposit
/// @param _oracleAddress The address of the Oracle contract
/// @param _systemAdministratorAddress Administrator address
/// @param _allowlistAddress Address of the allowlist contract
/// @param _operatorRegistryAddress Address of the operator registry
/// @param _collectorAddress Address receiving the the global fee on revenue
/// @param _globalFee Amount retained when the ETH balance increases and sent to the collector
function initRiverV1(
address _depositContractAddress,
address _elFeeRecipientAddress,
bytes32 _withdrawalCredentials,
address _oracleAddress,
address _systemAdministratorAddress,
address _allowlistAddress,
address _operatorRegistryAddress,
address _collectorAddress,
uint256 _globalFee
) external;
/// @notice Initialized version 1.1 of the River System
/// @param _redeemManager The redeem manager address
/// @param _epochsPerFrame The amounts of epochs in a frame
/// @param _slotsPerEpoch The slots inside an epoch
/// @param _secondsPerSlot The seconds inside a slot
/// @param _genesisTime The genesis timestamp
/// @param _epochsToAssumedFinality The number of epochs before an epoch is considered final on-chain
/// @param _annualAprUpperBound The reporting upper bound
/// @param _relativeLowerBound The reporting lower bound
/// @param _maxDailyNetCommittableAmount_ The net daily committable limit
/// @param _maxDailyRelativeCommittableAmount_ The relative daily committable limit
function initRiverV1_1(
address _redeemManager,
uint64 _epochsPerFrame,
uint64 _slotsPerEpoch,
uint64 _secondsPerSlot,
uint64 _genesisTime,
uint64 _epochsToAssumedFinality,
uint256 _annualAprUpperBound,
uint256 _relativeLowerBound,
uint128 _maxDailyNetCommittableAmount_,
uint128 _maxDailyRelativeCommittableAmount_
) external;
/// @notice Initializes version 1.2 of the River System
function initRiverV1_2() external;
/// @notice Get the current global fee
/// @return The global fee
function getGlobalFee() external view returns (uint256);
/// @notice Retrieve the allowlist address
/// @return The allowlist address
function getAllowlist() external view returns (address);
/// @notice Retrieve the collector address
/// @return The collector address
function getCollector() external view returns (address);
/// @notice Retrieve the execution layer fee recipient
/// @return The execution layer fee recipient address
function getELFeeRecipient() external view returns (address);
/// @notice Retrieve the coverage fund
/// @return The coverage fund address
function getCoverageFund() external view returns (address);
/// @notice Retrieve the redeem manager
/// @return The redeem manager address
function getRedeemManager() external view returns (address);
/// @notice Retrieve the operators registry
/// @return The operators registry address
function getOperatorsRegistry() external view returns (address);
/// @notice Retrieve the metadata uri string value
/// @return The metadata uri string value
function getMetadataURI() external view returns (string memory);
/// @notice Retrieve the configured daily committable limits
/// @return The daily committable limits structure
function getDailyCommittableLimits()
external
view
returns (DailyCommittableLimits.DailyCommittableLimitsStruct memory);
/// @notice Resolves the provided redeem requests by calling the redeem manager
/// @param _redeemRequestIds The list of redeem requests to resolve
/// @return withdrawalEventIds The list of matching withdrawal events, or error codes
function resolveRedeemRequests(uint32[] calldata _redeemRequestIds)
external
view
returns (int64[] memory withdrawalEventIds);
/// @notice Set the daily committable limits
/// @param _dcl The Daily Committable Limits structure
function setDailyCommittableLimits(DailyCommittableLimits.DailyCommittableLimitsStruct memory _dcl) external;
/// @notice Retrieve the current balance to redeem
/// @return The current balance to redeem
function getBalanceToRedeem() external view returns (uint256);
/// @notice Performs a redeem request on the redeem manager
/// @param _lsETHAmount The amount of LsETH to redeem
/// @param _recipient The address that will own the redeem request
/// @return redeemRequestId The ID of the newly created redeem request
function requestRedeem(uint256 _lsETHAmount, address _recipient) external returns (uint32 redeemRequestId);
/// @notice Claims several redeem requests
/// @param _redeemRequestIds The list of redeem requests to claim
/// @param _withdrawalEventIds The list of resolved withdrawal event ids
/// @return claimStatuses The operation status results
function claimRedeemRequests(uint32[] calldata _redeemRequestIds, uint32[] calldata _withdrawalEventIds)
external
returns (uint8[] memory claimStatuses);
/// @notice Changes the global fee parameter
/// @param _newFee New fee value
function setGlobalFee(uint256 _newFee) external;
/// @notice Changes the allowlist address
/// @param _newAllowlist New address for the allowlist
function setAllowlist(address _newAllowlist) external;
/// @notice Changes the collector address
/// @param _newCollector New address for the collector
function setCollector(address _newCollector) external;
/// @notice Changes the execution layer fee recipient
/// @param _newELFeeRecipient New address for the recipient
function setELFeeRecipient(address _newELFeeRecipient) external;
/// @notice Changes the coverage fund
/// @param _newCoverageFund New address for the fund
function setCoverageFund(address _newCoverageFund) external;
/// @notice Sets the metadata uri string value
/// @param _metadataURI The new metadata uri string value
function setMetadataURI(string memory _metadataURI) external;
/// @notice Input for execution layer fee earnings
function sendELFees() external payable;
/// @notice Input for consensus layer funds, containing both exit and skimming
function sendCLFunds() external payable;
/// @notice Input for coverage funds
function sendCoverageFunds() external payable;
/// @notice Input for the redeem manager funds
function sendRedeemManagerExceedingFunds() external payable;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Withdraw Interface (V1)
/// @author Alluvial Finance Inc.
/// @notice This contract is in charge of holding the exit and skimming funds and allow river to pull these funds
interface IWithdrawV1 {
/// @notice Emitted when the linked River address is changed
/// @param river The new River address
event SetRiver(address river);
/// @param _river The address of the River contract
function initializeWithdrawV1(address _river) external;
/// @notice Retrieve the withdrawal credentials to use
/// @return The withdrawal credentials
function getCredentials() external view returns (bytes32);
/// @notice Retrieve the linked River address
/// @return The River address
function getRiver() external view returns (address);
/// @notice Callable by River, sends the specified amount of ETH to River
/// @param _amount The amount to pull
function pullEth(uint256 _amount) external;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../state/shared/AdministratorAddress.sol";
import "../state/shared/PendingAdministratorAddress.sol";
/// @title Lib Administrable
/// @author Alluvial Finance Inc.
/// @notice This library handles the admin and pending admin storage vars
library LibAdministrable {
/// @notice Retrieve the system admin
/// @return The address of the system admin
function _getAdmin() internal view returns (address) {
return AdministratorAddress.get();
}
/// @notice Retrieve the pending system admin
/// @return The adress of the pending system admin
function _getPendingAdmin() internal view returns (address) {
return PendingAdministratorAddress.get();
}
/// @notice Sets the system admin
/// @param _admin New system admin
function _setAdmin(address _admin) internal {
AdministratorAddress.set(_admin);
}
/// @notice Sets the pending system admin
/// @param _pendingAdmin New pending system admin
function _setPendingAdmin(address _pendingAdmin) internal {
PendingAdministratorAddress.set(_pendingAdmin);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Lib Allowlist Masks
/// @notice Holds all the mask values
library LibAllowlistMasks {
/// @notice Mask used for denied accounts
uint256 internal constant DENY_MASK = 0x1 << 255;
/// @notice The mask for the deposit right
uint256 internal constant DEPOSIT_MASK = 0x1;
/// @notice The mask for the donation right
uint256 internal constant DONATE_MASK = 0x1 << 1;
/// @notice The mask for the redeem right
uint256 internal constant REDEEM_MASK = 0x1 << 2;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Lib Basis Points
/// @notice Holds the basis points max value
library LibBasisPoints {
/// @notice The max value for basis points (represents 100%)
uint256 internal constant BASIS_POINTS_MAX = 10_000;
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Lib Bytes
/// @notice This library helps manipulating bytes
library LibBytes {
/// @notice The length overflows an uint
error SliceOverflow();
/// @notice The slice is outside of the initial bytes bounds
error SliceOutOfBounds();
/// @notice Slices the provided bytes
/// @param _bytes Bytes to slice
/// @param _start The starting index of the slice
/// @param _length The length of the slice
/// @return The slice of _bytes starting at _start of length _length
function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
unchecked {
if (_length + 31 < _length) {
revert SliceOverflow();
}
}
if (_bytes.length < _start + _length) {
revert SliceOutOfBounds();
}
bytes memory tempBytes;
// solhint-disable-next-line no-inline-assembly
assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} { mstore(mc, mload(cc)) }
mstore(tempBytes, _length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Lib Errors
/// @notice Library of common errors
library LibErrors {
/// @notice The operator is unauthorized for the caller
/// @param caller Address performing the call
error Unauthorized(address caller);
/// @notice The call was invalid
error InvalidCall();
/// @notice The argument was invalid
error InvalidArgument();
/// @notice The address is zero
error InvalidZeroAddress();
/// @notice The string is empty
error InvalidEmptyString();
/// @notice The fee is invalid
error InvalidFee();
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "./LibErrors.sol";
import "./LibBasisPoints.sol";
/// @title Lib Sanitize
/// @notice Utilities to sanitize input values
library LibSanitize {
/// @notice Reverts if address is 0
/// @param _address Address to check
function _notZeroAddress(address _address) internal pure {
if (_address == address(0)) {
revert LibErrors.InvalidZeroAddress();
}
}
/// @notice Reverts if string is empty
/// @param _string String to check
function _notEmptyString(string memory _string) internal pure {
if (bytes(_string).length == 0) {
revert LibErrors.InvalidEmptyString();
}
}
/// @notice Reverts if fee is invalid
/// @param _fee Fee to check
function _validFee(uint256 _fee) internal pure {
if (_fee > LibBasisPoints.BASIS_POINTS_MAX) {
revert LibErrors.InvalidFee();
}
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Lib Uint256
/// @notice Utilities to perform uint operations
library LibUint256 {
/// @notice Converts a value to little endian (64 bits)
/// @param _value The value to convert
/// @return result The converted value
function toLittleEndian64(uint256 _value) internal pure returns (uint256 result) {
uint256 tempValue = _value;
result = tempValue & 0xFF;
tempValue >>= 8;
result = (result << 8) | (tempValue & 0xFF);
tempValue >>= 8;
result = (result << 8) | (tempValue & 0xFF);
tempValue >>= 8;
result = (result << 8) | (tempValue & 0xFF);
tempValue >>= 8;
result = (result << 8) | (tempValue & 0xFF);
tempValue >>= 8;
result = (result << 8) | (tempValue & 0xFF);
tempValue >>= 8;
result = (result << 8) | (tempValue & 0xFF);
tempValue >>= 8;
result = (result << 8) | (tempValue & 0xFF);
tempValue >>= 8;
assert(0 == tempValue); // fully converted
result <<= (24 * 8);
}
/// @notice Returns the minimum value
/// @param _a First value
/// @param _b Second value
/// @return Smallest value between _a and _b
function min(uint256 _a, uint256 _b) internal pure returns (uint256) {
return (_a > _b ? _b : _a);
}
/// @notice Returns the max value
/// @param _a First value
/// @param _b Second value
/// @return Highest value between _a and _b
function max(uint256 _a, uint256 _b) internal pure returns (uint256) {
return (_a < _b ? _b : _a);
}
/// @notice Performs a ceiled division
/// @param _a Numerator
/// @param _b Denominator
/// @return ceil(_a / _b)
function ceil(uint256 _a, uint256 _b) internal pure returns (uint256) {
return (_a / _b) + (_a % _b > 0 ? 1 : 0);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Lib Unstructured Storage
/// @notice Utilities to work with unstructured storage
library LibUnstructuredStorage {
/// @notice Retrieve a bool value at a storage slot
/// @param _position The storage slot to retrieve
/// @return data The bool value
function getStorageBool(bytes32 _position) internal view returns (bool data) {
// solhint-disable-next-line no-inline-assembly
assembly {
data := sload(_position)
}
}
/// @notice Retrieve an address value at a storage slot
/// @param _position The storage slot to retrieve
/// @return data The address value
function getStorageAddress(bytes32 _position) internal view returns (address data) {
// solhint-disable-next-line no-inline-assembly
assembly {
data := sload(_position)
}
}
/// @notice Retrieve a bytes32 value at a storage slot
/// @param _position The storage slot to retrieve
/// @return data The bytes32 value
function getStorageBytes32(bytes32 _position) internal view returns (bytes32 data) {
// solhint-disable-next-line no-inline-assembly
assembly {
data := sload(_position)
}
}
/// @notice Retrieve an uint256 value at a storage slot
/// @param _position The storage slot to retrieve
/// @return data The uint256 value
function getStorageUint256(bytes32 _position) internal view returns (uint256 data) {
// solhint-disable-next-line no-inline-assembly
assembly {
data := sload(_position)
}
}
/// @notice Sets a bool value at a storage slot
/// @param _position The storage slot to set
/// @param _data The bool value to set
function setStorageBool(bytes32 _position, bool _data) internal {
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(_position, _data)
}
}
/// @notice Sets an address value at a storage slot
/// @param _position The storage slot to set
/// @param _data The address value to set
function setStorageAddress(bytes32 _position, address _data) internal {
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(_position, _data)
}
}
/// @notice Sets a bytes32 value at a storage slot
/// @param _position The storage slot to set
/// @param _data The bytes32 value to set
function setStorageBytes32(bytes32 _position, bytes32 _data) internal {
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(_position, _data)
}
}
/// @notice Sets an uint256 value at a storage slot
/// @param _position The storage slot to set
/// @param _data The uint256 value to set
function setStorageUint256(bytes32 _position, uint256 _data) internal {
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(_position, _data)
}
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibSanitize.sol";
/// @title Operators Storage
/// @notice Utility to manage the Operators in storage
library OperatorsV2 {
/// @notice Storage slot of the Operators
bytes32 internal constant OPERATORS_SLOT = bytes32(uint256(keccak256("river.state.v2.operators")) - 1);
/// @notice The Operator structure in storage
struct Operator {
/// @dev The following values respect this invariant:
/// @dev keys >= limit >= funded >= RequestedExits
/// @custom:attribute Staking limit of the operator
uint32 limit;
/// @custom:attribute The count of funded validators
uint32 funded;
/// @custom:attribute The count of exit requests made to this operator
uint32 requestedExits;
/// @custom:attribute The total count of keys of the operator
uint32 keys;
/// @custom attribute The block at which the last edit happened in the operator details
uint64 latestKeysEditBlockNumber;
/// @custom:attribute True if the operator is active and allowed to operate on River
bool active;
/// @custom:attribute Display name of the operator
string name;
/// @custom:attribute Address of the operator
address operator;
}
/// @notice The Operator structure when loaded in memory
struct CachedOperator {
/// @custom:attribute Staking limit of the operator
uint32 limit;
/// @custom:attribute The count of funded validators
uint32 funded;
/// @custom:attribute The count of exit requests made to this operator
uint32 requestedExits;
/// @custom:attribute The original index of the operator
uint32 index;
/// @custom:attribute The amount of picked keys, buffer used before changing funded in storage
uint32 picked;
}
/// @notice The Operator structure when loaded in memory for the exit selection
struct CachedExitableOperator {
/// @custom:attribute The count of funded validators
uint32 funded;
/// @custom:attribute The count of exit requests made to this operator
uint32 requestedExits;
/// @custom:attribute The original index of the operator
uint32 index;
/// @custom:attribute The amount of picked keys, buffer used before changing funded in storage
uint32 picked;
}
/// @notice The structure at the storage slot
struct SlotOperator {
/// @custom:attribute Array containing all the operators
Operator[] value;
}
/// @notice The operator was not found
/// @param index The provided index
error OperatorNotFound(uint256 index);
/// @notice Retrieve the operator in storage
/// @param _index The index of the operator
/// @return The Operator structure
function get(uint256 _index) internal view returns (Operator storage) {
bytes32 slot = OPERATORS_SLOT;
SlotOperator storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
if (r.value.length <= _index) {
revert OperatorNotFound(_index);
}
return r.value[_index];
}
/// @notice Retrieve the operators in storage
/// @return The Operator structure array
function getAll() internal view returns (Operator[] storage) {
bytes32 slot = OPERATORS_SLOT;
SlotOperator storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value;
}
/// @notice Retrieve the operator count in storage
/// @return The count of operators in storage
function getCount() internal view returns (uint256) {
bytes32 slot = OPERATORS_SLOT;
SlotOperator storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value.length;
}
/// @notice Retrieve all the active operators
/// @return The list of active operator structures
function getAllActive() internal view returns (Operator[] memory) {
bytes32 slot = OPERATORS_SLOT;
SlotOperator storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
uint256 activeCount = 0;
uint256 operatorCount = r.value.length;
Operator[] memory activeOperators = new Operator[](operatorCount);
for (uint256 idx = 0; idx < operatorCount;) {
if (r.value[idx].active) {
activeOperators[activeCount] = r.value[idx];
unchecked {
++activeCount;
}
}
unchecked {
++idx;
}
}
assembly ("memory-safe") {
mstore(activeOperators, activeCount)
}
return activeOperators;
}
/// @notice Retrieve the stopped validator count for an operator by its index
/// @param stoppedValidatorCounts The storage pointer to the raw array containing the stopped validator counts
/// @param index The index of the operator to lookup
/// @return The amount of stopped validators for the given operator index
function _getStoppedValidatorCountAtIndex(uint32[] storage stoppedValidatorCounts, uint256 index)
internal
view
returns (uint32)
{
if (index + 1 >= stoppedValidatorCounts.length) {
return 0;
}
return stoppedValidatorCounts[index + 1];
}
/// @notice Retrieve all the active and fundable operators
/// @dev This method will return a memory array of length equal to the number of operator, but only
/// @dev populated up to the fundable operator count, also returned by the method
/// @return The list of active and fundable operators
/// @return The count of active and fundable operators
function getAllFundable() internal view returns (CachedOperator[] memory, uint256) {
bytes32 slot = OPERATORS_SLOT;
SlotOperator storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
uint256 fundableCount = 0;
uint256 operatorCount = r.value.length;
CachedOperator[] memory fundableOperators = new CachedOperator[](operatorCount);
uint32[] storage stoppedValidatorCounts = getStoppedValidators();
for (uint256 idx = 0; idx < operatorCount;) {
if (
_hasFundableKeys(r.value[idx])
&& _getStoppedValidatorCountAtIndex(stoppedValidatorCounts, idx) >= r.value[idx].requestedExits
) {
Operator storage op = r.value[idx];
fundableOperators[fundableCount] = CachedOperator({
limit: op.limit,
funded: op.funded,
requestedExits: op.requestedExits,
index: uint32(idx),
picked: 0
});
unchecked {
++fundableCount;
}
}
unchecked {
++idx;
}
}
assembly ("memory-safe") {
mstore(fundableOperators, fundableCount)
}
return (fundableOperators, fundableCount);
}
/// @notice Retrieve all the active and exitable operators
/// @dev This method will return a memory array of length equal to the number of operator, but only
/// @dev populated up to the exitable operator count, also returned by the method
/// @return The list of active and exitable operators
/// @return The count of active and exitable operators
function getAllExitable() internal view returns (CachedExitableOperator[] memory, uint256) {
bytes32 slot = OPERATORS_SLOT;
SlotOperator storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
uint256 exitableCount = 0;
uint256 operatorCount = r.value.length;
CachedExitableOperator[] memory exitableOperators = new CachedExitableOperator[](operatorCount);
for (uint256 idx = 0; idx < operatorCount;) {
if (_hasExitableKeys(r.value[idx])) {
Operator storage op = r.value[idx];
exitableOperators[exitableCount] = CachedExitableOperator({
funded: op.funded,
requestedExits: op.requestedExits,
index: uint32(idx),
picked: 0
});
unchecked {
++exitableCount;
}
}
unchecked {
++idx;
}
}
assembly ("memory-safe") {
mstore(exitableOperators, exitableCount)
}
return (exitableOperators, exitableCount);
}
/// @notice Add a new operator in storage
/// @param _newOperator Value of the new operator
/// @return The size of the operator array after the operation
function push(Operator memory _newOperator) internal returns (uint256) {
LibSanitize._notZeroAddress(_newOperator.operator);
LibSanitize._notEmptyString(_newOperator.name);
bytes32 slot = OPERATORS_SLOT;
SlotOperator storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
r.value.push(_newOperator);
return r.value.length;
}
/// @notice Atomic operation to set the key count and update the latestKeysEditBlockNumber field at the same time
/// @param _index The operator index
/// @param _newKeys The new value for the key count
function setKeys(uint256 _index, uint32 _newKeys) internal {
Operator storage op = get(_index);
op.keys = _newKeys;
op.latestKeysEditBlockNumber = uint64(block.number);
}
/// @notice Checks if an operator is active and has fundable keys
/// @param _operator The operator details
/// @return True if active and fundable
function _hasFundableKeys(OperatorsV2.Operator memory _operator) internal pure returns (bool) {
return (_operator.active && _operator.limit > _operator.funded);
}
/// @notice Checks if an operator is active and has exitable keys
/// @param _operator The operator details
/// @return True if active and exitable
function _hasExitableKeys(OperatorsV2.Operator memory _operator) internal pure returns (bool) {
return (_operator.active && _operator.funded > _operator.requestedExits);
}
/// @notice Storage slot of the Stopped Validators
bytes32 internal constant STOPPED_VALIDATORS_SLOT = bytes32(uint256(keccak256("river.state.stoppedValidators")) - 1);
struct SlotStoppedValidators {
uint32[] value;
}
/// @notice Retrieve the storage pointer of the Stopped Validators array
/// @return The Stopped Validators storage pointer
function getStoppedValidators() internal view returns (uint32[] storage) {
bytes32 slot = STOPPED_VALIDATORS_SLOT;
SlotStoppedValidators storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value;
}
/// @notice Sets the entire stopped validators array
/// @param value The new stopped validators array
function setRawStoppedValidators(uint32[] memory value) internal {
bytes32 slot = STOPPED_VALIDATORS_SLOT;
SlotStoppedValidators storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
r.value = value;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Redeem Manager Redeem Queue storage
/// @notice Utility to manage the Redeem Queue in the Redeem Manager
library RedeemQueueV2 {
/// @notice Storage slot of the Redeem Queue
bytes32 internal constant REDEEM_QUEUE_ID_SLOT = bytes32(uint256(keccak256("river.state.redeemQueue")) - 1);
/// @notice The updated V1_2 Redeemer structure represents the redeem request made by a user
struct RedeemRequest {
/// @custom:attribute The amount of the redeem request in LsETH
uint256 amount;
/// @custom:attribute The maximum amount of ETH redeemable by this request
uint256 maxRedeemableEth;
/// @custom:attribute The recipient of the redeem request
address recipient;
/// @custom:attribute The height is the cumulative sum of all the sizes of preceding redeem requests
uint256 height;
/// @custom:attribute The initiator of the redeem request
address initiator;
}
/// @notice Retrieve the Redeem Queue array storage pointer
/// @return data The Redeem Queue array storage pointer
function get() internal pure returns (RedeemRequest[] storage data) {
bytes32 position = REDEEM_QUEUE_ID_SLOT;
assembly {
data.slot := position
}
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Redeem Manager Withdrawal Stack storage
/// @notice Utility to manage the Withdrawal Stack in the Redeem Manager
library WithdrawalStack {
/// @notice Storage slot of the Withdrawal Stack
bytes32 internal constant WITHDRAWAL_STACK_ID_SLOT = bytes32(uint256(keccak256("river.state.withdrawalStack")) - 1);
/// @notice The Redeemer structure represents the withdrawal events made by River
struct WithdrawalEvent {
/// @custom:attribute The amount of the withdrawal event in LsETH
uint256 amount;
/// @custom:attribute The amount of the withdrawal event in ETH
uint256 withdrawnEth;
/// @custom:attribute The height is the cumulative sum of all the sizes of preceding withdrawal events
uint256 height;
}
/// @notice Retrieve the Withdrawal Stack array storage pointer
/// @return data The Withdrawal Stack array storage pointer
function get() internal pure returns (WithdrawalEvent[] storage data) {
bytes32 position = WITHDRAWAL_STACK_ID_SLOT;
assembly {
data.slot := position
}
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
import "../../libraries/LibSanitize.sol";
/// @title Allowlist Address Storage
/// @notice Utility to manage the Allowlist Address in storage
library AllowlistAddress {
/// @notice Storage slot of the Allowlist Address
bytes32 internal constant ALLOWLIST_ADDRESS_SLOT = bytes32(uint256(keccak256("river.state.allowlistAddress")) - 1);
/// @notice Retrieve the Allowlist Address
/// @return The Allowlist Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(ALLOWLIST_ADDRESS_SLOT);
}
/// @notice Sets the Allowlist Address
/// @param _newValue New Allowlist Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(ALLOWLIST_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
library BalanceToDeposit {
bytes32 internal constant BALANCE_TO_DEPOSIT_SLOT = bytes32(uint256(keccak256("river.state.balanceToDeposit")) - 1);
function get() internal view returns (uint256) {
return LibUnstructuredStorage.getStorageUint256(BALANCE_TO_DEPOSIT_SLOT);
}
function set(uint256 newValue) internal {
LibUnstructuredStorage.setStorageUint256(BALANCE_TO_DEPOSIT_SLOT, newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
library BalanceToRedeem {
bytes32 internal constant BALANCE_TO_REDEEM_SLOT = bytes32(uint256(keccak256("river.state.balanceToRedeem")) - 1);
function get() internal view returns (uint256) {
return LibUnstructuredStorage.getStorageUint256(BALANCE_TO_REDEEM_SLOT);
}
function set(uint256 newValue) internal {
LibUnstructuredStorage.setStorageUint256(BALANCE_TO_REDEEM_SLOT, newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Consensus Layer Spec Storage
/// @notice Utility to manage the Consensus Layer Spec in storage
library CLSpec {
/// @notice Storage slot of the Consensus Layer Spec
bytes32 internal constant CL_SPEC_SLOT = bytes32(uint256(keccak256("river.state.clSpec")) - 1);
/// @notice The Consensus Layer Spec structure
struct CLSpecStruct {
/// @custom:attribute The count of epochs per frame, 225 means 24h
uint64 epochsPerFrame;
/// @custom:attribute The count of slots in an epoch (32 on mainnet)
uint64 slotsPerEpoch;
/// @custom:attribute The seconds in a slot (12 on mainnet)
uint64 secondsPerSlot;
/// @custom:attribute The block timestamp of the first consensus layer block
uint64 genesisTime;
/// @custom:attribute The count of epochs before considering an epoch final on-chain
uint64 epochsToAssumedFinality;
}
/// @notice The structure in storage
struct Slot {
/// @custom:attribute The structure in storage
CLSpecStruct value;
}
/// @notice Retrieve the Consensus Layer Spec from storage
/// @return The Consensus Layer Spec
function get() internal view returns (CLSpecStruct memory) {
bytes32 slot = CL_SPEC_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value;
}
/// @notice Set the Consensus Layer Spec value in storage
/// @param _newCLSpec The new value to set in storage
function set(CLSpecStruct memory _newCLSpec) internal {
bytes32 slot = CL_SPEC_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
r.value = _newCLSpec;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Consensus Layer Validator Count Storage
/// @notice Utility to manage the Consensus Layer Validator Count in storage
/// @notice This state variable is deprecated and was kept due to migration logic needs
library CLValidatorCount {
/// @notice Storage slot of the Consensus Layer Validator Count
bytes32 internal constant CL_VALIDATOR_COUNT_SLOT = bytes32(uint256(keccak256("river.state.clValidatorCount")) - 1);
/// @notice Retrieve the Consensus Layer Validator Count
/// @return The Consensus Layer Validator Count
function get() internal view returns (uint256) {
return LibUnstructuredStorage.getStorageUint256(CL_VALIDATOR_COUNT_SLOT);
}
/// @notice Sets the Consensus Layer Validator Count
/// @param _newValue New Consensus Layer Validator Count
function set(uint256 _newValue) internal {
LibUnstructuredStorage.setStorageUint256(CL_VALIDATOR_COUNT_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Consensus Layer Validator Total Balance Storage
/// @notice Utility to manage the Consensus Layer Validator Total Balance in storage
/// @notice This state variable is deprecated and was kept due to migration logic needs
library CLValidatorTotalBalance {
/// @notice Storage slot of the Consensus Layer Validator Total Balance
bytes32 internal constant CL_VALIDATOR_TOTAL_BALANCE_SLOT =
bytes32(uint256(keccak256("river.state.clValidatorTotalBalance")) - 1);
/// @notice Retrieve the Consensus Layer Validator Total Balance
/// @return The Consensus Layer Validator Total Balance
function get() internal view returns (uint256) {
return LibUnstructuredStorage.getStorageUint256(CL_VALIDATOR_TOTAL_BALANCE_SLOT);
}
/// @notice Sets the Consensus Layer Validator Total Balance
/// @param _newValue New Consensus Layer Validator Total Balance
function set(uint256 _newValue) internal {
LibUnstructuredStorage.setStorageUint256(CL_VALIDATOR_TOTAL_BALANCE_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
import "../../libraries/LibSanitize.sol";
/// @title Collector Address Storage
/// @notice Utility to manage the Collector Address in storage
library CollectorAddress {
/// @notice Storage slot of the Collector Address
bytes32 internal constant COLLECTOR_ADDRESS_SLOT = bytes32(uint256(keccak256("river.state.collectorAddress")) - 1);
/// @notice Retrieve the Collector Address
/// @return The Collector Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(COLLECTOR_ADDRESS_SLOT);
}
/// @notice Sets the Collector Address
/// @param _newValue New Collector Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(COLLECTOR_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
library CommittedBalance {
bytes32 internal constant COMMITTED_BALANCE_SLOT = bytes32(uint256(keccak256("river.state.committedBalance")) - 1);
function get() internal view returns (uint256) {
return LibUnstructuredStorage.getStorageUint256(COMMITTED_BALANCE_SLOT);
}
function set(uint256 newValue) internal {
LibUnstructuredStorage.setStorageUint256(COMMITTED_BALANCE_SLOT, newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
import "../../libraries/LibSanitize.sol";
/// @title Coverage Fund Address Storage
/// @notice Utility to manage the Coverage Fund Address in storage
library CoverageFundAddress {
/// @notice Storage slot of the Coverage Fund Address
bytes32 internal constant COVERAGE_FUND_ADDRESS_SLOT =
bytes32(uint256(keccak256("river.state.coverageFundAddress")) - 1);
/// @notice Retrieve the Coverage Fund Address
/// @return The Coverage Fund Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(COVERAGE_FUND_ADDRESS_SLOT);
}
/// @notice Sets the Coverage Fund Address
/// @param _newValue New Coverage Fund Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(COVERAGE_FUND_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibSanitize.sol";
/// @title Daily Committable Limits storage
/// @notice Utility to manage the Daily Committable Limits in storage
library DailyCommittableLimits {
/// @notice Storage slot of the Daily Committable Limits storage
bytes32 internal constant DAILY_COMMITTABLE_LIMITS_SLOT =
bytes32(uint256(keccak256("river.state.dailyCommittableLimits")) - 1);
/// @notice The daily committable limits structure
struct DailyCommittableLimitsStruct {
uint128 minDailyNetCommittableAmount;
uint128 maxDailyRelativeCommittableAmount;
}
/// @notice The structure in storage
struct Slot {
/// @custom:attribute The structure in storage
DailyCommittableLimitsStruct value;
}
/// @notice Retrieve the Daily Committable Limits from storage
/// @return The Daily Committable Limits
function get() internal view returns (DailyCommittableLimitsStruct memory) {
bytes32 slot = DAILY_COMMITTABLE_LIMITS_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value;
}
/// @notice Set the Daily Committable Limits value in storage
/// @param _newValue The new value to set in storage
function set(DailyCommittableLimitsStruct memory _newValue) internal {
bytes32 slot = DAILY_COMMITTABLE_LIMITS_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
r.value = _newValue;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibSanitize.sol";
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Deposit Contract Address Storage
/// @notice Utility to manage the Deposit Contract Address in storage
library DepositContractAddress {
/// @notice Storage slot of the Deposit Contract Address
bytes32 internal constant DEPOSIT_CONTRACT_ADDRESS_SLOT =
bytes32(uint256(keccak256("river.state.depositContractAddress")) - 1);
/// @notice Retrieve the Deposit Contract Address
/// @return The Deposit Contract Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(DEPOSIT_CONTRACT_ADDRESS_SLOT);
}
/// @notice Sets the Deposit Contract Address
/// @param _newValue New Deposit Contract Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(DEPOSIT_CONTRACT_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Deposited Validator Count Storage
/// @notice Utility to manage the Deposited Validator Count in storage
library DepositedValidatorCount {
/// @notice Storage slot of the Deposited Validator Count
bytes32 internal constant DEPOSITED_VALIDATOR_COUNT_SLOT =
bytes32(uint256(keccak256("river.state.depositedValidatorCount")) - 1);
/// @notice Retrieve the Deposited Validator Count
/// @return The Deposited Validator Count
function get() internal view returns (uint256) {
return LibUnstructuredStorage.getStorageUint256(DEPOSITED_VALIDATOR_COUNT_SLOT);
}
/// @notice Sets the Deposited Validator Count
/// @param _newValue New Deposited Validator Count
function set(uint256 _newValue) internal {
LibUnstructuredStorage.setStorageUint256(DEPOSITED_VALIDATOR_COUNT_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
import "../../libraries/LibSanitize.sol";
/// @title Execution Layer Fee Recipient Address Storage
/// @notice Utility to manage the Execution Layer Fee Recipient Address in storage
library ELFeeRecipientAddress {
/// @notice Storage slot of the Execution Layer Fee Recipient Address
bytes32 internal constant EL_FEE_RECIPIENT_ADDRESS =
bytes32(uint256(keccak256("river.state.elFeeRecipientAddress")) - 1);
/// @notice Retrieve the Execution Layer Fee Recipient Address
/// @return The Execution Layer Fee Recipient Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(EL_FEE_RECIPIENT_ADDRESS);
}
/// @notice Sets the Execution Layer Fee Recipient Address
/// @param _newValue New Execution Layer Fee Recipient Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(EL_FEE_RECIPIENT_ADDRESS, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibSanitize.sol";
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Global Fee Storage
/// @notice Utility to manage the Global Fee in storage
library GlobalFee {
/// @notice Storage slot of the Global Fee
bytes32 internal constant GLOBAL_FEE_SLOT = bytes32(uint256(keccak256("river.state.globalFee")) - 1);
/// @notice Retrieve the Global Fee
/// @return The Global Fee
function get() internal view returns (uint256) {
return LibUnstructuredStorage.getStorageUint256(GLOBAL_FEE_SLOT);
}
/// @notice Sets the Global Fee
/// @param _newValue New Global Fee
function set(uint256 _newValue) internal {
LibSanitize._validFee(_newValue);
LibUnstructuredStorage.setStorageUint256(GLOBAL_FEE_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
import "../../libraries/LibSanitize.sol";
/// @title Keeper Address Storage
/// @notice Utility to manage the Keeper Address in storage
library KeeperAddress {
/// @notice Storage slot of the Keeper Address
bytes32 internal constant KEEPER_ADDRESS_SLOT = bytes32(uint256(keccak256("river.state.KeeperAddress")) - 1);
/// @notice Retrieve the Keeper Address
/// @return The Keeper Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(KEEPER_ADDRESS_SLOT);
}
/// @notice Sets the Keeper Address
/// @param _newValue New Keeper Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(KEEPER_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../interfaces/components/IOracleManager.1.sol";
/// @title Last Consensus Layer Report Storage
/// @notice Utility to manage the Last Consensus Layer Report in storage
library LastConsensusLayerReport {
/// @notice Storage slot of the Last Consensus Layer Report
bytes32 internal constant LAST_CONSENSUS_LAYER_REPORT_SLOT =
bytes32(uint256(keccak256("river.state.lastConsensusLayerReport")) - 1);
/// @notice The structure in storage
struct Slot {
/// @custom:attribute The structure in storage
IOracleManagerV1.StoredConsensusLayerReport value;
}
/// @notice Retrieve the Last Consensus Layer Report from storage
/// @return The Last Consensus Layer Report
function get() internal view returns (IOracleManagerV1.StoredConsensusLayerReport storage) {
bytes32 slot = LAST_CONSENSUS_LAYER_REPORT_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value;
}
/// @notice Set the Last Consensus Layer Report value in storage
/// @param _newValue The new value to set in storage
function set(IOracleManagerV1.StoredConsensusLayerReport memory _newValue) internal {
bytes32 slot = LAST_CONSENSUS_LAYER_REPORT_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
r.value = _newValue;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Last Oracle Round Id Storage
/// @notice Utility to manage the Last Oracle Round Id in storage
/// @notice This state variable is deprecated and was kept due to migration logic needs
library LastOracleRoundId {
/// @notice Storage slot of the Last Oracle Round Id
bytes32 internal constant LAST_ORACLE_ROUND_ID_SLOT =
bytes32(uint256(keccak256("river.state.lastOracleRoundId")) - 1);
/// @notice Retrieve the Last Oracle Round Id
/// @return The Last Oracle Round Id
function get() internal view returns (bytes32) {
return LibUnstructuredStorage.getStorageBytes32(LAST_ORACLE_ROUND_ID_SLOT);
}
/// @notice Sets the Last Oracle Round Id
/// @param _newValue New Last Oracle Round Id
function set(bytes32 _newValue) internal {
LibUnstructuredStorage.setStorageBytes32(LAST_ORACLE_ROUND_ID_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Metadata URI Storage
/// @notice Utility to manage the Metadata in storage
library MetadataURI {
/// @notice Storage slot of the Metadata URI
bytes32 internal constant METADATA_URI_SLOT = bytes32(uint256(keccak256("river.state.metadataUri")) - 1);
/// @notice Structure in storage
struct Slot {
/// @custom:attribute The metadata value
string value;
}
/// @notice Retrieve the metadata URI
/// @return The metadata URI string
function get() internal view returns (string memory) {
bytes32 slot = METADATA_URI_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value;
}
/// @notice Set the metadata URI value
/// @param _newValue The new metadata URI value
function set(string memory _newValue) internal {
bytes32 slot = METADATA_URI_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
r.value = _newValue;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
import "../../libraries/LibSanitize.sol";
/// @title Operators Registry Address Storage
/// @notice Utility to manage the Operators Registry Address in storage
library OperatorsRegistryAddress {
/// @notice Storage slot of the Operators Registry Address
bytes32 internal constant OPERATORS_REGISTRY_ADDRESS_SLOT =
bytes32(uint256(keccak256("river.state.operatorsRegistryAddress")) - 1);
/// @notice Retrieve the Operators Registry Address
/// @return The Operators Registry Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(OPERATORS_REGISTRY_ADDRESS_SLOT);
}
/// @notice Sets the Operators Registry Address
/// @param _newValue New Operators Registry Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(OPERATORS_REGISTRY_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
import "../../libraries/LibSanitize.sol";
/// @title Oracle Address Storage
/// @notice Utility to manage the Oracle Address in storage
library OracleAddress {
/// @notice Storage slot of the Oracle Address
bytes32 internal constant ORACLE_ADDRESS_SLOT = bytes32(uint256(keccak256("river.state.oracleAddress")) - 1);
/// @notice Retrieve the Oracle Address
/// @return The Oracle Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(ORACLE_ADDRESS_SLOT);
}
/// @notice Sets the Oracle Address
/// @param _newValue New Oracle Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(ORACLE_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
import "../../libraries/LibSanitize.sol";
/// @title Redeem Manager Address Storage
/// @notice Utility to manage the Redeem Manager Address in storage
library RedeemManagerAddress {
/// @notice Storage slot of the Redeem Manager Address
bytes32 internal constant REDEEM_MANAGER_ADDRESS_SLOT =
bytes32(uint256(keccak256("river.state.redeemManagerAddress")) - 1);
/// @notice Retrieve the Redeem Manager Address
/// @return The Redeem Manager Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(REDEEM_MANAGER_ADDRESS_SLOT);
}
/// @notice Sets the Redeem Manager Address
/// @param _newValue New Redeem Manager Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(REDEEM_MANAGER_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Report Bounds Storage
/// @notice Utility to manage the Report Bounds in storage
library ReportBounds {
/// @notice Storage slot of the Report Bounds
bytes32 internal constant REPORT_BOUNDS_SLOT = bytes32(uint256(keccak256("river.state.reportBounds")) - 1);
/// @notice The Report Bounds structure
struct ReportBoundsStruct {
/// @custom:attribute The maximum allowed annual apr, checked before submitting a report to River
uint256 annualAprUpperBound;
/// @custom:attribute The maximum allowed balance decrease, also checked before submitting a report to River
uint256 relativeLowerBound;
}
/// @notice The structure in storage
struct Slot {
/// @custom:attribute The structure in storage
ReportBoundsStruct value;
}
/// @notice Retrieve the Report Bounds from storage
/// @return The Report Bounds
function get() internal view returns (ReportBoundsStruct memory) {
bytes32 slot = REPORT_BOUNDS_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value;
}
/// @notice Set the Report Bounds in storage
/// @param _newReportBounds The new Report Bounds value
function set(ReportBoundsStruct memory _newReportBounds) internal {
bytes32 slot = REPORT_BOUNDS_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
r.value = _newReportBounds;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Shares Count Storage
/// @notice Utility to manage the Shares Count in storage
library Shares {
/// @notice Storage slot of the Shares Count
bytes32 internal constant SHARES_SLOT = bytes32(uint256(keccak256("river.state.shares")) - 1);
/// @notice Retrieve the Shares Count
/// @return The Shares Count
function get() internal view returns (uint256) {
return LibUnstructuredStorage.getStorageUint256(SHARES_SLOT);
}
/// @notice Sets the Shares Count
/// @param _newValue New Shares Count
function set(uint256 _newValue) internal {
LibUnstructuredStorage.setStorageUint256(SHARES_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Shares Per Owner Storage
/// @notice Utility to manage the Shares Per Owner in storage
library SharesPerOwner {
/// @notice Storage slot of the Shares Per Owner
bytes32 internal constant SHARES_PER_OWNER_SLOT = bytes32(uint256(keccak256("river.state.sharesPerOwner")) - 1);
/// @notice Structure in storage
struct Slot {
/// @custom:attribute The mapping from an owner to its share count
mapping(address => uint256) value;
}
/// @notice Retrieve the share count for given owner
/// @param _owner The address to get the balance of
/// @return The amount of shares
function get(address _owner) internal view returns (uint256) {
bytes32 slot = SHARES_PER_OWNER_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value[_owner];
}
/// @notice Set the amount of shares for an owner
/// @param _owner The owner of the shares to edit
/// @param _newValue The new shares value for the owner
function set(address _owner, uint256 _newValue) internal {
bytes32 slot = SHARES_PER_OWNER_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
r.value[_owner] = _newValue;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibErrors.sol";
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Withdrawal Credentials Storage
/// @notice Utility to manage the Withdrawal Credentials in storage
library WithdrawalCredentials {
/// @notice Storage slot of the Withdrawal Credentials
bytes32 internal constant WITHDRAWAL_CREDENTIALS_SLOT =
bytes32(uint256(keccak256("river.state.withdrawalCredentials")) - 1);
/// @notice Retrieve the Withdrawal Credentials
/// @return The Withdrawal Credentials
function get() internal view returns (bytes32) {
return LibUnstructuredStorage.getStorageBytes32(WITHDRAWAL_CREDENTIALS_SLOT);
}
/// @notice Retrieve the Withdrawal Credential under its address format
/// @return The Withdrawal Credentials in its address format
function getAddress() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(WITHDRAWAL_CREDENTIALS_SLOT);
}
/// @notice Sets the Withdrawal Credentials
/// @param _newValue New Withdrawal Credentials
function set(bytes32 _newValue) internal {
if (_newValue == bytes32(0)) {
revert LibErrors.InvalidArgument();
}
LibUnstructuredStorage.setStorageBytes32(WITHDRAWAL_CREDENTIALS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
import "../../libraries/LibSanitize.sol";
/// @title Administrator Address Storage
/// @notice Utility to manage the Administrator Address in storage
library AdministratorAddress {
/// @notice Storage slot of the Administrator Address
bytes32 public constant ADMINISTRATOR_ADDRESS_SLOT =
bytes32(uint256(keccak256("river.state.administratorAddress")) - 1);
/// @notice Retrieve the Administrator Address
/// @return The Administrator Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(ADMINISTRATOR_ADDRESS_SLOT);
}
/// @notice Sets the Administrator Address
/// @param _newValue New Administrator Address
function set(address _newValue) internal {
LibSanitize._notZeroAddress(_newValue);
LibUnstructuredStorage.setStorageAddress(ADMINISTRATOR_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/// @title Approvals Per Owner Storage
/// @notice Utility to manage the Approvals Per Owner in storage
library ApprovalsPerOwner {
/// @notice Storage slot of the Approvals Per Owner
bytes32 internal constant APPROVALS_PER_OWNER_SLOT =
bytes32(uint256(keccak256("river.state.approvalsPerOwner")) - 1);
/// @notice The structure in storage
struct Slot {
/// @custom:attribute The mapping from an owner to an operator to the approval amount
mapping(address => mapping(address => uint256)) value;
}
/// @notice Retrieve the approval for an owner to an operator
/// @param _owner The account that gave the approval
/// @param _operator The account receiving the approval
/// @return The value of the approval
function get(address _owner, address _operator) internal view returns (uint256) {
bytes32 slot = APPROVALS_PER_OWNER_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
return r.value[_owner][_operator];
}
/// @notice Set the approval value for an owner to an operator
/// @param _owner The account that gives the approval
/// @param _operator The account receiving the approval
/// @param _newValue The value of the approval
function set(address _owner, address _operator, uint256 _newValue) internal {
bytes32 slot = APPROVALS_PER_OWNER_SLOT;
Slot storage r;
// solhint-disable-next-line no-inline-assembly
assembly {
r.slot := slot
}
r.value[_owner][_operator] = _newValue;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Pending Administrator Address Storage
/// @notice Utility to manage the Pending Administrator Address in storage
library PendingAdministratorAddress {
/// @notice Storage slot of the Pending Administrator Address
bytes32 public constant PENDING_ADMINISTRATOR_ADDRESS_SLOT =
bytes32(uint256(keccak256("river.state.pendingAdministratorAddress")) - 1);
/// @notice Retrieve the Pending Administrator Address
/// @return The Pending Administrator Address
function get() internal view returns (address) {
return LibUnstructuredStorage.getStorageAddress(PENDING_ADMINISTRATOR_ADDRESS_SLOT);
}
/// @notice Sets the Pending Administrator Address
/// @param _newValue New Pending Administrator Address
function set(address _newValue) internal {
LibUnstructuredStorage.setStorageAddress(PENDING_ADMINISTRATOR_ADDRESS_SLOT, _newValue);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../../libraries/LibUnstructuredStorage.sol";
/// @title Version Storage
/// @notice Utility to manage the Version in storage
library Version {
/// @notice Storage slot of the Version
bytes32 public constant VERSION_SLOT = bytes32(uint256(keccak256("river.state.version")) - 1);
/// @notice Retrieve the Version
/// @return The Version
function get() internal view returns (uint256) {
return LibUnstructuredStorage.getStorageUint256(VERSION_SLOT);
}
/// @notice Sets the Version
/// @param _newValue New Version
function set(uint256 _newValue) internal {
LibUnstructuredStorage.setStorageUint256(VERSION_SLOT, _newValue);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}{
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 100
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"uint256","name":"_allowance","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"AllowanceTooLow","type":"error"},{"inputs":[],"name":"BalanceTooLow","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"Denied","type":"error"},{"inputs":[],"name":"EmptyDeposit","type":"error"},{"inputs":[],"name":"ErrorOnDeposit","type":"error"},{"inputs":[],"name":"InconsistentPublicKeys","type":"error"},{"inputs":[],"name":"InconsistentSignatures","type":"error"},{"inputs":[],"name":"InvalidArgument","type":"error"},{"inputs":[],"name":"InvalidCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentValidatorsExitedBalance","type":"uint256"},{"internalType":"uint256","name":"newValidatorsExitedBalance","type":"uint256"}],"name":"InvalidDecreasingValidatorsExitedBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentValidatorsSkimmedBalance","type":"uint256"},{"internalType":"uint256","name":"newValidatorsSkimmedBalance","type":"uint256"}],"name":"InvalidDecreasingValidatorsSkimmedBalance","type":"error"},{"inputs":[],"name":"InvalidDepositRoot","type":"error"},{"inputs":[],"name":"InvalidEmptyString","type":"error"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"InvalidEpoch","type":"error"},{"inputs":[],"name":"InvalidFee","type":"error"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"expectedVersion","type":"uint256"}],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidPublicKeyCount","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"received","type":"uint256"}],"name":"InvalidPulledClFundsAmount","type":"error"},{"inputs":[],"name":"InvalidSignatureCount","type":"error"},{"inputs":[{"internalType":"uint256","name":"providedValidatorCount","type":"uint256"},{"internalType":"uint256","name":"depositedValidatorCount","type":"uint256"},{"internalType":"uint256","name":"lastReportedValidatorCount","type":"uint256"}],"name":"InvalidValidatorCountReport","type":"error"},{"inputs":[],"name":"InvalidWithdrawalCredentials","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[],"name":"NoAvailableValidatorKeys","type":"error"},{"inputs":[],"name":"NotEnoughFunds","type":"error"},{"inputs":[],"name":"NullTransfer","type":"error"},{"inputs":[],"name":"OnlyKeeper","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"SliceOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"prevTotalEthIncludingExited","type":"uint256"},{"internalType":"uint256","name":"postTotalEthIncludingExited","type":"uint256"},{"internalType":"uint256","name":"timeElapsed","type":"uint256"},{"internalType":"uint256","name":"relativeLowerBound","type":"uint256"}],"name":"TotalValidatorBalanceDecreaseOutOfBound","type":"error"},{"inputs":[{"internalType":"uint256","name":"prevTotalEthIncludingExited","type":"uint256"},{"internalType":"uint256","name":"postTotalEthIncludingExited","type":"uint256"},{"internalType":"uint256","name":"timeElapsed","type":"uint256"},{"internalType":"uint256","name":"annualAprUpperBound","type":"uint256"}],"name":"TotalValidatorBalanceIncreaseOutOfBound","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"UnauthorizedTransfer","type":"error"},{"inputs":[],"name":"ZeroMintedShares","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"validatorCount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"validatorTotalBalance","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"roundId","type":"bytes32"}],"name":"ConsensusLayerDataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"Initialize","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorsBalance","type":"uint256"},{"internalType":"uint256","name":"validatorsSkimmedBalance","type":"uint256"},{"internalType":"uint256","name":"validatorsExitedBalance","type":"uint256"},{"internalType":"uint256","name":"validatorsExitingBalance","type":"uint256"},{"internalType":"uint32","name":"validatorsCount","type":"uint32"},{"internalType":"uint32[]","name":"stoppedValidatorCountPerOperator","type":"uint32[]"},{"internalType":"bool","name":"rebalanceDepositToRedeemMode","type":"bool"},{"internalType":"bool","name":"slashingContainmentMode","type":"bool"}],"indexed":false,"internalType":"struct IOracleManagerV1.ConsensusLayerReport","name":"report","type":"tuple"},{"components":[{"internalType":"uint256","name":"rewards","type":"uint256"},{"internalType":"uint256","name":"pulledELFees","type":"uint256"},{"internalType":"uint256","name":"pulledRedeemManagerExceedingEthBuffer","type":"uint256"},{"internalType":"uint256","name":"pulledCoverageFunds","type":"uint256"}],"indexed":false,"internalType":"struct IOracleManagerV1.ConsensusLayerDataReportingTrace","name":"trace","type":"tuple"}],"name":"ProcessedConsensusLayerReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pulledSkimmedEthAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pullExitedEthAmount","type":"uint256"}],"name":"PulledCLFunds","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PulledCoverageFunds","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PulledELFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PulledRedeemManagerExceedingEth","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"redeemManagerDemand","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"suppliedRedeemManagerDemand","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"suppliedRedeemManagerDemandInEth","type":"uint256"}],"name":"ReportedRedeemManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_collector","type":"address"},{"indexed":false,"internalType":"uint256","name":"_oldTotalUnderlyingBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_oldTotalSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newTotalUnderlyingBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newTotalSupply","type":"uint256"}],"name":"RewardsEarned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"allowlist","type":"address"}],"name":"SetAllowlist","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAmount","type":"uint256"}],"name":"SetBalanceCommittedToDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAmount","type":"uint256"}],"name":"SetBalanceToDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAmount","type":"uint256"}],"name":"SetBalanceToRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"annualAprUpperBound","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"relativeLowerBound","type":"uint256"}],"name":"SetBounds","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collector","type":"address"}],"name":"SetCollector","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"coverageFund","type":"address"}],"name":"SetCoverageFund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositContract","type":"address"}],"name":"SetDepositContractAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldDepositedValidatorCount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDepositedValidatorCount","type":"uint256"}],"name":"SetDepositedValidatorCount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"elFeeRecipient","type":"address"}],"name":"SetELFeeRecipient","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SetGlobalFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minNetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxRelativeAmount","type":"uint256"}],"name":"SetMaxDailyCommittableAmounts","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"metadataURI","type":"string"}],"name":"SetMetadataURI","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operatorRegistry","type":"address"}],"name":"SetOperatorsRegistry","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oracleAddress","type":"address"}],"name":"SetOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingAdmin","type":"address"}],"name":"SetPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"redeemManager","type":"address"}],"name":"SetRedeemManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"epochsPerFrame","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"slotsPerEpoch","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"secondsPerSlot","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"genesisTime","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"epochsToAssumedFinality","type":"uint64"}],"name":"SetSpec","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalSupply","type":"uint256"}],"name":"SetTotalSupply","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"withdrawalCredentials","type":"bytes32"}],"name":"SetWithdrawalCredentials","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UserDeposit","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"DEPOSIT_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PUBLIC_KEY_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SIGNATURE_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_DEPOSIT_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"_redeemRequestIds","type":"uint32[]"},{"internalType":"uint32[]","name":"_withdrawalEventIds","type":"uint32[]"}],"name":"claimRedeemRequests","outputs":[{"internalType":"uint8[]","name":"claimStatuses","type":"uint8[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_subtractableValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"depositAndTransfer","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxCount","type":"uint256"},{"internalType":"bytes32","name":"_depositRoot","type":"bytes32"}],"name":"depositToConsensusLayerWithDepositRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllowlist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalanceToDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalanceToRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCLSpec","outputs":[{"components":[{"internalType":"uint64","name":"epochsPerFrame","type":"uint64"},{"internalType":"uint64","name":"slotsPerEpoch","type":"uint64"},{"internalType":"uint64","name":"secondsPerSlot","type":"uint64"},{"internalType":"uint64","name":"genesisTime","type":"uint64"},{"internalType":"uint64","name":"epochsToAssumedFinality","type":"uint64"}],"internalType":"struct CLSpec.CLSpecStruct","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCLValidatorCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCLValidatorTotalBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCommittedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCoverageFund","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpochId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentFrame","outputs":[{"internalType":"uint256","name":"_startEpochId","type":"uint256"},{"internalType":"uint256","name":"_startTime","type":"uint256"},{"internalType":"uint256","name":"_endTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDailyCommittableLimits","outputs":[{"components":[{"internalType":"uint128","name":"minDailyNetCommittableAmount","type":"uint128"},{"internalType":"uint128","name":"maxDailyRelativeCommittableAmount","type":"uint128"}],"internalType":"struct DailyCommittableLimits.DailyCommittableLimitsStruct","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDepositedValidatorCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getELFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExpectedEpochId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epochId","type":"uint256"}],"name":"getFrameFirstEpochId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getKeeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastCompletedEpochId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastConsensusLayerReport","outputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorsBalance","type":"uint256"},{"internalType":"uint256","name":"validatorsSkimmedBalance","type":"uint256"},{"internalType":"uint256","name":"validatorsExitedBalance","type":"uint256"},{"internalType":"uint256","name":"validatorsExitingBalance","type":"uint256"},{"internalType":"uint32","name":"validatorsCount","type":"uint32"},{"internalType":"bool","name":"rebalanceDepositToRedeemMode","type":"bool"},{"internalType":"bool","name":"slashingContainmentMode","type":"bool"}],"internalType":"struct IOracleManagerV1.StoredConsensusLayerReport","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMetadataURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOperatorsRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedeemManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReportBounds","outputs":[{"components":[{"internalType":"uint256","name":"annualAprUpperBound","type":"uint256"},{"internalType":"uint256","name":"relativeLowerBound","type":"uint256"}],"internalType":"struct ReportBounds.ReportBoundsStruct","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawalCredentials","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_additionalValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_depositContractAddress","type":"address"},{"internalType":"address","name":"_elFeeRecipientAddress","type":"address"},{"internalType":"bytes32","name":"_withdrawalCredentials","type":"bytes32"},{"internalType":"address","name":"_oracleAddress","type":"address"},{"internalType":"address","name":"_systemAdministratorAddress","type":"address"},{"internalType":"address","name":"_allowlistAddress","type":"address"},{"internalType":"address","name":"_operatorRegistryAddress","type":"address"},{"internalType":"address","name":"_collectorAddress","type":"address"},{"internalType":"uint256","name":"_globalFee","type":"uint256"}],"name":"initRiverV1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_redeemManager","type":"address"},{"internalType":"uint64","name":"_epochsPerFrame","type":"uint64"},{"internalType":"uint64","name":"_slotsPerEpoch","type":"uint64"},{"internalType":"uint64","name":"_secondsPerSlot","type":"uint64"},{"internalType":"uint64","name":"_genesisTime","type":"uint64"},{"internalType":"uint64","name":"_epochsToAssumedFinality","type":"uint64"},{"internalType":"uint256","name":"_annualAprUpperBound","type":"uint256"},{"internalType":"uint256","name":"_relativeLowerBound","type":"uint256"},{"internalType":"uint128","name":"_minDailyNetCommittableAmount_","type":"uint128"},{"internalType":"uint128","name":"_maxDailyRelativeCommittableAmount_","type":"uint128"}],"name":"initRiverV1_1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initRiverV1_2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"isValidEpoch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"proposeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lsETHAmount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"requestRedeem","outputs":[{"internalType":"uint32","name":"_redeemRequestId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"_redeemRequestIds","type":"uint32[]"}],"name":"resolveRedeemRequests","outputs":[{"internalType":"int64[]","name":"withdrawalEventIds","type":"int64[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sendCLFunds","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"sendCoverageFunds","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"sendELFees","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"sendRedeemManagerExceedingFunds","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAllowlist","type":"address"}],"name":"setAllowlist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"epochsPerFrame","type":"uint64"},{"internalType":"uint64","name":"slotsPerEpoch","type":"uint64"},{"internalType":"uint64","name":"secondsPerSlot","type":"uint64"},{"internalType":"uint64","name":"genesisTime","type":"uint64"},{"internalType":"uint64","name":"epochsToAssumedFinality","type":"uint64"}],"internalType":"struct CLSpec.CLSpecStruct","name":"_newValue","type":"tuple"}],"name":"setCLSpec","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newCollector","type":"address"}],"name":"setCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorsBalance","type":"uint256"},{"internalType":"uint256","name":"validatorsSkimmedBalance","type":"uint256"},{"internalType":"uint256","name":"validatorsExitedBalance","type":"uint256"},{"internalType":"uint256","name":"validatorsExitingBalance","type":"uint256"},{"internalType":"uint32","name":"validatorsCount","type":"uint32"},{"internalType":"uint32[]","name":"stoppedValidatorCountPerOperator","type":"uint32[]"},{"internalType":"bool","name":"rebalanceDepositToRedeemMode","type":"bool"},{"internalType":"bool","name":"slashingContainmentMode","type":"bool"}],"internalType":"struct IOracleManagerV1.ConsensusLayerReport","name":"_report","type":"tuple"}],"name":"setConsensusLayerData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newCoverageFund","type":"address"}],"name":"setCoverageFund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint128","name":"minDailyNetCommittableAmount","type":"uint128"},{"internalType":"uint128","name":"maxDailyRelativeCommittableAmount","type":"uint128"}],"internalType":"struct DailyCommittableLimits.DailyCommittableLimitsStruct","name":"_dcl","type":"tuple"}],"name":"setDailyCommittableLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newELFeeRecipient","type":"address"}],"name":"setELFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newFee","type":"uint256"}],"name":"setGlobalFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"}],"name":"setKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_metadataURI","type":"string"}],"name":"setMetadataURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oracleAddress","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"annualAprUpperBound","type":"uint256"},{"internalType":"uint256","name":"relativeLowerBound","type":"uint256"}],"internalType":"struct ReportBounds.ReportBoundsStruct","name":"_newValue","type":"tuple"}],"name":"setReportBounds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_underlyingAssetAmount","type":"uint256"}],"name":"sharesFromUnderlyingBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUnderlyingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"underlyingBalanceFromShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60808060405234610093577f1809e49bba43f2d39fa57894b50cd6ccb428cc438230e065cac3eb24a1355a7190600019807f82055909238c0f5e63d6f174068ebb8f51bcec9bd37de63bb68f6551feec0cfc558152604060208201523660408201523660006060830137600060603683010152606081601f19601f3601168101030190a16040516158e09081620000998239f35b600080fdfe6080604052600436101561001d575b36614dd05761001b614dc7565b005b60003560e01c8063020f086e146104e85780630407de47146104e357806304843a17146104de578063056850c6146104d957806306fdde03146104d4578063095ea7b3146104cf5780630e18b681146104ca578063107703ab146104c5578063107d7fa0146104c05780631311cf8d146104bb578063143a08d4146104b6578063147bf6c4146104b15780631546962b146104ac57806315ca4cee146104a757806317e64858146104a257806318160ddd1461049d5780631bcbfaba1461049857806323b872dd14610493578063281a31221461048e578063291206f6146104895780632cb562e1146104845780632d6b59bf1461047f5780632dc5c97c1461047a578063313ce5671461047557806336bf33251461039e578063391b6f4e14610470578063395093511461046b5780633af9e6691461046657806346425ef0146104615780634b47b74f1461045c5780635022820114610457578063540bc5ea1461045257806354a29cdc1461044d57806354fd4d5014610448578063557ed1ba14610443578063563967151461043e57806357fa85471461043957806358bf3c7f146104345780635d95e8011461042f5780635f2e5f071461042a57806363f62aaf146104255780636e9960c31461042057806370a082311461041b57806372f79b1314610416578063748747e614610411578063750521f51461040c57806378a010e814610407578063799a1954146104025780637adbf973146103fd578063833b1fce146103f857806386a92af7146103f357806387f2adfb146103ee57806389896aef146103e95780639332525d146103e457806395d89b41146103df5780639b498e26146103da5780639d49cca1146103d5578063a29a839f146103d0578063a457c2d7146103cb578063a9059cbb146103c6578063ac232bde146103c1578063bb6583ec146103bc578063bf15af56146103b7578063c5eff3d0146103b2578063d0468156146103ad578063d0e30db0146103a8578063d4970524146103a3578063d7f8f4741461039e578063d96f186d14610399578063dd62ed3e14610394578063e3a88e611461038f578063efd603471461038a578063efd6684614610385578063f79c3f0214610380578063f9f95a901461037b578063fb5b82d0146103765763fe7c12ae0361000e57612bca565b612b4d565b612b1d565b612aff565b61251b565b6124ee565b6124ad565b612448565b612218565b611220565b6121eb565b6121d6565b6121a0565b61216a565b61214e565b6120e7565b6120c2565b612049565b611fe9565b611fc6565b611f90565b611f5a565b611f14565b611e19565b611db0565b611d85565b611cb9565b611c83565b611c42565b611c24565b611b34565b611ac2565b61197c565b61188d565b611846565b611810565b6117da565b6116a1565b611606565b611589565b611547565b61151c565b611501565b6114bb565b611411565b6113f5565b6113bf565b611351565b611326565b6112df565b61127a565b611244565b611204565b61119d565b611172565b61113c565b6110d2565b610ee2565b610d97565b610d6c565b610d41565b610bfe565b610b6f565b610b39565b610ac5565b610aa2565b610a25565b6109f4565b610887565b61081d565b6107ec565b610796565b61070d565b6106e2565b610648565b61058c565b600435906001600160a01b038216820361050357565b600080fd5b602435906001600160a01b038216820361050357565b606435906001600160a01b038216820361050357565b608435906001600160a01b038216820361050357565b60a435906001600160a01b038216820361050357565b60c435906001600160a01b038216820361050357565b60e435906001600160a01b038216820361050357565b34610503576020366003190112610503576105a56104ed565b6000805160206157ab833981519152546001600160a01b03919082163303610609576105d08161504f565b8060008051602061584b83398151915255167f67b26a33f305cc027b2d45b2f6f418793afcd3e22f7376afa7be068ce18604e8600080a2005b60405163472511eb60e11b8152336004820152602490fd5b6001600160401b0381160361050357565b602435906001600160801b038216820361050357565b3461050357610140366003190112610503576106626104ed565b60243561066e81610621565b6044359161067b83610621565b6064359161068883610621565b6084359161069583610621565b60a4356106a181610621565b6001600160801b03946101043594908686168603610503576101243596871687036105035761001b9760e4359560c43595612cf0565b600091031261050357565b346105035760003660031901126105035760206000805160206157eb83398151915254604051908152f35b6000366003190112610503576000805160206155ab833981519152546001600160a01b0316330361060957005b60005b83811061074d5750506000910152565b818101518382015260200161073d565b906020916107768151809281855285808601910161073a565b601f01601f1916010190565b90602061079392818152019061075d565b90565b34610503576000366003190112610503576107e86040516107b6816119fd565b6011815270098d2e2ead2c840a6e8c2d6cac8408aa89607b1b602082015260405191829160208352602083019061075d565b0390f35b34610503576040366003190112610503576108126108086104ed565b6024359033614b55565b602060405160018152f35b34610503576000806003193601126108845760008051602061556b8339815191528054906001600160a01b03821633036106095761085b8392612bf5565b55807f2a0f8515de3fa34ef68b99300347b8793c01683350743e96fe440594528298f48180a280f35b80fd5b34610503576040366003190112610503576004356108a3610508565b60008051602061582b833981519152549091906108d0906001600160a01b03165b6001600160a01b031690565b91823b1561050357604051632d20488160e11b815233600480830191909152602482015292600090849060449082905afa9182156109d65761097a936020936109db575b50610920813033614bc5565b506000805160206155ab83398151915254610943906001600160a01b03166108c4565b60405163107703ab60e01b815260048101929092526001600160a01b03909216602482015292839190829060009082906044820190565b03925af180156109d6576107e8916000916109a8575b5060405163ffffffff90911681529081906020820190565b6109c9915060203d81116109cf575b6109c18183611a6a565b81019061300e565b38610990565b503d6109b7565b613002565b806109e86109ee926119e5565b806106d7565b38610914565b3461050357600036600319011261050357602063ffffffff6000805160206157cb8339815191525416604051908152f35b3461050357602036600319011261050357610a3e6104ed565b6000805160206157ab833981519152546001600160a01b0391908216330361060957610a698161504f565b8060008051602061564b83398151915255167f1da4c245099590dc40be61880c9b97792f3694d970acc1e67ac0e6cc90f3780d600080a2005b34610503576000366003190112610503576020610abd613f5a565b604051908152f35b3461050357602036600319011261050357610ade6104ed565b6000805160206157ab833981519152546001600160a01b03919082163303610609578060008051602061556b83398151915255167f2a0f8515de3fa34ef68b99300347b8793c01683350743e96fe440594528298f4600080a2005b346105035760003660031901126105035760008051602061584b833981519152546040516001600160a01b039091168152602090f35b34610503576040366003190112610503576000805160206157ab833981519152546001600160a01b03163303610609577f5ab79ffcd89b6380c7fbdd89d02cfe3d9c53c99a85e150c2319075018d1aac5c604051610bcc816119fd565b600435808252610be460243592836020820152615497565b60408051918252602082019290925290819081015b0390a1005b3461050357600036600319011261050357610c176139b4565b506107e8604051610c2781611a18565b60008051602061562b83398151915254815260008051602061550b8339815191525460208201526000805160206155eb83398151915254604082015260008051602061568b8339815191525460608201527fdbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c705460808201526000805160206157cb8339815191525463ffffffff811660a0830152610cdd9060ff90602081901c8216151560c085015260281c16151560e0830152565b6040519182918291909160e080610100830194805184526020810151602085015260408101516040850152606081015160608501526080810151608085015263ffffffff60a08201511660a085015260c0810151151560c085015201511515910152565b3461050357600036600319011261050357602060008051602061558b83398151915254604051908152f35b3461050357600036600319011261050357602060008051602061578b83398151915254604051908152f35b3461050357606036600319011261050357610db06104ed565b610db8610508565b60443591610dc682826149d0565b8215610ed05782610dd6826154e3565b5410610ebe576001600160a01b0382811615610e92576001600160a01b038216600090815260008051602061572b8339815191526020908152604080832033845290915290205490848210610e65576107e8610e40868686868360018201610e52575b5050614bc5565b60405190151581529081906020820190565b610e5e91033383614b55565b8583610e39565b604051637b936de360e01b8152921660048301523360248301526044820152606481019290925250608490fd5b604051637617407560e11b81526001600160a01b038316600482015260006024820152604490fd5b0390fd5b6040516351940b3960e11b8152600490fd5b6040516336b216db60e21b8152600490fd5b346105035761012036600319011261050357610efc6104ed565b610f04610508565b90610f0d61051e565b90610f16610534565b92610f1f61054a565b93610f28610560565b90610f31610576565b95610104359160008051602061552b8339815191528054806110b3575060019055610f5b90612bf5565b610f648761504f565b6000805160206156eb8339815191528790556000966001600160a01b03948593849283167f0cc5437d7c9c1d9eab549acbb533eea3e9868e9443dd75309ed5820b33a3774e8b80a2610fb581615262565b60405190815260207fbd533e726baaf59b36f3914d950053f7e78f527057c97cd3f0043257fc0fc88491a1610fe98161504f565b8060008051602061564b83398151915255167f1da4c245099590dc40be61880c9b97792f3694d970acc1e67ac0e6cc90f3780d8880a2611028816150c1565b167f30f015a5d3c72c0a9414538199baa022323a483fa9e4ba2cd581596cf8ca04248680a261105681615419565b167fffc0721ef0563a1b0a51a0dc92113025f33ca434ada9ee3eebff2f385d2a8f9a8480a260443561108791613347565b61109090613936565b6040518061109e3682612c63565b0360008051602061554b83398151915291a180f35b60449060405190631cfd276760e31b8252600060048301526024820152fd5b34610503576020366003190112610503576000805160206157ab83398151915254600435906001600160a01b03163303610609576020816111337fbd533e726baaf59b36f3914d950053f7e78f527057c97cd3f0043257fc0fc88493615262565b604051908152a1005b346105035760003660031901126105035760008051602061564b833981519152546040516001600160a01b039091168152602090f35b346105035760003660031901126105035760206000805160206156ab83398151915254604051908152f35b34610503576000366003190112610503576111b66139f2565b5060a06111c16150dc565b6040519060806001600160401b03918281511684528260208201511660208501528260408201511660408501528260608201511660608501520151166080820152f35b3461050357600036600319011261050357602060405160128152f35b346105035760003660031901126105035760206040516801bc16d674ec8000008152f35b346105035760003660031901126105035760008051602061576b833981519152546040516001600160a01b039091168152602090f35b34610503576040366003190112610503576112936104ed565b33600090815260008051602061572b833981519152602090815260408083206001600160a01b038516845290915290205460243581018091116112da576108129133614b55565b612c40565b34610503576020366003190112610503576001600160a01b036113006104ed565b1660005260008051602061588b8339815191526020526020610abd604060002054614c42565b3461050357600036600319011261050357602060008051602061550b83398151915254604051908152f35b346105035760003660031901126105035761136a6150dc565b611373816148cd565b9060008051602061562b83398151915254906001600160401b03908181511683018093116112da57511680156113ba57820682039182116112da57602091610abd91615082565b612f5e565b34610503576000366003190112610503576000805160206156eb833981519152546040516001600160a01b039091168152602090f35b3461050357600036600319011261050357602060405160608152f35b346105035760003660031901126105035760008051602061552b83398151915280548060020361149c576003825561147f60008051602061580b833981519152546114696801bc16d674ec80000082068092036132fc565b6000805160206157eb83398151915254016132af565b60008051602061554b83398151915260405180610bf93682612c92565b60449060405190631cfd276760e31b8252600260048301526024820152fd5b34610503576000366003190112610503576107e86040516114db816119fd565b6005815264312e322e3160d81b602082015260405191829160208352602083019061075d565b34610503576000366003190112610503576020604051428152f35b3461050357600036600319011261050357602060008051602061560b83398151915254604051908152f35b34610503576020366003190112610503576004356001600160401b0361156b6150dc565b511680156113ba57810681039081116112da57602090604051908152f35b34610503576020366003190112610503576115a26104ed565b6000805160206157ab833981519152546001600160a01b03919082163303610609576115cd8161504f565b8060008051602061582b83398151915255167f30f015a5d3c72c0a9414538199baa022323a483fa9e4ba2cd581596cf8ca0424600080a2005b60003660031901126105035760008051602061560b833981519152546001600160a01b0316330361060957005b9181601f84011215610503578235916001600160401b038311610503576020808501948460051b01011161050357565b6020908160408183019282815285518094520193019160005b82811061168a575050505090565b835160070b8552938101939281019260010161167c565b3461050357602080600319360112610503576001600160401b03600435818111610503576116d3903690600401611633565b60018060a01b036000805160206155ab8339815191525416906040518092635f2e5f0760e01b8252866004830152818061171560009788966024840191612fbd565b03915afa9283156109d6578293611735575b604051806107e88682611663565b909192503d8084843e6117488184611a6a565b82019184818403126117d65780519182116117d657019181601f840112156108845782519061177682612f8d565b936117846040519586611a6a565b828552858086019360051b8201019384116117d2578501915b8383106117b557505050506107e89150388080611727565b82518060070b81036117ce57815291850191850161179d565b8280fd5b5080fd5b8380fd5b34610503576000366003190112610503576000805160206155ab833981519152546040516001600160a01b039091168152602090f35b34610503576000366003190112610503576000805160206157ab833981519152546040516001600160a01b039091168152602090f35b34610503576020366003190112610503576001600160a01b036118676104ed565b1660005260008051602061588b8339815191526020526020604060002054604051908152f35b34610503576000366003190112610503576118a66150dc565b6118af816148cd565b6001600160401b0382511680156113ba57810681039081116112da576107e861195f61195a8461191b61190f61194c8761191b61190f61194c61194661190f611938604061192160208d019861191b61190f8b516001600160401b031690565b6001600160401b031690565b90613461565b9b019a61191b61190f8d516001600160401b031690565b9e516001600160401b031690565b8c612c56565b92516001600160401b031690565b613474565b604051938493846040919493926060820195825260208201520152565b34610503576020366003190112610503576119956104ed565b6000805160206157ab833981519152546001600160a01b03163303610609576119bd8161504f565b60008051602061576b83398151915255005b634e487b7160e01b600052604160045260246000fd5b6001600160401b0381116119f857604052565b6119cf565b604081019081106001600160401b038211176119f857604052565b61010081019081106001600160401b038211176119f857604052565b60a081019081106001600160401b038211176119f857604052565b608081019081106001600160401b038211176119f857604052565b90601f801991011681019081106001600160401b038211176119f857604052565b60405190611a9882611a34565b565b60405190611a98826119fd565b6001600160401b0381116119f857601f01601f191660200190565b34610503576020366003190112610503576004356001600160401b0381116105035736602382011215610503578060040135611afd81611aa7565b90611b0b6040519283611a6a565b808252366024828501011161050357602081600092602461001b960183860137830101526130a6565b346105035760a0366003190112610503576000805160206157ab833981519152546001600160a01b0316330361060957611bca604051611b7381611a34565b600435611b7f81610621565b8152602435611b8d81610621565b6020820152604435611b9e81610621565b6040820152606435611baf81610621565b6060820152608435611bc081610621565b608082015261516a565b7f25777eb44be046f64180acf8275f0ac2ec51e63a65a5f8a0f2f6d86ba25b74cf611bf3613a1d565b611bfb613a29565b90610bf9611c07613a35565b611c0f613a41565b611c17613a4d565b9160405195869586613981565b34610503576020366003190112610503576020610abd600435614c65565b3461050357602036600319011261050357611c5b6104ed565b6000805160206157ab833981519152546001600160a01b031633036106095761001b90613936565b34610503576000366003190112610503576000805160206155cb833981519152546040516001600160a01b039091168152602090f35b3461050357600080600319360112610884576040518160008051602061570b8339815191528054611ce98161537e565b80855291600191808316908115611d5a5750600114611d1f575b6107e885611d1381890382611a6a565b60405191829182610782565b835260208084209095505b828410611d4757505050816107e893611d13928201019338611d03565b8054858501870152928501928101611d2a565b90506107e89650611d139450602092508593915060ff191682840152151560051b8201019338611d03565b346105035760003660031901126105035760206000805160206156cb83398151915254604051908152f35b3461050357600036600319011261050357602060008051602061562b83398151915254604051908152f35b6020908160408183019282815285518094520193019160005b828110611e02575050505090565b835160ff1685529381019392810192600101611df4565b3461050357600319604036820112610503576001600160401b0360043581811161050357611e4b903690600401611633565b6024929192359182116105035760009283611ec192611e71611eb2953690600401611633565b94909760018060a01b036000805160206155ab833981519152541695604051998a9889978896637c044e5560e01b8852608060048901526084880191612fbd565b92858403016024860152612fbd565b6001604483015261ffff606483015203925af180156109d6576107e891600091611ef3575b5060405191829182611ddb565b611f0e913d8091833e611f068183611a6a565b810190613023565b38611ee6565b34610503576000366003190112610503576107e8604051611f34816119fd565b6005815264098e68aa8960db1b602082015260405191829160208352602083019061075d565b346105035760003660031901126105035760008051602061586b833981519152546040516001600160a01b039091168152602090f35b3461050357600036600319011261050357611fa9612f74565b506040611fb4615434565b60208251918051835201516020820152f35b34610503576000366003190112610503576020610abd611fe46150dc565b6148cd565b34610503576040366003190112610503576120026104ed565b33600090815260008051602061572b833981519152602090815260408083206001600160a01b038516845290915290205460243581039081116112da576108129133614b55565b34610503576040366003190112610503576120626104ed565b6024359061207081336149d0565b8115610ed05781612080336154e3565b5410610ebe576001600160a01b038116156120a3576107e891610e409133614bc5565b604051637617407560e11b815233600482015260006024820152604490fd5b60203660031901126105035761001b6120d96104ed565b6120e28161504f565b614de2565b3461050357604036600319011261050357604051612104816119fd565b6004356001600160801b0381168103610503578152612121610632565b60208201526000805160206157ab833981519152546001600160a01b031633036106095761001b9061323d565b3461050357600036600319011261050357602060405160308152f35b346105035760003660031901126105035760008051602061582b833981519152546040516001600160a01b039091168152602090f35b346105035760003660031901126105035760008051602061556b833981519152546040516001600160a01b039091168152602090f35b60003660031901126105035761001b33614de2565b60003660031901126105035760008051602061584b833981519152546001600160a01b0316330361060957005b34610503576040806003193601126105035760008051602061576b833981519152546001600160a01b031633036124385760008051602061566b833981519152546020919061226f906001600160a01b03166108c4565b81519283809263c5f2892f60e01b825260049586915afa9081156109d65760009161240a575b50602435036123fd5760008051602061580b83398151915254916122c581356801bc16d674ec8000008504615071565b9081156123ef576122d582613562565b819291519384156123df5784116123d05760008051602061560b833981519152549182156123c2575060005b848110612399577f220ab8fd274cf58c09b0825ccf00e74ba4ce4117fd47285adc2183a635838f1b86866123466123418b61233b8461342a565b9061349a565b6132fc565b610bf96123826000805160206156cb833981519152549261237c61236a8286612c56565b6000805160206156cb83398151915255565b83612c56565b925191825260208201929092529081906040820190565b806123bc846123aa60019488613400565b516123b58487613400565b5190613740565b01612301565b8551639be7315960e01b8152fd5b508351635993bd5760e01b8152fd5b855163200149ad60e21b81528390fd5b8251631036b5ad60e31b8152fd5b51632b3b4dab60e21b8152fd5b61242b915060203d8111612431575b6124238183611a6a565b8101906133e7565b38612295565b503d612419565b5163c60eb33560e01b8152600490fd5b346105035760403660031901126105035760206124a46124666104ed565b61246e610508565b6001600160a01b03918216600090815260008051602061572b833981519152855260408082209290931681526020919091522090565b54604051908152f35b34610503576000366003190112610503576124c6612f74565b5060406124d1615214565b815190602060018060801b03918281511684520151166020820152f35b60003660031901126105035760008051602061564b833981519152546001600160a01b0316330361060957005b346105035760206003198181360112610503576004906001600160401b038235116105035761012090823536030112610503576000805160206155cb833981519152546001600160a01b03163303612ae1576125756150dc565b81358201359061258c6125888383614906565b1590565b612ac457612598613a59565b9060008051602061568b833981519152546040830181815260648635013591808310612aa257506125ca90518261349a565b91608084019283526000805160206155eb8339815191525490606085019382855260448835013592808410612a7f575060a48835019461260986613ad7565b6000805160206156cb83398151915254908163ffffffff80921611908115612a42575b506129e45750916126c995939161266a8961264c6126bc9896518661349a565b9360a08b0194855260008051602061562b8339815191525490614986565b60c0890152612677613f5a565b88526126868151835190612c56565b6129d0575b50506126956139b4565b96875260248835013589880152604087015260608601526084863501356080860152613ad7565b63ffffffff1660a0840152565b6126e26126d960e4853501613aeb565b151560c0840152565b612706610104843501926127016126f885613aeb565b151560e0830152565b61528f565b61270e615434565b93612720825160c0840151908761496c565b612728613f5a565b83830181815284519097918490821161291c575050865161274a838651612c56565b106128de57509160c09161279a61282f946127897f49ac0d2bb2a688ca460f7993eb93eccd3b9c9188da6b0727e9a409cf8b105875995185519061349a565b61012085019081515251519061349a565b60e08301525b60e0820151908161289c575b505060e08101518061286b575b5060e081015180612852575b5061281861280d61012083019586515180612843575b506127ed60c489350189358a01613af5565b92906128056127ff60e48c3501613aeb565b93613aeb565b933691613b2a565b6084893501356144b2565b6128206142a7565b6128286147a0565b0151614814565b5190610bf960405192828493350183613bc1565b61284c90613e37565b386127db565b61285b90613d62565b60606101208301510152386127c5565b6040612879612891926141e1565b610120840190828251015251015160e083015161349a565b60e0820152386127b9565b6128a86128d292613c95565b610120840190828251015280516128c3838201518251612c56565b905251015160e083015161349a565b60e082015238806127ac565b8351965160c09094015190516040805163eb7a968960e01b815297880198895260208901959095529387015250506060840152509081900360800190fd5b61292f9061271092930192835190613461565b0461294288519161233b87518092615071565b1161299257509160c09161298861282f946129827f49ac0d2bb2a688ca460f7993eb93eccd3b9c9188da6b0727e9a409cf8b10587599855190519061349a565b90612c56565b60e08301526127a0565b8351965160c09094015190516040805163063bb83f60e11b815297880198895260208901959095529387015250506060840152509081900360800190fd5b6129dd9151905190614057565b388061268b565b89610eba6129f189613ad7565b92612a0e6000805160206157cb8339815191525463ffffffff1690565b90604051948594632f776b1760e01b8652850191939290604091606084019563ffffffff8093168552602085015216910152565b9050612a4d88613ad7565b90612a76612a6d6000805160206157cb8339815191525463ffffffff1690565b63ffffffff1690565b9116103861262c565b60408051636f24834360e01b8152808b0192835260208301869052918291010390fd5b60408051621ee1cf60e11b815280890192835260208301859052918291010390fd5b5060405163a225656d60e01b815291820190815281906020010390fd5b60405163472511eb60e11b8152339181019182529081906020010390fd5b34610503576020366003190112610503576020610abd600435614c42565b34610503576020366003190112610503576020612b43600435612b3e6150dc565b614906565b6040519015158152f35b3461050357602036600319011261050357612b666104ed565b6000805160206157ab833981519152546001600160a01b0391908216330361060957612b918161504f565b806000805160206156eb83398151915255167f0cc5437d7c9c1d9eab549acbb533eea3e9868e9443dd75309ed5820b33a3774e600080a2005b3461050357600036600319011261050357602060008051602061580b83398151915254604051908152f35b612bfe8161504f565b6000805160206157ab8339815191528190556001600160a01b03167f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1600080a2565b634e487b7160e01b600052601160045260246000fd5b919082018092116112da57565b6060916000825260406020830152806040830152806000848401376000828201840152601f01601f1916010190565b6060916002825260406020830152806040830152806000848401376000828201840152601f01601f1916010190565b6060916001825260406020830152806040830152806000848401376000828201840152601f01601f1916010190565b979896959694919390929460008051602061552b833981519152805480600103612f3f575060029055612d228961504f565b6000805160206155ab833981519152899055604080516001600160a01b038b168152909a907faf890c07e266df31e0725841eb0e85596a0caa1b17bbae5c0b206fdcc92d7ce190602090a18a5191612d79836119fd565b6001600160801b039182168352166020820152612d959061323d565b612d9d611a8b565b6001600160401b03841681526001600160401b03851660208201526001600160401b038616818b01526001600160401b03821660608201526001600160401b0383166080820152612ded9061516a565b8851948594612dfc9486613981565b037f25777eb44be046f64180acf8275f0ac2ec51e63a65a5f8a0f2f6d86ba25b74cf91a1612e28611a9a565b828152816020820152612e3a90615497565b835191825260208201527f5ab79ffcd89b6380c7fbdd89d02cfe3d9c53c99a85e150c2319075018d1aac5c90604090a1612e726139b4565b7fd7f2d45e512a86049f7a113657b39731b6b558609584243063a52cd31a8eb5285481527ffedfd2c285a57fb23bf45a3fe9ac02d36a76ebb72801b1c8aaf553d74e9d4652546020820152600083820181905260608201819052608082018190527f0f1e7733641e4d843128fea0d2ec90d3d06a40b0fe244ff603d8c1aa200dc0f85463ffffffff1660a083015260c0820181905260e0820152612f159061528f565b612f1f9030614ad7565b5180612f2b3682612cc1565b0360008051602061554b83398151915291a1565b60449060405190631cfd276760e31b8252600160048301526024820152fd5b634e487b7160e01b600052601260045260246000fd5b60405190612f81826119fd565b60006020838281520152565b6001600160401b0381116119f85760051b60200190565b63ffffffff81160361050357565b3590611a9882612fa4565b91908082526020809201929160005b828110612fda575050505090565b90919293828060019263ffffffff8835612ff381612fa4565b16815201950193929101612fcc565b6040513d6000823e3d90fd5b90816020910312610503575161079381612fa4565b6020908181840312610503578051906001600160401b03821161050357019180601f8401121561050357825161305881612f8d565b936130666040519586611a6a565b818552838086019260051b820101928311610503578301905b82821061308d575050505090565b815160ff8116810361050357815290830190830161307f565b6000805160206157ab833981519152549091906001600160a01b031633036106095781511561322b578151916001600160401b0383116119f85760008051602061570b83398151915292613103816130fe865461537e565b6153b8565b602080601f83116001146131735750938161316393927f8d2df192dd17edf92a7964b78aa322f3d717b2ab9de00651bee32bbc4c5da63a9596600091613168575b508160011b916000199060031b1c191617905560405191829182610782565b0390a1565b905083015138613144565b60008051602061570b83398151915260005290601f1983167f992d979edf4f3c53d0b672011f4c4543809e6b0ed7af1d628abb72f3026d823f926000905b828210613213575050927f8d2df192dd17edf92a7964b78aa322f3d717b2ab9de00651bee32bbc4c5da63a959692600192826131639796106131fa575b5050811b019055611d13565b85015160001960f88460031b161c1916905538806131ee565b80600185968294968a015181550195019301906131b1565b604051638d46fe0560e01b8152600490fd5b60407e4180017d3dd609da6980999655a6bd2591e313d31d6230b1889c369a9713a09160018060801b0360208183511692015160018060801b03198160801b1683177fc305db9f63aeed94a45229ff5ab4174e72b223a38ab4c5e8f518ee4f437918d0558351928352166020820152a1565b6000805160206157eb833981519152805460408051918252602082018490527f48f67c1dada0cab2163f6282292ad97ea97376cfed46bb3851654aaa630db7279190819081015b0390a155565b60008051602061580b833981519152805460408051918252602082018490527f86fd21e9b5bd76b20471c7f93a82aa4e25c37d48b179bda0a4d1a45e22a842f49190819081016132f6565b6133508161504f565b60008051602061566b833981519152819055604051906001600160a01b03167e043cf7635f276413ae358250286a479a631abd9d74d57d4aa0bb87ebc7d117600080a281156133d857506020817f4c86ba184ea1a1558f84835ca34f6d67e222e8ee5cc4f324b8861dda4cf1740c9260008051602061560b83398151915255604051908152a1565b63a9cb9e0d60e01b8152600490fd5b90816020910312610503575190565b81156113ba570490565b80518210156134145760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b906801bc16d674ec80000091808302928304036112da57565b906801bc16d674ec800000918281029281840414901517156112da57565b818102929181159184041417156112da57565b6000198101919082116112da57565b6801bc16d674ec7fffff198101919082116112da57565b919082039182116112da57565b9080601f83011215610503578151916134bf83612f8d565b926040906134cf82519586611a6a565b808552602093848087019260051b8501019381851161050357858101925b8584106134fe575050505050505090565b83516001600160401b0381116105035782019083603f8301121561050357878201519061352a82611aa7565b61353688519182611a6a565b82815285888486010111610503576135578a949385948a868501910161073a565b8152019301926134ed565b60008051602061586b8339815191525460405163277c9d4560e01b81526004810192909252600092919083908290602490829084906001600160a01b03165af19283156109d657809181946135b8575b50509190565b92915092503d8084843e6135cc8184611a6a565b82016040838203126117d65782516001600160401b039081811161361857826135f69186016134a7565b9360208101519182116136185761361093949550016134a7565b9138806135b2565b8580fd5b9061362f6020928281519485920161073a565b0190565b90611a9860306040518461365182965180926020808601910161073a565b810160006020820152036010810185520183611a6a565b90611a98604080518461368582965180926020808601910161073a565b810160006020820152036020810185520183611a6a565b9060405191634059730760d81b6020840152604083015260408252606082018281106001600160401b038211176119f857604052565b9190604051926020840152604083015260408252606082018281106001600160401b038211176119f857604052565b94939261372d60609361371f61373b9460808a5260808a019061075d565b9088820360208a015261075d565b90868203604088015261075d565b930152565b909160308251036139245760608351036139125761375d82613633565b604093845191826020809282613776600097889361361c565b03600293845afa156109d6578351828561379d61379287614fb0565b8b519182809261361c565b0390855afa156109d657845183866137ca6137bf6137ba89615009565b613668565b8c519182809261361c565b0390865afa156109d657856137e561379286938351906136d2565b0390855afa156109d6578285613800613792898351956136d2565b0390855afa156109d657828561381a61379282519461369c565b0390855afa156109d657829161384161383687938451906136d2565b8a519182809261361c565b03915afa156109d65782519361385647613483565b60008051602061566b833981519152549096906138a19061387f906001600160a01b03166108c4565b936138938a51948592830160209181520190565b03601f198101845283611a6a565b823b1561390e576801bc16d674ec800000906138d28951978896879586946304512a2360e31b865260048601613701565b03925af180156109d6576138fb575b5047036138eb5750565b51638051a6bb60e01b8152600490fd5b806109e8613908926119e5565b386138e1565b8480fd5b60405163408ebd3960e01b8152600490fd5b60405163050cb55360e41b8152600490fd5b61393f8161504f565b6000805160206155cb8339815191528190556001600160a01b03167fd3b5d1e0ffaeff528910f3663f0adace7694ab8241d58e17a91351ced2e08031600080a2565b6001600160401b039182168152918116602083015291821660408201529181166060830152909116608082015260a00190565b604051906139c182611a18565b8160e06000918281528260208201528260408201528260608201528260808201528260a08201528260c08201520152565b604051906139ff82611a34565b60006080838281528260208201528260408201528260608201520152565b60043561079381610621565b60243561079381610621565b60443561079381610621565b60643561079381610621565b60843561079381610621565b6040519061014082018281106001600160401b038211176119f857604052816101206000918281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015260405192613abf84611a4f565b80845280602085015280604085015260608401520152565b3561079381612fa4565b8015150361050357565b3561079381613ae1565b903590601e198136030182121561050357018035906001600160401b03821161050357602001918160051b3603831361050357565b9291613b3582612f8d565b91613b436040519384611a6a565b829481845260208094019160051b810192831161050357905b828210613b695750505050565b8380918335613b7781612fa4565b815201910190613b5c565b9035601e19823603018112156105035701602081359101916001600160401b038211610503578160051b3603831361050357565b3590611a9882613ae1565b92916020611a989260a08652803560a08701528181013560c0870152604081013560e0870152613c6f613c656101006060840135818a0152613c456101206080860135818c0152613c25613c1760a08801612fb2565b63ffffffff166101408d0152565b613c3260c0870187613b82565b90916101608d01526101c08c0191612fbd565b93613c5f613c5560e08301613bb6565b15156101808c0152565b01613bb6565b15156101a0880152565b940190606080918051845260208101516020850152604081015160408501520151910152565b60008051602061564b8339815191525447916001600160a01b0390911690813b156105035760009160248392604051948593849263c8a6dfd360e01b845260048401525af180156109d657613d53575b504790808203918083116112da5703613d2d575b6040518181527fda841d3042d792e2509a333b9dcbd4b3dd9b9047d382011f8788fab90ca7e3c79080602081015b0390a190565b613d4e613d49826000805160206157eb83398151915254612c56565b6132af565b613cf9565b613d5c906119e5565b38613ce5565b60008051602061584b833981519152546001600160a01b03168015613e30574790803b1561050357604051638ede6b6b60e01b815260048101939093526000908390602490829084905af19182156109d657613dc492613e1d575b504761349a565b80613dfc575b6040518181527fd500b67e5bd8019c0af744cadeec120d1b5e3d3a3a011f18cf182aa4c97947b6908060208101613d27565b613e18613d49826000805160206157eb83398151915254612c56565b613dca565b806109e8613e2a926119e5565b38613dbd565b5050600090565b60008051602061558b8339815191529081548015613f4857613e57613f5a565b9260008051602061578b83398151915254613e7b81613e768587613461565b613461565b906127108087029087820414871517156112da5761233b613e9c9287613461565b80613f395750506000905b81613eb4575b5050505050565b7f3d1669e813a9845c288f0e1f642a4343a451103b87886d12de37e63b39bbd94293613eff613f2c92613ef76000805160206156eb833981519152549586614c8d565b54918761349a565b9560405194859460018060a01b031697859094939260609260808301968352602083015260408201520152565b0390a23880808080613ead565b613f42916133f6565b90613ea7565b604051630d35acd760e21b8152600490fd5b63ffffffff6000805160206157cb83398151915254166000805160206156cb83398151915254808210600014613ffa57612982613ff561079393613fef613fd9613fc360008051602061550b833981519152546000805160206157eb8339815191525490612c56565b60008051602061580b8339815191525490612c56565b6000805160206156ab8339815191525490612c56565b9361349a565b613443565b505060008051602061550b833981519152546000805160206157eb8339815191525481018091116112da5760008051602061580b8339815191525481018091116112da576000805160206156ab8339815191525461079391612c56565b9190476140648285612c56565b60008051602061560b83398151915254909190614089906001600160a01b03166108c4565b803b156105035760405163ea74f47960e01b815260048101939093526000908390602490829084905af19182156109d6576140c992613e1d57504761349a565b926140d48282612c56565b840361416a577fcb5410dc8f29b2f498e023c3f9237dbd600255a717edf94a6072bcd03b0c773c92935080614149575b81614123575b6040805191825260208201929092529081908101613163565b61414461413f836000805160206156ab83398151915254612c56565b614196565b61410a565b614165613d49826000805160206157eb83398151915254612c56565b614104565b6141749250612c56565b6040516349a1938b60e01b815260048101919091526024810191909152604490fd5b6000805160206156ab833981519152805460408051918252602082018490527f215c2b83e8c232e42091088056ab75d2ff643855c32997024f786cddb22d22909190819081016132f6565b6000805160206155ab8339815191525447916001600160a01b0390911690813b1561050357600091602483926040519485938492630c77940160e01b845260048401525af180156109d657614298575b504790808203918083116112da5703614277575b6040518181527f4e484734eb4d444bfa106f917d05d9ceb8ce18bf516c85d7aeb9b322925339f9908060208101613d27565b614293613d49826000805160206157eb83398151915254612c56565b614245565b6142a1906119e5565b38614231565b6000805160206155ab833981519152546001600160a01b03166142c8613f5a565b60008051602061558b833981519152549015159081614440575b506142ea5750565b6040516303634a9560e21b8152906001600160a01b0316602082600481845afa9182156109d657600092614420575b508161432483614c42565b926000805160206156ab83398151915254908185116143e8575b604080519182526020820184905281018590527f709263092d8d9fef472d907900405b5edae3d76f8ff4354858025b18424d710190606090a183614383575b50505050565b8361438e9103614196565b6143988183614d0a565b813b156105035760405163b30d8bb760e01b815260048101919091529160009183916024918391905af180156109d6576143d5575b80808061437d565b806109e86143e2926119e5565b386143cd565b9150809350927f709263092d8d9fef472d907900405b5edae3d76f8ff4354858025b18424d710161441882614c65565b92905061433e565b61443991925060203d8111612431576124238183611a6a565b9038614319565b90501515386142e2565b92919060408401604085528151809152606085019060208093019060005b848282106144795750505050930152565b835163ffffffff1685529384019390920191600101614468565b919082604091031261050357602082516144ac81612fa4565b92015190565b60008051602061586b8339815191528054929594909390926144dc906001600160a01b03166108c4565b936000805160206156cb83398151915293845495803b156105035761451f966040998a5192838092632d01785d60e21b82528160009c8d9687936004840161444a565b03925af180156109d65761478d575b506145485760008051602061558b83398151915254614551575b50505050509050565b6000805160206156ab83398151915254906000805160206157eb83398151915254600460206145976108c46000805160206155ab8339815191525460018060a01b031690565b8b516303634a9560e21b815292839182905afa80156109d6576145c191899161476f575b50614c42565b93846145cd8786612c56565b106145db575b505050614548565b80614766575b614715575b50546145fa906001600160a01b03166108c4565b928751634a3c63bb60e01b81528881600481885afa9081156109d657879088926146e1575b5063ffffffff1690818111156146d65761463c91613ff59161349a565b91836146518461464c8585612c56565b612c56565b1061465d575b806145d3565b6146729261464c614677959361233b93612c56565b61508e565b915491813b156117d6579451632d1fc39d60e01b8152600481019590955260248501919091529192918290604490829084905af180156109d6576146c3575b8080808080808796614657565b806109e86146d0926119e5565b386146b6565b505061463c86613443565b63ffffffff925061470891508a3d8c1161470e575b6147008183611a6a565b810190614493565b9161461f565b503d6146f6565b61473161472b84614726888861349a565b61349a565b82615071565b8061473d575b506145e6565b91614760613d49846147566145fa966108c49698612c56565b9661472688614196565b91614737565b508015156145e1565b614787915060203d8111612431576124238183611a6a565b386145bb565b806109e861479a926119e5565b3861452e565b6000805160206156ab833981519152805490816147bb575050565b6000805160206157eb833981519152549182018092116112da576147e06000926132af565b7f215c2b83e8c232e42091088056ab75d2ff643855c32997024f786cddb22d2290604082548151908152846020820152a155565b61481c613f5a565b906000805160206157eb8339815191525491614836615214565b9160018060801b03602081855116940151168483039283116112da576201518061488361488993613e7661488f9761271061487c6801bc16d674ec800000998c98613461565b0490615082565b04615071565b04613443565b80614898575050565b60008051602061580b833981519152548181018091116112da576148bb906132fc565b81039081116112da57611a98906132af565b6001600160401b0390816060820151164203914283116112da57806040830151169182156113ba5760200151169182156113ba57040490565b90614910826148cd565b6001600160401b0390816080850151168301908184116112da5710159283614953575b8361493f575b50505090565b5116915081156113ba570615388080614939565b60008051602061562b8339815191525483119350614933565b91613e766149829264496cebb800945190613461565b0490565b9082039182116112da576001600160401b03908160208160408401511692015116029081169081036112da5761079391613461565b90816020910312610503575161079381613ae1565b60008051602061582b833981519152546149f2906001600160a01b03166108c4565b60405163e838dfbb60e01b8082526001600160a01b038416600483015260209390918481602481875afa9081156109d657600091614aba575b50614a6a57506040519081526001600160a01b0384166004820152908290829060249082905afa9182156109d657600092614a8d575b5050614a6a5750565b60405163e7d05e2760e01b81526001600160a01b03919091166004820152602490fd5b614aac9250803d10614ab3575b614aa48183611a6a565b8101906149bb565b3880614a61565b503d614a9a565b614ad19150853d8711614ab357614aa48183611a6a565b38614a2b565b614ae08161504f565b614ae98261504f565b6001600160a01b03908116600081815260008051602061572b83398151915260209081526040808320868616845282529091206000199492937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92593909186905b556040519586521693a3565b60207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591614b828161504f565b614b8b8461504f565b6001600160a01b03908116600081815260008051602061572b83398151915284526040808220878516835260205290209094908690614b49565b9160018060a01b038093169160009383855260008051602061588b833981519152908160205260408620548481039081116112da57604092868852602052828720558216948581522054918083018093116112da5760008051602061574b83398151915292614c356020936154e3565b55604051908152a3600190565b60008051602061558b83398151915254908115613e30576149829061191b613f5a565b614c6d613f5a565b908115613e305760008051602061558b8339815191525461498291613461565b60008051602061558b833981519152548281018091116112da57614cb090614d89565b60018060a01b0316908160005260008051602061588b83398151915280602052604060002054908282018092116112da5760009260008051602061574b8339815191529260209286865283526040852055604051908152a3565b60008051602061558b833981519152548281039081116112da57614d2d90614d89565b6001600160a01b0316600081815260008051602061588b8339815191526020819052604090912054838103919082116112da5760009360008051602061574b8339815191529260209285875283526040862055604051908152a3565b60207fc80ea35a3f9016535e5b7c87746740c5045afe42188d02c5786eb97495c2f429918060008051602061558b83398151915255604051908152a1565b611a9833614de2565b60405163574b16a760e11b8152600490fd5b3415614f37576000805160206157eb833981519152543481018091116112da57614e0b906132af565b614e153433614f49565b60008051602061582b83398151915254614e37906001600160a01b03166108c4565b91823b1561050357604051632d20488160e11b815233600482015260016024820152600081604481875afa80156109d657614f24575b506001600160a01b0381169233849003614eb5575b50506040513481523391507f3bc57f469ad6d10d7723ea226cd22bd2b9e527def2b529f6ab44645a1668958290602090a3565b60405163e838dfbb60e01b81526001600160a01b038316600482015290602090829060249082905afa9081156109d657600091614f06575b50614a6a5790614efd9133614bc5565b50388080614e82565b614f1e915060203d8111614ab357614aa48183611a6a565b38614eed565b806109e8614f31926119e5565b38614e6d565b6040516395b66fe960e01b8152600490fd5b9190614f53613f5a565b908082038281116112da578160008051602061558b83398151915254931492838015614fa8575b15614f8d575050611a9891508093614c8d565b614f979192613461565b916113ba57611a9891048093614c8d565b508015614f7a565b6040815110614ff75760405190602080830191810190606084015b808410614fe65750505060408252601f801991011660405290565b825184529281019291810191614fcb565b604051633b99b53d60e01b8152600490fd5b6060815110614ff757604051906020808301916060604085019101905b80841061503e5750508252601f01601f191660405290565b815184529282019290820190615026565b6001600160a01b03161561505f57565b60405163f6b2911f60e01b8152600490fd5b8181111561507d575090565b905090565b8181101561507d575090565b6801bc16d674ec800000808204916000919006156150b9575060ff60015b1681018091116112da5790565b60ff906150ac565b6150ca8161504f565b60008051602061582b83398151915255565b6150e46139f2565b506040516150f181611a34565b7fd84ee2c84c954c65bef1459fe03b761a42f49234527e3cd8fd9dce87cb83742a546001600160401b03908181168352818160401c166020840152818160801c16604084015260c01c60608301527fd84ee2c84c954c65bef1459fe03b761a42f49234527e3cd8fd9dce87cb83742b5416608082015290565b60806001600160401b0391828151166020820151906040830151906001600160401b0360c01b606085015160c01b16926001600160401b0360401b9060401b1617906001600160401b03851b90851b1617177fd84ee2c84c954c65bef1459fe03b761a42f49234527e3cd8fd9dce87cb83742a550151167fd84ee2c84c954c65bef1459fe03b761a42f49234527e3cd8fd9dce87cb83742b906001600160401b0319825416179055565b61521c612f74565b50604051615229816119fd565b7fc305db9f63aeed94a45229ff5ab4174e72b223a38ab4c5e8f518ee4f437918d0546001600160801b038116825260801c602082015290565b612710811161527d5760008051602061578b83398151915255565b6040516358d620b360e01b8152600490fd5b61535360e0611a9892805160008051602061562b83398151915255602081015160008051602061550b8339815191525560408101516000805160206155eb83398151915255606081015160008051602061568b8339815191525560808101517fdbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c705563ffffffff60a0820151166000805160206157cb8339815191529081549064ff0000000060c0850151151560201b169164ffffffffff1916171790550151151590565b6000805160206157cb8339815191529060ff60281b825491151560281b169060ff60281b1916179055565b90600182811c921680156153ae575b602083101461539857565b634e487b7160e01b600052602260045260246000fd5b91607f169161538d565b601f81116153c4575050565b60009060008051602061570b833981519152825260208220906020601f850160051c8301941061540f575b601f0160051c01915b82811061540457505050565b8181556001016153f8565b90925082906153ef565b6154228161504f565b60008051602061586b83398151915255565b61543c612f74565b50604051615449816119fd565b7ff81b149de6749a3f46464b2cce61e24462f67599ea4a5ce028aaf4ab1521f96d5481527ff81b149de6749a3f46464b2cce61e24462f67599ea4a5ce028aaf4ab1521f96e54602082015290565b80517ff81b149de6749a3f46464b2cce61e24462f67599ea4a5ce028aaf4ab1521f96d55602001517ff81b149de6749a3f46464b2cce61e24462f67599ea4a5ce028aaf4ab1521f96e55565b6001600160a01b0316600090815260008051602061588b833981519152602052604090209056fedbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c6d82055909238c0f5e63d6f174068ebb8f51bcec9bd37de63bb68f6551feec0cfc1809e49bba43f2d39fa57894b50cd6ccb428cc438230e065cac3eb24a1355a716e6ab8b7c7aaba79eef8cc633522d606bb008c101cf9832c9ad05d10a98472806b842b424335d94ccad97e54548dfa02673c1268aba38d3c3c32d28c8988b70ae458cbedf69109e7d57b7f8c96816615aac0017187d87abab9001d22db32bae3c8cbea9407c380ae944f052b5a442330057683c5abdbd453493f9750806afecadbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c6eb649e50315f962b32d487e696a81b4828631b11f8424daaaa37e9e97766a2c41dbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c6c8e9e2a1d30fed357d8a000c1131fc77ed65d2052918caf1ccc6eb1e7af3d13b535efb61d8784060218d9d6aa40eae55904de43779c1afc79c74dfefcfdf9125fdbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c6f850308dd4e453a2a1bc3efb444560401d95c1a8bdea5b35c5c79531cd2241f1cc77078e3530c08cdb2440817c81de4836500b4708ea4d15672b7fe98956423a7082b6d18de7b10e438e6b1002141db737519cb4ceed679bc815e3aac00cd2226b63f3482ec84cb93d74a71645ad2e9e896fa02723e42170b4aad20a4bed6cdd5c852254d5b703a16bb13b3e233a335d6459c5da5db0ca732d7a684ee05407846ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef196d9102c3340d5a65e2b9be034bbfee2b1e2fa95880c2e72d47ee926be36321094efef62d2ce60c14ffacd35a1b50546d3a9d503aff1df040176fffd6c92a36b5b37715a3e346e996104f0086703f19825def429233930fd9399c38e05fb112dbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c712b6136e423ab70d76431e2a635e877e16ea2dd9a895e054ad7f35f89d6c7b710bcfa5a69252c74010ad3e9ab18cfbdc116fcf85f084db1a8dc577a339fbee58a867d8f1a5e39d11d5bebde854d5359a509530ad32450f581da63c06ec8d1a77ff4271262f3552a4108cde54dc75061de8de495709221d793b126e448522d6a7f3f075dea7ef99b1e5db245018e5e4ed8d2a0d93953f5932b2176bef59bd690600fb4a5ac9287f4f508aa7253ee2d57c6a228b1b30e210d73fffd59389d3a8837a264697066735822122026b30be0cba45e0341a7bcbf628ae1565d510cecf6caf056837068e206c69b9264736f6c63430008140033
Deployed Bytecode
0x6080604052600436101561001d575b36614dd05761001b614dc7565b005b60003560e01c8063020f086e146104e85780630407de47146104e357806304843a17146104de578063056850c6146104d957806306fdde03146104d4578063095ea7b3146104cf5780630e18b681146104ca578063107703ab146104c5578063107d7fa0146104c05780631311cf8d146104bb578063143a08d4146104b6578063147bf6c4146104b15780631546962b146104ac57806315ca4cee146104a757806317e64858146104a257806318160ddd1461049d5780631bcbfaba1461049857806323b872dd14610493578063281a31221461048e578063291206f6146104895780632cb562e1146104845780632d6b59bf1461047f5780632dc5c97c1461047a578063313ce5671461047557806336bf33251461039e578063391b6f4e14610470578063395093511461046b5780633af9e6691461046657806346425ef0146104615780634b47b74f1461045c5780635022820114610457578063540bc5ea1461045257806354a29cdc1461044d57806354fd4d5014610448578063557ed1ba14610443578063563967151461043e57806357fa85471461043957806358bf3c7f146104345780635d95e8011461042f5780635f2e5f071461042a57806363f62aaf146104255780636e9960c31461042057806370a082311461041b57806372f79b1314610416578063748747e614610411578063750521f51461040c57806378a010e814610407578063799a1954146104025780637adbf973146103fd578063833b1fce146103f857806386a92af7146103f357806387f2adfb146103ee57806389896aef146103e95780639332525d146103e457806395d89b41146103df5780639b498e26146103da5780639d49cca1146103d5578063a29a839f146103d0578063a457c2d7146103cb578063a9059cbb146103c6578063ac232bde146103c1578063bb6583ec146103bc578063bf15af56146103b7578063c5eff3d0146103b2578063d0468156146103ad578063d0e30db0146103a8578063d4970524146103a3578063d7f8f4741461039e578063d96f186d14610399578063dd62ed3e14610394578063e3a88e611461038f578063efd603471461038a578063efd6684614610385578063f79c3f0214610380578063f9f95a901461037b578063fb5b82d0146103765763fe7c12ae0361000e57612bca565b612b4d565b612b1d565b612aff565b61251b565b6124ee565b6124ad565b612448565b612218565b611220565b6121eb565b6121d6565b6121a0565b61216a565b61214e565b6120e7565b6120c2565b612049565b611fe9565b611fc6565b611f90565b611f5a565b611f14565b611e19565b611db0565b611d85565b611cb9565b611c83565b611c42565b611c24565b611b34565b611ac2565b61197c565b61188d565b611846565b611810565b6117da565b6116a1565b611606565b611589565b611547565b61151c565b611501565b6114bb565b611411565b6113f5565b6113bf565b611351565b611326565b6112df565b61127a565b611244565b611204565b61119d565b611172565b61113c565b6110d2565b610ee2565b610d97565b610d6c565b610d41565b610bfe565b610b6f565b610b39565b610ac5565b610aa2565b610a25565b6109f4565b610887565b61081d565b6107ec565b610796565b61070d565b6106e2565b610648565b61058c565b600435906001600160a01b038216820361050357565b600080fd5b602435906001600160a01b038216820361050357565b606435906001600160a01b038216820361050357565b608435906001600160a01b038216820361050357565b60a435906001600160a01b038216820361050357565b60c435906001600160a01b038216820361050357565b60e435906001600160a01b038216820361050357565b34610503576020366003190112610503576105a56104ed565b6000805160206157ab833981519152546001600160a01b03919082163303610609576105d08161504f565b8060008051602061584b83398151915255167f67b26a33f305cc027b2d45b2f6f418793afcd3e22f7376afa7be068ce18604e8600080a2005b60405163472511eb60e11b8152336004820152602490fd5b6001600160401b0381160361050357565b602435906001600160801b038216820361050357565b3461050357610140366003190112610503576106626104ed565b60243561066e81610621565b6044359161067b83610621565b6064359161068883610621565b6084359161069583610621565b60a4356106a181610621565b6001600160801b03946101043594908686168603610503576101243596871687036105035761001b9760e4359560c43595612cf0565b600091031261050357565b346105035760003660031901126105035760206000805160206157eb83398151915254604051908152f35b6000366003190112610503576000805160206155ab833981519152546001600160a01b0316330361060957005b60005b83811061074d5750506000910152565b818101518382015260200161073d565b906020916107768151809281855285808601910161073a565b601f01601f1916010190565b90602061079392818152019061075d565b90565b34610503576000366003190112610503576107e86040516107b6816119fd565b6011815270098d2e2ead2c840a6e8c2d6cac8408aa89607b1b602082015260405191829160208352602083019061075d565b0390f35b34610503576040366003190112610503576108126108086104ed565b6024359033614b55565b602060405160018152f35b34610503576000806003193601126108845760008051602061556b8339815191528054906001600160a01b03821633036106095761085b8392612bf5565b55807f2a0f8515de3fa34ef68b99300347b8793c01683350743e96fe440594528298f48180a280f35b80fd5b34610503576040366003190112610503576004356108a3610508565b60008051602061582b833981519152549091906108d0906001600160a01b03165b6001600160a01b031690565b91823b1561050357604051632d20488160e11b815233600480830191909152602482015292600090849060449082905afa9182156109d65761097a936020936109db575b50610920813033614bc5565b506000805160206155ab83398151915254610943906001600160a01b03166108c4565b60405163107703ab60e01b815260048101929092526001600160a01b03909216602482015292839190829060009082906044820190565b03925af180156109d6576107e8916000916109a8575b5060405163ffffffff90911681529081906020820190565b6109c9915060203d81116109cf575b6109c18183611a6a565b81019061300e565b38610990565b503d6109b7565b613002565b806109e86109ee926119e5565b806106d7565b38610914565b3461050357600036600319011261050357602063ffffffff6000805160206157cb8339815191525416604051908152f35b3461050357602036600319011261050357610a3e6104ed565b6000805160206157ab833981519152546001600160a01b0391908216330361060957610a698161504f565b8060008051602061564b83398151915255167f1da4c245099590dc40be61880c9b97792f3694d970acc1e67ac0e6cc90f3780d600080a2005b34610503576000366003190112610503576020610abd613f5a565b604051908152f35b3461050357602036600319011261050357610ade6104ed565b6000805160206157ab833981519152546001600160a01b03919082163303610609578060008051602061556b83398151915255167f2a0f8515de3fa34ef68b99300347b8793c01683350743e96fe440594528298f4600080a2005b346105035760003660031901126105035760008051602061584b833981519152546040516001600160a01b039091168152602090f35b34610503576040366003190112610503576000805160206157ab833981519152546001600160a01b03163303610609577f5ab79ffcd89b6380c7fbdd89d02cfe3d9c53c99a85e150c2319075018d1aac5c604051610bcc816119fd565b600435808252610be460243592836020820152615497565b60408051918252602082019290925290819081015b0390a1005b3461050357600036600319011261050357610c176139b4565b506107e8604051610c2781611a18565b60008051602061562b83398151915254815260008051602061550b8339815191525460208201526000805160206155eb83398151915254604082015260008051602061568b8339815191525460608201527fdbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c705460808201526000805160206157cb8339815191525463ffffffff811660a0830152610cdd9060ff90602081901c8216151560c085015260281c16151560e0830152565b6040519182918291909160e080610100830194805184526020810151602085015260408101516040850152606081015160608501526080810151608085015263ffffffff60a08201511660a085015260c0810151151560c085015201511515910152565b3461050357600036600319011261050357602060008051602061558b83398151915254604051908152f35b3461050357600036600319011261050357602060008051602061578b83398151915254604051908152f35b3461050357606036600319011261050357610db06104ed565b610db8610508565b60443591610dc682826149d0565b8215610ed05782610dd6826154e3565b5410610ebe576001600160a01b0382811615610e92576001600160a01b038216600090815260008051602061572b8339815191526020908152604080832033845290915290205490848210610e65576107e8610e40868686868360018201610e52575b5050614bc5565b60405190151581529081906020820190565b610e5e91033383614b55565b8583610e39565b604051637b936de360e01b8152921660048301523360248301526044820152606481019290925250608490fd5b604051637617407560e11b81526001600160a01b038316600482015260006024820152604490fd5b0390fd5b6040516351940b3960e11b8152600490fd5b6040516336b216db60e21b8152600490fd5b346105035761012036600319011261050357610efc6104ed565b610f04610508565b90610f0d61051e565b90610f16610534565b92610f1f61054a565b93610f28610560565b90610f31610576565b95610104359160008051602061552b8339815191528054806110b3575060019055610f5b90612bf5565b610f648761504f565b6000805160206156eb8339815191528790556000966001600160a01b03948593849283167f0cc5437d7c9c1d9eab549acbb533eea3e9868e9443dd75309ed5820b33a3774e8b80a2610fb581615262565b60405190815260207fbd533e726baaf59b36f3914d950053f7e78f527057c97cd3f0043257fc0fc88491a1610fe98161504f565b8060008051602061564b83398151915255167f1da4c245099590dc40be61880c9b97792f3694d970acc1e67ac0e6cc90f3780d8880a2611028816150c1565b167f30f015a5d3c72c0a9414538199baa022323a483fa9e4ba2cd581596cf8ca04248680a261105681615419565b167fffc0721ef0563a1b0a51a0dc92113025f33ca434ada9ee3eebff2f385d2a8f9a8480a260443561108791613347565b61109090613936565b6040518061109e3682612c63565b0360008051602061554b83398151915291a180f35b60449060405190631cfd276760e31b8252600060048301526024820152fd5b34610503576020366003190112610503576000805160206157ab83398151915254600435906001600160a01b03163303610609576020816111337fbd533e726baaf59b36f3914d950053f7e78f527057c97cd3f0043257fc0fc88493615262565b604051908152a1005b346105035760003660031901126105035760008051602061564b833981519152546040516001600160a01b039091168152602090f35b346105035760003660031901126105035760206000805160206156ab83398151915254604051908152f35b34610503576000366003190112610503576111b66139f2565b5060a06111c16150dc565b6040519060806001600160401b03918281511684528260208201511660208501528260408201511660408501528260608201511660608501520151166080820152f35b3461050357600036600319011261050357602060405160128152f35b346105035760003660031901126105035760206040516801bc16d674ec8000008152f35b346105035760003660031901126105035760008051602061576b833981519152546040516001600160a01b039091168152602090f35b34610503576040366003190112610503576112936104ed565b33600090815260008051602061572b833981519152602090815260408083206001600160a01b038516845290915290205460243581018091116112da576108129133614b55565b612c40565b34610503576020366003190112610503576001600160a01b036113006104ed565b1660005260008051602061588b8339815191526020526020610abd604060002054614c42565b3461050357600036600319011261050357602060008051602061550b83398151915254604051908152f35b346105035760003660031901126105035761136a6150dc565b611373816148cd565b9060008051602061562b83398151915254906001600160401b03908181511683018093116112da57511680156113ba57820682039182116112da57602091610abd91615082565b612f5e565b34610503576000366003190112610503576000805160206156eb833981519152546040516001600160a01b039091168152602090f35b3461050357600036600319011261050357602060405160608152f35b346105035760003660031901126105035760008051602061552b83398151915280548060020361149c576003825561147f60008051602061580b833981519152546114696801bc16d674ec80000082068092036132fc565b6000805160206157eb83398151915254016132af565b60008051602061554b83398151915260405180610bf93682612c92565b60449060405190631cfd276760e31b8252600260048301526024820152fd5b34610503576000366003190112610503576107e86040516114db816119fd565b6005815264312e322e3160d81b602082015260405191829160208352602083019061075d565b34610503576000366003190112610503576020604051428152f35b3461050357600036600319011261050357602060008051602061560b83398151915254604051908152f35b34610503576020366003190112610503576004356001600160401b0361156b6150dc565b511680156113ba57810681039081116112da57602090604051908152f35b34610503576020366003190112610503576115a26104ed565b6000805160206157ab833981519152546001600160a01b03919082163303610609576115cd8161504f565b8060008051602061582b83398151915255167f30f015a5d3c72c0a9414538199baa022323a483fa9e4ba2cd581596cf8ca0424600080a2005b60003660031901126105035760008051602061560b833981519152546001600160a01b0316330361060957005b9181601f84011215610503578235916001600160401b038311610503576020808501948460051b01011161050357565b6020908160408183019282815285518094520193019160005b82811061168a575050505090565b835160070b8552938101939281019260010161167c565b3461050357602080600319360112610503576001600160401b03600435818111610503576116d3903690600401611633565b60018060a01b036000805160206155ab8339815191525416906040518092635f2e5f0760e01b8252866004830152818061171560009788966024840191612fbd565b03915afa9283156109d6578293611735575b604051806107e88682611663565b909192503d8084843e6117488184611a6a565b82019184818403126117d65780519182116117d657019181601f840112156108845782519061177682612f8d565b936117846040519586611a6a565b828552858086019360051b8201019384116117d2578501915b8383106117b557505050506107e89150388080611727565b82518060070b81036117ce57815291850191850161179d565b8280fd5b5080fd5b8380fd5b34610503576000366003190112610503576000805160206155ab833981519152546040516001600160a01b039091168152602090f35b34610503576000366003190112610503576000805160206157ab833981519152546040516001600160a01b039091168152602090f35b34610503576020366003190112610503576001600160a01b036118676104ed565b1660005260008051602061588b8339815191526020526020604060002054604051908152f35b34610503576000366003190112610503576118a66150dc565b6118af816148cd565b6001600160401b0382511680156113ba57810681039081116112da576107e861195f61195a8461191b61190f61194c8761191b61190f61194c61194661190f611938604061192160208d019861191b61190f8b516001600160401b031690565b6001600160401b031690565b90613461565b9b019a61191b61190f8d516001600160401b031690565b9e516001600160401b031690565b8c612c56565b92516001600160401b031690565b613474565b604051938493846040919493926060820195825260208201520152565b34610503576020366003190112610503576119956104ed565b6000805160206157ab833981519152546001600160a01b03163303610609576119bd8161504f565b60008051602061576b83398151915255005b634e487b7160e01b600052604160045260246000fd5b6001600160401b0381116119f857604052565b6119cf565b604081019081106001600160401b038211176119f857604052565b61010081019081106001600160401b038211176119f857604052565b60a081019081106001600160401b038211176119f857604052565b608081019081106001600160401b038211176119f857604052565b90601f801991011681019081106001600160401b038211176119f857604052565b60405190611a9882611a34565b565b60405190611a98826119fd565b6001600160401b0381116119f857601f01601f191660200190565b34610503576020366003190112610503576004356001600160401b0381116105035736602382011215610503578060040135611afd81611aa7565b90611b0b6040519283611a6a565b808252366024828501011161050357602081600092602461001b960183860137830101526130a6565b346105035760a0366003190112610503576000805160206157ab833981519152546001600160a01b0316330361060957611bca604051611b7381611a34565b600435611b7f81610621565b8152602435611b8d81610621565b6020820152604435611b9e81610621565b6040820152606435611baf81610621565b6060820152608435611bc081610621565b608082015261516a565b7f25777eb44be046f64180acf8275f0ac2ec51e63a65a5f8a0f2f6d86ba25b74cf611bf3613a1d565b611bfb613a29565b90610bf9611c07613a35565b611c0f613a41565b611c17613a4d565b9160405195869586613981565b34610503576020366003190112610503576020610abd600435614c65565b3461050357602036600319011261050357611c5b6104ed565b6000805160206157ab833981519152546001600160a01b031633036106095761001b90613936565b34610503576000366003190112610503576000805160206155cb833981519152546040516001600160a01b039091168152602090f35b3461050357600080600319360112610884576040518160008051602061570b8339815191528054611ce98161537e565b80855291600191808316908115611d5a5750600114611d1f575b6107e885611d1381890382611a6a565b60405191829182610782565b835260208084209095505b828410611d4757505050816107e893611d13928201019338611d03565b8054858501870152928501928101611d2a565b90506107e89650611d139450602092508593915060ff191682840152151560051b8201019338611d03565b346105035760003660031901126105035760206000805160206156cb83398151915254604051908152f35b3461050357600036600319011261050357602060008051602061562b83398151915254604051908152f35b6020908160408183019282815285518094520193019160005b828110611e02575050505090565b835160ff1685529381019392810192600101611df4565b3461050357600319604036820112610503576001600160401b0360043581811161050357611e4b903690600401611633565b6024929192359182116105035760009283611ec192611e71611eb2953690600401611633565b94909760018060a01b036000805160206155ab833981519152541695604051998a9889978896637c044e5560e01b8852608060048901526084880191612fbd565b92858403016024860152612fbd565b6001604483015261ffff606483015203925af180156109d6576107e891600091611ef3575b5060405191829182611ddb565b611f0e913d8091833e611f068183611a6a565b810190613023565b38611ee6565b34610503576000366003190112610503576107e8604051611f34816119fd565b6005815264098e68aa8960db1b602082015260405191829160208352602083019061075d565b346105035760003660031901126105035760008051602061586b833981519152546040516001600160a01b039091168152602090f35b3461050357600036600319011261050357611fa9612f74565b506040611fb4615434565b60208251918051835201516020820152f35b34610503576000366003190112610503576020610abd611fe46150dc565b6148cd565b34610503576040366003190112610503576120026104ed565b33600090815260008051602061572b833981519152602090815260408083206001600160a01b038516845290915290205460243581039081116112da576108129133614b55565b34610503576040366003190112610503576120626104ed565b6024359061207081336149d0565b8115610ed05781612080336154e3565b5410610ebe576001600160a01b038116156120a3576107e891610e409133614bc5565b604051637617407560e11b815233600482015260006024820152604490fd5b60203660031901126105035761001b6120d96104ed565b6120e28161504f565b614de2565b3461050357604036600319011261050357604051612104816119fd565b6004356001600160801b0381168103610503578152612121610632565b60208201526000805160206157ab833981519152546001600160a01b031633036106095761001b9061323d565b3461050357600036600319011261050357602060405160308152f35b346105035760003660031901126105035760008051602061582b833981519152546040516001600160a01b039091168152602090f35b346105035760003660031901126105035760008051602061556b833981519152546040516001600160a01b039091168152602090f35b60003660031901126105035761001b33614de2565b60003660031901126105035760008051602061584b833981519152546001600160a01b0316330361060957005b34610503576040806003193601126105035760008051602061576b833981519152546001600160a01b031633036124385760008051602061566b833981519152546020919061226f906001600160a01b03166108c4565b81519283809263c5f2892f60e01b825260049586915afa9081156109d65760009161240a575b50602435036123fd5760008051602061580b83398151915254916122c581356801bc16d674ec8000008504615071565b9081156123ef576122d582613562565b819291519384156123df5784116123d05760008051602061560b833981519152549182156123c2575060005b848110612399577f220ab8fd274cf58c09b0825ccf00e74ba4ce4117fd47285adc2183a635838f1b86866123466123418b61233b8461342a565b9061349a565b6132fc565b610bf96123826000805160206156cb833981519152549261237c61236a8286612c56565b6000805160206156cb83398151915255565b83612c56565b925191825260208201929092529081906040820190565b806123bc846123aa60019488613400565b516123b58487613400565b5190613740565b01612301565b8551639be7315960e01b8152fd5b508351635993bd5760e01b8152fd5b855163200149ad60e21b81528390fd5b8251631036b5ad60e31b8152fd5b51632b3b4dab60e21b8152fd5b61242b915060203d8111612431575b6124238183611a6a565b8101906133e7565b38612295565b503d612419565b5163c60eb33560e01b8152600490fd5b346105035760403660031901126105035760206124a46124666104ed565b61246e610508565b6001600160a01b03918216600090815260008051602061572b833981519152855260408082209290931681526020919091522090565b54604051908152f35b34610503576000366003190112610503576124c6612f74565b5060406124d1615214565b815190602060018060801b03918281511684520151166020820152f35b60003660031901126105035760008051602061564b833981519152546001600160a01b0316330361060957005b346105035760206003198181360112610503576004906001600160401b038235116105035761012090823536030112610503576000805160206155cb833981519152546001600160a01b03163303612ae1576125756150dc565b81358201359061258c6125888383614906565b1590565b612ac457612598613a59565b9060008051602061568b833981519152546040830181815260648635013591808310612aa257506125ca90518261349a565b91608084019283526000805160206155eb8339815191525490606085019382855260448835013592808410612a7f575060a48835019461260986613ad7565b6000805160206156cb83398151915254908163ffffffff80921611908115612a42575b506129e45750916126c995939161266a8961264c6126bc9896518661349a565b9360a08b0194855260008051602061562b8339815191525490614986565b60c0890152612677613f5a565b88526126868151835190612c56565b6129d0575b50506126956139b4565b96875260248835013589880152604087015260608601526084863501356080860152613ad7565b63ffffffff1660a0840152565b6126e26126d960e4853501613aeb565b151560c0840152565b612706610104843501926127016126f885613aeb565b151560e0830152565b61528f565b61270e615434565b93612720825160c0840151908761496c565b612728613f5a565b83830181815284519097918490821161291c575050865161274a838651612c56565b106128de57509160c09161279a61282f946127897f49ac0d2bb2a688ca460f7993eb93eccd3b9c9188da6b0727e9a409cf8b105875995185519061349a565b61012085019081515251519061349a565b60e08301525b60e0820151908161289c575b505060e08101518061286b575b5060e081015180612852575b5061281861280d61012083019586515180612843575b506127ed60c489350189358a01613af5565b92906128056127ff60e48c3501613aeb565b93613aeb565b933691613b2a565b6084893501356144b2565b6128206142a7565b6128286147a0565b0151614814565b5190610bf960405192828493350183613bc1565b61284c90613e37565b386127db565b61285b90613d62565b60606101208301510152386127c5565b6040612879612891926141e1565b610120840190828251015251015160e083015161349a565b60e0820152386127b9565b6128a86128d292613c95565b610120840190828251015280516128c3838201518251612c56565b905251015160e083015161349a565b60e082015238806127ac565b8351965160c09094015190516040805163eb7a968960e01b815297880198895260208901959095529387015250506060840152509081900360800190fd5b61292f9061271092930192835190613461565b0461294288519161233b87518092615071565b1161299257509160c09161298861282f946129827f49ac0d2bb2a688ca460f7993eb93eccd3b9c9188da6b0727e9a409cf8b10587599855190519061349a565b90612c56565b60e08301526127a0565b8351965160c09094015190516040805163063bb83f60e11b815297880198895260208901959095529387015250506060840152509081900360800190fd5b6129dd9151905190614057565b388061268b565b89610eba6129f189613ad7565b92612a0e6000805160206157cb8339815191525463ffffffff1690565b90604051948594632f776b1760e01b8652850191939290604091606084019563ffffffff8093168552602085015216910152565b9050612a4d88613ad7565b90612a76612a6d6000805160206157cb8339815191525463ffffffff1690565b63ffffffff1690565b9116103861262c565b60408051636f24834360e01b8152808b0192835260208301869052918291010390fd5b60408051621ee1cf60e11b815280890192835260208301859052918291010390fd5b5060405163a225656d60e01b815291820190815281906020010390fd5b60405163472511eb60e11b8152339181019182529081906020010390fd5b34610503576020366003190112610503576020610abd600435614c42565b34610503576020366003190112610503576020612b43600435612b3e6150dc565b614906565b6040519015158152f35b3461050357602036600319011261050357612b666104ed565b6000805160206157ab833981519152546001600160a01b0391908216330361060957612b918161504f565b806000805160206156eb83398151915255167f0cc5437d7c9c1d9eab549acbb533eea3e9868e9443dd75309ed5820b33a3774e600080a2005b3461050357600036600319011261050357602060008051602061580b83398151915254604051908152f35b612bfe8161504f565b6000805160206157ab8339815191528190556001600160a01b03167f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1600080a2565b634e487b7160e01b600052601160045260246000fd5b919082018092116112da57565b6060916000825260406020830152806040830152806000848401376000828201840152601f01601f1916010190565b6060916002825260406020830152806040830152806000848401376000828201840152601f01601f1916010190565b6060916001825260406020830152806040830152806000848401376000828201840152601f01601f1916010190565b979896959694919390929460008051602061552b833981519152805480600103612f3f575060029055612d228961504f565b6000805160206155ab833981519152899055604080516001600160a01b038b168152909a907faf890c07e266df31e0725841eb0e85596a0caa1b17bbae5c0b206fdcc92d7ce190602090a18a5191612d79836119fd565b6001600160801b039182168352166020820152612d959061323d565b612d9d611a8b565b6001600160401b03841681526001600160401b03851660208201526001600160401b038616818b01526001600160401b03821660608201526001600160401b0383166080820152612ded9061516a565b8851948594612dfc9486613981565b037f25777eb44be046f64180acf8275f0ac2ec51e63a65a5f8a0f2f6d86ba25b74cf91a1612e28611a9a565b828152816020820152612e3a90615497565b835191825260208201527f5ab79ffcd89b6380c7fbdd89d02cfe3d9c53c99a85e150c2319075018d1aac5c90604090a1612e726139b4565b7fd7f2d45e512a86049f7a113657b39731b6b558609584243063a52cd31a8eb5285481527ffedfd2c285a57fb23bf45a3fe9ac02d36a76ebb72801b1c8aaf553d74e9d4652546020820152600083820181905260608201819052608082018190527f0f1e7733641e4d843128fea0d2ec90d3d06a40b0fe244ff603d8c1aa200dc0f85463ffffffff1660a083015260c0820181905260e0820152612f159061528f565b612f1f9030614ad7565b5180612f2b3682612cc1565b0360008051602061554b83398151915291a1565b60449060405190631cfd276760e31b8252600160048301526024820152fd5b634e487b7160e01b600052601260045260246000fd5b60405190612f81826119fd565b60006020838281520152565b6001600160401b0381116119f85760051b60200190565b63ffffffff81160361050357565b3590611a9882612fa4565b91908082526020809201929160005b828110612fda575050505090565b90919293828060019263ffffffff8835612ff381612fa4565b16815201950193929101612fcc565b6040513d6000823e3d90fd5b90816020910312610503575161079381612fa4565b6020908181840312610503578051906001600160401b03821161050357019180601f8401121561050357825161305881612f8d565b936130666040519586611a6a565b818552838086019260051b820101928311610503578301905b82821061308d575050505090565b815160ff8116810361050357815290830190830161307f565b6000805160206157ab833981519152549091906001600160a01b031633036106095781511561322b578151916001600160401b0383116119f85760008051602061570b83398151915292613103816130fe865461537e565b6153b8565b602080601f83116001146131735750938161316393927f8d2df192dd17edf92a7964b78aa322f3d717b2ab9de00651bee32bbc4c5da63a9596600091613168575b508160011b916000199060031b1c191617905560405191829182610782565b0390a1565b905083015138613144565b60008051602061570b83398151915260005290601f1983167f992d979edf4f3c53d0b672011f4c4543809e6b0ed7af1d628abb72f3026d823f926000905b828210613213575050927f8d2df192dd17edf92a7964b78aa322f3d717b2ab9de00651bee32bbc4c5da63a959692600192826131639796106131fa575b5050811b019055611d13565b85015160001960f88460031b161c1916905538806131ee565b80600185968294968a015181550195019301906131b1565b604051638d46fe0560e01b8152600490fd5b60407e4180017d3dd609da6980999655a6bd2591e313d31d6230b1889c369a9713a09160018060801b0360208183511692015160018060801b03198160801b1683177fc305db9f63aeed94a45229ff5ab4174e72b223a38ab4c5e8f518ee4f437918d0558351928352166020820152a1565b6000805160206157eb833981519152805460408051918252602082018490527f48f67c1dada0cab2163f6282292ad97ea97376cfed46bb3851654aaa630db7279190819081015b0390a155565b60008051602061580b833981519152805460408051918252602082018490527f86fd21e9b5bd76b20471c7f93a82aa4e25c37d48b179bda0a4d1a45e22a842f49190819081016132f6565b6133508161504f565b60008051602061566b833981519152819055604051906001600160a01b03167e043cf7635f276413ae358250286a479a631abd9d74d57d4aa0bb87ebc7d117600080a281156133d857506020817f4c86ba184ea1a1558f84835ca34f6d67e222e8ee5cc4f324b8861dda4cf1740c9260008051602061560b83398151915255604051908152a1565b63a9cb9e0d60e01b8152600490fd5b90816020910312610503575190565b81156113ba570490565b80518210156134145760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b906801bc16d674ec80000091808302928304036112da57565b906801bc16d674ec800000918281029281840414901517156112da57565b818102929181159184041417156112da57565b6000198101919082116112da57565b6801bc16d674ec7fffff198101919082116112da57565b919082039182116112da57565b9080601f83011215610503578151916134bf83612f8d565b926040906134cf82519586611a6a565b808552602093848087019260051b8501019381851161050357858101925b8584106134fe575050505050505090565b83516001600160401b0381116105035782019083603f8301121561050357878201519061352a82611aa7565b61353688519182611a6a565b82815285888486010111610503576135578a949385948a868501910161073a565b8152019301926134ed565b60008051602061586b8339815191525460405163277c9d4560e01b81526004810192909252600092919083908290602490829084906001600160a01b03165af19283156109d657809181946135b8575b50509190565b92915092503d8084843e6135cc8184611a6a565b82016040838203126117d65782516001600160401b039081811161361857826135f69186016134a7565b9360208101519182116136185761361093949550016134a7565b9138806135b2565b8580fd5b9061362f6020928281519485920161073a565b0190565b90611a9860306040518461365182965180926020808601910161073a565b810160006020820152036010810185520183611a6a565b90611a98604080518461368582965180926020808601910161073a565b810160006020820152036020810185520183611a6a565b9060405191634059730760d81b6020840152604083015260408252606082018281106001600160401b038211176119f857604052565b9190604051926020840152604083015260408252606082018281106001600160401b038211176119f857604052565b94939261372d60609361371f61373b9460808a5260808a019061075d565b9088820360208a015261075d565b90868203604088015261075d565b930152565b909160308251036139245760608351036139125761375d82613633565b604093845191826020809282613776600097889361361c565b03600293845afa156109d6578351828561379d61379287614fb0565b8b519182809261361c565b0390855afa156109d657845183866137ca6137bf6137ba89615009565b613668565b8c519182809261361c565b0390865afa156109d657856137e561379286938351906136d2565b0390855afa156109d6578285613800613792898351956136d2565b0390855afa156109d657828561381a61379282519461369c565b0390855afa156109d657829161384161383687938451906136d2565b8a519182809261361c565b03915afa156109d65782519361385647613483565b60008051602061566b833981519152549096906138a19061387f906001600160a01b03166108c4565b936138938a51948592830160209181520190565b03601f198101845283611a6a565b823b1561390e576801bc16d674ec800000906138d28951978896879586946304512a2360e31b865260048601613701565b03925af180156109d6576138fb575b5047036138eb5750565b51638051a6bb60e01b8152600490fd5b806109e8613908926119e5565b386138e1565b8480fd5b60405163408ebd3960e01b8152600490fd5b60405163050cb55360e41b8152600490fd5b61393f8161504f565b6000805160206155cb8339815191528190556001600160a01b03167fd3b5d1e0ffaeff528910f3663f0adace7694ab8241d58e17a91351ced2e08031600080a2565b6001600160401b039182168152918116602083015291821660408201529181166060830152909116608082015260a00190565b604051906139c182611a18565b8160e06000918281528260208201528260408201528260608201528260808201528260a08201528260c08201520152565b604051906139ff82611a34565b60006080838281528260208201528260408201528260608201520152565b60043561079381610621565b60243561079381610621565b60443561079381610621565b60643561079381610621565b60843561079381610621565b6040519061014082018281106001600160401b038211176119f857604052816101206000918281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015260405192613abf84611a4f565b80845280602085015280604085015260608401520152565b3561079381612fa4565b8015150361050357565b3561079381613ae1565b903590601e198136030182121561050357018035906001600160401b03821161050357602001918160051b3603831361050357565b9291613b3582612f8d565b91613b436040519384611a6a565b829481845260208094019160051b810192831161050357905b828210613b695750505050565b8380918335613b7781612fa4565b815201910190613b5c565b9035601e19823603018112156105035701602081359101916001600160401b038211610503578160051b3603831361050357565b3590611a9882613ae1565b92916020611a989260a08652803560a08701528181013560c0870152604081013560e0870152613c6f613c656101006060840135818a0152613c456101206080860135818c0152613c25613c1760a08801612fb2565b63ffffffff166101408d0152565b613c3260c0870187613b82565b90916101608d01526101c08c0191612fbd565b93613c5f613c5560e08301613bb6565b15156101808c0152565b01613bb6565b15156101a0880152565b940190606080918051845260208101516020850152604081015160408501520151910152565b60008051602061564b8339815191525447916001600160a01b0390911690813b156105035760009160248392604051948593849263c8a6dfd360e01b845260048401525af180156109d657613d53575b504790808203918083116112da5703613d2d575b6040518181527fda841d3042d792e2509a333b9dcbd4b3dd9b9047d382011f8788fab90ca7e3c79080602081015b0390a190565b613d4e613d49826000805160206157eb83398151915254612c56565b6132af565b613cf9565b613d5c906119e5565b38613ce5565b60008051602061584b833981519152546001600160a01b03168015613e30574790803b1561050357604051638ede6b6b60e01b815260048101939093526000908390602490829084905af19182156109d657613dc492613e1d575b504761349a565b80613dfc575b6040518181527fd500b67e5bd8019c0af744cadeec120d1b5e3d3a3a011f18cf182aa4c97947b6908060208101613d27565b613e18613d49826000805160206157eb83398151915254612c56565b613dca565b806109e8613e2a926119e5565b38613dbd565b5050600090565b60008051602061558b8339815191529081548015613f4857613e57613f5a565b9260008051602061578b83398151915254613e7b81613e768587613461565b613461565b906127108087029087820414871517156112da5761233b613e9c9287613461565b80613f395750506000905b81613eb4575b5050505050565b7f3d1669e813a9845c288f0e1f642a4343a451103b87886d12de37e63b39bbd94293613eff613f2c92613ef76000805160206156eb833981519152549586614c8d565b54918761349a565b9560405194859460018060a01b031697859094939260609260808301968352602083015260408201520152565b0390a23880808080613ead565b613f42916133f6565b90613ea7565b604051630d35acd760e21b8152600490fd5b63ffffffff6000805160206157cb83398151915254166000805160206156cb83398151915254808210600014613ffa57612982613ff561079393613fef613fd9613fc360008051602061550b833981519152546000805160206157eb8339815191525490612c56565b60008051602061580b8339815191525490612c56565b6000805160206156ab8339815191525490612c56565b9361349a565b613443565b505060008051602061550b833981519152546000805160206157eb8339815191525481018091116112da5760008051602061580b8339815191525481018091116112da576000805160206156ab8339815191525461079391612c56565b9190476140648285612c56565b60008051602061560b83398151915254909190614089906001600160a01b03166108c4565b803b156105035760405163ea74f47960e01b815260048101939093526000908390602490829084905af19182156109d6576140c992613e1d57504761349a565b926140d48282612c56565b840361416a577fcb5410dc8f29b2f498e023c3f9237dbd600255a717edf94a6072bcd03b0c773c92935080614149575b81614123575b6040805191825260208201929092529081908101613163565b61414461413f836000805160206156ab83398151915254612c56565b614196565b61410a565b614165613d49826000805160206157eb83398151915254612c56565b614104565b6141749250612c56565b6040516349a1938b60e01b815260048101919091526024810191909152604490fd5b6000805160206156ab833981519152805460408051918252602082018490527f215c2b83e8c232e42091088056ab75d2ff643855c32997024f786cddb22d22909190819081016132f6565b6000805160206155ab8339815191525447916001600160a01b0390911690813b1561050357600091602483926040519485938492630c77940160e01b845260048401525af180156109d657614298575b504790808203918083116112da5703614277575b6040518181527f4e484734eb4d444bfa106f917d05d9ceb8ce18bf516c85d7aeb9b322925339f9908060208101613d27565b614293613d49826000805160206157eb83398151915254612c56565b614245565b6142a1906119e5565b38614231565b6000805160206155ab833981519152546001600160a01b03166142c8613f5a565b60008051602061558b833981519152549015159081614440575b506142ea5750565b6040516303634a9560e21b8152906001600160a01b0316602082600481845afa9182156109d657600092614420575b508161432483614c42565b926000805160206156ab83398151915254908185116143e8575b604080519182526020820184905281018590527f709263092d8d9fef472d907900405b5edae3d76f8ff4354858025b18424d710190606090a183614383575b50505050565b8361438e9103614196565b6143988183614d0a565b813b156105035760405163b30d8bb760e01b815260048101919091529160009183916024918391905af180156109d6576143d5575b80808061437d565b806109e86143e2926119e5565b386143cd565b9150809350927f709263092d8d9fef472d907900405b5edae3d76f8ff4354858025b18424d710161441882614c65565b92905061433e565b61443991925060203d8111612431576124238183611a6a565b9038614319565b90501515386142e2565b92919060408401604085528151809152606085019060208093019060005b848282106144795750505050930152565b835163ffffffff1685529384019390920191600101614468565b919082604091031261050357602082516144ac81612fa4565b92015190565b60008051602061586b8339815191528054929594909390926144dc906001600160a01b03166108c4565b936000805160206156cb83398151915293845495803b156105035761451f966040998a5192838092632d01785d60e21b82528160009c8d9687936004840161444a565b03925af180156109d65761478d575b506145485760008051602061558b83398151915254614551575b50505050509050565b6000805160206156ab83398151915254906000805160206157eb83398151915254600460206145976108c46000805160206155ab8339815191525460018060a01b031690565b8b516303634a9560e21b815292839182905afa80156109d6576145c191899161476f575b50614c42565b93846145cd8786612c56565b106145db575b505050614548565b80614766575b614715575b50546145fa906001600160a01b03166108c4565b928751634a3c63bb60e01b81528881600481885afa9081156109d657879088926146e1575b5063ffffffff1690818111156146d65761463c91613ff59161349a565b91836146518461464c8585612c56565b612c56565b1061465d575b806145d3565b6146729261464c614677959361233b93612c56565b61508e565b915491813b156117d6579451632d1fc39d60e01b8152600481019590955260248501919091529192918290604490829084905af180156109d6576146c3575b8080808080808796614657565b806109e86146d0926119e5565b386146b6565b505061463c86613443565b63ffffffff925061470891508a3d8c1161470e575b6147008183611a6a565b810190614493565b9161461f565b503d6146f6565b61473161472b84614726888861349a565b61349a565b82615071565b8061473d575b506145e6565b91614760613d49846147566145fa966108c49698612c56565b9661472688614196565b91614737565b508015156145e1565b614787915060203d8111612431576124238183611a6a565b386145bb565b806109e861479a926119e5565b3861452e565b6000805160206156ab833981519152805490816147bb575050565b6000805160206157eb833981519152549182018092116112da576147e06000926132af565b7f215c2b83e8c232e42091088056ab75d2ff643855c32997024f786cddb22d2290604082548151908152846020820152a155565b61481c613f5a565b906000805160206157eb8339815191525491614836615214565b9160018060801b03602081855116940151168483039283116112da576201518061488361488993613e7661488f9761271061487c6801bc16d674ec800000998c98613461565b0490615082565b04615071565b04613443565b80614898575050565b60008051602061580b833981519152548181018091116112da576148bb906132fc565b81039081116112da57611a98906132af565b6001600160401b0390816060820151164203914283116112da57806040830151169182156113ba5760200151169182156113ba57040490565b90614910826148cd565b6001600160401b0390816080850151168301908184116112da5710159283614953575b8361493f575b50505090565b5116915081156113ba570615388080614939565b60008051602061562b8339815191525483119350614933565b91613e766149829264496cebb800945190613461565b0490565b9082039182116112da576001600160401b03908160208160408401511692015116029081169081036112da5761079391613461565b90816020910312610503575161079381613ae1565b60008051602061582b833981519152546149f2906001600160a01b03166108c4565b60405163e838dfbb60e01b8082526001600160a01b038416600483015260209390918481602481875afa9081156109d657600091614aba575b50614a6a57506040519081526001600160a01b0384166004820152908290829060249082905afa9182156109d657600092614a8d575b5050614a6a5750565b60405163e7d05e2760e01b81526001600160a01b03919091166004820152602490fd5b614aac9250803d10614ab3575b614aa48183611a6a565b8101906149bb565b3880614a61565b503d614a9a565b614ad19150853d8711614ab357614aa48183611a6a565b38614a2b565b614ae08161504f565b614ae98261504f565b6001600160a01b03908116600081815260008051602061572b83398151915260209081526040808320868616845282529091206000199492937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92593909186905b556040519586521693a3565b60207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591614b828161504f565b614b8b8461504f565b6001600160a01b03908116600081815260008051602061572b83398151915284526040808220878516835260205290209094908690614b49565b9160018060a01b038093169160009383855260008051602061588b833981519152908160205260408620548481039081116112da57604092868852602052828720558216948581522054918083018093116112da5760008051602061574b83398151915292614c356020936154e3565b55604051908152a3600190565b60008051602061558b83398151915254908115613e30576149829061191b613f5a565b614c6d613f5a565b908115613e305760008051602061558b8339815191525461498291613461565b60008051602061558b833981519152548281018091116112da57614cb090614d89565b60018060a01b0316908160005260008051602061588b83398151915280602052604060002054908282018092116112da5760009260008051602061574b8339815191529260209286865283526040852055604051908152a3565b60008051602061558b833981519152548281039081116112da57614d2d90614d89565b6001600160a01b0316600081815260008051602061588b8339815191526020819052604090912054838103919082116112da5760009360008051602061574b8339815191529260209285875283526040862055604051908152a3565b60207fc80ea35a3f9016535e5b7c87746740c5045afe42188d02c5786eb97495c2f429918060008051602061558b83398151915255604051908152a1565b611a9833614de2565b60405163574b16a760e11b8152600490fd5b3415614f37576000805160206157eb833981519152543481018091116112da57614e0b906132af565b614e153433614f49565b60008051602061582b83398151915254614e37906001600160a01b03166108c4565b91823b1561050357604051632d20488160e11b815233600482015260016024820152600081604481875afa80156109d657614f24575b506001600160a01b0381169233849003614eb5575b50506040513481523391507f3bc57f469ad6d10d7723ea226cd22bd2b9e527def2b529f6ab44645a1668958290602090a3565b60405163e838dfbb60e01b81526001600160a01b038316600482015290602090829060249082905afa9081156109d657600091614f06575b50614a6a5790614efd9133614bc5565b50388080614e82565b614f1e915060203d8111614ab357614aa48183611a6a565b38614eed565b806109e8614f31926119e5565b38614e6d565b6040516395b66fe960e01b8152600490fd5b9190614f53613f5a565b908082038281116112da578160008051602061558b83398151915254931492838015614fa8575b15614f8d575050611a9891508093614c8d565b614f979192613461565b916113ba57611a9891048093614c8d565b508015614f7a565b6040815110614ff75760405190602080830191810190606084015b808410614fe65750505060408252601f801991011660405290565b825184529281019291810191614fcb565b604051633b99b53d60e01b8152600490fd5b6060815110614ff757604051906020808301916060604085019101905b80841061503e5750508252601f01601f191660405290565b815184529282019290820190615026565b6001600160a01b03161561505f57565b60405163f6b2911f60e01b8152600490fd5b8181111561507d575090565b905090565b8181101561507d575090565b6801bc16d674ec800000808204916000919006156150b9575060ff60015b1681018091116112da5790565b60ff906150ac565b6150ca8161504f565b60008051602061582b83398151915255565b6150e46139f2565b506040516150f181611a34565b7fd84ee2c84c954c65bef1459fe03b761a42f49234527e3cd8fd9dce87cb83742a546001600160401b03908181168352818160401c166020840152818160801c16604084015260c01c60608301527fd84ee2c84c954c65bef1459fe03b761a42f49234527e3cd8fd9dce87cb83742b5416608082015290565b60806001600160401b0391828151166020820151906040830151906001600160401b0360c01b606085015160c01b16926001600160401b0360401b9060401b1617906001600160401b03851b90851b1617177fd84ee2c84c954c65bef1459fe03b761a42f49234527e3cd8fd9dce87cb83742a550151167fd84ee2c84c954c65bef1459fe03b761a42f49234527e3cd8fd9dce87cb83742b906001600160401b0319825416179055565b61521c612f74565b50604051615229816119fd565b7fc305db9f63aeed94a45229ff5ab4174e72b223a38ab4c5e8f518ee4f437918d0546001600160801b038116825260801c602082015290565b612710811161527d5760008051602061578b83398151915255565b6040516358d620b360e01b8152600490fd5b61535360e0611a9892805160008051602061562b83398151915255602081015160008051602061550b8339815191525560408101516000805160206155eb83398151915255606081015160008051602061568b8339815191525560808101517fdbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c705563ffffffff60a0820151166000805160206157cb8339815191529081549064ff0000000060c0850151151560201b169164ffffffffff1916171790550151151590565b6000805160206157cb8339815191529060ff60281b825491151560281b169060ff60281b1916179055565b90600182811c921680156153ae575b602083101461539857565b634e487b7160e01b600052602260045260246000fd5b91607f169161538d565b601f81116153c4575050565b60009060008051602061570b833981519152825260208220906020601f850160051c8301941061540f575b601f0160051c01915b82811061540457505050565b8181556001016153f8565b90925082906153ef565b6154228161504f565b60008051602061586b83398151915255565b61543c612f74565b50604051615449816119fd565b7ff81b149de6749a3f46464b2cce61e24462f67599ea4a5ce028aaf4ab1521f96d5481527ff81b149de6749a3f46464b2cce61e24462f67599ea4a5ce028aaf4ab1521f96e54602082015290565b80517ff81b149de6749a3f46464b2cce61e24462f67599ea4a5ce028aaf4ab1521f96d55602001517ff81b149de6749a3f46464b2cce61e24462f67599ea4a5ce028aaf4ab1521f96e55565b6001600160a01b0316600090815260008051602061588b833981519152602052604090209056fedbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c6d82055909238c0f5e63d6f174068ebb8f51bcec9bd37de63bb68f6551feec0cfc1809e49bba43f2d39fa57894b50cd6ccb428cc438230e065cac3eb24a1355a716e6ab8b7c7aaba79eef8cc633522d606bb008c101cf9832c9ad05d10a98472806b842b424335d94ccad97e54548dfa02673c1268aba38d3c3c32d28c8988b70ae458cbedf69109e7d57b7f8c96816615aac0017187d87abab9001d22db32bae3c8cbea9407c380ae944f052b5a442330057683c5abdbd453493f9750806afecadbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c6eb649e50315f962b32d487e696a81b4828631b11f8424daaaa37e9e97766a2c41dbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c6c8e9e2a1d30fed357d8a000c1131fc77ed65d2052918caf1ccc6eb1e7af3d13b535efb61d8784060218d9d6aa40eae55904de43779c1afc79c74dfefcfdf9125fdbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c6f850308dd4e453a2a1bc3efb444560401d95c1a8bdea5b35c5c79531cd2241f1cc77078e3530c08cdb2440817c81de4836500b4708ea4d15672b7fe98956423a7082b6d18de7b10e438e6b1002141db737519cb4ceed679bc815e3aac00cd2226b63f3482ec84cb93d74a71645ad2e9e896fa02723e42170b4aad20a4bed6cdd5c852254d5b703a16bb13b3e233a335d6459c5da5db0ca732d7a684ee05407846ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef196d9102c3340d5a65e2b9be034bbfee2b1e2fa95880c2e72d47ee926be36321094efef62d2ce60c14ffacd35a1b50546d3a9d503aff1df040176fffd6c92a36b5b37715a3e346e996104f0086703f19825def429233930fd9399c38e05fb112dbc0527c99b54cd325d6ce9eaae3a8413cd447b5205f37385f0ebc6551033c712b6136e423ab70d76431e2a635e877e16ea2dd9a895e054ad7f35f89d6c7b710bcfa5a69252c74010ad3e9ab18cfbdc116fcf85f084db1a8dc577a339fbee58a867d8f1a5e39d11d5bebde854d5359a509530ad32450f581da63c06ec8d1a77ff4271262f3552a4108cde54dc75061de8de495709221d793b126e448522d6a7f3f075dea7ef99b1e5db245018e5e4ed8d2a0d93953f5932b2176bef59bd690600fb4a5ac9287f4f508aa7253ee2d57c6a228b1b30e210d73fffd59389d3a8837a264697066735822122026b30be0cba45e0341a7bcbf628ae1565d510cecf6caf056837068e206c69b9264736f6c63430008140033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.