Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x6101a060 | 20516574 | 106 days ago | IN | 0 ETH | 0.01131904 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
NativeStakingSSVStrategy
Compiler Version
v0.8.7+commit.e28d00a7
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import { InitializableAbstractStrategy } from "../../utils/InitializableAbstractStrategy.sol"; import { IWETH9 } from "../../interfaces/IWETH9.sol"; import { FeeAccumulator } from "./FeeAccumulator.sol"; import { ValidatorAccountant } from "./ValidatorAccountant.sol"; import { ISSVNetwork } from "../../interfaces/ISSVNetwork.sol"; struct ValidatorStakeData { bytes pubkey; bytes signature; bytes32 depositDataRoot; } /// @title Native Staking SSV Strategy /// @notice Strategy to deploy funds into DVT validators powered by the SSV Network /// @author Origin Protocol Inc /// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that /// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an /// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all /// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present /// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is /// required since the rewards (reward token) is also in ETH. /// /// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies. /// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days /// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant /// immediately wraps ETH to WETH and sends it to the Vault. /// /// On the other hand any ETH on the contract (across multiple blocks) is there either: /// - as a result of already accounted for consensus rewards /// - as a result of not yet accounted for consensus rewards /// - as a results of not yet accounted for full validator withdrawals (or validator slashes) /// /// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the /// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time /// interval and not immediately. contract NativeStakingSSVStrategy is ValidatorAccountant, InitializableAbstractStrategy { using SafeERC20 for IERC20; /// @notice SSV ERC20 token that serves as a payment for operating SSV validators address public immutable SSV_TOKEN; /// @notice Fee collector address /// @dev this address will receive maximal extractable value (MEV) rewards. These are /// rewards for arranging transactions in a way that benefits the validator. address payable public immutable FEE_ACCUMULATOR_ADDRESS; /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators. /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track /// of WETH that has already been accounted for. /// This value represents the amount of WETH balance of this contract that has already been accounted for by the /// deposit events. /// It is important to note that this variable is not concerned with WETH that is a result of full/partial /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to /// be staked. uint256 public depositedWethAccountedFor; // For future use uint256[49] private __gap; /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI, /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy /// @param _wethAddress Address of the Erc20 WETH Token contract /// @param _ssvToken Address of the Erc20 SSV Token contract /// @param _ssvNetwork Address of the SSV Network contract /// @param _maxValidators Maximum number of validators that can be registered in the strategy /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards /// @param _beaconChainDepositContract Address of the beacon chain deposit contract constructor( BaseStrategyConfig memory _baseConfig, address _wethAddress, address _ssvToken, address _ssvNetwork, uint256 _maxValidators, address _feeAccumulator, address _beaconChainDepositContract ) InitializableAbstractStrategy(_baseConfig) ValidatorAccountant( _wethAddress, _baseConfig.vaultAddress, _beaconChainDepositContract, _ssvNetwork, _maxValidators ) { SSV_TOKEN = _ssvToken; FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator); } /// @notice Set up initial internal state including /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract. /// @param _rewardTokenAddresses Address of reward token for platform /// @param _assets Addresses of initial supported assets /// @param _pTokens Platform Token corresponding addresses function initialize( address[] memory _rewardTokenAddresses, address[] memory _assets, address[] memory _pTokens ) external onlyGovernor initializer { InitializableAbstractStrategy._initialize( _rewardTokenAddresses, _assets, _pTokens ); // Approves the SSV Network contract to transfer SSV tokens for deposits IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max); // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress( FEE_ACCUMULATOR_ADDRESS ); } /// @notice Unlike other strategies, this does not deposit assets into the underlying platform. /// It just checks the asset is WETH and emits the Deposit event. /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used. /// Will NOT revert if the strategy is paused from an accounting failure. /// @param _asset Address of asset to deposit. Has to be WETH. /// @param _amount Amount of assets that were transferred to the strategy by the vault. function deposit(address _asset, uint256 _amount) external override onlyVault nonReentrant { require(_asset == WETH, "Unsupported asset"); depositedWethAccountedFor += _amount; _deposit(_asset, _amount); } /// @dev Deposit WETH to this strategy so it can later be staked into a validator. /// @param _asset Address of WETH /// @param _amount Amount of WETH to deposit function _deposit(address _asset, uint256 _amount) internal { require(_amount > 0, "Must deposit something"); /* * We could do a check here that would revert when "_amount % 32 ether != 0". With the idea of * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches * of 32ETH have been staked. * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out. * * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH. */ emit Deposit(_asset, address(0), _amount); } /// @notice Unlike other strategies, this does not deposit assets into the underlying platform. /// It just emits the Deposit event. /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used. /// Will NOT revert if the strategy is paused from an accounting failure. function depositAll() external override onlyVault nonReentrant { uint256 wethBalance = IERC20(WETH).balanceOf(address(this)); uint256 newWeth = wethBalance - depositedWethAccountedFor; if (newWeth > 0) { depositedWethAccountedFor = wethBalance; _deposit(WETH, newWeth); } } /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract. /// That can happen when: /// - after mints if the strategy is the default /// - time between depositToStrategy and stakeEth /// - the deposit was not a multiple of 32 WETH /// - someone sent WETH directly to this contract /// Will NOT revert if the strategy is paused from an accounting failure. /// @param _recipient Address to receive withdrawn assets /// @param _asset WETH to withdraw /// @param _amount Amount of WETH to withdraw function withdraw( address _recipient, address _asset, uint256 _amount ) external override onlyVault nonReentrant { require(_asset == WETH, "Unsupported asset"); _withdraw(_recipient, _asset, _amount); } function _withdraw( address _recipient, address _asset, uint256 _amount ) internal { require(_amount > 0, "Must withdraw something"); require(_recipient != address(0), "Must specify recipient"); _wethWithdrawn(_amount); IERC20(_asset).safeTransfer(_recipient, _amount); emit Withdrawal(_asset, address(0), _amount); } /// @notice transfer all WETH deposits back to the vault. /// This does not withdraw from the validators. That has to be done separately with the /// `exitSsvValidator` and `removeSsvValidator` operations. /// This does not withdraw any execution rewards from the FeeAccumulator or /// consensus rewards in this strategy. /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn. /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`. /// Will NOT revert if the strategy is paused from an accounting failure. function withdrawAll() external override onlyVaultOrGovernor nonReentrant { uint256 wethBalance = IERC20(WETH).balanceOf(address(this)); if (wethBalance > 0) { _withdraw(vaultAddress, WETH, wethBalance); } } /// @notice Returns the total value of (W)ETH that is staked to the validators /// and WETH deposits that are still to be staked. /// This does not include ETH from consensus rewards sitting in this strategy /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested /// and sent to the Dripper so will eventually be sent to the Vault as WETH. /// @param _asset Address of weth asset /// @return balance Total value of (W)ETH function checkBalance(address _asset) external view override returns (uint256 balance) { require(_asset == WETH, "Unsupported asset"); balance = // add the ETH that has been staked in validators activeDepositedValidators * FULL_STAKE + // add the WETH in the strategy from deposits that are still to be staked IERC20(WETH).balanceOf(address(this)); } function pause() external onlyStrategist { _pause(); } /// @notice Returns bool indicating whether asset is supported by strategy. /// @param _asset The address of the asset token. function supportsAsset(address _asset) public view override returns (bool) { return _asset == WETH; } /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits function safeApproveAllTokens() external override { // Approves the SSV Network contract to transfer SSV tokens for deposits IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max); } /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to function setFeeRecipient() external { ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress( FEE_ACCUMULATOR_ADDRESS ); } /** * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when * unwrapping WETH just before staking it to the validator. * The strategy will also receive ETH from the priority fees of transactions when producing blocks * as defined in EIP-1559. * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH. * The tx fees are paid with each block produced. They are not included in the consensus rewards * which are periodically swept from the validators to this strategy. * For accounting purposes, the priority fees of transactions will be considered consensus rewards * and will be included in the AccountingConsensusRewards event. * @dev don't want to receive donations from anyone else as donations over the fuse limits will * mess with the accounting of the consensus rewards and validator full withdrawals. */ receive() external payable { require( msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH, "Eth not from allowed contracts" ); } /*************************************** Internal functions ****************************************/ function _abstractSetPToken(address _asset, address) internal override {} /// @dev Convert accumulated ETH to WETH and send to the Harvester. /// Will revert if the strategy is paused for accounting. function _collectRewardTokens() internal override whenNotPaused { // collect ETH from execution rewards from the fee accumulator uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS) .collect(); // total ETH rewards to be harvested = execution rewards + consensus rewards uint256 ethRewards = executionRewards + consensusRewards; require( address(this).balance >= ethRewards, "Insufficient eth balance" ); if (ethRewards > 0) { // reset the counter keeping track of beacon chain consensus rewards consensusRewards = 0; // Convert ETH rewards to WETH IWETH9(WETH).deposit{ value: ethRewards }(); IERC20(WETH).safeTransfer(harvesterAddress, ethRewards); emit RewardTokenCollected(harvesterAddress, WETH, ethRewards); } } /// @dev emits Withdrawal event from NativeStakingSSVStrategy function _wethWithdrawnToVault(uint256 _amount) internal override { emit Withdrawal(WETH, address(0), _amount); } /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so /// the strategy knows how much WETH it has on deposit. /// This is so it can emit the correct amount in the Deposit event in depositAll(). function _wethWithdrawn(uint256 _amount) internal override { /* In an ideal world we wouldn't need to reduce the deduction amount when the * depositedWethAccountedFor is smaller than the _amount. * * The reason this is required is that a malicious actor could sent WETH directly * to this contract and that would circumvent the increase of depositedWethAccountedFor * property. When the ETH would be staked the depositedWethAccountedFor amount could * be deducted so much that it would be negative. */ uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor); depositedWethAccountedFor -= deductAmount; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Address.sol) pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // 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 assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @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 up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol) pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ 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 substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ 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. * * _Available since v3.4._ */ 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. * * _Available since v3.4._ */ 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. * * _Available since v3.4._ */ 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 addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Base for contracts that are managed by the Origin Protocol's Governor. * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change * from owner to governor and renounce methods removed. Does not use * Context.sol like Ownable.sol does for simplification. * @author Origin Protocol Inc */ contract Governable { // Storage position of the owner and pendingOwner of the contract // keccak256("OUSD.governor"); bytes32 private constant governorPosition = 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a; // keccak256("OUSD.pending.governor"); bytes32 private constant pendingGovernorPosition = 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db; // keccak256("OUSD.reentry.status"); bytes32 private constant reentryStatusPosition = 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535; // See OpenZeppelin ReentrancyGuard implementation uint256 constant _NOT_ENTERED = 1; uint256 constant _ENTERED = 2; event PendingGovernorshipTransfer( address indexed previousGovernor, address indexed newGovernor ); event GovernorshipTransferred( address indexed previousGovernor, address indexed newGovernor ); /** * @dev Initializes the contract setting the deployer as the initial Governor. */ constructor() { _setGovernor(msg.sender); emit GovernorshipTransferred(address(0), _governor()); } /** * @notice Returns the address of the current Governor. */ function governor() public view returns (address) { return _governor(); } /** * @dev Returns the address of the current Governor. */ function _governor() internal view returns (address governorOut) { bytes32 position = governorPosition; // solhint-disable-next-line no-inline-assembly assembly { governorOut := sload(position) } } /** * @dev Returns the address of the pending Governor. */ function _pendingGovernor() internal view returns (address pendingGovernor) { bytes32 position = pendingGovernorPosition; // solhint-disable-next-line no-inline-assembly assembly { pendingGovernor := sload(position) } } /** * @dev Throws if called by any account other than the Governor. */ modifier onlyGovernor() { require(isGovernor(), "Caller is not the Governor"); _; } /** * @notice Returns true if the caller is the current Governor. */ function isGovernor() public view returns (bool) { return msg.sender == _governor(); } function _setGovernor(address newGovernor) internal { bytes32 position = governorPosition; // solhint-disable-next-line no-inline-assembly assembly { sstore(position, newGovernor) } } /** * @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 make it call a * `private` function that does the actual work. */ modifier nonReentrant() { bytes32 position = reentryStatusPosition; uint256 _reentry_status; // solhint-disable-next-line no-inline-assembly assembly { _reentry_status := sload(position) } // On the first call to nonReentrant, _notEntered will be true require(_reentry_status != _ENTERED, "Reentrant call"); // Any calls to nonReentrant after this point will fail // solhint-disable-next-line no-inline-assembly assembly { sstore(position, _ENTERED) } _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) // solhint-disable-next-line no-inline-assembly assembly { sstore(position, _NOT_ENTERED) } } function _setPendingGovernor(address newGovernor) internal { bytes32 position = pendingGovernorPosition; // solhint-disable-next-line no-inline-assembly assembly { sstore(position, newGovernor) } } /** * @notice Transfers Governance of the contract to a new account (`newGovernor`). * Can only be called by the current Governor. Must be claimed for this to complete * @param _newGovernor Address of the new Governor */ function transferGovernance(address _newGovernor) external onlyGovernor { _setPendingGovernor(_newGovernor); emit PendingGovernorshipTransfer(_governor(), _newGovernor); } /** * @notice Claim Governance of the contract to a new account (`newGovernor`). * Can only be called by the new Governor. */ function claimGovernance() external { require( msg.sender == _pendingGovernor(), "Only the pending Governor can complete the claim" ); _changeGovernor(msg.sender); } /** * @dev Change Governance of the contract to a new account (`newGovernor`). * @param _newGovernor Address of the new Governor */ function _changeGovernor(address _newGovernor) internal { require(_newGovernor != address(0), "New Governor is address(0)"); emit GovernorshipTransferred(_governor(), _newGovernor); _setGovernor(_newGovernor); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IBasicToken { function symbol() external view returns (string memory); function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IDepositContract { /// @notice A processed deposit event. event DepositEvent( bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index ); /// @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; /// @notice Query the current deposit root hash. /// @return The deposit root hash. function get_deposit_root() external view returns (bytes32); /// @notice Query the current deposit count. /// @return The deposit count encoded as a little endian 64-bit number. function get_deposit_count() external view returns (bytes memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; struct Cluster { uint32 validatorCount; uint64 networkFeeIndex; uint64 index; bool active; uint256 balance; } interface ISSVNetwork { /**********/ /* Errors */ /**********/ error CallerNotOwner(); // 0x5cd83192 error CallerNotWhitelisted(); // 0x8c6e5d71 error FeeTooLow(); // 0x732f9413 error FeeExceedsIncreaseLimit(); // 0x958065d9 error NoFeeDeclared(); // 0x1d226c30 error ApprovalNotWithinTimeframe(); // 0x97e4b518 error OperatorDoesNotExist(); // 0x961e3e8c error InsufficientBalance(); // 0xf4d678b8 error ValidatorDoesNotExist(); // 0xe51315d2 error ClusterNotLiquidatable(); // 0x60300a8d error InvalidPublicKeyLength(); // 0x637297a4 error InvalidOperatorIdsLength(); // 0x38186224 error ClusterAlreadyEnabled(); // 0x3babafd2 error ClusterIsLiquidated(); // 0x95a0cf33 error ClusterDoesNotExists(); // 0x185e2b16 error IncorrectClusterState(); // 0x12e04c87 error UnsortedOperatorsList(); // 0xdd020e25 error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac error ExceedValidatorLimit(); // 0x6df5ab76 error TokenTransferFailed(); // 0x045c4b02 error SameFeeChangeNotAllowed(); // 0xc81272f8 error FeeIncreaseNotAllowed(); // 0x410a2b6c error NotAuthorized(); // 0xea8e4eb5 error OperatorsListNotUnique(); // 0xa5a1ff5d error OperatorAlreadyExists(); // 0x289c9494 error TargetModuleDoesNotExist(); // 0x8f9195fb error MaxValueExceeded(); // 0x91aa3017 error FeeTooHigh(); // 0xcd4e6167 error PublicKeysSharesLengthMismatch(); // 0x9ad467b8 error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938 error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999 error EmptyPublicKeysList(); // df83e679 // legacy errors error ValidatorAlreadyExists(); // 0x8d09a73e error IncorrectValidatorState(); // 0x2feda3c1 event AdminChanged(address previousAdmin, address newAdmin); event BeaconUpgraded(address indexed beacon); event ClusterDeposited( address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster ); event ClusterLiquidated( address indexed owner, uint64[] operatorIds, Cluster cluster ); event ClusterReactivated( address indexed owner, uint64[] operatorIds, Cluster cluster ); event ClusterWithdrawn( address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster ); event DeclareOperatorFeePeriodUpdated(uint64 value); event ExecuteOperatorFeePeriodUpdated(uint64 value); event FeeRecipientAddressUpdated( address indexed owner, address recipientAddress ); event Initialized(uint8 version); event LiquidationThresholdPeriodUpdated(uint64 value); event MinimumLiquidationCollateralUpdated(uint256 value); event NetworkEarningsWithdrawn(uint256 value, address recipient); event NetworkFeeUpdated(uint256 oldFee, uint256 newFee); event OperatorAdded( uint64 indexed operatorId, address indexed owner, bytes publicKey, uint256 fee ); event OperatorFeeDeclarationCancelled( address indexed owner, uint64 indexed operatorId ); event OperatorFeeDeclared( address indexed owner, uint64 indexed operatorId, uint256 blockNumber, uint256 fee ); event OperatorFeeExecuted( address indexed owner, uint64 indexed operatorId, uint256 blockNumber, uint256 fee ); event OperatorFeeIncreaseLimitUpdated(uint64 value); event OperatorMaximumFeeUpdated(uint64 maxFee); event OperatorRemoved(uint64 indexed operatorId); event OperatorWhitelistUpdated( uint64 indexed operatorId, address whitelisted ); event OperatorWithdrawn( address indexed owner, uint64 indexed operatorId, uint256 value ); event OwnershipTransferStarted( address indexed previousOwner, address indexed newOwner ); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); event Upgraded(address indexed implementation); event ValidatorAdded( address indexed owner, uint64[] operatorIds, bytes publicKey, bytes shares, Cluster cluster ); event ValidatorExited( address indexed owner, uint64[] operatorIds, bytes publicKey ); event ValidatorRemoved( address indexed owner, uint64[] operatorIds, bytes publicKey, Cluster cluster ); fallback() external; function acceptOwnership() external; function cancelDeclaredOperatorFee(uint64 operatorId) external; function declareOperatorFee(uint64 operatorId, uint256 fee) external; function deposit( address clusterOwner, uint64[] memory operatorIds, uint256 amount, Cluster memory cluster ) external; function executeOperatorFee(uint64 operatorId) external; function exitValidator(bytes memory publicKey, uint64[] memory operatorIds) external; function bulkExitValidator( bytes[] calldata publicKeys, uint64[] calldata operatorIds ) external; function getVersion() external pure returns (string memory version); function initialize( address token_, address ssvOperators_, address ssvClusters_, address ssvDAO_, address ssvViews_, uint64 minimumBlocksBeforeLiquidation_, uint256 minimumLiquidationCollateral_, uint32 validatorsPerOperatorLimit_, uint64 declareOperatorFeePeriod_, uint64 executeOperatorFeePeriod_, uint64 operatorMaxFeeIncrease_ ) external; function liquidate( address clusterOwner, uint64[] memory operatorIds, Cluster memory cluster ) external; function owner() external view returns (address); function pendingOwner() external view returns (address); function proxiableUUID() external view returns (bytes32); function reactivate( uint64[] memory operatorIds, uint256 amount, Cluster memory cluster ) external; function reduceOperatorFee(uint64 operatorId, uint256 fee) external; function registerOperator(bytes memory publicKey, uint256 fee) external returns (uint64 id); function registerValidator( bytes memory publicKey, uint64[] memory operatorIds, bytes memory sharesData, uint256 amount, Cluster memory cluster ) external; function bulkRegisterValidator( bytes[] calldata publicKeys, uint64[] calldata operatorIds, bytes[] calldata sharesData, uint256 amount, Cluster memory cluster ) external; function removeOperator(uint64 operatorId) external; function removeValidator( bytes memory publicKey, uint64[] memory operatorIds, Cluster memory cluster ) external; function bulkRemoveValidator( bytes[] calldata publicKeys, uint64[] calldata operatorIds, Cluster memory cluster ) external; function renounceOwnership() external; function setFeeRecipientAddress(address recipientAddress) external; function setOperatorWhitelist(uint64 operatorId, address whitelisted) external; function transferOwnership(address newOwner) external; function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external; function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external; function updateLiquidationThresholdPeriod(uint64 blocks) external; function updateMaximumOperatorFee(uint64 maxFee) external; function updateMinimumLiquidationCollateral(uint256 amount) external; function updateModule(uint8 moduleId, address moduleAddress) external; function updateNetworkFee(uint256 fee) external; function updateOperatorFeeIncreaseLimit(uint64 percentage) external; function upgradeTo(address newImplementation) external; function upgradeToAndCall(address newImplementation, bytes memory data) external payable; function withdraw( uint64[] memory operatorIds, uint256 amount, Cluster memory cluster ) external; function withdrawAllOperatorEarnings(uint64 operatorId) external; function withdrawNetworkEarnings(uint256 amount) external; function withdrawOperatorEarnings(uint64 operatorId, uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Platform interface to integrate with lending platform like Compound, AAVE etc. */ interface IStrategy { /** * @dev Deposit the given asset to platform * @param _asset asset address * @param _amount Amount to deposit */ function deposit(address _asset, uint256 _amount) external; /** * @dev Deposit the entire balance of all supported assets in the Strategy * to the platform */ function depositAll() external; /** * @dev Withdraw given asset from Lending platform */ function withdraw( address _recipient, address _asset, uint256 _amount ) external; /** * @dev Liquidate all assets in strategy and return them to Vault. */ function withdrawAll() external; /** * @dev Returns the current balance of the given asset. */ function checkBalance(address _asset) external view returns (uint256 balance); /** * @dev Returns bool indicating whether strategy supports asset. */ function supportsAsset(address _asset) external view returns (bool); /** * @dev Collect reward tokens from the Strategy. */ function collectRewardTokens() external; /** * @dev The address array of the reward tokens for the Strategy. */ function getRewardTokenAddresses() external view returns (address[] memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { VaultStorage } from "../vault/VaultStorage.sol"; interface IVault { event AssetSupported(address _asset); event AssetDefaultStrategyUpdated(address _asset, address _strategy); event AssetAllocated(address _asset, address _strategy, uint256 _amount); event StrategyApproved(address _addr); event StrategyRemoved(address _addr); event Mint(address _addr, uint256 _value); event Redeem(address _addr, uint256 _value); event CapitalPaused(); event CapitalUnpaused(); event RebasePaused(); event RebaseUnpaused(); event VaultBufferUpdated(uint256 _vaultBuffer); event RedeemFeeUpdated(uint256 _redeemFeeBps); event PriceProviderUpdated(address _priceProvider); event AllocateThresholdUpdated(uint256 _threshold); event RebaseThresholdUpdated(uint256 _threshold); event StrategistUpdated(address _address); event MaxSupplyDiffChanged(uint256 maxSupplyDiff); event YieldDistribution(address _to, uint256 _yield, uint256 _fee); event TrusteeFeeBpsChanged(uint256 _basis); event TrusteeAddressChanged(address _address); event SwapperChanged(address _address); event SwapAllowedUndervalueChanged(uint256 _basis); event SwapSlippageChanged(address _asset, uint256 _basis); event Swapped( address indexed _fromAsset, address indexed _toAsset, uint256 _fromAssetAmount, uint256 _toAssetAmount ); event StrategyAddedToMintWhitelist(address indexed strategy); event StrategyRemovedFromMintWhitelist(address indexed strategy); event DripperChanged(address indexed _dripper); event WithdrawalRequested( address indexed _withdrawer, uint256 indexed _requestId, uint256 _amount, uint256 _queued ); event WithdrawalClaimed( address indexed _withdrawer, uint256 indexed _requestId, uint256 _amount ); event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable); // Governable.sol function transferGovernance(address _newGovernor) external; function claimGovernance() external; function governor() external view returns (address); // VaultAdmin.sol function setPriceProvider(address _priceProvider) external; function priceProvider() external view returns (address); function setRedeemFeeBps(uint256 _redeemFeeBps) external; function redeemFeeBps() external view returns (uint256); function setVaultBuffer(uint256 _vaultBuffer) external; function vaultBuffer() external view returns (uint256); function setAutoAllocateThreshold(uint256 _threshold) external; function autoAllocateThreshold() external view returns (uint256); function setRebaseThreshold(uint256 _threshold) external; function rebaseThreshold() external view returns (uint256); function setStrategistAddr(address _address) external; function strategistAddr() external view returns (address); function setMaxSupplyDiff(uint256 _maxSupplyDiff) external; function maxSupplyDiff() external view returns (uint256); function setTrusteeAddress(address _address) external; function trusteeAddress() external view returns (address); function setTrusteeFeeBps(uint256 _basis) external; function trusteeFeeBps() external view returns (uint256); function ousdMetaStrategy() external view returns (address); function setSwapper(address _swapperAddr) external; function setSwapAllowedUndervalue(uint16 _percentageBps) external; function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps) external; function supportAsset(address _asset, uint8 _supportsAsset) external; function approveStrategy(address _addr) external; function removeStrategy(address _addr) external; function setAssetDefaultStrategy(address _asset, address _strategy) external; function assetDefaultStrategies(address _asset) external view returns (address); function pauseRebase() external; function unpauseRebase() external; function rebasePaused() external view returns (bool); function pauseCapital() external; function unpauseCapital() external; function capitalPaused() external view returns (bool); function transferToken(address _asset, uint256 _amount) external; function priceUnitMint(address asset) external view returns (uint256); function priceUnitRedeem(address asset) external view returns (uint256); function withdrawAllFromStrategy(address _strategyAddr) external; function withdrawAllFromStrategies() external; function withdrawFromStrategy( address _strategyFromAddress, address[] calldata _assets, uint256[] calldata _amounts ) external; function depositToStrategy( address _strategyToAddress, address[] calldata _assets, uint256[] calldata _amounts ) external; // VaultCore.sol function mint( address _asset, uint256 _amount, uint256 _minimumOusdAmount ) external; function mintForStrategy(uint256 _amount) external; function redeem(uint256 _amount, uint256 _minimumUnitAmount) external; function burnForStrategy(uint256 _amount) external; function redeemAll(uint256 _minimumUnitAmount) external; function allocate() external; function rebase() external; function swapCollateral( address fromAsset, address toAsset, uint256 fromAssetAmount, uint256 minToAssetAmount, bytes calldata data ) external returns (uint256 toAssetAmount); function totalValue() external view returns (uint256 value); function checkBalance(address _asset) external view returns (uint256); function calculateRedeemOutputs(uint256 _amount) external view returns (uint256[] memory); function getAssetCount() external view returns (uint256); function getAssetConfig(address _asset) external view returns (VaultStorage.Asset memory config); function getAllAssets() external view returns (address[] memory); function getStrategyCount() external view returns (uint256); function swapper() external view returns (address); function allowedSwapUndervalue() external view returns (uint256); function getAllStrategies() external view returns (address[] memory); function isSupportedAsset(address _asset) external view returns (bool); function netOusdMintForStrategyThreshold() external view returns (uint256); function setOusdMetaStrategy(address _ousdMetaStrategy) external; function setNetOusdMintForStrategyThreshold(uint256 _threshold) external; function netOusdMintedForStrategy() external view returns (int256); function setDripper(address _dripper) external; function weth() external view returns (address); function cacheWETHAssetIndex() external; function wethAssetIndex() external view returns (uint256); function initialize(address, address) external; function setAdminImpl(address) external; function removeAsset(address _asset) external; function addStrategyToMintWhitelist(address strategyAddr) external; function removeStrategyFromMintWhitelist(address strategyAddr) external; function isMintWhitelistedStrategy(address strategyAddr) external view returns (bool); // These are OETH specific functions function addWithdrawalQueueLiquidity() external; function requestWithdrawal(uint256 _amount) external returns (uint256 requestId, uint256 queued); function claimWithdrawal(uint256 requestId) external returns (uint256 amount); function claimWithdrawals(uint256[] memory requestIds) external returns (uint256[] memory amounts, uint256 totalAmount); function withdrawalQueueMetadata() external view returns (VaultStorage.WithdrawalQueueMetadata memory); function withdrawalRequests(uint256 requestId) external view returns (VaultStorage.WithdrawalRequest memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IWETH9 { event Approval(address indexed src, address indexed guy, uint256 wad); event Deposit(address indexed dst, uint256 wad); event Transfer(address indexed src, address indexed dst, uint256 wad); event Withdrawal(address indexed src, uint256 wad); function allowance(address, address) external view returns (uint256); function approve(address guy, uint256 wad) external returns (bool); function balanceOf(address) external view returns (uint256); function decimals() external view returns (uint8); function deposit() external payable; function name() external view returns (string memory); function symbol() external view returns (string memory); function totalSupply() external view returns (uint256); function transfer(address dst, uint256 wad) external returns (bool); function transferFrom( address src, address dst, uint256 wad ) external returns (bool); function withdraw(uint256 wad) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; /** * @title Fee Accumulator for Native Staking SSV Strategy * @notice Receives execution rewards which includes tx fees and * MEV rewards like tx priority and tx ordering. * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals. * @author Origin Protocol Inc */ contract FeeAccumulator { /// @notice The address of the Native Staking Strategy address public immutable STRATEGY; event ExecutionRewardsCollected(address indexed strategy, uint256 amount); /** * @param _strategy Address of the Native Staking Strategy */ constructor(address _strategy) { STRATEGY = _strategy; } /** * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy. * @return eth The amount of execution rewards that were sent to the Native Staking Strategy */ function collect() external returns (uint256 eth) { require(msg.sender == STRATEGY, "Caller is not the Strategy"); eth = address(this).balance; if (eth > 0) { // Send the ETH to the Native Staking Strategy Address.sendValue(payable(STRATEGY), eth); emit ExecutionRewardsCollected(STRATEGY, eth); } } /** * @dev Accept ETH */ receive() external payable {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { ValidatorRegistrator } from "./ValidatorRegistrator.sol"; import { IWETH9 } from "../../interfaces/IWETH9.sol"; /// @title Validator Accountant /// @notice Attributes the ETH swept from beacon chain validators to this strategy contract /// as either full or partial withdrawals. Partial withdrawals being consensus rewards. /// Full withdrawals are from exited validators. /// @author Origin Protocol Inc abstract contract ValidatorAccountant is ValidatorRegistrator { /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day /// @notice Keeps track of the total consensus rewards swept from the beacon chain uint256 public consensusRewards; /// @notice start of fuse interval uint256 public fuseIntervalStart; /// @notice end of fuse interval uint256 public fuseIntervalEnd; /// @notice last block number manuallyFixAccounting has been called uint256 public lastFixAccountingBlockNumber; uint256[49] private __gap; event FuseIntervalUpdated(uint256 start, uint256 end); event AccountingFullyWithdrawnValidator( uint256 noOfValidators, uint256 remainingValidators, uint256 wethSentToVault ); event AccountingValidatorSlashed( uint256 remainingValidators, uint256 wethSentToVault ); event AccountingConsensusRewards(uint256 amount); event AccountingManuallyFixed( int256 validatorsDelta, int256 consensusRewardsDelta, uint256 wethToVault ); /// @param _wethAddress Address of the Erc20 WETH Token contract /// @param _vaultAddress Address of the Vault /// @param _beaconChainDepositContract Address of the beacon chain deposit contract /// @param _ssvNetwork Address of the SSV Network contract /// @param _maxValidators Maximum number of validators that can be registered in the strategy constructor( address _wethAddress, address _vaultAddress, address _beaconChainDepositContract, address _ssvNetwork, uint256 _maxValidators ) ValidatorRegistrator( _wethAddress, _vaultAddress, _beaconChainDepositContract, _ssvNetwork, _maxValidators ) {} /// @notice set fuse interval values function setFuseInterval( uint256 _fuseIntervalStart, uint256 _fuseIntervalEnd ) external onlyGovernor { require( _fuseIntervalStart < _fuseIntervalEnd && _fuseIntervalEnd < 32 ether && _fuseIntervalEnd - _fuseIntervalStart >= 4 ether, "Incorrect fuse interval" ); fuseIntervalStart = _fuseIntervalStart; fuseIntervalEnd = _fuseIntervalEnd; emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd); } /* solhint-disable max-line-length */ /// This notion page offers a good explanation of how the accounting functions /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart, /// the accounting function will treat that ETH as Beacon chain consensus rewards. /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32, /// the accounting function will treat that as a validator slashing. /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when /// accounting is valid and fuse isn't "blown". Returns false when fuse is blown. /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it /// for now. /// @return accountingValid true if accounting was successful, false if fuse is blown /* solhint-enable max-line-length */ function doAccounting() external onlyRegistrator whenNotPaused nonReentrant returns (bool accountingValid) { // pause the accounting on failure accountingValid = _doAccounting(true); } // slither-disable-start reentrancy-eth function _doAccounting(bool pauseOnFail) internal returns (bool accountingValid) { if (address(this).balance < consensusRewards) { return _failAccounting(pauseOnFail); } // Calculate all the new ETH that has been swept to the contract since the last accounting uint256 newSweptETH = address(this).balance - consensusRewards; accountingValid = true; // send the ETH that is from fully withdrawn validators to the Vault if (newSweptETH >= FULL_STAKE) { uint256 fullyWithdrawnValidators; // explicitly cast to uint256 as we want to round to a whole number of validators fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE); activeDepositedValidators -= fullyWithdrawnValidators; uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators; IWETH9(WETH).deposit{ value: wethToVault }(); // slither-disable-next-line unchecked-transfer IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault); _wethWithdrawnToVault(wethToVault); emit AccountingFullyWithdrawnValidator( fullyWithdrawnValidators, activeDepositedValidators, wethToVault ); } uint256 ethRemaining = address(this).balance - consensusRewards; // should be less than a whole validator stake require(ethRemaining < FULL_STAKE, "Unexpected accounting"); // If no Beacon chain consensus rewards swept if (ethRemaining == 0) { // do nothing return accountingValid; } else if (ethRemaining < fuseIntervalStart) { // Beacon chain consensus rewards swept (partial validator withdrawals) // solhint-disable-next-line reentrancy consensusRewards += ethRemaining; emit AccountingConsensusRewards(ethRemaining); } else if (ethRemaining > fuseIntervalEnd) { // Beacon chain consensus rewards swept but also a slashed validator fully exited IWETH9(WETH).deposit{ value: ethRemaining }(); // slither-disable-next-line unchecked-transfer IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining); activeDepositedValidators -= 1; _wethWithdrawnToVault(ethRemaining); emit AccountingValidatorSlashed( activeDepositedValidators, ethRemaining ); } // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values. else { return _failAccounting(pauseOnFail); } } // slither-disable-end reentrancy-eth /// @dev pause any further accounting if required and return false function _failAccounting(bool pauseOnFail) internal returns (bool accountingValid) { // pause if not already if (pauseOnFail) { _pause(); } // fail the accounting accountingValid = false; } /// @notice Allow the Strategist to fix the accounting of this strategy and unpause. /// @param _validatorsDelta adjust the active validators by up to plus three or minus three /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from /// the beacon chain enters the fuse area and there are no consensus rewards on the contract /// to "dip into"/use. To increase the amount of unaccounted ETH over the fuse end interval /// we need to reduce the amount of active deposited validators and immediately send WETH /// to the vault, so it doesn't interfere with further accounting. function manuallyFixAccounting( int256 _validatorsDelta, int256 _consensusRewardsDelta, uint256 _ethToVaultAmount ) external onlyStrategist whenPaused nonReentrant { require( lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE < block.number, "Fix accounting called too soon" ); require( _validatorsDelta >= -3 && _validatorsDelta <= 3 && // new value must be positive int256(activeDepositedValidators) + _validatorsDelta >= 0, "Invalid validatorsDelta" ); require( _consensusRewardsDelta >= -332 ether && _consensusRewardsDelta <= 332 ether && // new value must be positive int256(consensusRewards) + _consensusRewardsDelta >= 0, "Invalid consensusRewardsDelta" ); require(_ethToVaultAmount <= 32 ether * 3, "Invalid wethToVaultAmount"); activeDepositedValidators = uint256( int256(activeDepositedValidators) + _validatorsDelta ); consensusRewards = uint256( int256(consensusRewards) + _consensusRewardsDelta ); lastFixAccountingBlockNumber = block.number; if (_ethToVaultAmount > 0) { IWETH9(WETH).deposit{ value: _ethToVaultAmount }(); // slither-disable-next-line unchecked-transfer IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount); _wethWithdrawnToVault(_ethToVaultAmount); } emit AccountingManuallyFixed( _validatorsDelta, _consensusRewardsDelta, _ethToVaultAmount ); // rerun the accounting to see if it has now been fixed. // Do not pause the accounting on failure as it is already paused require(_doAccounting(false), "Fuse still blown"); // unpause since doAccounting was successful _unpause(); } /*************************************** Abstract ****************************************/ /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event function _wethWithdrawnToVault(uint256 _amount) internal virtual; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol"; import { Governable } from "../../governance/Governable.sol"; import { IDepositContract } from "../../interfaces/IDepositContract.sol"; import { IVault } from "../../interfaces/IVault.sol"; import { IWETH9 } from "../../interfaces/IWETH9.sol"; import { ISSVNetwork, Cluster } from "../../interfaces/ISSVNetwork.sol"; struct ValidatorStakeData { bytes pubkey; bytes signature; bytes32 depositDataRoot; } /** * @title Registrator of the validators * @notice This contract implements all the required functionality to register, exit and remove validators. * @author Origin Protocol Inc */ abstract contract ValidatorRegistrator is Governable, Pausable { /// @notice The maximum amount of ETH that can be staked by a validator /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE uint256 public constant FULL_STAKE = 32 ether; /// @notice The address of the Wrapped ETH (WETH) token contract address public immutable WETH; /// @notice The address of the beacon chain deposit contract address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT; /// @notice The address of the SSV Network contract used to interface with address public immutable SSV_NETWORK; /// @notice Address of the OETH Vault proxy contract address public immutable VAULT_ADDRESS; /// @notice Maximum number of validators that can be registered in this strategy uint256 public immutable MAX_VALIDATORS; /// @notice Address of the registrator - allowed to register, exit and remove validators address public validatorRegistrator; /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit /// to a validator happens this number increases, when a validator exit is detected this number /// decreases. uint256 public activeDepositedValidators; /// @notice State of the validators keccak256(pubKey) => state mapping(bytes32 => VALIDATOR_STATE) public validatorsStates; /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally address public stakingMonitor; /// @notice Amount of ETH that can be staked before staking on the contract is suspended /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally` uint256 public stakeETHThreshold; /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`. /// This can not go above `stakeETHThreshold`. uint256 public stakeETHTally; // For future use uint256[47] private __gap; enum VALIDATOR_STATE { NON_REGISTERED, // validator is not registered on the SSV network REGISTERED, // validator is registered on the SSV network STAKED, // validator has funds staked EXITING, // exit message has been posted and validator is in the process of exiting EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV } event RegistratorChanged(address indexed newAddress); event StakingMonitorChanged(address indexed newAddress); event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount); event SSVValidatorRegistered( bytes32 indexed pubKeyHash, bytes pubKey, uint64[] operatorIds ); event SSVValidatorExitInitiated( bytes32 indexed pubKeyHash, bytes pubKey, uint64[] operatorIds ); event SSVValidatorExitCompleted( bytes32 indexed pubKeyHash, bytes pubKey, uint64[] operatorIds ); event StakeETHThresholdChanged(uint256 amount); event StakeETHTallyReset(); /// @dev Throws if called by any account other than the Registrator modifier onlyRegistrator() { require( msg.sender == validatorRegistrator, "Caller is not the Registrator" ); _; } /// @dev Throws if called by any account other than the Staking monitor modifier onlyStakingMonitor() { require(msg.sender == stakingMonitor, "Caller is not the Monitor"); _; } /// @dev Throws if called by any account other than the Strategist modifier onlyStrategist() { require( msg.sender == IVault(VAULT_ADDRESS).strategistAddr(), "Caller is not the Strategist" ); _; } /// @param _wethAddress Address of the Erc20 WETH Token contract /// @param _vaultAddress Address of the Vault /// @param _beaconChainDepositContract Address of the beacon chain deposit contract /// @param _ssvNetwork Address of the SSV Network contract /// @param _maxValidators Maximum number of validators that can be registered in the strategy constructor( address _wethAddress, address _vaultAddress, address _beaconChainDepositContract, address _ssvNetwork, uint256 _maxValidators ) { WETH = _wethAddress; BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract; SSV_NETWORK = _ssvNetwork; VAULT_ADDRESS = _vaultAddress; MAX_VALIDATORS = _maxValidators; } /// @notice Set the address of the registrator which can register, exit and remove validators function setRegistrator(address _address) external onlyGovernor { validatorRegistrator = _address; emit RegistratorChanged(_address); } /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally function setStakingMonitor(address _address) external onlyGovernor { stakingMonitor = _address; emit StakingMonitorChanged(_address); } /// @notice Set the amount of ETH that can be staked before staking monitor // needs to a approve further staking by resetting the stake ETH tally function setStakeETHThreshold(uint256 _amount) external onlyGovernor { stakeETHThreshold = _amount; emit StakeETHThresholdChanged(_amount); } /// @notice Reset the stakeETHTally function resetStakeETHTally() external onlyStakingMonitor { stakeETHTally = 0; emit StakeETHTallyReset(); } /// @notice Stakes WETH to the node validators /// @param validators A list of validator data needed to stake. /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot. /// Only the registrator can call this function. // slither-disable-start reentrancy-eth function stakeEth(ValidatorStakeData[] calldata validators) external onlyRegistrator whenNotPaused nonReentrant { uint256 requiredETH = validators.length * FULL_STAKE; // Check there is enough WETH from the deposits sitting in this strategy contract require( requiredETH <= IWETH9(WETH).balanceOf(address(this)), "Insufficient WETH" ); require( activeDepositedValidators + validators.length <= MAX_VALIDATORS, "Max validators reached" ); require( stakeETHTally + requiredETH <= stakeETHThreshold, "Staking ETH over threshold" ); stakeETHTally += requiredETH; // Convert required ETH from WETH IWETH9(WETH).withdraw(requiredETH); _wethWithdrawn(requiredETH); /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function * can sweep funds to. * bytes11(0) to fill up the required zeros * remaining bytes20 are for the address */ bytes memory withdrawalCredentials = abi.encodePacked( bytes1(0x01), bytes11(0), address(this) ); // For each validator for (uint256 i = 0; i < validators.length; ++i) { bytes32 pubKeyHash = keccak256(validators[i].pubkey); require( validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED, "Validator not registered" ); IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{ value: FULL_STAKE }( validators[i].pubkey, withdrawalCredentials, validators[i].signature, validators[i].depositDataRoot ); validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED; emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE); } // save gas by changing this storage variable only once rather each time in the loop. activeDepositedValidators += validators.length; } // slither-disable-end reentrancy-eth /// @notice Registers a new validator in the SSV Cluster. /// Only the registrator can call this function. /// @param publicKeys The public keys of the validators /// @param operatorIds The operator IDs of the SSV Cluster /// @param sharesData The shares data for each validator /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster /// @param cluster The SSV cluster details including the validator count and SSV balance // slither-disable-start reentrancy-no-eth function registerSsvValidators( bytes[] calldata publicKeys, uint64[] calldata operatorIds, bytes[] calldata sharesData, uint256 ssvAmount, Cluster calldata cluster ) external onlyRegistrator whenNotPaused { require( publicKeys.length == sharesData.length, "Pubkey sharesData mismatch" ); // Check each public key has not already been used bytes32 pubKeyHash; VALIDATOR_STATE currentState; for (uint256 i = 0; i < publicKeys.length; ++i) { pubKeyHash = keccak256(publicKeys[i]); currentState = validatorsStates[pubKeyHash]; require( currentState == VALIDATOR_STATE.NON_REGISTERED, "Validator already registered" ); validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED; emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds); } ISSVNetwork(SSV_NETWORK).bulkRegisterValidator( publicKeys, operatorIds, sharesData, ssvAmount, cluster ); } // slither-disable-end reentrancy-no-eth /// @notice Exit a validator from the Beacon chain. /// The staked ETH will eventually swept to this native staking strategy. /// Only the registrator can call this function. /// @param publicKey The public key of the validator /// @param operatorIds The operator IDs of the SSV Cluster // slither-disable-start reentrancy-no-eth function exitSsvValidator( bytes calldata publicKey, uint64[] calldata operatorIds ) external onlyRegistrator whenNotPaused { bytes32 pubKeyHash = keccak256(publicKey); VALIDATOR_STATE currentState = validatorsStates[pubKeyHash]; require(currentState == VALIDATOR_STATE.STAKED, "Validator not staked"); ISSVNetwork(SSV_NETWORK).exitValidator(publicKey, operatorIds); validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING; emit SSVValidatorExitInitiated(pubKeyHash, publicKey, operatorIds); } // slither-disable-end reentrancy-no-eth /// @notice Remove a validator from the SSV Cluster. /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain. /// If removed before the validator has exited the beacon chain will result in the validator being slashed. /// Only the registrator can call this function. /// @param publicKey The public key of the validator /// @param operatorIds The operator IDs of the SSV Cluster /// @param cluster The SSV cluster details including the validator count and SSV balance // slither-disable-start reentrancy-no-eth function removeSsvValidator( bytes calldata publicKey, uint64[] calldata operatorIds, Cluster calldata cluster ) external onlyRegistrator whenNotPaused { bytes32 pubKeyHash = keccak256(publicKey); VALIDATOR_STATE currentState = validatorsStates[pubKeyHash]; // Can remove SSV validators that were incorrectly registered and can not be deposited to. require( currentState == VALIDATOR_STATE.EXITING || currentState == VALIDATOR_STATE.REGISTERED, "Validator not regd or exiting" ); ISSVNetwork(SSV_NETWORK).removeValidator( publicKey, operatorIds, cluster ); validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE; emit SSVValidatorExitCompleted(pubKeyHash, publicKey, operatorIds); } // slither-disable-end reentrancy-no-eth /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators. /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds. /// uses "onlyStrategist" modifier so continuous front-running can't DOS our maintenance service /// that tries to top up SSV tokens. /// @param operatorIds The operator IDs of the SSV Cluster /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster /// @param cluster The SSV cluster details including the validator count and SSV balance function depositSSV( uint64[] memory operatorIds, uint256 ssvAmount, Cluster memory cluster ) external onlyStrategist { ISSVNetwork(SSV_NETWORK).deposit( address(this), operatorIds, ssvAmount, cluster ); } /*************************************** Abstract ****************************************/ /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so /// the strategy knows how much WETH it has on deposit. /// This is so it can emit the correct amount in the Deposit event in depositAll(). function _wethWithdrawn(uint256 _amount) internal virtual; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title OUSD Token Contract * @dev ERC20 compatible contract for OUSD * @dev Implements an elastic supply * @author Origin Protocol Inc */ import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { Initializable } from "../utils/Initializable.sol"; import { InitializableERC20Detailed } from "../utils/InitializableERC20Detailed.sol"; import { StableMath } from "../utils/StableMath.sol"; import { Governable } from "../governance/Governable.sol"; /** * NOTE that this is an ERC20 token but the invariant that the sum of * balanceOf(x) for all x is not >= totalSupply(). This is a consequence of the * rebasing design. Any integrations with OUSD should be aware. */ contract OUSD is Initializable, InitializableERC20Detailed, Governable { using SafeMath for uint256; using StableMath for uint256; event TotalSupplyUpdatedHighres( uint256 totalSupply, uint256 rebasingCredits, uint256 rebasingCreditsPerToken ); event AccountRebasingEnabled(address account); event AccountRebasingDisabled(address account); enum RebaseOptions { NotSet, OptOut, OptIn } uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1 uint256 public _totalSupply; mapping(address => mapping(address => uint256)) private _allowances; address public vaultAddress = address(0); mapping(address => uint256) private _creditBalances; uint256 private _rebasingCredits; uint256 private _rebasingCreditsPerToken; // Frozen address/credits are non rebasing (value is held in contracts which // do not receive yield unless they explicitly opt in) uint256 public nonRebasingSupply; mapping(address => uint256) public nonRebasingCreditsPerToken; mapping(address => RebaseOptions) public rebaseState; mapping(address => uint256) public isUpgraded; uint256 private constant RESOLUTION_INCREASE = 1e9; function initialize( string calldata _nameArg, string calldata _symbolArg, address _vaultAddress, uint256 _initialCreditsPerToken ) external onlyGovernor initializer { InitializableERC20Detailed._initialize(_nameArg, _symbolArg, 18); _rebasingCreditsPerToken = _initialCreditsPerToken; vaultAddress = _vaultAddress; } /** * @dev Verifies that the caller is the Vault contract */ modifier onlyVault() { require(vaultAddress == msg.sender, "Caller is not the Vault"); _; } /** * @return The total supply of OUSD. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @return Low resolution rebasingCreditsPerToken */ function rebasingCreditsPerToken() public view returns (uint256) { return _rebasingCreditsPerToken / RESOLUTION_INCREASE; } /** * @return Low resolution total number of rebasing credits */ function rebasingCredits() public view returns (uint256) { return _rebasingCredits / RESOLUTION_INCREASE; } /** * @return High resolution rebasingCreditsPerToken */ function rebasingCreditsPerTokenHighres() public view returns (uint256) { return _rebasingCreditsPerToken; } /** * @return High resolution total number of rebasing credits */ function rebasingCreditsHighres() public view returns (uint256) { return _rebasingCredits; } /** * @dev Gets the balance of the specified address. * @param _account Address to query the balance of. * @return A uint256 representing the amount of base units owned by the * specified address. */ function balanceOf(address _account) public view override returns (uint256) { if (_creditBalances[_account] == 0) return 0; return _creditBalances[_account].divPrecisely(_creditsPerToken(_account)); } /** * @dev Gets the credits balance of the specified address. * @dev Backwards compatible with old low res credits per token. * @param _account The address to query the balance of. * @return (uint256, uint256) Credit balance and credits per token of the * address */ function creditsBalanceOf(address _account) public view returns (uint256, uint256) { uint256 cpt = _creditsPerToken(_account); if (cpt == 1e27) { // For a period before the resolution upgrade, we created all new // contract accounts at high resolution. Since they are not changing // as a result of this upgrade, we will return their true values return (_creditBalances[_account], cpt); } else { return ( _creditBalances[_account] / RESOLUTION_INCREASE, cpt / RESOLUTION_INCREASE ); } } /** * @dev Gets the credits balance of the specified address. * @param _account The address to query the balance of. * @return (uint256, uint256, bool) Credit balance, credits per token of the * address, and isUpgraded */ function creditsBalanceOfHighres(address _account) public view returns ( uint256, uint256, bool ) { return ( _creditBalances[_account], _creditsPerToken(_account), isUpgraded[_account] == 1 ); } /** * @dev Transfer tokens to a specified address. * @param _to the address to transfer to. * @param _value the amount to be transferred. * @return true on success. */ function transfer(address _to, uint256 _value) public override returns (bool) { require(_to != address(0), "Transfer to zero address"); require( _value <= balanceOf(msg.sender), "Transfer greater than balance" ); _executeTransfer(msg.sender, _to, _value); emit Transfer(msg.sender, _to, _value); return true; } /** * @dev Transfer tokens from one address to another. * @param _from The address you want to send tokens from. * @param _to The address you want to transfer to. * @param _value The amount of tokens to be transferred. */ function transferFrom( address _from, address _to, uint256 _value ) public override returns (bool) { require(_to != address(0), "Transfer to zero address"); require(_value <= balanceOf(_from), "Transfer greater than balance"); _allowances[_from][msg.sender] = _allowances[_from][msg.sender].sub( _value ); _executeTransfer(_from, _to, _value); emit Transfer(_from, _to, _value); return true; } /** * @dev Update the count of non rebasing credits in response to a transfer * @param _from The address you want to send tokens from. * @param _to The address you want to transfer to. * @param _value Amount of OUSD to transfer */ function _executeTransfer( address _from, address _to, uint256 _value ) internal { bool isNonRebasingTo = _isNonRebasingAccount(_to); bool isNonRebasingFrom = _isNonRebasingAccount(_from); // Credits deducted and credited might be different due to the // differing creditsPerToken used by each account uint256 creditsCredited = _value.mulTruncate(_creditsPerToken(_to)); uint256 creditsDeducted = _value.mulTruncate(_creditsPerToken(_from)); _creditBalances[_from] = _creditBalances[_from].sub( creditsDeducted, "Transfer amount exceeds balance" ); _creditBalances[_to] = _creditBalances[_to].add(creditsCredited); if (isNonRebasingTo && !isNonRebasingFrom) { // Transfer to non-rebasing account from rebasing account, credits // are removed from the non rebasing tally nonRebasingSupply = nonRebasingSupply.add(_value); // Update rebasingCredits by subtracting the deducted amount _rebasingCredits = _rebasingCredits.sub(creditsDeducted); } else if (!isNonRebasingTo && isNonRebasingFrom) { // Transfer to rebasing account from non-rebasing account // Decreasing non-rebasing credits by the amount that was sent nonRebasingSupply = nonRebasingSupply.sub(_value); // Update rebasingCredits by adding the credited amount _rebasingCredits = _rebasingCredits.add(creditsCredited); } } /** * @dev Function to check the amount of tokens that _owner has allowed to * `_spender`. * @param _owner The address which owns the funds. * @param _spender The address which will spend the funds. * @return The number of tokens still available for the _spender. */ function allowance(address _owner, address _spender) public view override returns (uint256) { return _allowances[_owner][_spender]; } /** * @dev Approve the passed address to spend the specified amount of tokens * on behalf of msg.sender. This method is included for ERC20 * compatibility. `increaseAllowance` and `decreaseAllowance` should be * used instead. * * Changing an allowance with this method brings the risk that someone * may transfer both the old and the new allowance - if they are both * greater than zero - if a transfer transaction is mined before the * later approve() call is mined. * @param _spender The address which will spend the funds. * @param _value The amount of tokens to be spent. */ function approve(address _spender, uint256 _value) public override returns (bool) { _allowances[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } /** * @dev Increase the amount of tokens that an owner has allowed to * `_spender`. * This method should be used instead of approve() to avoid the double * approval vulnerability described above. * @param _spender The address which will spend the funds. * @param _addedValue The amount of tokens to increase the allowance by. */ function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool) { _allowances[msg.sender][_spender] = _allowances[msg.sender][_spender] .add(_addedValue); emit Approval(msg.sender, _spender, _allowances[msg.sender][_spender]); return true; } /** * @dev Decrease the amount of tokens that an owner has allowed to `_spender`. * @param _spender The address which will spend the funds. * @param _subtractedValue The amount of tokens to decrease the allowance * by. */ function decreaseAllowance(address _spender, uint256 _subtractedValue) public returns (bool) { uint256 oldValue = _allowances[msg.sender][_spender]; if (_subtractedValue >= oldValue) { _allowances[msg.sender][_spender] = 0; } else { _allowances[msg.sender][_spender] = oldValue.sub(_subtractedValue); } emit Approval(msg.sender, _spender, _allowances[msg.sender][_spender]); return true; } /** * @dev Mints new tokens, increasing totalSupply. */ function mint(address _account, uint256 _amount) external onlyVault { _mint(_account, _amount); } /** * @dev Creates `_amount` tokens and assigns them to `_account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address _account, uint256 _amount) internal nonReentrant { require(_account != address(0), "Mint to the zero address"); bool isNonRebasingAccount = _isNonRebasingAccount(_account); uint256 creditAmount = _amount.mulTruncate(_creditsPerToken(_account)); _creditBalances[_account] = _creditBalances[_account].add(creditAmount); // If the account is non rebasing and doesn't have a set creditsPerToken // then set it i.e. this is a mint from a fresh contract if (isNonRebasingAccount) { nonRebasingSupply = nonRebasingSupply.add(_amount); } else { _rebasingCredits = _rebasingCredits.add(creditAmount); } _totalSupply = _totalSupply.add(_amount); require(_totalSupply < MAX_SUPPLY, "Max supply"); emit Transfer(address(0), _account, _amount); } /** * @dev Burns tokens, decreasing totalSupply. */ function burn(address account, uint256 amount) external onlyVault { _burn(account, amount); } /** * @dev Destroys `_amount` tokens from `_account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `_account` cannot be the zero address. * - `_account` must have at least `_amount` tokens. */ function _burn(address _account, uint256 _amount) internal nonReentrant { require(_account != address(0), "Burn from the zero address"); if (_amount == 0) { return; } bool isNonRebasingAccount = _isNonRebasingAccount(_account); uint256 creditAmount = _amount.mulTruncate(_creditsPerToken(_account)); uint256 currentCredits = _creditBalances[_account]; // Remove the credits, burning rounding errors if ( currentCredits == creditAmount || currentCredits - 1 == creditAmount ) { // Handle dust from rounding _creditBalances[_account] = 0; } else if (currentCredits > creditAmount) { _creditBalances[_account] = _creditBalances[_account].sub( creditAmount ); } else { revert("Remove exceeds balance"); } // Remove from the credit tallies and non-rebasing supply if (isNonRebasingAccount) { nonRebasingSupply = nonRebasingSupply.sub(_amount); } else { _rebasingCredits = _rebasingCredits.sub(creditAmount); } _totalSupply = _totalSupply.sub(_amount); emit Transfer(_account, address(0), _amount); } /** * @dev Get the credits per token for an account. Returns a fixed amount * if the account is non-rebasing. * @param _account Address of the account. */ function _creditsPerToken(address _account) internal view returns (uint256) { if (nonRebasingCreditsPerToken[_account] != 0) { return nonRebasingCreditsPerToken[_account]; } else { return _rebasingCreditsPerToken; } } /** * @dev Is an account using rebasing accounting or non-rebasing accounting? * Also, ensure contracts are non-rebasing if they have not opted in. * @param _account Address of the account. */ function _isNonRebasingAccount(address _account) internal returns (bool) { bool isContract = Address.isContract(_account); if (isContract && rebaseState[_account] == RebaseOptions.NotSet) { _ensureRebasingMigration(_account); } return nonRebasingCreditsPerToken[_account] > 0; } /** * @dev Ensures internal account for rebasing and non-rebasing credits and * supply is updated following deployment of frozen yield change. */ function _ensureRebasingMigration(address _account) internal { if (nonRebasingCreditsPerToken[_account] == 0) { emit AccountRebasingDisabled(_account); if (_creditBalances[_account] == 0) { // Since there is no existing balance, we can directly set to // high resolution, and do not have to do any other bookkeeping nonRebasingCreditsPerToken[_account] = 1e27; } else { // Migrate an existing account: // Set fixed credits per token for this account nonRebasingCreditsPerToken[_account] = _rebasingCreditsPerToken; // Update non rebasing supply nonRebasingSupply = nonRebasingSupply.add(balanceOf(_account)); // Update credit tallies _rebasingCredits = _rebasingCredits.sub( _creditBalances[_account] ); } } } /** * @notice Enable rebasing for an account. * @dev Add a contract address to the non-rebasing exception list. The * address's balance will be part of rebases and the account will be exposed * to upside and downside. * @param _account Address of the account. */ function governanceRebaseOptIn(address _account) public nonReentrant onlyGovernor { _rebaseOptIn(_account); } /** * @dev Add a contract address to the non-rebasing exception list. The * address's balance will be part of rebases and the account will be exposed * to upside and downside. */ function rebaseOptIn() public nonReentrant { _rebaseOptIn(msg.sender); } function _rebaseOptIn(address _account) internal { require(_isNonRebasingAccount(_account), "Account has not opted out"); // Convert balance into the same amount at the current exchange rate uint256 newCreditBalance = _creditBalances[_account] .mul(_rebasingCreditsPerToken) .div(_creditsPerToken(_account)); // Decreasing non rebasing supply nonRebasingSupply = nonRebasingSupply.sub(balanceOf(_account)); _creditBalances[_account] = newCreditBalance; // Increase rebasing credits, totalSupply remains unchanged so no // adjustment necessary _rebasingCredits = _rebasingCredits.add(_creditBalances[_account]); rebaseState[_account] = RebaseOptions.OptIn; // Delete any fixed credits per token delete nonRebasingCreditsPerToken[_account]; emit AccountRebasingEnabled(_account); } /** * @dev Explicitly mark that an address is non-rebasing. */ function rebaseOptOut() public nonReentrant { require(!_isNonRebasingAccount(msg.sender), "Account has not opted in"); // Increase non rebasing supply nonRebasingSupply = nonRebasingSupply.add(balanceOf(msg.sender)); // Set fixed credits per token nonRebasingCreditsPerToken[msg.sender] = _rebasingCreditsPerToken; // Decrease rebasing credits, total supply remains unchanged so no // adjustment necessary _rebasingCredits = _rebasingCredits.sub(_creditBalances[msg.sender]); // Mark explicitly opted out of rebasing rebaseState[msg.sender] = RebaseOptions.OptOut; emit AccountRebasingDisabled(msg.sender); } /** * @dev Modify the supply without minting new tokens. This uses a change in * the exchange rate between "credits" and OUSD tokens to change balances. * @param _newTotalSupply New total supply of OUSD. */ function changeSupply(uint256 _newTotalSupply) external onlyVault nonReentrant { require(_totalSupply > 0, "Cannot increase 0 supply"); if (_totalSupply == _newTotalSupply) { emit TotalSupplyUpdatedHighres( _totalSupply, _rebasingCredits, _rebasingCreditsPerToken ); return; } _totalSupply = _newTotalSupply > MAX_SUPPLY ? MAX_SUPPLY : _newTotalSupply; _rebasingCreditsPerToken = _rebasingCredits.divPrecisely( _totalSupply.sub(nonRebasingSupply) ); require(_rebasingCreditsPerToken > 0, "Invalid change in supply"); _totalSupply = _rebasingCredits .divPrecisely(_rebasingCreditsPerToken) .add(nonRebasingSupply); emit TotalSupplyUpdatedHighres( _totalSupply, _rebasingCredits, _rebasingCreditsPerToken ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IBasicToken } from "../interfaces/IBasicToken.sol"; library Helpers { /** * @notice Fetch the `symbol()` from an ERC20 token * @dev Grabs the `symbol()` from a contract * @param _token Address of the ERC20 token * @return string Symbol of the ERC20 token */ function getSymbol(address _token) internal view returns (string memory) { string memory symbol = IBasicToken(_token).symbol(); return symbol; } /** * @notice Fetch the `decimals()` from an ERC20 token * @dev Grabs the `decimals()` from a contract and fails if * the decimal value does not live within a certain range * @param _token Address of the ERC20 token * @return uint256 Decimals of the ERC20 token */ function getDecimals(address _token) internal view returns (uint256) { uint256 decimals = IBasicToken(_token).decimals(); require( decimals >= 4 && decimals <= 18, "Token must have sufficient decimal places" ); return decimals; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Base contract any contracts that need to initialize state after deployment. * @author Origin Protocol Inc */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { require( initializing || !initialized, "Initializable: contract is already initialized" ); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; initialized = true; } _; if (isTopLevelCall) { initializing = false; } } uint256[50] private ______gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Base contract for vault strategies. * @author Origin Protocol Inc */ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Initializable } from "../utils/Initializable.sol"; import { Governable } from "../governance/Governable.sol"; import { IVault } from "../interfaces/IVault.sol"; abstract contract InitializableAbstractStrategy is Initializable, Governable { using SafeERC20 for IERC20; event PTokenAdded(address indexed _asset, address _pToken); event PTokenRemoved(address indexed _asset, address _pToken); event Deposit(address indexed _asset, address _pToken, uint256 _amount); event Withdrawal(address indexed _asset, address _pToken, uint256 _amount); event RewardTokenCollected( address recipient, address rewardToken, uint256 amount ); event RewardTokenAddressesUpdated( address[] _oldAddresses, address[] _newAddresses ); event HarvesterAddressesUpdated( address _oldHarvesterAddress, address _newHarvesterAddress ); /// @notice Address of the underlying platform address public immutable platformAddress; /// @notice Address of the OToken vault address public immutable vaultAddress; /// @dev Replaced with an immutable variable // slither-disable-next-line constable-states address private _deprecated_platformAddress; /// @dev Replaced with an immutable // slither-disable-next-line constable-states address private _deprecated_vaultAddress; /// @notice asset => pToken (Platform Specific Token Address) mapping(address => address) public assetToPToken; /// @notice Full list of all assets supported by the strategy address[] internal assetsMapped; // Deprecated: Reward token address // slither-disable-next-line constable-states address private _deprecated_rewardTokenAddress; // Deprecated: now resides in Harvester's rewardTokenConfigs // slither-disable-next-line constable-states uint256 private _deprecated_rewardLiquidationThreshold; /// @notice Address of the Harvester contract allowed to collect reward tokens address public harvesterAddress; /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA address[] public rewardTokenAddresses; /* Reserved for future expansion. Used to be 100 storage slots * and has decreased to accommodate: * - harvesterAddress * - rewardTokenAddresses */ int256[98] private _reserved; struct BaseStrategyConfig { address platformAddress; // Address of the underlying platform address vaultAddress; // Address of the OToken's Vault } /** * @param _config The platform and OToken vault addresses */ constructor(BaseStrategyConfig memory _config) { platformAddress = _config.platformAddress; vaultAddress = _config.vaultAddress; } /** * @dev Internal initialize function, to set up initial internal state * @param _rewardTokenAddresses Address of reward token for platform * @param _assets Addresses of initial supported assets * @param _pTokens Platform Token corresponding addresses */ function _initialize( address[] memory _rewardTokenAddresses, address[] memory _assets, address[] memory _pTokens ) internal { rewardTokenAddresses = _rewardTokenAddresses; uint256 assetCount = _assets.length; require(assetCount == _pTokens.length, "Invalid input arrays"); for (uint256 i = 0; i < assetCount; ++i) { _setPTokenAddress(_assets[i], _pTokens[i]); } } /** * @notice Collect accumulated reward token and send to Vault. */ function collectRewardTokens() external virtual onlyHarvester nonReentrant { _collectRewardTokens(); } /** * @dev Default implementation that transfers reward tokens to the Harvester * Implementing strategies need to add custom logic to collect the rewards. */ function _collectRewardTokens() internal virtual { uint256 rewardTokenCount = rewardTokenAddresses.length; for (uint256 i = 0; i < rewardTokenCount; ++i) { IERC20 rewardToken = IERC20(rewardTokenAddresses[i]); uint256 balance = rewardToken.balanceOf(address(this)); if (balance > 0) { emit RewardTokenCollected( harvesterAddress, address(rewardToken), balance ); rewardToken.safeTransfer(harvesterAddress, balance); } } } /** * @dev Verifies that the caller is the Vault. */ modifier onlyVault() { require(msg.sender == vaultAddress, "Caller is not the Vault"); _; } /** * @dev Verifies that the caller is the Harvester. */ modifier onlyHarvester() { require(msg.sender == harvesterAddress, "Caller is not the Harvester"); _; } /** * @dev Verifies that the caller is the Vault or Governor. */ modifier onlyVaultOrGovernor() { require( msg.sender == vaultAddress || msg.sender == governor(), "Caller is not the Vault or Governor" ); _; } /** * @dev Verifies that the caller is the Vault, Governor, or Strategist. */ modifier onlyVaultOrGovernorOrStrategist() { require( msg.sender == vaultAddress || msg.sender == governor() || msg.sender == IVault(vaultAddress).strategistAddr(), "Caller is not the Vault, Governor, or Strategist" ); _; } /** * @notice Set the reward token addresses. Any old addresses will be overwritten. * @param _rewardTokenAddresses Array of reward token addresses */ function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses) external onlyGovernor { uint256 rewardTokenCount = _rewardTokenAddresses.length; for (uint256 i = 0; i < rewardTokenCount; ++i) { require( _rewardTokenAddresses[i] != address(0), "Can not set an empty address as a reward token" ); } emit RewardTokenAddressesUpdated( rewardTokenAddresses, _rewardTokenAddresses ); rewardTokenAddresses = _rewardTokenAddresses; } /** * @notice Get the reward token addresses. * @return address[] the reward token addresses. */ function getRewardTokenAddresses() external view returns (address[] memory) { return rewardTokenAddresses; } /** * @notice Provide support for asset by passing its pToken address. * This method can only be called by the system Governor * @param _asset Address for the asset * @param _pToken Address for the corresponding platform token */ function setPTokenAddress(address _asset, address _pToken) external virtual onlyGovernor { _setPTokenAddress(_asset, _pToken); } /** * @notice Remove a supported asset by passing its index. * This method can only be called by the system Governor * @param _assetIndex Index of the asset to be removed */ function removePToken(uint256 _assetIndex) external virtual onlyGovernor { require(_assetIndex < assetsMapped.length, "Invalid index"); address asset = assetsMapped[_assetIndex]; address pToken = assetToPToken[asset]; if (_assetIndex < assetsMapped.length - 1) { assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1]; } assetsMapped.pop(); assetToPToken[asset] = address(0); emit PTokenRemoved(asset, pToken); } /** * @notice Provide support for asset by passing its pToken address. * Add to internal mappings and execute the platform specific, * abstract method `_abstractSetPToken` * @param _asset Address for the asset * @param _pToken Address for the corresponding platform token */ function _setPTokenAddress(address _asset, address _pToken) internal { require(assetToPToken[_asset] == address(0), "pToken already set"); require( _asset != address(0) && _pToken != address(0), "Invalid addresses" ); assetToPToken[_asset] = _pToken; assetsMapped.push(_asset); emit PTokenAdded(_asset, _pToken); _abstractSetPToken(_asset, _pToken); } /** * @notice Transfer token to governor. Intended for recovering tokens stuck in * strategy contracts, i.e. mistaken sends. * @param _asset Address for the asset * @param _amount Amount of the asset to transfer */ function transferToken(address _asset, uint256 _amount) public onlyGovernor { require(!supportsAsset(_asset), "Cannot transfer supported asset"); IERC20(_asset).safeTransfer(governor(), _amount); } /** * @notice Set the Harvester contract that can collect rewards. * @param _harvesterAddress Address of the harvester contract. */ function setHarvesterAddress(address _harvesterAddress) external onlyGovernor { emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress); harvesterAddress = _harvesterAddress; } /*************************************** Abstract ****************************************/ function _abstractSetPToken(address _asset, address _pToken) internal virtual; function safeApproveAllTokens() external virtual; /** * @notice Deposit an amount of assets into the platform * @param _asset Address for the asset * @param _amount Units of asset to deposit */ function deposit(address _asset, uint256 _amount) external virtual; /** * @notice Deposit all supported assets in this strategy contract to the platform */ function depositAll() external virtual; /** * @notice Withdraw an `amount` of assets from the platform and * send to the `_recipient`. * @param _recipient Address to which the asset should be sent * @param _asset Address of the asset * @param _amount Units of asset to withdraw */ function withdraw( address _recipient, address _asset, uint256 _amount ) external virtual; /** * @notice Withdraw all supported assets from platform and * sends to the OToken's Vault. */ function withdrawAll() external virtual; /** * @notice Get the total asset value held in the platform. * This includes any interest that was generated since depositing. * @param _asset Address of the asset * @return balance Total value of the asset in the platform */ function checkBalance(address _asset) external view virtual returns (uint256 balance); /** * @notice Check if an asset is supported. * @param _asset Address of the asset * @return bool Whether asset is supported */ function supportsAsset(address _asset) public view virtual returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @dev Optional functions from the ERC20 standard. * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol * @author Origin Protocol Inc */ abstract contract InitializableERC20Detailed is IERC20 { // Storage gap to skip storage from prior to OUSD reset uint256[100] private _____gap; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of * these values are immutable: they can only be set once during * construction. * @notice To avoid variable shadowing appended `Arg` after arguments name. */ function _initialize( string memory nameArg, string memory symbolArg, uint8 decimalsArg ) internal { _name = nameArg; _symbol = symbolArg; _decimals = decimalsArg; } /** * @notice Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @notice Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @notice Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol"; // Based on StableMath from Stability Labs Pty. Ltd. // https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol library StableMath { using SafeMath for uint256; /** * @dev Scaling unit for use in specific calculations, * where 1 * 10**18, or 1e18 represents a unit '1' */ uint256 private constant FULL_SCALE = 1e18; /*************************************** Helpers ****************************************/ /** * @dev Adjust the scale of an integer * @param to Decimals to scale to * @param from Decimals to scale from */ function scaleBy( uint256 x, uint256 to, uint256 from ) internal pure returns (uint256) { if (to > from) { x = x.mul(10**(to - from)); } else if (to < from) { // slither-disable-next-line divide-before-multiply x = x.div(10**(from - to)); } return x; } /*************************************** Precise Arithmetic ****************************************/ /** * @dev Multiplies two precise units, and then truncates by the full scale * @param x Left hand input to multiplication * @param y Right hand input to multiplication * @return Result after multiplying the two inputs and then dividing by the shared * scale unit */ function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) { return mulTruncateScale(x, y, FULL_SCALE); } /** * @dev Multiplies two precise units, and then truncates by the given scale. For example, * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18 * @param x Left hand input to multiplication * @param y Right hand input to multiplication * @param scale Scale unit * @return Result after multiplying the two inputs and then dividing by the shared * scale unit */ function mulTruncateScale( uint256 x, uint256 y, uint256 scale ) internal pure returns (uint256) { // e.g. assume scale = fullScale // z = 10e18 * 9e17 = 9e36 uint256 z = x.mul(y); // return 9e36 / 1e18 = 9e18 return z.div(scale); } /** * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result * @param x Left hand input to multiplication * @param y Right hand input to multiplication * @return Result after multiplying the two inputs and then dividing by the shared * scale unit, rounded up to the closest base unit. */ function mulTruncateCeil(uint256 x, uint256 y) internal pure returns (uint256) { // e.g. 8e17 * 17268172638 = 138145381104e17 uint256 scaled = x.mul(y); // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17 uint256 ceil = scaled.add(FULL_SCALE.sub(1)); // e.g. 13814538111.399...e18 / 1e18 = 13814538111 return ceil.div(FULL_SCALE); } /** * @dev Precisely divides two units, by first scaling the left hand operand. Useful * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17) * @param x Left hand input to division * @param y Right hand input to division * @return Result after multiplying the left operand by the scale, and * executing the division on the right hand input. */ function divPrecisely(uint256 x, uint256 y) internal pure returns (uint256) { // e.g. 8e18 * 1e18 = 8e36 uint256 z = x.mul(FULL_SCALE); // e.g. 8e36 / 10e18 = 8e17 return z.div(y); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title OToken VaultStorage contract * @notice The VaultStorage contract defines the storage for the Vault contracts * @author Origin Protocol Inc */ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IStrategy } from "../interfaces/IStrategy.sol"; import { Governable } from "../governance/Governable.sol"; import { OUSD } from "../token/OUSD.sol"; import { Initializable } from "../utils/Initializable.sol"; import "../utils/Helpers.sol"; contract VaultStorage is Initializable, Governable { using SafeERC20 for IERC20; event AssetSupported(address _asset); event AssetRemoved(address _asset); event AssetDefaultStrategyUpdated(address _asset, address _strategy); event AssetAllocated(address _asset, address _strategy, uint256 _amount); event StrategyApproved(address _addr); event StrategyRemoved(address _addr); event Mint(address _addr, uint256 _value); event Redeem(address _addr, uint256 _value); event CapitalPaused(); event CapitalUnpaused(); event RebasePaused(); event RebaseUnpaused(); event VaultBufferUpdated(uint256 _vaultBuffer); event OusdMetaStrategyUpdated(address _ousdMetaStrategy); event RedeemFeeUpdated(uint256 _redeemFeeBps); event PriceProviderUpdated(address _priceProvider); event AllocateThresholdUpdated(uint256 _threshold); event RebaseThresholdUpdated(uint256 _threshold); event StrategistUpdated(address _address); event MaxSupplyDiffChanged(uint256 maxSupplyDiff); event YieldDistribution(address _to, uint256 _yield, uint256 _fee); event TrusteeFeeBpsChanged(uint256 _basis); event TrusteeAddressChanged(address _address); event NetOusdMintForStrategyThresholdChanged(uint256 _threshold); event SwapperChanged(address _address); event SwapAllowedUndervalueChanged(uint256 _basis); event SwapSlippageChanged(address _asset, uint256 _basis); event Swapped( address indexed _fromAsset, address indexed _toAsset, uint256 _fromAssetAmount, uint256 _toAssetAmount ); event DripperChanged(address indexed _dripper); event StrategyAddedToMintWhitelist(address indexed strategy); event StrategyRemovedFromMintWhitelist(address indexed strategy); event WithdrawalRequested( address indexed _withdrawer, uint256 indexed _requestId, uint256 _amount, uint256 _queued ); event WithdrawalClaimed( address indexed _withdrawer, uint256 indexed _requestId, uint256 _amount ); event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable); // Assets supported by the Vault, i.e. Stablecoins enum UnitConversion { DECIMALS, GETEXCHANGERATE } // Changed to fit into a single storage slot so the decimals needs to be recached struct Asset { // Note: OETHVaultCore doesn't use `isSupported` when minting, // redeeming or checking balance of assets. bool isSupported; UnitConversion unitConversion; uint8 decimals; // Max allowed slippage from the Oracle price when swapping collateral assets in basis points. // For example 40 == 0.4% slippage uint16 allowedOracleSlippageBps; } /// @dev mapping of supported vault assets to their configuration // slither-disable-next-line uninitialized-state mapping(address => Asset) internal assets; /// @dev list of all assets supported by the vault. // slither-disable-next-line uninitialized-state address[] internal allAssets; // Strategies approved for use by the Vault struct Strategy { bool isSupported; uint256 _deprecated; // Deprecated storage slot } /// @dev mapping of strategy contracts to their configuration // slither-disable-next-line uninitialized-state mapping(address => Strategy) internal strategies; /// @dev list of all vault strategies address[] internal allStrategies; /// @notice Address of the Oracle price provider contract // slither-disable-next-line uninitialized-state address public priceProvider; /// @notice pause rebasing if true bool public rebasePaused = false; /// @notice pause operations that change the OToken supply. /// eg mint, redeem, allocate, mint/burn for strategy bool public capitalPaused = true; /// @notice Redemption fee in basis points. eg 50 = 0.5% uint256 public redeemFeeBps; /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18. uint256 public vaultBuffer; /// @notice OToken mints over this amount automatically allocate funds. 18 decimals. uint256 public autoAllocateThreshold; /// @notice OToken mints over this amount automatically rebase. 18 decimals. uint256 public rebaseThreshold; /// @dev Address of the OToken token. eg OUSD or OETH. // slither-disable-next-line uninitialized-state OUSD internal oUSD; //keccak256("OUSD.vault.governor.admin.impl"); bytes32 constant adminImplPosition = 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9; // Address of the contract responsible for post rebase syncs with AMMs address private _deprecated_rebaseHooksAddr = address(0); // Deprecated: Address of Uniswap // slither-disable-next-line constable-states address private _deprecated_uniswapAddr = address(0); /// @notice Address of the Strategist address public strategistAddr = address(0); /// @notice Mapping of asset address to the Strategy that they should automatically // be allocated to // slither-disable-next-line uninitialized-state mapping(address => address) public assetDefaultStrategies; /// @notice Max difference between total supply and total value of assets. 18 decimals. // slither-disable-next-line uninitialized-state uint256 public maxSupplyDiff; /// @notice Trustee contract that can collect a percentage of yield address public trusteeAddress; /// @notice Amount of yield collected in basis points. eg 2000 = 20% uint256 public trusteeFeeBps; /// @dev Deprecated: Tokens that should be swapped for stablecoins address[] private _deprecated_swapTokens; uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18; /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral // slither-disable-start constable-states // slither-disable-next-line uninitialized-state address public ousdMetaStrategy; /// @notice How much OTokens are currently minted by the strategy // slither-disable-next-line uninitialized-state int256 public netOusdMintedForStrategy; /// @notice How much net total OTokens are allowed to be minted by all strategies // slither-disable-next-line uninitialized-state uint256 public netOusdMintForStrategyThreshold; // slither-disable-end constable-states uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18; uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18; /// @notice Collateral swap configuration. /// @dev is packed into a single storage slot to save gas. struct SwapConfig { // Contract that swaps the vault's collateral assets address swapper; // Max allowed percentage the total value can drop below the total supply in basis points. // For example 100 == 1% uint16 allowedUndervalueBps; } SwapConfig internal swapConfig = SwapConfig(address(0), 0); // List of strategies that can mint oTokens directly // Used in OETHBaseVaultCore // slither-disable-next-line uninitialized-state mapping(address => bool) public isMintWhitelistedStrategy; /// @notice Address of the Dripper contract that streams harvested rewards to the Vault /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract. // slither-disable-start constable-states // slither-disable-next-line uninitialized-state address public dripper; // slither-disable-end constable-states /// Withdrawal Queue Storage ///// struct WithdrawalQueueMetadata { // cumulative total of all withdrawal requests included the ones that have already been claimed uint128 queued; // cumulative total of all the requests that can be claimed including the ones that have already been claimed uint128 claimable; // total of all the requests that have been claimed uint128 claimed; // index of the next withdrawal request starting at 0 uint128 nextWithdrawalIndex; } /// @notice Global metadata for the withdrawal queue including: /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed /// claimed - total of all the requests that have been claimed /// nextWithdrawalIndex - index of the next withdrawal request starting at 0 // slither-disable-next-line uninitialized-state WithdrawalQueueMetadata public withdrawalQueueMetadata; struct WithdrawalRequest { address withdrawer; bool claimed; uint40 timestamp; // timestamp of the withdrawal request // Amount of oTokens to redeem. eg OETH uint128 amount; // cumulative total of all withdrawal requests including this one. // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount. uint128 queued; } /// @notice Mapping of withdrawal request indices to the user withdrawal request data mapping(uint256 => WithdrawalRequest) public withdrawalRequests; // For future use uint256[45] private __gap; /** * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it * @param newImpl address of the implementation */ function setAdminImpl(address newImpl) external onlyGovernor { require( Address.isContract(newImpl), "new implementation is not a contract" ); bytes32 position = adminImplPosition; // solhint-disable-next-line no-inline-assembly assembly { sstore(position, newImpl) } } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 200 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"components":[{"internalType":"address","name":"platformAddress","type":"address"},{"internalType":"address","name":"vaultAddress","type":"address"}],"internalType":"struct InitializableAbstractStrategy.BaseStrategyConfig","name":"_baseConfig","type":"tuple"},{"internalType":"address","name":"_wethAddress","type":"address"},{"internalType":"address","name":"_ssvToken","type":"address"},{"internalType":"address","name":"_ssvNetwork","type":"address"},{"internalType":"uint256","name":"_maxValidators","type":"uint256"},{"internalType":"address","name":"_feeAccumulator","type":"address"},{"internalType":"address","name":"_beaconChainDepositContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AccountingConsensusRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"noOfValidators","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingValidators","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wethSentToVault","type":"uint256"}],"name":"AccountingFullyWithdrawnValidator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"validatorsDelta","type":"int256"},{"indexed":false,"internalType":"int256","name":"consensusRewardsDelta","type":"int256"},{"indexed":false,"internalType":"uint256","name":"wethToVault","type":"uint256"}],"name":"AccountingManuallyFixed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"remainingValidators","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wethSentToVault","type":"uint256"}],"name":"AccountingValidatorSlashed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_asset","type":"address"},{"indexed":false,"internalType":"address","name":"_pToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"pubKeyHash","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"pubKey","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ETHStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"end","type":"uint256"}],"name":"FuseIntervalUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousGovernor","type":"address"},{"indexed":true,"internalType":"address","name":"newGovernor","type":"address"}],"name":"GovernorshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldHarvesterAddress","type":"address"},{"indexed":false,"internalType":"address","name":"_newHarvesterAddress","type":"address"}],"name":"HarvesterAddressesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_asset","type":"address"},{"indexed":false,"internalType":"address","name":"_pToken","type":"address"}],"name":"PTokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_asset","type":"address"},{"indexed":false,"internalType":"address","name":"_pToken","type":"address"}],"name":"PTokenRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousGovernor","type":"address"},{"indexed":true,"internalType":"address","name":"newGovernor","type":"address"}],"name":"PendingGovernorshipTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAddress","type":"address"}],"name":"RegistratorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_oldAddresses","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"_newAddresses","type":"address[]"}],"name":"RewardTokenAddressesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardTokenCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"pubKeyHash","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"pubKey","type":"bytes"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"}],"name":"SSVValidatorExitCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"pubKeyHash","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"pubKey","type":"bytes"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"}],"name":"SSVValidatorExitInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"pubKeyHash","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"pubKey","type":"bytes"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"}],"name":"SSVValidatorRegistered","type":"event"},{"anonymous":false,"inputs":[],"name":"StakeETHTallyReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"StakeETHThresholdChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAddress","type":"address"}],"name":"StakingMonitorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_asset","type":"address"},{"indexed":false,"internalType":"address","name":"_pToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"inputs":[],"name":"BEACON_CHAIN_DEPOSIT_CONTRACT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_ACCUMULATOR_ADDRESS","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FULL_STAKE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_VALIDATORS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_FIX_ACCOUNTING_CADENCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SSV_NETWORK","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SSV_TOKEN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeDepositedValidators","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"assetToPToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"checkBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectRewardTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"consensusRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"uint256","name":"ssvAmount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct Cluster","name":"cluster","type":"tuple"}],"name":"depositSSV","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositedWethAccountedFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"doAccounting","outputs":[{"internalType":"bool","name":"accountingValid","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"}],"name":"exitSsvValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fuseIntervalEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fuseIntervalStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTokenAddresses","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvesterAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_rewardTokenAddresses","type":"address[]"},{"internalType":"address[]","name":"_assets","type":"address[]"},{"internalType":"address[]","name":"_pTokens","type":"address[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isGovernor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastFixAccountingBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"_validatorsDelta","type":"int256"},{"internalType":"int256","name":"_consensusRewardsDelta","type":"int256"},{"internalType":"uint256","name":"_ethToVaultAmount","type":"uint256"}],"name":"manuallyFixAccounting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"publicKeys","type":"bytes[]"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"bytes[]","name":"sharesData","type":"bytes[]"},{"internalType":"uint256","name":"ssvAmount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct Cluster","name":"cluster","type":"tuple"}],"name":"registerSsvValidators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetIndex","type":"uint256"}],"name":"removePToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct Cluster","name":"cluster","type":"tuple"}],"name":"removeSsvValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resetStakeETHTally","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokenAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"safeApproveAllTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fuseIntervalStart","type":"uint256"},{"internalType":"uint256","name":"_fuseIntervalEnd","type":"uint256"}],"name":"setFuseInterval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_harvesterAddress","type":"address"}],"name":"setHarvesterAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_pToken","type":"address"}],"name":"setPTokenAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setRegistrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_rewardTokenAddresses","type":"address[]"}],"name":"setRewardTokenAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setStakeETHThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setStakingMonitor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeETHTally","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakeETHThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes32","name":"depositDataRoot","type":"bytes32"}],"internalType":"struct ValidatorStakeData[]","name":"validators","type":"tuple[]"}],"name":"stakeEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingMonitor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"supportsAsset","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newGovernor","type":"address"}],"name":"transferGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"validatorRegistrator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"validatorsStates","outputs":[{"internalType":"enum ValidatorRegistrator.VALIDATOR_STATE","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6101a06040523480156200001257600080fd5b50604051620056cf380380620056cf833981016040819052620000359162000144565b86868860200151838787848484848462000055336200011460201b60201c565b600080516020620056af833981519152546040516001600160a01b03909116906000907fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a908290a36033805460ff191690556001600160601b0319606095861b811660805292851b831660a05290841b821660c05291831b811660e052610100919091528751821b811661012052602090970151811b8716610140529a8b1b86166101605250505050509190941b1661018052506200022b9350505050565b600080516020620056af83398151915255565b80516001600160a01b03811681146200013f57600080fd5b919050565b60008060008060008060008789036101008112156200016257600080fd5b60408112156200017157600080fd5b50604080519081016001600160401b0381118282101715620001a357634e487b7160e01b600052604160045260246000fd5b604052620001b18962000127565b8152620001c160208a0162000127565b60208201529650620001d66040890162000127565b9550620001e66060890162000127565b9450620001f66080890162000127565b935060a088015192506200020d60c0890162000127565b91506200021d60e0890162000127565b905092959891949750929550565b60805160601c60a05160601c60c05160601c60e05160601c610100516101205160601c6101405160601c6101605160601c6101805160601c61529d62000412600039600081816103a001528181610b8701528181610d44015281816110bc01526136a6015260008181610490015281816110280152612ca701526000818161059301528181611162015281816120b10152818161222a01528181612e93015261312e01526000610b53015260008181610757015261197f01526000818161088601528181610dcc01528181611fdf0152818161227a0152818161262501528181613c960152613ef00152600081816108da01528181610d6c01528181610ea201528181610ff8015281816110e4015281816114b301528181611ee201528181612c770152613055015260008181610aa80152611bf40152600081816103d2015281816109ab01528181610a2201528181610c96015281816111d70152818161167d015281816116e1015281816118b501528181611a7d0152818161219b0152818161224b0152818161259f0152818161265401528181612f08015281816131b901528181613261015281816137ab0152818161382c0152818161386e01528181613b3e01528181613c1001528181613cc501528181613e6a0152613f1f015261529d6000f3fe6080604052600436106103905760003560e01c80638456cb59116101dc578063b16b7d0b11610102578063d9f00ec7116100a0578063de5f62681161006f578063de5f626814610bbf578063e752923914610bd4578063ee7afe2d14610bea578063f6ca71b014610bff57600080fd5b8063d9f00ec714610b21578063dbe55e5614610b41578063dd505df614610b75578063de34d71314610ba957600080fd5b8063cceab750116100dc578063cceab75014610a96578063d059f6ef14610aca578063d38bfff414610ae1578063d9caed1214610b0157600080fd5b8063b16b7d0b14610a44578063c2e1e3f414610a61578063c7af335214610a8157600080fd5b806396d538bb1161017a578063aa388af611610149578063aa388af61461098e578063ab12edf5146109db578063ad1728cb146109fb578063ad5c464814610a1057600080fd5b806396d538bb146108fc5780639da0e4621461091c578063a3b81e7314610959578063a4f98af41461097957600080fd5b80638d7c0e46116101b65780638d7c0e46146108545780639092c31c146108745780639136616a146108a857806391649751146108c857600080fd5b80638456cb5914610805578063853828b61461081a57806387bae8671461082f57600080fd5b80635a063f63116102c15780636e811d381161025f5780637260f8261161022e5780637260f826146107995780637b2d9b2c146107b95780637b8962f7146107d9578063842f5c46146107ef57600080fd5b80636e811d38146107055780636ef3879514610725578063714897df1461074557806371a735f31461077957600080fd5b80635f5152261161029b5780635f5152261461069957806363092383146106b957806366e3667e146106cf57806367c7066c146106e557600080fd5b80635a063f631461064b5780635c975abb146106605780635d36b1901461068457600080fd5b80633c8649591161032e57806347e7ef241161030857806347e7ef24146105d5578063484be812146105f55780635205c3801461060b57806359b80c0a1461062b57600080fd5b80633c8649591461055d578063430bf08a14610581578063435356d1146105b557600080fd5b80630fc3b4c41161036a5780630fc3b4c4146104d25780631072cbea1461050857806313cf69dd1461052857806322495dc81461053d57600080fd5b80630c340a241461044c5780630df1ecfd1461047e5780630ed57b3a146104b257600080fd5b3661044757336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806103f45750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b6104455760405162461bcd60e51b815260206004820152601e60248201527f457468206e6f742066726f6d20616c6c6f77656420636f6e747261637473000060448201526064015b60405180910390fd5b005b600080fd5b34801561045857600080fd5b50610461610c21565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561048a57600080fd5b506104617f000000000000000000000000000000000000000000000000000000000000000081565b3480156104be57600080fd5b506104456104cd36600461457b565b610c3e565b3480156104de57600080fd5b506104616104ed366004614541565b609f602052600090815260409020546001600160a01b031681565b34801561051457600080fd5b506104456105233660046145f5565b610c70565b34801561053457600080fd5b50610445610d2d565b34801561054957600080fd5b5061044561055836600461479d565b610dca565b34801561056957600080fd5b5061057360695481565b604051908152602001610475565b34801561058d57600080fd5b506104617f000000000000000000000000000000000000000000000000000000000000000081565b3480156105c157600080fd5b506104456105d0366004614662565b610f14565b3480156105e157600080fd5b506104456105f03660046145f5565b611157565b34801561060157600080fd5b50610573606a5481565b34801561061757600080fd5b50610445610626366004614873565b611252565b34801561063757600080fd5b506104456106463660046146e9565b6112b1565b34801561065757600080fd5b50610445611534565b34801561066c57600080fd5b5060335460ff165b6040519015158152602001610475565b34801561069057600080fd5b506104456115d3565b3480156106a557600080fd5b506105736106b4366004614541565b611679565b3480156106c557600080fd5b50610573611c2081565b3480156106db57600080fd5b5061057360345481565b3480156106f157600080fd5b5060a354610461906001600160a01b031681565b34801561071157600080fd5b50610445610720366004614541565b61178a565b34801561073157600080fd5b50610445610740366004614621565b611800565b34801561075157600080fd5b506105737f000000000000000000000000000000000000000000000000000000000000000081565b34801561078557600080fd5b506104456107943660046148f7565b611dca565b3480156107a557600080fd5b50603654610461906001600160a01b031681565b3480156107c557600080fd5b506104616107d4366004614873565b611fb3565b3480156107e557600080fd5b5061057360375481565b3480156107fb57600080fd5b5061057360685481565b34801561081157600080fd5b50610445611fdd565b34801561082657600080fd5b506104456120a6565b34801561083b57600080fd5b506033546104619061010090046001600160a01b031681565b34801561086057600080fd5b5061044561086f366004614978565b612278565b34801561088057600080fd5b506104617f000000000000000000000000000000000000000000000000000000000000000081565b3480156108b457600080fd5b506104456108c3366004614873565b61277a565b3480156108d457600080fd5b506104617f000000000000000000000000000000000000000000000000000000000000000081565b34801561090857600080fd5b50610445610917366004614621565b612946565b34801561092857600080fd5b5061094c610937366004614873565b60356020526000908152604090205460ff1681565b6040516104759190614e5b565b34801561096557600080fd5b50610445610974366004614541565b612a66565b34801561098557600080fd5b50610674612ad4565b34801561099a57600080fd5b506106746109a9366004614541565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b3480156109e757600080fd5b506104456109f63660046149bd565b612b74565b348015610a0757600080fd5b50610445612c60565b348015610a1c57600080fd5b506104617f000000000000000000000000000000000000000000000000000000000000000081565b348015610a5057600080fd5b506105736801bc16d674ec80000081565b348015610a6d57600080fd5b50610445610a7c366004614541565b612d26565b348015610a8d57600080fd5b50610674612db3565b348015610aa257600080fd5b506104617f000000000000000000000000000000000000000000000000000000000000000081565b348015610ad657600080fd5b506105736101075481565b348015610aed57600080fd5b50610445610afc366004614541565b612de4565b348015610b0d57600080fd5b50610445610b1c3660046145b4565b612e88565b348015610b2d57600080fd5b50610445610b3c36600461488c565b612f62565b348015610b4d57600080fd5b506104617f000000000000000000000000000000000000000000000000000000000000000081565b348015610b8157600080fd5b506104617f000000000000000000000000000000000000000000000000000000000000000081565b348015610bb557600080fd5b5061057360385481565b348015610bcb57600080fd5b50610445613123565b348015610be057600080fd5b50610573606b5481565b348015610bf657600080fd5b50610445613290565b348015610c0b57600080fd5b50610c1461331a565b6040516104759190614c39565b6000610c396000805160206152488339815191525490565b905090565b610c46612db3565b610c625760405162461bcd60e51b815260040161043c90614ecd565b610c6c828261337c565b5050565b610c78612db3565b610c945760405162461bcd60e51b815260040161043c90614ecd565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b039081169083161415610d115760405162461bcd60e51b815260206004820152601f60248201527f43616e6e6f74207472616e7366657220737570706f7274656420617373657400604482015260640161043c565b610c6c610d1c610c21565b6001600160a01b03841690836134db565b6040516336f370b360e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063dbcdc2cc90602401600060405180830381600087803b158015610db057600080fd5b505af1158015610dc4573d6000803e3d6000fd5b50505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663570d8e1d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e2357600080fd5b505afa158015610e37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5b919061455e565b6001600160a01b0316336001600160a01b031614610e8b5760405162461bcd60e51b815260040161043c90614fb8565b60405163bc26e7e560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063bc26e7e590610edd903090879087908790600401614b89565b600060405180830381600087803b158015610ef757600080fd5b505af1158015610f0b573d6000803e3d6000fd5b50505050505050565b610f1c612db3565b610f385760405162461bcd60e51b815260040161043c90614ecd565b600054610100900460ff1680610f51575060005460ff16155b610fb45760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161043c565b600054610100900460ff16158015610fd6576000805461ffff19166101011790555b610fe1848484613532565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b390604401602060405180830381600087803b15801561106c57600080fd5b505af1158015611080573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a49190614856565b506040516336f370b360e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063dbcdc2cc90602401600060405180830381600087803b15801561112857600080fd5b505af115801561113c573d6000803e3d6000fd5b505050508015610dc4576000805461ff001916905550505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461119f5760405162461bcd60e51b815260040161043c90614e96565b600080516020615228833981519152805460028114156111d15760405162461bcd60e51b815260040161043c90614f90565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146112265760405162461bcd60e51b815260040161043c90614f04565b82610107600082825461123991906150df565b90915550611249905084846135ed565b50600190555050565b61125a612db3565b6112765760405162461bcd60e51b815260040161043c90614ecd565b60378190556040518181527fe26b067424903962f951f568e52ec9a3bbe1589526ea54a4e69ca6eaae1a4c779060200160405180910390a150565b60335461010090046001600160a01b031633146112e05760405162461bcd60e51b815260040161043c90614f59565b60335460ff16156113035760405162461bcd60e51b815260040161043c90614f2f565b8683146113525760405162461bcd60e51b815260206004820152601a60248201527f5075626b65792073686172657344617461206d69736d61746368000000000000604482015260640161043c565b60008060005b8981101561149b578a8a82818110611372576113726151d8565b90506020028101906113849190614fef565b604051611392929190614b5d565b6040805191829003909120600081815260356020529182205490945060ff1692508260048111156113c5576113c56151ac565b146114125760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f7220616c7265616479207265676973746572656400000000604482015260640161043c565b6000838152603560205260409020805460ff19166001179055827facd38e900350661e325d592c959664c0000a306efb2004e7dc283f44e0ea04238c8c8481811061145f5761145f6151d8565b90506020028101906114719190614fef565b8c8c6040516114839493929190614d80565b60405180910390a26114948161517b565b9050611358565b506040516322f18bf560e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906322f18bf5906114f6908d908d908d908d908d908d908d908d90600401614d1e565b600060405180830381600087803b15801561151057600080fd5b505af1158015611524573d6000803e3d6000fd5b5050505050505050505050505050565b60a3546001600160a01b0316331461158e5760405162461bcd60e51b815260206004820152601b60248201527f43616c6c6572206973206e6f7420746865204861727665737465720000000000604482015260640161043c565b600080516020615228833981519152805460028114156115c05760405162461bcd60e51b815260040161043c90614f90565b600282556115cc61367f565b5060019055565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461166e5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b606482015260840161043c565b611677336138c4565b565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316146116cc5760405162461bcd60e51b815260040161043c90614f04565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b15801561172b57600080fd5b505afa15801561173f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176391906149a4565b6801bc16d674ec80000060345461177a9190615119565b61178491906150df565b92915050565b611792612db3565b6117ae5760405162461bcd60e51b815260040161043c90614ecd565b60338054610100600160a81b0319166101006001600160a01b038416908102919091179091556040517f83f29c79feb71f8fba9d0fbc4ba5f0982a28b6b1e868b3fc50e6400d100bca0f90600090a250565b60335461010090046001600160a01b0316331461182f5760405162461bcd60e51b815260040161043c90614f59565b60335460ff16156118525760405162461bcd60e51b815260040161043c90614f2f565b600080516020615228833981519152805460028114156118845760405162461bcd60e51b815260040161043c90614f90565b60028255600061189d6801bc16d674ec80000085615119565b6040516370a0823160e01b81523060048201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b1580156118ff57600080fd5b505afa158015611913573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061193791906149a4565b81111561197a5760405162461bcd60e51b8152602060048201526011602482015270092dce6eaccccd2c6d2cadce840ae8aa89607b1b604482015260640161043c565b6034547f0000000000000000000000000000000000000000000000000000000000000000906119aa9086906150df565b11156119f15760405162461bcd60e51b815260206004820152601660248201527513585e081d985b1a59185d1bdc9cc81c995858da195960521b604482015260640161043c565b60375481603854611a0291906150df565b1115611a505760405162461bcd60e51b815260206004820152601a60248201527f5374616b696e6720455448206f766572207468726573686f6c64000000000000604482015260640161043c565b8060386000828254611a6291906150df565b9091555050604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611ac957600080fd5b505af1158015611add573d6000803e3d6000fd5b50505050611aea81613985565b60408051600160f81b60208201526000602182018190526bffffffffffffffffffffffff193060601b16602c8301529101604051602081830303815290604052905060005b85811015611da3576000878783818110611b4b57611b4b6151d8565b9050602002810190611b5d9190615035565b611b679080614fef565b604051611b75929190614b5d565b6040519081900390209050600160008281526035602052604090205460ff166004811115611ba557611ba56151ac565b14611bf25760405162461bcd60e51b815260206004820152601860248201527f56616c696461746f72206e6f7420726567697374657265640000000000000000604482015260640161043c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663228951186801bc16d674ec8000008a8a86818110611c3d57611c3d6151d8565b9050602002810190611c4f9190615035565b611c599080614fef565b878d8d89818110611c6c57611c6c6151d8565b9050602002810190611c7e9190615035565b611c8c906020810190614fef565b8f8f8b818110611c9e57611c9e6151d8565b9050602002810190611cb09190615035565b604001356040518863ffffffff1660e01b8152600401611cd596959493929190614de8565b6000604051808303818588803b158015611cee57600080fd5b505af1158015611d02573d6000803e3d6000fd5b5050506000838152603560205260409020805460ff19166002179055508190507f958934bb53d6b4dc911b6173e586864efbc8076684a31f752c53d5778340b37f898985818110611d5557611d556151d8565b9050602002810190611d679190615035565b611d719080614fef565b6801bc16d674ec800000604051611d8a93929190614e37565b60405180910390a250611d9c8161517b565b9050611b2f565b508585905060346000828254611db991906150df565b909155505060019093555050505050565b60335461010090046001600160a01b03163314611df95760405162461bcd60e51b815260040161043c90614f59565b60335460ff1615611e1c5760405162461bcd60e51b815260040161043c90614f2f565b60008585604051611e2e929190614b5d565b604080519182900390912060008181526035602052919091205490915060ff166003816004811115611e6257611e626151ac565b1480611e7f57506001816004811115611e7d57611e7d6151ac565b145b611ecb5760405162461bcd60e51b815260206004820152601d60248201527f56616c696461746f72206e6f742072656764206f722065786974696e67000000604482015260640161043c565b6040516312b3fc1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906312b3fc1990611f1f908a908a908a908a908a90600401614da7565b600060405180830381600087803b158015611f3957600080fd5b505af1158015611f4d573d6000803e3d6000fd5b50505060008381526035602052604090819020805460ff19166004179055518391507f6aecca20726a17c1b81989b2fd09dfdf636bae9e564d4066ca18df62dc1f3dc290611fa2908a908a908a908a90614d80565b60405180910390a250505050505050565b60a48181548110611fc357600080fd5b6000918252602090912001546001600160a01b0316905081565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663570d8e1d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561203657600080fd5b505afa15801561204a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206e919061455e565b6001600160a01b0316336001600160a01b03161461209e5760405162461bcd60e51b815260040161043c90614fb8565b6116776139b2565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806120f557506120e0610c21565b6001600160a01b0316336001600160a01b0316145b61214d5760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f7420746865205661756c74206f7220476f7665726044820152623737b960e91b606482015260840161043c565b6000805160206152288339815191528054600281141561217f5760405162461bcd60e51b815260040161043c90614f90565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b1580156121e557600080fd5b505afa1580156121f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221d91906149a4565b90508015612270576122707f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083613a27565b505060019055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663570d8e1d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156122d157600080fd5b505afa1580156122e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612309919061455e565b6001600160a01b0316336001600160a01b0316146123395760405162461bcd60e51b815260040161043c90614fb8565b60335460ff166123825760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015260640161043c565b600080516020615228833981519152805460028114156123b45760405162461bcd60e51b815260040161043c90614f90565b6002825543611c20606b546123c991906150df565b106124165760405162461bcd60e51b815260206004820152601e60248201527f466978206163636f756e74696e672063616c6c656420746f6f20736f6f6e0000604482015260640161043c565b6002198512158015612429575060038513155b80156124435750600085603454612440919061509e565b12155b61248f5760405162461bcd60e51b815260206004820152601760248201527f496e76616c69642076616c696461746f727344656c7461000000000000000000604482015260640161043c565b6811ff6cf0fd15afffff1984121580156124b257506811ff6cf0fd15b000008413155b80156124cc57506000846068546124c9919061509e565b12155b6125185760405162461bcd60e51b815260206004820152601d60248201527f496e76616c696420636f6e73656e7375735265776172647344656c7461000000604482015260640161043c565b68053444835ec58000008311156125715760405162461bcd60e51b815260206004820152601960248201527f496e76616c69642077657468546f5661756c74416d6f756e7400000000000000604482015260640161043c565b8460345461257f919061509e565b60345560685461259090859061509e565b60685543606b5582156126de577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b1580156125f857600080fd5b505af115801561260c573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018890527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb92506044019050602060405180830381600087803b15801561269c57600080fd5b505af11580156126b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d49190614856565b506126de83613b25565b60408051868152602081018690529081018490527f80d022717ea022455c5886b8dd8a29c037570aae58aeb4d7b136d7a10ec2e4319060600160405180910390a16127296000613b8d565b6127685760405162461bcd60e51b815260206004820152601060248201526f233ab9b29039ba34b63610313637bbb760811b604482015260640161043c565b612770614013565b5060019055505050565b612782612db3565b61279e5760405162461bcd60e51b815260040161043c90614ecd565b60a05481106127df5760405162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840d2dcc8caf609b1b604482015260640161043c565b600060a082815481106127f4576127f46151d8565b60009182526020808320909101546001600160a01b03908116808452609f90925260409092205460a0549193509091169061283190600190615138565b8310156128b35760a0805461284890600190615138565b81548110612858576128586151d8565b60009182526020909120015460a080546001600160a01b039092169185908110612884576128846151d8565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b60a08054806128c4576128c46151c2565b60008281526020808220600019908401810180546001600160a01b031990811690915593019093556001600160a01b03858116808352609f855260409283902080549094169093559051908416815290917f16b7600acff27e39a8a96056b3d533045298de927507f5c1d97e4accde60488c91015b60405180910390a2505050565b61294e612db3565b61296a5760405162461bcd60e51b815260040161043c90614ecd565b8060005b81811015612a1d57600084848381811061298a5761298a6151d8565b905060200201602081019061299f9190614541565b6001600160a01b03161415612a0d5760405162461bcd60e51b815260206004820152602e60248201527f43616e206e6f742073657420616e20656d70747920616464726573732061732060448201526d30903932bbb0b932103a37b5b2b760911b606482015260840161043c565b612a168161517b565b905061296e565b507f04c0b9649497d316554306e53678d5f5f5dbc3a06f97dec13ff4cfe98b986bbc60a48484604051612a5293929190614c86565b60405180910390a1610dc460a4848461429a565b612a6e612db3565b612a8a5760405162461bcd60e51b815260040161043c90614ecd565b603680546001600160a01b0319166001600160a01b0383169081179091556040517f3329861a0008b3348767567d2405492b997abd79a088d0f2cef6b1a09a8e7ff790600090a250565b60335460009061010090046001600160a01b03163314612b065760405162461bcd60e51b815260040161043c90614f59565b60335460ff1615612b295760405162461bcd60e51b815260040161043c90614f2f565b60008051602061522883398151915280546002811415612b5b5760405162461bcd60e51b815260040161043c90614f90565b60028255612b696001613b8d565b925060018255505090565b612b7c612db3565b612b985760405162461bcd60e51b815260040161043c90614ecd565b8082108015612baf57506801bc16d674ec80000081105b8015612bcc5750673782dace9d900000612bc98383615138565b10155b612c185760405162461bcd60e51b815260206004820152601760248201527f496e636f7272656374206675736520696e74657276616c000000000000000000604482015260640161043c565b6069829055606a81905560408051838152602081018390527fcb8d24e46eb3c402bf344ee60a6576cba9ef2f59ea1af3b311520704924e901a91015b60405180910390a15050565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b390604401602060405180830381600087803b158015612ceb57600080fd5b505af1158015612cff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d239190614856565b50565b612d2e612db3565b612d4a5760405162461bcd60e51b815260040161043c90614ecd565b60a354604080516001600160a01b03928316815291831660208301527fe48386b84419f4d36e0f96c10cc3510b6fb1a33795620c5098b22472bbe90796910160405180910390a160a380546001600160a01b0319166001600160a01b0392909216919091179055565b6000612dcb6000805160206152488339815191525490565b6001600160a01b0316336001600160a01b031614905090565b612dec612db3565b612e085760405162461bcd60e51b815260040161043c90614ecd565b612e30817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b0316612e506000805160206152488339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612ed05760405162461bcd60e51b815260040161043c90614e96565b60008051602061522883398151915280546002811415612f025760405162461bcd60e51b815260040161043c90614f90565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031614612f575760405162461bcd60e51b815260040161043c90614f04565b612770858585613a27565b60335461010090046001600160a01b03163314612f915760405162461bcd60e51b815260040161043c90614f59565b60335460ff1615612fb45760405162461bcd60e51b815260040161043c90614f2f565b60008484604051612fc6929190614b5d565b604080519182900390912060008181526035602052919091205490915060ff166002816004811115612ffa57612ffa6151ac565b1461303e5760405162461bcd60e51b815260206004820152601460248201527315985b1a59185d1bdc881b9bdd081cdd185ad95960621b604482015260640161043c565b604051633877322b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690633877322b90613090908990899089908990600401614d80565b600060405180830381600087803b1580156130aa57600080fd5b505af11580156130be573d6000803e3d6000fd5b50505060008381526035602052604090819020805460ff19166003179055518391507f8c2e15303eb94e531acc988c2a01d1193bdaaa15eda7f16dda85316ed463578d90613113908990899089908990614d80565b60405180910390a2505050505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461316b5760405162461bcd60e51b815260040161043c90614e96565b6000805160206152288339815191528054600281141561319d5760405162461bcd60e51b815260040161043c90614f90565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b15801561320357600080fd5b505afa158015613217573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323b91906149a4565b90506000610107548261324e9190615138565b90508015613286576101078290556132867f0000000000000000000000000000000000000000000000000000000000000000826135ed565b5050600182555050565b6036546001600160a01b031633146132ea5760405162461bcd60e51b815260206004820152601960248201527f43616c6c6572206973206e6f7420746865204d6f6e69746f7200000000000000604482015260640161043c565b600060388190556040517fe765a88a37047c5d793dce22b9ceb5a0f5039d276da139b4c7d29613f341f1109190a1565b606060a480548060200260200160405190810160405280929190818152602001828054801561337257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613354575b5050505050905090565b6001600160a01b038281166000908152609f602052604090205416156133d95760405162461bcd60e51b81526020600482015260126024820152711c151bdad95b88185b1c9958591e481cd95d60721b604482015260640161043c565b6001600160a01b038216158015906133f957506001600160a01b03811615155b6134395760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642061646472657373657360781b604482015260640161043c565b6001600160a01b038281166000818152609f6020908152604080832080549587166001600160a01b0319968716811790915560a0805460018101825594527f78fdc8d422c49ced035a9edf18d00d3c6a8d81df210f3e5e448e045e77b41e8890930180549095168417909455925190815290917fef6485b84315f9b1483beffa32aae9a0596890395e3d7521f1c5fbb51790e765910160405180910390a25050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261352d90849061408d565b505050565b82516135459060a49060208601906142fd565b5081518151811461358f5760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420696e7075742061727261797360601b604482015260640161043c565b60005b818110156135e6576135d68482815181106135af576135af6151d8565b60200260200101518483815181106135c9576135c96151d8565b602002602001015161337c565b6135df8161517b565b9050613592565b5050505050565b600081116136365760405162461bcd60e51b81526020600482015260166024820152754d757374206465706f73697420736f6d657468696e6760501b604482015260640161043c565b6040805160008152602081018390526001600160a01b038416917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a25050565b60335460ff16156136a25760405162461bcd60e51b815260040161043c90614f2f565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e52253816040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156136ff57600080fd5b505af1158015613713573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373791906149a4565b905060006068548261374991906150df565b90508047101561379b5760405162461bcd60e51b815260206004820152601860248201527f496e73756666696369656e74206574682062616c616e63650000000000000000604482015260640161043c565b8015610c6c5760006068819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561380457600080fd5b505af1158015613818573d6000803e3d6000fd5b505060a35461385893506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169350169050836134db565b60a354604080516001600160a01b0392831681527f0000000000000000000000000000000000000000000000000000000000000000909216602083015281018290527ff6c07a063ed4e63808eb8da7112d46dbcd38de2b40a73dbcc9353c5a94c7235390606001612c54565b6001600160a01b03811661391a5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f722069732061646472657373283029000000000000604482015260640161043c565b806001600160a01b031661393a6000805160206152488339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3612d238160008051602061524883398151915255565b6000613994826101075461415f565b90508061010760008282546139a99190615138565b90915550505050565b60335460ff16156139d55760405162461bcd60e51b815260040161043c90614f2f565b6033805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613a0a3390565b6040516001600160a01b03909116815260200160405180910390a1565b60008111613a775760405162461bcd60e51b815260206004820152601760248201527f4d75737420776974686472617720736f6d657468696e67000000000000000000604482015260640161043c565b6001600160a01b038316613ac65760405162461bcd60e51b8152602060048201526016602482015275135d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b604482015260640161043c565b613acf81613985565b613ae36001600160a01b03831684836134db565b6040805160008152602081018390526001600160a01b038416917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989101612939565b6040805160008152602081018390526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b6398910160405180910390a250565b6000606854471015613ba25761178482614177565b600060685447613bb29190615138565b9050600191506801bc16d674ec8000008110613d96576000613bdd6801bc16d674ec800000836150f7565b90508060346000828254613bf19190615138565b9091555060009050613c0c826801bc16d674ec800000615119565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613c6957600080fd5b505af1158015613c7d573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb92506044019050602060405180830381600087803b158015613d0d57600080fd5b505af1158015613d21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d459190614856565b50613d4f81613b25565b60345460408051848152602081019290925281018290527fbe7040030ff7b347853214bf49820c6d455fedf58f3815f85c7bc5216993682b9060600160405180910390a150505b600060685447613da69190615138565b90506801bc16d674ec8000008110613df85760405162461bcd60e51b8152602060048201526015602482015274556e6578706563746564206163636f756e74696e6760581b604482015260640161043c565b80613e04575050919050565b606954811015613e5e578060686000828254613e2091906150df565b90915550506040518181527f7a745a2c63a535068f52ceca27debd5297bbad5f7f37ec53d044a59d0362445d906020015b60405180910390a161400c565b606a54811115613ffb577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613ec357600080fd5b505af1158015613ed7573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb92506044019050602060405180830381600087803b158015613f6757600080fd5b505af1158015613f7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f9f9190614856565b50600160346000828254613fb39190615138565b90915550613fc2905081613b25565b60345460408051918252602082018390527f6aa7e30787b26429ced603a7aba8b19c4b5d5bcf29a3257da953c8d53bcaa3a69101613e51565b61400484614177565b949350505050565b5050919050565b60335460ff1661405c5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015260640161043c565b6033805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613a0a565b60006140e2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661418f9092919063ffffffff16565b80519091501561352d57808060200190518101906141009190614856565b61352d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161043c565b600081831061416e5781614170565b825b9392505050565b60008115614187576141876139b2565b506000919050565b6060614004848460008585843b6141e85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161043c565b600080866001600160a01b031685876040516142049190614b6d565b60006040518083038185875af1925050503d8060008114614241576040519150601f19603f3d011682016040523d82523d6000602084013e614246565b606091505b5091509150614256828286614261565b979650505050505050565b60608315614270575081614170565b8251156142805782518084602001fd5b8160405162461bcd60e51b815260040161043c9190614e83565b8280548282559060005260206000209081019282156142ed579160200282015b828111156142ed5781546001600160a01b0319166001600160a01b038435161782556020909201916001909101906142ba565b506142f9929150614352565b5090565b8280548282559060005260206000209081019282156142ed579160200282015b828111156142ed57825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019061431d565b5b808211156142f95760008155600101614353565b60008083601f84011261437957600080fd5b5081356001600160401b0381111561439057600080fd5b6020830191508360208260051b85010111156143ab57600080fd5b9250929050565b600082601f8301126143c357600080fd5b813560206143d86143d38361507b565b61504b565b80838252828201915082860187848660051b89010111156143f857600080fd5b60005b8581101561442057813561440e81615204565b845292840192908401906001016143fb565b5090979650505050505050565b60008083601f84011261443f57600080fd5b5081356001600160401b0381111561445657600080fd5b6020830191508360208285010111156143ab57600080fd5b600060a0828403121561448057600080fd5b50919050565b600060a0828403121561449857600080fd5b60405160a081018181106001600160401b03821117156144ba576144ba6151ee565b6040529050806144c983614511565b81526144d76020840161452a565b60208201526144e86040840161452a565b604082015260608301356144fb81615219565b6060820152608092830135920191909152919050565b803563ffffffff8116811461452557600080fd5b919050565b80356001600160401b038116811461452557600080fd5b60006020828403121561455357600080fd5b813561417081615204565b60006020828403121561457057600080fd5b815161417081615204565b6000806040838503121561458e57600080fd5b823561459981615204565b915060208301356145a981615204565b809150509250929050565b6000806000606084860312156145c957600080fd5b83356145d481615204565b925060208401356145e481615204565b929592945050506040919091013590565b6000806040838503121561460857600080fd5b823561461381615204565b946020939093013593505050565b6000806020838503121561463457600080fd5b82356001600160401b0381111561464a57600080fd5b61465685828601614367565b90969095509350505050565b60008060006060848603121561467757600080fd5b83356001600160401b038082111561468e57600080fd5b61469a878388016143b2565b945060208601359150808211156146b057600080fd5b6146bc878388016143b2565b935060408601359150808211156146d257600080fd5b506146df868287016143b2565b9150509250925092565b600080600080600080600080610120898b03121561470657600080fd5b88356001600160401b038082111561471d57600080fd5b6147298c838d01614367565b909a50985060208b013591508082111561474257600080fd5b61474e8c838d01614367565b909850965060408b013591508082111561476757600080fd5b506147748b828c01614367565b9095509350506060890135915061478e8a60808b0161446e565b90509295985092959890939650565b600080600060e084860312156147b257600080fd5b83356001600160401b038111156147c857600080fd5b8401601f810186136147d957600080fd5b803560206147e96143d38361507b565b8083825282820191508285018a848660051b880101111561480957600080fd5b600095505b848610156148335761481f8161452a565b83526001959095019491830191830161480e565b50965050860135935061484d915086905060408601614486565b90509250925092565b60006020828403121561486857600080fd5b815161417081615219565b60006020828403121561488557600080fd5b5035919050565b600080600080604085870312156148a257600080fd5b84356001600160401b03808211156148b957600080fd5b6148c58883890161442d565b909650945060208701359150808211156148de57600080fd5b506148eb87828801614367565b95989497509550505050565b600080600080600060e0868803121561490f57600080fd5b85356001600160401b038082111561492657600080fd5b61493289838a0161442d565b9097509550602088013591508082111561494b57600080fd5b5061495888828901614367565b909450925061496c9050876040880161446e565b90509295509295909350565b60008060006060848603121561498d57600080fd5b505081359360208301359350604090920135919050565b6000602082840312156149b657600080fd5b5051919050565b600080604083850312156149d057600080fd5b50508035926020909101359150565b818352600060208085019450848460051b86018460005b878110156144205783830389528135601e19883603018112614a1757600080fd5b870180356001600160401b03811115614a2f57600080fd5b803603891315614a3e57600080fd5b614a4b8582898501614aa6565b9a87019a94505050908401906001016149f6565b8183526000602080850194508260005b85811015614a9b576001600160401b03614a888361452a565b1687529582019590820190600101614a6f565b509495945050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008151808452614ae781602086016020860161514f565b601f01601f19169290920160200192915050565b63ffffffff614b0982614511565b168252614b186020820161452a565b6001600160401b03808216602085015280614b356040850161452a565b16604085015250506060810135614b4b81615219565b15156060830152608090810135910152565b8183823760009101908152919050565b60008251614b7f81846020870161514f565b9190910192915050565b6001600160a01b03851681526101006020808301829052855191830182905260009161012084019187810191845b81811015614bdc5783516001600160401b031685529382019392820192600101614bb7565b505082935086604086015263ffffffff865116606086015280860151925050506001600160401b0380821660808501528060408601511660a085015250506060830151151560c0830152608083015160e083015295945050505050565b6020808252825182820181905260009190848201906040850190845b81811015614c7a5783516001600160a01b031683529284019291840191600101614c55565b50909695505050505050565b6000604082016040835280865480835260608501915087600052602092508260002060005b82811015614cd05781546001600160a01b031684529284019260019182019101614cab565b505050838103828501528481528590820160005b86811015614d12578235614cf781615204565b6001600160a01b031682529183019190830190600101614ce4565b50979650505050505050565b6000610120808352614d338184018b8d6149df565b90508281036020840152614d4881898b614a5f565b90508281036040840152614d5d8187896149df565b915050836060830152614d736080830184614afb565b9998505050505050505050565b604081526000614d94604083018688614aa6565b8281036020840152614256818587614a5f565b60e081526000614dbb60e083018789614aa6565b8281036020840152614dce818688614a5f565b915050614dde6040830184614afb565b9695505050505050565b608081526000614dfc60808301888a614aa6565b8281036020840152614e0e8188614acf565b90508281036040840152614e23818688614aa6565b915050826060830152979650505050505050565b604081526000614e4b604083018587614aa6565b9050826020830152949350505050565b6020810160058310614e7d57634e487b7160e01b600052602160045260246000fd5b91905290565b6020815260006141706020830184614acf565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b602080825260119082015270155b9cdd5c1c1bdc9d195908185cdcd95d607a1b604082015260600190565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6020808252601d908201527f43616c6c6572206973206e6f7420746865205265676973747261746f72000000604082015260600190565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b6020808252601c908201527f43616c6c6572206973206e6f7420746865205374726174656769737400000000604082015260600190565b6000808335601e1984360301811261500657600080fd5b8301803591506001600160401b0382111561502057600080fd5b6020019150368190038213156143ab57600080fd5b60008235605e19833603018112614b7f57600080fd5b604051601f8201601f191681016001600160401b0381118282101715615073576150736151ee565b604052919050565b60006001600160401b03821115615094576150946151ee565b5060051b60200190565b600080821280156001600160ff1b03849003851316156150c0576150c0615196565b600160ff1b83900384128116156150d9576150d9615196565b50500190565b600082198211156150f2576150f2615196565b500190565b60008261511457634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561513357615133615196565b500290565b60008282101561514a5761514a615196565b500390565b60005b8381101561516a578181015183820152602001615152565b83811115610dc45750506000910152565b600060001982141561518f5761518f615196565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114612d2357600080fd5b8015158114612d2357600080fdfe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac45357bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212202e71afd2d7b09b7df6654b21fac228181c64b8bacc3e8a258af4c4fd2123d43264736f6c634300080700337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009d65ff81a3c488d585bbfb0bfe3c7707c7917f54000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e100000000000000000000000000000000000000000000000000000000000001f4000000000000000000000000fee31c09fa5e9cdbc1f80c90b42b58640be91ddf00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa
Deployed Bytecode
0x6080604052600436106103905760003560e01c80638456cb59116101dc578063b16b7d0b11610102578063d9f00ec7116100a0578063de5f62681161006f578063de5f626814610bbf578063e752923914610bd4578063ee7afe2d14610bea578063f6ca71b014610bff57600080fd5b8063d9f00ec714610b21578063dbe55e5614610b41578063dd505df614610b75578063de34d71314610ba957600080fd5b8063cceab750116100dc578063cceab75014610a96578063d059f6ef14610aca578063d38bfff414610ae1578063d9caed1214610b0157600080fd5b8063b16b7d0b14610a44578063c2e1e3f414610a61578063c7af335214610a8157600080fd5b806396d538bb1161017a578063aa388af611610149578063aa388af61461098e578063ab12edf5146109db578063ad1728cb146109fb578063ad5c464814610a1057600080fd5b806396d538bb146108fc5780639da0e4621461091c578063a3b81e7314610959578063a4f98af41461097957600080fd5b80638d7c0e46116101b65780638d7c0e46146108545780639092c31c146108745780639136616a146108a857806391649751146108c857600080fd5b80638456cb5914610805578063853828b61461081a57806387bae8671461082f57600080fd5b80635a063f63116102c15780636e811d381161025f5780637260f8261161022e5780637260f826146107995780637b2d9b2c146107b95780637b8962f7146107d9578063842f5c46146107ef57600080fd5b80636e811d38146107055780636ef3879514610725578063714897df1461074557806371a735f31461077957600080fd5b80635f5152261161029b5780635f5152261461069957806363092383146106b957806366e3667e146106cf57806367c7066c146106e557600080fd5b80635a063f631461064b5780635c975abb146106605780635d36b1901461068457600080fd5b80633c8649591161032e57806347e7ef241161030857806347e7ef24146105d5578063484be812146105f55780635205c3801461060b57806359b80c0a1461062b57600080fd5b80633c8649591461055d578063430bf08a14610581578063435356d1146105b557600080fd5b80630fc3b4c41161036a5780630fc3b4c4146104d25780631072cbea1461050857806313cf69dd1461052857806322495dc81461053d57600080fd5b80630c340a241461044c5780630df1ecfd1461047e5780630ed57b3a146104b257600080fd5b3661044757336001600160a01b037f000000000000000000000000fee31c09fa5e9cdbc1f80c90b42b58640be91ddf1614806103f45750336001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216145b6104455760405162461bcd60e51b815260206004820152601e60248201527f457468206e6f742066726f6d20616c6c6f77656420636f6e747261637473000060448201526064015b60405180910390fd5b005b600080fd5b34801561045857600080fd5b50610461610c21565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561048a57600080fd5b506104617f0000000000000000000000009d65ff81a3c488d585bbfb0bfe3c7707c7917f5481565b3480156104be57600080fd5b506104456104cd36600461457b565b610c3e565b3480156104de57600080fd5b506104616104ed366004614541565b609f602052600090815260409020546001600160a01b031681565b34801561051457600080fd5b506104456105233660046145f5565b610c70565b34801561053457600080fd5b50610445610d2d565b34801561054957600080fd5b5061044561055836600461479d565b610dca565b34801561056957600080fd5b5061057360695481565b604051908152602001610475565b34801561058d57600080fd5b506104617f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab81565b3480156105c157600080fd5b506104456105d0366004614662565b610f14565b3480156105e157600080fd5b506104456105f03660046145f5565b611157565b34801561060157600080fd5b50610573606a5481565b34801561061757600080fd5b50610445610626366004614873565b611252565b34801561063757600080fd5b506104456106463660046146e9565b6112b1565b34801561065757600080fd5b50610445611534565b34801561066c57600080fd5b5060335460ff165b6040519015158152602001610475565b34801561069057600080fd5b506104456115d3565b3480156106a557600080fd5b506105736106b4366004614541565b611679565b3480156106c557600080fd5b50610573611c2081565b3480156106db57600080fd5b5061057360345481565b3480156106f157600080fd5b5060a354610461906001600160a01b031681565b34801561071157600080fd5b50610445610720366004614541565b61178a565b34801561073157600080fd5b50610445610740366004614621565b611800565b34801561075157600080fd5b506105737f00000000000000000000000000000000000000000000000000000000000001f481565b34801561078557600080fd5b506104456107943660046148f7565b611dca565b3480156107a557600080fd5b50603654610461906001600160a01b031681565b3480156107c557600080fd5b506104616107d4366004614873565b611fb3565b3480156107e557600080fd5b5061057360375481565b3480156107fb57600080fd5b5061057360685481565b34801561081157600080fd5b50610445611fdd565b34801561082657600080fd5b506104456120a6565b34801561083b57600080fd5b506033546104619061010090046001600160a01b031681565b34801561086057600080fd5b5061044561086f366004614978565b612278565b34801561088057600080fd5b506104617f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab81565b3480156108b457600080fd5b506104456108c3366004614873565b61277a565b3480156108d457600080fd5b506104617f000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e181565b34801561090857600080fd5b50610445610917366004614621565b612946565b34801561092857600080fd5b5061094c610937366004614873565b60356020526000908152604090205460ff1681565b6040516104759190614e5b565b34801561096557600080fd5b50610445610974366004614541565b612a66565b34801561098557600080fd5b50610674612ad4565b34801561099a57600080fd5b506106746109a9366004614541565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0390811691161490565b3480156109e757600080fd5b506104456109f63660046149bd565b612b74565b348015610a0757600080fd5b50610445612c60565b348015610a1c57600080fd5b506104617f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b348015610a5057600080fd5b506105736801bc16d674ec80000081565b348015610a6d57600080fd5b50610445610a7c366004614541565b612d26565b348015610a8d57600080fd5b50610674612db3565b348015610aa257600080fd5b506104617f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa81565b348015610ad657600080fd5b506105736101075481565b348015610aed57600080fd5b50610445610afc366004614541565b612de4565b348015610b0d57600080fd5b50610445610b1c3660046145b4565b612e88565b348015610b2d57600080fd5b50610445610b3c36600461488c565b612f62565b348015610b4d57600080fd5b506104617f000000000000000000000000000000000000000000000000000000000000000081565b348015610b8157600080fd5b506104617f000000000000000000000000fee31c09fa5e9cdbc1f80c90b42b58640be91ddf81565b348015610bb557600080fd5b5061057360385481565b348015610bcb57600080fd5b50610445613123565b348015610be057600080fd5b50610573606b5481565b348015610bf657600080fd5b50610445613290565b348015610c0b57600080fd5b50610c1461331a565b6040516104759190614c39565b6000610c396000805160206152488339815191525490565b905090565b610c46612db3565b610c625760405162461bcd60e51b815260040161043c90614ecd565b610c6c828261337c565b5050565b610c78612db3565b610c945760405162461bcd60e51b815260040161043c90614ecd565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b039081169083161415610d115760405162461bcd60e51b815260206004820152601f60248201527f43616e6e6f74207472616e7366657220737570706f7274656420617373657400604482015260640161043c565b610c6c610d1c610c21565b6001600160a01b03841690836134db565b6040516336f370b360e21b81526001600160a01b037f000000000000000000000000fee31c09fa5e9cdbc1f80c90b42b58640be91ddf811660048301527f000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e1169063dbcdc2cc90602401600060405180830381600087803b158015610db057600080fd5b505af1158015610dc4573d6000803e3d6000fd5b50505050565b7f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab6001600160a01b031663570d8e1d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e2357600080fd5b505afa158015610e37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5b919061455e565b6001600160a01b0316336001600160a01b031614610e8b5760405162461bcd60e51b815260040161043c90614fb8565b60405163bc26e7e560e01b81526001600160a01b037f000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e1169063bc26e7e590610edd903090879087908790600401614b89565b600060405180830381600087803b158015610ef757600080fd5b505af1158015610f0b573d6000803e3d6000fd5b50505050505050565b610f1c612db3565b610f385760405162461bcd60e51b815260040161043c90614ecd565b600054610100900460ff1680610f51575060005460ff16155b610fb45760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161043c565b600054610100900460ff16158015610fd6576000805461ffff19166101011790555b610fe1848484613532565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e18116600483015260001960248301527f0000000000000000000000009d65ff81a3c488d585bbfb0bfe3c7707c7917f54169063095ea7b390604401602060405180830381600087803b15801561106c57600080fd5b505af1158015611080573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a49190614856565b506040516336f370b360e21b81526001600160a01b037f000000000000000000000000fee31c09fa5e9cdbc1f80c90b42b58640be91ddf811660048301527f000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e1169063dbcdc2cc90602401600060405180830381600087803b15801561112857600080fd5b505af115801561113c573d6000803e3d6000fd5b505050508015610dc4576000805461ff001916905550505050565b336001600160a01b037f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab161461119f5760405162461bcd60e51b815260040161043c90614e96565b600080516020615228833981519152805460028114156111d15760405162461bcd60e51b815260040161043c90614f90565b600282557f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316846001600160a01b0316146112265760405162461bcd60e51b815260040161043c90614f04565b82610107600082825461123991906150df565b90915550611249905084846135ed565b50600190555050565b61125a612db3565b6112765760405162461bcd60e51b815260040161043c90614ecd565b60378190556040518181527fe26b067424903962f951f568e52ec9a3bbe1589526ea54a4e69ca6eaae1a4c779060200160405180910390a150565b60335461010090046001600160a01b031633146112e05760405162461bcd60e51b815260040161043c90614f59565b60335460ff16156113035760405162461bcd60e51b815260040161043c90614f2f565b8683146113525760405162461bcd60e51b815260206004820152601a60248201527f5075626b65792073686172657344617461206d69736d61746368000000000000604482015260640161043c565b60008060005b8981101561149b578a8a82818110611372576113726151d8565b90506020028101906113849190614fef565b604051611392929190614b5d565b6040805191829003909120600081815260356020529182205490945060ff1692508260048111156113c5576113c56151ac565b146114125760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f7220616c7265616479207265676973746572656400000000604482015260640161043c565b6000838152603560205260409020805460ff19166001179055827facd38e900350661e325d592c959664c0000a306efb2004e7dc283f44e0ea04238c8c8481811061145f5761145f6151d8565b90506020028101906114719190614fef565b8c8c6040516114839493929190614d80565b60405180910390a26114948161517b565b9050611358565b506040516322f18bf560e01b81526001600160a01b037f000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e116906322f18bf5906114f6908d908d908d908d908d908d908d908d90600401614d1e565b600060405180830381600087803b15801561151057600080fd5b505af1158015611524573d6000803e3d6000fd5b5050505050505050505050505050565b60a3546001600160a01b0316331461158e5760405162461bcd60e51b815260206004820152601b60248201527f43616c6c6572206973206e6f7420746865204861727665737465720000000000604482015260640161043c565b600080516020615228833981519152805460028114156115c05760405162461bcd60e51b815260040161043c90614f90565b600282556115cc61367f565b5060019055565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461166e5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b606482015260840161043c565b611677336138c4565b565b60007f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316826001600160a01b0316146116cc5760405162461bcd60e51b815260040161043c90614f04565b6040516370a0823160e01b81523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b15801561172b57600080fd5b505afa15801561173f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176391906149a4565b6801bc16d674ec80000060345461177a9190615119565b61178491906150df565b92915050565b611792612db3565b6117ae5760405162461bcd60e51b815260040161043c90614ecd565b60338054610100600160a81b0319166101006001600160a01b038416908102919091179091556040517f83f29c79feb71f8fba9d0fbc4ba5f0982a28b6b1e868b3fc50e6400d100bca0f90600090a250565b60335461010090046001600160a01b0316331461182f5760405162461bcd60e51b815260040161043c90614f59565b60335460ff16156118525760405162461bcd60e51b815260040161043c90614f2f565b600080516020615228833981519152805460028114156118845760405162461bcd60e51b815260040161043c90614f90565b60028255600061189d6801bc16d674ec80000085615119565b6040516370a0823160e01b81523060048201529091507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b1580156118ff57600080fd5b505afa158015611913573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061193791906149a4565b81111561197a5760405162461bcd60e51b8152602060048201526011602482015270092dce6eaccccd2c6d2cadce840ae8aa89607b1b604482015260640161043c565b6034547f00000000000000000000000000000000000000000000000000000000000001f4906119aa9086906150df565b11156119f15760405162461bcd60e51b815260206004820152601660248201527513585e081d985b1a59185d1bdc9cc81c995858da195960521b604482015260640161043c565b60375481603854611a0291906150df565b1115611a505760405162461bcd60e51b815260206004820152601a60248201527f5374616b696e6720455448206f766572207468726573686f6c64000000000000604482015260640161043c565b8060386000828254611a6291906150df565b9091555050604051632e1a7d4d60e01b8152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611ac957600080fd5b505af1158015611add573d6000803e3d6000fd5b50505050611aea81613985565b60408051600160f81b60208201526000602182018190526bffffffffffffffffffffffff193060601b16602c8301529101604051602081830303815290604052905060005b85811015611da3576000878783818110611b4b57611b4b6151d8565b9050602002810190611b5d9190615035565b611b679080614fef565b604051611b75929190614b5d565b6040519081900390209050600160008281526035602052604090205460ff166004811115611ba557611ba56151ac565b14611bf25760405162461bcd60e51b815260206004820152601860248201527f56616c696461746f72206e6f7420726567697374657265640000000000000000604482015260640161043c565b7f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa6001600160a01b031663228951186801bc16d674ec8000008a8a86818110611c3d57611c3d6151d8565b9050602002810190611c4f9190615035565b611c599080614fef565b878d8d89818110611c6c57611c6c6151d8565b9050602002810190611c7e9190615035565b611c8c906020810190614fef565b8f8f8b818110611c9e57611c9e6151d8565b9050602002810190611cb09190615035565b604001356040518863ffffffff1660e01b8152600401611cd596959493929190614de8565b6000604051808303818588803b158015611cee57600080fd5b505af1158015611d02573d6000803e3d6000fd5b5050506000838152603560205260409020805460ff19166002179055508190507f958934bb53d6b4dc911b6173e586864efbc8076684a31f752c53d5778340b37f898985818110611d5557611d556151d8565b9050602002810190611d679190615035565b611d719080614fef565b6801bc16d674ec800000604051611d8a93929190614e37565b60405180910390a250611d9c8161517b565b9050611b2f565b508585905060346000828254611db991906150df565b909155505060019093555050505050565b60335461010090046001600160a01b03163314611df95760405162461bcd60e51b815260040161043c90614f59565b60335460ff1615611e1c5760405162461bcd60e51b815260040161043c90614f2f565b60008585604051611e2e929190614b5d565b604080519182900390912060008181526035602052919091205490915060ff166003816004811115611e6257611e626151ac565b1480611e7f57506001816004811115611e7d57611e7d6151ac565b145b611ecb5760405162461bcd60e51b815260206004820152601d60248201527f56616c696461746f72206e6f742072656764206f722065786974696e67000000604482015260640161043c565b6040516312b3fc1960e01b81526001600160a01b037f000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e116906312b3fc1990611f1f908a908a908a908a908a90600401614da7565b600060405180830381600087803b158015611f3957600080fd5b505af1158015611f4d573d6000803e3d6000fd5b50505060008381526035602052604090819020805460ff19166004179055518391507f6aecca20726a17c1b81989b2fd09dfdf636bae9e564d4066ca18df62dc1f3dc290611fa2908a908a908a908a90614d80565b60405180910390a250505050505050565b60a48181548110611fc357600080fd5b6000918252602090912001546001600160a01b0316905081565b7f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab6001600160a01b031663570d8e1d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561203657600080fd5b505afa15801561204a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206e919061455e565b6001600160a01b0316336001600160a01b03161461209e5760405162461bcd60e51b815260040161043c90614fb8565b6116776139b2565b336001600160a01b037f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab1614806120f557506120e0610c21565b6001600160a01b0316336001600160a01b0316145b61214d5760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f7420746865205661756c74206f7220476f7665726044820152623737b960e91b606482015260840161043c565b6000805160206152288339815191528054600281141561217f5760405162461bcd60e51b815260040161043c90614f90565b600282556040516370a0823160e01b81523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b1580156121e557600080fd5b505afa1580156121f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221d91906149a4565b90508015612270576122707f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc283613a27565b505060019055565b7f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab6001600160a01b031663570d8e1d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156122d157600080fd5b505afa1580156122e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612309919061455e565b6001600160a01b0316336001600160a01b0316146123395760405162461bcd60e51b815260040161043c90614fb8565b60335460ff166123825760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015260640161043c565b600080516020615228833981519152805460028114156123b45760405162461bcd60e51b815260040161043c90614f90565b6002825543611c20606b546123c991906150df565b106124165760405162461bcd60e51b815260206004820152601e60248201527f466978206163636f756e74696e672063616c6c656420746f6f20736f6f6e0000604482015260640161043c565b6002198512158015612429575060038513155b80156124435750600085603454612440919061509e565b12155b61248f5760405162461bcd60e51b815260206004820152601760248201527f496e76616c69642076616c696461746f727344656c7461000000000000000000604482015260640161043c565b6811ff6cf0fd15afffff1984121580156124b257506811ff6cf0fd15b000008413155b80156124cc57506000846068546124c9919061509e565b12155b6125185760405162461bcd60e51b815260206004820152601d60248201527f496e76616c696420636f6e73656e7375735265776172647344656c7461000000604482015260640161043c565b68053444835ec58000008311156125715760405162461bcd60e51b815260206004820152601960248201527f496e76616c69642077657468546f5661756c74416d6f756e7400000000000000604482015260640161043c565b8460345461257f919061509e565b60345560685461259090859061509e565b60685543606b5582156126de577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b1580156125f857600080fd5b505af115801561260c573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab81166004830152602482018890527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216935063a9059cbb92506044019050602060405180830381600087803b15801561269c57600080fd5b505af11580156126b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d49190614856565b506126de83613b25565b60408051868152602081018690529081018490527f80d022717ea022455c5886b8dd8a29c037570aae58aeb4d7b136d7a10ec2e4319060600160405180910390a16127296000613b8d565b6127685760405162461bcd60e51b815260206004820152601060248201526f233ab9b29039ba34b63610313637bbb760811b604482015260640161043c565b612770614013565b5060019055505050565b612782612db3565b61279e5760405162461bcd60e51b815260040161043c90614ecd565b60a05481106127df5760405162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840d2dcc8caf609b1b604482015260640161043c565b600060a082815481106127f4576127f46151d8565b60009182526020808320909101546001600160a01b03908116808452609f90925260409092205460a0549193509091169061283190600190615138565b8310156128b35760a0805461284890600190615138565b81548110612858576128586151d8565b60009182526020909120015460a080546001600160a01b039092169185908110612884576128846151d8565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b60a08054806128c4576128c46151c2565b60008281526020808220600019908401810180546001600160a01b031990811690915593019093556001600160a01b03858116808352609f855260409283902080549094169093559051908416815290917f16b7600acff27e39a8a96056b3d533045298de927507f5c1d97e4accde60488c91015b60405180910390a2505050565b61294e612db3565b61296a5760405162461bcd60e51b815260040161043c90614ecd565b8060005b81811015612a1d57600084848381811061298a5761298a6151d8565b905060200201602081019061299f9190614541565b6001600160a01b03161415612a0d5760405162461bcd60e51b815260206004820152602e60248201527f43616e206e6f742073657420616e20656d70747920616464726573732061732060448201526d30903932bbb0b932103a37b5b2b760911b606482015260840161043c565b612a168161517b565b905061296e565b507f04c0b9649497d316554306e53678d5f5f5dbc3a06f97dec13ff4cfe98b986bbc60a48484604051612a5293929190614c86565b60405180910390a1610dc460a4848461429a565b612a6e612db3565b612a8a5760405162461bcd60e51b815260040161043c90614ecd565b603680546001600160a01b0319166001600160a01b0383169081179091556040517f3329861a0008b3348767567d2405492b997abd79a088d0f2cef6b1a09a8e7ff790600090a250565b60335460009061010090046001600160a01b03163314612b065760405162461bcd60e51b815260040161043c90614f59565b60335460ff1615612b295760405162461bcd60e51b815260040161043c90614f2f565b60008051602061522883398151915280546002811415612b5b5760405162461bcd60e51b815260040161043c90614f90565b60028255612b696001613b8d565b925060018255505090565b612b7c612db3565b612b985760405162461bcd60e51b815260040161043c90614ecd565b8082108015612baf57506801bc16d674ec80000081105b8015612bcc5750673782dace9d900000612bc98383615138565b10155b612c185760405162461bcd60e51b815260206004820152601760248201527f496e636f7272656374206675736520696e74657276616c000000000000000000604482015260640161043c565b6069829055606a81905560408051838152602081018390527fcb8d24e46eb3c402bf344ee60a6576cba9ef2f59ea1af3b311520704924e901a91015b60405180910390a15050565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e18116600483015260001960248301527f0000000000000000000000009d65ff81a3c488d585bbfb0bfe3c7707c7917f54169063095ea7b390604401602060405180830381600087803b158015612ceb57600080fd5b505af1158015612cff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d239190614856565b50565b612d2e612db3565b612d4a5760405162461bcd60e51b815260040161043c90614ecd565b60a354604080516001600160a01b03928316815291831660208301527fe48386b84419f4d36e0f96c10cc3510b6fb1a33795620c5098b22472bbe90796910160405180910390a160a380546001600160a01b0319166001600160a01b0392909216919091179055565b6000612dcb6000805160206152488339815191525490565b6001600160a01b0316336001600160a01b031614905090565b612dec612db3565b612e085760405162461bcd60e51b815260040161043c90614ecd565b612e30817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b0316612e506000805160206152488339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b336001600160a01b037f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab1614612ed05760405162461bcd60e51b815260040161043c90614e96565b60008051602061522883398151915280546002811415612f025760405162461bcd60e51b815260040161043c90614f90565b600282557f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316846001600160a01b031614612f575760405162461bcd60e51b815260040161043c90614f04565b612770858585613a27565b60335461010090046001600160a01b03163314612f915760405162461bcd60e51b815260040161043c90614f59565b60335460ff1615612fb45760405162461bcd60e51b815260040161043c90614f2f565b60008484604051612fc6929190614b5d565b604080519182900390912060008181526035602052919091205490915060ff166002816004811115612ffa57612ffa6151ac565b1461303e5760405162461bcd60e51b815260206004820152601460248201527315985b1a59185d1bdc881b9bdd081cdd185ad95960621b604482015260640161043c565b604051633877322b60e01b81526001600160a01b037f000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e11690633877322b90613090908990899089908990600401614d80565b600060405180830381600087803b1580156130aa57600080fd5b505af11580156130be573d6000803e3d6000fd5b50505060008381526035602052604090819020805460ff19166003179055518391507f8c2e15303eb94e531acc988c2a01d1193bdaaa15eda7f16dda85316ed463578d90613113908990899089908990614d80565b60405180910390a2505050505050565b336001600160a01b037f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab161461316b5760405162461bcd60e51b815260040161043c90614e96565b6000805160206152288339815191528054600281141561319d5760405162461bcd60e51b815260040161043c90614f90565b600282556040516370a0823160e01b81523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b15801561320357600080fd5b505afa158015613217573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323b91906149a4565b90506000610107548261324e9190615138565b90508015613286576101078290556132867f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2826135ed565b5050600182555050565b6036546001600160a01b031633146132ea5760405162461bcd60e51b815260206004820152601960248201527f43616c6c6572206973206e6f7420746865204d6f6e69746f7200000000000000604482015260640161043c565b600060388190556040517fe765a88a37047c5d793dce22b9ceb5a0f5039d276da139b4c7d29613f341f1109190a1565b606060a480548060200260200160405190810160405280929190818152602001828054801561337257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613354575b5050505050905090565b6001600160a01b038281166000908152609f602052604090205416156133d95760405162461bcd60e51b81526020600482015260126024820152711c151bdad95b88185b1c9958591e481cd95d60721b604482015260640161043c565b6001600160a01b038216158015906133f957506001600160a01b03811615155b6134395760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642061646472657373657360781b604482015260640161043c565b6001600160a01b038281166000818152609f6020908152604080832080549587166001600160a01b0319968716811790915560a0805460018101825594527f78fdc8d422c49ced035a9edf18d00d3c6a8d81df210f3e5e448e045e77b41e8890930180549095168417909455925190815290917fef6485b84315f9b1483beffa32aae9a0596890395e3d7521f1c5fbb51790e765910160405180910390a25050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261352d90849061408d565b505050565b82516135459060a49060208601906142fd565b5081518151811461358f5760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420696e7075742061727261797360601b604482015260640161043c565b60005b818110156135e6576135d68482815181106135af576135af6151d8565b60200260200101518483815181106135c9576135c96151d8565b602002602001015161337c565b6135df8161517b565b9050613592565b5050505050565b600081116136365760405162461bcd60e51b81526020600482015260166024820152754d757374206465706f73697420736f6d657468696e6760501b604482015260640161043c565b6040805160008152602081018390526001600160a01b038416917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a25050565b60335460ff16156136a25760405162461bcd60e51b815260040161043c90614f2f565b60007f000000000000000000000000fee31c09fa5e9cdbc1f80c90b42b58640be91ddf6001600160a01b031663e52253816040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156136ff57600080fd5b505af1158015613713573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373791906149a4565b905060006068548261374991906150df565b90508047101561379b5760405162461bcd60e51b815260206004820152601860248201527f496e73756666696369656e74206574682062616c616e63650000000000000000604482015260640161043c565b8015610c6c5760006068819055507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561380457600080fd5b505af1158015613818573d6000803e3d6000fd5b505060a35461385893506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281169350169050836134db565b60a354604080516001600160a01b0392831681527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2909216602083015281018290527ff6c07a063ed4e63808eb8da7112d46dbcd38de2b40a73dbcc9353c5a94c7235390606001612c54565b6001600160a01b03811661391a5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f722069732061646472657373283029000000000000604482015260640161043c565b806001600160a01b031661393a6000805160206152488339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3612d238160008051602061524883398151915255565b6000613994826101075461415f565b90508061010760008282546139a99190615138565b90915550505050565b60335460ff16156139d55760405162461bcd60e51b815260040161043c90614f2f565b6033805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613a0a3390565b6040516001600160a01b03909116815260200160405180910390a1565b60008111613a775760405162461bcd60e51b815260206004820152601760248201527f4d75737420776974686472617720736f6d657468696e67000000000000000000604482015260640161043c565b6001600160a01b038316613ac65760405162461bcd60e51b8152602060048201526016602482015275135d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b604482015260640161043c565b613acf81613985565b613ae36001600160a01b03831684836134db565b6040805160008152602081018390526001600160a01b038416917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989101612939565b6040805160008152602081018390526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b6398910160405180910390a250565b6000606854471015613ba25761178482614177565b600060685447613bb29190615138565b9050600191506801bc16d674ec8000008110613d96576000613bdd6801bc16d674ec800000836150f7565b90508060346000828254613bf19190615138565b9091555060009050613c0c826801bc16d674ec800000615119565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613c6957600080fd5b505af1158015613c7d573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab81166004830152602482018690527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216935063a9059cbb92506044019050602060405180830381600087803b158015613d0d57600080fd5b505af1158015613d21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d459190614856565b50613d4f81613b25565b60345460408051848152602081019290925281018290527fbe7040030ff7b347853214bf49820c6d455fedf58f3815f85c7bc5216993682b9060600160405180910390a150505b600060685447613da69190615138565b90506801bc16d674ec8000008110613df85760405162461bcd60e51b8152602060048201526015602482015274556e6578706563746564206163636f756e74696e6760581b604482015260640161043c565b80613e04575050919050565b606954811015613e5e578060686000828254613e2091906150df565b90915550506040518181527f7a745a2c63a535068f52ceca27debd5297bbad5f7f37ec53d044a59d0362445d906020015b60405180910390a161400c565b606a54811115613ffb577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613ec357600080fd5b505af1158015613ed7573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab81166004830152602482018690527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216935063a9059cbb92506044019050602060405180830381600087803b158015613f6757600080fd5b505af1158015613f7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f9f9190614856565b50600160346000828254613fb39190615138565b90915550613fc2905081613b25565b60345460408051918252602082018390527f6aa7e30787b26429ced603a7aba8b19c4b5d5bcf29a3257da953c8d53bcaa3a69101613e51565b61400484614177565b949350505050565b5050919050565b60335460ff1661405c5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015260640161043c565b6033805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613a0a565b60006140e2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661418f9092919063ffffffff16565b80519091501561352d57808060200190518101906141009190614856565b61352d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161043c565b600081831061416e5781614170565b825b9392505050565b60008115614187576141876139b2565b506000919050565b6060614004848460008585843b6141e85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161043c565b600080866001600160a01b031685876040516142049190614b6d565b60006040518083038185875af1925050503d8060008114614241576040519150601f19603f3d011682016040523d82523d6000602084013e614246565b606091505b5091509150614256828286614261565b979650505050505050565b60608315614270575081614170565b8251156142805782518084602001fd5b8160405162461bcd60e51b815260040161043c9190614e83565b8280548282559060005260206000209081019282156142ed579160200282015b828111156142ed5781546001600160a01b0319166001600160a01b038435161782556020909201916001909101906142ba565b506142f9929150614352565b5090565b8280548282559060005260206000209081019282156142ed579160200282015b828111156142ed57825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019061431d565b5b808211156142f95760008155600101614353565b60008083601f84011261437957600080fd5b5081356001600160401b0381111561439057600080fd5b6020830191508360208260051b85010111156143ab57600080fd5b9250929050565b600082601f8301126143c357600080fd5b813560206143d86143d38361507b565b61504b565b80838252828201915082860187848660051b89010111156143f857600080fd5b60005b8581101561442057813561440e81615204565b845292840192908401906001016143fb565b5090979650505050505050565b60008083601f84011261443f57600080fd5b5081356001600160401b0381111561445657600080fd5b6020830191508360208285010111156143ab57600080fd5b600060a0828403121561448057600080fd5b50919050565b600060a0828403121561449857600080fd5b60405160a081018181106001600160401b03821117156144ba576144ba6151ee565b6040529050806144c983614511565b81526144d76020840161452a565b60208201526144e86040840161452a565b604082015260608301356144fb81615219565b6060820152608092830135920191909152919050565b803563ffffffff8116811461452557600080fd5b919050565b80356001600160401b038116811461452557600080fd5b60006020828403121561455357600080fd5b813561417081615204565b60006020828403121561457057600080fd5b815161417081615204565b6000806040838503121561458e57600080fd5b823561459981615204565b915060208301356145a981615204565b809150509250929050565b6000806000606084860312156145c957600080fd5b83356145d481615204565b925060208401356145e481615204565b929592945050506040919091013590565b6000806040838503121561460857600080fd5b823561461381615204565b946020939093013593505050565b6000806020838503121561463457600080fd5b82356001600160401b0381111561464a57600080fd5b61465685828601614367565b90969095509350505050565b60008060006060848603121561467757600080fd5b83356001600160401b038082111561468e57600080fd5b61469a878388016143b2565b945060208601359150808211156146b057600080fd5b6146bc878388016143b2565b935060408601359150808211156146d257600080fd5b506146df868287016143b2565b9150509250925092565b600080600080600080600080610120898b03121561470657600080fd5b88356001600160401b038082111561471d57600080fd5b6147298c838d01614367565b909a50985060208b013591508082111561474257600080fd5b61474e8c838d01614367565b909850965060408b013591508082111561476757600080fd5b506147748b828c01614367565b9095509350506060890135915061478e8a60808b0161446e565b90509295985092959890939650565b600080600060e084860312156147b257600080fd5b83356001600160401b038111156147c857600080fd5b8401601f810186136147d957600080fd5b803560206147e96143d38361507b565b8083825282820191508285018a848660051b880101111561480957600080fd5b600095505b848610156148335761481f8161452a565b83526001959095019491830191830161480e565b50965050860135935061484d915086905060408601614486565b90509250925092565b60006020828403121561486857600080fd5b815161417081615219565b60006020828403121561488557600080fd5b5035919050565b600080600080604085870312156148a257600080fd5b84356001600160401b03808211156148b957600080fd5b6148c58883890161442d565b909650945060208701359150808211156148de57600080fd5b506148eb87828801614367565b95989497509550505050565b600080600080600060e0868803121561490f57600080fd5b85356001600160401b038082111561492657600080fd5b61493289838a0161442d565b9097509550602088013591508082111561494b57600080fd5b5061495888828901614367565b909450925061496c9050876040880161446e565b90509295509295909350565b60008060006060848603121561498d57600080fd5b505081359360208301359350604090920135919050565b6000602082840312156149b657600080fd5b5051919050565b600080604083850312156149d057600080fd5b50508035926020909101359150565b818352600060208085019450848460051b86018460005b878110156144205783830389528135601e19883603018112614a1757600080fd5b870180356001600160401b03811115614a2f57600080fd5b803603891315614a3e57600080fd5b614a4b8582898501614aa6565b9a87019a94505050908401906001016149f6565b8183526000602080850194508260005b85811015614a9b576001600160401b03614a888361452a565b1687529582019590820190600101614a6f565b509495945050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008151808452614ae781602086016020860161514f565b601f01601f19169290920160200192915050565b63ffffffff614b0982614511565b168252614b186020820161452a565b6001600160401b03808216602085015280614b356040850161452a565b16604085015250506060810135614b4b81615219565b15156060830152608090810135910152565b8183823760009101908152919050565b60008251614b7f81846020870161514f565b9190910192915050565b6001600160a01b03851681526101006020808301829052855191830182905260009161012084019187810191845b81811015614bdc5783516001600160401b031685529382019392820192600101614bb7565b505082935086604086015263ffffffff865116606086015280860151925050506001600160401b0380821660808501528060408601511660a085015250506060830151151560c0830152608083015160e083015295945050505050565b6020808252825182820181905260009190848201906040850190845b81811015614c7a5783516001600160a01b031683529284019291840191600101614c55565b50909695505050505050565b6000604082016040835280865480835260608501915087600052602092508260002060005b82811015614cd05781546001600160a01b031684529284019260019182019101614cab565b505050838103828501528481528590820160005b86811015614d12578235614cf781615204565b6001600160a01b031682529183019190830190600101614ce4565b50979650505050505050565b6000610120808352614d338184018b8d6149df565b90508281036020840152614d4881898b614a5f565b90508281036040840152614d5d8187896149df565b915050836060830152614d736080830184614afb565b9998505050505050505050565b604081526000614d94604083018688614aa6565b8281036020840152614256818587614a5f565b60e081526000614dbb60e083018789614aa6565b8281036020840152614dce818688614a5f565b915050614dde6040830184614afb565b9695505050505050565b608081526000614dfc60808301888a614aa6565b8281036020840152614e0e8188614acf565b90508281036040840152614e23818688614aa6565b915050826060830152979650505050505050565b604081526000614e4b604083018587614aa6565b9050826020830152949350505050565b6020810160058310614e7d57634e487b7160e01b600052602160045260246000fd5b91905290565b6020815260006141706020830184614acf565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b602080825260119082015270155b9cdd5c1c1bdc9d195908185cdcd95d607a1b604082015260600190565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6020808252601d908201527f43616c6c6572206973206e6f7420746865205265676973747261746f72000000604082015260600190565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b6020808252601c908201527f43616c6c6572206973206e6f7420746865205374726174656769737400000000604082015260600190565b6000808335601e1984360301811261500657600080fd5b8301803591506001600160401b0382111561502057600080fd5b6020019150368190038213156143ab57600080fd5b60008235605e19833603018112614b7f57600080fd5b604051601f8201601f191681016001600160401b0381118282101715615073576150736151ee565b604052919050565b60006001600160401b03821115615094576150946151ee565b5060051b60200190565b600080821280156001600160ff1b03849003851316156150c0576150c0615196565b600160ff1b83900384128116156150d9576150d9615196565b50500190565b600082198211156150f2576150f2615196565b500190565b60008261511457634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561513357615133615196565b500290565b60008282101561514a5761514a615196565b500390565b60005b8381101561516a578181015183820152602001615152565b83811115610dc45750506000910152565b600060001982141561518f5761518f615196565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114612d2357600080fd5b8015158114612d2357600080fdfe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac45357bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212202e71afd2d7b09b7df6654b21fac228181c64b8bacc3e8a258af4c4fd2123d43264736f6c63430008070033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009d65ff81a3c488d585bbfb0bfe3c7707c7917f54000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e100000000000000000000000000000000000000000000000000000000000001f4000000000000000000000000fee31c09fa5e9cdbc1f80c90b42b58640be91ddf00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa
-----Decoded View---------------
Arg [0] : _baseConfig (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [1] : _wethAddress (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [2] : _ssvToken (address): 0x9D65fF81a3c488d585bBfb0Bfe3c7707c7917f54
Arg [3] : _ssvNetwork (address): 0xDD9BC35aE942eF0cFa76930954a156B3fF30a4E1
Arg [4] : _maxValidators (uint256): 500
Arg [5] : _feeAccumulator (address): 0xfEE31c09fA5E9cdbC1f80C90b42B58640be91DDF
Arg [6] : _beaconChainDepositContract (address): 0x00000000219ab540356cBB839Cbe05303d7705Fa
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [1] : 00000000000000000000000039254033945aa2e4809cc2977e7087bee48bd7ab
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 0000000000000000000000009d65ff81a3c488d585bbfb0bfe3c7707c7917f54
Arg [4] : 000000000000000000000000dd9bc35ae942ef0cfa76930954a156b3ff30a4e1
Arg [5] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [6] : 000000000000000000000000fee31c09fa5e9cdbc1f80c90b42b58640be91ddf
Arg [7] : 00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.