Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00Multichain Info
N/A
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract contains unverified libraries: FeesHelper, MultiStrategyVaultHelper, RewardsHelper, StrategyHelper, VaultActionsHelper, WithdrawalQueueHelper
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
ConcreteMultiStrategyVault
Compiler Version
v0.8.24+commit.e11b9ed9
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; import { ERC4626Upgradeable, IERC20, IERC20Metadata, ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import { VaultFees, Strategy, IConcreteMultiStrategyVault, Allocation } from "../interfaces/IConcreteMultiStrategyVault.sol"; import {Errors} from "../interfaces/Errors.sol"; import {IStrategy, ReturnedRewards} from "../interfaces/IStrategy.sol"; import {IWithdrawalQueue} from "../interfaces/IWithdrawalQueue.sol"; import {IParkingLot} from "../interfaces/IParkingLot.sol"; import {MultiStrategyVaultHelper} from "../libraries/MultiStrategyVaultHelper.sol"; import {MAX_BASIS_POINTS, PRECISION, DUST, SECONDS_PER_YEAR} from "../utils/Constants.sol"; import {WithdrawalQueueHelper} from "../libraries/WithdrawalQueueHelper.sol"; import {VaultActionsHelper} from "../libraries/VaultActions.sol"; import {RewardsHelper} from "../libraries/RewardsHelper.sol"; import {StrategyHelper} from "../libraries/StrategyHelper.sol"; import {FeesHelper} from "../libraries/FeesHelper.sol"; import {TokenHelper} from "@blueprint-finance/hub-and-spokes-libraries/src/libraries/TokenHelper.sol"; /** * @title ConcreteMultiStrategyVault * @author Concrete * @notice An ERC4626 compliant vault that manages multiple yield generating strategies * @dev This vault: * - Implements ERC4626 standard for tokenized vaults * - Manages multiple yield strategies simultaneously * - Handles fee collection and distribution * - Supports emergency pausing * - Provides withdrawal queueing mechanism */ contract ConcreteMultiStrategyVault is ERC4626Upgradeable, Errors, ReentrancyGuardUpgradeable, PausableUpgradeable, OwnableUpgradeable, IConcreteMultiStrategyVault { using SafeERC20 for IERC20; using Math for uint256; uint256 public firstDeposit = 0; /// @dev Public variable storing the address of the protectStrategy contract. address public protectStrategy; /// @dev Internal variable to store the number of decimals the vault's shares will have. uint8 private _decimals; /// @notice The offset applied to decimals to prevent inflation attacks. /// @dev Public constant representing the offset applied to the vault's share decimals. uint8 public constant decimalOffset = 9; /// @notice The highest value of share price recorded, used for performance fee calculation. /// @dev Public variable to store the high water mark for performance fee calculation. uint256 public highWaterMark; /// @notice The maximum amount of assets that can be deposited into the vault. /// @dev Public variable to store the deposit limit of the vault. uint256 public depositLimit; /// @notice The timestamp at which the fees were last updated. /// @dev Public variable to store the last update time of the fees. uint256 public feesUpdatedAt; /// @notice The recipient address for any fees collected by the vault. /// @dev Public variable to store the address of the fee recipient. address public feeRecipient; /// @notice Indicates if the vault is in idle mode, where deposits are not passed to strategies. /// @dev Public boolean indicating if the vault is idle. bool public vaultIdle; /// @notice Indicates if the vault withdrawals are paused /// @dev Public boolean indicating if the vault withdrawals are paused bool public withdrawalsPaused; /// @notice The array of strategies that the vault can interact with. /// @dev Public array storing the strategies associated with the vault. Strategy[] internal strategies; /// @notice The fee structure of the vault. /// @dev Public variable storing the fees associated with the vault. VaultFees private fees; /// @notice The minimum queue request associated with the vault. /// @dev Public variable storing the minimum queue request associated with the vault. uint256 public minQueueRequest; /// @notice The withdrawal queue associated with the vault. /// @dev Public variable storing the withdrawal queue contract address associated with the vault. IWithdrawalQueue public withdrawalQueue; /// @notice The parking lot associated with the vault. /// @dev Public variable storing the parking lot contract address associated with the vault. IParkingLot public parkingLot; //Rewards Management /// @notice array to store all reward addresses address[] private rewardAddresses; /// @notice mapping to get the index of each reward address mapping(address => uint256) public rewardIndex; /// @notice mapping to store the reward index for each user and reward address mapping(address => mapping(address => uint256)) public userRewardIndex; /// @notice mapping to store the total rewards claimed by user for each reward address mapping(address => mapping(address => uint256)) public totalRewardsClaimed; /// @notice is withdrawal queue mandatory for withdrawals & redemptions. bool public isQueueMandatory; /// @notice storage gap for future upgrades uint256[50] private __gap; /// @notice event for vault initialization event Initialized(address indexed vaultName, address indexed underlyingAsset); /// @notice event for requested funds event RequestedFunds(address indexed protectStrategy, uint256 amount); /// @notice event for rewards harvested event RewardsHarvested(); /// @notice event for minimum queue request updated event MinimunQueueRequestUpdated(uint256 _oldMinQueueRequest, uint256 _newMinQueueRequest); /// @notice Modifier to restrict access to only the designated protection strategy account. /// @dev Reverts the transaction if the sender is not the protection strategy account. modifier onlyProtect() { if (protectStrategy != _msgSender()) { revert ProtectUnauthorizedAccount(_msgSender()); } _; } ///@notice Modifier that allows protocol to take fees modifier takeFees() { if (firstDeposit == 0 || totalSupply() == 0) { feesUpdatedAt = block.timestamp; } if (!paused()) { uint256 totalFee = accruedProtocolFee() + accruedPerformanceFee(); uint256 shareValue = convertToAssets(1e18); uint256 _totalAssets = totalAssets(); if (shareValue > highWaterMark) highWaterMark = shareValue; if (totalFee > 0 && _totalAssets > 0) { uint256 supply = totalSupply(); uint256 feeInShare = supply == 0 ? totalFee : totalFee.mulDiv(supply, _totalAssets - totalFee, Math.Rounding.Floor); _mint(feeRecipient, feeInShare); feesUpdatedAt = block.timestamp; } } _; } constructor() { _disableInitializers(); } function initialize( IERC20 baseAsset_, string memory shareName_, string memory shareSymbol_, Strategy[] memory strategies_, address feeRecipient_, VaultFees memory fees_, uint256 depositLimit_, address owner_, bool isQueueMandatory_ ) external virtual initializer nonReentrant { _initialize( baseAsset_, shareName_, shareSymbol_, strategies_, feeRecipient_, fees_, depositLimit_, owner_, isQueueMandatory_ ); } /** * @notice Initializes the vault with its core parameters * @dev Sets up the vault's initial state including strategies, fees, and limits * @param baseAsset_ The underlying asset token address * @param shareName_ The name for the vault's share token * @param shareSymbol_ The symbol for the vault's share token * @param strategies_ Array of initial strategies * @param feeRecipient_ Address to receive collected fees * @param fees_ Initial fee structure * @param depositLimit_ Maximum deposit amount allowed * @param owner_ Address of the vault owner */ // slither didn't detect the nonReentrant modifier // slither-disable-next-line reentrancy-no-eth,reentrancy-benign,calls-loop,costly-loop function _initialize( IERC20 baseAsset_, string memory shareName_, string memory shareSymbol_, Strategy[] memory strategies_, address feeRecipient_, VaultFees memory fees_, uint256 depositLimit_, address owner_, bool isQueueMandatory_ ) internal { __Pausable_init(); __ERC4626_init(baseAsset_); __ERC20_init(shareName_, shareSymbol_); __Ownable_init(owner_); __ReentrancyGuard_init(); if (address(baseAsset_) == address(0)) revert InvalidAssetAddress(); (protectStrategy, _decimals) = MultiStrategyVaultHelper.validateVaultParameters( baseAsset_, decimalOffset, strategies_, protectStrategy, strategies, fees_, fees ); if (feeRecipient_ == address(0)) { revert InvalidFeeRecipient(); } feeRecipient = feeRecipient_; highWaterMark = 1e9; // Set the initial high water mark for performance fee calculation. depositLimit = depositLimit_; isQueueMandatory = isQueueMandatory_; // By default, the vault is not idle. It can be set to idle mode using toggleVaultIdle(true). vaultIdle = false; withdrawalsPaused = false; feesUpdatedAt = block.timestamp; emit Initialized(address(this), address(baseAsset_)); } /** * @notice Returns the decimals of the vault's shares. * @dev Overrides the decimals function in inherited contracts to return the custom vault decimals. * @return The decimals of the vault's shares. */ function decimals() public view override returns (uint8) { return _decimals; } /** * @notice Toggles the withdrawals paused state * @dev Can only be called by the owner. Emits a `WithdrawalPausedToggled` event. * @param withdrawalsPaused_ The new state of the withdrawals paused state */ function toggleWithdrawalsPaused(bool withdrawalsPaused_) external onlyOwner { emit WithdrawalPausedToggled(withdrawalsPaused, withdrawalsPaused_); withdrawalsPaused = withdrawalsPaused_; } /** * @notice Pauses all deposit and withdrawal functions. * @dev Can only be called by the owner. Emits a `Paused` event. */ function pause() public takeFees onlyOwner { _pause(); } function isUpgradeable() external pure virtual returns (bool) { return false; } /** * @notice Unpauses the vault, allowing deposit and withdrawal functions. * @dev Can only be called by the owner. Emits an `Unpaused` event. */ function unpause() public takeFees onlyOwner { _unpause(); feesUpdatedAt = block.timestamp; } /** * @notice Sets the queue as mandatory (true) or optional (false) * @dev Can only be called by the owner */ function setIsQueueMandatory(bool _isQueueMandatory) external onlyOwner { bool previous = isQueueMandatory; isQueueMandatory = _isQueueMandatory; emit IsQueueMandatoryUpdated(previous, _isQueueMandatory); } // ========== PUBLIC ENTRY DEPOSIT/WITHDRAW ============= /** * @notice Allows a user to deposit assets into the vault in exchange for shares. * @dev This function is a wrapper that calls the main deposit function with the sender's address as the receiver. * @param assets_ The amount of assets to deposit. * @return The number of shares minted for the deposited assets. */ function deposit(uint256 assets_) external returns (uint256) { return deposit(assets_, msg.sender); } /** * @notice Deposits assets into the vault on behalf of a receiver, in exchange for shares. * @dev Calculates the deposit fee, mints shares to the fee recipient and the receiver, then transfers the assets from the sender. * If the vault is not idle, it also allocates the assets into the strategies according to their allocation. * @param assets_ The amount of assets to deposit. * @param receiver_ The address for which the shares will be minted. * @return shares The number of shares minted for the deposited assets. */ // We're not using the timestamp for comparisions // slither-disable-next-line timestamp function deposit(uint256 assets_, address receiver_) public override nonReentrant whenNotPaused takeFees returns (uint256 shares) { _validateAndUpdateDepositTimestamps(receiver_); if (totalAssets() + assets_ > depositLimit) { revert MaxError(); } // Calculate shares based on whether sender is fee recipient if (msg.sender == feeRecipient) { shares = _convertToShares(assets_, Math.Rounding.Floor); } else { // Calculate the fee in shares uint256 feeShares = _convertToShares( assets_.mulDiv(uint256(fees.depositFee), MAX_BASIS_POINTS, Math.Rounding.Ceil), Math.Rounding.Ceil ); // Calculate the net shares to mint for the deposited assets shares = _convertToShares(assets_, Math.Rounding.Floor) - feeShares; // Mint fee shares to fee recipient if (feeShares > 0) _mint(feeRecipient, feeShares); } if (shares <= DUST) revert ZeroAmount(); _mint(receiver_, shares); IERC20(asset()).safeTransferFrom(msg.sender, address(this), assets_); // Handle strategy allocation if vault is not idle if (!vaultIdle) { StrategyHelper.depositIntoStrategies(strategies, assets_, address(this), true); } emit Deposit(msg.sender, receiver_, assets_, shares); } /** * @notice Allows a user to mint shares in exchange for assets. * @dev This function is a wrapper that calls the main mint function with the sender's address as the receiver. * @param shares_ The number of shares to mint. * @return The amount of assets deposited in exchange for the minted shares. */ function mint(uint256 shares_) external returns (uint256) { return mint(shares_, msg.sender); } /** * @notice Mints shares on behalf of a receiver, in exchange for assets. * @dev Calculates the deposit fee in shares, mints shares to the fee recipient and the receiver, then transfers the assets from the sender. * If the vault is not idle, it also allocates the assets into the strategies according to their allocation. * @param shares_ The number of shares to mint. * @param receiver_ The address for which the shares will be minted. * @return assets The amount of assets deposited in exchange for the minted shares. */ // We're not using the timestamp for comparisions // slither-disable-next-line timestamp function mint(uint256 shares_, address receiver_) public override nonReentrant whenNotPaused takeFees returns (uint256 assets) { _validateAndUpdateDepositTimestamps(receiver_); if (shares_ <= DUST) revert ZeroAmount(); // Calculate the deposit fee in shares uint256 depositFee = uint256(fees.depositFee); uint256 feeShares = msg.sender != feeRecipient ? shares_.mulDiv(MAX_BASIS_POINTS, MAX_BASIS_POINTS - depositFee, Math.Rounding.Ceil) - shares_ : 0; // Calculate the total assets required for the minted shares, including fees assets = _convertToAssets(shares_ + feeShares, Math.Rounding.Ceil); if (totalAssets() + assets > depositLimit) revert MaxError(); if (assets > maxDeposit(receiver_)) revert MaxError(); // Mint shares to fee recipient and receiver if (feeShares > 0) _mint(feeRecipient, feeShares); _mint(receiver_, shares_); // Transfer the assets from the sender to the vault IERC20(asset()).safeTransferFrom(msg.sender, address(this), assets); // If the vault is not idle, allocate the assets into strategies if (!vaultIdle) { StrategyHelper.depositIntoStrategies(strategies, assets, address(this), true); } emit Deposit(msg.sender, receiver_, assets, shares_); } /** * @notice Redeems shares for the caller and sends the assets to the caller. * @dev This is a convenience function that calls the main redeem function with the caller as both receiver and owner. * @param shares_ The number of shares to redeem. * @return assets The amount of assets returned in exchange for the redeemed shares. */ function redeem(uint256 shares_) external returns (uint256) { return redeem(shares_, msg.sender, msg.sender); } /** * @notice Redeems shares on behalf of an owner and sends the assets to a receiver. * @dev Redeems the specified amount of shares from the owner's balance, deducts the withdrawal fee in shares, burns the shares, and sends the assets to the receiver. * If the caller is not the owner, it requires approval. * @param shares_ The number of shares to redeem. * @param receiver_ The address to receive the assets. * @param owner_ The owner of the shares being redeemed. * @return assets The expected amount of assets returned in exchange for the redeemed shares calculated for current block. */ function redeem(uint256 shares_, address receiver_, address owner_) public override nonReentrant whenNotPaused takeFees returns (uint256 assets) { VaultActionsHelper.validateRedeemParams(receiver_, shares_, maxRedeem(owner_)); if (shares_ <= DUST) revert ZeroAmount(); uint256 feeShares = msg.sender != feeRecipient ? shares_.mulDiv(uint256(fees.withdrawalFee), MAX_BASIS_POINTS, Math.Rounding.Ceil) : 0; assets = _convertToAssets(shares_ - feeShares, Math.Rounding.Floor); _redeem(shares_, receiver_, owner_, feeShares, assets); } /** * @notice Withdraws a specified amount of assets for the caller. * @dev This is a convenience function that calls the main withdraw function with the caller as both receiver and owner. * @param assets_ The amount of assets to withdraw. * @return shares The number of shares burned in exchange for the withdrawn assets. */ function withdraw(uint256 assets_) external returns (uint256) { return withdraw(assets_, msg.sender, msg.sender); } function maxWithdraw(address owner) public view virtual override returns (uint256) { return _maxWithdraw(owner); } function _maxWithdraw(address owner) internal view returns (uint256) { if (paused() || withdrawalsPaused) return 0; // Get the raw max withdrawal amount uint256 rawMaxWithdraw = _convertToAssets(balanceOf(owner), Math.Rounding.Floor); // Calculate pending fees (in assets) uint256 pendingFees = accruedProtocolFee() + accruedPerformanceFee(); uint256 ownerFees = pendingFees.mulDiv(rawMaxWithdraw, totalAssets(), Math.Rounding.Floor); // Return max withdraw minus pending fees uint256 maxWithdrawMinusFees = rawMaxWithdraw.mulDiv(MAX_BASIS_POINTS - fees.withdrawalFee, MAX_BASIS_POINTS, Math.Rounding.Floor) - ownerFees; if (address(withdrawalQueue) != address(0)) { return maxWithdrawMinusFees; } else { uint256 availableAssets = getAvailableAssetsForWithdrawal(); return availableAssets >= maxWithdrawMinusFees ? maxWithdrawMinusFees : availableAssets; } } function maxRedeem(address owner) public view virtual override returns (uint256) { if (paused() || withdrawalsPaused) return 0; uint256 userShares = balanceOf(owner); if (address(withdrawalQueue) != address(0)) { return userShares; } uint256 availableAssets = getAvailableAssetsForWithdrawal(); uint256 availableAssetsInShares = _convertToShares(availableAssets, Math.Rounding.Floor); return availableAssetsInShares >= userShares ? userShares : availableAssetsInShares; } /** * @notice Redeems shares corresponding to the specified amount of assets on behalf of an owner and sends corresponding assets to the receiver. * @dev Calculates the number of shares equivalent to the assets requested, deducts the withdrawal fee in shares, burns the shares, and sends the corresponding assets to the receiver. * If the caller is not the owner, it requires approval. * @param assets_ The amount of assets to withdraw. * @param receiver_ The address to receive the withdrawn assets. * @param owner_ The owner of the shares equivalent to the assets being withdrawn. * @return shares The number of shares burned in exchange for the withdrawn assets. */ // We're not using the timestamp for comparisions // slither-disable-next-line timestamp function withdraw(uint256 assets_, address receiver_, address owner_) public override nonReentrant whenNotPaused takeFees returns (uint256 shares) { if (receiver_ == address(0)) revert InvalidRecipient(); if (assets_ > maxWithdraw(owner_)) revert MaxError(); shares = _convertToShares(assets_, Math.Rounding.Ceil); if (shares <= DUST) revert ZeroAmount(); // If msg.sender is the withdrawal queue, go straght to the actual withdrawal uint256 withdrawalFee = uint256(fees.withdrawalFee); uint256 feeShares = msg.sender != feeRecipient ? shares.mulDiv(MAX_BASIS_POINTS, MAX_BASIS_POINTS - withdrawalFee, Math.Rounding.Ceil) - shares : 0; shares += feeShares; _redeem(shares, receiver_, owner_, feeShares, assets_); } /** * @notice Consumes allowance, burn shares, mint fees and transfer assets to receiver * @dev internal function for redeem and withdraw * @param sharesToRedeem The amount of shares to redeem including fees. * @param receiver_ The address to receive the withdrawn assets. * @param owner_ The owner of the shares equivalent to the assets being withdrawn. * @param feeShares The owner of the shares equivalent to the assets being withdrawn. */ // We're not using the timestamp for comparisions // slither-disable-next-line timestamp function _redeem(uint256 sharesToRedeem, address receiver_, address owner_, uint256 feeShares, uint256 assets) private { if (withdrawalsPaused) revert WithdrawalsPaused(); if (msg.sender != owner_) { _spendAllowance(owner_, msg.sender, sharesToRedeem); } _burn(owner_, sharesToRedeem); if (feeShares > 0) _mint(feeRecipient, feeShares); uint256 availableAssetsForWithdrawal = getAvailableAssetsForWithdrawal(); WithdrawalQueueHelper.processWithdrawal( assets, sharesToRedeem - feeShares, receiver_, availableAssetsForWithdrawal, asset(), address(withdrawalQueue), minQueueRequest, strategies, parkingLot, isQueueMandatory ); emit Withdraw(msg.sender, receiver_, owner_, assets, sharesToRedeem); } function getRewardTokens() public view returns (address[] memory) { return rewardAddresses; } function getAvailableAssetsForWithdrawal() public view returns (uint256) { return WithdrawalQueueHelper.getAvailableAssetsForWithdrawal(asset(), strategies); } /** * @notice Updates the user rewards to the current reward index. * @dev Calculates the rewards to be transferred to the user based on the difference between the current and previous reward indexes. * @param userAddress The address of the user to update rewards for. */ //slither-disable-next-line unused-return,calls-loop,reentrancy-no-eth function getUserRewards(address userAddress) external view returns (ReturnedRewards[] memory) { return RewardsHelper.getUserRewards( balanceOf(userAddress), userAddress, rewardAddresses, rewardIndex, userRewardIndex ); } // function to return all the rewards claimed by a user for all the reward tokens in the vault function getTotalRewardsClaimed(address userAddress) external view returns (ReturnedRewards[] memory) { return RewardsHelper.getTotalRewardsClaimed(rewardAddresses, totalRewardsClaimed, userAddress); } // ================= ACCOUNTING ===================== /** * @notice Calculates the total assets under management in the vault, including those allocated to strategies. * @dev Sums the balance of the vault's asset held directly and the assets managed by each strategy. * @return total The total assets under management in the vault. */ function totalAssets() public view override returns (uint256 total) { total = VaultActionsHelper.getTotalAssets(IERC20(asset()).balanceOf(address(this)), strategies); } /** * @notice Calculates the total supply of the vault shares, including shares pending redemption. * @dev Sums the balance of the vault's total supply and queue pending shares - unfinalizedAmount. * @return total The total supply of the the vault shares. */ function totalSupply() public view override(ERC20Upgradeable, IERC20) returns (uint256 total) { total = VaultActionsHelper.getTotalSupply(super.totalSupply(), withdrawalQueue); } /** * @notice Provides a preview of the number of shares that would be minted for a given deposit amount, after fees. * @dev Calculates the deposit fee and subtracts it from the deposit amount to determine the net amount for share conversion. * @param assets_ The amount of assets to be deposited. * @return The number of shares that would be minted for the given deposit amount. */ function previewDeposit(uint256 assets_) public view override returns (uint256) { // Calculate gross shares first uint256 grossShares = _convertToShares(assets_, Math.Rounding.Floor); // Calculate fee shares using same formula and rounding as deposit uint256 feeShares = msg.sender != feeRecipient ? _convertToShares( assets_.mulDiv(uint256(fees.depositFee), MAX_BASIS_POINTS, Math.Rounding.Ceil), Math.Rounding.Ceil ) : 0; // Return net shares return grossShares - feeShares; } /** * @notice Provides a preview of the amount of assets required to mint a specific number of shares, after accounting for deposit fees. * @dev Adds the deposit fee to the share amount to determine the gross amount for asset conversion. * @param shares_ The number of shares to be minted. * @return The amount of assets required to mint the specified number of shares. */ function previewMint(uint256 shares_) public view override returns (uint256) { uint256 grossShares = shares_; if (msg.sender != feeRecipient) { grossShares = shares_.mulDiv(MAX_BASIS_POINTS, MAX_BASIS_POINTS - fees.depositFee, Math.Rounding.Ceil); } return _convertToAssets(grossShares, Math.Rounding.Ceil); } /** * @notice Provides a preview of the number of shares that would be burned for a given withdrawal amount, after fees. * @dev Calculates the withdrawal fee and adds it to the share amount to determine the gross shares for asset conversion. * @param assets_ The amount of assets to be withdrawn. * @return shares The number of shares that would be burned for the given withdrawal amount. */ function previewWithdraw(uint256 assets_) public view override returns (uint256 shares) { shares = _convertToShares(assets_, Math.Rounding.Ceil); shares = msg.sender != feeRecipient ? shares.mulDiv(MAX_BASIS_POINTS, MAX_BASIS_POINTS - fees.withdrawalFee, Math.Rounding.Floor) : shares; } /** * @notice Provides a preview of the amount of assets that would be redeemed for a specific number of shares, after withdrawal fees. * @dev Subtracts the withdrawal fee from the share amount to determine the net shares for asset conversion. * @param shares_ The number of shares to be redeemed. * @return The amount of assets that would be redeemed for the specified number of shares. */ function previewRedeem(uint256 shares_) public view override returns (uint256) { if (msg.sender == feeRecipient) { // Fee recipient gets exact conversion return _convertToAssets(shares_, Math.Rounding.Floor); } uint256 feeShares = shares_.mulDiv(uint256(fees.withdrawalFee), MAX_BASIS_POINTS, Math.Rounding.Ceil); return _convertToAssets(shares_ - feeShares, Math.Rounding.Floor); } /** * @notice Calculates the maximum amount of assets that can be minted, considering the deposit limit and current total assets. * @dev Returns zero if the vault is paused or if the total assets are equal to or exceed the deposit limit. * @return The maximum amount of assets that can be minted. */ //We're not using the timestamp for comparisions //slither-disable-next-line timestamp function maxDeposit(address) public view override returns (uint256) { return (paused() || totalAssets() >= depositLimit) ? 0 : depositLimit - totalAssets(); } /** * @notice Calculates the maximum amount of assets that can be minted, considering the deposit limit and current total assets. * @dev Returns zero if the vault is paused or if the total assets are equal to or exceed the deposit limit. * @return The maximum amount of assets that can be minted. */ //We're not using the timestamp for comparisions //slither-disable-next-line timestamp function maxMint(address) public view override returns (uint256) { if (paused() || depositLimit == 0) return 0; if (depositLimit == type(uint256).max) return type(uint256).max; uint256 supplyLimit = _convertToShares(depositLimit, Math.Rounding.Floor); return supplyLimit - totalSupply(); } /** * @notice Converts an amount of assets to the equivalent amount of shares, considering the current share price and applying the specified rounding. * @dev Utilizes the total supply and total assets to calculate the share price for conversion. * @param assets The amount of assets to convert to shares. * @param rounding The rounding direction to use for the conversion. * @return shares The equivalent amount of shares for the given assets. */ function _convertToShares(uint256 assets, Math.Rounding rounding) internal view override returns (uint256 shares) { shares = assets.mulDiv(totalSupply() + 10 ** decimalOffset, totalAssets() + 1, rounding); } /** * @notice Converts an amount of shares to the equivalent amount of assets, considering the current share price and applying the specified rounding. * @dev Utilizes the total assets and total supply to calculate the asset price for conversion. * @param shares The amount of shares to convert to assets. * @param rounding The rounding direction to use for the conversion. * @return The equivalent amount of assets for the given shares. */ function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual override returns (uint256) { return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** decimalOffset, rounding); } // ============ FEE ACCOUNTING ===================== /** * @notice Calculates the accrued protocol fee based on the current protocol fee rate and time elapsed. * @dev The protocol fee is calculated as a percentage of the total assets, prorated over time since the last fee update. * @return The accrued protocol fee in asset units. */ function accruedProtocolFee() public view whenNotPaused returns (uint256) { // Only calculate if a protocol fee is set return FeesHelper.accruedProtocolFee(fees.protocolFee, totalAssets(), feesUpdatedAt); } /** * @notice Calculates the accrued performance fee based on the vault's performance relative to the high water mark. * @dev The performance fee is calculated as a percentage of the profit (asset value increase) since the last high water mark update. * @return fee The accrued performance fee in asset units. */ // We're not using the timestamp for comparisions // slither-disable-next-line timestamp function accruedPerformanceFee() public view returns (uint256) { // Calculate the share value in assets uint256 shareValue = convertToAssets(1e18); // Only calculate if a performance fee is set and the share value exceeds the high water mark return FeesHelper.accruedPerformanceFee( fees.performanceFee, totalAssets(), shareValue, highWaterMark, asset(), fees ); } /** * @notice Retrieves the current fee structure of the vault. * @dev Returns the vault's fees including deposit, withdrawal, protocol, and performance fees. * @return A `VaultFees` struct containing the current fee rates. */ function getVaultFees() public view returns (VaultFees memory) { return fees; } // ============== FEE LOGIC =================== /** * @notice Placeholder function for taking portfolio and protocol fees. * @dev This function is intended to be overridden with actual fee-taking logic. */ function takePortfolioAndProtocolFees() external nonReentrant takeFees onlyOwner { // Intentionally left blank for override } /** * @notice Updates the vault's fee structure. * @dev Can only be called by the vault owner. Emits an event upon successful update. * @param newFees_ The new fee structure to apply to the vault. */ function setVaultFees(VaultFees calldata newFees_) external takeFees onlyOwner { fees = newFees_; // Update the fee structure feesUpdatedAt = block.timestamp; // Record the time of the fee update } /** * @notice Sets a new fee recipient address for the vault. * @dev Can only be called by the vault owner. Reverts if the new recipient address is the zero address. * @param newRecipient_ The address of the new fee recipient. */ function setFeeRecipient(address newRecipient_) external onlyOwner { // Validate the new recipient address if (newRecipient_ == address(0)) revert InvalidFeeRecipient(); // Emit an event for the fee recipient update emit FeeRecipientUpdated(feeRecipient, newRecipient_); feeRecipient = newRecipient_; // Update the fee recipient } /** * @notice Sets a minimum amount required to queue a withdrawal request. * @param minQueueRequest_ The address of the new fee recipient. */ function setMinimunQueueRequest(uint256 minQueueRequest_) external onlyOwner { emit MinimunQueueRequestUpdated(minQueueRequest, minQueueRequest_); minQueueRequest = minQueueRequest_; } /** * @notice Sets a new fee recipient address for the vault. * @dev Can only be called by the vault owner. Reverts if the new recipient address is the zero address. * @param withdrawalQueue_ The address of the new withdrawlQueue. */ function setWithdrawalQueue(address withdrawalQueue_) external onlyOwner { withdrawalQueue = WithdrawalQueueHelper.setWithdrawalQueue(address(withdrawalQueue), withdrawalQueue_); } /** * @notice Sets a new parking lot address for the vault. * @dev Can only be called by the vault owner. Reverts if the new parking lot address is the zero address. * @param parkingLot_ The address of the new parking lot. */ function setParkingLot(address parkingLot_) external onlyOwner { // Validate the new recipient address if (parkingLot_ == address(0)) revert InvalidParkingLot(); // create a success // Emit an event for the fee recipient update address token = asset(); address currentParkingLot = address(parkingLot); if (currentParkingLot != address(0)) TokenHelper.attemptForceApprove(token, currentParkingLot, 0, true); TokenHelper.attemptForceApprove(token, parkingLot_, type(uint256).max, true); emit ParkingLotUpdated(currentParkingLot, parkingLot_, true); parkingLot = IParkingLot(parkingLot_); // Update the fee recipient } // ============= STRATEGIES =================== /** * @notice Retrieves the current strategies employed by the vault. * @dev Returns an array of `Strategy` structs representing each strategy. * @return An array of `Strategy` structs. */ function getStrategies() external view returns (Strategy[] memory) { return strategies; } /** * @notice Toggles the vault's idle state. * @dev Can only be called by the vault owner. Emits a `ToggleVaultIdle` event with the previous and new state. */ function toggleVaultIdle() external onlyOwner { emit ToggleVaultIdle(vaultIdle, !vaultIdle); vaultIdle = !vaultIdle; } /** * @notice Adds a new strategy or replaces an existing one. * @dev Can only be called by the vault owner. Validates the total allocation does not exceed 100%. * Emits a `StrategyAdded` or/and `StrategyRemoved` event. * @param index_ The index at which to add or replace the strategy. If replacing, this is the index of the existing strategy. * @param replace_ A boolean indicating whether to replace an existing strategy. * @param newStrategy_ The new strategy to add or replace the existing one with. */ // slither didn't detect the nonReentrant modifier // slither-disable-next-line reentrancy-no-eth function addStrategy(uint256 index_, bool replace_, Strategy calldata newStrategy_) external nonReentrant onlyOwner takeFees { IStrategy newStrategy; IStrategy removedStrategy; (protectStrategy, newStrategy, removedStrategy) = StrategyHelper.addOrReplaceStrategy( strategies, newStrategy_, replace_, index_, protectStrategy, IERC20(asset()) ); if (address(removedStrategy) != address(0)) { emit StrategyRemoved(address(removedStrategy)); } emit StrategyAdded(address(newStrategy)); } /** * @notice Adds a new strategy or replaces an existing one. * @dev Can only be called by the vault owner. Validates that the index to be removed exists. * Emits a `StrategyRemoved` event. * @param index_ The index of the strategy to be removed. */ // slither didn't detect the nonReentrant modifier // slither-disable-next-line reentrancy-no-eth function removeStrategy(uint256 index_) public nonReentrant onlyOwner takeFees { uint256 len = strategies.length; if (index_ >= len) revert InvalidIndex(index_); protectStrategy = StrategyHelper.removeStrategy(strategies, index_, protectStrategy, IERC20(asset())); } /// @notice Emergency function to force remove a strategy when it's unable to withdraw funds /// @dev Should only be used when a strategy is permanently compromised or frozen /// @param index_ The index of the strategy to remove /// @param forceEject_ If true, bypasses the locked assets check function emergencyRemoveStrategy(uint256 index_, bool forceEject_) external onlyOwner { if (index_ >= strategies.length) revert InvalidIndex(index_); protectStrategy = StrategyHelper.emergencyRemoveStrategy(strategies, asset(), index_, forceEject_, protectStrategy); } /** * @notice ERC20 _update function override. */ function _update(address from, address to, uint256 value) internal override { if (from != address(0)) updateUserRewardsToCurrent(from); if (to != address(0)) updateUserRewardsToCurrent(to); super._update(from, to, value); } /** * @notice Changes strategies allocations. * @dev Can only be called by the vault owner. Validates the total allocation does not exceed 100% and the length corresponds with the strategies array. * Emits a `StrategyAllocationsChanged` * @param allocations_ The array with the new allocations. * @param redistribute A boolean indicating whether to redistributes allocations. */ function changeAllocations(Allocation[] calldata allocations_, bool redistribute) external nonReentrant onlyOwner takeFees { StrategyHelper.changeAllocations(strategies, allocations_, redistribute, asset()); } /** * @notice Pushes funds from the vault into all strategies based on their allocation. * @dev Can only be called by the vault owner. Reverts if the vault is idle. */ function pushFundsToStrategies() public onlyOwner { if (vaultIdle) revert VaultIsIdle(); // Call the library function to distribute assets StrategyHelper.distributeAssetsToStrategies(strategies, IERC20(asset()).balanceOf(address(this))); } /** * @notice Pulls funds back from all strategies into the vault. * @dev Can only be called by the vault owner. */ // We are aware that we aren't using the return value // We control both the length of the array and the external call //slither-disable-next-line unused-return,calls-loop function pullFundsFromStrategies() public onlyOwner { StrategyHelper.pullFundsFromStrategies(strategies); } /** * @notice Pulls funds back from a single strategy into the vault. * @dev Can only be called by the vault owner. * @param index_ The index of the strategy from which to pull funds. */ // We are aware that we aren't using the return value // We control both the length of the array and the external call //slither-disable-next-line unused-return,calls-loop function pullFundsFromSingleStrategy(uint256 index_) public onlyOwner { StrategyHelper.pullFundsFromSingleStrategy(strategies, index_); } /** * @notice Pushes funds from the vault into a single strategy based on its allocation. * @dev Can only be called by the vault owner. Reverts if the vault is idle. * @param index_ The index of the strategy into which to push funds. */ function pushFundsIntoSingleStrategy(uint256 index_) external onlyOwner { StrategyHelper.pushFundsIntoSingleStrategyNoAmount( strategies, IERC20(asset()).balanceOf(address(this)), index_, vaultIdle ); } /** * @notice Pushes the amount sent from the vault into a single strategy. * @dev Can only be called by the vault owner. Reverts if the vault is idle. * @param index_ The index of the strategy into which to push funds. * @param amount The index of the strategy into which to push funds. */ function pushFundsIntoSingleStrategy(uint256 index_, uint256 amount) external onlyOwner { StrategyHelper.pushFundsIntoSingleStrategy( strategies, vaultIdle, IERC20(asset()).balanceOf(address(this)), index_, amount ); } /** * @notice Sets a new deposit limit for the vault. * @dev Can only be called by the vault owner. Emits a `DepositLimitSet` event with the new limit. * @param newLimit_ The new deposit limit to set. */ function setDepositLimit(uint256 newLimit_) external onlyOwner { depositLimit = newLimit_; emit DepositLimitSet(newLimit_); } /** * @notice Harvest rewards on every strategy. * @dev Calculates de reward index for each reward found. */ //we control the external call //slither-disable-next-line unused-return,calls-loop,reentrancy-no-eth function harvestRewards(bytes calldata encodedData) external nonReentrant onlyOwner { uint256[] memory indices; bytes[] memory data; if (encodedData.length != 0) { (indices, data) = abi.decode(encodedData, (uint256[], bytes[])); } uint256 totalSupply = totalSupply(); bytes memory rewardsData; uint256 lenIndices = indices.length; uint256 lenStrategies = strategies.length; uint256 lenRewards; for (uint256 i; i < lenStrategies;) { //We control both the length of the array and the external call //slither-disable-next-line unused-return,calls-loop for (uint256 k = 0; k < lenIndices;) { if (indices[k] == i) { rewardsData = data[k]; break; } rewardsData = ""; unchecked { k++; } } ReturnedRewards[] memory returnedRewards = strategies[i].strategy.harvestRewards(rewardsData); lenRewards = returnedRewards.length; for (uint256 j; j < lenRewards;) { uint256 amount = returnedRewards[j].rewardAmount; address rewardToken = returnedRewards[j].rewardAddress; if (amount != 0) { if (rewardIndex[rewardToken] == 0) { rewardAddresses.push(rewardToken); } if (totalSupply > 0) { rewardIndex[rewardToken] += amount.mulDiv(PRECISION, totalSupply, Math.Rounding.Floor); } } unchecked { j++; } } unchecked { i++; } } emit RewardsHarvested(); } /** * @notice Updates the user rewards to the current reward index. * @dev Calculates the rewards to be transferred to the user based on the difference between the current and previous reward indexes. * @param userAddress The address of the user to update rewards for. */ //slither-disable-next-line unused-return,calls-loop,reentrancy-no-eth function updateUserRewardsToCurrent(address userAddress) private { RewardsHelper.updateUserRewardsToCurrent( balanceOf(userAddress), userAddress, rewardAddresses, rewardIndex, userRewardIndex, totalRewardsClaimed ); } /** * @notice Claims multiple withdrawal requests starting from the lasFinalizedRequestId. * @dev This function allows the contract owner to claim multiple withdrawal requests in batches. * @param maxRequests The maximum number of withdrawal requests to be processed in this batch. */ function batchClaimWithdrawal(uint256 maxRequests) external onlyOwner nonReentrant { if (address(withdrawalQueue) == address(0)) revert QueueNotSet(); uint256 availableAssetsInShares = _convertToShares(getAvailableAssetsForWithdrawal(), Math.Rounding.Floor); WithdrawalQueueHelper.BatchClaimParams memory params = WithdrawalQueueHelper.BatchClaimParams(totalAssets(), totalSupply(), decimalOffset); WithdrawalQueueHelper.batchClaim( withdrawalQueue, maxRequests, availableAssetsInShares, asset(), strategies, parkingLot, params ); } function claimRewards() external { updateUserRewardsToCurrent(msg.sender); } /** * @notice Requests funds from available assets. * @dev This function allows the protect strategy to request funds from available assets, withdraws from other strategies if necessary, * and deposits the requested funds into the protect strategy. * @param amount The amount of funds to request. */ //we control the external call, only callable by the protect strategy //slither-disable-next-line calls-loop,,reentrancy-events function requestFunds(uint256 amount) external onlyProtect { uint256 acumulated = MultiStrategyVaultHelper.withdrawAssets(asset(), amount, protectStrategy, strategies); WithdrawalQueueHelper.requestFunds(amount, acumulated, protectStrategy); } // Helper function //////////////////////// function _validateAndUpdateDepositTimestamps(address receiver_) private { if (receiver_ == address(0)) revert InvalidRecipient(); if (firstDeposit == 0) { firstDeposit = block.timestamp; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard struct ReentrancyGuardStorage { uint256 _status; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) { assembly { $.slot := ReentrancyGuardStorageLocation } } /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); $._status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // On the first call to nonReentrant, _status will be NOT_ENTERED if ($._status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail $._status = ENTERED; } function _nonReentrantAfter() private { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) $._status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); return $._status == ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol) pragma solidity ^0.8.20; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {ERC20Upgradeable} from "../ERC20Upgradeable.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {Initializable} from "../../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. * * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this * contract and not the "assets" token which is an independent contract. * * [CAUTION] * ==== * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by * verifying the amount received is as expected, using a wrapper that performs these checks such as * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]. * * Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()` * corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault * decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself * determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset * (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's * donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more * expensive than it is profitable. More details about the underlying math can be found * xref:erc4626.adoc#inflation-attack[here]. * * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the * `_convertToShares` and `_convertToAssets` functions. * * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide]. * ==== */ abstract contract ERC4626Upgradeable is Initializable, ERC20Upgradeable, IERC4626 { using Math for uint256; /// @custom:storage-location erc7201:openzeppelin.storage.ERC4626 struct ERC4626Storage { IERC20 _asset; uint8 _underlyingDecimals; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC4626")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ERC4626StorageLocation = 0x0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e00; function _getERC4626Storage() private pure returns (ERC4626Storage storage $) { assembly { $.slot := ERC4626StorageLocation } } /** * @dev Attempted to deposit more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max); /** * @dev Attempted to mint more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max); /** * @dev Attempted to withdraw more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max); /** * @dev Attempted to redeem more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max); /** * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). */ function __ERC4626_init(IERC20 asset_) internal onlyInitializing { __ERC4626_init_unchained(asset_); } function __ERC4626_init_unchained(IERC20 asset_) internal onlyInitializing { ERC4626Storage storage $ = _getERC4626Storage(); (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_); $._underlyingDecimals = success ? assetDecimals : 18; $._asset = asset_; } /** * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way. */ function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) { (bool success, bytes memory encodedDecimals) = address(asset_).staticcall( abi.encodeCall(IERC20Metadata.decimals, ()) ); if (success && encodedDecimals.length >= 32) { uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256)); if (returnedDecimals <= type(uint8).max) { return (true, uint8(returnedDecimals)); } } return (false, 0); } /** * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals. * * See {IERC20Metadata-decimals}. */ function decimals() public view virtual override(IERC20Metadata, ERC20Upgradeable) returns (uint8) { ERC4626Storage storage $ = _getERC4626Storage(); return $._underlyingDecimals + _decimalsOffset(); } /** @dev See {IERC4626-asset}. */ function asset() public view virtual returns (address) { ERC4626Storage storage $ = _getERC4626Storage(); return address($._asset); } /** @dev See {IERC4626-totalAssets}. */ function totalAssets() public view virtual returns (uint256) { ERC4626Storage storage $ = _getERC4626Storage(); return $._asset.balanceOf(address(this)); } /** @dev See {IERC4626-convertToShares}. */ function convertToShares(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-convertToAssets}. */ function convertToAssets(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-maxDeposit}. */ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxMint}. */ function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxWithdraw}. */ function maxWithdraw(address owner) public view virtual returns (uint256) { return _convertToAssets(balanceOf(owner), Math.Rounding.Floor); } /** @dev See {IERC4626-maxRedeem}. */ function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf(owner); } /** @dev See {IERC4626-previewDeposit}. */ function previewDeposit(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-previewMint}. */ function previewMint(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewWithdraw}. */ function previewWithdraw(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewRedeem}. */ function previewRedeem(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-deposit}. */ function deposit(uint256 assets, address receiver) public virtual returns (uint256) { uint256 maxAssets = maxDeposit(receiver); if (assets > maxAssets) { revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets); } uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares); return shares; } /** @dev See {IERC4626-mint}. * * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero. * In this case, the shares will be minted without requiring any assets to be deposited. */ function mint(uint256 shares, address receiver) public virtual returns (uint256) { uint256 maxShares = maxMint(receiver); if (shares > maxShares) { revert ERC4626ExceededMaxMint(receiver, shares, maxShares); } uint256 assets = previewMint(shares); _deposit(_msgSender(), receiver, assets, shares); return assets; } /** @dev See {IERC4626-withdraw}. */ function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) { uint256 maxAssets = maxWithdraw(owner); if (assets > maxAssets) { revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets); } uint256 shares = previewWithdraw(assets); _withdraw(_msgSender(), receiver, owner, assets, shares); return shares; } /** @dev See {IERC4626-redeem}. */ function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) { uint256 maxShares = maxRedeem(owner); if (shares > maxShares) { revert ERC4626ExceededMaxRedeem(owner, shares, maxShares); } uint256 assets = previewRedeem(shares); _withdraw(_msgSender(), receiver, owner, assets, shares); return assets; } /** * @dev Internal conversion function (from assets to shares) with support for rounding direction. */ function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) { return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding); } /** * @dev Internal conversion function (from shares to assets) with support for rounding direction. */ function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) { return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding); } /** * @dev Deposit/mint common workflow. */ function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual { ERC4626Storage storage $ = _getERC4626Storage(); // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the // assets are transferred and before the shares are minted, which is a valid state. // slither-disable-next-line reentrancy-no-eth SafeERC20.safeTransferFrom($._asset, caller, address(this), assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } /** * @dev Withdraw/redeem common workflow. */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal virtual { ERC4626Storage storage $ = _getERC4626Storage(); if (caller != owner) { _spendAllowance(owner, caller, shares); } // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the // shares are burned and after the assets are transferred, which is a valid state. _burn(owner, shares); SafeERC20.safeTransfer($._asset, receiver, assets); emit Withdraw(caller, receiver, owner, assets, shares); } function _decimalsOffset() internal view virtual returns (uint8) { return 0; } }
//SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; uint256 constant MAX_BASIS_POINTS = 10_000; // Maximum basis points value uint256 constant PRECISION = 1e36; uint32 constant DUST = 1e8; uint32 constant ROUNDING_DELTA = 10; uint256 constant SECONDS_PER_YEAR = 365.25 days;
//SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; struct ReturnedRewards { address rewardAddress; uint256 rewardAmount; } interface IStrategy is IERC4626 { function getAvailableAssetsForWithdrawal() external view returns (uint256); function isProtectStrategy() external returns (bool); function harvestRewards(bytes memory) external returns (ReturnedRewards[] memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol) pragma solidity ^0.8.20; /** * @dev Standard ERC20 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens. */ interface IERC20Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC20InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC20InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. * @param spender Address that may be allowed to operate on tokens without being their owner. * @param allowance Amount of tokens a `spender` is allowed to operate with. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC20InvalidApprover(address approver); /** * @dev Indicates a failure with the `spender` to be approved. Used in approvals. * @param spender Address that may be allowed to operate on tokens without being their owner. */ error ERC20InvalidSpender(address spender); } /** * @dev Standard ERC721 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20. * Used in balance queries. * @param owner Address of the current owner of a token. */ error ERC721InvalidOwner(address owner); /** * @dev Indicates a `tokenId` whose `owner` is the zero address. * @param tokenId Identifier number of a token. */ error ERC721NonexistentToken(uint256 tokenId); /** * @dev Indicates an error related to the ownership over a particular token. Used in transfers. * @param sender Address whose tokens are being transferred. * @param tokenId Identifier number of a token. * @param owner Address of the current owner of a token. */ error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC721InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC721InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param tokenId Identifier number of a token. */ error ERC721InsufficientApproval(address operator, uint256 tokenId); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC721InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC721InvalidOperator(address operator); } /** * @dev Standard ERC1155 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens. */ interface IERC1155Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. * @param tokenId Identifier number of a token. */ error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC1155InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC1155InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param owner Address of the current owner of a token. */ error ERC1155MissingApprovalForAll(address operator, address owner); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC1155InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC1155InvalidOperator(address operator); /** * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. * Used in batch transfers. * @param idsLength Length of the array of token identifiers * @param valuesLength Length of the array of token amounts */ error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // ? `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // ? `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Ownable struct OwnableStorage { address _owner; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; function _getOwnableStorage() private pure returns (OwnableStorage storage $) { assembly { $.slot := OwnableStorageLocation } } /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ function __Ownable_init(address initialOwner) internal onlyInitializing { __Ownable_init_unchained(initialOwner); } function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { OwnableStorage storage $ = _getOwnableStorage(); return $._owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { OwnableStorage storage $ = _getOwnableStorage(); address oldOwner = $._owner; $._owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC20Metadata} from "./extensions/IERC20Metadata.sol"; import {Context} from "../../utils/Context.sol"; import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. */ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { mapping(address account => uint256) private _balances; mapping(address account => mapping(address spender => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev 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. This is the default value returned by this function, unless * it's overridden. * * 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 virtual returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `value`. */ function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `value`. * - the caller must have allowance for ``from``'s tokens of at least * `value`. */ function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; } /** * @dev Moves a `value` amount of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); } /** * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding * this function. * * Emits a {Transfer} event. */ function _update(address from, address to, uint256 value) internal virtual { if (from == address(0)) { // Overflow check required: The rest of the code assumes that totalSupply never overflows _totalSupply += value; } else { uint256 fromBalance = _balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { // Overflow not possible: value <= fromBalance <= totalSupply. _balances[from] = fromBalance - value; } } if (to == address(0)) { unchecked { // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. _totalSupply -= value; } } else { unchecked { // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. _balances[to] += value; } } emit Transfer(from, to, value); } /** * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). * Relies on the `_update` mechanism * * Emits a {Transfer} event with `from` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } /** * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. * Relies on the `_update` mechanism. * * Emits a {Transfer} event with `to` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead */ function _burn(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidSender(address(0)); } _update(account, address(0), value); } /** * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); } /** * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. * * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any * `Approval` event during `transferFrom` operations. * * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to * true using the following override: * ``` * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { * super._approve(owner, spender, value, true); * } * ``` * * Requirements are the same as {_approve}. */ function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } _allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } } /** * @dev Updates `owner` s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. * * Does not emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol"; import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. */ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors { /// @custom:storage-location erc7201:openzeppelin.storage.ERC20 struct ERC20Storage { mapping(address account => uint256) _balances; mapping(address account => mapping(address spender => uint256)) _allowances; uint256 _totalSupply; string _name; string _symbol; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00; function _getERC20Storage() private pure returns (ERC20Storage storage $) { assembly { $.slot := ERC20StorageLocation } } /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing { __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing { ERC20Storage storage $ = _getERC20Storage(); $._name = name_; $._symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { ERC20Storage storage $ = _getERC20Storage(); return $._name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { ERC20Storage storage $ = _getERC20Storage(); return $._symbol; } /** * @dev 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. This is the default value returned by this function, unless * it's overridden. * * 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 virtual returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual returns (uint256) { ERC20Storage storage $ = _getERC20Storage(); return $._totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual returns (uint256) { ERC20Storage storage $ = _getERC20Storage(); return $._balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `value`. */ function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual returns (uint256) { ERC20Storage storage $ = _getERC20Storage(); return $._allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `value`. * - the caller must have allowance for ``from``'s tokens of at least * `value`. */ function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; } /** * @dev Moves a `value` amount of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); } /** * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding * this function. * * Emits a {Transfer} event. */ function _update(address from, address to, uint256 value) internal virtual { ERC20Storage storage $ = _getERC20Storage(); if (from == address(0)) { // Overflow check required: The rest of the code assumes that totalSupply never overflows $._totalSupply += value; } else { uint256 fromBalance = $._balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { // Overflow not possible: value <= fromBalance <= totalSupply. $._balances[from] = fromBalance - value; } } if (to == address(0)) { unchecked { // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. $._totalSupply -= value; } } else { unchecked { // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. $._balances[to] += value; } } emit Transfer(from, to, value); } /** * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). * Relies on the `_update` mechanism * * Emits a {Transfer} event with `from` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } /** * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. * Relies on the `_update` mechanism. * * Emits a {Transfer} event with `to` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead */ function _burn(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidSender(address(0)); } _update(account, address(0), value); } /** * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); } /** * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. * * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any * `Approval` event during `transferFrom` operations. * * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to * true using the following override: * ``` * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { * super._approve(owner, spender, value, true); * } * ``` * * Requirements are the same as {_approve}. */ function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { ERC20Storage storage $ = _getERC20Storage(); if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } $._allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } } /** * @dev Updates `owner` s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. * * Does not emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol"; import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol"; /** * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. */ interface IERC4626 is IERC20, IERC20Metadata { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); }
//SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {IStrategy, ReturnedRewards} from "../interfaces/IStrategy.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC4626, IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; import {Strategy} from "../interfaces/IConcreteMultiStrategyVault.sol"; import {IWithdrawalQueue} from "../interfaces/IWithdrawalQueue.sol"; import {MAX_BASIS_POINTS, DUST} from "../utils/Constants.sol"; import {Errors} from "../interfaces/Errors.sol"; library VaultActionsHelper { using SafeERC20 for IERC20; using Math for uint256; uint256 private constant PRECISION = 1e36; function getTotalAssets(uint256 total, Strategy[] memory strategies) public view returns (uint256) { for (uint256 i; i < strategies.length;) { //We control both the length of the array and the external call //slither-disable-next-line calls-loop total += strategies[i].strategy.convertToAssets(strategies[i].strategy.balanceOf(address(this))); unchecked { i++; } } return total; } function getTotalSupply(uint256 total, IWithdrawalQueue withdrawalQueue) public view returns (uint256) { uint256 unfinalized = 0; if (address(withdrawalQueue) != address(0)) { unfinalized = withdrawalQueue.unfinalizedAmount(); } return total + unfinalized; } function validateRedeemParams(address receiver_, uint256 shares_, uint256 maxRedeem_) external pure { if (receiver_ == address(0)) revert Errors.InvalidRecipient(); if (shares_ == 0) revert Errors.ZeroAmount(); if (shares_ > maxRedeem_) revert Errors.MaxError(); } }
//SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; import {IStrategy} from "./IStrategy.sol"; // Example performanceFee: [{0000, 500, 300}, {501, 2000, 1000}, {2001, 5000, 2000}, {5001, 10000, 5000}] // == 0-5% increase 3%, 5.01-20% increase 10%, 20.01-50% increase 20%, 50.01-100% increase 50% struct GraduatedFee { uint256 lowerBound; uint256 upperBound; uint64 fee; } ///@notice VaultFees are represented in BPS ///@dev all downstream math needs to be / 10_000 because 10_000 bps == 100% struct VaultFees { uint64 depositFee; uint64 withdrawalFee; uint64 protocolFee; GraduatedFee[] performanceFee; } struct Allocation { uint256 index; uint256 amount; // Represented in BPS of the amount of ETF that should go into strategy } struct Strategy { IStrategy strategy; Allocation allocation; } struct VaultInitParams { address feeRecipient; VaultFees fees; uint256 depositLimit; address owner; } interface IConcreteMultiStrategyVault { event FeeRecipientUpdated(address indexed oldRecipient, address indexed newRecipient); event ParkingLotUpdated(address indexed oldParkingLot, address indexed newParkingLot, bool successfulApproval); event ToggleVaultIdle(bool pastValue, bool newValue); event StrategyAdded(address newStrategy); event StrategyRemoved(address oldStrategy); event DepositLimitSet(uint256 limit); event StrategyAllocationsChanged(Allocation[] newAllocations); event WithdrawalQueueUpdated(address oldQueue, address newQueue); event WithdrawalPausedToggled(bool pastValue, bool newValue); event IsQueueMandatoryUpdated(bool pastValue, bool newValue); function pause() external; function unpause() external; function setVaultFees(VaultFees calldata newFees_) external; function setFeeRecipient(address newRecipient_) external; function toggleVaultIdle() external; function addStrategy(uint256 index_, bool replace_, Strategy calldata newStrategy_) external; function removeStrategy(uint256 index_) external; function emergencyRemoveStrategy(uint256 index_, bool forceEject_) external; function changeAllocations(Allocation[] calldata allocations_, bool redistribute_) external; function setDepositLimit(uint256 limit_) external; function pushFundsToStrategies() external; function pushFundsIntoSingleStrategy(uint256 index_, uint256 amount) external; function pushFundsIntoSingleStrategy(uint256 index_) external; function pullFundsFromStrategies() external; function pullFundsFromSingleStrategy(uint256 index_) external; function protectStrategy() external view returns (address); function getAvailableAssetsForWithdrawal() external view returns (uint256); function requestFunds(uint256 amount_) external; function setWithdrawalQueue(address withdrawalQueue_) external; function setParkingLot(address parkingLot_) external; function batchClaimWithdrawal(uint256 maxRequests) external; function harvestRewards(bytes calldata encodedData) external; function setMinimunQueueRequest(uint256 minQueueRequest_) external; function isUpgradeable() external pure returns (bool); function toggleWithdrawalsPaused(bool withdrawalsPaused_) external; function takePortfolioAndProtocolFees() external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
//SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; import {IStrategy} from "../interfaces/IStrategy.sol"; import {Strategy, Allocation} from "../interfaces/IConcreteMultiStrategyVault.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {MAX_BASIS_POINTS} from "../utils/Constants.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {Errors} from "../interfaces/Errors.sol"; library StrategyHelper { using SafeERC20 for IERC20; using Math for uint256; /// @notice event for strategy allocations changed event StrategyAllocationsChanged(Allocation[] allocations_); /// @notice event for strategy removed event StrategyRemoved(address strategy); function emergencyRemoveStrategy( Strategy[] storage strategies, address asset, uint256 index_, bool forceEject_, address protectStrategy_ ) external returns (address protectStrategy) { IStrategy stratToRemove = strategies[index_].strategy; protectStrategy = _resetProtectStrategy(address(stratToRemove), protectStrategy_); if (forceEject_) { // Skip the locked assets check and asset redemption // Reset allowance to zero for the strategy being removed IERC20(asset).forceApprove(address(stratToRemove), 0); // Remove strategy from array and emit event _removeStrategyFromArray(index_, strategies); emit StrategyRemoved(address(stratToRemove)); } else { // Normal removal process removeStrategy(strategies, index_, protectStrategy, IERC20(asset)); } } function changeAllocations( Strategy[] storage strategies, Allocation[] calldata allocations_, bool redistribute, address asset ) external { uint256 len = allocations_.length; if (len != strategies.length) { revert Errors.InvalidLength(len, strategies.length); } uint256 allotmentTotals = 0; for (uint256 i; i < len;) { allotmentTotals += allocations_[i].amount; strategies[i].allocation = allocations_[i]; unchecked { i++; } } if (allotmentTotals > 10000) revert Errors.AllotmentTotalTooHigh(); if (redistribute) { pullFundsFromStrategies(strategies); distributeAssetsToStrategies(strategies, IERC20(asset).balanceOf(address(this))); } emit StrategyAllocationsChanged(allocations_); } /** * @notice Pulls funds back from all strategies into the vault. * @dev Can only be called by the vault owner. */ // We are aware that we aren't using the return value // We control both the length of the array and the external call //slither-disable-next-line unused-return,calls-loop function pullFundsFromStrategies(Strategy[] storage strategies) public { uint256 len = strategies.length; for (uint256 i; i < len;) { pullFundsFromSingleStrategy(strategies, i); unchecked { i++; } } } function pullFundsFromSingleStrategy(Strategy[] storage strategies, uint256 index_) public { IStrategy strategy = strategies[index_].strategy; if (strategy.balanceOf(address(this)) == 0) return; // slither-disable-next-line unused-return if (strategy.getAvailableAssetsForWithdrawal() != strategy.totalAssets()) { strategy.withdraw(strategy.getAvailableAssetsForWithdrawal(), address(this), address(this)); return; } strategy.redeem(strategy.balanceOf(address(this)), address(this), address(this)); } function pushFundsIntoSingleStrategyNoAmount( Strategy[] storage strategies, uint256 totalAssets, uint256 index_, bool vaultIdle ) external { if (index_ >= strategies.length) revert Errors.InvalidIndex(index_); if (vaultIdle) revert Errors.VaultIsIdle(); Strategy memory strategy = strategies[index_]; // slither-disable-next-line unused-return strategy.strategy.deposit( totalAssets.mulDiv(strategy.allocation.amount, MAX_BASIS_POINTS, Math.Rounding.Floor), address(this) ); } function pushFundsIntoSingleStrategy( Strategy[] storage strategies, bool vaultIdle, uint256 balance, uint256 index_, uint256 amount ) external { if (amount > balance) { revert Errors.InsufficientVaultFunds(address(this), amount, balance); } if (vaultIdle) revert Errors.VaultIsIdle(); // slither-disable-next-line unused-return strategies[index_].strategy.deposit(amount, address(this)); } /// @notice Distributes assets to each strategy based on their allocation. /// @param strategies The array of strategies, each with a specified allocation. /// @param _totalAssets The total amount of assets to be distributed. function distributeAssetsToStrategies(Strategy[] storage strategies, uint256 _totalAssets) public { uint256 len = strategies.length; for (uint256 i = 0; i < len;) { // Calculate the amount to allocate to each strategy based on its allocation percentage uint256 amountToDeposit = _totalAssets.mulDiv(strategies[i].allocation.amount, MAX_BASIS_POINTS, Math.Rounding.Floor); // Deposit the allocated amount into the strategy strategies[i].strategy.deposit(amountToDeposit, address(this)); unchecked { i++; } } } /// @notice Adds or replaces a strategy, ensuring allotment limits and setting protect strategy if needed. /// @param strategies The storage array of current strategies. /// @param newStrategy_ The new strategy to add or replace. /// @param replace_ Boolean indicating if the strategy should replace an existing one. /// @param index_ The index at which to replace the strategy if `replace_` is true. /// @param protectStrategy The current protect strategy address, which may be updated. /// @param asset The asset of the vault for approving the strategy. /// @return protectStrategy The address of the new protect strategy. /// @return newStrategyIfc The interface of the new strategy. /// @return stratToBeReplacedIfc The interface of the strategy to be replaced. (could be empty if not replacing) function addOrReplaceStrategy( Strategy[] storage strategies, Strategy memory newStrategy_, bool replace_, uint256 index_, address protectStrategy_, IERC20 asset ) public returns (address protectStrategy, IStrategy newStrategyIfc, IStrategy stratToBeReplacedIfc) { // Calculate total allotments of current strategies uint256 allotmentTotals = _getTotalAllotment(strategies); // Adding or replacing strategy based on `replace_` flag if (replace_) { if (index_ >= strategies.length) revert Errors.InvalidIndex(index_); // Ensure replacing doesn't exceed total allotment limit if ( allotmentTotals - strategies[index_].allocation.amount + newStrategy_.allocation.amount > MAX_BASIS_POINTS ) { revert Errors.AllotmentTotalTooHigh(); } // Replace the strategy at `index_` stratToBeReplacedIfc = strategies[index_].strategy; protectStrategy = _resetProtectStrategy(address(stratToBeReplacedIfc), protectStrategy_); _resetStrategy(stratToBeReplacedIfc, asset); strategies[index_] = newStrategy_; // Ensure the strategy is at the correct index if (newStrategy_.allocation.index != index_) { newStrategy_.allocation.index = index_; } } else { protectStrategy = protectStrategy_; // Ensure adding new strategy doesn't exceed total allotment limit if (allotmentTotals + newStrategy_.allocation.amount > MAX_BASIS_POINTS) { revert Errors.AllotmentTotalTooHigh(); } // Add the new strategy to the array strategies.push(newStrategy_); } // Handle protect strategy assignment if applicable if (newStrategy_.strategy.isProtectStrategy()) { if (protectStrategy != address(0)) revert Errors.MultipleProtectStrat(); protectStrategy = address(newStrategy_.strategy); } // Approve the asset for the new strategy asset.forceApprove(address(newStrategy_.strategy), type(uint256).max); // Return the address of the new strategy newStrategyIfc = newStrategy_.strategy; } /// @notice Removes a strategy, redeeming assets if necessary, and resets protect strategy if applicable. /// @param strategies The storage array of current strategies. /// @param index_ The index of the strategy to be removed. /// @param protectStrategy_ The current protect strategy address, which may be updated. /// @param asset The asset of the vault for resetting the allowance to the strategy. /// @return protectStrategy The address of the removed strategy. function removeStrategy(Strategy[] storage strategies, uint256 index_, address protectStrategy_, IERC20 asset) public returns (address protectStrategy) { IStrategy stratToBeRemoved_ = strategies[index_].strategy; protectStrategy = _resetProtectStrategy(address(stratToBeRemoved_), protectStrategy_); _resetStrategy(stratToBeRemoved_, asset); _removeStrategyFromArray(index_, strategies); emit StrategyRemoved(address(stratToBeRemoved_)); } function _resetStrategy(IStrategy strategy_, IERC20 asset) internal { // Check and redeem assets if not locked if (strategy_.getAvailableAssetsForWithdrawal() != strategy_.totalAssets()) { revert Errors.StrategyHasLockedAssets(address(strategy_)); } // Redeem all assets from the strategy if it has any assets if (strategy_.totalAssets() > 0) { strategy_.redeem(strategy_.balanceOf(address(this)), address(this), address(this)); } // Reset allowance to zero for the strategy asset.forceApprove(address(strategy_), 0); } function _removeStrategyFromArray(uint256 index_, Strategy[] storage strategies) internal { uint256 lastIndex = strategies.length - 1; if (index_ != lastIndex) { strategies[index_] = strategies[lastIndex]; strategies[index_].allocation.index = index_; } strategies.pop(); } function _getTotalAllotment(Strategy[] memory strategies) internal view returns (uint256 allotmentTotals) { uint256 len = strategies.length; for (uint256 i; i < len;) { allotmentTotals += strategies[i].allocation.amount; unchecked { ++i; } } } function _resetProtectStrategy(address strategyAddress, address protectStrategy_) internal pure returns (address) { // Reset protect strategy if the strategy being removed is the protect strategy if (strategyAddress == protectStrategy_) { return address(0); } return protectStrategy_; } function withdrawFromStrategies( Strategy[] memory strategies, uint256 amount_, uint256 float_, address receiver_, address vaultAddress ) external returns (uint256) { uint256 diff = amount_ - float_; uint256 len = strategies.length; uint256 totalWithdrawn = 0; for (uint256 i; i < len;) { Strategy memory strategy = strategies[i]; //We control both the length of the array and the external call //slither-disable-next-line calls-loop uint256 withdrawable = strategy.strategy.previewRedeem(strategy.strategy.balanceOf(vaultAddress)); if (diff.mulDiv(strategy.allocation.amount, MAX_BASIS_POINTS, Math.Rounding.Ceil) > withdrawable) { revert Errors.InsufficientFunds(strategy.strategy, diff * strategy.allocation.amount, withdrawable); } uint256 amountToWithdraw = amount_.mulDiv(strategy.allocation.amount, MAX_BASIS_POINTS, Math.Rounding.Ceil); //We control both the length of the array and the external call //slither-disable-next-line unused-return,calls-loop strategy.strategy.withdraw(amountToWithdraw, receiver_, vaultAddress); totalWithdrawn += amountToWithdraw; unchecked { i++; } } return totalWithdrawn; } function depositIntoStrategies( Strategy[] memory strategies, uint256 assets_, address vaultAddress, bool isRoundingFloor ) external { Math.Rounding rounding = isRoundingFloor ? Math.Rounding.Floor : Math.Rounding.Ceil; uint256 len = strategies.length; for (uint256 i; i < len;) { //We control both the length of the array and the external call //slither-disable-next-line unused-return,calls-loop strategies[i].strategy.deposit( assets_.mulDiv(strategies[i].allocation.amount, MAX_BASIS_POINTS, rounding), vaultAddress ); unchecked { i++; } } } }
//SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {SECONDS_PER_YEAR, MAX_BASIS_POINTS} from "../utils/Constants.sol"; import {VaultFees, GraduatedFee} from "../interfaces/IConcreteMultiStrategyVault.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; library FeesHelper { using Math for uint256; function accruedProtocolFee(uint256 protocolFee_, uint256 totalAssets_, uint256 feesUpdatedAt_) external view returns (uint256) { // Only calculate if a protocol fee is set if (protocolFee_ > 0) { // Calculate the fee based on time elapsed and total assets, using floor rounding for precision return protocolFee_.mulDiv( totalAssets_ * (block.timestamp - feesUpdatedAt_), SECONDS_PER_YEAR, Math.Rounding.Floor ) / 10000; // Normalize the fee percentage } else { return 0; } } function accruedPerformanceFee( GraduatedFee[] memory performanceFee_, uint256 totalAssets_, uint256 shareValue_, uint256 highWaterMark_, address asset_, VaultFees storage fees_ ) external view returns (uint256 fee) { if (performanceFee_.length > 0 && shareValue_ > highWaterMark_) { fee = calculateTieredFee(shareValue_, highWaterMark_, totalAssets_, fees_, IERC20Metadata(asset_).decimals()); } } /// @notice Calculates the tiered fee based on share value and high water mark. /// @param shareValue The current value of a share in assets. /// @param highWaterMark The high water mark for performance fee calculation. /// @param totalAssets The total assets in the vault. /// @param fees The fee structure containing performance fee tiers. /// @return fee The calculated performance fee. /// @dev This function Must only be called when the share value strictly exceeds the high water mark. function calculateTieredFee( uint256 shareValue, uint256 highWaterMark, uint256 totalAssets, VaultFees storage fees, uint256 underlayingDecimals ) public view returns (uint256 fee) { if (shareValue <= highWaterMark) return 0; uint256 excessAboveHighWaterMark = shareValue - highWaterMark; // Calculate the percentage difference (diff) between share value and high water mark uint256 diff = uint256(excessAboveHighWaterMark.mulDiv(MAX_BASIS_POINTS, highWaterMark, Math.Rounding.Floor)); // Loop through performance fee tiers uint256 len = fees.performanceFee.length; if (len == 0) return 0; for (uint256 i = 0; i < len;) { if (diff <= fees.performanceFee[i].upperBound && diff >= fees.performanceFee[i].lowerBound) { fee = ((shareValue - highWaterMark) * totalAssets).mulDiv( fees.performanceFee[i].fee, MAX_BASIS_POINTS * shareValue, Math.Rounding.Floor ); break; // Exit loop once the correct tier is found } unchecked { i++; } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.24; /// @title Errors /// @author Leonhard Horstmeyer /// @notice Provides functions related to policy calculations library Errors { /// @notice Error message for the case when the price of the collateral token in the borrow token is zero error PriceOfCollateralTokenInBorrowTokenIsZero(); /// @notice Error message for the case when the current supply in collateral is zero error CurrentSupplyInCollateralZero(); /// @notice Error message for the case when the current ltv is zero error CurrentLtvIsZero(); /// @notice Error message for the case when the current debt is zero error CurrentDebtIsZero(); /// @notice Error message for the case when the price quote denomination is zero error PriceQuoteDenominationIsZero(); /// @notice Error message for the case when the denomination is invalid error InvalidDenomination(); /// @notice Error message for the case when the number of claims is zero error ClaimsMustNotBeZero(uint8 number_of_claims); /// @notice Error message for the case when the number of claims exceeds the total claims error TooManyClaims(uint8 number_of_claims, uint8 total_claims); /// @notice Error message for the case when the quartic polynomial evaluates to a positive value error PositiveQuarticValue(int256 value); error CollateralValueBelowDebtValue(); error RepaymentExceedsUserBorrowedAmount(); error RepaymentExceedsConcreteBorrowedAmount(); error WithdrawalExceedsUserSuppliedAmount(); error WithdrawalExceedsConcreteSuppliedAmount(); error ExceedsWadPrecision(); // ==== Encoding Protection Data ============== error FractionExceedsUnityInMillionth(); error FractionExceedsUnityInBP(); error NumberOfProtectionClaimsTooHigh(); error OpeningFeeExceedsPromisedAmount(); error CancellationFeeExceedsPromisedAmount(); error TrancheAmountExceedsPromisedAmount(uint8 trancheNumber); error TrancheFeeExceedsPromisedAmount(uint8 trancheNumber); error InvalidTrancheNumber(uint8 trancheNumber); error InvalidRightsEncoding(uint8 rights); error ExceedsUint96MaxPrecision(); error InvalidProtectionEndTime(); error TrancheAmountsDoNotSumToPromisedAmount(); error AssetDivergence(); error PriceFeedNotSupportedErrorOnExternalOracle(address asset); error InvalidMorphoRewardsReceiver(); error InvalidAmountType(uint8 amountType); error AmountDiviatesTooMuch(uint256 providedAmount, uint256 actualAmount, uint256 wiggleFactor); error TransferFailed(address token, address recipient, uint256 amount); error ApprovalFailed(address token, address spender, uint256 amount); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
//SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; interface IParkingLot { function deposit(address recipient, uint256 amount) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol) pragma solidity ^0.8.20; import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol"; import {SafeERC20} from "../utils/SafeERC20.sol"; import {IERC4626} from "../../../interfaces/IERC4626.sol"; import {Math} from "../../../utils/math/Math.sol"; /** * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. * * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this * contract and not the "assets" token which is an independent contract. * * [CAUTION] * ==== * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by * verifying the amount received is as expected, using a wrapper that performs these checks such as * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]. * * Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()` * corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault * decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself * determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset * (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's * donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more * expensive than it is profitable. More details about the underlying math can be found * xref:erc4626.adoc#inflation-attack[here]. * * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the * `_convertToShares` and `_convertToAssets` functions. * * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide]. * ==== */ abstract contract ERC4626 is ERC20, IERC4626 { using Math for uint256; IERC20 private immutable _asset; uint8 private immutable _underlyingDecimals; /** * @dev Attempted to deposit more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max); /** * @dev Attempted to mint more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max); /** * @dev Attempted to withdraw more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max); /** * @dev Attempted to redeem more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max); /** * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). */ constructor(IERC20 asset_) { (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_); _underlyingDecimals = success ? assetDecimals : 18; _asset = asset_; } /** * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way. */ function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) { (bool success, bytes memory encodedDecimals) = address(asset_).staticcall( abi.encodeCall(IERC20Metadata.decimals, ()) ); if (success && encodedDecimals.length >= 32) { uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256)); if (returnedDecimals <= type(uint8).max) { return (true, uint8(returnedDecimals)); } } return (false, 0); } /** * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals. * * See {IERC20Metadata-decimals}. */ function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) { return _underlyingDecimals + _decimalsOffset(); } /** @dev See {IERC4626-asset}. */ function asset() public view virtual returns (address) { return address(_asset); } /** @dev See {IERC4626-totalAssets}. */ function totalAssets() public view virtual returns (uint256) { return _asset.balanceOf(address(this)); } /** @dev See {IERC4626-convertToShares}. */ function convertToShares(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-convertToAssets}. */ function convertToAssets(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-maxDeposit}. */ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxMint}. */ function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxWithdraw}. */ function maxWithdraw(address owner) public view virtual returns (uint256) { return _convertToAssets(balanceOf(owner), Math.Rounding.Floor); } /** @dev See {IERC4626-maxRedeem}. */ function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf(owner); } /** @dev See {IERC4626-previewDeposit}. */ function previewDeposit(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-previewMint}. */ function previewMint(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewWithdraw}. */ function previewWithdraw(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewRedeem}. */ function previewRedeem(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-deposit}. */ function deposit(uint256 assets, address receiver) public virtual returns (uint256) { uint256 maxAssets = maxDeposit(receiver); if (assets > maxAssets) { revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets); } uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares); return shares; } /** @dev See {IERC4626-mint}. * * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero. * In this case, the shares will be minted without requiring any assets to be deposited. */ function mint(uint256 shares, address receiver) public virtual returns (uint256) { uint256 maxShares = maxMint(receiver); if (shares > maxShares) { revert ERC4626ExceededMaxMint(receiver, shares, maxShares); } uint256 assets = previewMint(shares); _deposit(_msgSender(), receiver, assets, shares); return assets; } /** @dev See {IERC4626-withdraw}. */ function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) { uint256 maxAssets = maxWithdraw(owner); if (assets > maxAssets) { revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets); } uint256 shares = previewWithdraw(assets); _withdraw(_msgSender(), receiver, owner, assets, shares); return shares; } /** @dev See {IERC4626-redeem}. */ function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) { uint256 maxShares = maxRedeem(owner); if (shares > maxShares) { revert ERC4626ExceededMaxRedeem(owner, shares, maxShares); } uint256 assets = previewRedeem(shares); _withdraw(_msgSender(), receiver, owner, assets, shares); return assets; } /** * @dev Internal conversion function (from assets to shares) with support for rounding direction. */ function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) { return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding); } /** * @dev Internal conversion function (from shares to assets) with support for rounding direction. */ function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) { return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding); } /** * @dev Deposit/mint common workflow. */ function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual { // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the // assets are transferred and before the shares are minted, which is a valid state. // slither-disable-next-line reentrancy-no-eth SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } /** * @dev Withdraw/redeem common workflow. */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal virtual { if (caller != owner) { _spendAllowance(owner, caller, shares); } // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the // shares are burned and after the assets are transferred, which is a valid state. _burn(owner, shares); SafeERC20.safeTransfer(_asset, receiver, assets); emit Withdraw(caller, receiver, owner, assets, shares); } function _decimalsOffset() internal view virtual returns (uint8) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {IWithdrawalQueue} from "../interfaces/IWithdrawalQueue.sol"; import {Errors} from "../interfaces/Errors.sol"; import {Strategy} from "../interfaces/IConcreteMultiStrategyVault.sol"; import {IERC4626, IERC20} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {ROUNDING_DELTA} from "../utils/Constants.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IStrategy} from "../interfaces/IStrategy.sol"; import {TokenHelper} from "@blueprint-finance/hub-and-spokes-libraries/src/libraries/TokenHelper.sol"; import {IParkingLot} from "../interfaces/IParkingLot.sol"; import {Errors} from "../interfaces/Errors.sol"; library WithdrawalQueueHelper { using Math for uint256; using SafeERC20 for IERC20; /// @notice event for withdrawal queue updated event WithdrawalQueueUpdated(address oldWithdrawalQueue, address newWithdrawalQueue); /// @notice event for requested funds event RequestedFunds(address protectStrategy, uint256 amount); event WithdrawalsFinalized(uint256 previousLastRequestId, uint256 newLastRequestId); struct BatchClaimParams { uint256 totalAssets; uint256 totalSupply; uint8 decimalOffset; } function getAvailableAssetsForWithdrawal(address asset, Strategy[] memory strategies) external view returns (uint256) { uint256 totalAvailable = IERC20(asset).balanceOf(address(this)); uint256 len = strategies.length; for (uint256 i; i < len;) { Strategy memory strategy = strategies[i]; //We control both the length of the array and the external call //slither-disable-next-line calls-loop totalAvailable += strategy.strategy.getAvailableAssetsForWithdrawal(); unchecked { i++; } } return totalAvailable; } function processWithdrawal( uint256 assets_, uint256 shares_, address receiver_, uint256 availableAssets, address asset, address withdrawalQueue, uint256 minQueueRequest, Strategy[] memory strategies, IParkingLot parkingLot, bool forceQueue ) external { if (!forceQueue && availableAssets >= assets_) { // if queue is not mandatory and there is enough assets available withdraw using current rate _withdrawStrategyFunds(assets_, receiver_, asset, strategies, parkingLot); } else { if (address(withdrawalQueue) == address(0)) { revert Errors.InsufficientVaultFunds(address(this), shares_, availableAssets); } if (minQueueRequest != 0 && shares_ < minQueueRequest) { revert Errors.InsufficientQueueRequest(shares_, minQueueRequest); } IWithdrawalQueue(withdrawalQueue).requestWithdrawal(receiver_, shares_); } } function requestFunds(uint256 amount, uint256 acumulated, address protectStrategy) external { //after requesting funds deposits them into the protect strategy if (acumulated < amount) { revert Errors.InsufficientFunds(IStrategy(address(this)), amount, acumulated); } //slither-disable-next-line unused-return IStrategy(protectStrategy).deposit(amount, address(this)); emit RequestedFunds(protectStrategy, amount); } function setWithdrawalQueue(address oldWithdrawalQueue, address withdrawalQueue_) external returns (IWithdrawalQueue) { // Validate the new recipient address if (withdrawalQueue_ == address(0)) revert Errors.InvalidWithdrawlQueue(); if (address(oldWithdrawalQueue) != address(0)) { if (IWithdrawalQueue(oldWithdrawalQueue).unfinalizedAmount() != 0) { revert Errors.UnfinalizedWithdrawal(address(oldWithdrawalQueue)); } } // Emit an event for the fee recipient update emit WithdrawalQueueUpdated(address(oldWithdrawalQueue), withdrawalQueue_); return IWithdrawalQueue(withdrawalQueue_); // Update the fee recipient } function batchClaim( IWithdrawalQueue withdrawalQueue, uint256 maxRequests, uint256 availableAssetsInShares, address asset, Strategy[] memory strategies, IParkingLot parkingLot, BatchClaimParams memory params ) external { uint256 lastFinalizedId = withdrawalQueue.getLastFinalizedRequestId(); uint256 lastCreatedId = withdrawalQueue.getLastRequestId(); uint256 newLastFinalized = lastFinalizedId; uint256 max = lastCreatedId < lastFinalizedId + maxRequests ? lastCreatedId : lastFinalizedId + maxRequests; for (uint256 i = lastFinalizedId + 1; i <= max;) { uint256 newAvailiableAssetsInShares = claimWithdrawal( i, availableAssetsInShares, withdrawalQueue, asset, strategies, parkingLot, params.totalAssets, params.totalSupply, params.decimalOffset ); // slither-disable-next-line incorrect-equality if (newAvailiableAssetsInShares == availableAssetsInShares) break; availableAssetsInShares = newAvailiableAssetsInShares; newLastFinalized = i; unchecked { i++; } } if (newLastFinalized != lastFinalizedId) { withdrawalQueue._finalize(newLastFinalized); emit WithdrawalsFinalized(lastFinalizedId, newLastFinalized); } } function claimWithdrawal( uint256 _requestId, uint256 availableAssetsInShares, IWithdrawalQueue withdrawalQueue, address asset, Strategy[] memory strategies, IParkingLot parkingLot, uint256 totalAssets, uint256 totalSupply, uint8 decimalOffset ) public returns (uint256) { (address recipient, uint256 amount, uint256 newAvailableAssetsInShares) = withdrawalQueue.prepareWithdrawal(_requestId, availableAssetsInShares); uint256 amountOfAsset = _convertToAssets(amount, Math.Rounding.Floor, totalAssets, totalSupply, decimalOffset); // claim using current rate if (availableAssetsInShares != newAvailableAssetsInShares) { _withdrawStrategyFunds(amountOfAsset, recipient, asset, strategies, parkingLot); } return newAvailableAssetsInShares; } function _convertToAssets( uint256 shares, Math.Rounding rounding, uint256 totalAssets, uint256 totalSupply, uint8 decimalOffset ) internal view returns (uint256) { return shares.mulDiv(totalAssets + 1, totalSupply + 10 ** decimalOffset, rounding); } function _handleDirectTransfer(uint256 amount_, address receiver_, IERC20 asset_, IParkingLot parkingLot) internal { if (amount_ == 0) return; bool result = TokenHelper.attemptSafeTransfer(address(asset_), receiver_, amount_, false); if (!result) { parkingLot.deposit(receiver_, amount_); } } function _handleStrategyWithdrawal( Strategy memory strategy, uint256 amountToWithdraw, address receiver_, IParkingLot parkingLot ) internal returns (uint256) { try strategy.strategy.withdraw(amountToWithdraw, receiver_, address(this)) { return amountToWithdraw; } catch { strategy.strategy.withdraw(amountToWithdraw, address(this), address(this)); parkingLot.deposit(receiver_, amountToWithdraw); return amountToWithdraw; } } function _withdrawStrategyFunds( uint256 amount_, address receiver_, address asset_, Strategy[] memory strategies, IParkingLot parkingLot ) internal { IERC20 _asset = IERC20(asset_); uint256 float = _asset.balanceOf(address(this)); if (amount_ <= float) { _handleDirectTransfer(amount_, receiver_, _asset, parkingLot); return; } uint256 pullFundsFromStrategies = amount_ - float; uint256 totalAllocation = _getTotalAllocation(strategies); uint256 totalWithdrawn; for (uint256 i; i < strategies.length;) { Strategy memory strategy = strategies[i]; uint256 withdrawable = strategy.strategy.getAvailableAssetsForWithdrawal(); uint256 amountToWithdraw = _calculateWithdrawalAmount(pullFundsFromStrategies, strategy, totalAllocation); if (amountToWithdraw > withdrawable) { revert Errors.InsufficientFunds(strategy.strategy, amountToWithdraw, withdrawable); } totalWithdrawn += _handleStrategyWithdrawal(strategy, amountToWithdraw, receiver_, parkingLot); unchecked { ++i; } } if (totalWithdrawn >= amount_) { return; } uint256 fractionAmt = amount_ - totalWithdrawn; if (fractionAmt <= (float + ROUNDING_DELTA)) { uint256 net = fractionAmt > float ? float : fractionAmt; totalWithdrawn = amount_; _handleDirectTransfer(net, receiver_, _asset, parkingLot); } if (totalWithdrawn < amount_) { revert Errors.InsufficientFunds(IStrategy(address(this)), amount_, totalWithdrawn); } } function _calculateWithdrawalAmount(uint256 amount_, Strategy memory strategy, uint256 totalAllocation) internal pure returns (uint256) { return amount_.mulDiv(strategy.allocation.amount, totalAllocation, Math.Rounding.Floor); } function _getTotalAllocation(Strategy[] memory strategies) internal view returns (uint256 totalAllocation) { uint256 len = strategies.length; for (uint256 i; i < len;) { totalAllocation += strategies[i].allocation.amount; unchecked { ++i; } } } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Errors} from "./Errors.sol"; library TokenHelper { ///@notice Attempts to transfer tokens to a recipient, optionally revets on failure ///@param token The token to transfer (may also be invalid token address) ///@param recipient The recipient of the transfer ///@param amount The amount to transfer ///@param revertOnBadAttempt Whether to revert on a failed transfer ///@return successfullTransfer Whether the transfer was successful function attemptSafeTransfer(address token, address recipient, uint256 amount, bool revertOnBadAttempt) internal returns (bool successfullTransfer) { successfullTransfer = _callOptionalReturnBool(IERC20(token), abi.encodeWithSelector(IERC20.transfer.selector, recipient, amount)); if (!successfullTransfer && revertOnBadAttempt) { revert Errors.TransferFailed(token, recipient, amount); } } ///@notice Attempts to approve a spender to transfer tokens, optionally revets on failure ///@param token The token to be approved (may also be invalid token address) ///@param spender The spender to be approved ///@param amount The amount to approve ///@param revertOnBadAttempt Whether to revert on a failed approval ///@return successfullApproval Whether the approval was successful function attemptForceApprove(address token, address spender, uint256 amount, bool revertOnBadAttempt) internal returns (bool successfullApproval) { bytes memory approvalCall = abi.encodeWithSelector(IERC20.approve.selector, spender, amount); successfullApproval = _callOptionalReturnBool(IERC20(token), approvalCall); if (!successfullApproval) { // attempt to reset the approval to zero successfullApproval = _callOptionalReturnBool(IERC20(token), abi.encodeWithSelector(IERC20.approve.selector, spender, 0)); if (successfullApproval) { // attempt to set the approval to the desired amount successfullApproval = _callOptionalReturnBool(IERC20(token), approvalCall); } } if (!successfullApproval && revertOnBadAttempt) { revert Errors.ApprovalFailed(token, spender, amount); } } // OPENZEPPELIN SAFE ERC20 HELPER FUNCTIONS /////////////////////////////// /** * @dev This code is taken from the SafeERC20 library of OpenZeppelin Contracts v5.1.0 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.1.0/contracts/token/ERC20/utils/SafeERC20.sol * @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). * * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { bool success; uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) returnSize := returndatasize() returnValue := mload(0) } return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol) pragma solidity ^0.8.20; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.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 PausableUpgradeable is Initializable, ContextUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Pausable struct PausableStorage { bool _paused; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300; function _getPausableStorage() private pure returns (PausableStorage storage $) { assembly { $.slot := PausableStorageLocation } } /** * @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); /** * @dev The operation failed because the contract is paused. */ error EnforcedPause(); /** * @dev The operation failed because the contract is not paused. */ error ExpectedPause(); /** * @dev Initializes the contract in unpaused state. */ function __Pausable_init() internal onlyInitializing { __Pausable_init_unchained(); } function __Pausable_init_unchained() internal onlyInitializing { PausableStorage storage $ = _getPausableStorage(); $._paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { PausableStorage storage $ = _getPausableStorage(); return $._paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { if (paused()) { revert EnforcedPause(); } } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { if (!paused()) { revert ExpectedPause(); } } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { PausableStorage storage $ = _getPausableStorage(); $._paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { PausableStorage storage $ = _getPausableStorage(); $._paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../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; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @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); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @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). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // 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 cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
//SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; import {IERC20} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ReturnedRewards} from "../interfaces/IStrategy.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {TokenHelper} from "@blueprint-finance/hub-and-spokes-libraries/src/libraries/TokenHelper.sol"; import {PRECISION} from "../utils/Constants.sol"; library RewardsHelper { using Math for uint256; using SafeERC20 for IERC20; /// @notice event for reward token added event RewardTokenAdded(address token); /// @notice event for reward token removed event RewardTokenRemoved(address token); /// @notice event for rewards claimed event RewardsClaimed(address token, uint256 amount); function getUserRewards( uint256 userBalance, address userAddress, address[] memory rewardAddresses, mapping(address => uint256) storage rewardIndex, mapping(address => mapping(address => uint256)) storage userRewardIndex ) external view returns (ReturnedRewards[] memory) { uint256 len = rewardAddresses.length; ReturnedRewards[] memory returnedRewards = new ReturnedRewards[](len); for (uint256 i; i < len;) { uint256 tokenRewardIndex = rewardIndex[rewardAddresses[i]]; uint256 calculatedRewards = (tokenRewardIndex - userRewardIndex[userAddress][rewardAddresses[i]]).mulDiv( userBalance, PRECISION, Math.Rounding.Floor ); returnedRewards[i] = ReturnedRewards(rewardAddresses[i], calculatedRewards); unchecked { i++; } } return returnedRewards; } function getTotalRewardsClaimed( address[] storage rewardAddresses, mapping(address => mapping(address => uint256)) storage totalRewardsClaimed, address userAddress ) external view returns (ReturnedRewards[] memory claimedRewards) { uint256 len = rewardAddresses.length; claimedRewards = new ReturnedRewards[](len); for (uint256 i; i < len;) { claimedRewards[i] = ReturnedRewards(rewardAddresses[i], totalRewardsClaimed[userAddress][rewardAddresses[i]]); unchecked { i++; } } return claimedRewards; } function updateUserRewardsToCurrent( uint256 userBalance_, address userAddress_, address[] memory rewardAddresses_, mapping(address => uint256) storage rewardIndex_, mapping(address => mapping(address => uint256)) storage userRewardIndex_, mapping(address => mapping(address => uint256)) storage totalRewardsClaimed_ ) external { uint256 len = rewardAddresses_.length; for (uint256 i; i < len;) { uint256 tokenRewardIndex = rewardIndex_[rewardAddresses_[i]]; uint256 _userRewardIndex = userRewardIndex_[userAddress_][rewardAddresses_[i]]; userRewardIndex_[userAddress_][rewardAddresses_[i]] = tokenRewardIndex; if (userBalance_ != 0) { uint256 rewardsToTransfer = (tokenRewardIndex - _userRewardIndex).mulDiv(userBalance_, PRECISION, Math.Rounding.Floor); if (rewardsToTransfer != 0) { TokenHelper.attemptSafeTransfer( address(rewardAddresses_[i]), userAddress_, rewardsToTransfer, false ); totalRewardsClaimed_[userAddress_][rewardAddresses_[i]] += rewardsToTransfer; } } unchecked { i++; } } } }
//SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {MAX_BASIS_POINTS} from "../utils/Constants.sol"; import {VaultFees, Strategy} from "../interfaces/IConcreteMultiStrategyVault.sol"; import {IStrategy} from "../interfaces/IStrategy.sol"; import {Errors} from "../interfaces/Errors.sol"; library MultiStrategyVaultHelper { using Math for uint256; using SafeERC20 for IERC20; /// @notice Initializes, validates, and approves the base asset for each strategy. /// @param strategies_ The array of strategies to be initialized. /// @param baseAsset_ The base asset (IERC20 token) for approval. /// @param protectStrategy_ The address of the current protect strategy, if any. /// @param strategies The storage array where validated strategies will be stored. /// @return address The updated protect strategy address. function initializeStrategies( Strategy[] memory strategies_, IERC20 baseAsset_, address protectStrategy_, Strategy[] storage strategies ) private returns (address) { uint256 len = strategies_.length; for (uint256 i = 0; i < len;) { IStrategy currentStrategy = strategies_[i].strategy; // Validate that the strategy asset matches the base asset if (currentStrategy.asset() != address(baseAsset_)) { revert Errors.VaultAssetMismatch(); } // Check if the strategy is a protect strategy and ensure only one is allowed if (currentStrategy.isProtectStrategy()) { if (protectStrategy_ != address(0)) revert Errors.MultipleProtectStrat(); protectStrategy_ = address(currentStrategy); } // Add the validated strategy to the storage array strategies.push(strategies_[i]); // Approve the base asset for the strategy baseAsset_.forceApprove(address(currentStrategy), type(uint256).max); // Use unchecked increment to avoid gas cost for overflow checks (safe since len is controlled) unchecked { i++; } } return protectStrategy_; } /// @notice Validates and assigns fee values from `fees_` to `fees`. /// @param fees_ The input VaultFees structure containing fee values to validate and assign. /// @param fees The storage VaultFees structure where validated fees will be stored. function validateAndSetFees(VaultFees memory fees_, VaultFees storage fees) private { // Validate basic fee values to ensure they don't exceed MAX_BASIS_POINTS if ( fees_.depositFee >= MAX_BASIS_POINTS || fees_.withdrawalFee >= MAX_BASIS_POINTS || fees_.protocolFee >= MAX_BASIS_POINTS ) { revert Errors.InvalidVaultFees(); } // Assign validated fee values fees.depositFee = fees_.depositFee; fees.withdrawalFee = fees_.withdrawalFee; fees.protocolFee = fees_.protocolFee; // Copy the performanceFee array to storage with a loop uint256 len = fees_.performanceFee.length; for (uint256 i = 0; i < len;) { fees.performanceFee.push(fees_.performanceFee[i]); unchecked { i++; } } } /** * @notice Validates and initializes essential vault parameters, including the base asset, strategies, and fee structure. * @dev Ensures the provided base asset address is valid, initializes strategies with allocations, * adjusts decimals for the base asset, and validates and sets vault fees. * Reverts if the base asset address is zero or if the fees or strategy allocations are invalid. * @param baseAsset_ The IERC20 token that serves as the base asset of the vault. * @param decimalOffset The offset to be added to the base asset's decimals to calculate vault decimals. * @param strategies_ The array of strategies with allocation data to be initialized for the vault. * @param protectStrategy_ The current protect strategy address, if any, to be used for specific operations. * @param strategies The storage array where validated and initialized strategies will be stored. * @param fees_ The memory VaultFees structure containing the initial fee values for the vault. * @param fees The storage VaultFees structure where validated fees will be stored and used by the vault. * @return protectStrategy The address of the protect strategy if set after initialization. * @return decimals The calculated number of decimals for the vault based on the base asset and decimal offset. * @custom:reverts InvalidAssetAddress if the base asset address is zero. * @custom:reverts AllotmentTotalTooHigh if the total strategy allocations exceed 100%. * @custom:reverts InvalidVaultFees if any fee value exceeds the maximum basis points allowed. */ function validateVaultParameters( IERC20 baseAsset_, uint8 decimalOffset, Strategy[] memory strategies_, address protectStrategy_, Strategy[] storage strategies, VaultFees memory fees_, VaultFees storage fees ) public returns (address protectStrategy, uint8 decimals) { if (address(baseAsset_) == address(0)) { revert Errors.InvalidAssetAddress(); } uint256 totalAllocation; for (uint256 i = 0; i < strategies_.length;) { totalAllocation += strategies_[i].allocation.amount; unchecked { i++; } } if (totalAllocation > MAX_BASIS_POINTS) { revert Errors.AllotmentTotalTooHigh(); } protectStrategy = initializeStrategies(strategies_, baseAsset_, protectStrategy_, strategies); decimals = IERC20Metadata(address(baseAsset_)).decimals() + decimalOffset; validateAndSetFees(fees_, fees); } function withdrawAssets(address asset, uint256 amount, address protectStrategy, Strategy[] storage strategies) public returns (uint256) { uint256 availableAssets = IERC20(asset).balanceOf(address(this)); uint256 accumulated = availableAssets; if (availableAssets < amount) { // Calculate total allocation excluding protect strategy uint256 totalAllocation = 0; uint256 totalAvailable = 0; for (uint256 i = 0; i < strategies.length; i++) { if (address(strategies[i].strategy) != protectStrategy) { totalAllocation += strategies[i].allocation.amount; totalAvailable += strategies[i].strategy.getAvailableAssetsForWithdrawal(); } } // If we can't satisfy the request, return what we have if (totalAvailable + availableAssets < amount) { return accumulated + totalAvailable; } // withdraw proportionally uint256 remainingToWithdraw = amount - availableAssets; for (uint256 i = 0; i < strategies.length; i++) { if (address(strategies[i].strategy) == protectStrategy) { continue; } IStrategy currentStrategy = strategies[i].strategy; uint256 availableInStrategy = currentStrategy.getAvailableAssetsForWithdrawal(); if (availableInStrategy == 0) { continue; } // Calculate proportional withdrawal based on strategy's allocation uint256 targetWithdraw = remainingToWithdraw.mulDiv(strategies[i].allocation.amount, totalAllocation, Math.Rounding.Floor); // Cap withdrawal at available amount uint256 toWithdraw = Math.min(targetWithdraw, availableInStrategy); if (toWithdraw > 0) { currentStrategy.withdraw(toWithdraw, address(this), address(this)); accumulated += toWithdraw; } } // If we still haven't met the target amount and have available funds, // try to withdraw any remaining amount needed from strategies with remaining liquidity if (accumulated < amount) { uint256 stillNeeded = amount - accumulated; for (uint256 i = 0; i < strategies.length && stillNeeded > 0; i++) { if (address(strategies[i].strategy) == protectStrategy) { continue; } IStrategy currentStrategy = strategies[i].strategy; uint256 availableInStrategy = currentStrategy.getAvailableAssetsForWithdrawal(); if (availableInStrategy > 0) { uint256 toWithdraw = Math.min(stillNeeded, availableInStrategy); currentStrategy.withdraw(toWithdraw, address(this), address(this)); accumulated += toWithdraw; stillNeeded -= toWithdraw; } } } } return accumulated; } }
//SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.24; interface IWithdrawalQueue { function requestWithdrawal(address recipient, uint256 amount) external; function prepareWithdrawal(uint256 _requestId, uint256 _avaliableAssets) external returns (address recipient, uint256 amount, uint256 avaliableAssets); function unfinalizedAmount() external view returns (uint256); function getLastFinalizedRequestId() external view returns (uint256); function getLastRequestId() external view returns (uint256); //slither-disable-next-line naming-convention function _finalize(uint256 _lastRequestIdToBeFinalized) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; /// @title Errors Interface /// @notice Defines custom errors for the smart contract operations. interface Errors { /// @notice Error for invalid asset address. error InvalidAssetAddress(); /// @notice Error for mismatch between vault's asset and expected asset. error VaultAssetMismatch(); /// @notice Error for invalid vault fees configuration. error InvalidVaultFees(); /// @notice Error for invalid fee recipient address. error InvalidFeeRecipient(); /// @notice Error for invalid parking lot address. error InvalidParkingLot(); /// @notice Error for operations involving a zero amount. error ZeroAmount(); /// @notice Error for operations involving a zero amount. error InvalidAmount(); /// @notice Error for invalid recipient address. error InvalidRecipient(); /// @notice Error for exceeding maximum allowed value or count. error MaxError(); /// @notice Error for exceeding substraction. error InvalidSubstraction(); error WithdrawalsPaused(); /// @notice Error for insufficient funds in a strategy. /// @param strategy The strategy contract with insufficient funds. /// @param amount The amount attempted to be withdrawn. /// @param available The available amount in the strategy. error InsufficientFunds(IERC4626 strategy, uint256 amount, uint256 available); error QueueNotSet(); error InsufficientQueueRequest(uint256 assets, uint256 minRequest); error InsufficientVaultFunds(address vault, uint256 amount, uint256 available); /// @notice Error for total allotment exceeding allowed maximum. error AllotmentTotalTooHigh(); /// @notice Error for invalid allocation index. /// @param index The index of the allocation. /// @param expectedIndex The expected index of the allocation. error InvalidAllocationIndex(uint256 index, uint256 expectedIndex); /// @notice Error for expired permit deadline. /// @param deadline The deadline timestamp that has been exceeded. error PermitDeadlineExpired(uint256 deadline); /// @notice Error for invalid signer address. /// @param signer The address of the invalid signer. error InvalidSigner(address signer); /// @notice Error for vault being in an idle state when an active state is required. error VaultIsIdle(); /// @notice Error for invalid implementation identifier. /// @param id The bytes32 identifier of the implementation. error InvalidImplementation(bytes32 id); /// @notice Error for failed initialization of a vault deployment. error VaultDeployInitFailed(); /// @notice Error for an implementation identifier that already exists. /// @param id The bytes32 identifier of the existing implementation. error ImplementationAlreadyExists(bytes32 id); /// @notice Error for a non-existent implementation identifier. /// @param id The bytes32 identifier of the non-existent implementation. error ImplementationDoesNotExist(bytes32 id); /// @notice Error for attempting to add a vault that already exists. error VaultAlreadyExists(); /// @notice error when the vault address is the zero address error VaultZeroAddress(); error VaultNotUpgradeable(); /// @notice error when the vaultAddress does not exist /// @param vault the address of the vault error VaultDoesNotExist(address vault); /// @notice error for the total vaults allowed is exceeded /// @param total the total number of vaults if the addVault function is successful error TotalVaultsAllowedExceeded(uint256 total); /// @notice error for vault to token limit check /// @param token the address of the token to add to vault mapping /// @param total the total number of vaults if the addVault function is successful error VaultByTokenLimitExceeded(address token, uint256 total); /// @notice error for invalid withdrawl queue address (zero address) error InvalidWithdrawlQueue(); /// @notice error for beneficiary address check is not zero address error InvalidBeneficiary(); /// @notice error for zero deposit limit check error InvalidDepositLimit(); /// @notice error when setting new withdrawl queue to check if the old withdrawl queue is unfinalized /// @param queue the address of the withdrawl queue error UnfinalizedWithdrawal(address queue); /// @notice error for invalid token address error InvalidToken(); /// @notice error for invalid rescuer address error InvalidRescuer(); /// @notice error for ERC20 approve fail error ERC20ApproveFail(); /// @notice error for vesting period check error NotPassedYear(); /// @notice error for reward token address is zero address error InvalidRewardTokenAddress(); /// @notice error for reward token already approved error RewardTokenAlreadyApproved(); /// @notice error for reward token not approved error RewardTokenNotApproved(); /// @notice error for checking a new reward token to be added to the strategy /// @notice a new reward token must have accumulated fee accounted as zero error AccumulatedFeeAccountedMustBeZero(); /// @notice error for checking the vault must have only one protect strategy error MultipleProtectStrat(); /// @notice error for checking the strategy has no locked assets (before removing strategy) /// @param strategy the address of the strategy error StrategyHasLockedAssets(address strategy); /// @notice error for checking the strategy index is out of bounds /// @param index the index of the strategy error InvalidIndex(uint256 index); /// @notice error for checking the length of the arrays /// @param argLength the length of the allocations array /// @param expectedLength the expected length of the strategies array error InvalidLength(uint256 argLength, uint256 expectedLength); // TokenRegistry errors ///////////////////////////////////////////////// /// @notice Error for a token already being registered. /// @param tokenAddress The address of the token. error TokenAlreadyRegistered(address tokenAddress); /// @notice Error for a token not being registered. /// @param tokenAddress The address of the token. error TokenNotRegistered(address tokenAddress); /// @notice Error for a token not being a reward token. /// @param tokenAddress The address of the token. error NotValidRewardToken(address tokenAddress); /// @notice Treasury on the TokenRegistry is already set. error TreasuryAlreadySet(address attacker); /// @notice Unregistered tokens cannot be rewards. /// @param tokenAddress The address of the token. error UnregisteredTokensCannotBeRewards(address tokenAddress); /// @notice Error for a the treasury to be set to the zero address on constructor. error InvalidTreasuryAddress(); // Swapper errors ////////////////////////////////////////////////////// /// @notice The amount of a reward token is not available for withdrawal. /// @param token The address of the reward token. /// @param amount The amount required. error NotAvailableForWithdrawal(address token, uint256 amount); /// @notice The treasury change request cooldown has not elapsed. /// @param sender The address of the sender. error TreasuryChangeRequestCooldownNotElapsed(address sender); // RewardManager errors ///////////////////////////////////////////////// /// @notice The base reward rate must be less than 100%. error SwapperBaseRewardrate(); /// @notice The maximum progression factor must be less than 100%. error SwapperMaxProgressionFactor(); /// @notice The bonus reward rate for the user must be less than 100%. error SwapperBonusRewardrateUser(); /// @notice The bonus reward rate for the ctToken must be less than 100%. error SwapperBonusRewardrateCtToken(); /// @notice The bonus reward rate for the swap token must be less than 100%. error SwapperBonusRewardrateSwapToken(); /// @notice Invalid Address error InvalidUserAddress(); //Oracle plug /// @notice Invalid Token Registry Address error InvalidTokenRegistry(); //Claim Router errors ////////////////////////////////////////////////// /// @notice error for invalid vault registry address error InvalidVaultRegistry(); /// @notice error for unauthorized account error BlueprintUnauthorizedAccount(address account); /// @notice error for invalid default admin address error InvalidDefaultAdminAddress(); /// @notice error for no protection strategies found error NoProtectionStrategiesFound(); /// @notice error for only vault caller error OnlyVault(address caller); //Protect strategy errors /////////////////////////////////////////////// /// @notice error for unauthorized account error ProtectUnauthorizedAccount(address account); /// @notice error for claim router is unauthorized account error ClaimRouterUnauthorizedAccount(address account); /// @notice error for claim router address is zero address error InvalidClaimRouterAddress(); //MultiSigStrategy /// @notice error for unauthorized multi-sig account error MultiSigUnauthorizedAccount(address account); /// @notice Thrown when attempting to initialize with zero address for multi-sig error InvalidMultiSigAddress(); /// @notice Thrown when attempting to withdraw when withdraw is disabled error WithdrawDisabled(); /// @notice Thrown when deposit fails error DepositFailed(); /// @notice Thrown when attempting to initialize with zero address for target vault error InvalidTargetVaultAddress(); /// @notice Thrown when attempting to withdraw more than the available balance error InsufficientUnderlyingBalance(); }
{ "evmVersion": "cancun", "metadata": { "appendCBOR": true, "bytecodeHash": "ipfs", "useLiteralContent": false }, "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "remappings": [ "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/", "@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/", "@blueprint-finance/=node_modules/@blueprint-finance/", "@blueprint-finance/token-distribution-contracts/=node_modules/@blueprint-finance/token-distribution-contracts/", "local/src/=src/", "@morpho-org/=node_modules/@morpho-org/", "@uniswap/=node_modules/@uniswap/", "@layerzerolabs/lz-evm-oapp-v2/contracts/=node_modules/@layerzerolabs/lz-evm-oapp-v2/contracts/", "@layerzerolabs/lz-evm-messagelib-v2/=node_modules/@layerzerolabs/lz-evm-messagelib-v2/", "@layerzerolabs/lz-evm-protocol-v2/=node_modules/@layerzerolabs/lz-evm-protocol-v2/", "@layerzerolabs/oapp-evm/contracts/=node_modules/@layerzerolabs/oapp-evm/contracts/", "solidity-bytes-utils/=node_modules/solidity-bytes-utils/", "base64-sol/=node_modules/base64-sol/", "ds-test/=lib/forge-std/lib/ds-test/src/", "earn-v1-vault-manager-v1/=node_modules/earn-v1-vault-manager-v1/", "@blueprint-finance/multi-asset-vault/=node_modules/@blueprint-finance/multi-asset-vault/", "@blueprint-finance/concrete-earn-beacon/=node_modules/@blueprint-finance/concrete-earn-beacon/", "beacon-vaults-0.0.2/=node_modules/beacon-vaults-0.0.2/", "beacon-vaults-0.0.3/=node_modules/beacon-vaults-0.0.3/", "earn-v1-release-0-3-1/=node_modules/earn-v1-release-0-3-1/", "earn-v1-release-0-3-12/=node_modules/earn-v1-release-0-3-12/", "earn-v1-release-0-4-1/=node_modules/earn-v1-release-0-4-1/", "earn-v1-release-0-5-0/=node_modules/earn-v1-release-0-5-0/", "@gnosis.pm/safe-contracts/=node_modules/@gnosis.pm/safe-contracts/", "eth-gas-reporter/=node_modules/eth-gas-reporter/", "forge-std/=lib/forge-std/src/", "hardhat/=node_modules/hardhat/", "./=remappings.txt/" ], "viaIR": false, "libraries": { "node_modules/earn-v1-release-0-5-0/src/vault/ConcreteMultiStrategyVault.sol": { "FeesHelper": "0xa7e4636371e5606606aed9e314e67ea86cb9b634", "MultiStrategyVaultHelper": "0x21ea5cb6b67d5de79ca94682957e238448fdeca7", "RewardsHelper": "0xa9ce9db84b7eeabfb5c62d24863bb1c7f8d29cb7", "StrategyHelper": "0xd35a6627523f0b49e28b971de4629244a0491dae", "VaultActionsHelper": "0xe1df067ed5c02ea743df0cd072e715c538c8f43b", "WithdrawalQueueHelper": "0xaaa2ce316e920377701f63c4ffd0c0d6ebed9221" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccumulatedFeeAccountedMustBeZero","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"AllotmentTotalTooHigh","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ApprovalFailed","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"BlueprintUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ClaimRouterUnauthorizedAccount","type":"error"},{"inputs":[],"name":"DepositFailed","type":"error"},{"inputs":[],"name":"ERC20ApproveFail","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxDeposit","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxMint","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxRedeem","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxWithdraw","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"ImplementationAlreadyExists","type":"error"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"ImplementationDoesNotExist","type":"error"},{"inputs":[{"internalType":"contract IERC4626","name":"strategy","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"InsufficientFunds","type":"error"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"minRequest","type":"uint256"}],"name":"InsufficientQueueRequest","type":"error"},{"inputs":[],"name":"InsufficientUnderlyingBalance","type":"error"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"InsufficientVaultFunds","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"expectedIndex","type":"uint256"}],"name":"InvalidAllocationIndex","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidAssetAddress","type":"error"},{"inputs":[],"name":"InvalidBeneficiary","type":"error"},{"inputs":[],"name":"InvalidClaimRouterAddress","type":"error"},{"inputs":[],"name":"InvalidDefaultAdminAddress","type":"error"},{"inputs":[],"name":"InvalidDepositLimit","type":"error"},{"inputs":[],"name":"InvalidFeeRecipient","type":"error"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"InvalidImplementation","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"InvalidIndex","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"uint256","name":"argLength","type":"uint256"},{"internalType":"uint256","name":"expectedLength","type":"uint256"}],"name":"InvalidLength","type":"error"},{"inputs":[],"name":"InvalidMultiSigAddress","type":"error"},{"inputs":[],"name":"InvalidParkingLot","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidRescuer","type":"error"},{"inputs":[],"name":"InvalidRewardTokenAddress","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidSubstraction","type":"error"},{"inputs":[],"name":"InvalidTargetVaultAddress","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"InvalidTokenRegistry","type":"error"},{"inputs":[],"name":"InvalidTreasuryAddress","type":"error"},{"inputs":[],"name":"InvalidUserAddress","type":"error"},{"inputs":[],"name":"InvalidVaultFees","type":"error"},{"inputs":[],"name":"InvalidVaultRegistry","type":"error"},{"inputs":[],"name":"InvalidWithdrawlQueue","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"MaxError","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"MultiSigUnauthorizedAccount","type":"error"},{"inputs":[],"name":"MultipleProtectStrat","type":"error"},{"inputs":[],"name":"NoProtectionStrategiesFound","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NotAvailableForWithdrawal","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotPassedYear","type":"error"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"NotValidRewardToken","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"OnlyVault","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"PermitDeadlineExpired","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ProtectUnauthorizedAccount","type":"error"},{"inputs":[],"name":"QueueNotSet","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RewardTokenAlreadyApproved","type":"error"},{"inputs":[],"name":"RewardTokenNotApproved","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyHasLockedAssets","type":"error"},{"inputs":[],"name":"SwapperBaseRewardrate","type":"error"},{"inputs":[],"name":"SwapperBonusRewardrateCtToken","type":"error"},{"inputs":[],"name":"SwapperBonusRewardrateSwapToken","type":"error"},{"inputs":[],"name":"SwapperBonusRewardrateUser","type":"error"},{"inputs":[],"name":"SwapperMaxProgressionFactor","type":"error"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"TokenAlreadyRegistered","type":"error"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"TokenNotRegistered","type":"error"},{"inputs":[{"internalType":"uint256","name":"total","type":"uint256"}],"name":"TotalVaultsAllowedExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"attacker","type":"address"}],"name":"TreasuryAlreadySet","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"TreasuryChangeRequestCooldownNotElapsed","type":"error"},{"inputs":[{"internalType":"address","name":"queue","type":"address"}],"name":"UnfinalizedWithdrawal","type":"error"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"UnregisteredTokensCannotBeRewards","type":"error"},{"inputs":[],"name":"VaultAlreadyExists","type":"error"},{"inputs":[],"name":"VaultAssetMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"total","type":"uint256"}],"name":"VaultByTokenLimitExceeded","type":"error"},{"inputs":[],"name":"VaultDeployInitFailed","type":"error"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"VaultDoesNotExist","type":"error"},{"inputs":[],"name":"VaultIsIdle","type":"error"},{"inputs":[],"name":"VaultNotUpgradeable","type":"error"},{"inputs":[],"name":"VaultZeroAddress","type":"error"},{"inputs":[],"name":"WithdrawDisabled","type":"error"},{"inputs":[],"name":"WithdrawalsPaused","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"limit","type":"uint256"}],"name":"DepositLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRecipient","type":"address"},{"indexed":true,"internalType":"address","name":"newRecipient","type":"address"}],"name":"FeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vaultName","type":"address"},{"indexed":true,"internalType":"address","name":"underlyingAsset","type":"address"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"pastValue","type":"bool"},{"indexed":false,"internalType":"bool","name":"newValue","type":"bool"}],"name":"IsQueueMandatoryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_oldMinQueueRequest","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newMinQueueRequest","type":"uint256"}],"name":"MinimunQueueRequestUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldParkingLot","type":"address"},{"indexed":true,"internalType":"address","name":"newParkingLot","type":"address"},{"indexed":false,"internalType":"bool","name":"successfulApproval","type":"bool"}],"name":"ParkingLotUpdated","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":"protectStrategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RequestedFunds","type":"event"},{"anonymous":false,"inputs":[],"name":"RewardsHarvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newStrategy","type":"address"}],"name":"StrategyAdded","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct Allocation[]","name":"newAllocations","type":"tuple[]"}],"name":"StrategyAllocationsChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldStrategy","type":"address"}],"name":"StrategyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"pastValue","type":"bool"},{"indexed":false,"internalType":"bool","name":"newValue","type":"bool"}],"name":"ToggleVaultIdle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"pastValue","type":"bool"},{"indexed":false,"internalType":"bool","name":"newValue","type":"bool"}],"name":"WithdrawalPausedToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldQueue","type":"address"},{"indexed":false,"internalType":"address","name":"newQueue","type":"address"}],"name":"WithdrawalQueueUpdated","type":"event"},{"inputs":[],"name":"accruedPerformanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accruedProtocolFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"},{"internalType":"bool","name":"replace_","type":"bool"},{"components":[{"internalType":"contract IStrategy","name":"strategy","type":"address"},{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Allocation","name":"allocation","type":"tuple"}],"internalType":"struct Strategy","name":"newStrategy_","type":"tuple"}],"name":"addStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxRequests","type":"uint256"}],"name":"batchClaimWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Allocation[]","name":"allocations_","type":"tuple[]"},{"internalType":"bool","name":"redistribute","type":"bool"}],"name":"changeAllocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimalOffset","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"},{"internalType":"bool","name":"forceEject_","type":"bool"}],"name":"emergencyRemoveStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feesUpdatedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"firstDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAvailableAssetsForWithdrawal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStrategies","outputs":[{"components":[{"internalType":"contract IStrategy","name":"strategy","type":"address"},{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Allocation","name":"allocation","type":"tuple"}],"internalType":"struct Strategy[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"getTotalRewardsClaimed","outputs":[{"components":[{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"internalType":"struct ReturnedRewards[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"getUserRewards","outputs":[{"components":[{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"internalType":"struct ReturnedRewards[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaultFees","outputs":[{"components":[{"internalType":"uint64","name":"depositFee","type":"uint64"},{"internalType":"uint64","name":"withdrawalFee","type":"uint64"},{"internalType":"uint64","name":"protocolFee","type":"uint64"},{"components":[{"internalType":"uint256","name":"lowerBound","type":"uint256"},{"internalType":"uint256","name":"upperBound","type":"uint256"},{"internalType":"uint64","name":"fee","type":"uint64"}],"internalType":"struct GraduatedFee[]","name":"performanceFee","type":"tuple[]"}],"internalType":"struct VaultFees","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedData","type":"bytes"}],"name":"harvestRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"highWaterMark","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"baseAsset_","type":"address"},{"internalType":"string","name":"shareName_","type":"string"},{"internalType":"string","name":"shareSymbol_","type":"string"},{"components":[{"internalType":"contract IStrategy","name":"strategy","type":"address"},{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Allocation","name":"allocation","type":"tuple"}],"internalType":"struct Strategy[]","name":"strategies_","type":"tuple[]"},{"internalType":"address","name":"feeRecipient_","type":"address"},{"components":[{"internalType":"uint64","name":"depositFee","type":"uint64"},{"internalType":"uint64","name":"withdrawalFee","type":"uint64"},{"internalType":"uint64","name":"protocolFee","type":"uint64"},{"components":[{"internalType":"uint256","name":"lowerBound","type":"uint256"},{"internalType":"uint256","name":"upperBound","type":"uint256"},{"internalType":"uint64","name":"fee","type":"uint64"}],"internalType":"struct GraduatedFee[]","name":"performanceFee","type":"tuple[]"}],"internalType":"struct VaultFees","name":"fees_","type":"tuple"},{"internalType":"uint256","name":"depositLimit_","type":"uint256"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"bool","name":"isQueueMandatory_","type":"bool"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isQueueMandatory","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isUpgradeable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minQueueRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"parkingLot","outputs":[{"internalType":"contract IParkingLot","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protectStrategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"}],"name":"pullFundsFromSingleStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pullFundsFromStrategies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"pushFundsIntoSingleStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"}],"name":"pushFundsIntoSingleStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pushFundsToStrategies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"}],"name":"removeStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"requestFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newLimit_","type":"uint256"}],"name":"setDepositLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRecipient_","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isQueueMandatory","type":"bool"}],"name":"setIsQueueMandatory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minQueueRequest_","type":"uint256"}],"name":"setMinimunQueueRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"parkingLot_","type":"address"}],"name":"setParkingLot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"depositFee","type":"uint64"},{"internalType":"uint64","name":"withdrawalFee","type":"uint64"},{"internalType":"uint64","name":"protocolFee","type":"uint64"},{"components":[{"internalType":"uint256","name":"lowerBound","type":"uint256"},{"internalType":"uint256","name":"upperBound","type":"uint256"},{"internalType":"uint64","name":"fee","type":"uint64"}],"internalType":"struct GraduatedFee[]","name":"performanceFee","type":"tuple[]"}],"internalType":"struct VaultFees","name":"newFees_","type":"tuple"}],"name":"setVaultFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"withdrawalQueue_","type":"address"}],"name":"setWithdrawalQueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"takePortfolioAndProtocolFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleVaultIdle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"withdrawalsPaused_","type":"bool"}],"name":"toggleWithdrawalsPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"totalRewardsClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userRewardIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultIdle","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalQueue","outputs":[{"internalType":"contract IWithdrawalQueue","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60806040525f805534801562000013575f80fd5b506200001e62000024565b620000d8565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000755760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d55780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b615df480620000e65f395ff3fe608060405234801561000f575f80fd5b506004361061049a575f3560e01c806375a046a511610263578063ba0876521161014b578063daa3a163116100ca578063ecf708581161008f578063ecf7085814610a01578063ef8b30f714610a0a578063f1f741b414610a1d578063f2fde38b14610a30578063f4aa3efa14610a43578063f745d6ca14610a56575f80fd5b8063daa3a163146109ae578063db006a75146109b4578063dd62ed3e146109c7578063e74b981b146109da578063e9f2838e146109ed575f80fd5b8063c63d75b611610110578063c63d75b61461095a578063c6e6f5921461096d578063ce96cb7714610980578063d1b39ae514610993578063d905777e1461099b575f80fd5b8063ba087652146108f9578063bdc8144b1461090c578063bf3bd25d1461091f578063c0cbbca614610932578063c4f59f9b14610945575f80fd5b8063a0712d68116101e2578063b3d7f6b9116101a7578063b3d7f6b914610879578063b3e321141461088c578063b45ba4a8146108ab578063b460af94146108be578063b49a60bb146108d1578063b6b55f25146108e6575f80fd5b8063a0712d6814610830578063a5f5be5414610843578063a9059cbb1461084b578063acfd79281461085e578063b39ed6d914610866575f80fd5b806394bf804d1161022857806394bf804d146107cf57806395d89b41146107e25780639e2f0e22146107ea5780639f3d0a48146107fd578063a039c12914610806575f80fd5b806375a046a514610786578063839e950f146107995780638456cb59146107ac57806388fba066146107b45780638da5cb5b146107c7575f80fd5b806337d5fe991161038657806355b8b781116103055780636d3300e5116102ca5780636d3300e51461072a5780636e2a6fe51461073d5780636e553f651461074557806370a0823114610758578063715018a61461076b57806372e7160114610773575f80fd5b806355b8b781146106ec57806359f7c845146106f45780635c975abb1461070757806366584f8c1461070f5780636bedc46c14610717575f80fd5b8063465718991161034b578063465718991461069857806346904840146106a057806349d0ea97146106b35780634cdad506146106c65780635337e670146106d9575f80fd5b806337d5fe991461064257806337f1834e1461066d57806338d52e0f146106755780633f4ba83a1461067d578063402d267d14610685575f80fd5b806313ee9df41161041d57806323b872dd116103e257806323b872dd146105ce5780632a81196c146105e15780632e1a7d4d146105f4578063313ce5671461060757806332a8f9d414610627578063372500ab1461063a575f80fd5b806313ee9df41461057557806318160ddd1461058a57806319e67246146105925780631d2af0b91461059b5780631e8410da146105c5575f80fd5b8063095ea7b311610463578063095ea7b3146105165780630a28a477146105395780630c14935e1461054c578063111c8453146105545780631160265b14610568575f80fd5b806270e5951461049e57806301e1d114146104b357806306fdde03146104ce578063078b0fb7146104e357806307a2d13a14610503575b5f80fd5b6104b16104ac3660046147c7565b610a69565b005b6104bb610b69565b6040519081526020015b60405180910390f35b6104d6610c57565b6040516104c59190614842565b6104f66104f1366004614873565b610d17565b6040516104c5919061488e565b6104bb6105113660046148e5565b610daa565b6105296105243660046148fc565b610db5565b60405190151581526020016104c5565b6104bb6105473660046148e5565b610dcc565b6104bb610e21565b60055461052990600160a01b900460ff1681565b6010546105299060ff1681565b61057d610ece565b6040516104c59190614926565b6104bb610fad565b6104bb60095481565b6104bb6105a93660046149d2565b600f60209081525f928352604080842090915290825290205481565b6104bb60025481565b6105296105dc3660046149fe565b611025565b6104b16105ef366004614a3c565b61104a565b6104bb6106023660046148e5565b6112ea565b600154600160a01b900460ff165b60405160ff90911681526020016104c5565b6104b1610635366004614dae565b6112f6565b6104b1611422565b600a54610655906001600160a01b031681565b6040516001600160a01b0390911681526020016104c5565b6104bb61142d565b61065561146f565b6104b16114a3565b6104bb610693366004614873565b611595565b6104b16115d7565b600554610655906001600160a01b031681565b600b54610655906001600160a01b031681565b6104bb6106d43660046148e5565b6116c2565b6104b16106e7366004614873565b61171a565b6104b16117cb565b6104b1610702366004614873565b6118dd565b6105296119bc565b6104b16119d1565b6104b1610725366004614e9d565b611a11565b6104b16107383660046148e5565b611b02565b6104b1611b74565b6104bb610753366004614ed3565b611be4565b6104bb610766366004614873565b611ea1565b6104b1611ec7565b6104f6610781366004614873565b611ed8565b6104b1610794366004614ef6565b611f29565b6104b16107a7366004614f11565b611f98565b6104b16120a7565b6104b16107c2366004614ef6565b61218a565b6106556121e8565b6104bb6107dd366004614ed3565b612210565b6104d66124e6565b600154610655906001600160a01b031681565b6104bb60045481565b6104bb6108143660046149d2565b600e60209081525f928352604080842090915290825290205481565b6104bb61083e3660046148e5565b612524565b6104bb5f5481565b6105296108593660046148fc565b61252f565b6104bb61253c565b6104b16108743660046148e5565b6125bb565b6104bb6108873660046148e5565b612696565b6104bb61089a366004614873565b600d6020525f908152604090205481565b6104b16108b9366004614f31565b6126e2565b6104bb6108cc366004614f77565b612913565b6108d9612af1565b6040516104c59190614fb6565b6104bb6108f43660046148e5565b612b7a565b6104bb610907366004614f77565b612b85565b6104b161091a3660046148e5565b612d7c565b6104b161092d3660046148e5565b612dc0565b6104b16109403660046148e5565b612ee3565b61094d6130c9565b6040516104c59190615021565b6104bb610968366004614873565b613129565b6104bb61097b3660046148e5565b61317c565b6104bb61098e366004614873565b613187565b610615600981565b6104bb6109a9366004614873565b613191565b5f610529565b6104bb6109c23660046148e5565b61320f565b6104bb6109d53660046149d2565b61321b565b6104b16109e8366004614873565b613264565b60055461052990600160a81b900460ff1681565b6104bb60035481565b6104bb610a183660046148e5565b6132ee565b6104b1610a2b3660046148e5565b61334b565b6104b1610a3e366004614873565b613466565b6104b1610a51366004615061565b6134a0565b6104b1610a643660046148e5565b6135fe565b610a71613647565b6006548210610a9b5760405163042a2e7160e11b8152600481018390526024015b60405180910390fd5b73d35a6627523f0b49e28b971de4629244a0491dae634d05b5986006610abf61146f565b60015460405160e085901b6001600160e01b031916815260048101939093526001600160a01b03918216602484015260448301879052851515606484015216608482015260a401602060405180830381865af4158015610b21573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b4591906150d4565b600180546001600160a01b0319166001600160a01b03929092169190911790555050565b5f73e1df067ed5c02ea743df0cd072e715c538c8f43b63b55f6002610b8c61146f565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015610bd0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bf491906150ef565b60066040518363ffffffff1660e01b8152600401610c13929190615162565b602060405180830381865af4158015610c2e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c5291906150ef565b905090565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0380546060915f80516020615d7f83398151915291610c959061517a565b80601f0160208091040260200160405190810160405280929190818152602001828054610cc19061517a565b8015610d0c5780601f10610ce357610100808354040283529160200191610d0c565b820191905f5260205f20905b815481529060010190602001808311610cef57829003601f168201915b505050505091505090565b606073a9ce9db84b7eeabfb5c62d24863bb1c7f8d29cb7639eb4c8e8610d3c84611ea1565b84600c600d600e6040518663ffffffff1660e01b8152600401610d639594939291906151ed565b5f60405180830381865af4158015610d7d573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610da4919081019061522a565b92915050565b5f610da4825f613679565b5f33610dc28185856136b7565b5060019392505050565b5f610dd88260016136c4565b6005549091506001600160a01b03163303610df35780610da4565b600754610da49061271090610e1890600160401b90046001600160401b0316826152ea565b8391905f6136f9565b5f80610e34670de0b6b3a7640000610daa565b905073a7e4636371e5606606aed9e314e67ea86cb9b63463aabbf18d6008610e5a610b69565b84600254610e6661146f565b60076040518763ffffffff1660e01b8152600401610e89969594939291906152fd565b602060405180830381865af4158015610ea4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ec891906150ef565b91505090565b604080516080810182525f808252602082018190529181019190915260608082015260408051608081018252600780546001600160401b038082168452600160401b82048116602080860191909152600160801b909204168385015260088054855181840281018401909652808652939492936060860193925f9084015b82821015610fa0575f848152602090819020604080516060810182526003860290920180548352600180820154848601526002909101546001600160401b0316918301919091529083529092019101610f4c565b5050505081525050905090565b5f73e1df067ed5c02ea743df0cd072e715c538c8f43b634712c426610ff07f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b600a5460405160e084901b6001600160e01b031916815260048101929092526001600160a01b03166024820152604401610c13565b5f33611032858285613746565b61103d8585856137a3565b60019150505b9392505050565b611052613800565b61105a613647565b60608082156110755761106f83850185615432565b90925090505b5f61107e610fad565b83516006549192506060915f805b828110156112ae575f5b848110156110f757818982815181106110b1576110b16154e4565b6020026020010151036110df578781815181106110d0576110d06154e4565b602002602001015195506110f7565b60408051602081019091525f81529550600101611096565b505f6006828154811061110c5761110c6154e4565b5f918252602090912060039091020154604051630aa0465b60e21b81526001600160a01b0390911690632a81196c90611149908990600401614842565b5f604051808303815f875af1158015611164573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261118b919081019061522a565b9050805192505f5b838110156112a4575f8282815181106111ae576111ae6154e4565b60200260200101516020015190505f8383815181106111cf576111cf6154e4565b60200260200101515f01519050815f1461129a576001600160a01b0381165f908152600d6020526040812054900361124c57600c80546001810182555f919091527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319166001600160a01b0383161790555b891561129a5761126d826ec097ce7bc90715b34b9f10000000008c5f6136f9565b6001600160a01b0382165f908152600d6020526040812080549091906112949084906154f8565b90915550505b5050600101611193565b505060010161108c565b506040517f429eabcd6b69e5ca232c6a9d2c5af1805e375e951df8e9bfe831b445e5f69b2b905f90a1505050505050506112e661384a565b5050565b5f610da4823333612913565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561133a5750825b90505f826001600160401b031660011480156113555750303b155b905081158015611363575080155b156113815760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156113ab57845460ff60401b1916600160401b1785555b6113b3613800565b6113c48e8e8e8e8e8e8e8e8e613870565b6113cc61384a565b831561141257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050505050505050565b61142b33613a26565b565b5f73aaa2ce316e920377701f63c4ffd0c0d6ebed92216369e4822761145061146f565b60066040518363ffffffff1660e01b8152600401610c1392919061550b565b5f807f0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e005b546001600160a01b031692915050565b5f5415806114b657506114b4610fad565b155b156114c057426004555b6114c86119bc565b61157f575f6114d5610e21565b6114dd61253c565b6114e791906154f8565b90505f6114fb670de0b6b3a7640000610daa565b90505f611506610b69565b90506002548211156115185760028290555b5f8311801561152657505f81115b1561157b575f611534610fad565b90505f8115611559576115548261154b87866152ea565b8791905f6136f9565b61155b565b845b600554909150611574906001600160a01b031682613a73565b5050426004555b5050505b611587613647565b61158f613aa7565b42600455565b5f61159e6119bc565b806115b257506003546115af610b69565b10155b6115d0576115be610b69565b6003546115cb91906152ea565b610da4565b5f92915050565b6115df613800565b5f5415806115f257506115f0610fad565b155b156115fc57426004555b6116046119bc565b6116b2575f611611610e21565b61161961253c565b61162391906154f8565b90505f611637670de0b6b3a7640000610daa565b90505f611642610b69565b90506002548211156116545760028290555b5f8311801561166257505f81115b156116ae575f611670610fad565b90505f811561168c576116878261154b87866152ea565b61168e565b845b6005549091506116a7906001600160a01b031682613a73565b5050426004555b5050505b6116ba613647565b61142b61384a565b6005545f906001600160a01b031633036116e057610da4825f613679565b6007545f90611705908490600160401b90046001600160401b031661271060016136f9565b905061104361171482856152ea565b5f613679565b611722613647565b600a546040516344401bd560e01b81526001600160a01b039182166004820152908216602482015273aaa2ce316e920377701f63c4ffd0c0d6ebed9221906344401bd590604401602060405180830381865af4158015611784573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117a891906150d4565b600a80546001600160a01b0319166001600160a01b039290921691909117905550565b6117d3613647565b600554600160a01b900460ff16156117fe576040516303eb29d960e01b815260040160405180910390fd5b73d35a6627523f0b49e28b971de4629244a0491dae6338abfe2b600661182261146f565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015611866573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061188a91906150ef565b6040516001600160e01b031960e085901b168152600481019290925260248201526044015b5f6040518083038186803b1580156118c5575f80fd5b505af41580156118d7573d5f803e3d5ffd5b50505050565b6118e5613647565b6001600160a01b03811661190c5760405163dd2fe90160e01b815260040160405180910390fd5b5f61191561146f565b600b549091506001600160a01b031680156119395761193782825f6001613b00565b505b61194782845f196001613b00565b50826001600160a01b0316816001600160a01b03167f8a8682c8e0b406a8cfcc7c7c4ac90372d088ddb8dfaf1baea5435b69e832a43a6001604051611990911515815260200190565b60405180910390a35050600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f80516020615d9f8339815191525460ff1690565b6119d9613647565b6040516356380c2b60e01b81526006600482015273d35a6627523f0b49e28b971de4629244a0491dae906356380c2b906024016118af565b5f541580611a245750611a22610fad565b155b15611a2e57426004555b611a366119bc565b611ae4575f611a43610e21565b611a4b61253c565b611a5591906154f8565b90505f611a69670de0b6b3a7640000610daa565b90505f611a74610b69565b9050600254821115611a865760028290555b5f83118015611a9457505f81115b15611ae0575f611aa2610fad565b90505f8115611abe57611ab98261154b87866152ea565b611ac0565b845b600554909150611ad9906001600160a01b031682613a73565b5050426004555b5050505b611aec613647565b806007611af98282615621565b50504260045550565b611b0a613647565b60405163dd40c63b60e01b8152600660048201526024810182905273d35a6627523f0b49e28b971de4629244a0491dae9063dd40c63b906044015b5f6040518083038186803b158015611b5b575f80fd5b505af4158015611b6d573d5f803e3d5ffd5b5050505050565b611b7c613647565b60055460408051600160a01b90920460ff16158015835260208301527f40f0175b5fde6802a96ba1ba9eeaaa9564b53a1c021b6fcde51422b574abcdbd910160405180910390a16005805460ff60a01b198116600160a01b9182900460ff1615909102179055565b5f611bed613800565b611bf5613c0f565b5f541580611c085750611c06610fad565b155b15611c1257426004555b611c1a6119bc565b611cc8575f611c27610e21565b611c2f61253c565b611c3991906154f8565b90505f611c4d670de0b6b3a7640000610daa565b90505f611c58610b69565b9050600254821115611c6a5760028290555b5f83118015611c7857505f81115b15611cc4575f611c86610fad565b90505f8115611ca257611c9d8261154b87866152ea565b611ca4565b845b600554909150611cbd906001600160a01b031682613a73565b5050426004555b5050505b611cd182613c35565b60035483611cdd610b69565b611ce791906154f8565b1115611d0657604051631264675f60e01b815260040160405180910390fd5b6005546001600160a01b03163303611d2957611d22835f6136c4565b9050611d89565b6007545f90611d5290611d4b9086906001600160401b031661271060016136f9565b60016136c4565b905080611d5f855f6136c4565b611d6991906152ea565b91508015611d8757600554611d87906001600160a01b031682613a73565b505b6305f5e1008111611dad57604051631f2a200560e01b815260040160405180910390fd5b611db78282613a73565b611dd6333085611dc561146f565b6001600160a01b0316929190613c6a565b600554600160a01b900460ff16611e535760405163ed9add7360e01b815273d35a6627523f0b49e28b971de4629244a0491dae9063ed9add7390611e26906006908790309060019060040161571d565b5f6040518083038186803b158015611e3c575f80fd5b505af4158015611e4e573d5f803e3d5ffd5b505050505b60408051848152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610da461384a565b6001600160a01b03165f9081525f80516020615d7f833981519152602052604090205490565b611ecf613647565b61142b5f613cc4565b604051630274467d60e11b8152600c6004820152600f60248201526001600160a01b038216604482015260609073a9ce9db84b7eeabfb5c62d24863bb1c7f8d29cb7906304e88cfa90606401610d63565b611f31613647565b60055460408051600160a81b90920460ff161515825282151560208301527ffdc806f366660aa3e77ed7f1da541577007de0e4b7e0c5d351ae99d2d01cc8f3910160405180910390a160058054911515600160a81b0260ff60a81b19909216919091179055565b611fa0613647565b73d35a6627523f0b49e28b971de4629244a0491dae6394b3d1486006600560149054906101000a900460ff16611fd461146f565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015612018573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061203c91906150ef565b6040516001600160e01b031960e086901b168152600481019390935290151560248301526044820152606481018590526084810184905260a4015b5f6040518083038186803b15801561208d575f80fd5b505af415801561209f573d5f803e3d5ffd5b505050505050565b5f5415806120ba57506120b8610fad565b155b156120c457426004555b6120cc6119bc565b61217a575f6120d9610e21565b6120e161253c565b6120eb91906154f8565b90505f6120ff670de0b6b3a7640000610daa565b90505f61210a610b69565b905060025482111561211c5760028290555b5f8311801561212a57505f81115b15612176575f612138610fad565b90505f81156121545761214f8261154b87866152ea565b612156565b845b60055490915061216f906001600160a01b031682613a73565b5050426004555b5050505b612182613647565b61142b613d34565b612192613647565b6010805482151560ff19821681179092556040805160ff909216801515835260208301939093527f522d3c2ddc88f29bac386c82e605c5bf322d91aa85997ef97a688539d9d60333910160405180910390a15050565b5f807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300611493565b5f612219613800565b612221613c0f565b5f5415806122345750612232610fad565b155b1561223e57426004555b6122466119bc565b6122f4575f612253610e21565b61225b61253c565b61226591906154f8565b90505f612279670de0b6b3a7640000610daa565b90505f612284610b69565b90506002548211156122965760028290555b5f831180156122a457505f81115b156122f0575f6122b2610fad565b90505f81156122ce576122c98261154b87866152ea565b6122d0565b845b6005549091506122e9906001600160a01b031682613a73565b5050426004555b5050505b6122fd82613c35565b6305f5e100831161232157604051631f2a200560e01b815260040160405180910390fd5b6007546005546001600160401b03909116905f906001600160a01b0316330361234a575f61236f565b8461236561271061235b85826152ea565b88919060016136f9565b61236f91906152ea565b905061238561237e82876154f8565b6001613679565b925060035483612393610b69565b61239d91906154f8565b11156123bc57604051631264675f60e01b815260040160405180910390fd5b6123c584611595565b8311156123e557604051631264675f60e01b815260040160405180910390fd5b801561240157600554612401906001600160a01b031682613a73565b61240b8486613a73565b612419333085611dc561146f565b600554600160a01b900460ff166124965760405163ed9add7360e01b815273d35a6627523f0b49e28b971de4629244a0491dae9063ed9add7390612469906006908790309060019060040161571d565b5f6040518083038186803b15801561247f575f80fd5b505af4158015612491573d5f803e3d5ffd5b505050505b60408051848152602081018790526001600160a01b0386169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35050610da461384a565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0480546060915f80516020615d7f83398151915291610c959061517a565b5f610da48233612210565b5f33610dc28185856137a3565b5f612545613c0f565b60075473a7e4636371e5606606aed9e314e67ea86cb9b63490639d02c9f490600160801b90046001600160401b031661257c610b69565b600480546040516001600160e01b031960e087901b1681526001600160401b039094169184019190915260248301919091526044820152606401610c13565b6125c3613647565b73d35a6627523f0b49e28b971de4629244a0491dae637d69c04260066125e761146f565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa15801561262b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061264f91906150ef565b60055460405160e085901b6001600160e01b03191681526004810193909352602483019190915260448201859052600160a01b900460ff1615156064820152608401611b45565b6005545f9082906001600160a01b031633146126d7576007546126d490612710906126ca906001600160401b0316826152ea565b85919060016136f9565b90505b611043816001613679565b6126ea613800565b6126f2613647565b5f5415806127055750612703610fad565b155b1561270f57426004555b6127176119bc565b6127c5575f612724610e21565b61272c61253c565b61273691906154f8565b90505f61274a670de0b6b3a7640000610daa565b90505f612755610b69565b90506002548211156127675760028290555b5f8311801561277557505f81115b156127c1575f612783610fad565b90505f811561279f5761279a8261154b87866152ea565b6127a1565b845b6005549091506127ba906001600160a01b031682613a73565b5050426004555b5050505b5f8073d35a6627523f0b49e28b971de4629244a0491dae6370b9cd68600685878960015f9054906101000a90046001600160a01b031661280361146f565b6040518763ffffffff1660e01b815260040161282496959493929190615756565b606060405180830381865af415801561283f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061286391906157bd565b600180546001600160a01b0319166001600160a01b0394851617905590935091508116156128c8576040516001600160a01b03821681527f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea49060200160405180910390a15b6040516001600160a01b03831681527f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f19060200160405180910390a1505061290e61384a565b505050565b5f61291c613800565b612924613c0f565b5f5415806129375750612935610fad565b155b1561294157426004555b6129496119bc565b6129f7575f612956610e21565b61295e61253c565b61296891906154f8565b90505f61297c670de0b6b3a7640000610daa565b90505f612987610b69565b90506002548211156129995760028290555b5f831180156129a757505f81115b156129f3575f6129b5610fad565b90505f81156129d1576129cc8261154b87866152ea565b6129d3565b845b6005549091506129ec906001600160a01b031682613a73565b5050426004555b5050505b6001600160a01b038316612a1e57604051634e46966960e11b815260040160405180910390fd5b612a2782613187565b841115612a4757604051631264675f60e01b815260040160405180910390fd5b612a528460016136c4565b90506305f5e1008111612a7857604051631f2a200560e01b815260040160405180910390fd5b600754600554600160401b9091046001600160401b0316905f906001600160a01b03163303612aa7575f612acc565b82612ac2612710612ab885826152ea565b86919060016136f9565b612acc91906152ea565b9050612ad881846154f8565b9250612ae7838686848a613d7c565b505061104361384a565b60606006805480602002602001604051908101604052809291908181526020015f905b82821015612b71575f8481526020908190206040805180820182526003860290920180546001600160a01b031683528151808301909252600180820154835260029091015482850152828401919091529083529092019101612b14565b50505050905090565b5f610da48233611be4565b5f612b8e613800565b612b96613c0f565b5f541580612ba95750612ba7610fad565b155b15612bb357426004555b612bbb6119bc565b612c69575f612bc8610e21565b612bd061253c565b612bda91906154f8565b90505f612bee670de0b6b3a7640000610daa565b90505f612bf9610b69565b9050600254821115612c0b5760028290555b5f83118015612c1957505f81115b15612c65575f612c27610fad565b90505f8115612c4357612c3e8261154b87866152ea565b612c45565b845b600554909150612c5e906001600160a01b031682613a73565b5050426004555b5050505b73e1df067ed5c02ea743df0cd072e715c538c8f43b6383ff48f58486612c8e86613191565b6040516001600160e01b031960e086901b1681526001600160a01b039093166004840152602483019190915260448201526064015f6040518083038186803b158015612cd8575f80fd5b505af4158015612cea573d5f803e3d5ffd5b505050506305f5e10063ffffffff168411612d1857604051631f2a200560e01b815260040160405180910390fd5b6005545f906001600160a01b03163303612d32575f612d55565b600754612d55908690600160401b90046001600160401b031661271060016136f9565b9050612d6461171482876152ea565b9150612d738585858486613d7c565b5061104361384a565b612d84613647565b60038190556040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f2906020015b60405180910390a150565b6001546001600160a01b03163314612ded576040516311dc66cd60e11b8152336004820152602401610a92565b5f7321ea5cb6b67d5de79ca94682957e238448fdeca7631bc92572612e1061146f565b60015460405160e084901b6001600160e01b03191681526001600160a01b039283166004820152602481018790529116604482015260066064820152608401602060405180830381865af4158015612e6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e8e91906150ef565b600154604051639db88c9360e01b815260048101859052602481018390526001600160a01b03909116604482015290915073aaa2ce316e920377701f63c4ffd0c0d6ebed922190639db88c9390606401612077565b612eeb613800565b612ef3613647565b5f541580612f065750612f04610fad565b155b15612f1057426004555b612f186119bc565b612fc6575f612f25610e21565b612f2d61253c565b612f3791906154f8565b90505f612f4b670de0b6b3a7640000610daa565b90505f612f56610b69565b9050600254821115612f685760028290555b5f83118015612f7657505f81115b15612fc2575f612f84610fad565b90505f8115612fa057612f9b8261154b87866152ea565b612fa2565b845b600554909150612fbb906001600160a01b031682613a73565b5050426004555b5050505b600654808210612fec5760405163042a2e7160e11b815260048101839052602401610a92565b60015473d35a6627523f0b49e28b971de4629244a0491dae9063999553049060069085906001600160a01b031661302161146f565b6040516001600160e01b031960e087901b168152600481019490945260248401929092526001600160a01b039081166044840152166064820152608401602060405180830381865af4158015613079573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061309d91906150d4565b600180546001600160a01b0319166001600160a01b0392909216919091179055506130c661384a565b50565b6060600c80548060200260200160405190810160405280929190818152602001828054801561311f57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311613101575b5050505050905090565b5f6131326119bc565b8061313d5750600354155b1561314957505f919050565b5f196003540361315b57505f19919050565b5f6131686003545f6136c4565b9050613172610fad565b61104390826152ea565b5f610da4825f6136c4565b5f610da482613eea565b5f61319a6119bc565b806131ae5750600554600160a81b900460ff165b156131ba57505f919050565b5f6131c483611ea1565b600a549091506001600160a01b0316156131de5792915050565b5f6131e761142d565b90505f6131f4825f6136c4565b9050828110156132045780613206565b825b95945050505050565b5f610da4823333612b85565b6001600160a01b039182165f9081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b61326c613647565b6001600160a01b03811661329357604051630ed1b8b360e31b815260040160405180910390fd5b6005546040516001600160a01b038084169216907faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d3905f90a3600580546001600160a01b0319166001600160a01b0392909216919091179055565b5f806132fa835f6136c4565b6005549091505f906001600160a01b03163303613317575f613337565b60075461333790611d4b9086906001600160401b031661271060016136f9565b905061334381836152ea565b949350505050565b613353613647565b61335b613800565b600a546001600160a01b031661338457604051637c0c423f60e11b815260040160405180910390fd5b5f61339661339061142d565b5f6136c4565b90505f60405180606001604052806133ac610b69565b81526020016133b9610fad565b81526009602090910152600a5490915073aaa2ce316e920377701f63c4ffd0c0d6ebed92219063811a8b2a906001600160a01b031685856133f861146f565b600b546040516001600160e01b031960e088901b16815261343095949392916006916001600160a01b03909116908a906004016157fc565b5f6040518083038186803b158015613446575f80fd5b505af4158015613458573d5f803e3d5ffd5b5050505050506130c661384a565b61346e613647565b6001600160a01b03811661349757604051631e4fbdf760e01b81525f6004820152602401610a92565b6130c681613cc4565b6134a8613800565b6134b0613647565b5f5415806134c357506134c1610fad565b155b156134cd57426004555b6134d56119bc565b613583575f6134e2610e21565b6134ea61253c565b6134f491906154f8565b90505f613508670de0b6b3a7640000610daa565b90505f613513610b69565b90506002548211156135255760028290555b5f8311801561353357505f81115b1561357f575f613541610fad565b90505f811561355d576135588261154b87866152ea565b61355f565b845b600554909150613578906001600160a01b031682613a73565b5050426004555b5050505b73d35a6627523f0b49e28b971de4629244a0491dae63bd0ba4ec60068585856135aa61146f565b6040518663ffffffff1660e01b81526004016135ca959493929190615865565b5f6040518083038186803b1580156135e0575f80fd5b505af41580156135f2573d5f803e3d5ffd5b5050505061290e61384a565b613606613647565b60095460408051918252602082018390527f47ecb16e79d3795bad1b15b763dad4136c346b10a729a5e22a60ae3416b571ba910160405180910390a1600955565b336136506121e8565b6001600160a01b03161461142b5760405163118cdaa760e01b8152336004820152602401610a92565b5f611043613685610b69565b6136909060016154f8565b61369c6009600a6159aa565b6136a4610fad565b6136ae91906154f8565b859190856136f9565b61290e8383836001613fd7565b5f6110436136d46009600a6159aa565b6136dc610fad565b6136e691906154f8565b6136ee610b69565b6136ae9060016154f8565b5f806137068686866140ba565b905061371183614179565b801561372c57505f8480613727576137276159b8565b868809115b156132065761373c6001826154f8565b9695505050505050565b5f613751848461321b565b90505f1981146118d7578181101561379557604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401610a92565b6118d784848484035f613fd7565b6001600160a01b0383166137cc57604051634b637e8f60e11b81525f6004820152602401610a92565b6001600160a01b0382166137f55760405163ec442f0560e01b81525f6004820152602401610a92565b61290e8383836141a5565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0080546001190161384457604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b6138786141e0565b613881896141f0565b61388b8888614201565b61389482614213565b61389c614224565b6001600160a01b0389166138c357604051630ccd248560e21b815260040160405180910390fd5b60015460405163c03e99d960e01b81527321ea5cb6b67d5de79ca94682957e238448fdeca79163c03e99d991613916918d916009918c916001600160a01b03909116906006908c90600790600401615a6f565b6040805180830381865af4158015613930573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139549190615b25565b600180546001600160a81b031916600160a01b60ff93909316929092026001600160a01b031916919091176001600160a01b0392831617905585166139ac57604051630ed1b8b360e31b815260040160405180910390fd5b60058054633b9aca0060025560038590556010805484151560ff199091161790556001600160b01b0319166001600160a01b038088169190911790915542600455604051908a169030907f3cd5ec01b1ae7cfec6ca1863e2cd6aa25d6d1702825803ff2b7cc95010fffdc2905f90a3505050505050505050565b73a9ce9db84b7eeabfb5c62d24863bb1c7f8d29cb7631cdd9163613a4983611ea1565b83600c600d600e600f6040518763ffffffff1660e01b8152600401611b4596959493929190615b57565b6001600160a01b038216613a9c5760405163ec442f0560e01b81525f6004820152602401610a92565b6112e65f83836141a5565b613aaf614234565b5f80516020615d9f833981519152805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b039091168152602001612db5565b604080516001600160a01b038516602482015260448082018590528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790525f90613b538682614259565b915081613bc357604080516001600160a01b03871660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613bae908790614259565b91508115613bc357613bc08682614259565b91505b81158015613bce5750825b15613c0657604051632d28f16360e21b81526001600160a01b0380881660048301528616602482015260448101859052606401610a92565b50949350505050565b613c176119bc565b1561142b5760405163d93c066560e01b815260040160405180910390fd5b6001600160a01b038116613c5c57604051634e46966960e11b815260040160405180910390fd5b5f545f036130c657425f5550565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526118d790859061429e565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b613d3c613c0f565b5f80516020615d9f833981519152805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833613ae8565b600554600160a81b900460ff1615613da757604051636022a9e760e01b815260040160405180910390fd5b336001600160a01b03841614613dc257613dc2833387613746565b613dcc83866142ff565b8115613de857600554613de8906001600160a01b031683613a73565b5f613df161142d565b905073aaa2ce316e920377701f63c4ffd0c0d6ebed922163a49fe6f783613e18868a6152ea565b8885613e2261146f565b600a54600954600b546010546040516001600160e01b031960e08c901b168152613e6c9998979695946001600160a01b03908116949360069391169160ff90911690600401615b9e565b5f6040518083038186803b158015613e82575f80fd5b505af4158015613e94573d5f803e3d5ffd5b505060408051858152602081018a90526001600160a01b0380891694508916925033917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4505050505050565b5f613ef36119bc565b80613f075750600554600160a81b900460ff165b15613f1357505f919050565b5f613f2061171484611ea1565b90505f613f2b610e21565b613f3361253c565b613f3d91906154f8565b90505f613f5583613f4c610b69565b8491905f6136f9565b6007549091505f908290613f8a90613f7f90600160401b90046001600160401b03166127106152ea565b86906127105f6136f9565b613f9491906152ea565b600a549091506001600160a01b031615613fb15795945050505050565b5f613fba61142d565b905081811015613fca5780613fcc565b815b979650505050505050565b5f80516020615d7f8339815191526001600160a01b03851661400e5760405163e602df0560e01b81525f6004820152602401610a92565b6001600160a01b03841661403757604051634a1406b160e11b81525f6004820152602401610a92565b6001600160a01b038086165f90815260018301602090815260408083209388168352929052208390558115611b6d57836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925856040516140ab91815260200190565b60405180910390a35050505050565b5f838302815f1985870982811083820303915050805f036140ee578382816140e4576140e46159b8565b0492505050611043565b80841161410e5760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f600282600381111561418e5761418e615c08565b6141989190615c1c565b60ff166001149050919050565b6001600160a01b038316156141bd576141bd83613a26565b6001600160a01b038216156141d5576141d582613a26565b61290e838383614333565b6141e861446c565b61142b6144b5565b6141f861446c565b6130c6816144d5565b61420961446c565b6112e68282614558565b61421b61446c565b6130c6816145a8565b61422c61446c565b61142b6145b0565b61423c6119bc565b61142b57604051638dfc202b60e01b815260040160405180910390fd5b5f805f8060205f8651602088015f8a5af192503d91505f51905082801561373c5750811561428a578060011461373c565b50505050506001600160a01b03163b151590565b5f6142b26001600160a01b038416836145b8565b905080515f141580156142d65750808060200190518101906142d49190615c49565b155b1561290e57604051635274afe760e01b81526001600160a01b0384166004820152602401610a92565b6001600160a01b03821661432857604051634b637e8f60e11b81525f6004820152602401610a92565b6112e6825f836141a5565b5f80516020615d7f8339815191526001600160a01b03841661436d5781816002015f82825461436291906154f8565b909155506143dd9050565b6001600160a01b0384165f90815260208290526040902054828110156143bf5760405163391434e360e21b81526001600160a01b03861660048201526024810182905260448101849052606401610a92565b6001600160a01b0385165f9081526020839052604090209083900390555b6001600160a01b0383166143fb576002810180548390039055614419565b6001600160a01b0383165f9081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161445e91815260200190565b60405180910390a350505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661142b57604051631afcd79f60e31b815260040160405180910390fd5b6144bd61446c565b5f80516020615d9f833981519152805460ff19169055565b6144dd61446c565b7f0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e005f80614509846145c5565b915091508161451957601261451b565b805b83546001600160a81b031916600160a01b60ff92909216919091026001600160a01b031916176001600160a01b0394909416939093179091555050565b61456061446c565b5f80516020615d7f8339815191527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace036145998482615ca8565b50600481016118d78382615ca8565b61346e61446c565b61384a61446c565b606061104383835f61469b565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b17905290515f918291829182916001600160a01b0387169161460b91615d63565b5f60405180830381855afa9150503d805f8114614643576040519150601f19603f3d011682016040523d82523d5f602084013e614648565b606091505b509150915081801561465c57506020815110155b1561468f575f8180602001905181019061467691906150ef565b905060ff811161468d576001969095509350505050565b505b505f9485945092505050565b6060814710156146c05760405163cd78605960e01b8152306004820152602401610a92565b5f80856001600160a01b031684866040516146db9190615d63565b5f6040518083038185875af1925050503d805f8114614715576040519150601f19603f3d011682016040523d82523d5f602084013e61471a565b606091505b509150915061373c86838360608261473a5761473582614781565b611043565b815115801561475157506001600160a01b0384163b155b1561477a57604051639996b31560e01b81526001600160a01b0385166004820152602401610a92565b5080611043565b8051156147915780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80151581146130c6575f80fd5b80356147c2816147aa565b919050565b5f80604083850312156147d8575f80fd5b8235915060208301356147ea816147aa565b809150509250929050565b5f5b8381101561480f5781810151838201526020016147f7565b50505f910152565b5f815180845261482e8160208601602086016147f5565b601f01601f19169290920160200192915050565b602081525f6110436020830184614817565b6001600160a01b03811681146130c6575f80fd5b80356147c281614854565b5f60208284031215614883575f80fd5b813561104381614854565b602080825282518282018190525f919060409081850190868401855b828110156148d857815180516001600160a01b031685528601518685015292840192908501906001016148aa565b5091979650505050505050565b5f602082840312156148f5575f80fd5b5035919050565b5f806040838503121561490d575f80fd5b823561491881614854565b946020939093013593505050565b5f602080835260a083016001600160401b0380865116602086015280602087015116604086015260408601516060828216606088015260608801519250608080880152839150825180855260c0880192506020840194505f93505b808410156149c5576149b183865180518252602080820151908301526040908101516001600160401b0316910152565b938501936001939093019291810191614981565b5090979650505050505050565b5f80604083850312156149e3575f80fd5b82356149ee81614854565b915060208301356147ea81614854565b5f805f60608486031215614a10575f80fd5b8335614a1b81614854565b92506020840135614a2b81614854565b929592945050506040919091013590565b5f8060208385031215614a4d575f80fd5b82356001600160401b0380821115614a63575f80fd5b818501915085601f830112614a76575f80fd5b813581811115614a84575f80fd5b866020828501011115614a95575f80fd5b60209290920196919550909350505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715614add57614add614aa7565b60405290565b604051608081016001600160401b0381118282101715614add57614add614aa7565b604051606081016001600160401b0381118282101715614add57614add614aa7565b604051601f8201601f191681016001600160401b0381118282101715614b4f57614b4f614aa7565b604052919050565b5f6001600160401b03831115614b6f57614b6f614aa7565b614b82601f8401601f1916602001614b27565b9050828152838383011115614b95575f80fd5b828260208301375f602084830101529392505050565b5f82601f830112614bba575f80fd5b61104383833560208501614b57565b5f6001600160401b03821115614be157614be1614aa7565b5060051b60200190565b5f82601f830112614bfa575f80fd5b81356020614c0f614c0a83614bc9565b614b27565b82815260609283028501820192828201919087851115614c2d575f80fd5b8387015b858110156149c55780890382811215614c48575f80fd5b614c50614abb565b8235614c5b81614854565b81526040601f198301811315614c6f575f80fd5b614c77614abb565b848901358152908401358882015281880152855250928401928101614c31565b6001600160401b03811681146130c6575f80fd5b5f60808284031215614cbb575f80fd5b614cc3614ae3565b90508135614cd081614c97565b8152602082810135614ce181614c97565b82820152604083810135614cf481614c97565b60408401526060848101356001600160401b03811115614d12575f80fd5b8501601f81018713614d22575f80fd5b8035614d30614c0a82614bc9565b818152606090910282018501908581019089831115614d4d575f80fd5b928601925b82841015614d9c5784848b031215614d68575f80fd5b614d70614b05565b84358152878501358882015286850135614d8981614c97565b8188015282529284019290860190614d52565b60608801525094979650505050505050565b5f805f805f805f805f6101208a8c031215614dc7575f80fd5b614dd08a614868565b985060208a01356001600160401b0380821115614deb575f80fd5b614df78d838e01614bab565b995060408c0135915080821115614e0c575f80fd5b614e188d838e01614bab565b985060608c0135915080821115614e2d575f80fd5b614e398d838e01614beb565b9750614e4760808d01614868565b965060a08c0135915080821115614e5c575f80fd5b50614e698c828d01614cab565b94505060c08a01359250614e7f60e08b01614868565b9150614e8e6101008b016147b7565b90509295985092959850929598565b5f60208284031215614ead575f80fd5b81356001600160401b03811115614ec2575f80fd5b820160808185031215611043575f80fd5b5f8060408385031215614ee4575f80fd5b8235915060208301356147ea81614854565b5f60208284031215614f06575f80fd5b8135611043816147aa565b5f8060408385031215614f22575f80fd5b50508035926020909101359150565b5f805f83850360a0811215614f44575f80fd5b843593506020850135614f56816147aa565b92506060603f1982011215614f69575f80fd5b506040840190509250925092565b5f805f60608486031215614f89575f80fd5b833592506020840135614f9b81614854565b91506040840135614fab81614854565b809150509250925092565b602080825282518282018190525f9190848201906040850190845b8181101561501557835180516001600160a01b031684528501516150018685018280518252602090810151910152565b509284019260609290920191600101614fd1565b50909695505050505050565b602080825282518282018190525f9190848201906040850190845b818110156150155783516001600160a01b03168352928401929184019160010161503c565b5f805f60408486031215615073575f80fd5b83356001600160401b0380821115615089575f80fd5b818601915086601f83011261509c575f80fd5b8135818111156150aa575f80fd5b8760208260061b85010111156150be575f80fd5b60209283019550935050840135614fab816147aa565b5f602082840312156150e4575f80fd5b815161104381614854565b5f602082840312156150ff575f80fd5b5051919050565b5f815480845260208085019450835f5260205f205f5b838110156151575781546001600160a01b0316875260018281015484890152600283015460408901526060909701966003909201910161511c565b509495945050505050565b828152604060208201525f6133436040830184615106565b600181811c9082168061518e57607f821691505b6020821081036151ac57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f815480845260208085019450835f5260205f205f5b838110156151575781546001600160a01b0316875295820195600191820191016151c8565b8581526001600160a01b038516602082015260a0604082018190525f90615216908301866151b2565b606083019490945250608001529392505050565b5f602080838503121561523b575f80fd5b82516001600160401b03811115615250575f80fd5b8301601f81018513615260575f80fd5b805161526e614c0a82614bc9565b81815260069190911b8201830190838101908783111561528c575f80fd5b928401925b82841015613fcc57604084890312156152a8575f80fd5b6152b0614abb565b84516152bb81614854565b81528486015186820152825260409093019290840190615291565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610da457610da46152d6565b5f60c0820160c0835280895480835260e0850191508a5f526020925060205f205f5b8281101561535a57815484526001808301548686015260028301546001600160401b031660408601526060909401936003909201910161531f565b505050809250505086602083015285604083015284606083015261538960808301856001600160a01b03169052565b8260a0830152979650505050505050565b5f82601f8301126153a9575f80fd5b813560206153b9614c0a83614bc9565b82815260059290921b840181019181810190868411156153d7575f80fd5b8286015b848110156154275780356001600160401b038111156153f8575f80fd5b8701603f81018913615408575f80fd5b615419898683013560408401614b57565b8452509183019183016153db565b509695505050505050565b5f8060408385031215615443575f80fd5b82356001600160401b0380821115615459575f80fd5b818501915085601f83011261546c575f80fd5b8135602061547c614c0a83614bc9565b82815260059290921b8401810191818101908984111561549a575f80fd5b948201945b838610156154b85785358252948201949082019061549f565b965050860135925050808211156154cd575f80fd5b506154da8582860161539a565b9150509250929050565b634e487b7160e01b5f52603260045260245ffd5b80820180821115610da457610da46152d6565b6001600160a01b03831681526040602082018190525f9061334390830184615106565b8135815560208201356001820155604082013561554a81614c97565b60028201805467ffffffffffffffff19166001600160401b038316179055505050565b600160401b83111561558157615581614aa7565b8054838255808410156155ed576003816003026003810483146155a6576155a66152d6565b856003026003810487146155bc576155bc6152d6565b5f8581526020902091820191015b818110156155e9575f80825560018201819055600282015582016155ca565b5050505b505f8181526020812083915b8581101561209f5761560b838361552e565b60609290920191600391909101906001016155f9565b813561562c81614c97565b815467ffffffffffffffff19166001600160401b03821617825550602082013561565581614c97565b81546fffffffffffffffff0000000000000000604092831b166fffffffffffffffff0000000000000000198216811784559184013561569381614c97565b77ffffffffffffffffffffffffffffffff0000000000000000199190911690911760809190911b67ffffffffffffffff60801b16178155606082013536839003601e190181126156e1575f80fd5b820180356001600160401b038111156156f8575f80fd5b60208201915060608102360382131561570f575f80fd5b6118d781836001860161556d565b608081525f61572f6080830187615106565b6020830195909552506001600160a01b039290921660408301521515606090910152919050565b8681526101008101863561576981614854565b60018060a01b0380821660208501526157926040850160208b0180358252602090810135910152565b87151560808501528660a085015280861660c085015280851660e08501525050979650505050505050565b5f805f606084860312156157cf575f80fd5b83516157da81614854565b60208501519093506157eb81614854565b6040850151909250614fab81614854565b5f61012060018060a01b03808b168452896020850152886040850152808816606085015281608085015261583282850188615106565b951660a08401525050815160c0820152602082015160e082015260409091015160ff166101009091015295945050505050565b85815260806020820181905281018490525f8560a08301825b878110156158a65782358252602080840135908301526040928301929091019060010161587e565b50941515604084015250506001600160a01b03919091166060909101529392505050565b600181815b8085111561590457815f19048211156158ea576158ea6152d6565b808516156158f757918102915b93841c93908002906158cf565b509250929050565b5f8261591a57506001610da4565b8161592657505f610da4565b816001811461593c576002811461594657615962565b6001915050610da4565b60ff841115615957576159576152d6565b50506001821b610da4565b5060208310610133831016604e8410600b8410161715615985575081810a610da4565b61598f83836158ca565b805f19048211156159a2576159a26152d6565b029392505050565b5f61104360ff84168361590c565b634e487b7160e01b5f52601260045260245ffd5b5f608083016001600160401b0380845116855260208160208601511660208701528160408601511660408701526060915060608501516080606088015283815180865260a0890191506020830195505f92505b80831015615a6357615a4f82875180518252602080820151908301526040908101516001600160401b0316910152565b948301946001929092019190840190615a1f565b50979650505050505050565b5f60e0820160018060a01b03808b168452602060ff8b16602086015260e06040860152828a518085526101008701915060208c0194505f5b81811015615ae4578551805186168452840151615ad08585018280518252602090810151910152565b509483019460609290920191600101615aa7565b50506001600160a01b038a16606087015288608087015285810360a0870152615b0d81896159cc565b9450505050508260c083015298975050505050505050565b5f8060408385031215615b36575f80fd5b8251615b4181614854565b602084015190925060ff811681146147ea575f80fd5b8681526001600160a01b038616602082015260c0604082018190525f90615b80908301876151b2565b606083019590955250608081019290925260a0909101529392505050565b5f6101408c83528b602084015260018060a01b03808c1660408501528a6060850152808a16608085015280891660a08501528760c08501528160e0850152615be882850188615106565b951661010084015250509015156101209091015298975050505050505050565b634e487b7160e01b5f52602160045260245ffd5b5f60ff831680615c3a57634e487b7160e01b5f52601260045260245ffd5b8060ff84160691505092915050565b5f60208284031215615c59575f80fd5b8151611043816147aa565b601f82111561290e57805f5260205f20601f840160051c81016020851015615c895750805b601f840160051c820191505b81811015611b6d575f8155600101615c95565b81516001600160401b03811115615cc157615cc1614aa7565b615cd581615ccf845461517a565b84615c64565b602080601f831160018114615d08575f8415615cf15750858301515b5f19600386901b1c1916600185901b17855561209f565b5f85815260208120601f198616915b82811015615d3657888601518255948401946001909101908401615d17565b5085821015615d5357878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b5f8251615d748184602087016147f5565b919091019291505056fe52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a26469706673582212208da67acd433b98a67d8b5bdde83cef47a42041c3fce6a2cf484417cf8b2a128864736f6c63430008180033
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061049a575f3560e01c806375a046a511610263578063ba0876521161014b578063daa3a163116100ca578063ecf708581161008f578063ecf7085814610a01578063ef8b30f714610a0a578063f1f741b414610a1d578063f2fde38b14610a30578063f4aa3efa14610a43578063f745d6ca14610a56575f80fd5b8063daa3a163146109ae578063db006a75146109b4578063dd62ed3e146109c7578063e74b981b146109da578063e9f2838e146109ed575f80fd5b8063c63d75b611610110578063c63d75b61461095a578063c6e6f5921461096d578063ce96cb7714610980578063d1b39ae514610993578063d905777e1461099b575f80fd5b8063ba087652146108f9578063bdc8144b1461090c578063bf3bd25d1461091f578063c0cbbca614610932578063c4f59f9b14610945575f80fd5b8063a0712d68116101e2578063b3d7f6b9116101a7578063b3d7f6b914610879578063b3e321141461088c578063b45ba4a8146108ab578063b460af94146108be578063b49a60bb146108d1578063b6b55f25146108e6575f80fd5b8063a0712d6814610830578063a5f5be5414610843578063a9059cbb1461084b578063acfd79281461085e578063b39ed6d914610866575f80fd5b806394bf804d1161022857806394bf804d146107cf57806395d89b41146107e25780639e2f0e22146107ea5780639f3d0a48146107fd578063a039c12914610806575f80fd5b806375a046a514610786578063839e950f146107995780638456cb59146107ac57806388fba066146107b45780638da5cb5b146107c7575f80fd5b806337d5fe991161038657806355b8b781116103055780636d3300e5116102ca5780636d3300e51461072a5780636e2a6fe51461073d5780636e553f651461074557806370a0823114610758578063715018a61461076b57806372e7160114610773575f80fd5b806355b8b781146106ec57806359f7c845146106f45780635c975abb1461070757806366584f8c1461070f5780636bedc46c14610717575f80fd5b8063465718991161034b578063465718991461069857806346904840146106a057806349d0ea97146106b35780634cdad506146106c65780635337e670146106d9575f80fd5b806337d5fe991461064257806337f1834e1461066d57806338d52e0f146106755780633f4ba83a1461067d578063402d267d14610685575f80fd5b806313ee9df41161041d57806323b872dd116103e257806323b872dd146105ce5780632a81196c146105e15780632e1a7d4d146105f4578063313ce5671461060757806332a8f9d414610627578063372500ab1461063a575f80fd5b806313ee9df41461057557806318160ddd1461058a57806319e67246146105925780631d2af0b91461059b5780631e8410da146105c5575f80fd5b8063095ea7b311610463578063095ea7b3146105165780630a28a477146105395780630c14935e1461054c578063111c8453146105545780631160265b14610568575f80fd5b806270e5951461049e57806301e1d114146104b357806306fdde03146104ce578063078b0fb7146104e357806307a2d13a14610503575b5f80fd5b6104b16104ac3660046147c7565b610a69565b005b6104bb610b69565b6040519081526020015b60405180910390f35b6104d6610c57565b6040516104c59190614842565b6104f66104f1366004614873565b610d17565b6040516104c5919061488e565b6104bb6105113660046148e5565b610daa565b6105296105243660046148fc565b610db5565b60405190151581526020016104c5565b6104bb6105473660046148e5565b610dcc565b6104bb610e21565b60055461052990600160a01b900460ff1681565b6010546105299060ff1681565b61057d610ece565b6040516104c59190614926565b6104bb610fad565b6104bb60095481565b6104bb6105a93660046149d2565b600f60209081525f928352604080842090915290825290205481565b6104bb60025481565b6105296105dc3660046149fe565b611025565b6104b16105ef366004614a3c565b61104a565b6104bb6106023660046148e5565b6112ea565b600154600160a01b900460ff165b60405160ff90911681526020016104c5565b6104b1610635366004614dae565b6112f6565b6104b1611422565b600a54610655906001600160a01b031681565b6040516001600160a01b0390911681526020016104c5565b6104bb61142d565b61065561146f565b6104b16114a3565b6104bb610693366004614873565b611595565b6104b16115d7565b600554610655906001600160a01b031681565b600b54610655906001600160a01b031681565b6104bb6106d43660046148e5565b6116c2565b6104b16106e7366004614873565b61171a565b6104b16117cb565b6104b1610702366004614873565b6118dd565b6105296119bc565b6104b16119d1565b6104b1610725366004614e9d565b611a11565b6104b16107383660046148e5565b611b02565b6104b1611b74565b6104bb610753366004614ed3565b611be4565b6104bb610766366004614873565b611ea1565b6104b1611ec7565b6104f6610781366004614873565b611ed8565b6104b1610794366004614ef6565b611f29565b6104b16107a7366004614f11565b611f98565b6104b16120a7565b6104b16107c2366004614ef6565b61218a565b6106556121e8565b6104bb6107dd366004614ed3565b612210565b6104d66124e6565b600154610655906001600160a01b031681565b6104bb60045481565b6104bb6108143660046149d2565b600e60209081525f928352604080842090915290825290205481565b6104bb61083e3660046148e5565b612524565b6104bb5f5481565b6105296108593660046148fc565b61252f565b6104bb61253c565b6104b16108743660046148e5565b6125bb565b6104bb6108873660046148e5565b612696565b6104bb61089a366004614873565b600d6020525f908152604090205481565b6104b16108b9366004614f31565b6126e2565b6104bb6108cc366004614f77565b612913565b6108d9612af1565b6040516104c59190614fb6565b6104bb6108f43660046148e5565b612b7a565b6104bb610907366004614f77565b612b85565b6104b161091a3660046148e5565b612d7c565b6104b161092d3660046148e5565b612dc0565b6104b16109403660046148e5565b612ee3565b61094d6130c9565b6040516104c59190615021565b6104bb610968366004614873565b613129565b6104bb61097b3660046148e5565b61317c565b6104bb61098e366004614873565b613187565b610615600981565b6104bb6109a9366004614873565b613191565b5f610529565b6104bb6109c23660046148e5565b61320f565b6104bb6109d53660046149d2565b61321b565b6104b16109e8366004614873565b613264565b60055461052990600160a81b900460ff1681565b6104bb60035481565b6104bb610a183660046148e5565b6132ee565b6104b1610a2b3660046148e5565b61334b565b6104b1610a3e366004614873565b613466565b6104b1610a51366004615061565b6134a0565b6104b1610a643660046148e5565b6135fe565b610a71613647565b6006548210610a9b5760405163042a2e7160e11b8152600481018390526024015b60405180910390fd5b73d35a6627523f0b49e28b971de4629244a0491dae634d05b5986006610abf61146f565b60015460405160e085901b6001600160e01b031916815260048101939093526001600160a01b03918216602484015260448301879052851515606484015216608482015260a401602060405180830381865af4158015610b21573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b4591906150d4565b600180546001600160a01b0319166001600160a01b03929092169190911790555050565b5f73e1df067ed5c02ea743df0cd072e715c538c8f43b63b55f6002610b8c61146f565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015610bd0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bf491906150ef565b60066040518363ffffffff1660e01b8152600401610c13929190615162565b602060405180830381865af4158015610c2e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c5291906150ef565b905090565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0380546060915f80516020615d7f83398151915291610c959061517a565b80601f0160208091040260200160405190810160405280929190818152602001828054610cc19061517a565b8015610d0c5780601f10610ce357610100808354040283529160200191610d0c565b820191905f5260205f20905b815481529060010190602001808311610cef57829003601f168201915b505050505091505090565b606073a9ce9db84b7eeabfb5c62d24863bb1c7f8d29cb7639eb4c8e8610d3c84611ea1565b84600c600d600e6040518663ffffffff1660e01b8152600401610d639594939291906151ed565b5f60405180830381865af4158015610d7d573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610da4919081019061522a565b92915050565b5f610da4825f613679565b5f33610dc28185856136b7565b5060019392505050565b5f610dd88260016136c4565b6005549091506001600160a01b03163303610df35780610da4565b600754610da49061271090610e1890600160401b90046001600160401b0316826152ea565b8391905f6136f9565b5f80610e34670de0b6b3a7640000610daa565b905073a7e4636371e5606606aed9e314e67ea86cb9b63463aabbf18d6008610e5a610b69565b84600254610e6661146f565b60076040518763ffffffff1660e01b8152600401610e89969594939291906152fd565b602060405180830381865af4158015610ea4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ec891906150ef565b91505090565b604080516080810182525f808252602082018190529181019190915260608082015260408051608081018252600780546001600160401b038082168452600160401b82048116602080860191909152600160801b909204168385015260088054855181840281018401909652808652939492936060860193925f9084015b82821015610fa0575f848152602090819020604080516060810182526003860290920180548352600180820154848601526002909101546001600160401b0316918301919091529083529092019101610f4c565b5050505081525050905090565b5f73e1df067ed5c02ea743df0cd072e715c538c8f43b634712c426610ff07f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b600a5460405160e084901b6001600160e01b031916815260048101929092526001600160a01b03166024820152604401610c13565b5f33611032858285613746565b61103d8585856137a3565b60019150505b9392505050565b611052613800565b61105a613647565b60608082156110755761106f83850185615432565b90925090505b5f61107e610fad565b83516006549192506060915f805b828110156112ae575f5b848110156110f757818982815181106110b1576110b16154e4565b6020026020010151036110df578781815181106110d0576110d06154e4565b602002602001015195506110f7565b60408051602081019091525f81529550600101611096565b505f6006828154811061110c5761110c6154e4565b5f918252602090912060039091020154604051630aa0465b60e21b81526001600160a01b0390911690632a81196c90611149908990600401614842565b5f604051808303815f875af1158015611164573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261118b919081019061522a565b9050805192505f5b838110156112a4575f8282815181106111ae576111ae6154e4565b60200260200101516020015190505f8383815181106111cf576111cf6154e4565b60200260200101515f01519050815f1461129a576001600160a01b0381165f908152600d6020526040812054900361124c57600c80546001810182555f919091527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319166001600160a01b0383161790555b891561129a5761126d826ec097ce7bc90715b34b9f10000000008c5f6136f9565b6001600160a01b0382165f908152600d6020526040812080549091906112949084906154f8565b90915550505b5050600101611193565b505060010161108c565b506040517f429eabcd6b69e5ca232c6a9d2c5af1805e375e951df8e9bfe831b445e5f69b2b905f90a1505050505050506112e661384a565b5050565b5f610da4823333612913565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561133a5750825b90505f826001600160401b031660011480156113555750303b155b905081158015611363575080155b156113815760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156113ab57845460ff60401b1916600160401b1785555b6113b3613800565b6113c48e8e8e8e8e8e8e8e8e613870565b6113cc61384a565b831561141257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050505050505050565b61142b33613a26565b565b5f73aaa2ce316e920377701f63c4ffd0c0d6ebed92216369e4822761145061146f565b60066040518363ffffffff1660e01b8152600401610c1392919061550b565b5f807f0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e005b546001600160a01b031692915050565b5f5415806114b657506114b4610fad565b155b156114c057426004555b6114c86119bc565b61157f575f6114d5610e21565b6114dd61253c565b6114e791906154f8565b90505f6114fb670de0b6b3a7640000610daa565b90505f611506610b69565b90506002548211156115185760028290555b5f8311801561152657505f81115b1561157b575f611534610fad565b90505f8115611559576115548261154b87866152ea565b8791905f6136f9565b61155b565b845b600554909150611574906001600160a01b031682613a73565b5050426004555b5050505b611587613647565b61158f613aa7565b42600455565b5f61159e6119bc565b806115b257506003546115af610b69565b10155b6115d0576115be610b69565b6003546115cb91906152ea565b610da4565b5f92915050565b6115df613800565b5f5415806115f257506115f0610fad565b155b156115fc57426004555b6116046119bc565b6116b2575f611611610e21565b61161961253c565b61162391906154f8565b90505f611637670de0b6b3a7640000610daa565b90505f611642610b69565b90506002548211156116545760028290555b5f8311801561166257505f81115b156116ae575f611670610fad565b90505f811561168c576116878261154b87866152ea565b61168e565b845b6005549091506116a7906001600160a01b031682613a73565b5050426004555b5050505b6116ba613647565b61142b61384a565b6005545f906001600160a01b031633036116e057610da4825f613679565b6007545f90611705908490600160401b90046001600160401b031661271060016136f9565b905061104361171482856152ea565b5f613679565b611722613647565b600a546040516344401bd560e01b81526001600160a01b039182166004820152908216602482015273aaa2ce316e920377701f63c4ffd0c0d6ebed9221906344401bd590604401602060405180830381865af4158015611784573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117a891906150d4565b600a80546001600160a01b0319166001600160a01b039290921691909117905550565b6117d3613647565b600554600160a01b900460ff16156117fe576040516303eb29d960e01b815260040160405180910390fd5b73d35a6627523f0b49e28b971de4629244a0491dae6338abfe2b600661182261146f565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015611866573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061188a91906150ef565b6040516001600160e01b031960e085901b168152600481019290925260248201526044015b5f6040518083038186803b1580156118c5575f80fd5b505af41580156118d7573d5f803e3d5ffd5b50505050565b6118e5613647565b6001600160a01b03811661190c5760405163dd2fe90160e01b815260040160405180910390fd5b5f61191561146f565b600b549091506001600160a01b031680156119395761193782825f6001613b00565b505b61194782845f196001613b00565b50826001600160a01b0316816001600160a01b03167f8a8682c8e0b406a8cfcc7c7c4ac90372d088ddb8dfaf1baea5435b69e832a43a6001604051611990911515815260200190565b60405180910390a35050600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f80516020615d9f8339815191525460ff1690565b6119d9613647565b6040516356380c2b60e01b81526006600482015273d35a6627523f0b49e28b971de4629244a0491dae906356380c2b906024016118af565b5f541580611a245750611a22610fad565b155b15611a2e57426004555b611a366119bc565b611ae4575f611a43610e21565b611a4b61253c565b611a5591906154f8565b90505f611a69670de0b6b3a7640000610daa565b90505f611a74610b69565b9050600254821115611a865760028290555b5f83118015611a9457505f81115b15611ae0575f611aa2610fad565b90505f8115611abe57611ab98261154b87866152ea565b611ac0565b845b600554909150611ad9906001600160a01b031682613a73565b5050426004555b5050505b611aec613647565b806007611af98282615621565b50504260045550565b611b0a613647565b60405163dd40c63b60e01b8152600660048201526024810182905273d35a6627523f0b49e28b971de4629244a0491dae9063dd40c63b906044015b5f6040518083038186803b158015611b5b575f80fd5b505af4158015611b6d573d5f803e3d5ffd5b5050505050565b611b7c613647565b60055460408051600160a01b90920460ff16158015835260208301527f40f0175b5fde6802a96ba1ba9eeaaa9564b53a1c021b6fcde51422b574abcdbd910160405180910390a16005805460ff60a01b198116600160a01b9182900460ff1615909102179055565b5f611bed613800565b611bf5613c0f565b5f541580611c085750611c06610fad565b155b15611c1257426004555b611c1a6119bc565b611cc8575f611c27610e21565b611c2f61253c565b611c3991906154f8565b90505f611c4d670de0b6b3a7640000610daa565b90505f611c58610b69565b9050600254821115611c6a5760028290555b5f83118015611c7857505f81115b15611cc4575f611c86610fad565b90505f8115611ca257611c9d8261154b87866152ea565b611ca4565b845b600554909150611cbd906001600160a01b031682613a73565b5050426004555b5050505b611cd182613c35565b60035483611cdd610b69565b611ce791906154f8565b1115611d0657604051631264675f60e01b815260040160405180910390fd5b6005546001600160a01b03163303611d2957611d22835f6136c4565b9050611d89565b6007545f90611d5290611d4b9086906001600160401b031661271060016136f9565b60016136c4565b905080611d5f855f6136c4565b611d6991906152ea565b91508015611d8757600554611d87906001600160a01b031682613a73565b505b6305f5e1008111611dad57604051631f2a200560e01b815260040160405180910390fd5b611db78282613a73565b611dd6333085611dc561146f565b6001600160a01b0316929190613c6a565b600554600160a01b900460ff16611e535760405163ed9add7360e01b815273d35a6627523f0b49e28b971de4629244a0491dae9063ed9add7390611e26906006908790309060019060040161571d565b5f6040518083038186803b158015611e3c575f80fd5b505af4158015611e4e573d5f803e3d5ffd5b505050505b60408051848152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610da461384a565b6001600160a01b03165f9081525f80516020615d7f833981519152602052604090205490565b611ecf613647565b61142b5f613cc4565b604051630274467d60e11b8152600c6004820152600f60248201526001600160a01b038216604482015260609073a9ce9db84b7eeabfb5c62d24863bb1c7f8d29cb7906304e88cfa90606401610d63565b611f31613647565b60055460408051600160a81b90920460ff161515825282151560208301527ffdc806f366660aa3e77ed7f1da541577007de0e4b7e0c5d351ae99d2d01cc8f3910160405180910390a160058054911515600160a81b0260ff60a81b19909216919091179055565b611fa0613647565b73d35a6627523f0b49e28b971de4629244a0491dae6394b3d1486006600560149054906101000a900460ff16611fd461146f565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015612018573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061203c91906150ef565b6040516001600160e01b031960e086901b168152600481019390935290151560248301526044820152606481018590526084810184905260a4015b5f6040518083038186803b15801561208d575f80fd5b505af415801561209f573d5f803e3d5ffd5b505050505050565b5f5415806120ba57506120b8610fad565b155b156120c457426004555b6120cc6119bc565b61217a575f6120d9610e21565b6120e161253c565b6120eb91906154f8565b90505f6120ff670de0b6b3a7640000610daa565b90505f61210a610b69565b905060025482111561211c5760028290555b5f8311801561212a57505f81115b15612176575f612138610fad565b90505f81156121545761214f8261154b87866152ea565b612156565b845b60055490915061216f906001600160a01b031682613a73565b5050426004555b5050505b612182613647565b61142b613d34565b612192613647565b6010805482151560ff19821681179092556040805160ff909216801515835260208301939093527f522d3c2ddc88f29bac386c82e605c5bf322d91aa85997ef97a688539d9d60333910160405180910390a15050565b5f807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300611493565b5f612219613800565b612221613c0f565b5f5415806122345750612232610fad565b155b1561223e57426004555b6122466119bc565b6122f4575f612253610e21565b61225b61253c565b61226591906154f8565b90505f612279670de0b6b3a7640000610daa565b90505f612284610b69565b90506002548211156122965760028290555b5f831180156122a457505f81115b156122f0575f6122b2610fad565b90505f81156122ce576122c98261154b87866152ea565b6122d0565b845b6005549091506122e9906001600160a01b031682613a73565b5050426004555b5050505b6122fd82613c35565b6305f5e100831161232157604051631f2a200560e01b815260040160405180910390fd5b6007546005546001600160401b03909116905f906001600160a01b0316330361234a575f61236f565b8461236561271061235b85826152ea565b88919060016136f9565b61236f91906152ea565b905061238561237e82876154f8565b6001613679565b925060035483612393610b69565b61239d91906154f8565b11156123bc57604051631264675f60e01b815260040160405180910390fd5b6123c584611595565b8311156123e557604051631264675f60e01b815260040160405180910390fd5b801561240157600554612401906001600160a01b031682613a73565b61240b8486613a73565b612419333085611dc561146f565b600554600160a01b900460ff166124965760405163ed9add7360e01b815273d35a6627523f0b49e28b971de4629244a0491dae9063ed9add7390612469906006908790309060019060040161571d565b5f6040518083038186803b15801561247f575f80fd5b505af4158015612491573d5f803e3d5ffd5b505050505b60408051848152602081018790526001600160a01b0386169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35050610da461384a565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0480546060915f80516020615d7f83398151915291610c959061517a565b5f610da48233612210565b5f33610dc28185856137a3565b5f612545613c0f565b60075473a7e4636371e5606606aed9e314e67ea86cb9b63490639d02c9f490600160801b90046001600160401b031661257c610b69565b600480546040516001600160e01b031960e087901b1681526001600160401b039094169184019190915260248301919091526044820152606401610c13565b6125c3613647565b73d35a6627523f0b49e28b971de4629244a0491dae637d69c04260066125e761146f565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa15801561262b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061264f91906150ef565b60055460405160e085901b6001600160e01b03191681526004810193909352602483019190915260448201859052600160a01b900460ff1615156064820152608401611b45565b6005545f9082906001600160a01b031633146126d7576007546126d490612710906126ca906001600160401b0316826152ea565b85919060016136f9565b90505b611043816001613679565b6126ea613800565b6126f2613647565b5f5415806127055750612703610fad565b155b1561270f57426004555b6127176119bc565b6127c5575f612724610e21565b61272c61253c565b61273691906154f8565b90505f61274a670de0b6b3a7640000610daa565b90505f612755610b69565b90506002548211156127675760028290555b5f8311801561277557505f81115b156127c1575f612783610fad565b90505f811561279f5761279a8261154b87866152ea565b6127a1565b845b6005549091506127ba906001600160a01b031682613a73565b5050426004555b5050505b5f8073d35a6627523f0b49e28b971de4629244a0491dae6370b9cd68600685878960015f9054906101000a90046001600160a01b031661280361146f565b6040518763ffffffff1660e01b815260040161282496959493929190615756565b606060405180830381865af415801561283f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061286391906157bd565b600180546001600160a01b0319166001600160a01b0394851617905590935091508116156128c8576040516001600160a01b03821681527f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea49060200160405180910390a15b6040516001600160a01b03831681527f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f19060200160405180910390a1505061290e61384a565b505050565b5f61291c613800565b612924613c0f565b5f5415806129375750612935610fad565b155b1561294157426004555b6129496119bc565b6129f7575f612956610e21565b61295e61253c565b61296891906154f8565b90505f61297c670de0b6b3a7640000610daa565b90505f612987610b69565b90506002548211156129995760028290555b5f831180156129a757505f81115b156129f3575f6129b5610fad565b90505f81156129d1576129cc8261154b87866152ea565b6129d3565b845b6005549091506129ec906001600160a01b031682613a73565b5050426004555b5050505b6001600160a01b038316612a1e57604051634e46966960e11b815260040160405180910390fd5b612a2782613187565b841115612a4757604051631264675f60e01b815260040160405180910390fd5b612a528460016136c4565b90506305f5e1008111612a7857604051631f2a200560e01b815260040160405180910390fd5b600754600554600160401b9091046001600160401b0316905f906001600160a01b03163303612aa7575f612acc565b82612ac2612710612ab885826152ea565b86919060016136f9565b612acc91906152ea565b9050612ad881846154f8565b9250612ae7838686848a613d7c565b505061104361384a565b60606006805480602002602001604051908101604052809291908181526020015f905b82821015612b71575f8481526020908190206040805180820182526003860290920180546001600160a01b031683528151808301909252600180820154835260029091015482850152828401919091529083529092019101612b14565b50505050905090565b5f610da48233611be4565b5f612b8e613800565b612b96613c0f565b5f541580612ba95750612ba7610fad565b155b15612bb357426004555b612bbb6119bc565b612c69575f612bc8610e21565b612bd061253c565b612bda91906154f8565b90505f612bee670de0b6b3a7640000610daa565b90505f612bf9610b69565b9050600254821115612c0b5760028290555b5f83118015612c1957505f81115b15612c65575f612c27610fad565b90505f8115612c4357612c3e8261154b87866152ea565b612c45565b845b600554909150612c5e906001600160a01b031682613a73565b5050426004555b5050505b73e1df067ed5c02ea743df0cd072e715c538c8f43b6383ff48f58486612c8e86613191565b6040516001600160e01b031960e086901b1681526001600160a01b039093166004840152602483019190915260448201526064015f6040518083038186803b158015612cd8575f80fd5b505af4158015612cea573d5f803e3d5ffd5b505050506305f5e10063ffffffff168411612d1857604051631f2a200560e01b815260040160405180910390fd5b6005545f906001600160a01b03163303612d32575f612d55565b600754612d55908690600160401b90046001600160401b031661271060016136f9565b9050612d6461171482876152ea565b9150612d738585858486613d7c565b5061104361384a565b612d84613647565b60038190556040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f2906020015b60405180910390a150565b6001546001600160a01b03163314612ded576040516311dc66cd60e11b8152336004820152602401610a92565b5f7321ea5cb6b67d5de79ca94682957e238448fdeca7631bc92572612e1061146f565b60015460405160e084901b6001600160e01b03191681526001600160a01b039283166004820152602481018790529116604482015260066064820152608401602060405180830381865af4158015612e6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e8e91906150ef565b600154604051639db88c9360e01b815260048101859052602481018390526001600160a01b03909116604482015290915073aaa2ce316e920377701f63c4ffd0c0d6ebed922190639db88c9390606401612077565b612eeb613800565b612ef3613647565b5f541580612f065750612f04610fad565b155b15612f1057426004555b612f186119bc565b612fc6575f612f25610e21565b612f2d61253c565b612f3791906154f8565b90505f612f4b670de0b6b3a7640000610daa565b90505f612f56610b69565b9050600254821115612f685760028290555b5f83118015612f7657505f81115b15612fc2575f612f84610fad565b90505f8115612fa057612f9b8261154b87866152ea565b612fa2565b845b600554909150612fbb906001600160a01b031682613a73565b5050426004555b5050505b600654808210612fec5760405163042a2e7160e11b815260048101839052602401610a92565b60015473d35a6627523f0b49e28b971de4629244a0491dae9063999553049060069085906001600160a01b031661302161146f565b6040516001600160e01b031960e087901b168152600481019490945260248401929092526001600160a01b039081166044840152166064820152608401602060405180830381865af4158015613079573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061309d91906150d4565b600180546001600160a01b0319166001600160a01b0392909216919091179055506130c661384a565b50565b6060600c80548060200260200160405190810160405280929190818152602001828054801561311f57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311613101575b5050505050905090565b5f6131326119bc565b8061313d5750600354155b1561314957505f919050565b5f196003540361315b57505f19919050565b5f6131686003545f6136c4565b9050613172610fad565b61104390826152ea565b5f610da4825f6136c4565b5f610da482613eea565b5f61319a6119bc565b806131ae5750600554600160a81b900460ff165b156131ba57505f919050565b5f6131c483611ea1565b600a549091506001600160a01b0316156131de5792915050565b5f6131e761142d565b90505f6131f4825f6136c4565b9050828110156132045780613206565b825b95945050505050565b5f610da4823333612b85565b6001600160a01b039182165f9081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b61326c613647565b6001600160a01b03811661329357604051630ed1b8b360e31b815260040160405180910390fd5b6005546040516001600160a01b038084169216907faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d3905f90a3600580546001600160a01b0319166001600160a01b0392909216919091179055565b5f806132fa835f6136c4565b6005549091505f906001600160a01b03163303613317575f613337565b60075461333790611d4b9086906001600160401b031661271060016136f9565b905061334381836152ea565b949350505050565b613353613647565b61335b613800565b600a546001600160a01b031661338457604051637c0c423f60e11b815260040160405180910390fd5b5f61339661339061142d565b5f6136c4565b90505f60405180606001604052806133ac610b69565b81526020016133b9610fad565b81526009602090910152600a5490915073aaa2ce316e920377701f63c4ffd0c0d6ebed92219063811a8b2a906001600160a01b031685856133f861146f565b600b546040516001600160e01b031960e088901b16815261343095949392916006916001600160a01b03909116908a906004016157fc565b5f6040518083038186803b158015613446575f80fd5b505af4158015613458573d5f803e3d5ffd5b5050505050506130c661384a565b61346e613647565b6001600160a01b03811661349757604051631e4fbdf760e01b81525f6004820152602401610a92565b6130c681613cc4565b6134a8613800565b6134b0613647565b5f5415806134c357506134c1610fad565b155b156134cd57426004555b6134d56119bc565b613583575f6134e2610e21565b6134ea61253c565b6134f491906154f8565b90505f613508670de0b6b3a7640000610daa565b90505f613513610b69565b90506002548211156135255760028290555b5f8311801561353357505f81115b1561357f575f613541610fad565b90505f811561355d576135588261154b87866152ea565b61355f565b845b600554909150613578906001600160a01b031682613a73565b5050426004555b5050505b73d35a6627523f0b49e28b971de4629244a0491dae63bd0ba4ec60068585856135aa61146f565b6040518663ffffffff1660e01b81526004016135ca959493929190615865565b5f6040518083038186803b1580156135e0575f80fd5b505af41580156135f2573d5f803e3d5ffd5b5050505061290e61384a565b613606613647565b60095460408051918252602082018390527f47ecb16e79d3795bad1b15b763dad4136c346b10a729a5e22a60ae3416b571ba910160405180910390a1600955565b336136506121e8565b6001600160a01b03161461142b5760405163118cdaa760e01b8152336004820152602401610a92565b5f611043613685610b69565b6136909060016154f8565b61369c6009600a6159aa565b6136a4610fad565b6136ae91906154f8565b859190856136f9565b61290e8383836001613fd7565b5f6110436136d46009600a6159aa565b6136dc610fad565b6136e691906154f8565b6136ee610b69565b6136ae9060016154f8565b5f806137068686866140ba565b905061371183614179565b801561372c57505f8480613727576137276159b8565b868809115b156132065761373c6001826154f8565b9695505050505050565b5f613751848461321b565b90505f1981146118d7578181101561379557604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401610a92565b6118d784848484035f613fd7565b6001600160a01b0383166137cc57604051634b637e8f60e11b81525f6004820152602401610a92565b6001600160a01b0382166137f55760405163ec442f0560e01b81525f6004820152602401610a92565b61290e8383836141a5565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0080546001190161384457604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b6138786141e0565b613881896141f0565b61388b8888614201565b61389482614213565b61389c614224565b6001600160a01b0389166138c357604051630ccd248560e21b815260040160405180910390fd5b60015460405163c03e99d960e01b81527321ea5cb6b67d5de79ca94682957e238448fdeca79163c03e99d991613916918d916009918c916001600160a01b03909116906006908c90600790600401615a6f565b6040805180830381865af4158015613930573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139549190615b25565b600180546001600160a81b031916600160a01b60ff93909316929092026001600160a01b031916919091176001600160a01b0392831617905585166139ac57604051630ed1b8b360e31b815260040160405180910390fd5b60058054633b9aca0060025560038590556010805484151560ff199091161790556001600160b01b0319166001600160a01b038088169190911790915542600455604051908a169030907f3cd5ec01b1ae7cfec6ca1863e2cd6aa25d6d1702825803ff2b7cc95010fffdc2905f90a3505050505050505050565b73a9ce9db84b7eeabfb5c62d24863bb1c7f8d29cb7631cdd9163613a4983611ea1565b83600c600d600e600f6040518763ffffffff1660e01b8152600401611b4596959493929190615b57565b6001600160a01b038216613a9c5760405163ec442f0560e01b81525f6004820152602401610a92565b6112e65f83836141a5565b613aaf614234565b5f80516020615d9f833981519152805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b039091168152602001612db5565b604080516001600160a01b038516602482015260448082018590528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790525f90613b538682614259565b915081613bc357604080516001600160a01b03871660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613bae908790614259565b91508115613bc357613bc08682614259565b91505b81158015613bce5750825b15613c0657604051632d28f16360e21b81526001600160a01b0380881660048301528616602482015260448101859052606401610a92565b50949350505050565b613c176119bc565b1561142b5760405163d93c066560e01b815260040160405180910390fd5b6001600160a01b038116613c5c57604051634e46966960e11b815260040160405180910390fd5b5f545f036130c657425f5550565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526118d790859061429e565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b613d3c613c0f565b5f80516020615d9f833981519152805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833613ae8565b600554600160a81b900460ff1615613da757604051636022a9e760e01b815260040160405180910390fd5b336001600160a01b03841614613dc257613dc2833387613746565b613dcc83866142ff565b8115613de857600554613de8906001600160a01b031683613a73565b5f613df161142d565b905073aaa2ce316e920377701f63c4ffd0c0d6ebed922163a49fe6f783613e18868a6152ea565b8885613e2261146f565b600a54600954600b546010546040516001600160e01b031960e08c901b168152613e6c9998979695946001600160a01b03908116949360069391169160ff90911690600401615b9e565b5f6040518083038186803b158015613e82575f80fd5b505af4158015613e94573d5f803e3d5ffd5b505060408051858152602081018a90526001600160a01b0380891694508916925033917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4505050505050565b5f613ef36119bc565b80613f075750600554600160a81b900460ff165b15613f1357505f919050565b5f613f2061171484611ea1565b90505f613f2b610e21565b613f3361253c565b613f3d91906154f8565b90505f613f5583613f4c610b69565b8491905f6136f9565b6007549091505f908290613f8a90613f7f90600160401b90046001600160401b03166127106152ea565b86906127105f6136f9565b613f9491906152ea565b600a549091506001600160a01b031615613fb15795945050505050565b5f613fba61142d565b905081811015613fca5780613fcc565b815b979650505050505050565b5f80516020615d7f8339815191526001600160a01b03851661400e5760405163e602df0560e01b81525f6004820152602401610a92565b6001600160a01b03841661403757604051634a1406b160e11b81525f6004820152602401610a92565b6001600160a01b038086165f90815260018301602090815260408083209388168352929052208390558115611b6d57836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925856040516140ab91815260200190565b60405180910390a35050505050565b5f838302815f1985870982811083820303915050805f036140ee578382816140e4576140e46159b8565b0492505050611043565b80841161410e5760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f600282600381111561418e5761418e615c08565b6141989190615c1c565b60ff166001149050919050565b6001600160a01b038316156141bd576141bd83613a26565b6001600160a01b038216156141d5576141d582613a26565b61290e838383614333565b6141e861446c565b61142b6144b5565b6141f861446c565b6130c6816144d5565b61420961446c565b6112e68282614558565b61421b61446c565b6130c6816145a8565b61422c61446c565b61142b6145b0565b61423c6119bc565b61142b57604051638dfc202b60e01b815260040160405180910390fd5b5f805f8060205f8651602088015f8a5af192503d91505f51905082801561373c5750811561428a578060011461373c565b50505050506001600160a01b03163b151590565b5f6142b26001600160a01b038416836145b8565b905080515f141580156142d65750808060200190518101906142d49190615c49565b155b1561290e57604051635274afe760e01b81526001600160a01b0384166004820152602401610a92565b6001600160a01b03821661432857604051634b637e8f60e11b81525f6004820152602401610a92565b6112e6825f836141a5565b5f80516020615d7f8339815191526001600160a01b03841661436d5781816002015f82825461436291906154f8565b909155506143dd9050565b6001600160a01b0384165f90815260208290526040902054828110156143bf5760405163391434e360e21b81526001600160a01b03861660048201526024810182905260448101849052606401610a92565b6001600160a01b0385165f9081526020839052604090209083900390555b6001600160a01b0383166143fb576002810180548390039055614419565b6001600160a01b0383165f9081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161445e91815260200190565b60405180910390a350505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661142b57604051631afcd79f60e31b815260040160405180910390fd5b6144bd61446c565b5f80516020615d9f833981519152805460ff19169055565b6144dd61446c565b7f0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e005f80614509846145c5565b915091508161451957601261451b565b805b83546001600160a81b031916600160a01b60ff92909216919091026001600160a01b031916176001600160a01b0394909416939093179091555050565b61456061446c565b5f80516020615d7f8339815191527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace036145998482615ca8565b50600481016118d78382615ca8565b61346e61446c565b61384a61446c565b606061104383835f61469b565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b17905290515f918291829182916001600160a01b0387169161460b91615d63565b5f60405180830381855afa9150503d805f8114614643576040519150601f19603f3d011682016040523d82523d5f602084013e614648565b606091505b509150915081801561465c57506020815110155b1561468f575f8180602001905181019061467691906150ef565b905060ff811161468d576001969095509350505050565b505b505f9485945092505050565b6060814710156146c05760405163cd78605960e01b8152306004820152602401610a92565b5f80856001600160a01b031684866040516146db9190615d63565b5f6040518083038185875af1925050503d805f8114614715576040519150601f19603f3d011682016040523d82523d5f602084013e61471a565b606091505b509150915061373c86838360608261473a5761473582614781565b611043565b815115801561475157506001600160a01b0384163b155b1561477a57604051639996b31560e01b81526001600160a01b0385166004820152602401610a92565b5080611043565b8051156147915780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80151581146130c6575f80fd5b80356147c2816147aa565b919050565b5f80604083850312156147d8575f80fd5b8235915060208301356147ea816147aa565b809150509250929050565b5f5b8381101561480f5781810151838201526020016147f7565b50505f910152565b5f815180845261482e8160208601602086016147f5565b601f01601f19169290920160200192915050565b602081525f6110436020830184614817565b6001600160a01b03811681146130c6575f80fd5b80356147c281614854565b5f60208284031215614883575f80fd5b813561104381614854565b602080825282518282018190525f919060409081850190868401855b828110156148d857815180516001600160a01b031685528601518685015292840192908501906001016148aa565b5091979650505050505050565b5f602082840312156148f5575f80fd5b5035919050565b5f806040838503121561490d575f80fd5b823561491881614854565b946020939093013593505050565b5f602080835260a083016001600160401b0380865116602086015280602087015116604086015260408601516060828216606088015260608801519250608080880152839150825180855260c0880192506020840194505f93505b808410156149c5576149b183865180518252602080820151908301526040908101516001600160401b0316910152565b938501936001939093019291810191614981565b5090979650505050505050565b5f80604083850312156149e3575f80fd5b82356149ee81614854565b915060208301356147ea81614854565b5f805f60608486031215614a10575f80fd5b8335614a1b81614854565b92506020840135614a2b81614854565b929592945050506040919091013590565b5f8060208385031215614a4d575f80fd5b82356001600160401b0380821115614a63575f80fd5b818501915085601f830112614a76575f80fd5b813581811115614a84575f80fd5b866020828501011115614a95575f80fd5b60209290920196919550909350505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715614add57614add614aa7565b60405290565b604051608081016001600160401b0381118282101715614add57614add614aa7565b604051606081016001600160401b0381118282101715614add57614add614aa7565b604051601f8201601f191681016001600160401b0381118282101715614b4f57614b4f614aa7565b604052919050565b5f6001600160401b03831115614b6f57614b6f614aa7565b614b82601f8401601f1916602001614b27565b9050828152838383011115614b95575f80fd5b828260208301375f602084830101529392505050565b5f82601f830112614bba575f80fd5b61104383833560208501614b57565b5f6001600160401b03821115614be157614be1614aa7565b5060051b60200190565b5f82601f830112614bfa575f80fd5b81356020614c0f614c0a83614bc9565b614b27565b82815260609283028501820192828201919087851115614c2d575f80fd5b8387015b858110156149c55780890382811215614c48575f80fd5b614c50614abb565b8235614c5b81614854565b81526040601f198301811315614c6f575f80fd5b614c77614abb565b848901358152908401358882015281880152855250928401928101614c31565b6001600160401b03811681146130c6575f80fd5b5f60808284031215614cbb575f80fd5b614cc3614ae3565b90508135614cd081614c97565b8152602082810135614ce181614c97565b82820152604083810135614cf481614c97565b60408401526060848101356001600160401b03811115614d12575f80fd5b8501601f81018713614d22575f80fd5b8035614d30614c0a82614bc9565b818152606090910282018501908581019089831115614d4d575f80fd5b928601925b82841015614d9c5784848b031215614d68575f80fd5b614d70614b05565b84358152878501358882015286850135614d8981614c97565b8188015282529284019290860190614d52565b60608801525094979650505050505050565b5f805f805f805f805f6101208a8c031215614dc7575f80fd5b614dd08a614868565b985060208a01356001600160401b0380821115614deb575f80fd5b614df78d838e01614bab565b995060408c0135915080821115614e0c575f80fd5b614e188d838e01614bab565b985060608c0135915080821115614e2d575f80fd5b614e398d838e01614beb565b9750614e4760808d01614868565b965060a08c0135915080821115614e5c575f80fd5b50614e698c828d01614cab565b94505060c08a01359250614e7f60e08b01614868565b9150614e8e6101008b016147b7565b90509295985092959850929598565b5f60208284031215614ead575f80fd5b81356001600160401b03811115614ec2575f80fd5b820160808185031215611043575f80fd5b5f8060408385031215614ee4575f80fd5b8235915060208301356147ea81614854565b5f60208284031215614f06575f80fd5b8135611043816147aa565b5f8060408385031215614f22575f80fd5b50508035926020909101359150565b5f805f83850360a0811215614f44575f80fd5b843593506020850135614f56816147aa565b92506060603f1982011215614f69575f80fd5b506040840190509250925092565b5f805f60608486031215614f89575f80fd5b833592506020840135614f9b81614854565b91506040840135614fab81614854565b809150509250925092565b602080825282518282018190525f9190848201906040850190845b8181101561501557835180516001600160a01b031684528501516150018685018280518252602090810151910152565b509284019260609290920191600101614fd1565b50909695505050505050565b602080825282518282018190525f9190848201906040850190845b818110156150155783516001600160a01b03168352928401929184019160010161503c565b5f805f60408486031215615073575f80fd5b83356001600160401b0380821115615089575f80fd5b818601915086601f83011261509c575f80fd5b8135818111156150aa575f80fd5b8760208260061b85010111156150be575f80fd5b60209283019550935050840135614fab816147aa565b5f602082840312156150e4575f80fd5b815161104381614854565b5f602082840312156150ff575f80fd5b5051919050565b5f815480845260208085019450835f5260205f205f5b838110156151575781546001600160a01b0316875260018281015484890152600283015460408901526060909701966003909201910161511c565b509495945050505050565b828152604060208201525f6133436040830184615106565b600181811c9082168061518e57607f821691505b6020821081036151ac57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f815480845260208085019450835f5260205f205f5b838110156151575781546001600160a01b0316875295820195600191820191016151c8565b8581526001600160a01b038516602082015260a0604082018190525f90615216908301866151b2565b606083019490945250608001529392505050565b5f602080838503121561523b575f80fd5b82516001600160401b03811115615250575f80fd5b8301601f81018513615260575f80fd5b805161526e614c0a82614bc9565b81815260069190911b8201830190838101908783111561528c575f80fd5b928401925b82841015613fcc57604084890312156152a8575f80fd5b6152b0614abb565b84516152bb81614854565b81528486015186820152825260409093019290840190615291565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610da457610da46152d6565b5f60c0820160c0835280895480835260e0850191508a5f526020925060205f205f5b8281101561535a57815484526001808301548686015260028301546001600160401b031660408601526060909401936003909201910161531f565b505050809250505086602083015285604083015284606083015261538960808301856001600160a01b03169052565b8260a0830152979650505050505050565b5f82601f8301126153a9575f80fd5b813560206153b9614c0a83614bc9565b82815260059290921b840181019181810190868411156153d7575f80fd5b8286015b848110156154275780356001600160401b038111156153f8575f80fd5b8701603f81018913615408575f80fd5b615419898683013560408401614b57565b8452509183019183016153db565b509695505050505050565b5f8060408385031215615443575f80fd5b82356001600160401b0380821115615459575f80fd5b818501915085601f83011261546c575f80fd5b8135602061547c614c0a83614bc9565b82815260059290921b8401810191818101908984111561549a575f80fd5b948201945b838610156154b85785358252948201949082019061549f565b965050860135925050808211156154cd575f80fd5b506154da8582860161539a565b9150509250929050565b634e487b7160e01b5f52603260045260245ffd5b80820180821115610da457610da46152d6565b6001600160a01b03831681526040602082018190525f9061334390830184615106565b8135815560208201356001820155604082013561554a81614c97565b60028201805467ffffffffffffffff19166001600160401b038316179055505050565b600160401b83111561558157615581614aa7565b8054838255808410156155ed576003816003026003810483146155a6576155a66152d6565b856003026003810487146155bc576155bc6152d6565b5f8581526020902091820191015b818110156155e9575f80825560018201819055600282015582016155ca565b5050505b505f8181526020812083915b8581101561209f5761560b838361552e565b60609290920191600391909101906001016155f9565b813561562c81614c97565b815467ffffffffffffffff19166001600160401b03821617825550602082013561565581614c97565b81546fffffffffffffffff0000000000000000604092831b166fffffffffffffffff0000000000000000198216811784559184013561569381614c97565b77ffffffffffffffffffffffffffffffff0000000000000000199190911690911760809190911b67ffffffffffffffff60801b16178155606082013536839003601e190181126156e1575f80fd5b820180356001600160401b038111156156f8575f80fd5b60208201915060608102360382131561570f575f80fd5b6118d781836001860161556d565b608081525f61572f6080830187615106565b6020830195909552506001600160a01b039290921660408301521515606090910152919050565b8681526101008101863561576981614854565b60018060a01b0380821660208501526157926040850160208b0180358252602090810135910152565b87151560808501528660a085015280861660c085015280851660e08501525050979650505050505050565b5f805f606084860312156157cf575f80fd5b83516157da81614854565b60208501519093506157eb81614854565b6040850151909250614fab81614854565b5f61012060018060a01b03808b168452896020850152886040850152808816606085015281608085015261583282850188615106565b951660a08401525050815160c0820152602082015160e082015260409091015160ff166101009091015295945050505050565b85815260806020820181905281018490525f8560a08301825b878110156158a65782358252602080840135908301526040928301929091019060010161587e565b50941515604084015250506001600160a01b03919091166060909101529392505050565b600181815b8085111561590457815f19048211156158ea576158ea6152d6565b808516156158f757918102915b93841c93908002906158cf565b509250929050565b5f8261591a57506001610da4565b8161592657505f610da4565b816001811461593c576002811461594657615962565b6001915050610da4565b60ff841115615957576159576152d6565b50506001821b610da4565b5060208310610133831016604e8410600b8410161715615985575081810a610da4565b61598f83836158ca565b805f19048211156159a2576159a26152d6565b029392505050565b5f61104360ff84168361590c565b634e487b7160e01b5f52601260045260245ffd5b5f608083016001600160401b0380845116855260208160208601511660208701528160408601511660408701526060915060608501516080606088015283815180865260a0890191506020830195505f92505b80831015615a6357615a4f82875180518252602080820151908301526040908101516001600160401b0316910152565b948301946001929092019190840190615a1f565b50979650505050505050565b5f60e0820160018060a01b03808b168452602060ff8b16602086015260e06040860152828a518085526101008701915060208c0194505f5b81811015615ae4578551805186168452840151615ad08585018280518252602090810151910152565b509483019460609290920191600101615aa7565b50506001600160a01b038a16606087015288608087015285810360a0870152615b0d81896159cc565b9450505050508260c083015298975050505050505050565b5f8060408385031215615b36575f80fd5b8251615b4181614854565b602084015190925060ff811681146147ea575f80fd5b8681526001600160a01b038616602082015260c0604082018190525f90615b80908301876151b2565b606083019590955250608081019290925260a0909101529392505050565b5f6101408c83528b602084015260018060a01b03808c1660408501528a6060850152808a16608085015280891660a08501528760c08501528160e0850152615be882850188615106565b951661010084015250509015156101209091015298975050505050505050565b634e487b7160e01b5f52602160045260245ffd5b5f60ff831680615c3a57634e487b7160e01b5f52601260045260245ffd5b8060ff84160691505092915050565b5f60208284031215615c59575f80fd5b8151611043816147aa565b601f82111561290e57805f5260205f20601f840160051c81016020851015615c895750805b601f840160051c820191505b81811015611b6d575f8155600101615c95565b81516001600160401b03811115615cc157615cc1614aa7565b615cd581615ccf845461517a565b84615c64565b602080601f831160018114615d08575f8415615cf15750858301515b5f19600386901b1c1916600185901b17855561209f565b5f85815260208120601f198616915b82811015615d3657888601518255948401946001909101908401615d17565b5085821015615d5357878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b5f8251615d748184602087016147f5565b919091019291505056fe52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a26469706673582212208da67acd433b98a67d8b5bdde83cef47a42041c3fce6a2cf484417cf8b2a128864736f6c63430008180033
Deployed Bytecode Sourcemap
2122:47593:32:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;40749:288;;;;;;:::i;:::-;;:::i;:::-;;25888:180;;;:::i;:::-;;;730:25:33;;;718:2;703:18;25888:180:32;;;;;;;;3011:144:4;;;:::i;:::-;;;;;;;:::i;24949:252:32:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;7381:148:5:-;;;;;;:::i;:::-;;:::i;5505:186:4:-;;;;;;:::i;:::-;;:::i;:::-;;;3663:14:33;;3656:22;3638:41;;3626:2;3611:18;5505:186:4;3498:187:33;28741:331:32;;;;;;:::i;:::-;;:::i;33771:420::-;;;:::i;3792:21::-;;;;;-1:-1:-1;;;3792:21:32;;;;;;5573:28;;;;;;;;;34448:91;;;:::i;:::-;;;;;;;:::i;26357:190::-;;;:::i;4489:30::-;;;;;;5415:74;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;3057:28;;;;;;6251:244:4;;;;;;:::i;:::-;;:::i;45195:1864:32:-;;;;;;:::i;:::-;;:::i;19280:127::-;;;;;;:::i;:::-;;:::i;10428:90::-;10502:9;;-1:-1:-1;;;10502:9:32;;;;10428:90;;;6624:4:33;6612:17;;;6594:36;;6582:2;6567:18;10428:90:32;6452:184:33;7447:615:32;;;;;;:::i;:::-;;:::i;48606:88::-;;;:::i;4692:39::-;;;;;-1:-1:-1;;;;;4692:39:32;;;;;;-1:-1:-1;;;;;13368:32:33;;;13350:51;;13338:2;13323:18;4692:39:32;13179:228:33;24401:171:32;;;:::i;6747:153:5:-;;;:::i;11454:113:32:-;;;:::i;30366:170::-;;;;;;:::i;:::-;;:::i;34775:137::-;;;:::i;3597:27::-;;;;;-1:-1:-1;;;;;3597:27:32;;;4894:29;;;;;-1:-1:-1;;;;;4894:29:32;;;29498:443;;;;;;:::i;:::-;;:::i;36631:192::-;;;;;;:::i;:::-;;:::i;42232:268::-;;;:::i;37079:701::-;;;;;;:::i;:::-;;:::i;2692:145:7:-;;;:::i;42825:119:32:-;;;:::i;35142:217::-;;;;;;:::i;:::-;;:::i;43346:149::-;;;;;;:::i;:::-;;:::i;38337:138::-;;;:::i;13134:1452::-;;;;;;:::i;:::-;;:::i;4401:171:4:-;;;;;;:::i;:::-;;:::i;3155:101:2:-;;;:::i;25306:213:32:-;;;;;;:::i;:::-;;:::i;10757:209::-;;;;;;:::i;:::-;;:::i;44322:250::-;;;;;;:::i;:::-;;:::i;11117:68::-;;;:::i;11703:234::-;;;;;;:::i;:::-;;:::i;2441:144:2:-;;;:::i;15704:1422:32:-;;;;;;:::i;:::-;;:::i;3268:148:4:-;;;:::i;2496:30:32:-;;;;;-1:-1:-1;;;;;2496:30:32;;;3416:28;;;;;;5247:70;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;14925:107;;;;;;:::i;:::-;;:::i;2377:31::-;;;;;;4767:178:4;;;;;;:::i;:::-;;:::i;33105:226:32:-;;;:::i;43762:234::-;;;;;;:::i;:::-;;:::i;27957:357::-;;;;;;:::i;:::-;;:::i;5111:46::-;;;;;;:::i;:::-;;;;;;;;;;;;;;39139:603;;;;;;:::i;:::-;;:::i;21915:868::-;;;;;;:::i;:::-;;:::i;38051:101::-;;;:::i;:::-;;;;;;;:::i;12350:113::-;;;;;;:::i;:::-;;:::i;18266:653::-;;;;;;:::i;:::-;;:::i;44806:145::-;;;;;;:::i;:::-;;:::i;49165:263::-;;;;;;:::i;:::-;;:::i;40139:294::-;;;;;;:::i;:::-;;:::i;24290:105::-;;;:::i;:::-;;;;;;;:::i;30961:325::-;;;;;;:::i;:::-;;:::i;7179:148:5:-;;;;;;:::i;:::-;;:::i;19413:126:32:-;;;;;;:::i;:::-;;:::i;2824:39::-;;2862:1;2824:39;;20551:543;;;;;;:::i;:::-;;:::i;11191:91::-;11247:4;11191:91;;17495:123;;;;;;:::i;:::-;;:::i;5003:195:4:-;;;;;;:::i;:::-;;:::i;35619:376:32:-;;;;;;:::i;:::-;;:::i;3957:29::-;;;;;-1:-1:-1;;;3957:29:32;;;;;;3244:27;;;;;;26965:584;;;;;;:::i;:::-;;:::i;48000:600::-;;;;;;:::i;:::-;;:::i;3405:215:2:-;;;;;;:::i;:::-;;:::i;41782:257:32:-;;;;;;:::i;:::-;;:::i;36163:204::-;;;;;;:::i;:::-;;:::i;40749:288::-;2334:13:2;:11;:13::i;:::-;40859:10:32::1;:17:::0;40849:27;::::1;40845:60;;40885:20;::::0;-1:-1:-1;;;40885:20:32;;::::1;::::0;::::1;730:25:33::0;;;703:18;;40885:20:32::1;;;;;;;;40845:60;40933:14;:38;40972:10;40984:7;:5;:7::i;:::-;41014:15;::::0;40933:97:::1;::::0;::::1;::::0;;;-1:-1:-1;;;;;;40933:97:32;;;::::1;::::0;::::1;18773:25:33::0;;;;-1:-1:-1;;;;;18872:15:33;;;18852:18;;;18845:43;18904:18;;;18897:34;;;18974:14;;18967:22;18947:18;;;18940:50;41014:15:32::1;19006:19:33::0;;;18999:44;18745:19;;40933:97:32::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;40915:15;:115:::0;;-1:-1:-1;;;;;;40915:115:32::1;-1:-1:-1::0;;;;;40915:115:32;;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;;40749:288:32:o;25888:180::-;25941:13;25974:18;:33;26015:7;:5;:7::i;:::-;26008:40;;-1:-1:-1;;;26008:40:32;;26042:4;26008:40;;;13350:51:33;-1:-1:-1;;;;;26008:25:32;;;;;;;13323:18:33;;26008:40:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;26050:10;25974:87;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;25966:95;;25888:180;:::o;3011:144:4:-;3141:7;3134:14;;3056:13;;-1:-1:-1;;;;;;;;;;;2359:20:4;3134:14;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3011:144;:::o;24949:252:32:-;25017:24;25060:13;:28;25102:22;25112:11;25102:9;:22::i;:::-;25126:11;25139:15;25156:11;25169:15;25060:134;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;25060:134:32;;;;;;;;;;;;:::i;:::-;25053:141;24949:252;-1:-1:-1;;24949:252:32:o;7381:148:5:-;7451:7;7477:45;7494:6;7502:19;7477:16;:45::i;5505:186:4:-;5578:4;966:10:6;5632:31:4;966:10:6;5648:7:4;5657:5;5632:8;:31::i;:::-;-1:-1:-1;5680:4:4;;5505:186;-1:-1:-1;;;5505:186:4:o;28741:331:32:-;28813:14;28848:45;28865:7;28874:18;28848:16;:45::i;:::-;28926:12;;28839:54;;-1:-1:-1;;;;;;28926:12:32;28912:10;:26;:153;;29059:6;28912:153;;;29004:4;:18;28953:91;;97:6:31;;28985:37:32;;-1:-1:-1;;;29004:18:32;;-1:-1:-1;;;;;29004:18:32;97:6:31;28985:37:32;:::i;:::-;28953:6;;:91;29024:19;28953:13;:91::i;33771:420::-;33825:7;33891:18;33912:21;33928:4;33912:15;:21::i;:::-;33891:42;-1:-1:-1;34052:10:32;:32;34098:19;34119:13;:11;:13::i;:::-;34134:10;34146:13;;34161:7;:5;:7::i;:::-;34170:4;34052:132;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;34045:139;;;33771:420;:::o;34448:91::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;34521:11:32;;;;;;;;34528:4;34521:11;;-1:-1:-1;;;;;34521:11:32;;;;;-1:-1:-1;;;34521:11:32;;;;;;;;;;;;-1:-1:-1;;;34521:11:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;34528:4;;34521:11;;;;;-1:-1:-1;;34521:11:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;34521:11:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;34448:91;:::o;26357:190::-;26436:13;26469:18;:33;26503:19;4322:14:4;;;4191:152;26503:19:32;26524:15;;26469:71;;;;;;-1:-1:-1;;;;;;26469:71:32;;;;;;25194:25:33;;;;-1:-1:-1;;;;;26524:15:32;25235:18:33;;;25228:60;25167:18;;26469:71:32;24987:307:33;6251:244:4;6338:4;966:10:6;6394:37:4;6410:4;966:10:6;6425:5:4;6394:15;:37::i;:::-;6441:26;6451:4;6457:2;6461:5;6441:9;:26::i;:::-;6484:4;6477:11;;;6251:244;;;;;;:::o;45195:1864:32:-;3251:21:8;:19;:21::i;:::-;2334:13:2::1;:11;:13::i;:::-;45289:24:32::2;::::0;45356:23;;45352:117:::2;;45413:45;::::0;;::::2;45424:11:::0;45413:45:::2;:::i;:::-;45395:63:::0;;-1:-1:-1;45395:63:32;-1:-1:-1;45352:117:32::2;45478:19;45500:13;:11;:13::i;:::-;45578:14:::0;;45626:10:::2;:17:::0;45478:35;;-1:-1:-1;45523:24:32::2;::::0;45557:18:::2;::::0;45681:1339:::2;45701:13;45697:1;:17;45681:1339;;;45877:9;45872:285;45896:10;45892:1;:14;45872:285;;;45945:1;45931:7;45939:1;45931:10;;;;;;;;:::i;:::-;;;;;;;:15:::0;45927:110:::2;;45984:4;45989:1;45984:7;;;;;;;;:::i;:::-;;;;;;;45970:21;;46013:5;;45927:110;46054:16;::::0;;::::2;::::0;::::2;::::0;;;-1:-1:-1;46054:16:32;;;-1:-1:-1;46121:3:32::2;;45872:285;;;;46170:40;46213:10;46224:1;46213:13;;;;;;;;:::i;:::-;;::::0;;;::::2;::::0;;;::::2;::::0;;::::2;;:22:::0;:50:::2;::::0;-1:-1:-1;;;46213:50:32;;-1:-1:-1;;;;;46213:22:32;;::::2;::::0;:37:::2;::::0;:50:::2;::::0;46251:11;;46213:50:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;::::0;;::::2;-1:-1:-1::0;;46213:50:32::2;::::0;::::2;;::::0;::::2;::::0;;;::::2;::::0;::::2;:::i;:::-;46170:93;;46290:15;:22;46277:35;;46331:9;46326:625;46346:10;46342:1;:14;46326:625;;;46377:14;46394:15;46410:1;46394:18;;;;;;;;:::i;:::-;;;;;;;:31;;;46377:48;;46443:19;46465:15;46481:1;46465:18;;;;;;;;:::i;:::-;;;;;;;:32;;;46443:54;;46519:6;46529:1;46519:11;46515:351;;-1:-1:-1::0;;;;;46558:24:32;::::2;;::::0;;;:11:::2;:24;::::0;;;;;:29;;46554:117:::2;;46615:15;:33:::0;;::::2;::::0;::::2;::::0;;-1:-1:-1;46615:33:32;;;;;::::2;::::0;;-1:-1:-1;;;;;;46615:33:32::2;-1:-1:-1::0;;;;;46615:33:32;::::2;;::::0;;46554:117:::2;46696:15:::0;;46692:156:::2;;46767:58;:6:::0;164:4:31::2;46792:11:32::0;46805:19:::2;46767:13;:58::i;:::-;-1:-1:-1::0;;;;;46739:24:32;::::2;;::::0;;;:11:::2;:24;::::0;;;;:86;;:24;;;:86:::2;::::0;;;::::2;:::i;:::-;::::0;;;-1:-1:-1;;46692:156:32::2;-1:-1:-1::0;;46915:3:32::2;;46326:625;;;-1:-1:-1::0;;46992:3:32::2;;45681:1339;;;-1:-1:-1::0;47034:18:32::2;::::0;::::2;::::0;;;::::2;45279:1780;;;;;;;3293:20:8::0;:18;:20::i;:::-;45195:1864:32;;:::o;19280:127::-;19333:7;19359:41;19368:7;19377:10;19389;19359:8;:41::i;7447:615::-;8870:21:3;4302:15;;-1:-1:-1;;;4302:15:3;;;;4301:16;;-1:-1:-1;;;;;4348:14:3;4158:30;4726:16;;:34;;;;;4746:14;4726:34;4706:54;;4770:17;4790:11;-1:-1:-1;;;;;4790:16:3;4805:1;4790:16;:50;;;;-1:-1:-1;4818:4:3;4810:25;:30;4790:50;4770:70;;4856:12;4855:13;:30;;;;;4873:12;4872:13;4855:30;4851:91;;;4908:23;;-1:-1:-1;;;4908:23:3;;;;;;;;;;;4851:91;4951:18;;-1:-1:-1;;4951:18:3;4968:1;4951:18;;;4979:67;;;;5013:22;;-1:-1:-1;;;;5013:22:3;-1:-1:-1;;;5013:22:3;;;4979:67;3251:21:8::1;:19;:21::i;:::-;7811:244:32::2;7836:10;7860;7884:12;7910:11;7935:13;7962:5;7981:13;8008:6;8028:17;7811:11;:244::i;:::-;3293:20:8::1;:18;:20::i;:::-;5070:14:3::0;5066:101;;;5100:23;;-1:-1:-1;;;;5100:23:3;;;5142:14;;-1:-1:-1;28058:50:33;;5142:14:3;;28046:2:33;28031:18;5142:14:3;;;;;;;5066:101;4092:1081;;;;;7447:615:32;;;;;;;;;:::o;48606:88::-;48649:38;48676:10;48649:26;:38::i;:::-;48606:88::o;24401:171::-;24465:7;24491:21;:53;24545:7;:5;:7::i;:::-;24554:10;24491:74;;;;;;;;;;;;;;;;:::i;6747:153:5:-;6793:7;;4093:22;6839:20;6884:8;-1:-1:-1;;;;;6884:8:5;;6747:153;-1:-1:-1;;6747:153:5:o;11454:113:32:-;6609:12;;:17;;:39;;;6630:13;:11;:13::i;:::-;:18;6609:39;6605:101;;;6680:15;6664:13;:31;6605:101;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26;;;6930:58;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30;-1:-1:-1;7107:18:32;7148:11;;:94;;7173:69;7189:6;7197:23;7212:8;7197:12;:23;:::i;:::-;7173:8;;:69;7222:19;7173:15;:69::i;:::-;7148:94;;;7162:8;7148:94;7266:12;;7107:135;;-1:-1:-1;7260:31:32;;-1:-1:-1;;;;;7266:12:32;7107:135;7260:5;:31::i;:::-;-1:-1:-1;;7325:15:32;7309:13;:31;7003:352;6730:635;;;6715:650;2334:13:2::1;:11;:13::i;:::-;11509:10:32::2;:8;:10::i;:::-;11545:15;11529:13;:31:::0;11454:113::o;30366:170::-;30425:7;30452:8;:6;:8::i;:::-;:41;;;;30481:12;;30464:13;:11;:13::i;:::-;:29;;30452:41;30451:78;;30516:13;:11;:13::i;:::-;30501:12;;:28;;;;:::i;:::-;30451:78;;;30497:1;30444:85;30366:170;-1:-1:-1;;30366:170:32:o;34775:137::-;3251:21:8;:19;:21::i;:::-;6609:12:32::1;::::0;:17;;:39:::1;;;6630:13;:11;:13::i;:::-;:18:::0;6609:39:::1;6605:101;;;6680:15;6664:13;:31:::0;6605:101:::1;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26:::0;;;6930:58:::1;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30:::0;-1:-1:-1;7107:18:32::1;7148:11:::0;;:94:::1;;7173:69;7189:6:::0;7197:23:::1;7212:8:::0;7197:12;:23:::1;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;::::0;7107:135;;-1:-1:-1;7260:31:32::1;::::0;-1:-1:-1;;;;;7266:12:32::1;7107:135:::0;7260:5:::1;:31::i;:::-;-1:-1:-1::0;;7325:15:32::1;7309:13;:31:::0;7003:352:::1;6730:635;;;6715:650;2334:13:2::2;:11;:13::i;:::-;3293:20:8::0;:18;:20::i;29498:443:32:-;29605:12;;29568:7;;-1:-1:-1;;;;;29605:12:32;29591:10;:26;29587:161;;29691:46;29708:7;29717:19;29691:16;:46::i;29587:161::-;29801:4;:18;29758:17;;29778:81;;:7;;-1:-1:-1;;;29801:18:32;;-1:-1:-1;;;;;29801:18:32;97:6:31;29840:18:32;29778:14;:81::i;:::-;29758:101;-1:-1:-1;29876:58:32;29893:19;29758:101;29893:7;:19;:::i;:::-;29914;29876:16;:58::i;36631:192::-;2334:13:2;:11;:13::i;:::-;36781:15:32::1;::::0;36732:84:::1;::::0;-1:-1:-1;;;36732:84:32;;-1:-1:-1;;;;;36781:15:32;;::::1;36732:84;::::0;::::1;28772:34:33::0;28842:15;;;28822:18;;;28815:43;36732:21:32::1;::::0;:40:::1;::::0;28707:18:33;;36732:84:32::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;36714:15;:102:::0;;-1:-1:-1;;;;;;36714:102:32::1;-1:-1:-1::0;;;;;36714:102:32;;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;36631:192:32:o;42232:268::-;2334:13:2;:11;:13::i;:::-;42296:9:32::1;::::0;-1:-1:-1;;;42296:9:32;::::1;;;42292:35;;;42314:13;;-1:-1:-1::0;;;42314:13:32::1;;;;;;;;;;;42292:35;42396:14;:43;42440:10;42459:7;:5;:7::i;:::-;42452:40;::::0;-1:-1:-1;;;42452:40:32;;42486:4:::1;42452:40;::::0;::::1;13350:51:33::0;-1:-1:-1;;;;;42452:25:32;;;::::1;::::0;::::1;::::0;13323:18:33;;42452:40:32::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;42396:97;::::0;-1:-1:-1;;;;;;42396:97:32::1;::::0;;;;;;::::1;::::0;::::1;29377:25:33::0;;;;29418:18;;;29411:34;29350:18;;42396:97:32::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;42232:268::o:0;37079:701::-;2334:13:2;:11;:13::i;:::-;-1:-1:-1;;;;;37202:25:32;::::1;37198:57;;37236:19;;-1:-1:-1::0;;;37236:19:32::1;;;;;;;;;;;37198:57;37348:13;37364:7;:5;:7::i;:::-;37417:10;::::0;37348:23;;-1:-1:-1;;;;;;37417:10:32::1;37442:31:::0;;37438:103:::1;;37475:66;37507:5;37514:17;37533:1;37536:4;37475:31;:66::i;:::-;;37438:103;37551:76;37583:5;37590:11;-1:-1:-1::0;;37622:4:32::1;37551:31;:76::i;:::-;;37679:11;-1:-1:-1::0;;;;;37642:55:32::1;37660:17;-1:-1:-1::0;;;;;37642:55:32::1;;37692:4;37642:55;;;;3663:14:33::0;3656:22;3638:41;;3626:2;3611:18;;3498:187;37642:55:32::1;;;;;;;;-1:-1:-1::0;;37708:10:32::1;:37:::0;;-1:-1:-1;;;;;;37708:37:32::1;-1:-1:-1::0;;;;;37708:37:32;;;::::1;::::0;;;::::1;::::0;;37079:701::o;2692:145:7:-;-1:-1:-1;;;;;;;;;;;2821:9:7;;;;2692:145::o;42825:119:32:-;2334:13:2;:11;:13::i;:::-;42887:50:32::1;::::0;-1:-1:-1;;;42887:50:32;;42926:10:::1;42887:50;::::0;::::1;730:25:33::0;42887:14:32::1;::::0;:38:::1;::::0;703:18:33;;42887:50:32::1;584:177:33::0;35142:217:32;6609:12;;:17;;:39;;;6630:13;:11;:13::i;:::-;:18;6609:39;6605:101;;;6680:15;6664:13;:31;6605:101;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26;;;6930:58;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30;-1:-1:-1;7107:18:32;7148:11;;:94;;7173:69;7189:6;7197:23;7212:8;7197:12;:23;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;;7107:135;;-1:-1:-1;7260:31:32;;-1:-1:-1;;;;;7266:12:32;7107:135;7260:5;:31::i;:::-;-1:-1:-1;;7325:15:32;7309:13;:31;7003:352;6730:635;;;6715:650;2334:13:2::1;:11;:13::i;:::-;35238:8:32::0;35231:4:::2;:15;35238:8:::0;35231:4;:15:::2;:::i;:::-;-1:-1:-1::0;;35300:15:32::2;35284:13;:31:::0;-1:-1:-1;35142:217:32:o;43346:149::-;2334:13:2;:11;:13::i;:::-;43426:62:32::1;::::0;-1:-1:-1;;;43426:62:32;;43469:10:::1;43426:62;::::0;::::1;29377:25:33::0;29418:18;;;29411:34;;;43426:14:32::1;::::0;:42:::1;::::0;29350:18:33;;43426:62:32::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;43346:149:::0;:::o;38337:138::-;2334:13:2;:11;:13::i;:::-;38414:9:32::1;::::0;38398:38:::1;::::0;;-1:-1:-1;;;38414:9:32;;::::1;;;38425:10;33192:22:33::0;;33174:41;;33246:2;33231:18;;33224:50;38398:38:32::1;::::0;33147:18:33;38398:38:32::1;;;;;;;38459:9;::::0;;-1:-1:-1;;;;38446:22:32;::::1;-1:-1:-1::0;;;38459:9:32;;;::::1;;;38458:10;38446:22:::0;;::::1;;::::0;;38337:138::o;13134:1452::-;13296:14;3251:21:8;:19;:21::i;:::-;2316:19:7::1;:17;:19::i;:::-;6609:12:32::2;::::0;:17;;:39:::2;;;6630:13;:11;:13::i;:::-;:18:::0;6609:39:::2;6605:101;;;6680:15;6664:13;:31:::0;6605:101:::2;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26:::0;;;6930:58:::2;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30:::0;-1:-1:-1;7107:18:32::2;7148:11:::0;;:94:::2;;7173:69;7189:6:::0;7197:23:::2;7212:8:::0;7197:12;:23:::2;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;::::0;7107:135;;-1:-1:-1;7260:31:32::2;::::0;-1:-1:-1;;;;;7266:12:32::2;7107:135:::0;7260:5:::2;:31::i;:::-;-1:-1:-1::0;;7325:15:32::2;7309:13;:31:::0;7003:352:::2;6730:635;;;6715:650;13326:46:::3;13362:9;13326:35;:46::i;:::-;13413:12;;13403:7;13387:13;:11;:13::i;:::-;:23;;;;:::i;:::-;:38;13383:86;;;13448:10;;-1:-1:-1::0;;;13448:10:32::3;;;;;;;;;;;13383:86;13566:12;::::0;-1:-1:-1;;;;;13566:12:32::3;13552:10;:26:::0;13548:619:::3;;13603:46;13620:7;13629:19;13603:16;:46::i;:::-;13594:55;;13548:619;;;13800:4;:15:::0;13723:17:::3;::::0;13743:146:::3;::::0;13777:78:::3;::::0;:7;;-1:-1:-1;;;;;13800:15:32::3;97:6:31;13800:15:32::0;13777:14:::3;:78::i;:::-;13857:18;13743:16;:146::i;:::-;13723:166;;14035:9;13986:46;14003:7;14012:19;13986:16;:46::i;:::-;:58;;;;:::i;:::-;13977:67:::0;-1:-1:-1;14111:13:32;;14107:49:::3;;14132:12;::::0;14126:30:::3;::::0;-1:-1:-1;;;;;14132:12:32::3;14146:9:::0;14126:5:::3;:30::i;:::-;13666:501;13548:619;193:3:31;14181:14:32::0;::::3;14177:39;;14204:12;;-1:-1:-1::0;;;14204:12:32::3;;;;;;;;;;;14177:39;14227:24;14233:9;14244:6;14227:5;:24::i;:::-;14261:68;14294:10;14314:4;14321:7;14268;:5;:7::i;:::-;-1:-1:-1::0;;;;;14261:32:32::3;::::0;:68;;:32:::3;:68::i;:::-;14404:9;::::0;-1:-1:-1;;;14404:9:32;::::3;;;14399:119;;14429:78;::::0;-1:-1:-1;;;14429:78:32;;:14:::3;::::0;:36:::3;::::0;:78:::3;::::0;14466:10:::3;::::0;14478:7;;14495:4:::3;::::0;14502::::3;::::0;14429:78:::3;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;14399:119;14532:47;::::0;;29377:25:33;;;29433:2;29418:18;;29411:34;;;-1:-1:-1;;;;;14532:47:32;::::3;::::0;14540:10:::3;::::0;14532:47:::3;::::0;29350:18:33;14532:47:32::3;;;;;;;3293:20:8::0;:18;:20::i;4401:171:4:-;-1:-1:-1;;;;;4545:20:4;4466:7;4545:20;;;-1:-1:-1;;;;;;;;;;;4545:20:4;;;;;;;4401:171::o;3155:101:2:-;2334:13;:11;:13::i;:::-;3219:30:::1;3246:1;3219:18;:30::i;25306:213:32:-:0;25425:87;;-1:-1:-1;;;25425:87:32;;25462:15;25425:87;;;34407:25:33;25479:19:32;34448:18:33;;;34441:34;-1:-1:-1;;;;;34511:32:33;;34491:18;;;34484:60;25382:24:32;;25425:13;;:36;;34380:18:33;;25425:87:32;34125:425:33;10757:209:32;2334:13:2;:11;:13::i;:::-;10873:17:32::1;::::0;10849:62:::1;::::0;;-1:-1:-1;;;10873:17:32;;::::1;;;33199:14:33::0;33192:22;33174:41;;33258:14;;33251:22;33246:2;33231:18;;33224:50;10849:62:32::1;::::0;33147:18:33;10849:62:32::1;;;;;;;10921:17;:38:::0;;;::::1;;-1:-1:-1::0;;;10921:38:32::1;-1:-1:-1::0;;;;10921:38:32;;::::1;::::0;;;::::1;::::0;;10757:209::o;44322:250::-;2334:13:2;:11;:13::i;:::-;44420:14:32::1;:42;44476:10;44488:9;;;;;;;;;;;44506:7;:5;:7::i;:::-;44499:40;::::0;-1:-1:-1;;;44499:40:32;;44533:4:::1;44499:40;::::0;::::1;13350:51:33::0;-1:-1:-1;;;;;44499:25:32;;;::::1;::::0;::::1;::::0;13323:18:33;;44499:40:32::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;44420:145;::::0;-1:-1:-1;;;;;;44420:145:32::1;::::0;;;;;;::::1;::::0;::::1;34861:25:33::0;;;;34929:14;;34922:22;34902:18;;;34895:50;34961:18;;;34954:34;35004:18;;;34997:34;;;35047:19;;;35040:35;;;34833:19;;44420:145:32::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;44322:250:::0;;:::o;11117:68::-;6609:12;;:17;;:39;;;6630:13;:11;:13::i;:::-;:18;6609:39;6605:101;;;6680:15;6664:13;:31;6605:101;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26;;;6930:58;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30;-1:-1:-1;7107:18:32;7148:11;;:94;;7173:69;7189:6;7197:23;7212:8;7197:12;:23;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;;7107:135;;-1:-1:-1;7260:31:32;;-1:-1:-1;;;;;7266:12:32;7107:135;7260:5;:31::i;:::-;-1:-1:-1;;7325:15:32;7309:13;:31;7003:352;6730:635;;;6715:650;2334:13:2::1;:11;:13::i;:::-;11170:8:32::2;:6;:8::i;11703:234::-:0;2334:13:2;:11;:13::i;:::-;11801:16:32::1;::::0;;11827:36;::::1;;-1:-1:-1::0;;11827:36:32;::::1;::::0;::::1;::::0;;;11878:52:::1;::::0;;11801:16:::1;::::0;;::::1;33199:14:33::0;;33192:22;33174:41;;33246:2;33231:18;;33224:50;;;;11878:52:32::1;::::0;33147:18:33;11878:52:32::1;;;;;;;11775:162;11703:234:::0;:::o;2441:144:2:-;2487:7;;1313:22;2533:20;1192:159;15704:1422:32;15863:14;3251:21:8;:19;:21::i;:::-;2316:19:7::1;:17;:19::i;:::-;6609:12:32::2;::::0;:17;;:39:::2;;;6630:13;:11;:13::i;:::-;:18:::0;6609:39:::2;6605:101;;;6680:15;6664:13;:31:::0;6605:101:::2;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26:::0;;;6930:58:::2;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30:::0;-1:-1:-1;7107:18:32::2;7148:11:::0;;:94:::2;;7173:69;7189:6:::0;7197:23:::2;7212:8:::0;7197:12;:23:::2;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;::::0;7107:135;;-1:-1:-1;7260:31:32::2;::::0;-1:-1:-1;;;;;7266:12:32::2;7107:135:::0;7260:5:::2;:31::i;:::-;-1:-1:-1::0;;7325:15:32::2;7309:13;:31:::0;7003:352:::2;6730:635;;;6715:650;15893:46:::3;15929:9;15893:35;:46::i;:::-;193:3:31;15954:15:32::0;::::3;15950:40;;15978:12;;-1:-1:-1::0;;;15978:12:32::3;;;;;;;;;;;15950:40;16077:4;:15:::0;16137:12:::3;::::0;-1:-1:-1;;;;;16077:15:32;;::::3;::::0;16048:18:::3;::::0;-1:-1:-1;;;;;16137:12:32::3;16123:10;:26:::0;:150:::3;;16272:1;16123:150;;;16250:7:::0;16164:83:::3;97:6:31;16197:29:32;16216:10:::0;97:6:31;16197:29:32::3;:::i;:::-;16164:7:::0;;:83;16228:18:::3;16164:14;:83::i;:::-;:93;;;;:::i;:::-;16103:170:::0;-1:-1:-1;16378:57:32::3;16395:19;16103:170:::0;16395:7;:19:::3;:::i;:::-;16416:18;16378:16;:57::i;:::-;16369:66;;16475:12;;16466:6;16450:13;:11;:13::i;:::-;:22;;;;:::i;:::-;:37;16446:60;;;16496:10;;-1:-1:-1::0;;;16496:10:32::3;;;;;;;;;;;16446:60;16530:21;16541:9;16530:10;:21::i;:::-;16521:6;:30;16517:53;;;16560:10;;-1:-1:-1::0;;;16560:10:32::3;;;;;;;;;;;16517:53;16638:13:::0;;16634:49:::3;;16659:12;::::0;16653:30:::3;::::0;-1:-1:-1;;;;;16659:12:32::3;16673:9:::0;16653:5:::3;:30::i;:::-;16693:25;16699:9;16710:7;16693:5;:25::i;:::-;16789:67;16822:10;16842:4;16849:6;16796:7;:5;:7::i;16789:67::-;16945:9;::::0;-1:-1:-1;;;16945:9:32;::::3;;;16940:118;;16970:77;::::0;-1:-1:-1;;;16970:77:32;;:14:::3;::::0;:36:::3;::::0;:77:::3;::::0;17007:10:::3;::::0;17019:6;;17035:4:::3;::::0;17042::::3;::::0;16970:77:::3;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;16940:118;17072:47;::::0;;29377:25:33;;;29433:2;29418:18;;29411:34;;;-1:-1:-1;;;;;17072:47:32;::::3;::::0;17080:10:::3;::::0;17072:47:::3;::::0;29350:18:33;17072:47:32::3;;;;;;;15883:1243;;3293:20:8::0;:18;:20::i;3268:148:4:-;3400:9;3393:16;;3315:13;;-1:-1:-1;;;;;;;;;;;2359:20:4;3393:16;;;:::i;14925:107:32:-;14974:7;15000:25;15005:7;15014:10;15000:4;:25::i;4767:178:4:-;4836:4;966:10:6;4890:27:4;966:10:6;4907:2:4;4911:5;4890:9;:27::i;33105:226:32:-;33170:7;2316:19:7;:17;:19::i;:::-;33277:4:32::1;:16:::0;33247:10:::1;::::0;:29:::1;::::0;-1:-1:-1;;;33277:16:32;::::1;-1:-1:-1::0;;;;;33277:16:32::1;33295:13;:11;:13::i;:::-;33310;::::0;;33247:77:::1;::::0;-1:-1:-1;;;;;;33247:77:32::1;::::0;;;;;;-1:-1:-1;;;;;35313:31:33;;;33247:77:32;;::::1;35295:50:33::0;;;;35361:18;;;35354:34;;;;35404:18;;;35397:34;35268:18;;33247:77:32::1;35086:351:33::0;43762:234:32;2334:13:2;:11;:13::i;:::-;43844:14:32::1;:50;43908:10;43927:7;:5;:7::i;:::-;43920:40;::::0;-1:-1:-1;;;43920:40:32;;43954:4:::1;43920:40;::::0;::::1;13350:51:33::0;-1:-1:-1;;;;;43920:25:32;;;::::1;::::0;::::1;::::0;13323:18:33;;43920:40:32::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;43970:9;::::0;43844:145:::1;::::0;::::1;::::0;;;-1:-1:-1;;;;;;43844:145:32;;;::::1;::::0;::::1;35720:25:33::0;;;;35761:18;;;35754:34;;;;35804:18;;;35797:34;;;-1:-1:-1;;;43970:9:32;::::1;;;35874:14:33::0;35867:22;35847:18;;;35840:50;35692:19;;43844:145:32::1;35442:454:33::0;27957:357:32;28101:12;;28025:7;;28066;;-1:-1:-1;;;;;28101:12:32;28087:10;:26;28083:159;;28195:4;:15;28143:88;;97:6:31;;28176:34:32;;-1:-1:-1;;;;;28195:15:32;97:6:31;28176:34:32;:::i;:::-;28143:7;;:88;28212:18;28143:14;:88::i;:::-;28129:102;;28083:159;28258:49;28275:11;28288:18;28258:16;:49::i;39139:603::-;3251:21:8;:19;:21::i;:::-;2334:13:2::1;:11;:13::i;:::-;6609:12:32::2;::::0;:17;;:39:::2;;;6630:13;:11;:13::i;:::-;:18:::0;6609:39:::2;6605:101;;;6680:15;6664:13;:31:::0;6605:101:::2;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26:::0;;;6930:58:::2;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30:::0;-1:-1:-1;7107:18:32::2;7148:11:::0;;:94:::2;;7173:69;7189:6:::0;7197:23:::2;7212:8:::0;7197:12;:23:::2;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;::::0;7107:135;;-1:-1:-1;7260:31:32::2;::::0;-1:-1:-1;;;;;7266:12:32::2;7107:135:::0;7260:5:::2;:31::i;:::-;-1:-1:-1::0;;7325:15:32::2;7309:13;:31:::0;7003:352:::2;6730:635;;;6715:650;39310:21:::3;39341:25:::0;39426:14:::3;:35;39475:10;39487:12;39501:8;39511:6;39519:15;;;;;;;;;-1:-1:-1::0;;;;;39519:15:32::3;39543:7;:5;:7::i;:::-;39426:135;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;39377:15;39376:185:::0;;-1:-1:-1;;;;;;39376:185:32::3;-1:-1:-1::0;;;;;39376:185:32;;::::3;;::::0;;;;-1:-1:-1;39376:185:32;-1:-1:-1;39575:38:32;::::3;::::0;39571:115:::3;;39634:41;::::0;-1:-1:-1;;;;;13368:32:33;;13350:51;;39634:41:32::3;::::0;13338:2:33;13323:18;39634:41:32::3;;;;;;;39571:115;39700:35;::::0;-1:-1:-1;;;;;13368:32:33;;13350:51;;39700:35:32::3;::::0;13338:2:33;13323:18;39700:35:32::3;;;;;;;39300:442;;3293:20:8::0;:18;:20::i;:::-;39139:603:32;;;:::o;21915:868::-;22094:14;3251:21:8;:19;:21::i;:::-;2316:19:7::1;:17;:19::i;:::-;6609:12:32::2;::::0;:17;;:39:::2;;;6630:13;:11;:13::i;:::-;:18:::0;6609:39:::2;6605:101;;;6680:15;6664:13;:31:::0;6605:101:::2;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26:::0;;;6930:58:::2;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30:::0;-1:-1:-1;7107:18:32::2;7148:11:::0;;:94:::2;;7173:69;7189:6:::0;7197:23:::2;7212:8:::0;7197:12;:23:::2;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;::::0;7107:135;;-1:-1:-1;7260:31:32::2;::::0;-1:-1:-1;;;;;7266:12:32::2;7107:135:::0;7260:5:::2;:31::i;:::-;-1:-1:-1::0;;7325:15:32::2;7309:13;:31:::0;7003:352:::2;6730:635;;;6715:650;-1:-1:-1::0;;;;;22128:23:32;::::3;22124:54;;22160:18;;-1:-1:-1::0;;;22160:18:32::3;;;;;;;;;;;22124:54;22202:19;22214:6;22202:11;:19::i;:::-;22192:7;:29;22188:52;;;22230:10;;-1:-1:-1::0;;;22230:10:32::3;;;;;;;;;;;22188:52;22259:45;22276:7;22285:18;22259:16;:45::i;:::-;22250:54:::0;-1:-1:-1;193:3:31::3;22318:14:32::0;::::3;22314:39;;22341:12;;-1:-1:-1::0;;;22341:12:32::3;;;;;;;;;;;22314:39;22482:4;:18:::0;22545:12:::3;::::0;-1:-1:-1;;;22482:18:32;;::::3;-1:-1:-1::0;;;;;22482:18:32::3;::::0;22450:21:::3;::::0;-1:-1:-1;;;;;22545:12:32::3;22531:10;:26:::0;:151:::3;;22681:1;22531:151;;;22660:6:::0;22572:85:::3;97:6:31;22604:32:32;22623:13:::0;97:6:31;22604:32:32::3;:::i;:::-;22572:6:::0;;:85;22638:18:::3;22572:13;:85::i;:::-;:94;;;;:::i;:::-;22511:171:::0;-1:-1:-1;22692:19:32::3;22511:171:::0;22692:19;::::3;:::i;:::-;;;22722:54;22730:6;22738:9;22749:6;22757:9;22768:7;22722;:54::i;:::-;22114:669;;3293:20:8::0;:18;:20::i;38051:101:32:-;38099:17;38135:10;38128:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;38128:17:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;38051:101;:::o;12350:113::-;12402:7;12428:28;12436:7;12445:10;12428:7;:28::i;18266:653::-;18443:14;3251:21:8;:19;:21::i;:::-;2316:19:7::1;:17;:19::i;:::-;6609:12:32::2;::::0;:17;;:39:::2;;;6630:13;:11;:13::i;:::-;:18:::0;6609:39:::2;6605:101;;;6680:15;6664:13;:31:::0;6605:101:::2;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26:::0;;;6930:58:::2;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30:::0;-1:-1:-1;7107:18:32::2;7148:11:::0;;:94:::2;;7173:69;7189:6:::0;7197:23:::2;7212:8:::0;7197:12;:23:::2;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;::::0;7107:135;;-1:-1:-1;7260:31:32::2;::::0;-1:-1:-1;;;;;7266:12:32::2;7107:135:::0;7260:5:::2;:31::i;:::-;-1:-1:-1::0;;7325:15:32::2;7309:13;:31:::0;7003:352:::2;6730:635;;;6715:650;18473:18:::3;:39;18513:9;18524:7;18533:17;18543:6;18533:9;:17::i;:::-;18473:78;::::0;-1:-1:-1;;;;;;18473:78:32::3;::::0;;;;;;-1:-1:-1;;;;;37771:32:33;;;18473:78:32::3;::::0;::::3;37753:51:33::0;37820:18;;;37813:34;;;;37863:18;;;37856:34;37726:18;;18473:78:32::3;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;193:3:31;18565:15:32;;:7;:15;18561:40;;18589:12;;-1:-1:-1::0;;;18589:12:32::3;;;;;;;;;;;18561:40;18645:12;::::0;18611:17:::3;::::0;-1:-1:-1;;;;;18645:12:32::3;18631:10;:26:::0;:138:::3;;18768:1;18631:138;;;18695:4;:18:::0;18672:81:::3;::::0;:7;;-1:-1:-1;;;18695:18:32;::::3;-1:-1:-1::0;;;;;18695:18:32::3;97:6:31;18734:18:32;18672:14;:81::i;:::-;18611:158:::0;-1:-1:-1;18789:58:32::3;18806:19;18611:158:::0;18806:7;:19:::3;:::i;18789:58::-;18780:67;;18858:54;18866:7;18875:9;18886:6;18894:9;18905:6;18858:7;:54::i;:::-;18463:456;3293:20:8::0;:18;:20::i;44806:145:32:-;2334:13:2;:11;:13::i;:::-;44879:12:32::1;:24:::0;;;44918:26:::1;::::0;730:25:33;;;44918:26:32::1;::::0;718:2:33;703:18;44918:26:32::1;;;;;;;;44806:145:::0;:::o;49165:263::-;6389:15;;-1:-1:-1;;;;;6389:15:32;966:10:6;6389:31:32;6385:109;;6443:40;;-1:-1:-1;;;6443:40:32;;966:10:6;6443:40:32;;;13350:51:33;13323:18;;6443:40:32;13179:228:33;6385:109:32;49234:18:::1;49255:24;:39;49295:7;:5;:7::i;:::-;49312:15;::::0;49255:85:::1;::::0;::::1;::::0;;;-1:-1:-1;;;;;;49255:85:32;;;-1:-1:-1;;;;;38241:15:33;;;49255:85:32::1;::::0;::::1;38223:34:33::0;38273:18;;;38266:34;;;49312:15:32;::::1;38316:18:33::0;;;38309:43;49329:10:32::1;38368:18:33::0;;;38361:34;38157:19;;49255:85:32::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;49405:15;::::0;49350:71:::1;::::0;-1:-1:-1;;;49350:71:32;;::::1;::::0;::::1;34407:25:33::0;;;34448:18;;;34441:34;;;-1:-1:-1;;;;;49405:15:32;;::::1;34491:18:33::0;;;34484:60;49234:106:32;;-1:-1:-1;49350:21:32::1;::::0;:34:::1;::::0;34380:18:33;;49350:71:32::1;34125:425:33::0;40139:294:32;3251:21:8;:19;:21::i;:::-;2334:13:2::1;:11;:13::i;:::-;6609:12:32::2;::::0;:17;;:39:::2;;;6630:13;:11;:13::i;:::-;:18:::0;6609:39:::2;6605:101;;;6680:15;6664:13;:31:::0;6605:101:::2;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26:::0;;;6930:58:::2;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30:::0;-1:-1:-1;7107:18:32::2;7148:11:::0;;:94:::2;;7173:69;7189:6:::0;7197:23:::2;7212:8:::0;7197:12;:23:::2;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;::::0;7107:135;;-1:-1:-1;7260:31:32::2;::::0;-1:-1:-1;;;;;7266:12:32::2;7107:135:::0;7260:5:::2;:31::i;:::-;-1:-1:-1::0;;7325:15:32::2;7309:13;:31:::0;7003:352:::2;6730:635;;;6715:650;40242:10:::3;:17:::0;40273:13;;::::3;40269:46;;40295:20;::::0;-1:-1:-1;;;40295:20:32;;::::3;::::0;::::3;730:25:33::0;;;703:18;;40295:20:32::3;584:177:33::0;40269:46:32::3;40393:15;::::0;40343:14:::3;::::0;:29:::3;::::0;40373:10:::3;::::0;40385:6;;-1:-1:-1;;;;;40393:15:32::3;40417:7;:5;:7::i;:::-;40343:83;::::0;-1:-1:-1;;;;;;40343:83:32::3;::::0;;;;;;::::3;::::0;::::3;39063:25:33::0;;;;39104:18;;;39097:34;;;;-1:-1:-1;;;;;39205:15:33;;;39185:18;;;39178:43;39257:15;39237:18;;;39230:43;39035:19;;40343:83:32::3;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;40325:15;:101:::0;;-1:-1:-1;;;;;;40325:101:32::3;-1:-1:-1::0;;;;;40325:101:32;;;::::3;::::0;;;::::3;::::0;;-1:-1:-1;3293:20:8;:18;:20::i;:::-;40139:294:32;:::o;24290:105::-;24338:16;24373:15;24366:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;24366:22:32;;;;;;;;;;;;;;;;;;;;;;;24290:105;:::o;30961:325::-;31017:7;31040:8;:6;:8::i;:::-;:29;;;-1:-1:-1;31052:12:32;;:17;31040:29;31036:43;;;-1:-1:-1;31078:1:32;;30961:325;-1:-1:-1;30961:325:32:o;31036:43::-;-1:-1:-1;;31093:12:32;;:33;31089:63;;-1:-1:-1;;;31135:17:32;30961:325;-1:-1:-1;30961:325:32:o;31089:63::-;31162:19;31184:51;31201:12;;31215:19;31184:16;:51::i;:::-;31162:73;;31266:13;:11;:13::i;:::-;31252:27;;:11;:27;:::i;7179:148:5:-;7249:7;7275:45;7292:6;7300:19;7275:16;:45::i;19413:126:32:-;19487:7;19513:19;19526:5;19513:12;:19::i;20551:543::-;20623:7;20646:8;:6;:8::i;:::-;:29;;;-1:-1:-1;20658:17:32;;-1:-1:-1;;;20658:17:32;;;;20646:29;20642:43;;;-1:-1:-1;20684:1:32;;20551:543;-1:-1:-1;20551:543:32:o;20642:43::-;20695:18;20716:16;20726:5;20716:9;:16::i;:::-;20754:15;;20695:37;;-1:-1:-1;;;;;;20754:15:32;20746:38;20742:86;;20807:10;20551:543;-1:-1:-1;;20551:543:32:o;20742:86::-;20837:23;20863:33;:31;:33::i;:::-;20837:59;;20906:31;20940:54;20957:15;20974:19;20940:16;:54::i;:::-;20906:88;;21038:10;21011:23;:37;;:76;;21064:23;21011:76;;;21051:10;21011:76;21004:83;20551:543;-1:-1:-1;;;;;20551:543:32:o;17495:123::-;17546:7;17572:39;17579:7;17588:10;17600;17572:6;:39::i;5003:195:4:-;-1:-1:-1;;;;;5162:20:4;;;5083:7;5162:20;;;:13;:20;;;;;;;;:29;;;;;;;;;;;;;5003:195::o;35619:376:32:-;2334:13:2;:11;:13::i;:::-;-1:-1:-1;;;;;35746:27:32;::::1;35742:61;;35782:21;;-1:-1:-1::0;;;35782:21:32::1;;;;;;;;;;;35742:61;35893:12;::::0;35873:48:::1;::::0;-1:-1:-1;;;;;35873:48:32;;::::1;::::0;35893:12:::1;::::0;35873:48:::1;::::0;35893:12:::1;::::0;35873:48:::1;35932:12;:28:::0;;-1:-1:-1;;;;;;35932:28:32::1;-1:-1:-1::0;;;;;35932:28:32;;;::::1;::::0;;;::::1;::::0;;35619:376::o;26965:584::-;27036:7;27095:19;27117:46;27134:7;27143:19;27117:16;:46::i;:::-;27283:12;;27095:68;;-1:-1:-1;27249:17:32;;-1:-1:-1;;;;;27283:12:32;27269:10;:26;:203;;27471:1;27269:203;;;27367:4;:15;27310:146;;27344:78;;:7;;-1:-1:-1;;;;;27367:15:32;97:6:31;27367:15:32;27344:14;:78::i;27310:146::-;27249:223;-1:-1:-1;27519:23:32;27249:223;27519:11;:23;:::i;:::-;27512:30;26965:584;-1:-1:-1;;;;26965:584:32:o;48000:600::-;2334:13:2;:11;:13::i;:::-;3251:21:8::1;:19;:21::i;:::-;48105:15:32::2;::::0;-1:-1:-1;;;;;48105:15:32::2;48093:64;;48144:13;;-1:-1:-1::0;;;48144:13:32::2;;;;;;;;;;;48093:64;48167:31;48201:72;48218:33;:31;:33::i;:::-;48253:19;48201:16;:72::i;:::-;48167:106;;48283:52;48350:83;;;;;;;;48389:13;:11;:13::i;:::-;48350:83;;;;48404:13;:11;:13::i;:::-;48350:83:::0;;2862:1:::2;48350:83;::::0;;::::2;::::0;48489:15:::2;::::0;48283:150;;-1:-1:-1;48443:21:32::2;::::0;:32:::2;::::0;-1:-1:-1;;;;;48489:15:32::2;48506:11:::0;48519:23;48544:7:::2;:5;:7::i;:::-;48565:10;::::0;48443:150:::2;::::0;-1:-1:-1;;;;;;48443:150:32::2;::::0;;;;;;::::2;::::0;;;;;48553:10:::2;::::0;-1:-1:-1;;;;;48565:10:32;;::::2;::::0;48577:6;;48443:150:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;48083:517;;3293:20:8::1;:18;:20::i;3405:215:2:-:0;2334:13;:11;:13::i;:::-;-1:-1:-1;;;;;3489:22:2;::::1;3485:91;;3534:31;::::0;-1:-1:-1;;;3534:31:2;;3562:1:::1;3534:31;::::0;::::1;13350:51:33::0;13323:18;;3534:31:2::1;13179:228:33::0;3485:91:2::1;3585:28;3604:8;3585:18;:28::i;41782:257:32:-:0;3251:21:8;:19;:21::i;:::-;2334:13:2::1;:11;:13::i;:::-;6609:12:32::2;::::0;:17;;:39:::2;;;6630:13;:11;:13::i;:::-;:18:::0;6609:39:::2;6605:101;;;6680:15;6664:13;:31:::0;6605:101:::2;6720:8;:6;:8::i;:::-;6715:650;;6744:16;6786:23;:21;:23::i;:::-;6763:20;:18;:20::i;:::-;:46;;;;:::i;:::-;6744:65;;6823:18;6844:21;6860:4;6844:15;:21::i;:::-;6823:42;;6879:20;6902:13;:11;:13::i;:::-;6879:36;;6947:13;;6934:10;:26;6930:58;;;6962:13;:26:::0;;;6930:58:::2;7018:1;7007:8;:12;:32;;;;;7038:1;7023:12;:16;7007:32;7003:352;;;7059:14;7076:13;:11;:13::i;:::-;7059:30:::0;-1:-1:-1;7107:18:32::2;7148:11:::0;;:94:::2;;7173:69;7189:6:::0;7197:23:::2;7212:8:::0;7197:12;:23:::2;:::i;7173:69::-;7148:94;;;7162:8;7148:94;7266:12;::::0;7107:135;;-1:-1:-1;7260:31:32::2;::::0;-1:-1:-1;;;;;7266:12:32::2;7107:135:::0;7260:5:::2;:31::i;:::-;-1:-1:-1::0;;7325:15:32::2;7309:13;:31:::0;7003:352:::2;6730:635;;;6715:650;41951:14:::3;:32;41984:10;41996:12;;42010;42024:7;:5;:7::i;:::-;41951:81;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;3293:20:8::0;:18;:20::i;36163:204:32:-;2334:13:2;:11;:13::i;:::-;36282:15:32::1;::::0;36255:61:::1;::::0;;29377:25:33;;;29433:2;29418:18;;29411:34;;;36255:61:32::1;::::0;29350:18:33;36255:61:32::1;;;;;;;36326:15;:34:::0;36163:204::o;2658:162:2:-;966:10:6;2717:7:2;:5;:7::i;:::-;-1:-1:-1;;;;;2717:23:2;;2713:101;;2763:40;;-1:-1:-1;;;2763:40:2;;966:10:6;2763:40:2;;;13350:51:33;13323:18;;2763:40:2;13179:228:33;32474:262:32;32620:7;32650:79;32664:13;:11;:13::i;:::-;:17;;32680:1;32664:17;:::i;:::-;32699:19;2862:1;32699:2;:19;:::i;:::-;32683:13;:11;:13::i;:::-;:35;;;;:::i;:::-;32650:6;;:79;32720:8;32650:13;:79::i;10264:128:4:-;10348:37;10357:5;10364:7;10373:5;10380:4;10348:8;:37::i;31774:219:32:-;31872:14;31907:79;31937:19;2862:1;31937:2;:19;:::i;:::-;31921:13;:11;:13::i;:::-;:35;;;;:::i;:::-;31958:13;:11;:13::i;:::-;:17;;31974:1;31958:17;:::i;8051:302:19:-;8152:7;8171:14;8188:25;8195:1;8198;8201:11;8188:6;:25::i;:::-;8171:42;;8227:26;8244:8;8227:16;:26::i;:::-;:59;;;;;8285:1;8270:11;8257:25;;;;;:::i;:::-;8267:1;8264;8257:25;:29;8227:59;8223:101;;;8302:11;8312:1;8302:11;;:::i;:::-;;8340:6;-1:-1:-1;;;;;;8051:302:19:o;11993:477:4:-;12092:24;12119:25;12129:5;12136:7;12119:9;:25::i;:::-;12092:52;;-1:-1:-1;;12158:16:4;:37;12154:310;;12234:5;12215:16;:24;12211:130;;;12266:60;;-1:-1:-1;;;12266:60:4;;-1:-1:-1;;;;;37771:32:33;;12266:60:4;;;37753:51:33;37820:18;;;37813:34;;;37863:18;;;37856:34;;;37726:18;;12266:60:4;37543:353:33;12211:130:4;12382:57;12391:5;12398:7;12426:5;12407:16;:24;12433:5;12382:8;:57::i;6868:300::-;-1:-1:-1;;;;;6951:18:4;;6947:86;;6992:30;;-1:-1:-1;;;6992:30:4;;7019:1;6992:30;;;13350:51:33;13323:18;;6992:30:4;13179:228:33;6947:86:4;-1:-1:-1;;;;;7046:16:4;;7042:86;;7085:32;;-1:-1:-1;;;7085:32:4;;7114:1;7085:32;;;13350:51:33;13323:18;;7085:32:4;13179:228:33;7042:86:4;7137:24;7145:4;7151:2;7155:5;7137:7;:24::i;3326:384:8:-;2388:30;3526:9;;-1:-1:-1;;3526:20:8;3522:88;;3569:30;;-1:-1:-1;;;3569:30:8;;;;;;;;;;;3522:88;1847:1;3684:19;;3326:384::o;3716:283::-;1805:1;2388:30;3969:23;3716:283::o;8831:1361:32:-;9163:17;:15;:17::i;:::-;9190:26;9205:10;9190:14;:26::i;:::-;9226:38;9239:10;9251:12;9226;:38::i;:::-;9274:22;9289:6;9274:14;:22::i;:::-;9306:24;:22;:24::i;:::-;-1:-1:-1;;;;;9345:33:32;;9341:67;;9387:21;;-1:-1:-1;;;9387:21:32;;;;;;;;;;;9341:67;9552:15;;9450:152;;-1:-1:-1;;;9450:152:32;;:24;;:48;;:152;;9512:10;;2862:1;;9539:11;;-1:-1:-1;;;;;9552:15:32;;;;9569:10;;9581:5;;9588:4;;9450:152;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9420:15;9419:183;;-1:-1:-1;;;;;;9419:183:32;-1:-1:-1;;;9419:183:32;;;;;;;;;-1:-1:-1;;;;;;9419:183:32;;;;;-1:-1:-1;;;;;9419:183:32;;;;;;9616:27;;9612:86;;9666:21;;-1:-1:-1;;;9666:21:32;;;;;;;;;;;9612:86;9707:12;:28;;9762:3;9746:13;:19;-1:-1:-1;9843:28:32;;;9881:16;:36;;;;;-1:-1:-1;;9881:36:32;;;;;;-1:-1:-1;;;;;;10057:25:32;-1:-1:-1;;;;;9707:28:32;;;10057:25;;;;;;;10108:15;10092:13;:31;10138:47;;;;;;10158:4;;10138:47;;9707:12;;10138:47;8831:1361;;;;;;;;;:::o;47436:249::-;47511:13;:40;47565:22;47575:11;47565:9;:22::i;:::-;47589:11;47602:15;47619:11;47632:15;47649:19;47511:167;;;;;;;;;;;;;;;;;;;;:::i;8996:208:4:-;-1:-1:-1;;;;;9066:21:4;;9062:91;;9110:32;;-1:-1:-1;;;9110:32:4;;9139:1;9110:32;;;13350:51:33;13323:18;;9110:32:4;13179:228:33;9062:91:4;9162:35;9178:1;9182:7;9191:5;9162:7;:35::i;3674:178:7:-;2563:16;:14;:16::i;:::-;-1:-1:-1;;;;;;;;;;;3791:17:7;;-1:-1:-1;;3791:17:7::1;::::0;;3823:22:::1;966:10:6::0;3832:12:7::1;3823:22;::::0;-1:-1:-1;;;;;13368:32:33;;;13350:51;;13338:2;13323:18;3823:22:7::1;13179:228:33::0;1483:957:1;1687:64;;;-1:-1:-1;;;;;47060:32:33;;1687:64:1;;;47042:51:33;47109:18;;;;47102:34;;;1687:64:1;;;;;;;;;;47015:18:33;;;;1687:64:1;;;;;;;;-1:-1:-1;;;;;1687:64:1;-1:-1:-1;;;1687:64:1;;;-1:-1:-1;;1783:52:1;1814:5;1687:64;1783:23;:52::i;:::-;1761:74;;1850:19;1845:455;;2015:59;;;-1:-1:-1;;;;;47345:32:33;;2015:59:1;;;47327:51:33;2072:1:1;47394:18:33;;;;47387:45;;;;2015:59:1;;;;;;;;;;47300:18:33;;;;2015:59:1;;;;;;;;-1:-1:-1;;;;;2015:59:1;-1:-1:-1;;;2015:59:1;;;1976:99;;2007:5;;1976:23;:99::i;:::-;1938:137;;2093:19;2089:201;;;2223:52;2254:5;2262:12;2223:23;:52::i;:::-;2201:74;;2089:201;2314:19;2313:20;:42;;;;;2337:18;2313:42;2309:125;;;2378:45;;-1:-1:-1;;;2378:45:1;;-1:-1:-1;;;;;47701:15:33;;;2378:45:1;;;47683:34:33;47753:15;;47733:18;;;47726:43;47785:18;;;47778:34;;;47618:18;;2378:45:1;47443:375:33;2309:125:1;1649:791;1483:957;;;;;;:::o;2905:128:7:-;2970:8;:6;:8::i;:::-;2966:61;;;3001:15;;-1:-1:-1;;;3001:15:7;;;;;;;;;;;49483:230:32;-1:-1:-1;;;;;49569:23:32;;49565:54;;49601:18;;-1:-1:-1;;;49601:18:32;;;;;;;;;;;49565:54;49633:12;;49649:1;49633:17;49629:78;;49681:15;49666:12;:30;49483:230;:::o;1702:188:16:-;1829:53;;;-1:-1:-1;;;;;47701:15:33;;;1829:53:16;;;47683:34:33;47753:15;;47733:18;;;47726:43;47785:18;;;;47778:34;;;1829:53:16;;;;;;;;;;47618:18:33;;;;1829:53:16;;;;;;;;-1:-1:-1;;;;;1829:53:16;-1:-1:-1;;;1829:53:16;;;1802:81;;1822:5;;1802:19;:81::i;3774:248:2:-;1313:22;3923:8;;-1:-1:-1;;;;;;3941:19:2;;-1:-1:-1;;;;;3941:19:2;;;;;;;;3975:40;;3923:8;;;;;3975:40;;3847:24;;3975:40;3837:185;;3774:248;:::o;3366:176:7:-;2316:19;:17;:19::i;:::-;-1:-1:-1;;;;;;;;;;;3484:16:7;;-1:-1:-1;;3484:16:7::1;3496:4;3484:16;::::0;;3515:20:::1;966:10:6::0;3522:12:7::1;887:96:6::0;23368:916:32;23513:17;;-1:-1:-1;;;23513:17:32;;;;23509:49;;;23539:19;;-1:-1:-1;;;23539:19:32;;;;;;;;;;;23509:49;23572:10;-1:-1:-1;;;;;23572:20:32;;;23568:102;;23608:51;23624:6;23632:10;23644:14;23608:15;:51::i;:::-;23679:29;23685:6;23693:14;23679:5;:29::i;:::-;23722:13;;23718:49;;23743:12;;23737:30;;-1:-1:-1;;;;;23743:12:32;23757:9;23737:5;:30::i;:::-;23777:36;23816:33;:31;:33::i;:::-;23777:72;-1:-1:-1;23859:21:32;:39;23912:6;23932:26;23949:9;23932:14;:26;:::i;:::-;23972:9;23995:28;24037:7;:5;:7::i;:::-;24066:15;;24096;;24149:10;;24173:16;;23859:340;;-1:-1:-1;;;;;;23859:340:32;;;;;;;;;;;;;;-1:-1:-1;;;;;24066:15:32;;;;24096;24125:10;;24149;;;24173:16;;;;;23859:340;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;24214:63:32;;;29377:25:33;;;29433:2;29418:18;;29411:34;;;-1:-1:-1;;;;;24214:63:32;;;;-1:-1:-1;24214:63:32;;;-1:-1:-1;24223:10:32;;24214:63;;29350:18:33;24214:63:32;;;;;;;23499:785;23368:916;;;;;:::o;19545:1000::-;19605:7;19628:8;:6;:8::i;:::-;:29;;;-1:-1:-1;19640:17:32;;-1:-1:-1;;;19640:17:32;;;;19628:29;19624:43;;;-1:-1:-1;19666:1:32;;19545:1000;-1:-1:-1;19545:1000:32:o;19624:43::-;19722:22;19747:55;19764:16;19774:5;19764:9;:16::i;19747:55::-;19722:80;;19858:19;19903:23;:21;:23::i;:::-;19880:20;:18;:20::i;:::-;:46;;;;:::i;:::-;19858:68;;19936:17;19956:70;19975:14;19991:13;:11;:13::i;:::-;19956:11;;:70;20006:19;19956:18;:70::i;:::-;20159:4;:18;19936:90;;-1:-1:-1;20087:28:32;;19936:90;;20118:99;;20140:37;;-1:-1:-1;;;20159:18:32;;-1:-1:-1;;;;;20159:18:32;97:6:31;20140:37:32;:::i;:::-;20118:14;;97:6:31;20197:19:32;20118:21;:99::i;:::-;:123;;;;:::i;:::-;20264:15;;20087:154;;-1:-1:-1;;;;;;20264:15:32;20256:38;20252:287;;20317:20;19545:1000;-1:-1:-1;;;;;19545:1000:32:o;20252:287::-;20368:23;20394:33;:31;:33::i;:::-;20368:59;;20467:20;20448:15;:39;;:80;;20513:15;20448:80;;;20490:20;20448:80;20441:87;19545:1000;-1:-1:-1;;;;;;;19545:1000:32:o;11224:487:4:-;-1:-1:-1;;;;;;;;;;;;;;;;11389:19:4;;11385:89;;11431:32;;-1:-1:-1;;;11431:32:4;;11460:1;11431:32;;;13350:51:33;13323:18;;11431:32:4;13179:228:33;11385:89:4;-1:-1:-1;;;;;11487:21:4;;11483:90;;11531:31;;-1:-1:-1;;;11531:31:4;;11559:1;11531:31;;;13350:51:33;13323:18;;11531:31:4;13179:228:33;11483:90:4;-1:-1:-1;;;;;11582:20:4;;;;;;;:13;;;:20;;;;;;;;:29;;;;;;;;;:37;;;11629:76;;;;11679:7;-1:-1:-1;;;;;11663:31:4;11672:5;-1:-1:-1;;;;;11663:31:4;;11688:5;11663:31;;;;730:25:33;;718:2;703:18;;584:177;11663:31:4;;;;;;;;11322:389;11224:487;;;;:::o;3803:4116:19:-;3885:14;4248:5;;;3885:14;-1:-1:-1;;4252:1:19;4248;4420:20;4493:5;4489:2;4486:13;4478:5;4474:2;4470:14;4466:34;4457:43;;;4595:5;4604:1;4595:10;4591:368;;4933:11;4925:5;:19;;;;;:::i;:::-;;4918:26;;;;;;4591:368;5080:5;5065:11;:20;5061:88;;5112:22;;-1:-1:-1;;;5112:22:19;;;;;;;;;;;5061:88;5404:17;5539:11;5536:1;5533;5526:25;5939:12;5969:15;;;5954:31;;6088:22;;;;;6813:1;6794;:15;;6793:21;;7046;;;7042:25;;7031:36;7115:21;;;7111:25;;7100:36;7185:21;;;7181:25;;7170:36;7255:21;;;7251:25;;7240:36;7325:21;;;7321:25;;7310:36;7396:21;;;7392:25;;;7381:36;6333:12;;;;6329:23;;;6354:1;6325:31;5653:20;;;5642:32;;;6445:12;;;;5700:21;;;;6186:16;;;;6436:21;;;;7860:15;;;;;-1:-1:-1;;3803:4116:19;;;;;:::o;14989:122::-;15057:4;15098:1;15086:8;15080:15;;;;;;;;:::i;:::-;:19;;;;:::i;:::-;:24;;15103:1;15080:24;15073:31;;14989:122;;;:::o;41107:251:32:-;-1:-1:-1;;;;;41197:18:32;;;41193:56;;41217:32;41244:4;41217:26;:32::i;:::-;-1:-1:-1;;;;;41263:16:32;;;41259:52;;41281:30;41308:2;41281:26;:30::i;:::-;41321;41335:4;41341:2;41345:5;41321:13;:30::i;1836:97:7:-;6931:20:3;:18;:20::i;:::-;1899:27:7::1;:25;:27::i;4977:114:5:-:0;6931:20:3;:18;:20::i;:::-;5052:32:5::1;5077:6;5052:24;:32::i;2577:147:4:-:0;6931:20:3;:18;:20::i;:::-;2679:38:4::1;2702:5;2709:7;2679:22;:38::i;1847:127:2:-:0;6931:20:3;:18;:20::i;:::-;1929:38:2::1;1954:12;1929:24;:38::i;2540:111:8:-:0;6931:20:3;:18;:20::i;:::-;2610:34:8::1;:32;:34::i;3105:126:7:-:0;3168:8;:6;:8::i;:::-;3163:62;;3199:15;;-1:-1:-1;;;3199:15:7;;;;;;;;;;;3230:480:1;3313:4;3329:12;3351:18;3379:19;3513:4;3510:1;3503:4;3497:11;3490:4;3484;3480:15;3477:1;3470:5;3463;3458:60;3447:71;;3545:16;3531:30;;3595:1;3589:8;3574:23;;3623:7;:80;;;;-1:-1:-1;3635:15:1;;:67;;3686:11;3701:1;3686:16;3635:67;;;-1:-1:-1;;;;;;;;;;3653:26:1;;:30;;;3230:480::o;4059:629:16:-;4478:23;4504:33;-1:-1:-1;;;;;4504:27:16;;4532:4;4504:27;:33::i;:::-;4478:59;;4551:10;:17;4572:1;4551:22;;:57;;;;;4589:10;4578:30;;;;;;;;;;;;:::i;:::-;4577:31;4551:57;4547:135;;;4631:40;;-1:-1:-1;;;4631:40:16;;-1:-1:-1;;;;;13368:32:33;;4631:40:16;;;13350:51:33;13323:18;;4631:40:16;13179:228:33;9522:206:4;-1:-1:-1;;;;;9592:21:4;;9588:89;;9636:30;;-1:-1:-1;;;9636:30:4;;9663:1;9636:30;;;13350:51:33;13323:18;;9636:30:4;13179:228:33;9588:89:4;9686:35;9694:7;9711:1;9715:5;9686:7;:35::i;7483:1170::-;-1:-1:-1;;;;;;;;;;;;;;;;7625:18:4;;7621:546;;7779:5;7761:1;:14;;;:23;;;;;;;:::i;:::-;;;;-1:-1:-1;7621:546:4;;-1:-1:-1;7621:546:4;;-1:-1:-1;;;;;7837:17:4;;7815:19;7837:17;;;;;;;;;;;7872:19;;;7868:115;;;7918:50;;-1:-1:-1;;;7918:50:4;;-1:-1:-1;;;;;37771:32:33;;7918:50:4;;;37753:51:33;37820:18;;;37813:34;;;37863:18;;;37856:34;;;37726:18;;7918:50:4;37543:353:33;7868:115:4;-1:-1:-1;;;;;8103:17:4;;:11;:17;;;;;;;;;;8123:19;;;;8103:39;;7621:546;-1:-1:-1;;;;;8181:16:4;;8177:429;;8344:14;;;:23;;;;;;;8177:429;;;-1:-1:-1;;;;;8557:15:4;;:11;:15;;;;;;;;;;:24;;;;;;8177:429;8636:2;-1:-1:-1;;;;;8621:25:4;8630:4;-1:-1:-1;;;;;8621:25:4;;8640:5;8621:25;;;;730::33;;718:2;703:18;;584:177;8621:25:4;;;;;;;;7558:1095;7483:1170;;;:::o;7084:141:3:-;8870:21;8560:40;-1:-1:-1;;;8560:40:3;;;;7146:73;;7191:17;;-1:-1:-1;;;7191:17:3;;;;;;;;;;;1939:156:7;6931:20:3;:18;:20::i;:::-;-1:-1:-1;;;;;;;;;;;2071:17:7;;-1:-1:-1;;2071:17:7::1;::::0;;1939:156::o;5097:304:5:-;6931:20:3;:18;:20::i;:::-;4093:22:5;5182:24:::1;::::0;5277:28:::1;5298:6:::0;5277:20:::1;:28::i;:::-;5239:66;;;;5339:7;:28;;5365:2;5339:28;;;5349:13;5339:28;5315:52:::0;;-1:-1:-1;;;;;;5377:17:5;-1:-1:-1;;;5315:52:5::1;::::0;;;::::1;::::0;;;::::1;-1:-1:-1::0;;;;;;5377:17:5;;-1:-1:-1;;;;;5377:17:5;;;::::1;::::0;;;::::1;::::0;;;-1:-1:-1;;5097:304:5:o;2730:216:4:-;6931:20:3;:18;:20::i;:::-;-1:-1:-1;;;;;;;;;;;2895:7:4;:15:::1;2905:5:::0;2895:7;:15:::1;:::i;:::-;-1:-1:-1::0;2920:9:4::1;::::0;::::1;:19;2932:7:::0;2920:9;:19:::1;:::i;1980:235:2:-:0;6931:20:3;:18;:20::i;2657:183:8:-;6931:20:3;:18;:20::i;2705:151:17:-;2780:12;2811:38;2833:6;2841:4;2847:1;2811:21;:38::i;5544:533:5:-;5721:43;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5721:43:5;-1:-1:-1;;;5721:43:5;;;5681:93;;5611:4;;;;;;;;-1:-1:-1;;;;;5681:26:5;;;:93;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5634:140;;;;5788:7;:39;;;;;5825:2;5799:15;:22;:28;;5788:39;5784:260;;;5843:24;5881:15;5870:38;;;;;;;;;;;;:::i;:::-;5843:65;-1:-1:-1;5946:15:5;5926:35;;5922:112;;5989:4;;6001:16;;-1:-1:-1;5544:533:5;-1:-1:-1;;;;5544:533:5:o;5922:112::-;5829:215;5784:260;-1:-1:-1;6061:5:5;;;;-1:-1:-1;5544:533:5;-1:-1:-1;;;5544:533:5:o;3180:392:17:-;3279:12;3331:5;3307:21;:29;3303:108;;;3359:41;;-1:-1:-1;;;3359:41:17;;3394:4;3359:41;;;13350:51:33;13323:18;;3359:41:17;13179:228:33;3303:108:17;3421:12;3435:23;3462:6;-1:-1:-1;;;;;3462:11:17;3481:5;3488:4;3462:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3420:73;;;;3510:55;3537:6;3545:7;3554:10;4769:12;4798:7;4793:408;;4821:19;4829:10;4821:7;:19::i;:::-;4793:408;;;5045:17;;:22;:49;;;;-1:-1:-1;;;;;;5071:18:17;;;:23;5045:49;5041:119;;;5121:24;;-1:-1:-1;;;5121:24:17;;-1:-1:-1;;;;;13368:32:33;;5121:24:17;;;13350:51:33;13323:18;;5121:24:17;13179:228:33;5041:119:17;-1:-1:-1;5180:10:17;5173:17;;5743:516;5874:17;;:21;5870:383;;6102:10;6096:17;6158:15;6145:10;6141:2;6137:19;6130:44;5870:383;6225:17;;-1:-1:-1;;;6225:17:17;;;;;;;;;;;14:118:33;100:5;93:13;86:21;79:5;76:32;66:60;;122:1;119;112:12;137:128;202:20;;231:28;202:20;231:28;:::i;:::-;137:128;;;:::o;270:309::-;335:6;343;396:2;384:9;375:7;371:23;367:32;364:52;;;412:1;409;402:12;364:52;448:9;435:23;425:33;;508:2;497:9;493:18;480:32;521:28;543:5;521:28;:::i;:::-;568:5;558:15;;;270:309;;;;;:::o;766:250::-;851:1;861:113;875:6;872:1;869:13;861:113;;;951:11;;;945:18;932:11;;;925:39;897:2;890:10;861:113;;;-1:-1:-1;;1008:1:33;990:16;;983:27;766:250::o;1021:271::-;1063:3;1101:5;1095:12;1128:6;1123:3;1116:19;1144:76;1213:6;1206:4;1201:3;1197:14;1190:4;1183:5;1179:16;1144:76;:::i;:::-;1274:2;1253:15;-1:-1:-1;;1249:29:33;1240:39;;;;1281:4;1236:50;;1021:271;-1:-1:-1;;1021:271:33:o;1297:220::-;1446:2;1435:9;1428:21;1409:4;1466:45;1507:2;1496:9;1492:18;1484:6;1466:45;:::i;1522:131::-;-1:-1:-1;;;;;1597:31:33;;1587:42;;1577:70;;1643:1;1640;1633:12;1658:134;1726:20;;1755:31;1726:20;1755:31;:::i;1797:247::-;1856:6;1909:2;1897:9;1888:7;1884:23;1880:32;1877:52;;;1925:1;1922;1915:12;1877:52;1964:9;1951:23;1983:31;2008:5;1983:31;:::i;2158:830::-;2395:2;2447:21;;;2517:13;;2420:18;;;2539:22;;;2366:4;;2395:2;2580;;2598:18;;;;2639:15;;;2366:4;2682:280;2696:6;2693:1;2690:13;2682:280;;;2755:13;;2797:9;;-1:-1:-1;;;;;2793:35:33;2781:48;;2869:11;;2863:18;2849:12;;;2842:40;2902:12;;;;2937:15;;;;2825:1;2711:9;2682:280;;;-1:-1:-1;2979:3:33;;2158:830;-1:-1:-1;;;;;;;2158:830:33:o;2993:180::-;3052:6;3105:2;3093:9;3084:7;3080:23;3076:32;3073:52;;;3121:1;3118;3111:12;3073:52;-1:-1:-1;3144:23:33;;2993:180;-1:-1:-1;2993:180:33:o;3178:315::-;3246:6;3254;3307:2;3295:9;3286:7;3282:23;3278:32;3275:52;;;3323:1;3320;3313:12;3275:52;3362:9;3349:23;3381:31;3406:5;3381:31;:::i;:::-;3431:5;3483:2;3468:18;;;;3455:32;;-1:-1:-1;;;3178:315:33:o;3929:1068::-;4075:4;4104:2;4133;4122:9;4115:21;4174:3;4163:9;4159:19;-1:-1:-1;;;;;4270:2:33;4261:6;4255:13;4251:22;4246:2;4235:9;4231:18;4224:50;4338:2;4332;4324:6;4320:15;4314:22;4310:31;4305:2;4294:9;4290:18;4283:59;4389:2;4381:6;4377:15;4371:22;4412:2;4468;4454:12;4450:21;4445:2;4434:9;4430:18;4423:49;4521:2;4513:6;4509:15;4503:22;4481:44;;4563:4;4556;4545:9;4541:20;4534:34;4588:6;4577:17;;4623:14;4617:21;4662:6;4654;4647:22;4700:3;4689:9;4685:19;4678:26;;4747:2;4731:14;4727:23;4713:37;;4768:1;4759:10;;4778:193;4792:6;4789:1;4786:13;4778:193;;;4841:50;4887:3;4878:6;4872:13;3768:12;;3756:25;;3830:4;3819:16;;;3813:23;3797:14;;;3790:47;3890:4;3879:16;;;3873:23;-1:-1:-1;;;;;3869:48:33;3853:14;;3846:72;3690:234;4841:50;4946:15;;;;4814:1;4807:9;;;;;4911:12;;;;4778:193;;;-1:-1:-1;4988:3:33;;3929:1068;-1:-1:-1;;;;;;;3929:1068:33:o;5002:388::-;5070:6;5078;5131:2;5119:9;5110:7;5106:23;5102:32;5099:52;;;5147:1;5144;5137:12;5099:52;5186:9;5173:23;5205:31;5230:5;5205:31;:::i;:::-;5255:5;-1:-1:-1;5312:2:33;5297:18;;5284:32;5325:33;5284:32;5325:33;:::i;5395:456::-;5472:6;5480;5488;5541:2;5529:9;5520:7;5516:23;5512:32;5509:52;;;5557:1;5554;5547:12;5509:52;5596:9;5583:23;5615:31;5640:5;5615:31;:::i;:::-;5665:5;-1:-1:-1;5722:2:33;5707:18;;5694:32;5735:33;5694:32;5735:33;:::i;:::-;5395:456;;5787:7;;-1:-1:-1;;;5841:2:33;5826:18;;;;5813:32;;5395:456::o;5856:591::-;5926:6;5934;5987:2;5975:9;5966:7;5962:23;5958:32;5955:52;;;6003:1;6000;5993:12;5955:52;6043:9;6030:23;-1:-1:-1;;;;;6113:2:33;6105:6;6102:14;6099:34;;;6129:1;6126;6119:12;6099:34;6167:6;6156:9;6152:22;6142:32;;6212:7;6205:4;6201:2;6197:13;6193:27;6183:55;;6234:1;6231;6224:12;6183:55;6274:2;6261:16;6300:2;6292:6;6289:14;6286:34;;;6316:1;6313;6306:12;6286:34;6361:7;6356:2;6347:6;6343:2;6339:15;6335:24;6332:37;6329:57;;;6382:1;6379;6372:12;6329:57;6413:2;6405:11;;;;;6435:6;;-1:-1:-1;5856:591:33;;-1:-1:-1;;;;5856:591:33:o;6641:127::-;6702:10;6697:3;6693:20;6690:1;6683:31;6733:4;6730:1;6723:15;6757:4;6754:1;6747:15;6773:257;6845:4;6839:11;;;6877:17;;-1:-1:-1;;;;;6909:34:33;;6945:22;;;6906:62;6903:88;;;6971:18;;:::i;:::-;7007:4;7000:24;6773:257;:::o;7035:253::-;7107:2;7101:9;7149:4;7137:17;;-1:-1:-1;;;;;7169:34:33;;7205:22;;;7166:62;7163:88;;;7231:18;;:::i;7293:251::-;7365:2;7359:9;7407:2;7395:15;;-1:-1:-1;;;;;7425:34:33;;7461:22;;;7422:62;7419:88;;;7487:18;;:::i;7549:275::-;7620:2;7614:9;7685:2;7666:13;;-1:-1:-1;;7662:27:33;7650:40;;-1:-1:-1;;;;;7705:34:33;;7741:22;;;7702:62;7699:88;;;7767:18;;:::i;:::-;7803:2;7796:22;7549:275;;-1:-1:-1;7549:275:33:o;7829:407::-;7894:5;-1:-1:-1;;;;;7920:6:33;7917:30;7914:56;;;7950:18;;:::i;:::-;7988:57;8033:2;8012:15;;-1:-1:-1;;8008:29:33;8039:4;8004:40;7988:57;:::i;:::-;7979:66;;8068:6;8061:5;8054:21;8108:3;8099:6;8094:3;8090:16;8087:25;8084:45;;;8125:1;8122;8115:12;8084:45;8174:6;8169:3;8162:4;8155:5;8151:16;8138:43;8228:1;8221:4;8212:6;8205:5;8201:18;8197:29;8190:40;7829:407;;;;;:::o;8241:222::-;8284:5;8337:3;8330:4;8322:6;8318:17;8314:27;8304:55;;8355:1;8352;8345:12;8304:55;8377:80;8453:3;8444:6;8431:20;8424:4;8416:6;8412:17;8377:80;:::i;8468:191::-;8536:4;-1:-1:-1;;;;;8561:6:33;8558:30;8555:56;;;8591:18;;:::i;:::-;-1:-1:-1;8636:1:33;8632:14;8648:4;8628:25;;8468:191::o;8664:1246::-;8726:5;8779:3;8772:4;8764:6;8760:17;8756:27;8746:55;;8797:1;8794;8787:12;8746:55;8833:6;8820:20;8859:4;8883:68;8899:51;8947:2;8899:51;:::i;:::-;8883:68;:::i;:::-;8985:15;;;9047:4;9090:13;;;9078:26;;9074:35;;;9016:12;;;;8973:3;9121:15;;;9118:35;;;9149:1;9146;9139:12;9118:35;9185:2;9177:6;9173:15;9197:684;9213:6;9208:3;9205:15;9197:684;;;9286:3;9281;9277:13;9314:2;9310;9306:11;9303:31;;;9330:1;9327;9320:12;9303:31;9360:22;;:::i;:::-;9423:3;9410:17;9440:33;9465:7;9440:33;:::i;:::-;9486:22;;9531:4;-1:-1:-1;;9555:16:33;;9551:25;-1:-1:-1;9548:45:33;;;9589:1;9586;9579:12;9548:45;9621:22;;:::i;:::-;9685:12;;;9672:26;9656:43;;9750:12;;;9737:26;9719:16;;;9712:52;9784:14;;;9777:31;9821:18;;-1:-1:-1;9859:12:33;;;;9230;;9197:684;;9915:129;-1:-1:-1;;;;;9993:5:33;9989:30;9982:5;9979:41;9969:69;;10034:1;10031;10024:12;10049:1711;10105:5;10153:4;10141:9;10136:3;10132:19;10128:30;10125:50;;;10171:1;10168;10161:12;10125:50;10193:22;;:::i;:::-;10184:31;;10252:9;10239:23;10271:32;10295:7;10271:32;:::i;:::-;10312:22;;10353:2;10392:18;;;10379:32;10420;10379;10420;:::i;:::-;10468:14;;;10461:31;10511:2;10550:18;;;10537:32;10578;10537;10578;:::i;:::-;10637:2;10626:14;;10619:31;10669:2;10707:18;;;10694:32;-1:-1:-1;;;;;10738:30:33;;10735:50;;;10781:1;10778;10771:12;10735:50;10804:22;;10857:4;10849:13;;10845:23;-1:-1:-1;10835:51:33;;10882:1;10879;10872:12;10835:51;10918:2;10905:16;10941:68;10957:51;11005:2;10957:51;:::i;10941:68::-;11043:15;;;11129:2;11121:11;;;11113:20;;11109:29;;;11074:12;;;;11150:15;;;11147:35;;;11178:1;11175;11168:12;11147:35;11202:11;;;;11222:494;11238:6;11233:3;11230:15;11222:494;;;11314:2;11308:3;11303;11299:13;11295:22;11292:42;;;11330:1;11327;11320:12;11292:42;11362:22;;:::i;:::-;11426:3;11413:17;11404:7;11397:34;11491:2;11486:3;11482:12;11469:26;11464:2;11455:7;11451:16;11444:52;11546:2;11541:3;11537:12;11524:26;11563:32;11587:7;11563:32;:::i;:::-;11615:16;;;11608:33;11654:20;;11255:12;;;;11694;;;;11222:494;;;11743:2;11732:14;;11725:29;-1:-1:-1;11736:5:33;;10049:1711;-1:-1:-1;;;;;;;10049:1711:33:o;11765:1409::-;12006:6;12014;12022;12030;12038;12046;12054;12062;12070;12123:3;12111:9;12102:7;12098:23;12094:33;12091:53;;;12140:1;12137;12130:12;12091:53;12163:29;12182:9;12163:29;:::i;:::-;12153:39;;12243:2;12232:9;12228:18;12215:32;-1:-1:-1;;;;;12307:2:33;12299:6;12296:14;12293:34;;;12323:1;12320;12313:12;12293:34;12346:50;12388:7;12379:6;12368:9;12364:22;12346:50;:::i;:::-;12336:60;;12449:2;12438:9;12434:18;12421:32;12405:48;;12478:2;12468:8;12465:16;12462:36;;;12494:1;12491;12484:12;12462:36;12517:52;12561:7;12550:8;12539:9;12535:24;12517:52;:::i;:::-;12507:62;;12622:2;12611:9;12607:18;12594:32;12578:48;;12651:2;12641:8;12638:16;12635:36;;;12667:1;12664;12657:12;12635:36;12690:71;12753:7;12742:8;12731:9;12727:24;12690:71;:::i;:::-;12680:81;;12780:39;12814:3;12803:9;12799:19;12780:39;:::i;:::-;12770:49;;12872:3;12861:9;12857:19;12844:33;12828:49;;12902:2;12892:8;12889:16;12886:36;;;12918:1;12915;12908:12;12886:36;;12941:62;12995:7;12984:8;12973:9;12969:24;12941:62;:::i;:::-;12931:72;;;13050:3;13039:9;13035:19;13022:33;13012:43;;13074:39;13108:3;13097:9;13093:19;13074:39;:::i;:::-;13064:49;;13132:36;13163:3;13152:9;13148:19;13132:36;:::i;:::-;13122:46;;11765:1409;;;;;;;;;;;:::o;13848:389::-;13936:6;13989:2;13977:9;13968:7;13964:23;13960:32;13957:52;;;14005:1;14002;13995:12;13957:52;14045:9;14032:23;-1:-1:-1;;;;;14070:6:33;14067:30;14064:50;;;14110:1;14107;14100:12;14064:50;14133:22;;14189:3;14171:16;;;14167:26;14164:46;;;14206:1;14203;14196:12;14242:315;14310:6;14318;14371:2;14359:9;14350:7;14346:23;14342:32;14339:52;;;14387:1;14384;14377:12;14339:52;14423:9;14410:23;14400:33;;14483:2;14472:9;14468:18;14455:32;14496:31;14521:5;14496:31;:::i;14562:241::-;14618:6;14671:2;14659:9;14650:7;14646:23;14642:32;14639:52;;;14687:1;14684;14677:12;14639:52;14726:9;14713:23;14745:28;14767:5;14745:28;:::i;14808:248::-;14876:6;14884;14937:2;14925:9;14916:7;14912:23;14908:32;14905:52;;;14953:1;14950;14943:12;14905:52;-1:-1:-1;;14976:23:33;;;15046:2;15031:18;;;15018:32;;-1:-1:-1;14808:248:33:o;15061:467::-;15163:6;15171;15179;15223:9;15214:7;15210:23;15253:3;15249:2;15245:12;15242:32;;;15270:1;15267;15260:12;15242:32;15306:9;15293:23;15283:33;;15366:2;15355:9;15351:18;15338:32;15379:28;15401:5;15379:28;:::i;:::-;15426:5;-1:-1:-1;15465:2:33;-1:-1:-1;;15447:16:33;;15443:25;15440:45;;;15481:1;15478;15471:12;15440:45;;15519:2;15508:9;15504:18;15494:28;;15061:467;;;;;:::o;15533:456::-;15610:6;15618;15626;15679:2;15667:9;15658:7;15654:23;15650:32;15647:52;;;15695:1;15692;15685:12;15647:52;15731:9;15718:23;15708:33;;15791:2;15780:9;15776:18;15763:32;15804:31;15829:5;15804:31;:::i;:::-;15854:5;-1:-1:-1;15911:2:33;15896:18;;15883:32;15924:33;15883:32;15924:33;:::i;:::-;15976:7;15966:17;;;15533:456;;;;;:::o;16150:864::-;16373:2;16425:21;;;16495:13;;16398:18;;;16517:22;;;16344:4;;16373:2;16596:15;;;;16570:2;16555:18;;;16344:4;16639:349;16653:6;16650:1;16647:13;16639:349;;;16712:13;;16754:9;;-1:-1:-1;;;;;16750:35:33;16738:48;;16825:11;;16819:18;16850:56;16893:12;;;16819:18;16070:12;;16058:25;;16132:4;16121:16;;;16115:23;16099:14;;16092:47;15994:151;16850:56;-1:-1:-1;16963:15:33;;;;16935:4;16926:14;;;;;16675:1;16668:9;16639:349;;;-1:-1:-1;17005:3:33;;16150:864;-1:-1:-1;;;;;;16150:864:33:o;17019:658::-;17190:2;17242:21;;;17312:13;;17215:18;;;17334:22;;;17161:4;;17190:2;17413:15;;;;17387:2;17372:18;;;17161:4;17456:195;17470:6;17467:1;17464:13;17456:195;;;17535:13;;-1:-1:-1;;;;;17531:39:33;17519:52;;17626:15;;;;17591:12;;;;17567:1;17485:9;17456:195;;17682:780;17804:6;17812;17820;17873:2;17861:9;17852:7;17848:23;17844:32;17841:52;;;17889:1;17886;17879:12;17841:52;17929:9;17916:23;-1:-1:-1;;;;;17999:2:33;17991:6;17988:14;17985:34;;;18015:1;18012;18005:12;17985:34;18053:6;18042:9;18038:22;18028:32;;18098:7;18091:4;18087:2;18083:13;18079:27;18069:55;;18120:1;18117;18110:12;18069:55;18160:2;18147:16;18186:2;18178:6;18175:14;18172:34;;;18202:1;18199;18192:12;18172:34;18257:7;18250:4;18240:6;18237:1;18233:14;18229:2;18225:23;18221:34;18218:47;18215:67;;;18278:1;18275;18268:12;18215:67;18309:4;18301:13;;;;-1:-1:-1;18333:6:33;-1:-1:-1;;18374:20:33;;18361:34;18404:28;18361:34;18404:28;:::i;19054:251::-;19124:6;19177:2;19165:9;19156:7;19152:23;19148:32;19145:52;;;19193:1;19190;19183:12;19145:52;19225:9;19219:16;19244:31;19269:5;19244:31;:::i;19310:184::-;19380:6;19433:2;19421:9;19412:7;19408:23;19404:32;19401:52;;;19449:1;19446;19439:12;19401:52;-1:-1:-1;19472:16:33;;19310:184;-1:-1:-1;19310:184:33:o;19644:624::-;19713:3;19751:5;19745:12;19778:6;19773:3;19766:19;19804:4;19833;19828:3;19824:14;19817:21;;19857:5;19854:1;19847:16;19899:4;19896:1;19886:18;19922:1;19932:311;19946:6;19943:1;19940:13;19932:311;;;20011:13;;-1:-1:-1;;;;;20007:39:33;19995:52;;20043:1;20087:14;;;20081:21;20067:12;;;20060:43;20155:1;20143:14;;20137:21;20132:2;20123:12;;20116:43;20188:4;20179:14;;;;20228:4;20216:17;;;;19961:9;19932:311;;;-1:-1:-1;20259:3:33;;19644:624;-1:-1:-1;;;;;19644:624:33:o;20273:402::-;20534:6;20523:9;20516:25;20577:2;20572;20561:9;20557:18;20550:30;20497:4;20597:72;20665:2;20654:9;20650:18;20642:6;20597:72;:::i;20680:380::-;20759:1;20755:12;;;;20802;;;20823:61;;20877:4;20869:6;20865:17;20855:27;;20823:61;20930:2;20922:6;20919:14;20899:18;20896:38;20893:161;;20976:10;20971:3;20967:20;20964:1;20957:31;21011:4;21008:1;21001:15;21039:4;21036:1;21029:15;20893:161;;20680:380;;;:::o;21065:499::-;21126:3;21164:5;21158:12;21191:6;21186:3;21179:19;21217:4;21246;21241:3;21237:14;21230:21;;21270:5;21267:1;21260:16;21312:4;21309:1;21299:18;21335:1;21345:194;21359:6;21356:1;21353:13;21345:194;;;21424:13;;-1:-1:-1;;;;;21420:39:33;21408:52;;21480:12;;;;21456:1;21515:14;;;;21374:9;21345:194;;21569:662;21922:25;;;-1:-1:-1;;;;;21983:32:33;;21978:2;21963:18;;21956:60;22003:3;22047:2;22032:18;;22025:31;;;-1:-1:-1;;22073:65:33;;22118:19;;22110:6;22073:65;:::i;:::-;22169:2;22154:18;;22147:34;;;;-1:-1:-1;22212:3:33;22197:19;22190:35;22065:73;21569:662;-1:-1:-1;;;21569:662:33:o;22236:1203::-;22364:6;22395:2;22438;22426:9;22417:7;22413:23;22409:32;22406:52;;;22454:1;22451;22444:12;22406:52;22487:9;22481:16;-1:-1:-1;;;;;22512:6:33;22509:30;22506:50;;;22552:1;22549;22542:12;22506:50;22575:22;;22628:4;22620:13;;22616:27;-1:-1:-1;22606:55:33;;22657:1;22654;22647:12;22606:55;22686:2;22680:9;22709:68;22725:51;22773:2;22725:51;:::i;22709:68::-;22811:15;;;22893:1;22889:10;;;;22881:19;;22877:28;;;22842:12;;;;22917:19;;;22914:39;;;22949:1;22946;22939:12;22914:39;22973:11;;;;22993:416;23009:6;23004:3;23001:15;22993:416;;;23091:4;23085:3;23076:7;23072:17;23068:28;23065:48;;;23109:1;23106;23099:12;23065:48;23139:22;;:::i;:::-;23195:3;23189:10;23212:33;23237:7;23212:33;:::i;:::-;23258:22;;23322:12;;;23316:19;23300:14;;;23293:43;23349:18;;23035:4;23026:14;;;;23387:12;;;;22993:416;;23444:127;23505:10;23500:3;23496:20;23493:1;23486:31;23536:4;23533:1;23526:15;23560:4;23557:1;23550:15;23576:128;23643:9;;;23664:11;;;23661:37;;;23678:18;;:::i;23709:1273::-;24077:4;24125:3;24114:9;24110:19;24156:3;24145:9;24138:22;24180:6;24215;24209:13;24246:6;24238;24231:22;24284:3;24273:9;24269:19;24262:26;;24307:6;24304:1;24297:17;24333:4;24323:14;;24373:4;24370:1;24360:18;24396:1;24406:315;24420:6;24417:1;24414:13;24406:315;;;24481:13;;24469:26;;24547:1;24535:14;;;24529:21;24515:12;;;24508:43;24609:4;24597:17;;24591:24;-1:-1:-1;;;;;24587:49:33;24580:4;24571:14;;24564:73;24666:4;24657:14;;;;24706:4;24694:17;;;;24435:9;24406:315;;;24410:3;;;24738;24730:11;;;;24779:6;24772:4;24761:9;24757:20;24750:36;24824:6;24817:4;24806:9;24802:20;24795:36;24869:6;24862:4;24851:9;24847:20;24840:36;24885:47;24927:3;24916:9;24912:19;24904:6;-1:-1:-1;;;;;2115:31:33;2103:44;;2049:104;24885:47;24969:6;24963:3;24952:9;24948:19;24941:35;23709:1273;;;;;;;;;:::o;25299:956::-;25351:5;25404:3;25397:4;25389:6;25385:17;25381:27;25371:55;;25422:1;25419;25412:12;25371:55;25458:6;25445:20;25484:4;25508:68;25524:51;25572:2;25524:51;:::i;25508:68::-;25610:15;;;25696:1;25692:10;;;;25680:23;;25676:32;;;25641:12;;;;25720:15;;;25717:35;;;25748:1;25745;25738:12;25717:35;25784:2;25776:6;25772:15;25796:430;25812:6;25807:3;25804:15;25796:430;;;25898:3;25885:17;-1:-1:-1;;;;;25921:11:33;25918:35;25915:55;;;25966:1;25963;25956:12;25915:55;25993:24;;26052:2;26044:11;;26040:21;-1:-1:-1;26030:49:33;;26075:1;26072;26065:12;26030:49;26104:79;26179:3;26173:2;26169;26165:11;26152:25;26147:2;26143;26139:11;26104:79;:::i;:::-;26092:92;;-1:-1:-1;26204:12:33;;;;25829;;25796:430;;;-1:-1:-1;26244:5:33;25299:956;-1:-1:-1;;;;;;25299:956:33:o;26260:1155::-;26387:6;26395;26448:2;26436:9;26427:7;26423:23;26419:32;26416:52;;;26464:1;26461;26454:12;26416:52;26504:9;26491:23;-1:-1:-1;;;;;26574:2:33;26566:6;26563:14;26560:34;;;26590:1;26587;26580:12;26560:34;26628:6;26617:9;26613:22;26603:32;;26673:7;26666:4;26662:2;26658:13;26654:27;26644:55;;26695:1;26692;26685:12;26644:55;26731:2;26718:16;26753:4;26777:68;26793:51;26841:2;26793:51;:::i;26777:68::-;26879:15;;;26961:1;26957:10;;;;26949:19;;26945:28;;;26910:12;;;;26985:19;;;26982:39;;;27017:1;27014;27007:12;26982:39;27041:11;;;;27061:142;27077:6;27072:3;27069:15;27061:142;;;27143:17;;27131:30;;27094:12;;;;27181;;;;27061:142;;;27222:5;-1:-1:-1;;27265:18:33;;27252:32;;-1:-1:-1;;27296:16:33;;;27293:36;;;27325:1;27322;27315:12;27293:36;;27348:61;27401:7;27390:8;27379:9;27375:24;27348:61;:::i;:::-;27338:71;;;26260:1155;;;;;:::o;27420:127::-;27481:10;27476:3;27472:20;27469:1;27462:31;27512:4;27509:1;27502:15;27536:4;27533:1;27526:15;27775:125;27840:9;;;27861:10;;;27858:36;;;27874:18;;:::i;28119:428::-;-1:-1:-1;;;;;28380:32:33;;28362:51;;28449:2;28444;28429:18;;28422:30;;;-1:-1:-1;;28469:72:33;;28522:18;;28514:6;28469:72;:::i;29875:384::-;30017:5;30004:19;29998:4;29991:33;30078:2;30071:5;30067:14;30054:28;30050:1;30044:4;30040:12;30033:50;30131:2;30124:5;30120:14;30107:28;30144:32;30168:7;30144:32;:::i;:::-;30241:1;30231:12;;29793:11;;-1:-1:-1;;29789:41:33;-1:-1:-1;;;;;29832:30:33;;29786:77;29773:91;;39139:603:32;;;:::o;30264:1328:33:-;-1:-1:-1;;;30420:3:33;30417:29;30414:55;;;30449:18;;:::i;:::-;30498:4;30492:11;30525:3;30519:4;30512:17;30549:6;30544:3;30541:15;30538:655;;;30589:1;30625:6;30622:1;30618:14;30679:1;30670:7;30666:15;30658:6;30655:27;30645:61;;30686:18;;:::i;:::-;30743:3;30740:1;30736:11;30793:1;30782:9;30778:17;30773:3;30770:26;30760:60;;30800:18;;:::i;:::-;30840:1;30833:15;;;30886:4;30873:18;;30914;;;;30958:20;30991:192;31009:2;31002:5;30999:13;30991:192;;;31085:1;31071:16;;;31122:1;31111:13;;31104:24;;;31163:1;31152:13;;31145:24;31024:14;;30991:192;;;30995:3;;;30538:655;-1:-1:-1;19591:1:33;19584:14;;;19628:4;19615:18;;31216:5;;31331:255;31345:3;31342:1;31339:10;31331:255;;;31391:100;31484:6;31471:11;31391:100;:::i;:::-;31526:2;31514:15;;;;;31574:1;31557:19;;;;;31364:1;31357:9;31331:255;;31597:1410;31766:5;31753:19;31781:32;31805:7;31781:32;:::i;:::-;29793:11;;-1:-1:-1;;29789:41:33;-1:-1:-1;;;;;29832:30:33;;29786:77;29773:91;;31822:60;31930:2;31923:5;31919:14;31906:28;31943:32;31967:7;31943:32;:::i;:::-;31994:11;;32052:34;32038:2;32034:16;;;32030:57;-1:-1:-1;;32112:48:33;;32109:62;;32096:76;;32030:57;32209:14;;32196:28;32233:32;32196:28;32233:32;:::i;:::-;-1:-1:-1;;32293:64:33;;;;32290:78;;;32397:3;32374:17;;;;-1:-1:-1;;;32370:52:33;32287:136;32274:150;;32483:2;32472:14;;32459:28;32538:14;32534:26;;;-1:-1:-1;;32530:40:33;32506:65;;32496:93;;32585:1;32582;32575:12;32496:93;32610:30;;32663:18;;-1:-1:-1;;;;;32693:30:33;;32690:50;;;32736:1;32733;32726:12;32690:50;32773:2;32767:4;32763:13;32749:27;;32832:2;32824:6;32820:15;32804:14;32800:36;32792:6;32788:49;32785:69;;;32850:1;32847;32840:12;32785:69;32863:138;32994:6;32986;32982:1;32976:4;32972:12;32863:138;:::i;33285:582::-;33596:3;33585:9;33578:22;33559:4;33617:73;33685:3;33674:9;33670:19;33662:6;33617:73;:::i;:::-;33721:2;33706:18;;33699:34;;;;-1:-1:-1;;;;;;33769:32:33;;;;33764:2;33749:18;;33742:60;33845:14;33838:22;33833:2;33818:18;;;33811:50;33609:81;33285:582;-1:-1:-1;33285:582:33:o;36080:898::-;36483:25;;;36470:3;36455:19;;36530:20;;36559:31;36530:20;36559:31;:::i;:::-;36626:1;36622;36617:3;36613:11;36609:19;36675:2;36668:5;36664:14;36659:2;36648:9;36644:18;36637:42;36688:74;36758:2;36747:9;36743:18;36738:2;36730:6;36726:15;35986:19;;35974:32;;36062:4;36051:16;;;36038:30;36022:14;;36015:54;35901:174;36688:74;36813:6;36806:14;36799:22;36793:3;36782:9;36778:19;36771:51;36859:6;36853:3;36842:9;36838:19;36831:35;36915:2;36907:6;36903:15;36897:3;36886:9;36882:19;36875:44;36968:2;36960:6;36956:15;36950:3;36939:9;36935:19;36928:44;;;36080:898;;;;;;;;;:::o;36983:555::-;37107:6;37115;37123;37176:2;37164:9;37155:7;37151:23;37147:32;37144:52;;;37192:1;37189;37182:12;37144:52;37224:9;37218:16;37243:31;37268:5;37243:31;:::i;:::-;37343:2;37328:18;;37322:25;37293:5;;-1:-1:-1;37356:33:33;37322:25;37356:33;:::i;:::-;37460:2;37445:18;;37439:25;37408:7;;-1:-1:-1;37473:33:33;37439:25;37473:33;:::i;39284:1098::-;39761:4;39790:3;39829:1;39825;39820:3;39816:11;39812:19;39870:2;39862:6;39858:15;39847:9;39840:34;39910:6;39905:2;39894:9;39890:18;39883:34;39953:6;39948:2;39937:9;39933:18;39926:34;40008:2;40000:6;39996:15;39991:2;39980:9;39976:18;39969:43;40049:2;40043:3;40032:9;40028:19;40021:31;40069:72;40137:2;40126:9;40122:18;40114:6;40069:72;:::i;:::-;40178:15;;40172:3;40157:19;;40150:44;-1:-1:-1;;40231:13:33;;40225:3;40210:19;;40203:42;40300:2;40288:15;;40282:22;40276:3;40261:19;;40254:51;40364:2;40352:15;;;40346:22;40370:4;40342:33;40336:3;40321:19;;;40314:62;40061:80;39284:1098;-1:-1:-1;;;;;39284:1098:33:o;40387:992::-;40789:25;;;40776:3;40845:2;40830:18;;40823:31;;;40761:19;;40889:22;;;40728:4;40969:6;40942:3;40927:19;;40728:4;41003:220;41017:6;41014:1;41011:13;41003:220;;;35986:19;;35974:32;;36062:4;36051:16;;;36038:30;36022:14;;;36015:54;41139:4;41198:15;;;;41163:12;;;;41039:1;41032:9;41003:220;;;-1:-1:-1;41288:14:33;;41281:22;41274:4;41259:20;;41252:52;-1:-1:-1;;;;;;;41340:32:33;;;;41335:2;41320:18;;;41313:60;41240:3;40387:992;-1:-1:-1;;;40387:992:33:o;41384:416::-;41473:1;41510:5;41473:1;41524:270;41545:7;41535:8;41532:21;41524:270;;;41604:4;41600:1;41596:6;41592:17;41586:4;41583:27;41580:53;;;41613:18;;:::i;:::-;41663:7;41653:8;41649:22;41646:55;;;41683:16;;;;41646:55;41762:22;;;;41722:15;;;;41524:270;;;41528:3;41384:416;;;;;:::o;41805:806::-;41854:5;41884:8;41874:80;;-1:-1:-1;41925:1:33;41939:5;;41874:80;41973:4;41963:76;;-1:-1:-1;42010:1:33;42024:5;;41963:76;42055:4;42073:1;42068:59;;;;42141:1;42136:130;;;;42048:218;;42068:59;42098:1;42089:10;;42112:5;;;42136:130;42173:3;42163:8;42160:17;42157:43;;;42180:18;;:::i;:::-;-1:-1:-1;;42236:1:33;42222:16;;42251:5;;42048:218;;42350:2;42340:8;42337:16;42331:3;42325:4;42322:13;42318:36;42312:2;42302:8;42299:16;42294:2;42288:4;42285:12;42281:35;42278:77;42275:159;;;-1:-1:-1;42387:19:33;;;42419:5;;42275:159;42466:34;42491:8;42485:4;42466:34;:::i;:::-;42536:6;42532:1;42528:6;42524:19;42515:7;42512:32;42509:58;;;42547:18;;:::i;:::-;42585:20;;41805:806;-1:-1:-1;;;41805:806:33:o;42616:140::-;42674:5;42703:47;42744:4;42734:8;42730:19;42724:4;42703:47;:::i;42761:127::-;42822:10;42817:3;42813:20;42810:1;42803:31;42853:4;42850:1;42843:15;42877:4;42874:1;42867:15;43243:869;43295:3;43334:4;43329:3;43325:14;-1:-1:-1;;;;;43415:2:33;43407:5;43401:12;43397:21;43392:3;43385:34;43438:4;43503:2;43495:4;43488:5;43484:16;43478:23;43474:32;43467:4;43462:3;43458:14;43451:56;43568:2;43560:4;43553:5;43549:16;43543:23;43539:32;43532:4;43527:3;43523:14;43516:56;43591:4;43581:14;;43641:4;43634:5;43630:16;43624:23;43679:4;43672;43667:3;43663:14;43656:28;43706:4;43739:12;43733:19;43774:6;43768:4;43761:20;43808:3;43803;43799:13;43790:22;;43853:4;43839:12;43835:23;43821:37;;43876:1;43867:10;;43886:199;43900:6;43897:1;43894:13;43886:199;;;43949:52;43995:5;43986:6;43980:13;3768:12;;3756:25;;3830:4;3819:16;;;3813:23;3797:14;;;3790:47;3890:4;3879:16;;;3873:23;-1:-1:-1;;;;;3869:48:33;3853:14;;3846:72;3690:234;43949:52;44060:15;;;;43922:1;43915:9;;;;;44023:14;;;;43886:199;;;-1:-1:-1;44101:5:33;43243:869;-1:-1:-1;;;;;;;43243:869:33:o;44117:1543::-;44621:4;44669:3;44658:9;44654:19;44709:1;44705;44700:3;44696:11;44692:19;44750:2;44742:6;44738:15;44727:9;44720:34;44773:2;44823:4;44815:6;44811:17;44806:2;44795:9;44791:18;44784:45;44865:3;44860:2;44849:9;44845:18;44838:31;44889:6;44924;44918:13;44955:6;44947;44940:22;44993:3;44982:9;44978:19;44971:26;;45032:2;45024:6;45020:15;45006:29;;45053:1;45063:332;45077:6;45074:1;45071:13;45063:332;;;45136:13;;45178:9;;45174:18;;45162:31;;45232:11;;45226:18;45257:56;45300:12;;;45226:18;16070:12;;16058:25;;16132:4;16121:16;;;16115:23;16099:14;;16092:47;15994:151;45257:56;-1:-1:-1;45370:15:33;;;;45342:4;45333:14;;;;;45099:1;45092:9;45063:332;;;-1:-1:-1;;;;;;;2115:31:33;;45446:4;45431:20;;2103:44;45489:6;45483:3;45472:9;45468:19;45461:35;45542:9;45537:3;45533:19;45527:3;45516:9;45512:19;45505:48;45570:40;45606:3;45598:6;45570:40;:::i;:::-;45562:48;;;;;;45647:6;45641:3;45630:9;45626:19;45619:35;44117:1543;;;;;;;;;;:::o;45665:409::-;45742:6;45750;45803:2;45791:9;45782:7;45778:23;45774:32;45771:52;;;45819:1;45816;45809:12;45771:52;45851:9;45845:16;45870:31;45895:5;45870:31;:::i;:::-;45970:2;45955:18;;45949:25;45920:5;;-1:-1:-1;46018:4:33;46005:18;;45993:31;;45983:59;;46038:1;46035;46028:12;46079:784;46510:25;;;-1:-1:-1;;;;;46571:32:33;;46566:2;46551:18;;46544:60;46640:3;46635:2;46620:18;;46613:31;;;-1:-1:-1;;46661:65:33;;46706:19;;46698:6;46661:65;:::i;:::-;46757:2;46742:18;;46735:34;;;;-1:-1:-1;46800:3:33;46785:19;;46778:35;;;;46844:3;46829:19;;;46822:35;46653:73;46079:784;-1:-1:-1;;;46079:784:33:o;47823:1102::-;48285:4;48314:3;48344:6;48333:9;48326:25;48387:6;48382:2;48371:9;48367:18;48360:34;48430:1;48426;48421:3;48417:11;48413:19;48480:2;48472:6;48468:15;48463:2;48452:9;48448:18;48441:43;48520:6;48515:2;48504:9;48500:18;48493:34;48576:2;48568:6;48564:15;48558:3;48547:9;48543:19;48536:44;48629:2;48621:6;48617:15;48611:3;48600:9;48596:19;48589:44;48670:6;48664:3;48653:9;48649:19;48642:35;48714:2;48708:3;48697:9;48693:19;48686:31;48734:72;48802:2;48791:9;48787:18;48779:6;48734:72;:::i;:::-;48843:15;;48837:3;48822:19;;48815:44;-1:-1:-1;;48903:14:33;;48896:22;48890:3;48875:19;;;48868:51;48726:80;47823:1102;-1:-1:-1;;;;;;;;47823:1102:33:o;48930:127::-;48991:10;48986:3;48982:20;48979:1;48972:31;49022:4;49019:1;49012:15;49046:4;49043:1;49036:15;49062:254;49092:1;49126:4;49123:1;49119:12;49150:3;49140:134;;49196:10;49191:3;49187:20;49184:1;49177:31;49231:4;49228:1;49221:15;49259:4;49256:1;49249:15;49140:134;49306:3;49299:4;49296:1;49292:12;49288:22;49283:27;;;49062:254;;;;:::o;49321:245::-;49388:6;49441:2;49429:9;49420:7;49416:23;49412:32;49409:52;;;49457:1;49454;49447:12;49409:52;49489:9;49483:16;49508:28;49530:5;49508:28;:::i;49571:518::-;49673:2;49668:3;49665:11;49662:421;;;49709:5;49706:1;49699:16;49753:4;49750:1;49740:18;49823:2;49811:10;49807:19;49804:1;49800:27;49794:4;49790:38;49859:4;49847:10;49844:20;49841:47;;;-1:-1:-1;49882:4:33;49841:47;49937:2;49932:3;49928:12;49925:1;49921:20;49915:4;49911:31;49901:41;;49992:81;50010:2;50003:5;50000:13;49992:81;;;50069:1;50055:16;;50036:1;50025:13;49992:81;;50265:1364;50391:3;50385:10;-1:-1:-1;;;;;50410:6:33;50407:30;50404:56;;;50440:18;;:::i;:::-;50469:97;50559:6;50519:38;50551:4;50545:11;50519:38;:::i;:::-;50513:4;50469:97;:::i;:::-;50621:4;;50678:2;50667:14;;50695:1;50690:682;;;;51416:1;51433:6;51430:89;;;-1:-1:-1;51485:19:33;;;51479:26;51430:89;-1:-1:-1;;50222:1:33;50218:11;;;50214:24;50210:29;50200:40;50246:1;50242:11;;;50197:57;51532:81;;50660:963;;50690:682;19591:1;19584:14;;;19628:4;19615:18;;-1:-1:-1;;50726:20:33;;;50863:236;50877:7;50874:1;50871:14;50863:236;;;50966:19;;;50960:26;50945:42;;51058:27;;;;51026:1;51014:14;;;;50893:19;;50863:236;;;50867:3;51127:6;51118:7;51115:19;51112:201;;;51188:19;;;51182:26;-1:-1:-1;;51271:1:33;51267:14;;;51283:3;51263:24;51259:37;51255:42;51240:58;51225:74;;51112:201;-1:-1:-1;;;;;51359:1:33;51343:14;;;51339:22;51326:36;;-1:-1:-1;50265:1364:33:o;51634:287::-;51763:3;51801:6;51795:13;51817:66;51876:6;51871:3;51864:4;51856:6;51852:17;51817:66;:::i;:::-;51899:16;;;;;51634:287;-1:-1:-1;;51634:287:33:o
Swarm Source
ipfs://8da67acd433b98a67d8b5bdde83cef47a42041c3fce6a2cf484417cf8b2a1288
Loading...
Loading
Loading...
Loading

Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.