Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
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 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
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.