Source Code
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
EthPrivVault
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {IEthPrivVault} from '../../interfaces/IEthPrivVault.sol';
import {IEthVaultFactory} from '../../interfaces/IEthVaultFactory.sol';
import {VaultEthStaking, IVaultEthStaking} from '../modules/VaultEthStaking.sol';
import {VaultOsToken, IVaultOsToken} from '../modules/VaultOsToken.sol';
import {VaultWhitelist} from '../modules/VaultWhitelist.sol';
import {IVaultVersion} from '../modules/VaultVersion.sol';
import {EthVault, IEthVault} from './EthVault.sol';
/**
* @title EthPrivVault
* @author StakeWise
* @notice Defines the Ethereum staking Vault with whitelist
*/
contract EthPrivVault is Initializable, EthVault, VaultWhitelist, IEthPrivVault {
// slither-disable-next-line shadowing-state
uint8 private constant _version = 3;
/**
* @dev Constructor
* @dev Since the immutable variable value is stored in the bytecode,
* its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
* @param _keeper The address of the Keeper contract
* @param _vaultsRegistry The address of the VaultsRegistry contract
* @param _validatorsRegistry The contract address used for registering validators in beacon chain
* @param osTokenVaultController The address of the OsTokenVaultController contract
* @param osTokenConfig The address of the OsTokenConfig contract
* @param osTokenVaultEscrow The address of the OsTokenVaultEscrow contract
* @param sharedMevEscrow The address of the shared MEV escrow
* @param depositDataRegistry The address of the DepositDataRegistry contract
* @param exitingAssetsClaimDelay The delay after which the assets can be claimed after exiting from staking
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(
address _keeper,
address _vaultsRegistry,
address _validatorsRegistry,
address osTokenVaultController,
address osTokenConfig,
address osTokenVaultEscrow,
address sharedMevEscrow,
address depositDataRegistry,
uint256 exitingAssetsClaimDelay
)
EthVault(
_keeper,
_vaultsRegistry,
_validatorsRegistry,
osTokenVaultController,
osTokenConfig,
osTokenVaultEscrow,
sharedMevEscrow,
depositDataRegistry,
exitingAssetsClaimDelay
)
{}
/// @inheritdoc IEthVault
function initialize(
bytes calldata params
) external payable virtual override(IEthVault, EthVault) reinitializer(_version) {
// if admin is already set, it's an upgrade from version 2 to 3
if (admin != address(0)) {
__EthVault_initV3();
return;
}
// initialize deployed vault
address _admin = IEthVaultFactory(msg.sender).vaultAdmin();
__EthVault_init(
_admin,
IEthVaultFactory(msg.sender).ownMevEscrow(),
abi.decode(params, (EthVaultInitParams))
);
// whitelister is initially set to admin address
__VaultWhitelist_init(_admin);
}
/// @inheritdoc IVaultEthStaking
function deposit(
address receiver,
address referrer
) public payable virtual override(IVaultEthStaking, VaultEthStaking) returns (uint256 shares) {
_checkWhitelist(msg.sender);
_checkWhitelist(receiver);
return super.deposit(receiver, referrer);
}
/// @inheritdoc VaultEthStaking
receive() external payable virtual override {
_checkWhitelist(msg.sender);
_deposit(msg.sender, msg.value, address(0));
}
/// @inheritdoc IVaultOsToken
function mintOsToken(
address receiver,
uint256 osTokenShares,
address referrer
) public virtual override(IVaultOsToken, VaultOsToken) returns (uint256 assets) {
_checkWhitelist(msg.sender);
return super.mintOsToken(receiver, osTokenShares, referrer);
}
/// @inheritdoc IVaultVersion
function vaultId() public pure virtual override(IVaultVersion, EthVault) returns (bytes32) {
return keccak256('EthPrivVault');
}
/// @inheritdoc IVaultVersion
function version() public pure virtual override(IVaultVersion, EthVault) returns (uint8) {
return _version;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
struct ReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
assembly {
$.slot := ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTERED
if ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function _nonReentrantAfter() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.20;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.20;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Sorts the pair (a, b) and hashes the result.
*/
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.20;
import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Safe Wallet (previously Gnosis Safe).
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
(address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
return
(error == ECDSA.RecoverError.NoError && recovered == signer) ||
isValidERC1271SignatureNow(signer, hash, signature);
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.22;
import '../interfaces/IMulticall.sol';
/**
* @title Multicall
* @author Uniswap
* @notice Adopted from https://github.com/Uniswap/v3-periphery/blob/1d69caf0d6c8cfeae9acd1f34ead30018d6e6400/contracts/base/Multicall.sol
* @notice Enables calling multiple methods in a single call to the contract
*/
abstract contract Multicall is IMulticall {
/// @inheritdoc IMulticall
function multicall(bytes[] calldata data) external override returns (bytes[] memory results) {
uint256 dataLength = data.length;
results = new bytes[](dataLength);
for (uint256 i = 0; i < dataLength; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
if (!success) {
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
if (result.length < 68) revert();
assembly {
result := add(result, 0x04)
}
revert(abi.decode(result, (string)));
}
results[i] = result;
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IKeeperValidators} from './IKeeperValidators.sol';
import {IKeeperRewards} from './IKeeperRewards.sol';
import {IMulticall} from './IMulticall.sol';
/**
* @title IDepositDataRegistry
* @author StakeWise
* @notice Defines the interface for DepositDataRegistry
*/
interface IDepositDataRegistry is IMulticall {
/**
* @notice Event emitted on deposit data manager update
* @param vault The address of the vault
* @param depositDataManager The address of the new deposit data manager
*/
event DepositDataManagerUpdated(address indexed vault, address depositDataManager);
/**
* @notice Event emitted on deposit data root update
* @param vault The address of the vault
* @param depositDataRoot The new deposit data Merkle tree root
*/
event DepositDataRootUpdated(address indexed vault, bytes32 depositDataRoot);
/**
* @notice Event emitted on deposit data migration
* @param vault The address of the vault
* @param depositDataRoot The deposit data root
* @param validatorIndex The index of the next validator to be registered
* @param depositDataManager The address of the deposit data manager
*/
event DepositDataMigrated(
address indexed vault,
bytes32 depositDataRoot,
uint256 validatorIndex,
address depositDataManager
);
/**
* @notice The vault deposit data index
* @param vault The address of the vault
* @return validatorIndex The index of the next validator to be registered
*/
function depositDataIndexes(address vault) external view returns (uint256 validatorIndex);
/**
* @notice The vault deposit data root
* @param vault The address of the vault
* @return depositDataRoot The deposit data root
*/
function depositDataRoots(address vault) external view returns (bytes32 depositDataRoot);
/**
* @notice The vault deposit data manager. Defaults to the vault admin if not set.
* @param vault The address of the vault
* @return depositDataManager The address of the deposit data manager
*/
function getDepositDataManager(address vault) external view returns (address);
/**
* @notice Function for setting the deposit data manager for the vault. Can only be called by the vault admin.
* @param vault The address of the vault
* @param depositDataManager The address of the new deposit data manager
*/
function setDepositDataManager(address vault, address depositDataManager) external;
/**
* @notice Function for setting the deposit data root for the vault. Can only be called by the deposit data manager.
* @param vault The address of the vault
* @param depositDataRoot The new deposit data Merkle tree root
*/
function setDepositDataRoot(address vault, bytes32 depositDataRoot) external;
/**
* @notice Updates the vault state. Can be used in multicall to update state and register validator(s).
* @param vault The address of the vault
* @param harvestParams The harvest params to use for updating the vault state
*/
function updateVaultState(
address vault,
IKeeperRewards.HarvestParams calldata harvestParams
) external;
/**
* @notice Function for registering single validator
* @param vault The address of the vault
* @param keeperParams The parameters for getting approval from Keeper oracles
* @param proof The proof used to verify that the validator is part of the deposit data merkle tree
*/
function registerValidator(
address vault,
IKeeperValidators.ApprovalParams calldata keeperParams,
bytes32[] calldata proof
) external;
/**
* @notice Function for registering multiple validators
* @param vault The address of the vault
* @param keeperParams The parameters for getting approval from Keeper oracles
* @param indexes The indexes of the leaves for the merkle tree multi proof verification
* @param proofFlags The multi proof flags for the merkle tree verification
* @param proof The proof used for the merkle tree verification
*/
function registerValidators(
address vault,
IKeeperValidators.ApprovalParams calldata keeperParams,
uint256[] calldata indexes,
bool[] calldata proofFlags,
bytes32[] calldata proof
) external;
/**
* @notice Function for migrating the deposit data of the Vault. Can only be called once by a vault during an upgrade.
* @param depositDataRoot The current deposit data root
* @param validatorIndex The current index of the next validator to be registered
* @param depositDataManager The address of the deposit data manager
*/
function migrate(
bytes32 depositDataRoot,
uint256 validatorIndex,
address depositDataManager
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IVaultWhitelist} from './IVaultWhitelist.sol';
import {IEthVault} from './IEthVault.sol';
/**
* @title IEthPrivVault
* @author StakeWise
* @notice Defines the interface for the EthPrivVault contract
*/
interface IEthPrivVault is IEthVault, IVaultWhitelist {}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.22;
import {IValidatorsRegistry} from './IValidatorsRegistry.sol';
/**
* @title IEthValidatorsRegistry
* @author Ethereum Foundation
* @notice This is the Ethereum validators deposit contract interface.
* See https://github.com/ethereum/consensus-specs/blob/v1.2.0/solidity_deposit_contract/deposit_contract.sol.
* For more information see the Phase 0 specification under https://github.com/ethereum/consensus-specs.
*/
interface IEthValidatorsRegistry is IValidatorsRegistry {
/// @notice Submit a Phase 0 DepositData object.
/// @param pubkey A BLS12-381 public key.
/// @param withdrawal_credentials Commitment to a public key for withdrawals.
/// @param signature A BLS12-381 signature.
/// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
/// Used as a protection against malformed input.
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) external payable;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IKeeperRewards} from './IKeeperRewards.sol';
import {IVaultAdmin} from './IVaultAdmin.sol';
import {IVaultVersion} from './IVaultVersion.sol';
import {IVaultFee} from './IVaultFee.sol';
import {IVaultState} from './IVaultState.sol';
import {IVaultValidators} from './IVaultValidators.sol';
import {IVaultEnterExit} from './IVaultEnterExit.sol';
import {IVaultOsToken} from './IVaultOsToken.sol';
import {IVaultMev} from './IVaultMev.sol';
import {IVaultEthStaking} from './IVaultEthStaking.sol';
import {IMulticall} from './IMulticall.sol';
/**
* @title IEthVault
* @author StakeWise
* @notice Defines the interface for the EthVault contract
*/
interface IEthVault is
IVaultAdmin,
IVaultVersion,
IVaultFee,
IVaultState,
IVaultValidators,
IVaultEnterExit,
IVaultOsToken,
IVaultMev,
IVaultEthStaking,
IMulticall
{
/**
* @dev Struct for initializing the EthVault contract
* @param capacity The Vault stops accepting deposits after exceeding the capacity
* @param feePercent The fee percent that is charged by the Vault
* @param metadataIpfsHash The IPFS hash of the Vault's metadata file
*/
struct EthVaultInitParams {
uint256 capacity;
uint16 feePercent;
string metadataIpfsHash;
}
/**
* @notice Initializes or upgrades the EthVault contract. Must transfer security deposit during the deployment.
* @param params The encoded parameters for initializing the EthVault contract
*/
function initialize(bytes calldata params) external payable;
/**
* @notice Deposits assets to the vault and mints OsToken shares to the receiver
* @param receiver The address to receive the OsToken
* @param osTokenShares The amount of OsToken shares to mint.
* If set to type(uint256).max, max OsToken shares will be minted.
* @param referrer The address of the referrer
* @return The amount of OsToken assets minted
*/
function depositAndMintOsToken(
address receiver,
uint256 osTokenShares,
address referrer
) external payable returns (uint256);
/**
* @notice Updates the state, deposits assets to the vault and mints OsToken shares to the receiver
* @param receiver The address to receive the OsToken
* @param osTokenShares The amount of OsToken shares to mint.
* If set to type(uint256).max, max OsToken shares will be minted.
* @param referrer The address of the referrer
* @param harvestParams The parameters for the harvest
* @return The amount of OsToken assets minted
*/
function updateStateAndDepositAndMintOsToken(
address receiver,
uint256 osTokenShares,
address referrer,
IKeeperRewards.HarvestParams calldata harvestParams
) external payable returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
/**
* @title IEthVaultFactory
* @author StakeWise
* @notice Defines the interface for the ETH Vault Factory contract
*/
interface IEthVaultFactory {
/**
* @notice Event emitted on a Vault creation
* @param admin The address of the Vault admin
* @param vault The address of the created Vault
* @param ownMevEscrow The address of the own MEV escrow contract. Zero address if shared MEV escrow is used.
* @param params The encoded parameters for initializing the Vault contract
*/
event VaultCreated(
address indexed admin,
address indexed vault,
address ownMevEscrow,
bytes params
);
/**
* @notice The address of the Vault implementation contract used for proxy creation
* @return The address of the Vault implementation contract
*/
function implementation() external view returns (address);
/**
* @notice The address of the own MEV escrow contract used for Vault creation
* @return The address of the MEV escrow contract
*/
function ownMevEscrow() external view returns (address);
/**
* @notice The address of the Vault admin used for Vault creation
* @return The address of the Vault admin
*/
function vaultAdmin() external view returns (address);
/**
* @notice Create Vault. Must transfer security deposit together with a call.
* @param params The encoded parameters for initializing the Vault contract
* @param isOwnMevEscrow Whether to deploy own escrow contract or connect to a smoothing pool for priority fees and MEV rewards
* @return vault The address of the created Vault
*/
function createVault(
bytes calldata params,
bool isOwnMevEscrow
) external payable returns (address vault);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IERC5267} from '@openzeppelin/contracts/interfaces/IERC5267.sol';
/**
* @title IKeeperOracles
* @author StakeWise
* @notice Defines the interface for the KeeperOracles contract
*/
interface IKeeperOracles is IERC5267 {
/**
* @notice Event emitted on the oracle addition
* @param oracle The address of the added oracle
*/
event OracleAdded(address indexed oracle);
/**
* @notice Event emitted on the oracle removal
* @param oracle The address of the removed oracle
*/
event OracleRemoved(address indexed oracle);
/**
* @notice Event emitted on oracles config update
* @param configIpfsHash The IPFS hash of the new config
*/
event ConfigUpdated(string configIpfsHash);
/**
* @notice Function for verifying whether oracle is registered or not
* @param oracle The address of the oracle to check
* @return `true` for the registered oracle, `false` otherwise
*/
function isOracle(address oracle) external view returns (bool);
/**
* @notice Total Oracles
* @return The total number of oracles registered
*/
function totalOracles() external view returns (uint256);
/**
* @notice Function for adding oracle to the set
* @param oracle The address of the oracle to add
*/
function addOracle(address oracle) external;
/**
* @notice Function for removing oracle from the set
* @param oracle The address of the oracle to remove
*/
function removeOracle(address oracle) external;
/**
* @notice Function for updating the config IPFS hash
* @param configIpfsHash The new config IPFS hash
*/
function updateConfig(string calldata configIpfsHash) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IKeeperOracles} from './IKeeperOracles.sol';
/**
* @title IKeeperRewards
* @author StakeWise
* @notice Defines the interface for the Keeper contract rewards
*/
interface IKeeperRewards is IKeeperOracles {
/**
* @notice Event emitted on rewards update
* @param caller The address of the function caller
* @param rewardsRoot The new rewards merkle tree root
* @param avgRewardPerSecond The new average reward per second
* @param updateTimestamp The update timestamp used for rewards calculation
* @param nonce The nonce used for verifying signatures
* @param rewardsIpfsHash The new rewards IPFS hash
*/
event RewardsUpdated(
address indexed caller,
bytes32 indexed rewardsRoot,
uint256 avgRewardPerSecond,
uint64 updateTimestamp,
uint64 nonce,
string rewardsIpfsHash
);
/**
* @notice Event emitted on Vault harvest
* @param vault The address of the Vault
* @param rewardsRoot The rewards merkle tree root
* @param totalAssetsDelta The Vault total assets delta since last sync. Can be negative in case of penalty/slashing.
* @param unlockedMevDelta The Vault execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
*/
event Harvested(
address indexed vault,
bytes32 indexed rewardsRoot,
int256 totalAssetsDelta,
uint256 unlockedMevDelta
);
/**
* @notice Event emitted on rewards min oracles number update
* @param oracles The new minimum number of oracles required to update rewards
*/
event RewardsMinOraclesUpdated(uint256 oracles);
/**
* @notice A struct containing the last synced Vault's cumulative reward
* @param assets The Vault cumulative reward earned since the start. Can be negative in case of penalty/slashing.
* @param nonce The nonce of the last sync
*/
struct Reward {
int192 assets;
uint64 nonce;
}
/**
* @notice A struct containing the last unlocked Vault's cumulative execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
* @param assets The shared MEV Vault's cumulative execution reward that can be withdrawn
* @param nonce The nonce of the last sync
*/
struct UnlockedMevReward {
uint192 assets;
uint64 nonce;
}
/**
* @notice A struct containing parameters for rewards update
* @param rewardsRoot The new rewards merkle root
* @param avgRewardPerSecond The new average reward per second
* @param updateTimestamp The update timestamp used for rewards calculation
* @param rewardsIpfsHash The new IPFS hash with all the Vaults' rewards for the new root
* @param signatures The concatenation of the Oracles' signatures
*/
struct RewardsUpdateParams {
bytes32 rewardsRoot;
uint256 avgRewardPerSecond;
uint64 updateTimestamp;
string rewardsIpfsHash;
bytes signatures;
}
/**
* @notice A struct containing parameters for harvesting rewards. Can only be called by Vault.
* @param rewardsRoot The rewards merkle root
* @param reward The Vault cumulative reward earned since the start. Can be negative in case of penalty/slashing.
* @param unlockedMevReward The Vault cumulative execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
* @param proof The proof to verify that Vault's reward is correct
*/
struct HarvestParams {
bytes32 rewardsRoot;
int160 reward;
uint160 unlockedMevReward;
bytes32[] proof;
}
/**
* @notice Previous Rewards Root
* @return The previous merkle tree root of the rewards accumulated by the Vaults
*/
function prevRewardsRoot() external view returns (bytes32);
/**
* @notice Rewards Root
* @return The latest merkle tree root of the rewards accumulated by the Vaults
*/
function rewardsRoot() external view returns (bytes32);
/**
* @notice Rewards Nonce
* @return The nonce used for updating rewards merkle tree root
*/
function rewardsNonce() external view returns (uint64);
/**
* @notice The last rewards update
* @return The timestamp of the last rewards update
*/
function lastRewardsTimestamp() external view returns (uint64);
/**
* @notice The minimum number of oracles required to update rewards
* @return The minimum number of oracles
*/
function rewardsMinOracles() external view returns (uint256);
/**
* @notice The rewards delay
* @return The delay in seconds between rewards updates
*/
function rewardsDelay() external view returns (uint256);
/**
* @notice Get last synced Vault cumulative reward
* @param vault The address of the Vault
* @return assets The last synced reward assets
* @return nonce The last synced reward nonce
*/
function rewards(address vault) external view returns (int192 assets, uint64 nonce);
/**
* @notice Get last unlocked shared MEV Vault cumulative reward
* @param vault The address of the Vault
* @return assets The last synced reward assets
* @return nonce The last synced reward nonce
*/
function unlockedMevRewards(address vault) external view returns (uint192 assets, uint64 nonce);
/**
* @notice Checks whether Vault must be harvested
* @param vault The address of the Vault
* @return `true` if the Vault requires harvesting, `false` otherwise
*/
function isHarvestRequired(address vault) external view returns (bool);
/**
* @notice Checks whether the Vault can be harvested
* @param vault The address of the Vault
* @return `true` if Vault can be harvested, `false` otherwise
*/
function canHarvest(address vault) external view returns (bool);
/**
* @notice Checks whether rewards can be updated
* @return `true` if rewards can be updated, `false` otherwise
*/
function canUpdateRewards() external view returns (bool);
/**
* @notice Checks whether the Vault has registered validators
* @param vault The address of the Vault
* @return `true` if Vault is collateralized, `false` otherwise
*/
function isCollateralized(address vault) external view returns (bool);
/**
* @notice Update rewards data
* @param params The struct containing rewards update parameters
*/
function updateRewards(RewardsUpdateParams calldata params) external;
/**
* @notice Harvest rewards. Can be called only by Vault.
* @param params The struct containing rewards harvesting parameters
* @return totalAssetsDelta The total reward/penalty accumulated by the Vault since the last sync
* @return unlockedMevDelta The Vault execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
* @return harvested `true` when the rewards were harvested, `false` otherwise
*/
function harvest(
HarvestParams calldata params
) external returns (int256 totalAssetsDelta, uint256 unlockedMevDelta, bool harvested);
/**
* @notice Set min number of oracles for confirming rewards update. Can only be called by the owner.
* @param _rewardsMinOracles The new min number of oracles for confirming rewards update
*/
function setRewardsMinOracles(uint256 _rewardsMinOracles) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IKeeperRewards} from './IKeeperRewards.sol';
import {IKeeperOracles} from './IKeeperOracles.sol';
/**
* @title IKeeperValidators
* @author StakeWise
* @notice Defines the interface for the Keeper validators
*/
interface IKeeperValidators is IKeeperOracles, IKeeperRewards {
/**
* @notice Event emitted on validators approval
* @param vault The address of the Vault
* @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
*/
event ValidatorsApproval(address indexed vault, string exitSignaturesIpfsHash);
/**
* @notice Event emitted on exit signatures update
* @param caller The address of the function caller
* @param vault The address of the Vault
* @param nonce The nonce used for verifying Oracles' signatures
* @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
*/
event ExitSignaturesUpdated(
address indexed caller,
address indexed vault,
uint256 nonce,
string exitSignaturesIpfsHash
);
/**
* @notice Event emitted on validators min oracles number update
* @param oracles The new minimum number of oracles required to approve validators
*/
event ValidatorsMinOraclesUpdated(uint256 oracles);
/**
* @notice Get nonce for the next vault exit signatures update
* @param vault The address of the Vault to get the nonce for
* @return The nonce of the Vault for updating signatures
*/
function exitSignaturesNonces(address vault) external view returns (uint256);
/**
* @notice Struct for approving registration of one or more validators
* @param validatorsRegistryRoot The deposit data root used to verify that oracles approved validators
* @param deadline The deadline for submitting the approval
* @param validators The concatenation of the validators' public key, signature and deposit data root
* @param signatures The concatenation of Oracles' signatures
* @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
*/
struct ApprovalParams {
bytes32 validatorsRegistryRoot;
uint256 deadline;
bytes validators;
bytes signatures;
string exitSignaturesIpfsHash;
}
/**
* @notice The minimum number of oracles required to update validators
* @return The minimum number of oracles
*/
function validatorsMinOracles() external view returns (uint256);
/**
* @notice Function for approving validators registration
* @param params The parameters for approving validators registration
*/
function approveValidators(ApprovalParams calldata params) external;
/**
* @notice Function for updating exit signatures for every hard fork
* @param vault The address of the Vault to update signatures for
* @param deadline The deadline for submitting signatures update
* @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
* @param oraclesSignatures The concatenation of Oracles' signatures
*/
function updateExitSignatures(
address vault,
uint256 deadline,
string calldata exitSignaturesIpfsHash,
bytes calldata oraclesSignatures
) external;
/**
* @notice Function for updating validators min oracles number
* @param _validatorsMinOracles The new minimum number of oracles required to approve validators
*/
function setValidatorsMinOracles(uint256 _validatorsMinOracles) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.22;
/**
* @title Multicall
* @author Uniswap
* @notice Adopted from https://github.com/Uniswap/v3-periphery/blob/1d69caf0d6c8cfeae9acd1f34ead30018d6e6400/contracts/base/Multicall.sol
* @notice Enables calling multiple methods in a single call to the contract
*/
interface IMulticall {
/**
* @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
* @param data The encoded function data for each of the calls to make to this contract
* @return results The results from each of the calls passed in via data
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
/**
* @title IOsTokenConfig
* @author StakeWise
* @notice Defines the interface for the OsTokenConfig contract
*/
interface IOsTokenConfig {
/**
* @notice Emitted when OsToken minting and liquidating configuration values are updated
* @param vault The address of the vault to update the config for. Will be zero address if it is a default config.
* @param liqBonusPercent The new liquidation bonus percent value
* @param liqThresholdPercent The new liquidation threshold percent value
* @param ltvPercent The new loan-to-value (LTV) percent value
*/
event OsTokenConfigUpdated(
address vault,
uint128 liqBonusPercent,
uint64 liqThresholdPercent,
uint64 ltvPercent
);
/**
* @notice Emitted when the OsToken redeemer address is updated
* @param newRedeemer The address of the new redeemer
*/
event RedeemerUpdated(address newRedeemer);
/**
* @notice The OsToken minting and liquidating configuration values
* @param liqThresholdPercent The liquidation threshold percent used to calculate health factor for OsToken position
* @param liqBonusPercent The minimal bonus percent that liquidator earns on OsToken position liquidation
* @param ltvPercent The percent used to calculate how much user can mint OsToken shares
*/
struct Config {
uint128 liqBonusPercent;
uint64 liqThresholdPercent;
uint64 ltvPercent;
}
/**
* @notice The address of the OsToken redeemer
* @return The address of the redeemer
*/
function redeemer() external view returns (address);
/**
* @notice Returns the OsToken minting and liquidating configuration values for the vault
* @param vault The address of the vault to get the config for
* @return config The OsToken config for the vault
*/
function getConfig(address vault) external view returns (Config memory config);
/**
* @notice Sets the OsToken redeemer address. Can only be called by the owner.
* @param newRedeemer The address of the new redeemer
*/
function setRedeemer(address newRedeemer) external;
/**
* @notice Updates the OsToken minting and liquidating configuration values. Can only be called by the owner.
* @param vault The address of the vault. Set to zero address to update the default config.
* @param config The new OsToken configuration
*/
function updateConfig(address vault, Config memory config) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
/**
* @title IOsTokenVaultController
* @author StakeWise
* @notice Defines the interface for the OsTokenVaultController contract
*/
interface IOsTokenVaultController {
/**
* @notice Event emitted on minting shares
* @param vault The address of the Vault
* @param receiver The address that received the shares
* @param assets The number of assets collateralized
* @param shares The number of tokens the owner received
*/
event Mint(address indexed vault, address indexed receiver, uint256 assets, uint256 shares);
/**
* @notice Event emitted on burning shares
* @param vault The address of the Vault
* @param owner The address that owns the shares
* @param assets The total number of assets withdrawn
* @param shares The total number of shares burned
*/
event Burn(address indexed vault, address indexed owner, uint256 assets, uint256 shares);
/**
* @notice Event emitted on state update
* @param profitAccrued The profit accrued since the last update
* @param treasuryShares The number of shares minted for the treasury
* @param treasuryAssets The number of assets minted for the treasury
*/
event StateUpdated(uint256 profitAccrued, uint256 treasuryShares, uint256 treasuryAssets);
/**
* @notice Event emitted on capacity update
* @param capacity The amount after which the OsToken stops accepting deposits
*/
event CapacityUpdated(uint256 capacity);
/**
* @notice Event emitted on treasury address update
* @param treasury The new treasury address
*/
event TreasuryUpdated(address indexed treasury);
/**
* @notice Event emitted on fee percent update
* @param feePercent The new fee percent
*/
event FeePercentUpdated(uint16 feePercent);
/**
* @notice Event emitted on average reward per second update
* @param avgRewardPerSecond The new average reward per second
*/
event AvgRewardPerSecondUpdated(uint256 avgRewardPerSecond);
/**
* @notice Event emitted on keeper address update
* @param keeper The new keeper address
*/
event KeeperUpdated(address keeper);
/**
* @notice The OsToken capacity
* @return The amount after which the OsToken stops accepting deposits
*/
function capacity() external view returns (uint256);
/**
* @notice The DAO treasury address that receives OsToken fees
* @return The address of the treasury
*/
function treasury() external view returns (address);
/**
* @notice The fee percent (multiplied by 100)
* @return The fee percent applied by the OsToken on the rewards
*/
function feePercent() external view returns (uint64);
/**
* @notice The address that can update avgRewardPerSecond
* @return The address of the keeper contract
*/
function keeper() external view returns (address);
/**
* @notice The average reward per second used to mint OsToken rewards
* @return The average reward per second earned by the Vaults
*/
function avgRewardPerSecond() external view returns (uint256);
/**
* @notice The fee per share used for calculating the fee for every position
* @return The cumulative fee per share
*/
function cumulativeFeePerShare() external view returns (uint256);
/**
* @notice The total number of shares controlled by the OsToken
* @return The total number of shares
*/
function totalShares() external view returns (uint256);
/**
* @notice Total assets controlled by the OsToken
* @return The total amount of the underlying asset that is "managed" by OsToken
*/
function totalAssets() external view returns (uint256);
/**
* @notice Converts shares to assets
* @param assets The amount of assets to convert to shares
* @return shares The amount of shares that the OsToken would exchange for the amount of assets provided
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @notice Converts assets to shares
* @param shares The amount of shares to convert to assets
* @return assets The amount of assets that the OsToken would exchange for the amount of shares provided
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @notice Updates rewards and treasury fee checkpoint for the OsToken
*/
function updateState() external;
/**
* @notice Mint OsToken shares. Can only be called by the registered vault.
* @param receiver The address that will receive the shares
* @param shares The amount of shares to mint
* @return assets The amount of assets minted
*/
function mintShares(address receiver, uint256 shares) external returns (uint256 assets);
/**
* @notice Burn shares for withdrawn assets. Can only be called by the registered vault.
* @param owner The address that owns the shares
* @param shares The amount of shares to burn
* @return assets The amount of assets withdrawn
*/
function burnShares(address owner, uint256 shares) external returns (uint256 assets);
/**
* @notice Update treasury address. Can only be called by the owner.
* @param _treasury The new treasury address
*/
function setTreasury(address _treasury) external;
/**
* @notice Update capacity. Can only be called by the owner.
* @param _capacity The amount after which the OsToken stops accepting deposits
*/
function setCapacity(uint256 _capacity) external;
/**
* @notice Update fee percent. Can only be called by the owner. Cannot be larger than 10 000 (100%).
* @param _feePercent The new fee percent
*/
function setFeePercent(uint16 _feePercent) external;
/**
* @notice Update keeper address. Can only be called by the owner.
* @param _keeper The new keeper address
*/
function setKeeper(address _keeper) external;
/**
* @notice Updates average reward per second. Can only be called by the keeper.
* @param _avgRewardPerSecond The new average reward per second
*/
function setAvgRewardPerSecond(uint256 _avgRewardPerSecond) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IMulticall} from './IMulticall.sol';
/**
* @title IOsTokenVaultEscrow
* @author StakeWise
* @notice Interface for OsTokenVaultEscrow contract
*/
interface IOsTokenVaultEscrow is IMulticall {
/**
* @notice Struct to store the escrow position details
* @param owner The address of the assets owner
* @param exitedAssets The amount of assets exited and ready to be claimed
* @param osTokenShares The amount of osToken shares
* @param cumulativeFeePerShare The cumulative fee per share used to calculate the osToken fee
*/
struct Position {
address owner;
uint96 exitedAssets;
uint128 osTokenShares;
uint128 cumulativeFeePerShare;
}
/**
* @notice Event emitted on position creation
* @param vault The address of the vault
* @param exitPositionTicket The exit position ticket
* @param owner The address of the assets owner
* @param osTokenShares The amount of osToken shares
* @param cumulativeFeePerShare The cumulative fee per share used to calculate the osToken fee
*/
event PositionCreated(
address indexed vault,
uint256 indexed exitPositionTicket,
address owner,
uint256 osTokenShares,
uint256 cumulativeFeePerShare
);
/**
* @notice Event emitted on assets exit processing
* @param vault The address of the vault
* @param caller The address of the caller
* @param exitPositionTicket The exit position ticket
* @param exitedAssets The amount of exited assets claimed
*/
event ExitedAssetsProcessed(
address indexed vault,
address indexed caller,
uint256 indexed exitPositionTicket,
uint256 exitedAssets
);
/**
* @notice Event emitted on osToken liquidation
* @param caller The address of the function caller
* @param vault The address of the vault
* @param exitPositionTicket The exit position ticket
* @param receiver The address of the receiver of the liquidated assets
* @param osTokenShares The amount of osToken shares to liquidate
* @param receivedAssets The amount of assets received
*/
event OsTokenLiquidated(
address indexed caller,
address indexed vault,
uint256 indexed exitPositionTicket,
address receiver,
uint256 osTokenShares,
uint256 receivedAssets
);
/**
* @notice Event emitted on osToken redemption
* @param caller The address of the function caller
* @param vault The address of the vault
* @param exitPositionTicket The exit position ticket
* @param receiver The address of the receiver of the redeemed assets
* @param osTokenShares The amount of osToken shares to redeem
* @param receivedAssets The amount of assets received
*/
event OsTokenRedeemed(
address indexed caller,
address indexed vault,
uint256 indexed exitPositionTicket,
address receiver,
uint256 osTokenShares,
uint256 receivedAssets
);
/**
* @notice Event emitted on exited assets claim
* @param receiver The address of the receiver of the exited assets
* @param vault The address of the vault
* @param exitPositionTicket The exit position ticket
* @param osTokenShares The amount of osToken shares burned
* @param assets The amount of assets claimed
*/
event ExitedAssetsClaimed(
address indexed receiver,
address indexed vault,
uint256 indexed exitPositionTicket,
uint256 osTokenShares,
uint256 assets
);
/**
* @notice Event emitted on liquidation configuration update
* @param liqThresholdPercent The liquidation threshold percent
* @param liqBonusPercent The liquidation bonus percent
*/
event LiqConfigUpdated(uint64 liqThresholdPercent, uint256 liqBonusPercent);
/**
* @notice Event emitted on authenticator update
* @param newAuthenticator The address of the new authenticator
*/
event AuthenticatorUpdated(address newAuthenticator);
/**
* @notice The liquidation threshold percent
* @return The liquidation threshold percent starting from which the osToken shares can be liquidated
*/
function liqThresholdPercent() external view returns (uint64);
/**
* @notice The liquidation bonus percent
* @return The liquidation bonus percent paid for liquidating the osToken shares
*/
function liqBonusPercent() external view returns (uint256);
/**
* @notice The address of the authenticator
* @return The address of the authenticator contract
*/
function authenticator() external view returns (address);
/**
* @notice Get the position details
* @param vault The address of the vault
* @param positionTicket The exit position ticket
* @return owner The address of the assets owner
* @return exitedAssets The amount of assets exited and ready to be claimed
* @return osTokenShares The amount of osToken shares
*/
function getPosition(
address vault,
uint256 positionTicket
) external view returns (address, uint256, uint256);
/**
* @notice Registers the new escrow position
* @param owner The address of the exited assets owner
* @param exitPositionTicket The exit position ticket
* @param osTokenShares The amount of osToken shares
* @param cumulativeFeePerShare The cumulative fee per share used to calculate the osToken fee
*/
function register(
address owner,
uint256 exitPositionTicket,
uint256 osTokenShares,
uint256 cumulativeFeePerShare
) external;
/**
* @notice Claims exited assets from the vault to the escrow
* @param vault The address of the vault
* @param exitPositionTicket The exit position ticket
* @param timestamp The timestamp of the exit
* @param exitQueueIndex The index of the exit in the queue
*/
function processExitedAssets(
address vault,
uint256 exitPositionTicket,
uint256 timestamp,
uint256 exitQueueIndex
) external;
/**
* @notice Claims the exited assets from the escrow to the owner. Can only be called by the position owner.
* @param vault The address of the vault
* @param exitPositionTicket The exit position ticket
* @param osTokenShares The amount of osToken shares to burn
* @return claimedAssets The amount of assets claimed
*/
function claimExitedAssets(
address vault,
uint256 exitPositionTicket,
uint256 osTokenShares
) external returns (uint256 claimedAssets);
/**
* @notice Liquidates the osToken shares
* @param vault The address of the vault
* @param exitPositionTicket The exit position ticket
* @param osTokenShares The amount of osToken shares to liquidate
* @param receiver The address of the receiver of the liquidated assets
*/
function liquidateOsToken(
address vault,
uint256 exitPositionTicket,
uint256 osTokenShares,
address receiver
) external;
/**
* @notice Redeems the osToken shares. Can only be called by the osToken redeemer.
* @param vault The address of the vault
* @param exitPositionTicket The exit position ticket
* @param osTokenShares The amount of osToken shares to redeem
* @param receiver The address of the receiver of the redeemed assets
*/
function redeemOsToken(
address vault,
uint256 exitPositionTicket,
uint256 osTokenShares,
address receiver
) external;
/**
* @notice Updates the authenticator. Can only be called by the owner.
* @param newAuthenticator The address of the new authenticator
*/
function setAuthenticator(address newAuthenticator) external;
/**
* @notice Updates the liquidation configuration. Can only be called by the owner.
* @param _liqThresholdPercent The liquidation threshold percent
* @param _liqBonusPercent The liquidation bonus percent
*/
function updateLiqConfig(uint64 _liqThresholdPercent, uint256 _liqBonusPercent) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
/**
* @title IOwnMevEscrow
* @author StakeWise
* @notice Defines the interface for the OwnMevEscrow contract
*/
interface IOwnMevEscrow {
/**
* @notice Event emitted on received MEV
* @param assets The amount of MEV assets received
*/
event MevReceived(uint256 assets);
/**
* @notice Event emitted on harvest
* @param assets The amount of assets withdrawn
*/
event Harvested(uint256 assets);
/**
* @notice Vault address
* @return The address of the vault that owns the escrow
*/
function vault() external view returns (address payable);
/**
* @notice Withdraws MEV accumulated in the escrow. Can be called only by the Vault.
* @dev IMPORTANT: because control is transferred to the Vault, care must be
* taken to not create reentrancy vulnerabilities. The Vault must follow the checks-effects-interactions pattern:
* https://docs.soliditylang.org/en/v0.8.22/security-considerations.html#use-the-checks-effects-interactions-pattern
* @return assets The amount of assets withdrawn
*/
function harvest() external returns (uint256 assets);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
/**
* @title ISharedMevEscrow
* @author StakeWise
* @notice Defines the interface for the SharedMevEscrow contract
*/
interface ISharedMevEscrow {
/**
* @notice Event emitted on received MEV
* @param assets The amount of MEV assets received
*/
event MevReceived(uint256 assets);
/**
* @notice Event emitted on harvest
* @param caller The function caller
* @param assets The amount of assets withdrawn
*/
event Harvested(address indexed caller, uint256 assets);
/**
* @notice Withdraws MEV accumulated in the escrow. Can be called only by the Vault.
* @dev IMPORTANT: because control is transferred to the Vault, care must be
* taken to not create reentrancy vulnerabilities. The Vault must follow the checks-effects-interactions pattern:
* https://docs.soliditylang.org/en/v0.8.22/security-considerations.html#use-the-checks-effects-interactions-pattern
* @param assets The amount of assets to withdraw
*/
function harvest(uint256 assets) external;
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.22;
/**
* @title IValidatorsRegistry
* @author Ethereum Foundation
* @notice The validators deposit contract common interface
*/
interface IValidatorsRegistry {
/// @notice A processed deposit event.
event DepositEvent(
bytes pubkey,
bytes withdrawal_credentials,
bytes amount,
bytes signature,
bytes index
);
/// @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.22;
/**
* @title IVaultState
* @author StakeWise
* @notice Defines the interface for the VaultAdmin contract
*/
interface IVaultAdmin {
/**
* @notice Event emitted on metadata ipfs hash update
* @param caller The address of the function caller
* @param metadataIpfsHash The new metadata IPFS hash
*/
event MetadataUpdated(address indexed caller, string metadataIpfsHash);
/**
* @notice The Vault admin
* @return The address of the Vault admin
*/
function admin() external view returns (address);
/**
* @notice Function for updating the metadata IPFS hash. Can only be called by Vault admin.
* @param metadataIpfsHash The new metadata IPFS hash
*/
function setMetadata(string calldata metadataIpfsHash) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IVaultState} from './IVaultState.sol';
/**
* @title IVaultEnterExit
* @author StakeWise
* @notice Defines the interface for the VaultEnterExit contract
*/
interface IVaultEnterExit is IVaultState {
/**
* @notice Event emitted on deposit
* @param caller The address that called the deposit function
* @param receiver The address that received the shares
* @param assets The number of assets deposited by the caller
* @param shares The number of shares received
* @param referrer The address of the referrer
*/
event Deposited(
address indexed caller,
address indexed receiver,
uint256 assets,
uint256 shares,
address referrer
);
/**
* @notice Event emitted on redeem
* @param owner The address that owns the shares
* @param receiver The address that received withdrawn assets
* @param assets The total number of withdrawn assets
* @param shares The total number of withdrawn shares
*/
event Redeemed(address indexed owner, address indexed receiver, uint256 assets, uint256 shares);
/**
* @notice Event emitted on shares added to the exit queue
* @param owner The address that owns the shares
* @param receiver The address that will receive withdrawn assets
* @param positionTicket The exit queue ticket that was assigned to the position
* @param shares The number of shares that queued for the exit
*/
event ExitQueueEntered(
address indexed owner,
address indexed receiver,
uint256 positionTicket,
uint256 shares
);
/**
* @notice Event emitted on shares added to the V2 exit queue (deprecated)
* @param owner The address that owns the shares
* @param receiver The address that will receive withdrawn assets
* @param positionTicket The exit queue ticket that was assigned to the position
* @param shares The number of shares that queued for the exit
* @param assets The number of assets that queued for the exit
*/
event V2ExitQueueEntered(
address indexed owner,
address indexed receiver,
uint256 positionTicket,
uint256 shares,
uint256 assets
);
/**
* @notice Event emitted on claim of the exited assets
* @param receiver The address that has received withdrawn assets
* @param prevPositionTicket The exit queue ticket received after the `enterExitQueue` call
* @param newPositionTicket The new exit queue ticket in case not all the shares were withdrawn. Otherwise 0.
* @param withdrawnAssets The total number of assets withdrawn
*/
event ExitedAssetsClaimed(
address indexed receiver,
uint256 prevPositionTicket,
uint256 newPositionTicket,
uint256 withdrawnAssets
);
/**
* @notice Locks shares to the exit queue. The shares continue earning rewards until they will be burned by the Vault.
* @param shares The number of shares to lock
* @param receiver The address that will receive assets upon withdrawal
* @return positionTicket The position ticket of the exit queue. Returns uint256 max if no ticket created.
*/
function enterExitQueue(
uint256 shares,
address receiver
) external returns (uint256 positionTicket);
/**
* @notice Get the exit queue index to claim exited assets from
* @param positionTicket The exit queue position ticket to get the index for
* @return The exit queue index that should be used to claim exited assets.
* Returns -1 in case such index does not exist.
*/
function getExitQueueIndex(uint256 positionTicket) external view returns (int256);
/**
* @notice Calculates the number of shares and assets that can be claimed from the exit queue.
* @param receiver The address that will receive assets upon withdrawal
* @param positionTicket The exit queue ticket received after the `enterExitQueue` call
* @param timestamp The timestamp when the shares entered the exit queue
* @param exitQueueIndex The exit queue index at which the shares were burned. It can be looked up by calling `getExitQueueIndex`.
* @return leftTickets The number of tickets left in the queue
* @return exitedTickets The number of tickets that have already exited
* @return exitedAssets The number of assets that can be claimed
*/
function calculateExitedAssets(
address receiver,
uint256 positionTicket,
uint256 timestamp,
uint256 exitQueueIndex
) external view returns (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets);
/**
* @notice Claims assets that were withdrawn by the Vault. It can be called only after the `enterExitQueue` call by the `receiver`.
* @param positionTicket The exit queue ticket received after the `enterExitQueue` call
* @param timestamp The timestamp when the assets entered the exit queue
* @param exitQueueIndex The exit queue index at which the shares were burned.
* It can be looked up by calling `getExitQueueIndex`.
*/
function claimExitedAssets(
uint256 positionTicket,
uint256 timestamp,
uint256 exitQueueIndex
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IVaultState} from './IVaultState.sol';
import {IVaultValidators} from './IVaultValidators.sol';
import {IVaultEnterExit} from './IVaultEnterExit.sol';
import {IKeeperRewards} from './IKeeperRewards.sol';
import {IVaultMev} from './IVaultMev.sol';
/**
* @title IVaultEthStaking
* @author StakeWise
* @notice Defines the interface for the VaultEthStaking contract
*/
interface IVaultEthStaking is IVaultState, IVaultValidators, IVaultEnterExit, IVaultMev {
/**
* @notice Deposit ETH to the Vault
* @param receiver The address that will receive Vault's shares
* @param referrer The address of the referrer. Set to zero address if not used.
* @return shares The number of shares minted
*/
function deposit(address receiver, address referrer) external payable returns (uint256 shares);
/**
* @notice Used by MEV escrow to transfer ETH.
*/
function receiveFromMevEscrow() external payable;
/**
* @notice Updates Vault state and deposits ETH to the Vault
* @param receiver The address that will receive Vault's shares
* @param referrer The address of the referrer. Set to zero address if not used.
* @param harvestParams The parameters for harvesting Keeper rewards
* @return shares The number of shares minted
*/
function updateStateAndDeposit(
address receiver,
address referrer,
IKeeperRewards.HarvestParams calldata harvestParams
) external payable returns (uint256 shares);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IVaultAdmin} from './IVaultAdmin.sol';
/**
* @title IVaultFee
* @author StakeWise
* @notice Defines the interface for the VaultFee contract
*/
interface IVaultFee is IVaultAdmin {
/**
* @notice Event emitted on fee recipient update
* @param caller The address of the function caller
* @param feeRecipient The address of the new fee recipient
*/
event FeeRecipientUpdated(address indexed caller, address indexed feeRecipient);
/**
* @notice The Vault's fee recipient
* @return The address of the Vault's fee recipient
*/
function feeRecipient() external view returns (address);
/**
* @notice The Vault's fee percent in BPS
* @return The fee percent applied by the Vault on the rewards
*/
function feePercent() external view returns (uint16);
/**
* @notice Function for updating the fee recipient address. Can only be called by the admin.
* @param _feeRecipient The address of the new fee recipient
*/
function setFeeRecipient(address _feeRecipient) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IVaultState} from './IVaultState.sol';
/**
* @title IVaultMev
* @author StakeWise
* @notice Common interface for the VaultMev contracts
*/
interface IVaultMev is IVaultState {
/**
* @notice The contract that accumulates MEV rewards
* @return The MEV escrow contract address
*/
function mevEscrow() external view returns (address);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IVaultState} from './IVaultState.sol';
import {IVaultEnterExit} from './IVaultEnterExit.sol';
/**
* @title IVaultOsToken
* @author StakeWise
* @notice Defines the interface for the VaultOsToken contract
*/
interface IVaultOsToken is IVaultState, IVaultEnterExit {
/**
* @notice Event emitted on minting osToken
* @param caller The address of the function caller
* @param receiver The address of the osToken receiver
* @param assets The amount of minted assets
* @param shares The amount of minted shares
* @param referrer The address of the referrer
*/
event OsTokenMinted(
address indexed caller,
address receiver,
uint256 assets,
uint256 shares,
address referrer
);
/**
* @notice Event emitted on burning OsToken
* @param caller The address of the function caller
* @param assets The amount of burned assets
* @param shares The amount of burned shares
*/
event OsTokenBurned(address indexed caller, uint256 assets, uint256 shares);
/**
* @notice Event emitted on osToken position liquidation
* @param caller The address of the function caller
* @param user The address of the user liquidated
* @param receiver The address of the receiver of the liquidated assets
* @param osTokenShares The amount of osToken shares to liquidate
* @param shares The amount of vault shares burned
* @param receivedAssets The amount of assets received
*/
event OsTokenLiquidated(
address indexed caller,
address indexed user,
address receiver,
uint256 osTokenShares,
uint256 shares,
uint256 receivedAssets
);
/**
* @notice Event emitted on osToken position redemption
* @param caller The address of the function caller
* @param user The address of the position owner to redeem from
* @param receiver The address of the receiver of the redeemed assets
* @param osTokenShares The amount of osToken shares to redeem
* @param shares The amount of vault shares burned
* @param assets The amount of assets received
*/
event OsTokenRedeemed(
address indexed caller,
address indexed user,
address receiver,
uint256 osTokenShares,
uint256 shares,
uint256 assets
);
/**
* @notice Struct of osToken position
* @param shares The total number of minted osToken shares. Will increase based on the treasury fee.
* @param cumulativeFeePerShare The cumulative fee per share
*/
struct OsTokenPosition {
uint128 shares;
uint128 cumulativeFeePerShare;
}
/**
* @notice Get total amount of minted osToken shares
* @param user The address of the user
* @return shares The number of minted osToken shares
*/
function osTokenPositions(address user) external view returns (uint128 shares);
/**
* @notice Mints OsToken shares
* @param receiver The address that will receive the minted OsToken shares
* @param osTokenShares The number of OsToken shares to mint to the receiver. To mint the maximum amount of shares, use 2^256 - 1.
* @param referrer The address of the referrer
* @return assets The number of assets minted to the receiver
*/
function mintOsToken(
address receiver,
uint256 osTokenShares,
address referrer
) external returns (uint256 assets);
/**
* @notice Burns osToken shares
* @param osTokenShares The number of shares to burn
* @return assets The number of assets burned
*/
function burnOsToken(uint128 osTokenShares) external returns (uint256 assets);
/**
* @notice Liquidates a user position and returns the number of received assets.
* Can only be called when health factor is below 1 by the liquidator.
* @param osTokenShares The number of shares to cover
* @param owner The address of the position owner to liquidate
* @param receiver The address of the receiver of the liquidated assets
*/
function liquidateOsToken(uint256 osTokenShares, address owner, address receiver) external;
/**
* @notice Redeems osToken shares for assets. Can only be called when health factor is above redeemFromHealthFactor by the redeemer.
* @param osTokenShares The number of osToken shares to redeem
* @param owner The address of the position owner to redeem from
* @param receiver The address of the receiver of the redeemed assets
*/
function redeemOsToken(uint256 osTokenShares, address owner, address receiver) external;
/**
* @notice Transfers minted osToken shares to the OsTokenVaultEscrow contract, enters the exit queue for staked assets
* @param osTokenShares The number of osToken shares to transfer
* @return positionTicket The exit position ticket
*/
function transferOsTokenPositionToEscrow(
uint256 osTokenShares
) external returns (uint256 positionTicket);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
/**
* @title IVaultsRegistry
* @author StakeWise
* @notice Defines the interface for the VaultsRegistry
*/
interface IVaultsRegistry {
/**
* @notice Event emitted on a Vault addition
* @param caller The address that has added the Vault
* @param vault The address of the added Vault
*/
event VaultAdded(address indexed caller, address indexed vault);
/**
* @notice Event emitted on adding Vault implementation contract
* @param impl The address of the new implementation contract
*/
event VaultImplAdded(address indexed impl);
/**
* @notice Event emitted on removing Vault implementation contract
* @param impl The address of the removed implementation contract
*/
event VaultImplRemoved(address indexed impl);
/**
* @notice Event emitted on whitelisting the factory
* @param factory The address of the whitelisted factory
*/
event FactoryAdded(address indexed factory);
/**
* @notice Event emitted on removing the factory from the whitelist
* @param factory The address of the factory removed from the whitelist
*/
event FactoryRemoved(address indexed factory);
/**
* @notice Registered Vaults
* @param vault The address of the vault to check whether it is registered
* @return `true` for the registered Vault, `false` otherwise
*/
function vaults(address vault) external view returns (bool);
/**
* @notice Registered Vault implementations
* @param impl The address of the vault implementation
* @return `true` for the registered implementation, `false` otherwise
*/
function vaultImpls(address impl) external view returns (bool);
/**
* @notice Registered Factories
* @param factory The address of the factory to check whether it is whitelisted
* @return `true` for the whitelisted Factory, `false` otherwise
*/
function factories(address factory) external view returns (bool);
/**
* @notice Function for adding Vault to the registry. Can only be called by the whitelisted Factory.
* @param vault The address of the Vault to add
*/
function addVault(address vault) external;
/**
* @notice Function for adding Vault implementation contract
* @param newImpl The address of the new implementation contract
*/
function addVaultImpl(address newImpl) external;
/**
* @notice Function for removing Vault implementation contract
* @param impl The address of the removed implementation contract
*/
function removeVaultImpl(address impl) external;
/**
* @notice Function for adding the factory to the whitelist
* @param factory The address of the factory to add to the whitelist
*/
function addFactory(address factory) external;
/**
* @notice Function for removing the factory from the whitelist
* @param factory The address of the factory to remove from the whitelist
*/
function removeFactory(address factory) external;
/**
* @notice Function for initializing the registry. Can only be called once during the deployment.
* @param _owner The address of the owner of the contract
*/
function initialize(address _owner) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IKeeperRewards} from './IKeeperRewards.sol';
import {IVaultFee} from './IVaultFee.sol';
/**
* @title IVaultState
* @author StakeWise
* @notice Defines the interface for the VaultState contract
*/
interface IVaultState is IVaultFee {
/**
* @notice Event emitted on checkpoint creation
* @param shares The number of burned shares
* @param assets The amount of exited assets
*/
event CheckpointCreated(uint256 shares, uint256 assets);
/**
* @notice Event emitted on minting fee recipient shares
* @param receiver The address of the fee recipient
* @param shares The number of minted shares
* @param assets The amount of minted assets
*/
event FeeSharesMinted(address receiver, uint256 shares, uint256 assets);
/**
* @notice Event emitted when exiting assets are penalized
* @param penalty The total penalty amount
*/
event ExitingAssetsPenalized(uint256 penalty);
/**
* @notice Total assets in the Vault
* @return The total amount of the underlying asset that is "managed" by Vault
*/
function totalAssets() external view returns (uint256);
/**
* @notice Function for retrieving total shares
* @return The amount of shares in existence
*/
function totalShares() external view returns (uint256);
/**
* @notice The Vault's capacity
* @return The amount after which the Vault stops accepting deposits
*/
function capacity() external view returns (uint256);
/**
* @notice Total assets available in the Vault. They can be staked or withdrawn.
* @return The total amount of withdrawable assets
*/
function withdrawableAssets() external view returns (uint256);
/**
* @notice Queued Shares
* @return The total number of shares queued for exit
*/
function queuedShares() external view returns (uint128);
/**
* @notice Total Exiting Assets (deprecated)
* @return The total number of assets queued for exit
*/
function totalExitingAssets() external view returns (uint128);
/**
* @notice Returns the number of shares held by an account
* @param account The account for which to look up the number of shares it has, i.e. its balance
* @return The number of shares held by the account
*/
function getShares(address account) external view returns (uint256);
/**
* @notice Converts assets to shares
* @param assets The amount of assets to convert to shares
* @return shares The amount of shares that the Vault would exchange for the amount of assets provided
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @notice Converts shares to assets
* @param shares The amount of shares to convert to assets
* @return assets The amount of assets that the Vault would exchange for the amount of shares provided
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @notice Check whether state update is required
* @return `true` if state update is required, `false` otherwise
*/
function isStateUpdateRequired() external view returns (bool);
/**
* @notice Updates the total amount of assets in the Vault and its exit queue
* @param harvestParams The parameters for harvesting Keeper rewards
*/
function updateState(IKeeperRewards.HarvestParams calldata harvestParams) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IKeeperValidators} from './IKeeperValidators.sol';
import {IVaultAdmin} from './IVaultAdmin.sol';
import {IVaultState} from './IVaultState.sol';
/**
* @title IVaultValidators
* @author StakeWise
* @notice Defines the interface for VaultValidators contract
*/
interface IVaultValidators is IVaultAdmin, IVaultState {
/**
* @notice Event emitted on validator registration
* @param publicKey The public key of the validator that was registered
*/
event ValidatorRegistered(bytes publicKey);
/**
* @notice Event emitted on keys manager address update (deprecated)
* @param caller The address of the function caller
* @param keysManager The address of the new keys manager
*/
event KeysManagerUpdated(address indexed caller, address indexed keysManager);
/**
* @notice Event emitted on validators merkle tree root update (deprecated)
* @param caller The address of the function caller
* @param validatorsRoot The new validators merkle tree root
*/
event ValidatorsRootUpdated(address indexed caller, bytes32 indexed validatorsRoot);
/**
* @notice Event emitted on validators manager address update
* @param caller The address of the function caller
* @param validatorsManager The address of the new validators manager
*/
event ValidatorsManagerUpdated(address indexed caller, address indexed validatorsManager);
/**
* @notice The Vault validators manager address
* @return The address that can register validators
*/
function validatorsManager() external view returns (address);
/**
* @notice Function for registering single or multiple validators
* @param keeperParams The parameters for getting approval from Keeper oracles
* @param validatorsManagerSignature The optional signature from the validators manager
*/
function registerValidators(
IKeeperValidators.ApprovalParams calldata keeperParams,
bytes calldata validatorsManagerSignature
) external;
/**
* @notice Function for updating the validators manager. Can only be called by the admin. Default is the DepositDataRegistry contract.
* @param _validatorsManager The new validators manager address
*/
function setValidatorsManager(address _validatorsManager) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IERC1822Proxiable} from '@openzeppelin/contracts/interfaces/draft-IERC1822.sol';
import {IVaultAdmin} from './IVaultAdmin.sol';
/**
* @title IVaultVersion
* @author StakeWise
* @notice Defines the interface for VaultVersion contract
*/
interface IVaultVersion is IERC1822Proxiable, IVaultAdmin {
/**
* @notice Vault Unique Identifier
* @return The unique identifier of the Vault
*/
function vaultId() external pure returns (bytes32);
/**
* @notice Version
* @return The version of the Vault implementation contract
*/
function version() external pure returns (uint8);
/**
* @notice Implementation
* @return The address of the Vault implementation contract
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IVaultAdmin} from './IVaultAdmin.sol';
/**
* @title IVaultWhitelist
* @author StakeWise
* @notice Defines the interface for the VaultWhitelist contract
*/
interface IVaultWhitelist is IVaultAdmin {
/**
* @notice Event emitted on whitelist update
* @param caller The address of the function caller
* @param account The address of the account updated
* @param approved Whether account is approved or not
*/
event WhitelistUpdated(address indexed caller, address indexed account, bool approved);
/**
* @notice Event emitted when whitelister address is updated
* @param caller The address of the function caller
* @param whitelister The address of the new whitelister
*/
event WhitelisterUpdated(address indexed caller, address indexed whitelister);
/**
* @notice Whitelister address
* @return The address of the whitelister
*/
function whitelister() external view returns (address);
/**
* @notice Checks whether account is whitelisted or not
* @param account The account to check
* @return `true` for the whitelisted account, `false` otherwise
*/
function whitelistedAccounts(address account) external view returns (bool);
/**
* @notice Add or remove account from the whitelist. Can only be called by the whitelister.
* @param account The account to add or remove from the whitelist
* @param approved Whether account should be whitelisted or not
*/
function updateWhitelist(address account, bool approved) external;
/**
* @notice Used to update the whitelister. Can only be called by the Vault admin.
* @param _whitelister The address of the new whitelister
*/
function setWhitelister(address _whitelister) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
/**
* @title Errors
* @author StakeWise
* @notice Contains all the custom errors
*/
library Errors {
error AccessDenied();
error InvalidShares();
error InvalidAssets();
error ZeroAddress();
error InsufficientAssets();
error CapacityExceeded();
error InvalidCapacity();
error InvalidSecurityDeposit();
error InvalidFeeRecipient();
error InvalidFeePercent();
error NotHarvested();
error NotCollateralized();
error InvalidProof();
error LowLtv();
error InvalidPosition();
error InvalidHealthFactor();
error InvalidReceivedAssets();
error InvalidTokenMeta();
error UpgradeFailed();
error InvalidValidators();
error DeadlineExpired();
error PermitInvalidSigner();
error InvalidValidatorsRegistryRoot();
error InvalidVault();
error AlreadyAdded();
error AlreadyRemoved();
error InvalidOracles();
error NotEnoughSignatures();
error InvalidOracle();
error TooEarlyUpdate();
error InvalidAvgRewardPerSecond();
error InvalidRewardsRoot();
error HarvestFailed();
error LiquidationDisabled();
error InvalidLiqThresholdPercent();
error InvalidLiqBonusPercent();
error InvalidLtvPercent();
error InvalidCheckpointIndex();
error InvalidCheckpointValue();
error MaxOraclesExceeded();
error ExitRequestNotProcessed();
error ValueNotChanged();
error InvalidWithdrawalCredentials();
error EigenPodNotFound();
error InvalidQueuedShares();
error FlashLoanFailed();
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';
import {SafeCast} from '@openzeppelin/contracts/utils/math/SafeCast.sol';
import {Errors} from './Errors.sol';
/**
* @title ExitQueue
* @author StakeWise
* @notice ExitQueue represent checkpoints of burned shares and exited assets
*/
library ExitQueue {
/**
* @notice A struct containing checkpoint data
* @param totalTickets The cumulative number of tickets (shares) exited
* @param exitedAssets The number of assets that exited in this checkpoint
*/
struct Checkpoint {
uint160 totalTickets;
uint96 exitedAssets;
}
/**
* @notice A struct containing the history of checkpoints data
* @param checkpoints An array of checkpoints
*/
struct History {
Checkpoint[] checkpoints;
}
/**
* @notice Get the latest checkpoint total tickets
* @param self An array containing checkpoints
* @return The current total tickets or zero if there are no checkpoints
*/
function getLatestTotalTickets(History storage self) internal view returns (uint256) {
uint256 pos = self.checkpoints.length;
unchecked {
// cannot underflow as subtraction happens in case pos > 0
return pos == 0 ? 0 : _unsafeAccess(self.checkpoints, pos - 1).totalTickets;
}
}
/**
* @notice Get checkpoint index for the burned shares
* @param self An array containing checkpoints
* @param positionTicket The position ticket to search the closest checkpoint for
* @return The checkpoint index or the length of checkpoints array in case there is no such
*/
function getCheckpointIndex(
History storage self,
uint256 positionTicket
) internal view returns (uint256) {
uint256 high = self.checkpoints.length;
uint256 low;
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self.checkpoints, mid).totalTickets > positionTicket) {
high = mid;
} else {
unchecked {
// cannot underflow as mid < high
low = mid + 1;
}
}
}
return high;
}
/**
* @notice Calculates burned shares and exited assets
* @param self An array containing checkpoints
* @param checkpointIdx The index of the checkpoint to start calculating from
* @param positionTicket The position ticket to start calculating exited assets from
* @param positionShares The number of shares to calculate assets for
* @return burnedShares The number of shares burned
* @return exitedAssets The number of assets exited
*/
function calculateExitedAssets(
History storage self,
uint256 checkpointIdx,
uint256 positionTicket,
uint256 positionShares
) internal view returns (uint256 burnedShares, uint256 exitedAssets) {
uint256 length = self.checkpoints.length;
// there are no exited assets for such checkpoint index or no shares to burn
if (checkpointIdx >= length || positionShares == 0) return (0, 0);
// previous total tickets for calculating how much shares were burned for the period
uint256 prevTotalTickets;
unchecked {
// cannot underflow as subtraction happens in case checkpointIdx > 0
prevTotalTickets = checkpointIdx == 0
? 0
: _unsafeAccess(self.checkpoints, checkpointIdx - 1).totalTickets;
}
// current total tickets for calculating assets per burned share
// can be used with _unsafeAccess as checkpointIdx < length
Checkpoint memory checkpoint = _unsafeAccess(self.checkpoints, checkpointIdx);
uint256 currTotalTickets = checkpoint.totalTickets;
uint256 checkpointAssets = checkpoint.exitedAssets;
// check whether position ticket is in [prevTotalTickets, currTotalTickets) range
if (positionTicket < prevTotalTickets || currTotalTickets <= positionTicket) {
revert Errors.InvalidCheckpointIndex();
}
// calculate amount of available shares that will be updated while iterating over checkpoints
uint256 availableShares;
unchecked {
// cannot underflow as positionTicket < currTotalTickets
availableShares = currTotalTickets - positionTicket;
}
// accumulate assets until the number of required shares is collected
uint256 checkpointShares;
uint256 sharesDelta;
while (true) {
unchecked {
// cannot underflow as prevTotalTickets <= positionTicket
checkpointShares = currTotalTickets - prevTotalTickets;
// cannot underflow as positionShares > burnedShares while in the loop
sharesDelta = Math.min(availableShares, positionShares - burnedShares);
// cannot overflow as it is capped with underlying asset total supply
burnedShares += sharesDelta;
exitedAssets += Math.mulDiv(sharesDelta, checkpointAssets, checkpointShares);
// cannot overflow as checkpoints are created max once per day
checkpointIdx++;
}
// stop when required shares collected or reached end of checkpoints list
if (positionShares <= burnedShares || checkpointIdx >= length) {
return (burnedShares, exitedAssets);
}
// take next checkpoint
prevTotalTickets = currTotalTickets;
// can use _unsafeAccess as checkpointIdx < length is checked above
checkpoint = _unsafeAccess(self.checkpoints, checkpointIdx);
currTotalTickets = checkpoint.totalTickets;
checkpointAssets = checkpoint.exitedAssets;
unchecked {
// cannot underflow as every next checkpoint total tickets is larger than previous
availableShares = currTotalTickets - prevTotalTickets;
}
}
}
/**
* @notice Pushes a new checkpoint onto a History
* @param self An array containing checkpoints
* @param shares The number of shares to add to the latest checkpoint
* @param assets The number of assets that were exited for this checkpoint
*/
function push(History storage self, uint256 shares, uint256 assets) internal {
if (shares == 0) revert Errors.InvalidCheckpointValue();
Checkpoint memory checkpoint = Checkpoint({
totalTickets: SafeCast.toUint160(getLatestTotalTickets(self) + shares),
exitedAssets: SafeCast.toUint96(assets)
});
self.checkpoints.push(checkpoint);
}
function _unsafeAccess(
Checkpoint[] storage self,
uint256 pos
) private pure returns (Checkpoint storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {IEthVault} from '../../interfaces/IEthVault.sol';
import {IEthVaultFactory} from '../../interfaces/IEthVaultFactory.sol';
import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
import {Multicall} from '../../base/Multicall.sol';
import {VaultValidators} from '../modules/VaultValidators.sol';
import {VaultAdmin} from '../modules/VaultAdmin.sol';
import {VaultFee} from '../modules/VaultFee.sol';
import {VaultVersion, IVaultVersion} from '../modules/VaultVersion.sol';
import {VaultImmutables} from '../modules/VaultImmutables.sol';
import {VaultState} from '../modules/VaultState.sol';
import {VaultEnterExit, IVaultEnterExit} from '../modules/VaultEnterExit.sol';
import {VaultOsToken} from '../modules/VaultOsToken.sol';
import {VaultEthStaking} from '../modules/VaultEthStaking.sol';
import {VaultMev} from '../modules/VaultMev.sol';
/**
* @title EthVault
* @author StakeWise
* @notice Defines the Ethereum staking Vault
*/
contract EthVault is
VaultImmutables,
Initializable,
VaultAdmin,
VaultVersion,
VaultFee,
VaultState,
VaultValidators,
VaultEnterExit,
VaultOsToken,
VaultMev,
VaultEthStaking,
Multicall,
IEthVault
{
uint8 private constant _version = 3;
/**
* @dev Constructor
* @dev Since the immutable variable value is stored in the bytecode,
* its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
* @param _keeper The address of the Keeper contract
* @param _vaultsRegistry The address of the VaultsRegistry contract
* @param _validatorsRegistry The contract address used for registering validators in beacon chain
* @param osTokenVaultController The address of the OsTokenVaultController contract
* @param osTokenConfig The address of the OsTokenConfig contract
* @param osTokenVaultEscrow The address of the OsTokenVaultEscrow contract
* @param sharedMevEscrow The address of the shared MEV escrow
* @param depositDataRegistry The address of the DepositDataRegistry contract
* @param exitingAssetsClaimDelay The delay after which the assets can be claimed after exiting from staking
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(
address _keeper,
address _vaultsRegistry,
address _validatorsRegistry,
address osTokenVaultController,
address osTokenConfig,
address osTokenVaultEscrow,
address sharedMevEscrow,
address depositDataRegistry,
uint256 exitingAssetsClaimDelay
)
VaultImmutables(_keeper, _vaultsRegistry, _validatorsRegistry)
VaultValidators(depositDataRegistry)
VaultEnterExit(exitingAssetsClaimDelay)
VaultOsToken(osTokenVaultController, osTokenConfig, osTokenVaultEscrow)
VaultMev(sharedMevEscrow)
{
_disableInitializers();
}
/// @inheritdoc IEthVault
function initialize(
bytes calldata params
) external payable virtual override reinitializer(_version) {
// if admin is already set, it's an upgrade from version 2 to 3
if (admin != address(0)) {
__EthVault_initV3();
return;
}
// initialize deployed vault
__EthVault_init(
IEthVaultFactory(msg.sender).vaultAdmin(),
IEthVaultFactory(msg.sender).ownMevEscrow(),
abi.decode(params, (EthVaultInitParams))
);
}
/// @inheritdoc IEthVault
function depositAndMintOsToken(
address receiver,
uint256 osTokenShares,
address referrer
) public payable override returns (uint256) {
deposit(msg.sender, referrer);
return mintOsToken(receiver, osTokenShares, referrer);
}
/// @inheritdoc IEthVault
function updateStateAndDepositAndMintOsToken(
address receiver,
uint256 osTokenShares,
address referrer,
IKeeperRewards.HarvestParams calldata harvestParams
) external payable override returns (uint256) {
updateState(harvestParams);
return depositAndMintOsToken(receiver, osTokenShares, referrer);
}
/// @inheritdoc IVaultEnterExit
function enterExitQueue(
uint256 shares,
address receiver
)
public
virtual
override(IVaultEnterExit, VaultEnterExit, VaultOsToken)
returns (uint256 positionTicket)
{
return super.enterExitQueue(shares, receiver);
}
/// @inheritdoc VaultVersion
function vaultId() public pure virtual override(IVaultVersion, VaultVersion) returns (bytes32) {
return keccak256('EthVault');
}
/// @inheritdoc IVaultVersion
function version() public pure virtual override(IVaultVersion, VaultVersion) returns (uint8) {
return _version;
}
/**
* @dev Initializes the EthVault contract upgrade to V3
*/
function __EthVault_initV3() internal {
__VaultState_initV3();
__VaultValidators_initV3();
}
/**
* @dev Initializes the EthVault contract
* @param admin The address of the admin of the Vault
* @param ownMevEscrow The address of the MEV escrow owned by the Vault. Zero address if shared MEV escrow is used.
* @param params The decoded parameters for initializing the EthVault contract
*/
function __EthVault_init(
address admin,
address ownMevEscrow,
EthVaultInitParams memory params
) internal onlyInitializing {
__VaultAdmin_init(admin, params.metadataIpfsHash);
// fee recipient is initially set to admin address
__VaultFee_init(admin, params.feePercent);
__VaultState_init(params.capacity);
__VaultValidators_init();
__VaultMev_init(ownMevEscrow);
__VaultEthStaking_init();
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {IVaultAdmin} from '../../interfaces/IVaultAdmin.sol';
import {Errors} from '../../libraries/Errors.sol';
/**
* @title VaultAdmin
* @author StakeWise
* @notice Defines the admin functionality for the Vault
*/
abstract contract VaultAdmin is Initializable, IVaultAdmin {
/// @inheritdoc IVaultAdmin
address public override admin;
/// @inheritdoc IVaultAdmin
function setMetadata(string calldata metadataIpfsHash) external override {
_checkAdmin();
emit MetadataUpdated(msg.sender, metadataIpfsHash);
}
/**
* @dev Initializes the VaultAdmin contract
* @param _admin The address of the Vault admin
*/
function __VaultAdmin_init(
address _admin,
string memory metadataIpfsHash
) internal onlyInitializing {
if (_admin == address(0)) revert Errors.ZeroAddress();
admin = _admin;
emit MetadataUpdated(msg.sender, metadataIpfsHash);
}
/**
* @dev Internal method for checking whether the caller is admin
*/
function _checkAdmin() internal view {
if (msg.sender != admin) revert Errors.AccessDenied();
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {SafeCast} from '@openzeppelin/contracts/utils/math/SafeCast.sol';
import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';
import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
import {IVaultEnterExit} from '../../interfaces/IVaultEnterExit.sol';
import {ExitQueue} from '../../libraries/ExitQueue.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultImmutables} from './VaultImmutables.sol';
import {VaultState} from './VaultState.sol';
/**
* @title VaultEnterExit
* @author StakeWise
* @notice Defines the functionality for entering and exiting the Vault
*/
abstract contract VaultEnterExit is VaultImmutables, Initializable, VaultState, IVaultEnterExit {
using ExitQueue for ExitQueue.History;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
uint256 private immutable _exitingAssetsClaimDelay;
/**
* @dev Constructor
* @dev Since the immutable variable value is stored in the bytecode,
* its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
* @param exitingAssetsClaimDelay The minimum delay after which the assets can be claimed after joining the exit queue
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(uint256 exitingAssetsClaimDelay) {
_exitingAssetsClaimDelay = exitingAssetsClaimDelay;
}
/// @inheritdoc IVaultEnterExit
function getExitQueueIndex(uint256 positionTicket) external view override returns (int256) {
uint256 checkpointIdx = _exitQueue.getCheckpointIndex(positionTicket);
return checkpointIdx < _exitQueue.checkpoints.length ? int256(checkpointIdx) : -1;
}
/// @inheritdoc IVaultEnterExit
function enterExitQueue(
uint256 shares,
address receiver
) public virtual override returns (uint256 positionTicket) {
return _enterExitQueue(msg.sender, shares, receiver);
}
/// @inheritdoc IVaultEnterExit
function calculateExitedAssets(
address receiver,
uint256 positionTicket,
uint256 timestamp,
uint256 exitQueueIndex
)
public
view
override
returns (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets)
{
uint256 exitingTickets = _exitRequests[
keccak256(abi.encode(receiver, timestamp, positionTicket))
];
if (exitingTickets == 0) return (0, 0, 0);
// calculate exited tickets and assets
(exitedTickets, exitedAssets) = _exitQueue.calculateExitedAssets(
exitQueueIndex,
positionTicket,
exitingTickets
);
leftTickets = exitingTickets - exitedTickets;
}
/// @inheritdoc IVaultEnterExit
function claimExitedAssets(
uint256 positionTicket,
uint256 timestamp,
uint256 exitQueueIndex
) external override {
// calculate exited tickets and assets
(uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) = calculateExitedAssets(
msg.sender,
positionTicket,
timestamp,
exitQueueIndex
);
if (
block.timestamp < timestamp + _exitingAssetsClaimDelay ||
exitedTickets == 0 ||
exitedAssets == 0
) {
revert Errors.ExitRequestNotProcessed();
}
// update unclaimed assets
_unclaimedAssets -= SafeCast.toUint128(exitedAssets);
// clean up current exit request
delete _exitRequests[keccak256(abi.encode(msg.sender, timestamp, positionTicket))];
// skip creating new position for the tickets rounding error
uint256 newPositionTicket;
if (leftTickets > 1) {
// update user's queue position
newPositionTicket = positionTicket + exitedTickets;
_exitRequests[keccak256(abi.encode(msg.sender, timestamp, newPositionTicket))] = leftTickets;
}
// transfer assets to the receiver
_transferVaultAssets(msg.sender, exitedAssets);
emit ExitedAssetsClaimed(msg.sender, positionTicket, newPositionTicket, exitedAssets);
}
/**
* @dev Internal function that must be used to process user deposits
* @param to The address to mint shares to
* @param assets The number of assets deposited
* @param referrer The address of the referrer. Set to zero address if not used.
* @return shares The total amount of shares minted
*/
function _deposit(
address to,
uint256 assets,
address referrer
) internal virtual returns (uint256 shares) {
_checkHarvested();
if (to == address(0)) revert Errors.ZeroAddress();
if (assets == 0) revert Errors.InvalidAssets();
uint256 totalAssetsAfter;
unchecked {
// cannot overflow as it is capped with underlying asset total supply
totalAssetsAfter = _totalAssets + assets;
}
if (totalAssetsAfter > capacity()) revert Errors.CapacityExceeded();
// calculate amount of shares to mint
shares = _convertToShares(assets, Math.Rounding.Ceil);
// update state
_totalAssets = SafeCast.toUint128(totalAssetsAfter);
_mintShares(to, shares);
emit Deposited(msg.sender, to, assets, shares, referrer);
}
/**
* @dev Internal function for sending user shares to the exit queue
* @param user The address of the user
* @param shares The number of shares to send to exit queue
* @param receiver The address that will receive the assets
* @return positionTicket The position ticket in the exit queue. Returns max uint256 if no ticket is created.
*/
function _enterExitQueue(
address user,
uint256 shares,
address receiver
) internal virtual returns (uint256 positionTicket) {
if (shares == 0) revert Errors.InvalidShares();
if (receiver == address(0)) revert Errors.ZeroAddress();
if (!_isCollateralized()) {
// calculate amount of assets to burn
uint256 assets = convertToAssets(shares);
if (assets == 0) revert Errors.InvalidAssets();
// update total assets
_totalAssets -= SafeCast.toUint128(assets);
// burn owner shares
_burnShares(user, shares);
// transfer assets to the receiver
_transferVaultAssets(receiver, assets);
emit Redeemed(user, receiver, assets, shares);
// no ticket is created, return max value
return type(uint256).max;
}
// SLOAD to memory
uint256 _queuedShares = queuedShares;
// calculate position ticket
positionTicket = _exitQueue.getLatestTotalTickets() + _totalExitingTickets + _queuedShares;
// add to the exit requests
_exitRequests[keccak256(abi.encode(receiver, block.timestamp, positionTicket))] = shares;
// reverts if owner does not have enough shares
_balances[user] -= shares;
unchecked {
// cannot overflow as it is capped with _totalShares
queuedShares = SafeCast.toUint128(_queuedShares + shares);
}
emit ExitQueueEntered(user, receiver, positionTicket, shares);
}
/**
* @dev Internal function for transferring assets from the Vault to the receiver
* @dev IMPORTANT: because control is transferred to the receiver, care must be
* taken to not create reentrancy vulnerabilities. The Vault must follow the checks-effects-interactions pattern:
* https://docs.soliditylang.org/en/v0.8.22/security-considerations.html#use-the-checks-effects-interactions-pattern
* @param receiver The address that will receive the assets
* @param assets The number of assets to transfer
*/
function _transferVaultAssets(address receiver, uint256 assets) internal virtual;
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {ReentrancyGuardUpgradeable} from '@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol';
import {Address} from '@openzeppelin/contracts/utils/Address.sol';
import {IEthValidatorsRegistry} from '../../interfaces/IEthValidatorsRegistry.sol';
import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
import {IVaultEthStaking} from '../../interfaces/IVaultEthStaking.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultValidators} from './VaultValidators.sol';
import {VaultState} from './VaultState.sol';
import {VaultEnterExit} from './VaultEnterExit.sol';
import {VaultMev} from './VaultMev.sol';
/**
* @title VaultEthStaking
* @author StakeWise
* @notice Defines the Ethereum staking functionality for the Vault
*/
abstract contract VaultEthStaking is
Initializable,
ReentrancyGuardUpgradeable,
VaultState,
VaultValidators,
VaultEnterExit,
VaultMev,
IVaultEthStaking
{
uint256 private constant _securityDeposit = 1e9;
/// @inheritdoc IVaultEthStaking
function deposit(
address receiver,
address referrer
) public payable virtual override returns (uint256 shares) {
return _deposit(receiver, msg.value, referrer);
}
/// @inheritdoc IVaultEthStaking
function updateStateAndDeposit(
address receiver,
address referrer,
IKeeperRewards.HarvestParams calldata harvestParams
) public payable virtual override returns (uint256 shares) {
updateState(harvestParams);
return deposit(receiver, referrer);
}
/**
* @dev Function for depositing using fallback function
*/
receive() external payable virtual {
_deposit(msg.sender, msg.value, address(0));
}
/// @inheritdoc IVaultEthStaking
function receiveFromMevEscrow() external payable override {
if (msg.sender != mevEscrow()) revert Errors.AccessDenied();
}
/// @inheritdoc VaultValidators
function _registerSingleValidator(bytes calldata validator) internal virtual override {
bytes calldata publicKey = validator[:48];
IEthValidatorsRegistry(_validatorsRegistry).deposit{value: _validatorDeposit()}(
publicKey,
_withdrawalCredentials(),
validator[48:144],
bytes32(validator[144:_validatorLength()])
);
emit ValidatorRegistered(publicKey);
}
/// @inheritdoc VaultValidators
function _registerMultipleValidators(bytes calldata validators) internal virtual override {
uint256 startIndex;
uint256 endIndex;
uint256 validatorsCount = validators.length / _validatorLength();
bytes memory withdrawalCredentials = _withdrawalCredentials();
bytes calldata validator;
bytes calldata publicKey;
for (uint256 i = 0; i < validatorsCount; ) {
unchecked {
// cannot realistically overflow
endIndex += _validatorLength();
}
validator = validators[startIndex:endIndex];
publicKey = validator[:48];
IEthValidatorsRegistry(_validatorsRegistry).deposit{value: _validatorDeposit()}(
publicKey,
withdrawalCredentials,
validator[48:144],
bytes32(validator[144:_validatorLength()])
);
emit ValidatorRegistered(publicKey);
startIndex = endIndex;
unchecked {
// cannot realistically overflow
++i;
}
}
}
/// @inheritdoc VaultState
function _vaultAssets() internal view virtual override returns (uint256) {
return address(this).balance;
}
/// @inheritdoc VaultEnterExit
function _transferVaultAssets(
address receiver,
uint256 assets
) internal virtual override nonReentrant {
return Address.sendValue(payable(receiver), assets);
}
/// @inheritdoc VaultValidators
function _validatorLength() internal pure virtual override returns (uint256) {
return 176;
}
/// @inheritdoc VaultValidators
function _validatorDeposit() internal pure virtual override returns (uint256) {
return 32 ether;
}
/**
* @dev Internal function for calculating Vault withdrawal credentials
* @return The credentials used for the validators withdrawals
*/
function _withdrawalCredentials() private view returns (bytes memory) {
return abi.encodePacked(bytes1(0x01), bytes11(0x0), address(this));
}
/**
* @dev Initializes the VaultEthStaking contract
*/
function __VaultEthStaking_init() internal onlyInitializing {
__ReentrancyGuard_init();
// see https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706
if (msg.value < _securityDeposit) revert Errors.InvalidSecurityDeposit();
_deposit(address(this), msg.value, address(0));
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {IVaultFee} from '../../interfaces/IVaultFee.sol';
import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultAdmin} from './VaultAdmin.sol';
import {VaultImmutables} from './VaultImmutables.sol';
/**
* @title VaultFee
* @author StakeWise
* @notice Defines the fee functionality for the Vault
*/
abstract contract VaultFee is VaultImmutables, Initializable, VaultAdmin, IVaultFee {
uint256 internal constant _maxFeePercent = 10_000; // @dev 100.00 %
/// @inheritdoc IVaultFee
address public override feeRecipient;
/// @inheritdoc IVaultFee
uint16 public override feePercent;
/// @inheritdoc IVaultFee
function setFeeRecipient(address _feeRecipient) external override {
_checkAdmin();
_setFeeRecipient(_feeRecipient);
}
/**
* @dev Internal function for updating the fee recipient externally or from the initializer
* @param _feeRecipient The address of the new fee recipient
*/
function _setFeeRecipient(address _feeRecipient) private {
_checkHarvested();
if (_feeRecipient == address(0)) revert Errors.InvalidFeeRecipient();
// update fee recipient address
feeRecipient = _feeRecipient;
emit FeeRecipientUpdated(msg.sender, _feeRecipient);
}
/**
* @dev Initializes the VaultFee contract
* @param _feeRecipient The address of the fee recipient
* @param _feePercent The fee percent that is charged by the Vault
*/
function __VaultFee_init(address _feeRecipient, uint16 _feePercent) internal onlyInitializing {
if (_feePercent > _maxFeePercent) revert Errors.InvalidFeePercent();
_setFeeRecipient(_feeRecipient);
feePercent = _feePercent;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
import {Errors} from '../../libraries/Errors.sol';
/**
* @title VaultImmutables
* @author StakeWise
* @notice Defines the Vault common immutable variables
*/
abstract contract VaultImmutables {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address internal immutable _keeper;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address internal immutable _vaultsRegistry;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address internal immutable _validatorsRegistry;
/**
* @dev Constructor
* @dev Since the immutable variable value is stored in the bytecode,
* its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
* @param keeper The address of the Keeper contract
* @param vaultsRegistry The address of the VaultsRegistry contract
* @param validatorsRegistry The contract address used for registering validators in beacon chain
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address keeper, address vaultsRegistry, address validatorsRegistry) {
_keeper = keeper;
_vaultsRegistry = vaultsRegistry;
_validatorsRegistry = validatorsRegistry;
}
/**
* @dev Internal method for checking whether the vault is harvested
*/
function _checkHarvested() internal view {
if (IKeeperRewards(_keeper).isHarvestRequired(address(this))) revert Errors.NotHarvested();
}
/**
* @dev Internal method for checking whether the vault is collateralized
*/
function _checkCollateralized() internal view {
if (!_isCollateralized()) revert Errors.NotCollateralized();
}
/**
* @dev Returns whether the vault is collateralized
* @return true if the vault is collateralized
*/
function _isCollateralized() internal view returns (bool) {
return IKeeperRewards(_keeper).isCollateralized(address(this));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
import {ISharedMevEscrow} from '../../interfaces/ISharedMevEscrow.sol';
import {IOwnMevEscrow} from '../../interfaces/IOwnMevEscrow.sol';
import {IVaultMev} from '../../interfaces/IVaultMev.sol';
import {VaultState} from './VaultState.sol';
/**
* @title VaultMev
* @author StakeWise
* @notice Defines the Vaults' MEV functionality
*/
abstract contract VaultMev is Initializable, VaultState, IVaultMev {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _sharedMevEscrow;
address private _ownMevEscrow;
/**
* @dev Constructor
* @dev Since the immutable variable value is stored in the bytecode,
* its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
* @param sharedMevEscrow The address of the shared MEV escrow
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address sharedMevEscrow) {
_sharedMevEscrow = sharedMevEscrow;
}
/// @inheritdoc IVaultMev
function mevEscrow() public view override returns (address) {
// SLOAD to memory
address ownMevEscrow = _ownMevEscrow;
return ownMevEscrow != address(0) ? ownMevEscrow : _sharedMevEscrow;
}
/// @inheritdoc VaultState
function _harvestAssets(
IKeeperRewards.HarvestParams calldata harvestParams
) internal override returns (int256, bool) {
(int256 totalAssetsDelta, uint256 unlockedMevDelta, bool harvested) = IKeeperRewards(_keeper)
.harvest(harvestParams);
// harvest execution rewards only when consensus rewards were harvested
if (!harvested) return (totalAssetsDelta, harvested);
// SLOAD to memory
address _mevEscrow = mevEscrow();
if (_mevEscrow == _sharedMevEscrow) {
if (unlockedMevDelta > 0) {
// withdraw assets from shared escrow only in case reward is positive
ISharedMevEscrow(_mevEscrow).harvest(unlockedMevDelta);
}
return (totalAssetsDelta, harvested);
}
// execution rewards are always equal to what was accumulated in own MEV escrow
return (totalAssetsDelta + int256(IOwnMevEscrow(_mevEscrow).harvest()), harvested);
}
/**
* @dev Initializes the VaultMev contract
* @param ownMevEscrow The address of the own MEV escrow contract
*/
function __VaultMev_init(address ownMevEscrow) internal onlyInitializing {
if (ownMevEscrow != address(0)) _ownMevEscrow = ownMevEscrow;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';
import {SafeCast} from '@openzeppelin/contracts/utils/math/SafeCast.sol';
import {IOsTokenVaultController} from '../../interfaces/IOsTokenVaultController.sol';
import {IOsTokenConfig} from '../../interfaces/IOsTokenConfig.sol';
import {IVaultOsToken} from '../../interfaces/IVaultOsToken.sol';
import {IOsTokenVaultEscrow} from '../../interfaces/IOsTokenVaultEscrow.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultImmutables} from './VaultImmutables.sol';
import {VaultEnterExit, IVaultEnterExit} from './VaultEnterExit.sol';
import {VaultState} from './VaultState.sol';
/**
* @title VaultOsToken
* @author StakeWise
* @notice Defines the functionality for minting OsToken
*/
abstract contract VaultOsToken is VaultImmutables, VaultState, VaultEnterExit, IVaultOsToken {
uint256 private constant _wad = 1e18;
uint256 private constant _hfLiqThreshold = 1e18;
uint256 private constant _maxPercent = 1e18;
uint256 private constant _disabledLiqThreshold = type(uint64).max;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IOsTokenVaultController private immutable _osTokenVaultController;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IOsTokenConfig private immutable _osTokenConfig;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IOsTokenVaultEscrow private immutable _osTokenVaultEscrow;
mapping(address => OsTokenPosition) private _positions;
/**
* @dev Constructor
* @dev Since the immutable variable value is stored in the bytecode,
* its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
* @param osTokenVaultController The address of the OsTokenVaultController contract
* @param osTokenConfig The address of the OsTokenConfig contract
* @param osTokenVaultEscrow The address of the OsTokenVaultEscrow contract
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address osTokenVaultController, address osTokenConfig, address osTokenVaultEscrow) {
_osTokenVaultController = IOsTokenVaultController(osTokenVaultController);
_osTokenConfig = IOsTokenConfig(osTokenConfig);
_osTokenVaultEscrow = IOsTokenVaultEscrow(osTokenVaultEscrow);
}
/// @inheritdoc IVaultOsToken
function osTokenPositions(address user) public view override returns (uint128 shares) {
OsTokenPosition memory position = _positions[user];
if (position.shares != 0) _syncPositionFee(position);
return position.shares;
}
/// @inheritdoc IVaultOsToken
function mintOsToken(
address receiver,
uint256 osTokenShares,
address referrer
) public virtual override returns (uint256 assets) {
return _mintOsToken(msg.sender, receiver, osTokenShares, referrer);
}
/// @inheritdoc IVaultOsToken
function burnOsToken(uint128 osTokenShares) external override returns (uint256 assets) {
// burn osToken shares
assets = _osTokenVaultController.burnShares(msg.sender, osTokenShares);
// fetch user position
OsTokenPosition memory position = _positions[msg.sender];
if (position.shares == 0) revert Errors.InvalidPosition();
_syncPositionFee(position);
// update osToken position
position.shares -= osTokenShares;
_positions[msg.sender] = position;
// emit event
emit OsTokenBurned(msg.sender, assets, osTokenShares);
}
/// @inheritdoc IVaultOsToken
function liquidateOsToken(
uint256 osTokenShares,
address owner,
address receiver
) external override {
(uint256 burnedShares, uint256 receivedAssets) = _redeemOsToken(
owner,
receiver,
osTokenShares,
true
);
emit OsTokenLiquidated(
msg.sender,
owner,
receiver,
osTokenShares,
burnedShares,
receivedAssets
);
}
/// @inheritdoc IVaultOsToken
function redeemOsToken(uint256 osTokenShares, address owner, address receiver) external override {
if (msg.sender != _osTokenConfig.redeemer()) revert Errors.AccessDenied();
(uint256 burnedShares, uint256 receivedAssets) = _redeemOsToken(
owner,
receiver,
osTokenShares,
false
);
emit OsTokenRedeemed(msg.sender, owner, receiver, osTokenShares, burnedShares, receivedAssets);
}
/// @inheritdoc IVaultOsToken
function transferOsTokenPositionToEscrow(
uint256 osTokenShares
) external override returns (uint256 positionTicket) {
// check whether vault assets are up to date
_checkHarvested();
// fetch user osToken position
OsTokenPosition memory position = _positions[msg.sender];
if (position.shares == 0) revert Errors.InvalidPosition();
// sync accumulated fee
_syncPositionFee(position);
if (position.shares < osTokenShares) revert Errors.InvalidShares();
// calculate shares to enter the exit queue
uint256 exitShares = _balances[msg.sender];
if (position.shares != osTokenShares) {
// calculate exit shares
exitShares = Math.mulDiv(exitShares, osTokenShares, position.shares);
// update osToken position
unchecked {
// cannot underflow because position.shares >= osTokenShares
position.shares -= SafeCast.toUint128(osTokenShares);
}
_positions[msg.sender] = position;
} else {
// all the assets are sent to the exit queue, remove position
delete _positions[msg.sender];
}
// enter the exit queue
positionTicket = super.enterExitQueue(exitShares, address(_osTokenVaultEscrow));
// transfer to escrow
_osTokenVaultEscrow.register(
msg.sender,
positionTicket,
osTokenShares,
position.cumulativeFeePerShare
);
}
/// @inheritdoc IVaultEnterExit
function enterExitQueue(
uint256 shares,
address receiver
) public virtual override(IVaultEnterExit, VaultEnterExit) returns (uint256 positionTicket) {
positionTicket = super.enterExitQueue(shares, receiver);
_checkOsTokenPosition(msg.sender);
}
/**
* @dev Internal function for minting osToken shares
* @param owner The owner of the osToken position
* @param receiver The receiver of the osToken shares
* @param osTokenShares The amount of osToken shares to mint
* @param referrer The address of the referrer
* @return assets The amount of assets minted
*/
function _mintOsToken(
address owner,
address receiver,
uint256 osTokenShares,
address referrer
) internal returns (uint256 assets) {
_checkCollateralized();
_checkHarvested();
// fetch user position
OsTokenPosition memory position = _positions[owner];
if (position.shares != 0) {
_syncPositionFee(position);
} else {
position.cumulativeFeePerShare = SafeCast.toUint128(
_osTokenVaultController.cumulativeFeePerShare()
);
}
// calculate max osToken shares that user can mint
uint256 userMaxOsTokenShares = _calcMaxOsTokenShares(convertToAssets(_balances[owner]));
if (osTokenShares == type(uint256).max) {
if (userMaxOsTokenShares <= position.shares) {
return 0;
}
// calculate max OsToken shares that can be minted
unchecked {
// cannot underflow because position.shares < userMaxOsTokenShares
osTokenShares = userMaxOsTokenShares - position.shares;
}
}
// mint osToken shares to the receiver
assets = _osTokenVaultController.mintShares(receiver, osTokenShares);
// add minted shares to the position
position.shares += SafeCast.toUint128(osTokenShares);
// calculate and validate LTV
if (userMaxOsTokenShares < position.shares) {
revert Errors.LowLtv();
}
// update state
_positions[owner] = position;
// emit event
emit OsTokenMinted(owner, receiver, assets, osTokenShares, referrer);
}
/**
* @dev Internal function for redeeming and liquidating osToken shares
* @param owner The minter of the osToken shares
* @param receiver The receiver of the assets
* @param osTokenShares The amount of osToken shares to redeem or liquidate
* @param isLiquidation Whether the liquidation or redemption is being performed
* @return burnedShares The amount of shares burned
* @return receivedAssets The amount of assets received
*/
function _redeemOsToken(
address owner,
address receiver,
uint256 osTokenShares,
bool isLiquidation
) private returns (uint256 burnedShares, uint256 receivedAssets) {
if (receiver == address(0)) revert Errors.ZeroAddress();
_checkHarvested();
// update osToken state for gas efficiency
_osTokenVaultController.updateState();
// fetch user position
OsTokenPosition memory position = _positions[owner];
if (position.shares == 0) revert Errors.InvalidPosition();
_syncPositionFee(position);
// SLOAD to memory
IOsTokenConfig.Config memory osTokenConfig = _osTokenConfig.getConfig(address(this));
if (isLiquidation && osTokenConfig.liqThresholdPercent == _disabledLiqThreshold) {
revert Errors.LiquidationDisabled();
}
// calculate received assets
if (isLiquidation) {
receivedAssets = Math.mulDiv(
_osTokenVaultController.convertToAssets(osTokenShares),
osTokenConfig.liqBonusPercent,
_maxPercent
);
} else {
receivedAssets = _osTokenVaultController.convertToAssets(osTokenShares);
}
{
// check whether received assets are valid
uint256 depositedAssets = convertToAssets(_balances[owner]);
if (receivedAssets > depositedAssets || receivedAssets > withdrawableAssets()) {
revert Errors.InvalidReceivedAssets();
}
uint256 mintedAssets = _osTokenVaultController.convertToAssets(position.shares);
if (isLiquidation) {
// check health factor violation in case of liquidation
if (
Math.mulDiv(
depositedAssets * _wad,
osTokenConfig.liqThresholdPercent,
mintedAssets * _maxPercent
) >= _hfLiqThreshold
) {
revert Errors.InvalidHealthFactor();
}
}
}
// reduce osToken supply
_osTokenVaultController.burnShares(msg.sender, osTokenShares);
// update osToken position
position.shares -= SafeCast.toUint128(osTokenShares);
_positions[owner] = position;
burnedShares = convertToShares(receivedAssets);
// update total assets
unchecked {
_totalAssets -= SafeCast.toUint128(receivedAssets);
}
// burn owner shares
_burnShares(owner, burnedShares);
// transfer assets to the receiver
_transferVaultAssets(receiver, receivedAssets);
}
/**
* @dev Internal function for syncing the osToken fee
* @param position The position to sync the fee for
*/
function _syncPositionFee(OsTokenPosition memory position) private view {
// fetch current cumulative fee per share
uint256 cumulativeFeePerShare = _osTokenVaultController.cumulativeFeePerShare();
// check whether fee is already up to date
if (cumulativeFeePerShare == position.cumulativeFeePerShare) return;
// add treasury fee to the position
position.shares = SafeCast.toUint128(
Math.mulDiv(position.shares, cumulativeFeePerShare, position.cumulativeFeePerShare)
);
position.cumulativeFeePerShare = SafeCast.toUint128(cumulativeFeePerShare);
}
/**
* @notice Internal function for checking position validity. Reverts if it is invalid.
* @param user The address of the user
*/
function _checkOsTokenPosition(address user) internal view {
// fetch user position
OsTokenPosition memory position = _positions[user];
if (position.shares == 0) return;
// check whether vault assets are up to date
_checkHarvested();
// sync fee
_syncPositionFee(position);
// calculate and validate position LTV
if (_calcMaxOsTokenShares(convertToAssets(_balances[user])) < position.shares) {
revert Errors.LowLtv();
}
}
/**
* @dev Internal function for calculating the maximum amount of osToken shares that can be minted
* @param assets The amount of assets to convert to osToken shares
* @return maxOsTokenShares The maximum amount of osToken shares that can be minted
*/
function _calcMaxOsTokenShares(uint256 assets) internal view returns (uint256) {
uint256 maxOsTokenAssets = Math.mulDiv(
assets,
_osTokenConfig.getConfig(address(this)).ltvPercent,
_maxPercent
);
return _osTokenVaultController.convertToShares(maxOsTokenAssets);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {SafeCast} from '@openzeppelin/contracts/utils/math/SafeCast.sol';
import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';
import {IVaultState} from '../../interfaces/IVaultState.sol';
import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
import {ExitQueue} from '../../libraries/ExitQueue.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultImmutables} from './VaultImmutables.sol';
import {VaultFee} from './VaultFee.sol';
/**
* @title VaultState
* @author StakeWise
* @notice Defines Vault's state manipulation
*/
abstract contract VaultState is VaultImmutables, Initializable, VaultFee, IVaultState {
using ExitQueue for ExitQueue.History;
uint128 internal _totalShares;
uint128 internal _totalAssets;
/// @inheritdoc IVaultState
uint128 public override queuedShares;
uint128 internal _unclaimedAssets;
ExitQueue.History internal _exitQueue;
mapping(bytes32 => uint256) internal _exitRequests;
mapping(address => uint256) internal _balances;
uint256 private _capacity;
/// @inheritdoc IVaultState
uint128 public override totalExitingAssets; // deprecated
uint128 internal _totalExitingTickets; // deprecated
uint256 internal _totalExitedTickets; // deprecated
/// @inheritdoc IVaultState
function totalShares() external view override returns (uint256) {
return _totalShares;
}
/// @inheritdoc IVaultState
function totalAssets() external view override returns (uint256) {
return _totalAssets;
}
/// @inheritdoc IVaultState
function getShares(address account) external view override returns (uint256) {
return _balances[account];
}
/// @inheritdoc IVaultState
function convertToShares(uint256 assets) public view override returns (uint256 shares) {
return _convertToShares(assets, Math.Rounding.Floor);
}
/// @inheritdoc IVaultState
function convertToAssets(uint256 shares) public view override returns (uint256 assets) {
uint256 totalShares_ = _totalShares;
return (totalShares_ == 0) ? shares : Math.mulDiv(shares, _totalAssets, totalShares_);
}
/// @inheritdoc IVaultState
function capacity() public view override returns (uint256) {
// SLOAD to memory
uint256 capacity_ = _capacity;
// if capacity is not set, it is unlimited
return capacity_ == 0 ? type(uint256).max : capacity_;
}
/// @inheritdoc IVaultState
function withdrawableAssets() public view override returns (uint256) {
uint256 vaultAssets = _vaultAssets();
unchecked {
// calculate assets that are reserved by users who queued for exit
// cannot overflow as it is capped with underlying asset total supply
uint256 reservedAssets = convertToAssets(queuedShares) +
totalExitingAssets +
_unclaimedAssets;
return vaultAssets > reservedAssets ? vaultAssets - reservedAssets : 0;
}
}
/// @inheritdoc IVaultState
function isStateUpdateRequired() external view override returns (bool) {
return IKeeperRewards(_keeper).isHarvestRequired(address(this));
}
/// @inheritdoc IVaultState
function updateState(
IKeeperRewards.HarvestParams calldata harvestParams
) public virtual override {
// process total assets delta since last update
(int256 totalAssetsDelta, bool harvested) = _harvestAssets(harvestParams);
// process total assets delta if it has changed
_processTotalAssetsDelta(totalAssetsDelta);
// update exit queue every time new update is harvested
if (harvested) _updateExitQueue();
}
/**
* @dev Internal function for processing rewards and penalties
* @param totalAssetsDelta The number of assets earned or lost
*/
function _processTotalAssetsDelta(int256 totalAssetsDelta) internal virtual {
// skip processing if there is no change in assets
if (totalAssetsDelta == 0) return;
// SLOAD to memory
uint256 newTotalAssets = _totalAssets;
if (totalAssetsDelta < 0) {
uint256 penalty = uint256(-totalAssetsDelta);
// SLOAD to memory
uint256 _totalExitingAssets = totalExitingAssets;
if (_totalExitingAssets > 0) {
// apply penalty to exiting assets
uint256 exitingAssetsPenalty = Math.mulDiv(
penalty,
_totalExitingAssets,
_totalExitingAssets + newTotalAssets
);
// apply penalty to total exiting assets
unchecked {
// cannot underflow as exitingAssetsPenalty <= penalty
penalty -= exitingAssetsPenalty;
// cannot underflow as exitingAssetsPenalty <= _totalExitingAssets
totalExitingAssets = SafeCast.toUint128(_totalExitingAssets - exitingAssetsPenalty);
}
emit ExitingAssetsPenalized(exitingAssetsPenalty);
}
// subtract penalty from total assets (excludes exiting assets)
if (penalty > 0) {
_totalAssets = SafeCast.toUint128(newTotalAssets - penalty);
}
return;
}
// convert assets delta as it is positive
uint256 profitAssets = uint256(totalAssetsDelta);
newTotalAssets += profitAssets;
// update state
_totalAssets = SafeCast.toUint128(newTotalAssets);
// calculate admin fee recipient assets
uint256 feeRecipientAssets = Math.mulDiv(profitAssets, feePercent, _maxFeePercent);
if (feeRecipientAssets == 0) return;
// SLOAD to memory
uint256 totalShares_ = _totalShares;
// calculate fee recipient's shares
uint256 feeRecipientShares;
if (totalShares_ == 0) {
feeRecipientShares = feeRecipientAssets;
} else {
unchecked {
feeRecipientShares = Math.mulDiv(
feeRecipientAssets,
totalShares_,
newTotalAssets - feeRecipientAssets
);
}
}
// SLOAD to memory
address _feeRecipient = feeRecipient;
// mint shares to the fee recipient
_mintShares(_feeRecipient, feeRecipientShares);
emit FeeSharesMinted(_feeRecipient, feeRecipientShares, feeRecipientAssets);
}
/**
* @dev Internal function that must be used to process exit queue
* @dev Make sure that sufficient time passed between exit queue updates (at least 1 day).
* Currently it's restricted by the keeper's harvest interval
* @return burnedShares The total amount of burned shares
*/
function _updateExitQueue() internal virtual returns (uint256 burnedShares) {
// calculate assets that can be used to process the exit requests
uint256 availableAssets = _vaultAssets() - _unclaimedAssets;
if (availableAssets == 0) return 0;
// SLOAD to memory
uint256 _totalExitingAssets = totalExitingAssets;
if (_totalExitingAssets > 0) {
// wait for all the exiting assets from v2 to be processed
if (availableAssets < _totalExitingAssets) return 0;
// SLOAD to memory
uint256 totalExitingTickets = _totalExitingTickets;
// push checkpoint so that exited assets could be claimed
_exitQueue.push(totalExitingTickets, _totalExitingAssets);
emit CheckpointCreated(totalExitingTickets, _totalExitingAssets);
unchecked {
// cannot underflow as _totalExitingAssets <= availableAssets
availableAssets -= _totalExitingAssets;
}
// update state
_unclaimedAssets += SafeCast.toUint128(_totalExitingAssets);
_totalExitingTickets = 0;
totalExitingAssets = 0;
}
// SLOAD to memory
uint256 _queuedShares = queuedShares;
if (_queuedShares == 0 || availableAssets == 0) return 0;
// calculate the amount of assets that can be exited
uint256 exitedAssets = Math.min(availableAssets, convertToAssets(_queuedShares));
if (exitedAssets == 0) return 0;
// calculate the amount of shares that can be burned
burnedShares = convertToShares(exitedAssets);
if (burnedShares == 0) return 0;
// update queued shares and unclaimed assets
queuedShares = SafeCast.toUint128(_queuedShares - burnedShares);
_unclaimedAssets += SafeCast.toUint128(exitedAssets);
// push checkpoint so that exited assets could be claimed
_exitQueue.push(burnedShares, exitedAssets);
emit CheckpointCreated(burnedShares, exitedAssets);
// update state
_totalShares -= SafeCast.toUint128(burnedShares);
_totalAssets -= SafeCast.toUint128(exitedAssets);
}
/**
* @dev Internal function for minting shares
* @param owner The address of the owner to mint shares to
* @param shares The number of shares to mint
*/
function _mintShares(address owner, uint256 shares) internal virtual {
// update total shares
_totalShares += SafeCast.toUint128(shares);
// mint shares
unchecked {
// cannot overflow because the sum of all user
// balances can't exceed the max uint256 value
_balances[owner] += shares;
}
}
/**
* @dev Internal function for burning shares
* @param owner The address of the owner to burn shares for
* @param shares The number of shares to burn
*/
function _burnShares(address owner, uint256 shares) internal virtual {
// burn shares
_balances[owner] -= shares;
// update total shares
unchecked {
// cannot underflow because the sum of all shares can't exceed the _totalShares
_totalShares -= SafeCast.toUint128(shares);
}
}
/**
* @dev Internal conversion function (from assets to shares) with support for rounding direction.
*/
function _convertToShares(
uint256 assets,
Math.Rounding rounding
) internal view returns (uint256 shares) {
uint256 totalShares_ = _totalShares;
// Will revert if assets > 0, totalShares > 0 and _totalAssets = 0.
// That corresponds to a case where any asset would represent an infinite amount of shares.
return
(assets == 0 || totalShares_ == 0)
? assets
: Math.mulDiv(assets, totalShares_, _totalAssets, rounding);
}
/**
* @dev Internal function for harvesting Vaults' new assets
* @return The total assets delta after harvest
* @return `true` when the rewards were harvested, `false` otherwise
*/
function _harvestAssets(
IKeeperRewards.HarvestParams calldata harvestParams
) internal virtual returns (int256, bool);
/**
* @dev Internal function for retrieving the total assets stored in the Vault.
* NB! Assets can be forcibly sent to the vault, the returned value must be used with caution
* @return The total amount of assets stored in the Vault
*/
function _vaultAssets() internal view virtual returns (uint256);
/**
* @dev Initializes the VaultState contract
* @param capacity_ The amount after which the Vault stops accepting deposits
*/
function __VaultState_init(uint256 capacity_) internal onlyInitializing {
if (capacity_ == 0) revert Errors.InvalidCapacity();
// skip setting capacity if it is unlimited
if (capacity_ != type(uint256).max) _capacity = capacity_;
}
/**
* @dev Initializes the VaultState contract upgrade to V3
*/
function __VaultState_initV3() internal onlyInitializing {
// SLOAD to memory
uint256 _queuedShares = queuedShares;
if (_queuedShares > 1) revert Errors.InvalidQueuedShares();
if (_queuedShares == 1) {
// burn the rounding error queued share
_totalShares -= 1;
queuedShares = 0;
_exitQueue.push(1, 0);
emit CheckpointCreated(1, 0);
}
// SLOAD to memory
uint256 totalExitedTickets = _totalExitedTickets;
uint256 exitQueueTicket = _exitQueue.getLatestTotalTickets();
if (exitQueueTicket < totalExitedTickets) {
uint256 exitedTickets;
unchecked {
// cannot underflow as latestTicket >= exitQueueTicket
exitedTickets = totalExitedTickets - exitQueueTicket;
}
_exitQueue.push(exitedTickets, 0);
emit CheckpointCreated(exitedTickets, 0);
}
_totalExitedTickets = 0;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[48] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {MerkleProof} from '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';
import {MessageHashUtils} from '@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol';
import {SignatureChecker} from '@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol';
import {IKeeperValidators} from '../../interfaces/IKeeperValidators.sol';
import {IDepositDataRegistry} from '../../interfaces/IDepositDataRegistry.sol';
import {IVaultValidators} from '../../interfaces/IVaultValidators.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultImmutables} from './VaultImmutables.sol';
import {VaultAdmin} from './VaultAdmin.sol';
import {VaultState} from './VaultState.sol';
/**
* @title VaultValidators
* @author StakeWise
* @notice Defines the validators functionality for the Vault
*/
abstract contract VaultValidators is
VaultImmutables,
Initializable,
VaultAdmin,
VaultState,
IVaultValidators
{
bytes32 private constant _registerValidatorsTypeHash =
keccak256('VaultValidators(bytes32 validatorsRegistryRoot,bytes validators)');
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _depositDataRegistry;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
uint256 private immutable _initialChainId;
/// deprecated. Deposit data management is moved to DepositDataRegistry contract
bytes32 private _validatorsRoot;
/// deprecated. Deposit data management is moved to DepositDataRegistry contract
uint256 private _validatorIndex;
address private _validatorsManager;
bytes32 private _initialDomainSeparator;
/**
* @dev Constructor
* @dev Since the immutable variable value is stored in the bytecode,
* its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
* @param depositDataRegistry The address of the DepositDataRegistry contract
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address depositDataRegistry) {
_depositDataRegistry = depositDataRegistry;
_initialChainId = block.chainid;
}
/// @inheritdoc IVaultValidators
function validatorsManager() public view override returns (address) {
// SLOAD to memory
address validatorsManager_ = _validatorsManager;
// if validatorsManager is not set, use DepositDataRegistry contract address
return validatorsManager_ == address(0) ? _depositDataRegistry : validatorsManager_;
}
/// @inheritdoc IVaultValidators
function registerValidators(
IKeeperValidators.ApprovalParams calldata keeperParams,
bytes calldata validatorsManagerSignature
) external override {
// get approval from oracles
IKeeperValidators(_keeper).approveValidators(keeperParams);
// check vault is up to date
_checkHarvested();
// check access
address validatorsManager_ = validatorsManager();
if (
msg.sender != validatorsManager_ &&
!SignatureChecker.isValidSignatureNow(
validatorsManager_,
_getSignedMessageHash(keeperParams),
validatorsManagerSignature
)
) {
revert Errors.AccessDenied();
}
// check validators length is valid
uint256 validatorLength = _validatorLength();
uint256 validatorsCount = keeperParams.validators.length / validatorLength;
unchecked {
if (
validatorsCount == 0 || validatorsCount * validatorLength != keeperParams.validators.length
) {
revert Errors.InvalidValidators();
}
}
// check enough withdrawable assets
if (withdrawableAssets() < _validatorDeposit() * validatorsCount) {
revert Errors.InsufficientAssets();
}
if (keeperParams.validators.length == validatorLength) {
// register single validator
_registerSingleValidator(keeperParams.validators);
} else {
// register multiple validators
_registerMultipleValidators(keeperParams.validators);
}
}
/// @inheritdoc IVaultValidators
function setValidatorsManager(address validatorsManager_) external override {
_checkAdmin();
// update validatorsManager address
_validatorsManager = validatorsManager_;
emit ValidatorsManagerUpdated(msg.sender, validatorsManager_);
}
/**
* @dev Internal function for registering validator. Must emit ValidatorRegistered event.
* @param validator The validator registration data
*/
function _registerSingleValidator(bytes calldata validator) internal virtual;
/**
* @dev Internal function for registering multiple validators. Must emit ValidatorRegistered event for every validator.
* @param validators The validators registration data
*/
function _registerMultipleValidators(bytes calldata validators) internal virtual;
/**
* @dev Internal function for defining the length of the validator data
* @return The length of the single validator data
*/
function _validatorLength() internal pure virtual returns (uint256);
/**
* @dev Internal function for fetching validator deposit amount
*/
function _validatorDeposit() internal pure virtual returns (uint256);
/**
* @dev Initializes the VaultValidators contract
* @dev NB! This initializer must be called after VaultState initializer
*/
function __VaultValidators_init() internal onlyInitializing {
if (capacity() < _validatorDeposit()) revert Errors.InvalidCapacity();
// initialize domain separator
_initialDomainSeparator = _computeVaultValidatorsDomain();
}
/**
* @dev Initializes the V2 of the VaultValidators contract
*/
function __VaultValidators_initV2() internal onlyInitializing {
// initialize domain separator
_initialDomainSeparator = _computeVaultValidatorsDomain();
// migrate deposit data variables to DepositDataRegistry contract
IDepositDataRegistry(_depositDataRegistry).migrate(
_validatorsRoot,
_validatorIndex,
_validatorsManager
);
// clean up variables
delete _validatorsRoot;
delete _validatorIndex;
delete _validatorsManager;
}
/**
* @dev Initializes the V3 of the VaultValidators contract
*/
function __VaultValidators_initV3() internal onlyInitializing {
// initialize domain separator
bytes32 newInitialDomainSeparator = _computeVaultValidatorsDomain();
if (newInitialDomainSeparator != _initialDomainSeparator) {
_initialDomainSeparator = newInitialDomainSeparator;
}
}
/**
* @notice Get the hash to be signed by the validators manager
* @param keeperParams The keeper approval parameters
* @return The hash to be signed
*/
function _getSignedMessageHash(
IKeeperValidators.ApprovalParams calldata keeperParams
) private view returns (bytes32) {
bytes32 domainSeparator = block.chainid == _initialChainId
? _initialDomainSeparator
: _computeVaultValidatorsDomain();
return
MessageHashUtils.toTypedDataHash(
domainSeparator,
keccak256(
abi.encode(
_registerValidatorsTypeHash,
keeperParams.validatorsRegistryRoot,
keccak256(keeperParams.validators)
)
)
);
}
/**
* @notice Computes the hash of the EIP712 typed data
* @dev This function is used to compute the hash of the EIP712 typed data
* @return The hash of the EIP712 typed data
*/
function _computeVaultValidatorsDomain() private view returns (bytes32) {
return
keccak256(
abi.encode(
keccak256(
'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
),
keccak256(bytes('VaultValidators')),
keccak256('1'),
block.chainid,
address(this)
)
);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {UUPSUpgradeable} from '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {ERC1967Utils} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol';
import {IVaultsRegistry} from '../../interfaces/IVaultsRegistry.sol';
import {IVaultVersion} from '../../interfaces/IVaultVersion.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultAdmin} from './VaultAdmin.sol';
import {VaultImmutables} from './VaultImmutables.sol';
/**
* @title VaultVersion
* @author StakeWise
* @notice Defines the versioning functionality for the Vault
*/
abstract contract VaultVersion is
VaultImmutables,
Initializable,
UUPSUpgradeable,
VaultAdmin,
IVaultVersion
{
bytes4 private constant _initSelector = bytes4(keccak256('initialize(bytes)'));
/// @inheritdoc IVaultVersion
function implementation() external view override returns (address) {
return ERC1967Utils.getImplementation();
}
/// @inheritdoc UUPSUpgradeable
function upgradeToAndCall(
address newImplementation,
bytes memory data
) public payable override onlyProxy {
super.upgradeToAndCall(newImplementation, abi.encodeWithSelector(_initSelector, data));
}
/// @inheritdoc UUPSUpgradeable
function _authorizeUpgrade(address newImplementation) internal view override {
_checkAdmin();
if (
newImplementation == address(0) ||
ERC1967Utils.getImplementation() == newImplementation || // cannot reinit the same implementation
IVaultVersion(newImplementation).vaultId() != vaultId() || // vault must be of the same type
IVaultVersion(newImplementation).version() != version() + 1 || // vault cannot skip versions between
!IVaultsRegistry(_vaultsRegistry).vaultImpls(newImplementation) // new implementation must be registered
) {
revert Errors.UpgradeFailed();
}
}
/// @inheritdoc IVaultVersion
function vaultId() public pure virtual override returns (bytes32);
/// @inheritdoc IVaultVersion
function version() public pure virtual override returns (uint8);
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {IVaultWhitelist} from '../../interfaces/IVaultWhitelist.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultAdmin} from './VaultAdmin.sol';
/**
* @title VaultWhitelist
* @author StakeWise
* @notice Defines the whitelisting functionality for the Vault
*/
abstract contract VaultWhitelist is Initializable, VaultAdmin, IVaultWhitelist {
/// @inheritdoc IVaultWhitelist
address public override whitelister;
/// @inheritdoc IVaultWhitelist
mapping(address => bool) public override whitelistedAccounts;
/// @inheritdoc IVaultWhitelist
function updateWhitelist(address account, bool approved) external override {
if (msg.sender != whitelister) revert Errors.AccessDenied();
if (whitelistedAccounts[account] == approved) return;
whitelistedAccounts[account] = approved;
emit WhitelistUpdated(msg.sender, account, approved);
}
/// @inheritdoc IVaultWhitelist
function setWhitelister(address _whitelister) external override {
_checkAdmin();
_setWhitelister(_whitelister);
}
/**
* @notice Internal function for checking whether account is in the whitelist
* @param account The address of the account to check
*/
function _checkWhitelist(address account) internal view {
if (!whitelistedAccounts[account]) revert Errors.AccessDenied();
}
/**
* @dev Internal function for updating the whitelister externally or from the initializer
* @param _whitelister The address of the new whitelister
*/
function _setWhitelister(address _whitelister) private {
// update whitelister address
whitelister = _whitelister;
emit WhitelisterUpdated(msg.sender, _whitelister);
}
/**
* @dev Initializes the VaultWhitelist contract
* @param _whitelister The address of the whitelister
*/
function __VaultWhitelist_init(address _whitelister) internal onlyInitializing {
_setWhitelister(_whitelister);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}{
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 200,
"details": {
"yul": true
}
},
"evmVersion": "shanghai",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_vaultsRegistry","type":"address"},{"internalType":"address","name":"_validatorsRegistry","type":"address"},{"internalType":"address","name":"osTokenVaultController","type":"address"},{"internalType":"address","name":"osTokenConfig","type":"address"},{"internalType":"address","name":"osTokenVaultEscrow","type":"address"},{"internalType":"address","name":"sharedMevEscrow","type":"address"},{"internalType":"address","name":"depositDataRegistry","type":"address"},{"internalType":"uint256","name":"exitingAssetsClaimDelay","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"CapacityExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"ExitRequestNotProcessed","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientAssets","type":"error"},{"inputs":[],"name":"InvalidAssets","type":"error"},{"inputs":[],"name":"InvalidCapacity","type":"error"},{"inputs":[],"name":"InvalidCheckpointIndex","type":"error"},{"inputs":[],"name":"InvalidCheckpointValue","type":"error"},{"inputs":[],"name":"InvalidFeePercent","type":"error"},{"inputs":[],"name":"InvalidFeeRecipient","type":"error"},{"inputs":[],"name":"InvalidHealthFactor","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidPosition","type":"error"},{"inputs":[],"name":"InvalidQueuedShares","type":"error"},{"inputs":[],"name":"InvalidReceivedAssets","type":"error"},{"inputs":[],"name":"InvalidSecurityDeposit","type":"error"},{"inputs":[],"name":"InvalidShares","type":"error"},{"inputs":[],"name":"InvalidValidators","type":"error"},{"inputs":[],"name":"LiquidationDisabled","type":"error"},{"inputs":[],"name":"LowLtv","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"NotCollateralized","type":"error"},{"inputs":[],"name":"NotHarvested","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[],"name":"UpgradeFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"CheckpointCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"address","name":"referrer","type":"address"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"positionTicket","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"ExitQueueEntered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"prevPositionTicket","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPositionTicket","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"withdrawnAssets","type":"uint256"}],"name":"ExitedAssetsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"}],"name":"ExitingAssetsPenalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"FeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"FeeSharesMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"keysManager","type":"address"}],"name":"KeysManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"string","name":"metadataIpfsHash","type":"string"}],"name":"MetadataUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"OsTokenBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"osTokenShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"receivedAssets","type":"uint256"}],"name":"OsTokenLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"address","name":"referrer","type":"address"}],"name":"OsTokenMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"osTokenShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"OsTokenRedeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Redeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"positionTicket","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"V2ExitQueueEntered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"publicKey","type":"bytes"}],"name":"ValidatorRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"validatorsManager","type":"address"}],"name":"ValidatorsManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"bytes32","name":"validatorsRoot","type":"bytes32"}],"name":"ValidatorsRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"WhitelistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"whitelister","type":"address"}],"name":"WhitelisterUpdated","type":"event"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"osTokenShares","type":"uint128"}],"name":"burnOsToken","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"positionTicket","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"exitQueueIndex","type":"uint256"}],"name":"calculateExitedAssets","outputs":[{"internalType":"uint256","name":"leftTickets","type":"uint256"},{"internalType":"uint256","name":"exitedTickets","type":"uint256"},{"internalType":"uint256","name":"exitedAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"capacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionTicket","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"exitQueueIndex","type":"uint256"}],"name":"claimExitedAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"referrer","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"osTokenShares","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"}],"name":"depositAndMintOsToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"enterExitQueue","outputs":[{"internalType":"uint256","name":"positionTicket","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feePercent","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionTicket","type":"uint256"}],"name":"getExitQueueIndex","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"params","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"isStateUpdateRequired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"osTokenShares","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"receiver","type":"address"}],"name":"liquidateOsToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mevEscrow","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"osTokenShares","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"}],"name":"mintOsToken","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"osTokenPositions","outputs":[{"internalType":"uint128","name":"shares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"queuedShares","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"receiveFromMevEscrow","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"osTokenShares","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"receiver","type":"address"}],"name":"redeemOsToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"validatorsRegistryRoot","type":"bytes32"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"validators","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"internalType":"string","name":"exitSignaturesIpfsHash","type":"string"}],"internalType":"struct IKeeperValidators.ApprovalParams","name":"keeperParams","type":"tuple"},{"internalType":"bytes","name":"validatorsManagerSignature","type":"bytes"}],"name":"registerValidators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeRecipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"metadataIpfsHash","type":"string"}],"name":"setMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"validatorsManager_","type":"address"}],"name":"setValidatorsManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_whitelister","type":"address"}],"name":"setWhitelister","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalExitingAssets","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"osTokenShares","type":"uint256"}],"name":"transferOsTokenPositionToEscrow","outputs":[{"internalType":"uint256","name":"positionTicket","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"rewardsRoot","type":"bytes32"},{"internalType":"int160","name":"reward","type":"int160"},{"internalType":"uint160","name":"unlockedMevReward","type":"uint160"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct IKeeperRewards.HarvestParams","name":"harvestParams","type":"tuple"}],"name":"updateState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"referrer","type":"address"},{"components":[{"internalType":"bytes32","name":"rewardsRoot","type":"bytes32"},{"internalType":"int160","name":"reward","type":"int160"},{"internalType":"uint160","name":"unlockedMevReward","type":"uint160"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct IKeeperRewards.HarvestParams","name":"harvestParams","type":"tuple"}],"name":"updateStateAndDeposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"osTokenShares","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"components":[{"internalType":"bytes32","name":"rewardsRoot","type":"bytes32"},{"internalType":"int160","name":"reward","type":"int160"},{"internalType":"uint160","name":"unlockedMevReward","type":"uint160"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct IKeeperRewards.HarvestParams","name":"harvestParams","type":"tuple"}],"name":"updateStateAndDepositAndMintOsToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"updateWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"validatorsManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedAccounts","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelister","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawableAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6101e034620002695762004fff6001600160401b03601f38839003908101601f1916840190828211858310176200026d5780859460409384528539610120938491810103126200026957620000548462000281565b91620000636020860162000281565b916200007181870162000281565b93620000806060880162000281565b916200008f6080890162000281565b936200009e60a08a0162000281565b95620000ad60c08b0162000281565b97620000bc60e08c0162000281565b91610100809c01519560805260a05260c0523060e052895246885261014092835260018060a01b03806101609516855280610180961686526101a0961686526101c09687527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff82851c166200025857808083160362000213575b5050505195614d6897886200029789396080518881816114720152818161160a015281816129e4015281816136a40152614c15015260a0518861113f015260c0518881816139940152613ab5015260e0518881816112be0152613467015251876126730152518661371a015251856117b001525184818161042701528181610739015281816126bd01528181612f62015281816141ea015281816143570152614a6b0152518381816107f801528181610dbc0152818161302101526149ef01525182611a7a01525181818161209a0152612a2c0152f35b6001600160401b0319909116811790915581519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80806200013c565b835163f92ee8a960e01b8152600490fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620002695756fe60806040526004361015610022575b3615610018575f80fd5b610020612695565b005b5f3560e01c806301d523b61461031157806301e1d1141461030c578063066055e01461030757806307a2d13a146103025780630d392cd9146102fd57806318f72950146102f85780631a7ff553146102f3578063201b9eb5146102ee57806322758a4a146102e95780632999ad3f146102e45780632cdf7401146102df5780633229fa95146102da57806333194c0a146102d557806336fe59d2146102d05780633a98ef39146102cb578063439fab91146102c657806343e82a79146102c157806346904840146102bc5780634ec96b22146102b75780634f1ef286146102b257806352d1902d146102ad57806353156f28146102a857806354fd4d50146102a35780635c60da1b1461029e5780635cfc1a511461029957806360d60e6e1461029457806372b410a81461028f578063754c38881461028a57806376b58b90146102855780637fd6f15c1461028057806383d430d51461027b5780638697d2c2146102765780638ceab9aa146102715780639267842a1461026c578063a49a1e7d14610267578063ac9650d814610262578063ad3cb1cc1461025d578063b1f0e7c714610258578063c6e6f59214610253578063d83ad00c1461024e578063e74b981b14610249578063ee3bd5df14610244578063f04da65b1461023f578063f6a6830f1461023a578063f851a44014610235578063f9609f08146102305763f98f5b920361000e57611e86565b611e5c565b611e35565b611df4565b611db9565b611d93565b611d66565b611d40565b611d22565b611d08565b611cc3565b611c4d565b611b52565b61195e565b61192a565b611784565b6115b4565b611590565b61153f565b6114d4565b611447565b6113a2565b611388565b611354565b611339565b611315565b6112ac565b611027565b610ec2565b610e9a565b610d8d565b610cc7565b610c46565b610c32565b610bf8565b610bcc565b610bb2565b61070f565b6106bd565b61069a565b61063b565b6105d5565b6105a3565b61057b565b6103d7565b6103a6565b610339565b6001600160a01b0381160361032757565b5f80fd5b908160809103126103275790565b60803660031901126103275760043561035181610316565b60443561035d81610316565b606435906001600160401b0382116103275760209261038b61038661039494369060040161032b565b612027565b602435906120bc565b604051908152f35b5f91031261032757565b34610327575f36600319011261032757602060985460801c604051908152f35b6001600160801b0381160361032757565b34610327576020366003190112610327576004356103f4816103c6565b604051633b9e9f0160e21b81523360048201526001600160801b03821660248201526020816044815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1908115610576575f91610547575b50335f9081526101376020526040902061047090611ecd565b916001600160801b0361048a84516001600160801b031690565b1615610535576104e3836104a0610531956126a8565b6104ca6104bd846104b884516001600160801b031690565b611f1e565b6001600160801b03168252565b335f90815261013760205260409020611f37565b611f37565b604080518381526001600160801b0392909216602083015233917f3f7354ba02880b4fa37a629985852a38417ff369369ce1e52fa6f8342a9100a79190a26040519081529081906020820190565b0390f35b60405163673f032f60e11b8152600490fd5b610569915060203d60201161056f575b6105618183610fb5565b810190611eb3565b5f610457565b503d610557565b611ec2565b34610327576020366003190112610327576020610394600435611f69565b8015150361032757565b34610327576040366003190112610327576100206004356105c381610316565b602435906105d082610599565b611f8f565b6060366003190112610327576004356105ed81610316565b6024356105f981610316565b604435906001600160401b0382116103275760209261062261038661039494369060040161032b565b61062b33612ece565b61063481612ece565b3490613e87565b34610327576020366003190112610327576004356001600160401b0381116103275761038661002091369060040161032b565b60609060031901126103275760043561068681610316565b906024359060443561069781610316565b90565b346103275760206103946106ad3661066e565b916106b733612ece565b33614126565b34610327575f36600319011261032757610201546040516001600160a01b039091168152602090f35b6060906003190112610327576004359060243561070281610316565b9060443561069781610316565b346103275761071d366106e6565b906001600160a01b0380831615610ba057610736613689565b807f00000000000000000000000000000000000000000000000000000000000000001691823b156103275760408051631d8557d760e01b815260049491905f81878183875af1801561057657610b87575b506001600160a01b0383165f908152610137602052604090206107a990611ecd565b6001600160801b0392836107c483516001600160801b031690565b1615610b77576107d3826126a8565b825163e48a5f7b60e01b8152308882019081529097906060908990819060200103818a7f0000000000000000000000000000000000000000000000000000000000000000165afa978815610576575f98610b46575b50602097888101956001600160401b0391828061084c8a516001600160401b031690565b1614610b3657908c92918751918c83806108786303d1689d60e11b988983528a83019190602083019252565b03818a5afa918215610576576108a4938e5f94610b11575b5050516001600160801b03165b16906127d7565b966108c86108c28a60018060a01b03165f52609c60205260405f2090565b54611f69565b928389118015610b01575b610af157908b61091193926108ef89516001600160801b031690565b908a51958692839283528983019190916001600160801b036020820193169052565b0381895afa91821561057657670de0b6b3a764000094610960948e5f95610ac4575b50506109526109446109589261244a565b93516001600160401b031690565b9361244a565b92169061284b565b1015610ab6578351633b9e9f0160e21b815233918101918252602082018b905294939291889186919082905f90829060400103925af1908115610576577f61fd285f9e34a3dbfa9846bdcf22a023e37a3c93549902843b30dd74a18c535097610a9395610a0a93610a98575b50506109ed6104bd6109dd8c613bba565b83516001600160801b0316611f1e565b6001600160a01b0386165f90815261013760205260409020611f37565b610a1382613c87565b90610a52610a37610a2385613bba565b60985460801c5b036001600160801b031690565b6001600160801b036098549181199060801b16911617609855565b610a5c82866143d1565b610a668389613bed565b51948594169733978590949392606092608083019660018060a01b03168352602083015260408201520152565b0390a3005b81610aae92903d1061056f576105618183610fb5565b505f806109cc565b835163185cfc6d60e11b8152fd5b610958929550610ae7610952928261094493903d1061056f576105618183610fb5565b959250508e610933565b875163efda1a2760e01b81528590fd5b50610b0a61204b565b89116108d3565b61089d9294509081610b2e92903d1061056f576105618183610fb5565b92908e610890565b8651630709133160e01b81528490fd5b610b6991985060603d606011610b70575b610b618183610fb5565b810190612f04565b965f610828565b503d610b57565b825163673f032f60e11b81528790fd5b80610b94610b9a92610f6c565b8061039c565b5f610787565b60405163d92e233d60e01b8152600490fd5b34610327575f36600319011261032757602061039461204b565b34610327575f366003190112610327576020610be6612081565b6040516001600160a01b039091168152f35b34610327575f3660031901126103275760206040517fa90b5863127e5f962890f832f07b9f40c8df2fc043326a0e4b538552d600f2d98152f35b6020610394610c403661066e565b916120bc565b34610327575f3660031901126103275760206001600160801b0360985416604051908152f35b9181601f84011215610327578235916001600160401b038311610327576020838186019501011161032757565b602060031982011261032757600435906001600160401b03821161032757610cc391600401610c6c565b9091565b610cd036610c99565b907ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009182549160ff8360401c168015610d79575b610d675768010000000000000003610d2a9368ffffffffffffffffff191617845561217d565b68ff00000000000000001981541690557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160038152a1005b60405163f92ee8a960e01b8152600490fd5b5060036001600160401b0384161015610d04565b3461032757610d9b366106e6565b60405163057453a760e31b81529091906001600160a01b03906020816004817f000000000000000000000000000000000000000000000000000000000000000086165afa80156105765782915f91610e6b575b50163303610e595781610a93610e2686867f57f5eb636bf62215c111b54545422f11dfb0cb115f606be905f0be08e8859dd396612f44565b604080516001600160a01b0390981688526020880198909852968601526060850195909552169233929081906080820190565b604051634ca8886760e01b8152600490fd5b610e8d915060203d602011610e93575b610e858183610fb5565b8101906120e9565b5f610dee565b503d610e7b565b34610327575f366003190112610327576065546040516001600160a01b039091168152602090f35b3461032757602036600319011261032757600435610edf81610316565b60018060a01b03165f52610137602052602060405f2060405190610f0282610f4c565b54906001600160801b03918281169081835260801c84830152610f2a575b5116604051908152f35b610f33816126a8565b610f20565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610f6757604052565b610f38565b6001600160401b038111610f6757604052565b606081019081106001600160401b03821117610f6757604052565b608081019081106001600160401b03821117610f6757604052565b90601f801991011681019081106001600160401b03821117610f6757604052565b6001600160401b038111610f6757601f01601f191660200190565b929192610ffd82610fd6565b9161100b6040519384610fb5565b829481845281830111610327578281602093845f960137010152565b60408060031936011261032757600490813561104281610316565b6024356001600160401b0381116103275736602382011215610327576110719036906024818701359101610ff1565b9161107a61345d565b8051926110b1846110a360209363439fab9160e01b858401528460248401526044830190611bc5565b03601f198101865285610fb5565b6110b961345d565b6110c16134b6565b6001600160a01b03838116801592919087908415611277575b8415611209575b84156111a6575b50508215611110575b5050611101576100208383614582565b516355299b4960e01b81528390fd5b83516345da87c560e01b81526001600160a01b03861688820190815292935091839183918290819060200103917f0000000000000000000000000000000000000000000000000000000000000000165afa918215610576575f92611179575b5050155f806110f1565b6111989250803d1061119f575b6111908183610fb5565b81019061225a565b5f8061116f565b503d611186565b855163054fd4d560e41b81529294508391839182905afa90811561057657879160ff915f916111dc575b5016141591865f6110e8565b6111fc9150843d8611611202575b6111f48183610fb5565b810190614569565b5f6111d0565b503d6111ea565b935050835163198ca60560e11b815282818981875afa9081156105765788917fa90b5863127e5f962890f832f07b9f40c8df2fc043326a0e4b538552d600f2d9915f9161125a575b501415936110e1565b6112719150853d871161056f576105618183610fb5565b5f611251565b5f80516020614d138339815191525490945084906112a5906001600160a01b03165b6001600160a01b031690565b14936110da565b34610327575f366003190112610327577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630036113035760206040515f80516020614d138339815191528152f35b60405163703e46dd60e11b8152600490fd5b5f366003190112610327576001600160a01b03611330612081565b163303610e5957005b34610327575f36600319011261032757602060405160038152f35b34610327575f366003190112610327575f80516020614d13833981519152546040516001600160a01b039091168152602090f35b34610327575f36600319011261032757602061039461224c565b3461032757602036600319011261032757609a80549081905f6004355b8482106113f0575050508110156113e557610531905b6040519081529081906020820190565b506105315f196113d5565b909193808316906001818518811c8301809311611442575f8790525f80516020614cd38339815191528301546001600160a01b0316841015611437575050935b91906113bf565b909591019250611430565b611ef2565b34610327575f36600319011261032757604051633eb1acf760e11b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015610576576020915f916114b7575b506040519015158152f35b6114ce9150823d841161119f576111908183610fb5565b5f6114ac565b34610327576020366003190112610327576004356114f181610316565b6114f96134b6565b60d280546001600160a01b0319166001600160a01b03929092169182179055337f6bdc78d8c88160b3fc3638e67f2afe523b3f4c7d00c56ebb6216790e4c3eb2cb5f80a3005b346103275760803660031901126103275761053161157360043561156281610316565b60643590604435906024359061227c565b604080519384526020840192909252908201529081906060820190565b34610327575f36600319011261032757602061ffff60655460a01c16604051908152f35b34610327576003196040368201126103275760049081356001600160401b038082116103275760a082850193833603011261032757602435908111610327576116009036908501610c6c565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116803b156103275760405163837d444160e01b8152905f9082908183816116538c828f0161235e565b03925af1801561057657611771575b5061166b613689565b61167361265f565b9081163314159182611742575b50509050611731576044019160b061169884846123dc565b90500480158015611719575b611709576116b96116b361204b565b91612431565b116116fa575060b06116cb83836123dc565b9050145f146116e757610020916116e1916123dc565b90613aa7565b610020916116f4916123dc565b9061397f565b6040516396d8043360e01b8152fd5b50604051631c6c4cf360e31b8152fd5b5061172484846123dc565b905060b0820214156116a4565b604051634ca8886760e01b81528390fd5b611765925061175f6117699461175788613714565b923691610ff1565b916137ee565b1590565b805f80611680565b80610b9461177e92610f6c565b5f611662565b34610327576060366003190112610327576004356024356117a960443582843361227c565b91926117d57f000000000000000000000000000000000000000000000000000000000000000082612467565b42108015611922575b801561191a575b611908577feb3b05c070c24f667611fdb3ff75fe007d42401c573aed8d8faca95fd00ccb569361184061182561181a86613bba565b60995460801c611f1e565b6001600160801b036099549181199060801b16911617609955565b60408051336020820190815291810184905260608082018990528152601f1993915f916118849190611873608082610fb5565b5190205f52609b60205260405f2090565b555f93600183116118bd575b5050505061189e8233613bed565b604080519485526020850191909152830152339180606081015b0390a2005b6118fe929394506118ce9088612467565b60408051336020820190815291810193909352606083018290526080958601835290949091906118739082610fb5565b555f808080611890565b604051630e3d8e8d60e11b8152600490fd5b5082156117e5565b5081156117de565b3461032757604036600319011261032757602061195560243561194c81610316565b600435336147dd565b6103943361475d565b346103275760203660031901126103275760043561197a613689565b335f9081526101376020526040902061199290611ecd565b6001600160801b03806119ac83516001600160801b031690565b16156105355782906119bd836126a8565b82516001600160801b03161610611b4057335f908152609c6020526040902054826119fe6119f284516001600160801b031690565b6001600160801b031690565b14611b2a57611a229083611a1c6119f285516001600160801b031690565b9161284b565b91611a4f611a42611a3283613bba565b84516001600160801b0316610a2a565b6001600160801b03168352565b335f90815261013760205260409020611a69908390611f37565b611ab16020611aa260018060a01b037f0000000000000000000000000000000000000000000000000000000000000000168096336147dd565b9301516001600160801b031690565b833b156103275760405163074ee96960e31b81523360048201526024810184905260448101929092526001600160801b03166064820152915f908390608490829084905af19182156105765761053192611b17575b506040519081529081906020820190565b80610b94611b2492610f6c565b5f611b06565b335f908152610137602052604081205591611a69565b604051636edcc52360e01b8152600490fd5b34610327576118b87f2013570c343af8ab14a9778150e381a0fda34ed6368127a95fd5e7210cbec5bf611b8436610c99565b9290611b8e6134b6565b604051918291602083523395602084019161233e565b5f5b838110611bb55750505f910152565b8181015183820152602001611ba6565b90602091611bde81518092818552858086019101611ba4565b601f01601f1916010190565b6020808201906020835283518092526040830192602060408460051b8301019501935f915b848310611c1f5750505050505090565b9091929394958480611c3d600193603f198682030187528a51611bc5565b9801930193019194939290611c0f565b34610327576020366003190112610327576001600160401b036004358181116103275736602382011215610327578060040135918211610327573660248360051b8301011161032757610531916024611ca692016125b2565b60405191829182611bea565b906020610697928181520190611bc5565b34610327575f36600319011261032757610531604051611ce281610f4c565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611bc5565b34610327575f366003190112610327576020610be661265f565b34610327576020366003190112610327576020610394600435613c87565b34610327575f3660031901126103275760206001600160801b0360995416604051908152f35b3461032757602036600319011261032757610020600435611d8681610316565b611d8e6134b6565b613d16565b34610327575f3660031901126103275760206001600160801b03609e5416604051908152f35b3461032757602036600319011261032757600435611dd681610316565b60018060a01b03165f52609c602052602060405f2054604051908152f35b3461032757602036600319011261032757600435611e1181610316565b60018060a01b03165f52610202602052602060ff60405f2054166040519015158152f35b34610327575f366003190112610327575f546040516001600160a01b039091168152602090f35b6040366003190112610327576020610394600435611e7981610316565b6024359061062282610316565b3461032757602036600319011261032757610020600435611ea681610316565b611eae6134b6565b613d78565b90816020910312610327575190565b6040513d5f823e3d90fd5b90604051611eda81610f4c565b91546001600160801b038116835260801c6020830152565b634e487b7160e01b5f52601160045260245ffd5b6001600160801b039081165f19019190821161144257565b6001600160801b03918216908216039190821161144257565b815160209092015160801b6fffffffffffffffffffffffffffffffff19166001600160801b0392909216919091179055565b6098546001600160801b0381169081611f8157505090565b916106979260801c9061284b565b610201546001600160a01b03919082163303610e59576001600160a01b0381165f90815261020260205260409020549215159260ff1615158314612022576001600160a01b0381165f9081526102026020526040902060ff1981541660ff851617905560405192835216907fd9c6c3eabe38e3b9a606a66358d8f225489216a59eeba66facefb7d91663526660203392a3565b505050565b61203361203a916129b3565b9190612b49565b61204057565b612048612cfa565b50565b476099546001600160801b03612062818316611f69565b90609e5416019060801c01908181115f1461207b570390565b50505f90565b61016a546001600160a01b031680156120975790565b507f000000000000000000000000000000000000000000000000000000000000000090565b9061069792916120cb33612ece565b6120d433612ece565b6120df833433613e87565b506106b733612ece565b90816020910312610327575161069781610316565b906020828203126103275781356001600160401b0392838211610327570191606083830312610327576040519261213484610f7f565b80358452602081013561ffff81168103610327576020850152604081013591821161032757019080601f830112156103275781602061217593359101610ff1565b604082015290565b5f549091906001600160a01b03166122425760405163e7f6f22560e01b8152906020908183600481335afa928315610576575f93612223575b50604051636f4fa30f60e01b8152938285600481335afa908115610576576121fe956121f9945f93612200575b50506121f291928101906120fe565b9083613369565b613451565b565b6121f29350908161221c92903d10610e9357610e858183610fb5565b915f6121e3565b61223b919350823d8411610e9357610e858183610fb5565b915f6121b6565b50506121fe61325d565b609d548061069757505f1990565b90816020910312610327575161069781610599565b9190820391821161144257565b604080516001600160a01b039092166020830190815290820193909352606081018290529091906122ba81608081015b03601f198101835282610fb5565b5190205f52609b60205260405f20549182156122ea576122db9183916134ee565b90918281039081116114425792565b5050505f905f905f90565b9035601e19823603018112156103275701602081359101916001600160401b03821161032757813603831361032757565b90603060609281835260208301375f60508201520190565b908060209392818452848401375f828201840152601f01601f1916010190565b9060a06106979260208152823560208201526020830135604082015261239a61238a60408501856122f5565b84606085015260c084019161233e565b906123cd6123c26123ae60608701876122f5565b601f1985870381016080870152959161233e565b9460808101906122f5565b9390928286030191015261233e565b903590601e198136030182121561032757018035906001600160401b0382116103275760200191813603831361032757565b634e487b7160e01b5f52601260045260245ffd5b811561242c570490565b61240e565b906801bc16d674ec800000918083029283040361144257565b90670de0b6b3a76400009182810292818404149015171561144257565b9190820180921161144257565b6001600160401b038111610f675760051b60200190565b9061249582612474565b6124a26040519182610fb5565b82815280926124b3601f1991612474565b01905f5b8281106124c357505050565b8060606020809385010152016124b7565b634e487b7160e01b5f52603260045260245ffd5b908210156124ff57610cc39160051b8101906123dc565b6124d4565b908092918237015f815290565b3d1561253b573d9061252282610fd6565b916125306040519384610fb5565b82523d5f602084013e565b606090565b602081830312610327578051906001600160401b038211610327570181601f8201121561032757805161257281610fd6565b926125806040519485610fb5565b81845260208284010111610327576106979160208085019101611ba4565b80518210156124ff5760209160051b010190565b9190916125be8361248b565b925f5b8181106125cd57505050565b5f806125da8385876124e8565b604093916125ec855180938193612504565b0390305af4906125fa612511565b911561262157509060019161260f828861259e565b5261261a818761259e565b50016125c1565b9060448151106103275761265b61264660049283810151602480918301019101612540565b925162461bcd60e51b81529283928301611cb2565b0390fd5b60d2546001600160a01b03168061069757507f000000000000000000000000000000000000000000000000000000000000000090565b61269e33612ece565b6120483433613dbf565b60405163752a536d60e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610576575f9161273e575b5060208201916001600160801b03918284511691828214612737578361272a61272561273295858486511661284b565b613bba565b169052613bba565b169052565b5050505050565b612757915060203d60201161056f576105618183610fb5565b5f6126f5565b90808202905f19818409908280831092039180830392146127cc5761271090828211156127ba577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e91940990828211900360fc1b910360041c170290565b60405163227bc15360e01b8152600490fd5b505061271091500490565b90808202905f198184099082808310920391808303921461283a57670de0b6b3a764000090828211156127ba577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac10669940990828211900360ee1b910360121c170290565b5050670de0b6b3a764000091500490565b9091828202915f19848209938380861095039480860395146128be57848311156127ba57829109815f038216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b5050906106979250612422565b90816060910312610327578051916040602083015192015161069781610599565b81835290916001600160fb1b0383116103275760209260051b809284830137010190565b90602082528035602083015260208101358060130b809103610327576040830152604081013561293f81610316565b6001600160a01b031660608381019190915281013536829003601e19018112156103275701602081359101906001600160401b038111610327578060051b360382136103275760a08360808061069796015201916128ec565b9190915f838201938412911290801582169115161761144257565b6040516325f56f1160e01b81526001600160a01b039291606090829081906129de9060048301612910565b03815f877f0000000000000000000000000000000000000000000000000000000000000000165af1928315610576575f915f905f95612b04575b508415612ab15781612a28612081565b16917f0000000000000000000000000000000000000000000000000000000000000000168214612aaa57509060205f92600460405180958193634641257d60e01b83525af190811561057657612a85925f92612a89575b50612998565b9190565b612aa391925060203d60201161056f576105618183610fb5565b905f612a7f565b9081612ab7575b50509190565b803b1561032757604051636ee3193160e11b815260048101929092525f908290602490829084905af1801561057657612af1575b80612ab1565b80610b94612afe92610f6c565b5f612aeb565b91945050612b2a915060603d606011612b32575b612b228183610fb5565b8101906128cb565b93905f612a18565b503d612b18565b600160ff1b8114611442575f0390565b801561204857612b5e6119f260985460801c90565b5f8212612c305781612b6f91612467565b90612b7c610a3783613bba565b612b916065549161ffff8360a01c169061275d565b801561202257807f555ee6b2ef9506d870f386c067e47d3689435330b012ad263d8cc3531868654793612bcf6119f26098546001600160801b031690565b80612c1a575050612c1590925b6001600160a01b031691612bf08484613f33565b60405193849384604091949392606082019560018060a01b0316825260208201520152565b0390a1565b612c1592612c2a9203908461284b565b92612bdc565b90612c3a90612b39565b612c4f6119f2609e546001600160801b031690565b80612c6f575b5080612c5f575050565b612725610a37916121fe9361226f565b90612cd67f3623a54e8078be0d90ecfbef82da6a31ff3e6be8aa1718e7a7f3d0d33ff1d32a916113d5612cba612caf612ca88888612467565b878561284b565b808094039603613bba565b6001600160801b03166001600160801b0319609e541617609e55565b0390a15f612c55565b9190916001600160801b038080941691160191821161144257565b612d1347612d0d6119f260995460801c90565b9061226f565b908115612ec957609e546001600160801b0381169081612e51575b5050612d456119f26099546001600160801b031690565b9182158015612e49575b612e4357612d6690612d6084611f69565b90614114565b8015612e4357612d7581613c87565b928315612e3c57612e316121fe92612db2612d9661272588610a379661226f565b6001600160801b03166001600160801b03196099541617609955565b612dcc611825612dc183613bba565b60995460801c612cdf565b612dd68187613fdd565b60408051878152602081018390525f80516020614cf38339815191529190a1612725612e15612e0488613bba565b6098546001600160801b0316611f1e565b6001600160801b03166001600160801b03196098541617609855565b60985460801c611f1e565b505f925050565b505f9150565b508015612d4f565b8184929410612e3c57612dc184612e99935f80516020614cf3833981519152826118259560801c612e828282613fdd565b604080519182526020820192909252a10394613bba565b612ead6001600160801b03609e5416609e55565b612ec26001600160801b0319609e5416609e55565b5f80612d2e565b5f9150565b6001600160a01b03165f908152610202602052604090205460ff1615610e5957565b51906001600160401b038216820361032757565b90816060910312610327576121756040805192612f2084610f7f565b8051612f2b816103c6565b8452612f3960208201612ef0565b602085015201612ef0565b92906001600160a01b039081811615610ba057612f5f613689565b817f00000000000000000000000000000000000000000000000000000000000000001690813b1561032757604094855193631d8557d760e01b85526004945f81878183895af180156105765761324a575b506001600160a01b0388165f90815261013760205260409020612fd290611ecd565b906001600160801b03612fec83516001600160801b031690565b161561323a57612ffb826126a8565b875163e48a5f7b60e01b81523087820190815290916060918391908290819060200103917f0000000000000000000000000000000000000000000000000000000000000000165afa80156105765761321b575b508651936303d1689d60e11b97888652602091828780613075888c83019190602083019252565b0381845afa968715610576575f976131fc575b5086996130a86108c28d60018060a01b03165f52609c60205260405f2090565b881180156131ec575b6131dc5790836130ee926130cc87516001600160801b031690565b908551948592839283528d83019190916001600160801b036020820193169052565b0381845afa988915610576575f859488946131339c6131bf575b5051633b9e9f0160e21b815233928101928352602083019490945292998a9384929091839160400190565b03925af1958615610576576109dd61317a9461315d936104bd936121fe9a6131a1575b5050613bba565b6001600160a01b0388165f90815261013760205260409020611f37565b61319c61318683613c87565b8097613197610a37610a2387613bba565b6143d1565b613bed565b816131b792903d1061056f576105618183610fb5565b505f80613156565b6131d590873d891161056f576105618183610fb5565b505f613108565b825163efda1a2760e01b81528990fd5b506131f561204b565b88116130b1565b613214919750833d851161056f576105618183610fb5565b955f613088565b6132339060603d606011610b7057610b618183610fb5565b505f61304e565b875163673f032f60e11b81528690fd5b80610b9461325792610f6c565b5f612fb0565b61326561443f565b61327a6119f26099546001600160801b031690565b60018111613357576001146132ee575b609f54613295614969565b908082106132b1575b50506132a95f609f55565b6121fe61441e565b5f80516020614cf3833981519152916132e491036132ce816140db565b604080519182525f602083015290918291820190565b0390a15f8061329e565b61330b612e156133066098546001600160801b031690565b611f06565b6133206001600160801b031960995416609955565b613328614099565b5f80516020614cf38339815191526040518061334f81905f60206040840193600181520152565b0390a161328a565b604051630299325160e41b8152600490fd5b61337161443f565b604083015161337e61443f565b6001600160a01b0382168015610ba0576001600160601b0360a01b5f5416175f557f2013570c343af8ab14a9778150e381a0fda34ed6368127a95fd5e7210cbec5bf60405160208152806133d733946020830190611bc5565b0390a26020830151926133e861443f565b61271061ffff85161161343f576134379361340561342a93613d16565b6065805461ffff60a01b191660a09290921b61ffff60a01b1691909117905551614480565b6134326144b0565b6144d7565b6121fe614506565b604051638a81d3b360e01b8152600490fd5b6121fe90611eae61443f565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811630811491821561349b575b505061130357565b5f80516020614d138339815191525416141590505f80613493565b5f546001600160a01b03163303610e5957565b906040516134d681610f4c565b91546001600160a01b038116835260a01c6020830152565b609a545f948594939091808410801590613681575b613674578361363e575f5b609a5f526001600160a01b03166135335f80516020614cd383398151915286016134c9565b805190979061354a906001600160a01b0316611299565b9861356f6135636020809b01516001600160601b031690565b6001600160601b031690565b948381108015613634575b6136225791600193979a956135996135a5939488035b838c0390614114565b8092019887039161284b565b01970193808611801590613618575b61360d57609a5f5282906135d65f80516020614cd383398151915287016134c9565b805190890151969992966001600160a01b0390911694600193926135a59290916001600160601b0390911690613599908803613590565b945050509250509190565b50818510156135b4565b60405163e8722f8f60e01b8152600490fd5b50808b111561357a565b609a5f527f44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be38401546001600160a01b031661350e565b505093505050505f905f90565b508415613503565b604051633eb1acf760e11b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610576575f916136f5575b506136e357565b60405163e775715160e01b8152600490fd5b61370e915060203d60201161119f576111908183610fb5565b5f6136dc565b604290467f0000000000000000000000000000000000000000000000000000000000000000036137c25760d354905b61375a61375360408301836123dc565b3691610ff1565b602081519101206040519060208201927f838af86bfca91ada6557e38d913af1c2f24ef9b2567b3b77cc9e1144314b35b084523560408301526060820152606081526137a581610f9a565b5190206040519161190160f01b8352600283015260228201522090565b6137ca61462d565b90613743565b600411156137da57565b634e487b7160e01b5f52602160045260245ffd5b6137f883836146fa565b50613805819592956137d0565b1593846138a1575b50831561381b575b50505090565b5f929350908291604051613853816122ac6020820194630b135d3f60e11b998a87526024840152604060448401526064830190611bc5565b51915afa90613860612511565b82613893575b82613876575b50505f8080613815565b61388b91925060208082518301019101611eb3565b145f8061386c565b915060208251101591613866565b6001600160a01b0383811691161493505f61380d565b906030116103275790603090565b906090116103275760300190606090565b9060b0116103275760900190602090565b90939293848311610327578411610327578101920390565b35906020811061390d575090565b5f199060200360031b1b1690565b969594906139589361393c61394a926060979560808c5260808c019161233e565b9089820360208b0152611bc5565b91878303604089015261233e565b930152565b906020610697928181520190612326565b91602061069793818152019161233e565b60b091828104915f9081613991614734565b957f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316935f92905b8784106139d357505050505050505050565b826139e29101809288876138e7565b906139ed82826138b7565b91613a0c613a066139fe86846138c5565b9690936138d6565b906138ff565b90893b15610327575f908d613a39604094855198899485946304512a2360e31b86528a8a6004880161391b565b03816801bc16d674ec8000008d5af1908115610576577f64b6e61d93b7a91e8cc4376183ede0997a27b44fd9dd2f30a866b2a5730efdb194613a8592613a94575b50519283928361396e565b0390a1600181930192906139c1565b80610b94613aa192610f6c565b5f613a7a565b8160301161032757613a06917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316613ae5614734565b90613afc613af384866138c5565b969094866138d6565b94813b15610327576801bc16d674ec8000005f94613b6297604051988996879586946304512a2360e31b865260806004870152613b53613b408d6084890190612326565b60031994858983030160248a0152611bc5565b9286840301604487015261233e565b90606483015203925af1908115610576577f64b6e61d93b7a91e8cc4376183ede0997a27b44fd9dd2f30a866b2a5730efdb192612c1592613bab575b506040519182918261395d565b613bb490610f6c565b5f613b9e565b6001600160801b0390818111613bce571690565b604490604051906306dfcc6560e41b8252608060048301526024820152fd5b907f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00916002835414613c755760028355814710613c5d575f918291829182916001600160a01b03165af1613c3f612511565b5015613c4b5760019055565b604051630a12f52160e11b8152600490fd5b60405163cd78605960e01b8152306004820152602490fd5b604051633ee5aeb560e01b8152600490fd5b609854906001600160801b03821681158015613cb6575b15613ca95750905090565b6106979260801c9161284b565b508015613c9e565b6098546001600160801b0381169082158015613d0e575b15613cdf57505090565b60801c90613cee82828561284b565b92821561242c5709613cfd5790565b600181018091111561069757611ef2565b508115613cd5565b613d1e613689565b6001600160a01b03168015613d6657606580546001600160a01b03191682179055337faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d35f80a3565b604051630ed1b8b360e31b8152600490fd5b61020180546001600160a01b0319166001600160a01b03929092169182179055337fda2bcad4d57ac529886ff995d07bce191c08b5424c8f5824de6c73f90cc623d45f80a3565b9190613dc9613689565b6001600160a01b038316908115610ba0578015613e755780613df06119f260985460801c90565b0193613dfa61224c565b8511613e6357610a3794613e2191613e1c613e1485613cbe565b978893613bba565b613f33565b60408051918252602082018590525f9082015233907f861a4138e41fb21c121a7dbb1053df465c837fc77380cc7226189a662281be2c9080606081015b0390a3565b6040516304ffa0ff60e51b8152600490fd5b6040516318374fd160e21b8152600490fd5b90929192613e93613689565b6001600160a01b038216918215610ba0578115613e755781613eba6119f260985460801c90565b01613ec361224c565b8111613e6357610a3795613f0a613e5e927f861a4138e41fb21c121a7dbb1053df465c837fc77380cc7226189a662281be2c94613e1c613f0288613cbe565b9a8b93613bba565b60408051948552602085018890526001600160a01b039091169084015233929081906060820190565b613f3c82613bba565b60985490613f546001600160801b0391828416612cdf565b16906001600160801b0319161760985560018060a01b03165f52609c60205260405f20908154019055565b609a549068010000000000000000821015610f67576001820180609a558210156124ff57609a5f52805160209091015160a01b6001600160a01b0319166001600160a01b0391909116175f80516020614cd383398151915290910155565b919091801561408757613fee614969565b908101809111611442576001600160a01b03808211614067576001600160601b039081851161404757906121fe9394614042926040519461402e86610f4c565b168452166001600160601b03166020830152565b613f7f565b6040516306dfcc6560e41b81526060600482015260248101869052604490fd5b6040516306dfcc6560e41b815260a0600482015260248101839052604490fd5b604051632ec8835b60e21b8152600490fd5b6140a1614969565b60018101809111611442576001600160a01b0380821161406757906121fe91604051916140cd83610f4c565b1681525f6020820152613f7f565b8015614087576140e9614969565b908101809111611442576001600160a01b0380821161406757906121fe91604051916140cd83610f4c565b9080821015614121575090565b905090565b93929190916141336149ab565b61413b613689565b6001600160a01b0385165f9081526101376020526040902061415c90611ecd565b946001600160801b0361417687516001600160801b031690565b161561434257614185866126a8565b6001600160a01b0381165f908152609c602052604090206141ae906141a9906108c2565b6149cc565b955f198414614312575b6040516329460cc560e11b81526001600160a01b0386811660048301526024820186905290949093906020866044815f7f00000000000000000000000000000000000000000000000000000000000000008a165af1958615610576575f966142f1575b50859861424a61423d61422d84613bba565b86516001600160801b0316612cdf565b6001600160801b03168552565b61425e6119f285516001600160801b031690565b116142df577fa16d97739893e1436c9753925fb5cef174c4f368699dc86cc8fdb0e6e60f8e58956142a76142da946104de8760018060a01b03165f5261013760205260405f2090565b604080516001600160a01b03998a1681526020810192909252810191909152951660608601529116929081906080820190565b0390a2565b604051633684c65960e01b8152600490fd5b61430b91965060203d60201161056f576105618183610fb5565b945f61421b565b92506143286119f284516001600160801b031690565b80871115614338578603926141b8565b505f955050505050565b60405163752a536d60e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610576576143ad9161439d915f916143b2575b50613bba565b6001600160801b03166020880152565b614185565b6143cb915060203d60201161056f576105618183610fb5565b5f614397565b60018060a01b03165f52609c60205260405f20908154818103908111611442576143fb9255613bba565b609854906001600160801b03908183160316906001600160801b03191617609855565b61442661443f565b61442e61462d565b60d354810361443a5750565b60d355565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c161561446e57565b604051631afcd79f60e31b8152600490fd5b61448861443f565b801561449e57600181016144995750565b609d55565b6040516331278a8760e01b8152600490fd5b6144b861443f565b6801bc16d674ec8000006144ca61224c565b1061449e5761443a61462d565b6144df61443f565b6001600160a01b0316806144f05750565b61016a80546001600160a01b0319169091179055565b61450e61443f565b61451661443f565b61451e61443f565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055633b9aca003410614557576120483430613dbf565b60405163ea2559bb60e01b8152600490fd5b90816020910312610327575160ff811681036103275790565b6040516352d1902d60e01b81529290916020846004816001600160a01b0387165afa5f948161460c575b506145d257604051634c9c8ce360e01b81526001600160a01b0384166004820152602490fd5b90915f80516020614d1383398151915284036145f3576121fe929350614ad6565b604051632a87526960e21b815260048101859052602490fd5b61462691955060203d60201161056f576105618183610fb5565b935f6145ac565b6e5661756c7456616c696461746f727360881b602060405161464e81610f4c565b600f8152015260405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f35d6cf9768d8be929c3a11ed667b1560ae6f1920195a985758fdd7265505d1ca60408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c081018181106001600160401b03821117610f675760405251902090565b815191906041830361472a576147239250602082015190606060408401519301515f1a90614b78565b9192909190565b50505f9160029190565b604051600160f81b60208201525f60218201523060601b602c8201526020815261069781610f4c565b60018060a01b0381165f5261013760205260405f20906040519161478083610f4c565b54906001600160801b03918281169081855260801c602085015215612022576141a96108c26147d3926147b1613689565b6147ba866126a8565b6001600160a01b03165f908152609c6020526040902090565b915116116142df57565b9291908015611b40576001600160a01b03828116928315610ba057614803611765614bfa565b6148f05790611873956148d4612d96857f211091c5bf013c1230f996c3bb2bc327e3de429a3d3c356dcea9a0c858bc407f95816148a961484e6119f26099546001600160801b031690565b926122ac61487b85614876614861614969565b6148706119f2609e5460801c90565b90612467565b612467565b9e8f6040519283916020830195429087604091949392606082019560018060a01b0316825260208201520152565b556001600160a01b0385165f908152609c602052604090206148cc83825461226f565b905501613bba565b6040805188815260208101959095529116929081908101613e5e565b9293946148fc83611f69565b938415613e75577f5cdf07ad0fc222442720b108e3ed4c4640f0fadc2ab2253e66f259a0fea8348093614946866149619361493c610a37612e3184613bba565b61319c84876143d1565b60405193849316958360209093929193604081019481520152565b0390a35f1990565b609a548061497657505f90565b609a5f527f44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be301546001600160a01b0316611299565b6149b3614bfa565b156149ba57565b604051630a62fbdb60e11b8152600490fd5b60405163e48a5f7b60e01b81523060048201526001600160a01b036060826024817f000000000000000000000000000000000000000000000000000000000000000085165afa801561057657614a67936001600160401b0361089d6040614a46946020975f91614ab7575b5001516001600160401b031690565b9060405180809581946363737ac960e11b8352600483019190602083019252565b03917f0000000000000000000000000000000000000000000000000000000000000000165afa908115610576575f91614a9e575090565b610697915060203d60201161056f576105618183610fb5565b614ad0915060603d606011610b7057610b618183610fb5565b5f614a37565b90813b15614b57575f80516020614d1383398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2805115614b3c5761204891614c69565b505034614b4557565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411614bef579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15610576575f516001600160a01b03811615614be557905f905f90565b505f906001905f90565b5050505f9160039190565b604051630156a69560e11b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610576575f91614c50575090565b610697915060203d60201161119f576111908183610fb5565b5f8061069793602081519101845af4614c80612511565b9190614c965750805115613c4b57805190602001fd5b81511580614cc9575b614ca7575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15614c9f56fe44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be4624ea167e477f9d39f7f4094b9dfe2e6346eb4a7aada54338db51abd554c4b9f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca2646970667358221220cb77d17a21bab254b77e3cfcc5749b1f46242117392555f1dccf6dc7fdc6d82f64736f6c634300081600330000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b50000000000000000000000003a0008a588772446f6e656133c2d5029cc4fc20e00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f5000306000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb5900000000000000000000000009e84205df7c68907e619d07afd90143c576360500000000000000000000000048319f97e5da1233c21c48b80097c0fb7a20ff8600000000000000000000000075ab6ddce07556639333d3df1eaa684f5735223e0000000000000000000000000000000000000000000000000000000000015180
Deployed Bytecode
0x60806040526004361015610022575b3615610018575f80fd5b610020612695565b005b5f3560e01c806301d523b61461031157806301e1d1141461030c578063066055e01461030757806307a2d13a146103025780630d392cd9146102fd57806318f72950146102f85780631a7ff553146102f3578063201b9eb5146102ee57806322758a4a146102e95780632999ad3f146102e45780632cdf7401146102df5780633229fa95146102da57806333194c0a146102d557806336fe59d2146102d05780633a98ef39146102cb578063439fab91146102c657806343e82a79146102c157806346904840146102bc5780634ec96b22146102b75780634f1ef286146102b257806352d1902d146102ad57806353156f28146102a857806354fd4d50146102a35780635c60da1b1461029e5780635cfc1a511461029957806360d60e6e1461029457806372b410a81461028f578063754c38881461028a57806376b58b90146102855780637fd6f15c1461028057806383d430d51461027b5780638697d2c2146102765780638ceab9aa146102715780639267842a1461026c578063a49a1e7d14610267578063ac9650d814610262578063ad3cb1cc1461025d578063b1f0e7c714610258578063c6e6f59214610253578063d83ad00c1461024e578063e74b981b14610249578063ee3bd5df14610244578063f04da65b1461023f578063f6a6830f1461023a578063f851a44014610235578063f9609f08146102305763f98f5b920361000e57611e86565b611e5c565b611e35565b611df4565b611db9565b611d93565b611d66565b611d40565b611d22565b611d08565b611cc3565b611c4d565b611b52565b61195e565b61192a565b611784565b6115b4565b611590565b61153f565b6114d4565b611447565b6113a2565b611388565b611354565b611339565b611315565b6112ac565b611027565b610ec2565b610e9a565b610d8d565b610cc7565b610c46565b610c32565b610bf8565b610bcc565b610bb2565b61070f565b6106bd565b61069a565b61063b565b6105d5565b6105a3565b61057b565b6103d7565b6103a6565b610339565b6001600160a01b0381160361032757565b5f80fd5b908160809103126103275790565b60803660031901126103275760043561035181610316565b60443561035d81610316565b606435906001600160401b0382116103275760209261038b61038661039494369060040161032b565b612027565b602435906120bc565b604051908152f35b5f91031261032757565b34610327575f36600319011261032757602060985460801c604051908152f35b6001600160801b0381160361032757565b34610327576020366003190112610327576004356103f4816103c6565b604051633b9e9f0160e21b81523360048201526001600160801b03821660248201526020816044815f6001600160a01b037f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f5000306165af1908115610576575f91610547575b50335f9081526101376020526040902061047090611ecd565b916001600160801b0361048a84516001600160801b031690565b1615610535576104e3836104a0610531956126a8565b6104ca6104bd846104b884516001600160801b031690565b611f1e565b6001600160801b03168252565b335f90815261013760205260409020611f37565b611f37565b604080518381526001600160801b0392909216602083015233917f3f7354ba02880b4fa37a629985852a38417ff369369ce1e52fa6f8342a9100a79190a26040519081529081906020820190565b0390f35b60405163673f032f60e11b8152600490fd5b610569915060203d60201161056f575b6105618183610fb5565b810190611eb3565b5f610457565b503d610557565b611ec2565b34610327576020366003190112610327576020610394600435611f69565b8015150361032757565b34610327576040366003190112610327576100206004356105c381610316565b602435906105d082610599565b611f8f565b6060366003190112610327576004356105ed81610316565b6024356105f981610316565b604435906001600160401b0382116103275760209261062261038661039494369060040161032b565b61062b33612ece565b61063481612ece565b3490613e87565b34610327576020366003190112610327576004356001600160401b0381116103275761038661002091369060040161032b565b60609060031901126103275760043561068681610316565b906024359060443561069781610316565b90565b346103275760206103946106ad3661066e565b916106b733612ece565b33614126565b34610327575f36600319011261032757610201546040516001600160a01b039091168152602090f35b6060906003190112610327576004359060243561070281610316565b9060443561069781610316565b346103275761071d366106e6565b906001600160a01b0380831615610ba057610736613689565b807f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003061691823b156103275760408051631d8557d760e01b815260049491905f81878183875af1801561057657610b87575b506001600160a01b0383165f908152610137602052604090206107a990611ecd565b6001600160801b0392836107c483516001600160801b031690565b1615610b77576107d3826126a8565b825163e48a5f7b60e01b8152308882019081529097906060908990819060200103818a7f000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb59165afa978815610576575f98610b46575b50602097888101956001600160401b0391828061084c8a516001600160401b031690565b1614610b3657908c92918751918c83806108786303d1689d60e11b988983528a83019190602083019252565b03818a5afa918215610576576108a4938e5f94610b11575b5050516001600160801b03165b16906127d7565b966108c86108c28a60018060a01b03165f52609c60205260405f2090565b54611f69565b928389118015610b01575b610af157908b61091193926108ef89516001600160801b031690565b908a51958692839283528983019190916001600160801b036020820193169052565b0381895afa91821561057657670de0b6b3a764000094610960948e5f95610ac4575b50506109526109446109589261244a565b93516001600160401b031690565b9361244a565b92169061284b565b1015610ab6578351633b9e9f0160e21b815233918101918252602082018b905294939291889186919082905f90829060400103925af1908115610576577f61fd285f9e34a3dbfa9846bdcf22a023e37a3c93549902843b30dd74a18c535097610a9395610a0a93610a98575b50506109ed6104bd6109dd8c613bba565b83516001600160801b0316611f1e565b6001600160a01b0386165f90815261013760205260409020611f37565b610a1382613c87565b90610a52610a37610a2385613bba565b60985460801c5b036001600160801b031690565b6001600160801b036098549181199060801b16911617609855565b610a5c82866143d1565b610a668389613bed565b51948594169733978590949392606092608083019660018060a01b03168352602083015260408201520152565b0390a3005b81610aae92903d1061056f576105618183610fb5565b505f806109cc565b835163185cfc6d60e11b8152fd5b610958929550610ae7610952928261094493903d1061056f576105618183610fb5565b959250508e610933565b875163efda1a2760e01b81528590fd5b50610b0a61204b565b89116108d3565b61089d9294509081610b2e92903d1061056f576105618183610fb5565b92908e610890565b8651630709133160e01b81528490fd5b610b6991985060603d606011610b70575b610b618183610fb5565b810190612f04565b965f610828565b503d610b57565b825163673f032f60e11b81528790fd5b80610b94610b9a92610f6c565b8061039c565b5f610787565b60405163d92e233d60e01b8152600490fd5b34610327575f36600319011261032757602061039461204b565b34610327575f366003190112610327576020610be6612081565b6040516001600160a01b039091168152f35b34610327575f3660031901126103275760206040517fa90b5863127e5f962890f832f07b9f40c8df2fc043326a0e4b538552d600f2d98152f35b6020610394610c403661066e565b916120bc565b34610327575f3660031901126103275760206001600160801b0360985416604051908152f35b9181601f84011215610327578235916001600160401b038311610327576020838186019501011161032757565b602060031982011261032757600435906001600160401b03821161032757610cc391600401610c6c565b9091565b610cd036610c99565b907ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009182549160ff8360401c168015610d79575b610d675768010000000000000003610d2a9368ffffffffffffffffff191617845561217d565b68ff00000000000000001981541690557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160038152a1005b60405163f92ee8a960e01b8152600490fd5b5060036001600160401b0384161015610d04565b3461032757610d9b366106e6565b60405163057453a760e31b81529091906001600160a01b03906020816004817f000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb5986165afa80156105765782915f91610e6b575b50163303610e595781610a93610e2686867f57f5eb636bf62215c111b54545422f11dfb0cb115f606be905f0be08e8859dd396612f44565b604080516001600160a01b0390981688526020880198909852968601526060850195909552169233929081906080820190565b604051634ca8886760e01b8152600490fd5b610e8d915060203d602011610e93575b610e858183610fb5565b8101906120e9565b5f610dee565b503d610e7b565b34610327575f366003190112610327576065546040516001600160a01b039091168152602090f35b3461032757602036600319011261032757600435610edf81610316565b60018060a01b03165f52610137602052602060405f2060405190610f0282610f4c565b54906001600160801b03918281169081835260801c84830152610f2a575b5116604051908152f35b610f33816126a8565b610f20565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610f6757604052565b610f38565b6001600160401b038111610f6757604052565b606081019081106001600160401b03821117610f6757604052565b608081019081106001600160401b03821117610f6757604052565b90601f801991011681019081106001600160401b03821117610f6757604052565b6001600160401b038111610f6757601f01601f191660200190565b929192610ffd82610fd6565b9161100b6040519384610fb5565b829481845281830111610327578281602093845f960137010152565b60408060031936011261032757600490813561104281610316565b6024356001600160401b0381116103275736602382011215610327576110719036906024818701359101610ff1565b9161107a61345d565b8051926110b1846110a360209363439fab9160e01b858401528460248401526044830190611bc5565b03601f198101865285610fb5565b6110b961345d565b6110c16134b6565b6001600160a01b03838116801592919087908415611277575b8415611209575b84156111a6575b50508215611110575b5050611101576100208383614582565b516355299b4960e01b81528390fd5b83516345da87c560e01b81526001600160a01b03861688820190815292935091839183918290819060200103917f0000000000000000000000003a0008a588772446f6e656133c2d5029cc4fc20e165afa918215610576575f92611179575b5050155f806110f1565b6111989250803d1061119f575b6111908183610fb5565b81019061225a565b5f8061116f565b503d611186565b855163054fd4d560e41b81529294508391839182905afa90811561057657879160ff915f916111dc575b5016141591865f6110e8565b6111fc9150843d8611611202575b6111f48183610fb5565b810190614569565b5f6111d0565b503d6111ea565b935050835163198ca60560e11b815282818981875afa9081156105765788917fa90b5863127e5f962890f832f07b9f40c8df2fc043326a0e4b538552d600f2d9915f9161125a575b501415936110e1565b6112719150853d871161056f576105618183610fb5565b5f611251565b5f80516020614d138339815191525490945084906112a5906001600160a01b03165b6001600160a01b031690565b14936110da565b34610327575f366003190112610327577f000000000000000000000000fe076029b7d46fbe2ad4b9cbf377aa10b309e5606001600160a01b031630036113035760206040515f80516020614d138339815191528152f35b60405163703e46dd60e11b8152600490fd5b5f366003190112610327576001600160a01b03611330612081565b163303610e5957005b34610327575f36600319011261032757602060405160038152f35b34610327575f366003190112610327575f80516020614d13833981519152546040516001600160a01b039091168152602090f35b34610327575f36600319011261032757602061039461224c565b3461032757602036600319011261032757609a80549081905f6004355b8482106113f0575050508110156113e557610531905b6040519081529081906020820190565b506105315f196113d5565b909193808316906001818518811c8301809311611442575f8790525f80516020614cd38339815191528301546001600160a01b0316841015611437575050935b91906113bf565b909591019250611430565b611ef2565b34610327575f36600319011261032757604051633eb1acf760e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa8015610576576020915f916114b7575b506040519015158152f35b6114ce9150823d841161119f576111908183610fb5565b5f6114ac565b34610327576020366003190112610327576004356114f181610316565b6114f96134b6565b60d280546001600160a01b0319166001600160a01b03929092169182179055337f6bdc78d8c88160b3fc3638e67f2afe523b3f4c7d00c56ebb6216790e4c3eb2cb5f80a3005b346103275760803660031901126103275761053161157360043561156281610316565b60643590604435906024359061227c565b604080519384526020840192909252908201529081906060820190565b34610327575f36600319011261032757602061ffff60655460a01c16604051908152f35b34610327576003196040368201126103275760049081356001600160401b038082116103275760a082850193833603011261032757602435908111610327576116009036908501610c6c565b6001600160a01b037f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b58116803b156103275760405163837d444160e01b8152905f9082908183816116538c828f0161235e565b03925af1801561057657611771575b5061166b613689565b61167361265f565b9081163314159182611742575b50509050611731576044019160b061169884846123dc565b90500480158015611719575b611709576116b96116b361204b565b91612431565b116116fa575060b06116cb83836123dc565b9050145f146116e757610020916116e1916123dc565b90613aa7565b610020916116f4916123dc565b9061397f565b6040516396d8043360e01b8152fd5b50604051631c6c4cf360e31b8152fd5b5061172484846123dc565b905060b0820214156116a4565b604051634ca8886760e01b81528390fd5b611765925061175f6117699461175788613714565b923691610ff1565b916137ee565b1590565b805f80611680565b80610b9461177e92610f6c565b5f611662565b34610327576060366003190112610327576004356024356117a960443582843361227c565b91926117d57f000000000000000000000000000000000000000000000000000000000001518082612467565b42108015611922575b801561191a575b611908577feb3b05c070c24f667611fdb3ff75fe007d42401c573aed8d8faca95fd00ccb569361184061182561181a86613bba565b60995460801c611f1e565b6001600160801b036099549181199060801b16911617609955565b60408051336020820190815291810184905260608082018990528152601f1993915f916118849190611873608082610fb5565b5190205f52609b60205260405f2090565b555f93600183116118bd575b5050505061189e8233613bed565b604080519485526020850191909152830152339180606081015b0390a2005b6118fe929394506118ce9088612467565b60408051336020820190815291810193909352606083018290526080958601835290949091906118739082610fb5565b555f808080611890565b604051630e3d8e8d60e11b8152600490fd5b5082156117e5565b5081156117de565b3461032757604036600319011261032757602061195560243561194c81610316565b600435336147dd565b6103943361475d565b346103275760203660031901126103275760043561197a613689565b335f9081526101376020526040902061199290611ecd565b6001600160801b03806119ac83516001600160801b031690565b16156105355782906119bd836126a8565b82516001600160801b03161610611b4057335f908152609c6020526040902054826119fe6119f284516001600160801b031690565b6001600160801b031690565b14611b2a57611a229083611a1c6119f285516001600160801b031690565b9161284b565b91611a4f611a42611a3283613bba565b84516001600160801b0316610a2a565b6001600160801b03168352565b335f90815261013760205260409020611a69908390611f37565b611ab16020611aa260018060a01b037f00000000000000000000000009e84205df7c68907e619d07afd90143c5763605168096336147dd565b9301516001600160801b031690565b833b156103275760405163074ee96960e31b81523360048201526024810184905260448101929092526001600160801b03166064820152915f908390608490829084905af19182156105765761053192611b17575b506040519081529081906020820190565b80610b94611b2492610f6c565b5f611b06565b335f908152610137602052604081205591611a69565b604051636edcc52360e01b8152600490fd5b34610327576118b87f2013570c343af8ab14a9778150e381a0fda34ed6368127a95fd5e7210cbec5bf611b8436610c99565b9290611b8e6134b6565b604051918291602083523395602084019161233e565b5f5b838110611bb55750505f910152565b8181015183820152602001611ba6565b90602091611bde81518092818552858086019101611ba4565b601f01601f1916010190565b6020808201906020835283518092526040830192602060408460051b8301019501935f915b848310611c1f5750505050505090565b9091929394958480611c3d600193603f198682030187528a51611bc5565b9801930193019194939290611c0f565b34610327576020366003190112610327576001600160401b036004358181116103275736602382011215610327578060040135918211610327573660248360051b8301011161032757610531916024611ca692016125b2565b60405191829182611bea565b906020610697928181520190611bc5565b34610327575f36600319011261032757610531604051611ce281610f4c565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611bc5565b34610327575f366003190112610327576020610be661265f565b34610327576020366003190112610327576020610394600435613c87565b34610327575f3660031901126103275760206001600160801b0360995416604051908152f35b3461032757602036600319011261032757610020600435611d8681610316565b611d8e6134b6565b613d16565b34610327575f3660031901126103275760206001600160801b03609e5416604051908152f35b3461032757602036600319011261032757600435611dd681610316565b60018060a01b03165f52609c602052602060405f2054604051908152f35b3461032757602036600319011261032757600435611e1181610316565b60018060a01b03165f52610202602052602060ff60405f2054166040519015158152f35b34610327575f366003190112610327575f546040516001600160a01b039091168152602090f35b6040366003190112610327576020610394600435611e7981610316565b6024359061062282610316565b3461032757602036600319011261032757610020600435611ea681610316565b611eae6134b6565b613d78565b90816020910312610327575190565b6040513d5f823e3d90fd5b90604051611eda81610f4c565b91546001600160801b038116835260801c6020830152565b634e487b7160e01b5f52601160045260245ffd5b6001600160801b039081165f19019190821161144257565b6001600160801b03918216908216039190821161144257565b815160209092015160801b6fffffffffffffffffffffffffffffffff19166001600160801b0392909216919091179055565b6098546001600160801b0381169081611f8157505090565b916106979260801c9061284b565b610201546001600160a01b03919082163303610e59576001600160a01b0381165f90815261020260205260409020549215159260ff1615158314612022576001600160a01b0381165f9081526102026020526040902060ff1981541660ff851617905560405192835216907fd9c6c3eabe38e3b9a606a66358d8f225489216a59eeba66facefb7d91663526660203392a3565b505050565b61203361203a916129b3565b9190612b49565b61204057565b612048612cfa565b50565b476099546001600160801b03612062818316611f69565b90609e5416019060801c01908181115f1461207b570390565b50505f90565b61016a546001600160a01b031680156120975790565b507f00000000000000000000000048319f97e5da1233c21c48b80097c0fb7a20ff8690565b9061069792916120cb33612ece565b6120d433612ece565b6120df833433613e87565b506106b733612ece565b90816020910312610327575161069781610316565b906020828203126103275781356001600160401b0392838211610327570191606083830312610327576040519261213484610f7f565b80358452602081013561ffff81168103610327576020850152604081013591821161032757019080601f830112156103275781602061217593359101610ff1565b604082015290565b5f549091906001600160a01b03166122425760405163e7f6f22560e01b8152906020908183600481335afa928315610576575f93612223575b50604051636f4fa30f60e01b8152938285600481335afa908115610576576121fe956121f9945f93612200575b50506121f291928101906120fe565b9083613369565b613451565b565b6121f29350908161221c92903d10610e9357610e858183610fb5565b915f6121e3565b61223b919350823d8411610e9357610e858183610fb5565b915f6121b6565b50506121fe61325d565b609d548061069757505f1990565b90816020910312610327575161069781610599565b9190820391821161144257565b604080516001600160a01b039092166020830190815290820193909352606081018290529091906122ba81608081015b03601f198101835282610fb5565b5190205f52609b60205260405f20549182156122ea576122db9183916134ee565b90918281039081116114425792565b5050505f905f905f90565b9035601e19823603018112156103275701602081359101916001600160401b03821161032757813603831361032757565b90603060609281835260208301375f60508201520190565b908060209392818452848401375f828201840152601f01601f1916010190565b9060a06106979260208152823560208201526020830135604082015261239a61238a60408501856122f5565b84606085015260c084019161233e565b906123cd6123c26123ae60608701876122f5565b601f1985870381016080870152959161233e565b9460808101906122f5565b9390928286030191015261233e565b903590601e198136030182121561032757018035906001600160401b0382116103275760200191813603831361032757565b634e487b7160e01b5f52601260045260245ffd5b811561242c570490565b61240e565b906801bc16d674ec800000918083029283040361144257565b90670de0b6b3a76400009182810292818404149015171561144257565b9190820180921161144257565b6001600160401b038111610f675760051b60200190565b9061249582612474565b6124a26040519182610fb5565b82815280926124b3601f1991612474565b01905f5b8281106124c357505050565b8060606020809385010152016124b7565b634e487b7160e01b5f52603260045260245ffd5b908210156124ff57610cc39160051b8101906123dc565b6124d4565b908092918237015f815290565b3d1561253b573d9061252282610fd6565b916125306040519384610fb5565b82523d5f602084013e565b606090565b602081830312610327578051906001600160401b038211610327570181601f8201121561032757805161257281610fd6565b926125806040519485610fb5565b81845260208284010111610327576106979160208085019101611ba4565b80518210156124ff5760209160051b010190565b9190916125be8361248b565b925f5b8181106125cd57505050565b5f806125da8385876124e8565b604093916125ec855180938193612504565b0390305af4906125fa612511565b911561262157509060019161260f828861259e565b5261261a818761259e565b50016125c1565b9060448151106103275761265b61264660049283810151602480918301019101612540565b925162461bcd60e51b81529283928301611cb2565b0390fd5b60d2546001600160a01b03168061069757507f00000000000000000000000075ab6ddce07556639333d3df1eaa684f5735223e90565b61269e33612ece565b6120483433613dbf565b60405163752a536d60e01b81526020816004817f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003066001600160a01b03165afa908115610576575f9161273e575b5060208201916001600160801b03918284511691828214612737578361272a61272561273295858486511661284b565b613bba565b169052613bba565b169052565b5050505050565b612757915060203d60201161056f576105618183610fb5565b5f6126f5565b90808202905f19818409908280831092039180830392146127cc5761271090828211156127ba577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e91940990828211900360fc1b910360041c170290565b60405163227bc15360e01b8152600490fd5b505061271091500490565b90808202905f198184099082808310920391808303921461283a57670de0b6b3a764000090828211156127ba577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac10669940990828211900360ee1b910360121c170290565b5050670de0b6b3a764000091500490565b9091828202915f19848209938380861095039480860395146128be57848311156127ba57829109815f038216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b5050906106979250612422565b90816060910312610327578051916040602083015192015161069781610599565b81835290916001600160fb1b0383116103275760209260051b809284830137010190565b90602082528035602083015260208101358060130b809103610327576040830152604081013561293f81610316565b6001600160a01b031660608381019190915281013536829003601e19018112156103275701602081359101906001600160401b038111610327578060051b360382136103275760a08360808061069796015201916128ec565b9190915f838201938412911290801582169115161761144257565b6040516325f56f1160e01b81526001600160a01b039291606090829081906129de9060048301612910565b03815f877f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b5165af1928315610576575f915f905f95612b04575b508415612ab15781612a28612081565b16917f00000000000000000000000048319f97e5da1233c21c48b80097c0fb7a20ff86168214612aaa57509060205f92600460405180958193634641257d60e01b83525af190811561057657612a85925f92612a89575b50612998565b9190565b612aa391925060203d60201161056f576105618183610fb5565b905f612a7f565b9081612ab7575b50509190565b803b1561032757604051636ee3193160e11b815260048101929092525f908290602490829084905af1801561057657612af1575b80612ab1565b80610b94612afe92610f6c565b5f612aeb565b91945050612b2a915060603d606011612b32575b612b228183610fb5565b8101906128cb565b93905f612a18565b503d612b18565b600160ff1b8114611442575f0390565b801561204857612b5e6119f260985460801c90565b5f8212612c305781612b6f91612467565b90612b7c610a3783613bba565b612b916065549161ffff8360a01c169061275d565b801561202257807f555ee6b2ef9506d870f386c067e47d3689435330b012ad263d8cc3531868654793612bcf6119f26098546001600160801b031690565b80612c1a575050612c1590925b6001600160a01b031691612bf08484613f33565b60405193849384604091949392606082019560018060a01b0316825260208201520152565b0390a1565b612c1592612c2a9203908461284b565b92612bdc565b90612c3a90612b39565b612c4f6119f2609e546001600160801b031690565b80612c6f575b5080612c5f575050565b612725610a37916121fe9361226f565b90612cd67f3623a54e8078be0d90ecfbef82da6a31ff3e6be8aa1718e7a7f3d0d33ff1d32a916113d5612cba612caf612ca88888612467565b878561284b565b808094039603613bba565b6001600160801b03166001600160801b0319609e541617609e55565b0390a15f612c55565b9190916001600160801b038080941691160191821161144257565b612d1347612d0d6119f260995460801c90565b9061226f565b908115612ec957609e546001600160801b0381169081612e51575b5050612d456119f26099546001600160801b031690565b9182158015612e49575b612e4357612d6690612d6084611f69565b90614114565b8015612e4357612d7581613c87565b928315612e3c57612e316121fe92612db2612d9661272588610a379661226f565b6001600160801b03166001600160801b03196099541617609955565b612dcc611825612dc183613bba565b60995460801c612cdf565b612dd68187613fdd565b60408051878152602081018390525f80516020614cf38339815191529190a1612725612e15612e0488613bba565b6098546001600160801b0316611f1e565b6001600160801b03166001600160801b03196098541617609855565b60985460801c611f1e565b505f925050565b505f9150565b508015612d4f565b8184929410612e3c57612dc184612e99935f80516020614cf3833981519152826118259560801c612e828282613fdd565b604080519182526020820192909252a10394613bba565b612ead6001600160801b03609e5416609e55565b612ec26001600160801b0319609e5416609e55565b5f80612d2e565b5f9150565b6001600160a01b03165f908152610202602052604090205460ff1615610e5957565b51906001600160401b038216820361032757565b90816060910312610327576121756040805192612f2084610f7f565b8051612f2b816103c6565b8452612f3960208201612ef0565b602085015201612ef0565b92906001600160a01b039081811615610ba057612f5f613689565b817f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003061690813b1561032757604094855193631d8557d760e01b85526004945f81878183895af180156105765761324a575b506001600160a01b0388165f90815261013760205260409020612fd290611ecd565b906001600160801b03612fec83516001600160801b031690565b161561323a57612ffb826126a8565b875163e48a5f7b60e01b81523087820190815290916060918391908290819060200103917f000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb59165afa80156105765761321b575b508651936303d1689d60e11b97888652602091828780613075888c83019190602083019252565b0381845afa968715610576575f976131fc575b5086996130a86108c28d60018060a01b03165f52609c60205260405f2090565b881180156131ec575b6131dc5790836130ee926130cc87516001600160801b031690565b908551948592839283528d83019190916001600160801b036020820193169052565b0381845afa988915610576575f859488946131339c6131bf575b5051633b9e9f0160e21b815233928101928352602083019490945292998a9384929091839160400190565b03925af1958615610576576109dd61317a9461315d936104bd936121fe9a6131a1575b5050613bba565b6001600160a01b0388165f90815261013760205260409020611f37565b61319c61318683613c87565b8097613197610a37610a2387613bba565b6143d1565b613bed565b816131b792903d1061056f576105618183610fb5565b505f80613156565b6131d590873d891161056f576105618183610fb5565b505f613108565b825163efda1a2760e01b81528990fd5b506131f561204b565b88116130b1565b613214919750833d851161056f576105618183610fb5565b955f613088565b6132339060603d606011610b7057610b618183610fb5565b505f61304e565b875163673f032f60e11b81528690fd5b80610b9461325792610f6c565b5f612fb0565b61326561443f565b61327a6119f26099546001600160801b031690565b60018111613357576001146132ee575b609f54613295614969565b908082106132b1575b50506132a95f609f55565b6121fe61441e565b5f80516020614cf3833981519152916132e491036132ce816140db565b604080519182525f602083015290918291820190565b0390a15f8061329e565b61330b612e156133066098546001600160801b031690565b611f06565b6133206001600160801b031960995416609955565b613328614099565b5f80516020614cf38339815191526040518061334f81905f60206040840193600181520152565b0390a161328a565b604051630299325160e41b8152600490fd5b61337161443f565b604083015161337e61443f565b6001600160a01b0382168015610ba0576001600160601b0360a01b5f5416175f557f2013570c343af8ab14a9778150e381a0fda34ed6368127a95fd5e7210cbec5bf60405160208152806133d733946020830190611bc5565b0390a26020830151926133e861443f565b61271061ffff85161161343f576134379361340561342a93613d16565b6065805461ffff60a01b191660a09290921b61ffff60a01b1691909117905551614480565b6134326144b0565b6144d7565b6121fe614506565b604051638a81d3b360e01b8152600490fd5b6121fe90611eae61443f565b6001600160a01b037f000000000000000000000000fe076029b7d46fbe2ad4b9cbf377aa10b309e560811630811491821561349b575b505061130357565b5f80516020614d138339815191525416141590505f80613493565b5f546001600160a01b03163303610e5957565b906040516134d681610f4c565b91546001600160a01b038116835260a01c6020830152565b609a545f948594939091808410801590613681575b613674578361363e575f5b609a5f526001600160a01b03166135335f80516020614cd383398151915286016134c9565b805190979061354a906001600160a01b0316611299565b9861356f6135636020809b01516001600160601b031690565b6001600160601b031690565b948381108015613634575b6136225791600193979a956135996135a5939488035b838c0390614114565b8092019887039161284b565b01970193808611801590613618575b61360d57609a5f5282906135d65f80516020614cd383398151915287016134c9565b805190890151969992966001600160a01b0390911694600193926135a59290916001600160601b0390911690613599908803613590565b945050509250509190565b50818510156135b4565b60405163e8722f8f60e01b8152600490fd5b50808b111561357a565b609a5f527f44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be38401546001600160a01b031661350e565b505093505050505f905f90565b508415613503565b604051633eb1acf760e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa908115610576575f916136f5575b506136e357565b60405163e775715160e01b8152600490fd5b61370e915060203d60201161119f576111908183610fb5565b5f6136dc565b604290467f0000000000000000000000000000000000000000000000000000000000000001036137c25760d354905b61375a61375360408301836123dc565b3691610ff1565b602081519101206040519060208201927f838af86bfca91ada6557e38d913af1c2f24ef9b2567b3b77cc9e1144314b35b084523560408301526060820152606081526137a581610f9a565b5190206040519161190160f01b8352600283015260228201522090565b6137ca61462d565b90613743565b600411156137da57565b634e487b7160e01b5f52602160045260245ffd5b6137f883836146fa565b50613805819592956137d0565b1593846138a1575b50831561381b575b50505090565b5f929350908291604051613853816122ac6020820194630b135d3f60e11b998a87526024840152604060448401526064830190611bc5565b51915afa90613860612511565b82613893575b82613876575b50505f8080613815565b61388b91925060208082518301019101611eb3565b145f8061386c565b915060208251101591613866565b6001600160a01b0383811691161493505f61380d565b906030116103275790603090565b906090116103275760300190606090565b9060b0116103275760900190602090565b90939293848311610327578411610327578101920390565b35906020811061390d575090565b5f199060200360031b1b1690565b969594906139589361393c61394a926060979560808c5260808c019161233e565b9089820360208b0152611bc5565b91878303604089015261233e565b930152565b906020610697928181520190612326565b91602061069793818152019161233e565b60b091828104915f9081613991614734565b957f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa6001600160a01b0316935f92905b8784106139d357505050505050505050565b826139e29101809288876138e7565b906139ed82826138b7565b91613a0c613a066139fe86846138c5565b9690936138d6565b906138ff565b90893b15610327575f908d613a39604094855198899485946304512a2360e31b86528a8a6004880161391b565b03816801bc16d674ec8000008d5af1908115610576577f64b6e61d93b7a91e8cc4376183ede0997a27b44fd9dd2f30a866b2a5730efdb194613a8592613a94575b50519283928361396e565b0390a1600181930192906139c1565b80610b94613aa192610f6c565b5f613a7a565b8160301161032757613a06917f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa6001600160a01b0316613ae5614734565b90613afc613af384866138c5565b969094866138d6565b94813b15610327576801bc16d674ec8000005f94613b6297604051988996879586946304512a2360e31b865260806004870152613b53613b408d6084890190612326565b60031994858983030160248a0152611bc5565b9286840301604487015261233e565b90606483015203925af1908115610576577f64b6e61d93b7a91e8cc4376183ede0997a27b44fd9dd2f30a866b2a5730efdb192612c1592613bab575b506040519182918261395d565b613bb490610f6c565b5f613b9e565b6001600160801b0390818111613bce571690565b604490604051906306dfcc6560e41b8252608060048301526024820152fd5b907f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00916002835414613c755760028355814710613c5d575f918291829182916001600160a01b03165af1613c3f612511565b5015613c4b5760019055565b604051630a12f52160e11b8152600490fd5b60405163cd78605960e01b8152306004820152602490fd5b604051633ee5aeb560e01b8152600490fd5b609854906001600160801b03821681158015613cb6575b15613ca95750905090565b6106979260801c9161284b565b508015613c9e565b6098546001600160801b0381169082158015613d0e575b15613cdf57505090565b60801c90613cee82828561284b565b92821561242c5709613cfd5790565b600181018091111561069757611ef2565b508115613cd5565b613d1e613689565b6001600160a01b03168015613d6657606580546001600160a01b03191682179055337faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d35f80a3565b604051630ed1b8b360e31b8152600490fd5b61020180546001600160a01b0319166001600160a01b03929092169182179055337fda2bcad4d57ac529886ff995d07bce191c08b5424c8f5824de6c73f90cc623d45f80a3565b9190613dc9613689565b6001600160a01b038316908115610ba0578015613e755780613df06119f260985460801c90565b0193613dfa61224c565b8511613e6357610a3794613e2191613e1c613e1485613cbe565b978893613bba565b613f33565b60408051918252602082018590525f9082015233907f861a4138e41fb21c121a7dbb1053df465c837fc77380cc7226189a662281be2c9080606081015b0390a3565b6040516304ffa0ff60e51b8152600490fd5b6040516318374fd160e21b8152600490fd5b90929192613e93613689565b6001600160a01b038216918215610ba0578115613e755781613eba6119f260985460801c90565b01613ec361224c565b8111613e6357610a3795613f0a613e5e927f861a4138e41fb21c121a7dbb1053df465c837fc77380cc7226189a662281be2c94613e1c613f0288613cbe565b9a8b93613bba565b60408051948552602085018890526001600160a01b039091169084015233929081906060820190565b613f3c82613bba565b60985490613f546001600160801b0391828416612cdf565b16906001600160801b0319161760985560018060a01b03165f52609c60205260405f20908154019055565b609a549068010000000000000000821015610f67576001820180609a558210156124ff57609a5f52805160209091015160a01b6001600160a01b0319166001600160a01b0391909116175f80516020614cd383398151915290910155565b919091801561408757613fee614969565b908101809111611442576001600160a01b03808211614067576001600160601b039081851161404757906121fe9394614042926040519461402e86610f4c565b168452166001600160601b03166020830152565b613f7f565b6040516306dfcc6560e41b81526060600482015260248101869052604490fd5b6040516306dfcc6560e41b815260a0600482015260248101839052604490fd5b604051632ec8835b60e21b8152600490fd5b6140a1614969565b60018101809111611442576001600160a01b0380821161406757906121fe91604051916140cd83610f4c565b1681525f6020820152613f7f565b8015614087576140e9614969565b908101809111611442576001600160a01b0380821161406757906121fe91604051916140cd83610f4c565b9080821015614121575090565b905090565b93929190916141336149ab565b61413b613689565b6001600160a01b0385165f9081526101376020526040902061415c90611ecd565b946001600160801b0361417687516001600160801b031690565b161561434257614185866126a8565b6001600160a01b0381165f908152609c602052604090206141ae906141a9906108c2565b6149cc565b955f198414614312575b6040516329460cc560e11b81526001600160a01b0386811660048301526024820186905290949093906020866044815f7f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003068a165af1958615610576575f966142f1575b50859861424a61423d61422d84613bba565b86516001600160801b0316612cdf565b6001600160801b03168552565b61425e6119f285516001600160801b031690565b116142df577fa16d97739893e1436c9753925fb5cef174c4f368699dc86cc8fdb0e6e60f8e58956142a76142da946104de8760018060a01b03165f5261013760205260405f2090565b604080516001600160a01b03998a1681526020810192909252810191909152951660608601529116929081906080820190565b0390a2565b604051633684c65960e01b8152600490fd5b61430b91965060203d60201161056f576105618183610fb5565b945f61421b565b92506143286119f284516001600160801b031690565b80871115614338578603926141b8565b505f955050505050565b60405163752a536d60e01b81526020816004817f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003066001600160a01b03165afa908115610576576143ad9161439d915f916143b2575b50613bba565b6001600160801b03166020880152565b614185565b6143cb915060203d60201161056f576105618183610fb5565b5f614397565b60018060a01b03165f52609c60205260405f20908154818103908111611442576143fb9255613bba565b609854906001600160801b03908183160316906001600160801b03191617609855565b61442661443f565b61442e61462d565b60d354810361443a5750565b60d355565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c161561446e57565b604051631afcd79f60e31b8152600490fd5b61448861443f565b801561449e57600181016144995750565b609d55565b6040516331278a8760e01b8152600490fd5b6144b861443f565b6801bc16d674ec8000006144ca61224c565b1061449e5761443a61462d565b6144df61443f565b6001600160a01b0316806144f05750565b61016a80546001600160a01b0319169091179055565b61450e61443f565b61451661443f565b61451e61443f565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055633b9aca003410614557576120483430613dbf565b60405163ea2559bb60e01b8152600490fd5b90816020910312610327575160ff811681036103275790565b6040516352d1902d60e01b81529290916020846004816001600160a01b0387165afa5f948161460c575b506145d257604051634c9c8ce360e01b81526001600160a01b0384166004820152602490fd5b90915f80516020614d1383398151915284036145f3576121fe929350614ad6565b604051632a87526960e21b815260048101859052602490fd5b61462691955060203d60201161056f576105618183610fb5565b935f6145ac565b6e5661756c7456616c696461746f727360881b602060405161464e81610f4c565b600f8152015260405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f35d6cf9768d8be929c3a11ed667b1560ae6f1920195a985758fdd7265505d1ca60408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c081018181106001600160401b03821117610f675760405251902090565b815191906041830361472a576147239250602082015190606060408401519301515f1a90614b78565b9192909190565b50505f9160029190565b604051600160f81b60208201525f60218201523060601b602c8201526020815261069781610f4c565b60018060a01b0381165f5261013760205260405f20906040519161478083610f4c565b54906001600160801b03918281169081855260801c602085015215612022576141a96108c26147d3926147b1613689565b6147ba866126a8565b6001600160a01b03165f908152609c6020526040902090565b915116116142df57565b9291908015611b40576001600160a01b03828116928315610ba057614803611765614bfa565b6148f05790611873956148d4612d96857f211091c5bf013c1230f996c3bb2bc327e3de429a3d3c356dcea9a0c858bc407f95816148a961484e6119f26099546001600160801b031690565b926122ac61487b85614876614861614969565b6148706119f2609e5460801c90565b90612467565b612467565b9e8f6040519283916020830195429087604091949392606082019560018060a01b0316825260208201520152565b556001600160a01b0385165f908152609c602052604090206148cc83825461226f565b905501613bba565b6040805188815260208101959095529116929081908101613e5e565b9293946148fc83611f69565b938415613e75577f5cdf07ad0fc222442720b108e3ed4c4640f0fadc2ab2253e66f259a0fea8348093614946866149619361493c610a37612e3184613bba565b61319c84876143d1565b60405193849316958360209093929193604081019481520152565b0390a35f1990565b609a548061497657505f90565b609a5f527f44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be301546001600160a01b0316611299565b6149b3614bfa565b156149ba57565b604051630a62fbdb60e11b8152600490fd5b60405163e48a5f7b60e01b81523060048201526001600160a01b036060826024817f000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb5985165afa801561057657614a67936001600160401b0361089d6040614a46946020975f91614ab7575b5001516001600160401b031690565b9060405180809581946363737ac960e11b8352600483019190602083019252565b03917f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f5000306165afa908115610576575f91614a9e575090565b610697915060203d60201161056f576105618183610fb5565b614ad0915060603d606011610b7057610b618183610fb5565b5f614a37565b90813b15614b57575f80516020614d1383398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2805115614b3c5761204891614c69565b505034614b4557565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411614bef579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15610576575f516001600160a01b03811615614be557905f905f90565b505f906001905f90565b5050505f9160039190565b604051630156a69560e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa908115610576575f91614c50575090565b610697915060203d60201161119f576111908183610fb5565b5f8061069793602081519101845af4614c80612511565b9190614c965750805115613c4b57805190602001fd5b81511580614cc9575b614ca7575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15614c9f56fe44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be4624ea167e477f9d39f7f4094b9dfe2e6346eb4a7aada54338db51abd554c4b9f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca2646970667358221220cb77d17a21bab254b77e3cfcc5749b1f46242117392555f1dccf6dc7fdc6d82f64736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b50000000000000000000000003a0008a588772446f6e656133c2d5029cc4fc20e00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f5000306000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb5900000000000000000000000009e84205df7c68907e619d07afd90143c576360500000000000000000000000048319f97e5da1233c21c48b80097c0fb7a20ff8600000000000000000000000075ab6ddce07556639333d3df1eaa684f5735223e0000000000000000000000000000000000000000000000000000000000015180
-----Decoded View---------------
Arg [0] : _keeper (address): 0x6B5815467da09DaA7DC83Db21c9239d98Bb487b5
Arg [1] : _vaultsRegistry (address): 0x3a0008a588772446f6e656133C2D5029CC4FC20E
Arg [2] : _validatorsRegistry (address): 0x00000000219ab540356cBB839Cbe05303d7705Fa
Arg [3] : osTokenVaultController (address): 0x2A261e60FB14586B474C208b1B7AC6D0f5000306
Arg [4] : osTokenConfig (address): 0x287d1e2A8dE183A8bf8f2b09Fa1340fBd766eb59
Arg [5] : osTokenVaultEscrow (address): 0x09e84205DF7c68907e619D07aFD90143c5763605
Arg [6] : sharedMevEscrow (address): 0x48319f97E5Da1233c21c48b80097c0FB7a20Ff86
Arg [7] : depositDataRegistry (address): 0x75AB6DdCe07556639333d3Df1eaa684F5735223e
Arg [8] : exitingAssetsClaimDelay (uint256): 86400
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b5
Arg [1] : 0000000000000000000000003a0008a588772446f6e656133c2d5029cc4fc20e
Arg [2] : 00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa
Arg [3] : 0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f5000306
Arg [4] : 000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb59
Arg [5] : 00000000000000000000000009e84205df7c68907e619d07afd90143c5763605
Arg [6] : 00000000000000000000000048319f97e5da1233c21c48b80097c0fb7a20ff86
Arg [7] : 00000000000000000000000075ab6ddce07556639333d3df1eaa684f5735223e
Arg [8] : 0000000000000000000000000000000000000000000000000000000000015180
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 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.