Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Vault
Compiler Version
v0.8.11+commit.d7f03943
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "./vault/VaultRestricted.sol";
/**
* @notice Implementation of the {IVault} interface.
*
* @dev
* All vault instances are meant to be deployed via the Controller
* as a proxy and will not be recognizable by the Spool if they are
* not done so.
*
* The vault contract is capable of supporting a single currency underlying
* asset and deposit to multiple strategies at once, including dual-collateral
* ones.
*
* The vault also supports the additional distribution of extra reward tokens as
* an incentivization mechanism proportionate to each user's deposit amount within
* the vhe vault.
*
* Vault implementation consists of following contracts:
* 1. VaultImmutable: reads vault specific immutable variable from vault proxy contract
* 2. VaultBase: holds vault state variables and provides some of the common vault functions
* 3. RewardDrip: distributes vault incentivized rewards to users participating in the vault
* 4. VaultIndexActions: implements functions to synchronize the vault with central Spool contract
* 5. VaultRestricted: exposes functions restricted for other Spool specific contracts
* 6. Vault: exposes unrestricted functons to interact with the core vault functionality (deposit/withdraw/claim)
*/
contract Vault is VaultRestricted {
using SafeERC20 for IERC20;
using Bitwise for uint256;
/* ========== CONSTRUCTOR ========== */
/**
* @notice Sets the initial immutable values of the contract.
*
* @dev
* All values have been sanitized by the controller contract, meaning
* that no additional checks need to be applied here.
*
* @param _spool the spool implemenation
* @param _controller the controller implemenation
* @param _fastWithdraw fast withdraw implementation
* @param _feeHandler fee handler implementation
* @param _spoolOwner spool owner contract
*/
constructor(
ISpool _spool,
IController _controller,
IFastWithdraw _fastWithdraw,
IFeeHandler _feeHandler,
ISpoolOwner _spoolOwner
)
VaultBase(
_spool,
_controller,
_fastWithdraw,
_feeHandler
)
SpoolOwnable(_spoolOwner)
{}
/* ========== DEPOSIT ========== */
/**
* @notice Allows a user to perform a particular deposit to the vault.
*
* @dev
* Emits a {Deposit} event indicating the amount newly deposited for index.
*
* Perform redeem if possible:
* - Vault: Index has been completed (sync deposits/withdrawals)
* - User: Claim deposit shares or withdrawn amount
*
* Requirements:
*
* - the provided strategies must be valid
* - the caller must have pre-approved the contract for the token amount deposited
* - the caller cannot deposit zero value
* - the system should not be paused
*
* @param vaultStrategies strategies of this vault (verified internally)
* @param amount amount to deposit
* @param transferFromVault if the transfer should occur from the funds transfer(controller) address
*/
function deposit(address[] memory vaultStrategies, uint128 amount, bool transferFromVault)
external
verifyStrategies(vaultStrategies)
hasStrategies(vaultStrategies)
redeemVaultStrategiesModifier(vaultStrategies)
redeemUserModifier
updateRewards
{
require(amount > 0, "NDP");
// get next possible index to deposit
uint24 activeGlobalIndex = _getActiveGlobalIndex();
// Mark user deposited amount for active index
vaultIndexAction[activeGlobalIndex].depositAmount += amount;
userIndexAction[msg.sender][activeGlobalIndex].depositAmount += amount;
// Mark vault strategies to deposit at index
_distributeInStrats(vaultStrategies, amount, activeGlobalIndex);
// mark that vault and user have interacted at this global index
_updateInteractedIndex(activeGlobalIndex);
_updateUserInteractedIndex(activeGlobalIndex);
// transfer user deposit to Spool
_transferDepositToSpool(amount, transferFromVault);
// store user deposit amount
_addInstantDeposit(amount);
emit Deposit(msg.sender, activeGlobalIndex, amount);
}
/**
* @notice Distributes a deposit to the various strategies based on the allocations of the vault.
*/
function _distributeInStrats(
address[] memory vaultStrategies,
uint128 amount,
uint256 activeGlobalIndex
) private {
uint128 amountLeft = amount;
uint256 lastElement = vaultStrategies.length - 1;
uint256 _proportions = proportions;
for (uint256 i; i < lastElement; i++) {
uint128 proportionateAmount = _getStrategyDepositAmount(_proportions, i, amount);
if (proportionateAmount > 0) {
spool.deposit(vaultStrategies[i], proportionateAmount, activeGlobalIndex);
amountLeft -= proportionateAmount;
}
}
if (amountLeft > 0) {
spool.deposit(vaultStrategies[lastElement], amountLeft, activeGlobalIndex);
}
}
/* ========== WITHDRAW ========== */
/**
* @notice Allows a user to withdraw their deposited funds from the vault at next possible index.
* The withdrawal is queued for when do hard work for index is completed.
*
* @dev
* Perform redeem if possible:
* - Vault: Index has been completed (sync deposits/withdrawals)
* - User: Claim deposit shares or withdrawn amount
*
* Emits a {Withdrawal} event indicating the shares burned, index of the withdraw and the amount of funds withdrawn.
*
* Requirements:
*
* - vault must not be reallocating
* - the provided strategies must be valid
* - the caller must have a non-zero amount of shares to withdraw
* - the caller must have enough shares to withdraw the specified share amount
* - the system should not be paused
*
* @param vaultStrategies strategies of this vault (verified internally)
* @param sharesToWithdraw shares amount to withdraw
* @param withdrawAll if all shares should be removed
*/
function withdraw(
address[] memory vaultStrategies,
uint128 sharesToWithdraw,
bool withdrawAll
)
external
verifyStrategies(vaultStrategies)
redeemVaultStrategiesModifier(vaultStrategies)
noReallocation
redeemUserModifier
updateRewards
{
sharesToWithdraw = _withdrawShares(sharesToWithdraw, withdrawAll);
// get next possible index to withdraw
uint24 activeGlobalIndex = _getActiveGlobalIndex();
// mark user withdrawn shares amount for active index
userIndexAction[msg.sender][activeGlobalIndex].withdrawShares += sharesToWithdraw;
vaultIndexAction[activeGlobalIndex].withdrawShares += sharesToWithdraw;
// mark strategies in the spool contract to be withdrawn at next possible index
_withdrawFromStrats(vaultStrategies, sharesToWithdraw, activeGlobalIndex);
// mark that vault and user interacted at this global index
_updateInteractedIndex(activeGlobalIndex);
_updateUserInteractedIndex(activeGlobalIndex);
emit Withdraw(msg.sender, activeGlobalIndex, sharesToWithdraw);
}
/* ========== FAST WITHDRAW ========== */
/**
* @notice Allows a user to withdraw their deposited funds right away.
*
* @dev
* @dev
* User can execute the withdrawal of his shares from the vault at any time without
* waiting for the DHW to process it. This is done independently of other events (e.g. DHW)
* and the gas cost is paid entirely by the user.
* Shares belonging to the user and are sent back to the FastWithdraw contract
* where an actual withdrawal can be peformed, where user recieves the underlying tokens
* right away.
*
* Requirements:
*
* - vault must not be reallocating
* - the spool system must not be mid reallocation
* (started DHW and not finished, at index the reallocation was initiated)
* - the provided strategies must be valid
* - the sistem must not be in the middle of the reallocation
* - the system should not be paused
*
* @param vaultStrategies strategies of this vault
* @param sharesToWithdraw shares amount to withdraw
* @param withdrawAll if all shares should be removed
* @param fastWithdrawParams extra parameters to perform fast withdraw
*/
function withdrawFast(
address[] memory vaultStrategies,
uint128 sharesToWithdraw,
bool withdrawAll,
FastWithdrawParams memory fastWithdrawParams
)
external
noMidReallocation
verifyStrategies(vaultStrategies)
redeemVaultStrategiesModifier(vaultStrategies)
noReallocation
redeemUserModifier
updateRewards
{
sharesToWithdraw = _withdrawShares(sharesToWithdraw, withdrawAll);
uint256 vaultShareProportion = _getVaultShareProportion(sharesToWithdraw);
totalShares -= sharesToWithdraw;
uint128[] memory strategyRemovedShares = spool.removeShares(vaultStrategies, vaultShareProportion);
uint256 proportionateDeposit = _getUserProportionateDeposit(sharesToWithdraw);
// transfer removed shares to fast withdraw contract
fastWithdraw.transferShares(
vaultStrategies,
strategyRemovedShares,
proportionateDeposit,
msg.sender,
fastWithdrawParams
);
emit WithdrawFast(msg.sender, sharesToWithdraw);
}
/**
* @dev Updates storage values according to shares withdrawn.
* If `withdrawAll` is true, all shares are removed from the users
* @param sharesToWithdraw Amount of shares to withdraw
* @param withdrawAll Withdraw all user shares
*/
function _withdrawShares(uint128 sharesToWithdraw, bool withdrawAll) private returns(uint128) {
User storage user = users[msg.sender];
uint128 userShares = user.shares;
uint128 userActiveInstantDeposit = user.instantDeposit;
// Substract the not processed instant deposit
// This way we don't consider the deposit that was not yet processed by the DHW
// when calculating amount of it withdrawn
LastIndexInteracted memory userIndexInteracted = userLastInteractions[msg.sender];
if (userIndexInteracted.index1 > 0) {
userActiveInstantDeposit -= userIndexAction[msg.sender][userIndexInteracted.index1].depositAmount;
// also check if user second index has pending actions
if (userIndexInteracted.index2 > 0) {
userActiveInstantDeposit -= userIndexAction[msg.sender][userIndexInteracted.index2].depositAmount;
}
}
// check if withdraw all flag was set or user requested
// withdraw of all shares in `sharesToWithdraw`
if (withdrawAll || (userShares > 0 && userShares == sharesToWithdraw)) {
sharesToWithdraw = userShares;
// set user shares to 0
user.shares = 0;
// substract all the users instant deposit processed till now
// substract the same amount from vault total instand deposit value
totalInstantDeposit -= userActiveInstantDeposit;
user.instantDeposit -= userActiveInstantDeposit;
} else {
require(
userShares >= sharesToWithdraw &&
sharesToWithdraw > 0,
"WSH"
);
// if we didnt withdraw all calculate the proportion of
// the instant deposit to substract it from the user and vault amounts
uint128 instantDepositWithdrawn = _getProportion128(userActiveInstantDeposit, sharesToWithdraw, userShares);
totalInstantDeposit -= instantDepositWithdrawn;
user.instantDeposit -= instantDepositWithdrawn;
// susrtact withdrawn shares from the user
// NOTE: vault shares will be substracted when the at the redeem
// for the current active index is processed. This way we substract it
// only once for all the users.
unchecked {
user.shares = userShares - sharesToWithdraw;
}
}
return sharesToWithdraw;
}
/**
* @notice Calculates user proportionate deposit when withdrawing and updated user deposit storage
* @dev Checks user index action to see if user already has some withdrawn shares
* pending to be processed.
* Called when performing the fast withdraw
*
* @param sharesToWithdraw shares amount to withdraw
*
* @return User deposit amount proportionate to the amount of shares being withdrawn
*/
function _getUserProportionateDeposit(uint128 sharesToWithdraw) private returns(uint256) {
User storage user = users[msg.sender];
LastIndexInteracted memory userIndexInteracted = userLastInteractions[msg.sender];
uint128 proportionateDeposit;
uint128 sharesAtWithdrawal = user.shares + sharesToWithdraw;
if (userIndexInteracted.index1 > 0) {
sharesAtWithdrawal += userIndexAction[msg.sender][userIndexInteracted.index1].withdrawShares;
if (userIndexInteracted.index2 > 0) {
sharesAtWithdrawal += userIndexAction[msg.sender][userIndexInteracted.index2].withdrawShares;
}
}
if (sharesAtWithdrawal > sharesToWithdraw) {
uint128 userTotalDeposit = user.activeDeposit;
proportionateDeposit = _getProportion128(userTotalDeposit, sharesToWithdraw, sharesAtWithdrawal);
user.activeDeposit = userTotalDeposit - proportionateDeposit;
} else {
proportionateDeposit = user.activeDeposit;
user.activeDeposit = 0;
}
return proportionateDeposit;
}
function _withdrawFromStrats(address[] memory vaultStrategies, uint128 totalSharesToWithdraw, uint256 activeGlobalIndex) private {
uint256 vaultShareProportion = _getVaultShareProportion(totalSharesToWithdraw);
for (uint256 i; i < vaultStrategies.length; i++) {
spool.withdraw(vaultStrategies[i], vaultShareProportion, activeGlobalIndex);
}
}
/* ========== CLAIM ========== */
/**
* @notice Allows a user to claim their debt from the vault after withdrawn shares were processed.
*
* @dev
* Fee is taken from the profit
* Perform redeem on user demand
*
* Emits a {DebtClaim} event indicating the debt the user claimed.
*
* Requirements:
*
* - if `doRedeemVault` is true, the provided strategies must be valid
* - the caller must have a non-zero debt owed
* - the system should not be paused (if doRedeemVault)
*
* @param doRedeemVault flag, to execute redeem for the vault (synchronize deposit/withdrawals with the system)
* @param vaultStrategies vault stratigies
* @param doRedeemUser flag, to execute redeem for the caller
*
* @return claimAmount amount of underlying asset, claimed by the caller
*/
function claim(
bool doRedeemVault,
address[] memory vaultStrategies,
bool doRedeemUser
) external returns (uint128 claimAmount) {
User storage user = users[msg.sender];
if (doRedeemVault) {
_verifyStrategies(vaultStrategies);
_redeemVaultStrategies(vaultStrategies);
}
if (doRedeemUser) {
_redeemUser();
}
claimAmount = user.owed;
require(claimAmount > 0, "CA0");
user.owed = 0;
// Calculate profit and take fees
uint128 userWithdrawnDeposits = user.withdrawnDeposits;
if (claimAmount > userWithdrawnDeposits) {
user.withdrawnDeposits = 0;
uint128 profit = claimAmount - userWithdrawnDeposits;
uint128 feesPaid = _payFeesAndTransfer(profit);
// Substract fees paid from claim amount
claimAmount -= feesPaid;
} else {
user.withdrawnDeposits = userWithdrawnDeposits - claimAmount;
}
_underlying().safeTransfer(msg.sender, claimAmount);
emit Claimed(msg.sender, claimAmount);
}
/* ========== REDEEM ========== */
/**
* @notice Redeem vault and user deposit and withdrawals
*
* Requirements:
*
* - the provided strategies must be valid
*
* @param vaultStrategies vault stratigies
*/
function redeemVaultAndUser(address[] memory vaultStrategies)
external
verifyStrategies(vaultStrategies)
redeemVaultStrategiesModifier(vaultStrategies)
redeemUserModifier
{}
/**
* @notice Redeem vault and user and return the user state
* @dev Intended to be called as a static call for view purposes
*
* Requirements:
*
* - the provided strategies must be valid
*
* @param vaultStrategies vault strategies
*
* @return userShares current user shares
* @return activeDeposit user active deposit (already processed by the DHW)
* @return userOwed user total unclaimed amount
* @return userWithdrawnDeposits unclaimed withdrawn deposit amount
* @return userTotalUnderlying current user total underlying
* @return pendingDeposit1 pending user deposit for the next index
* @return pendingWithdrawalShares1 pending user withdrawal shares for the next index
* @return pendingDeposit2 pending user deposit for the index after the next one
* @return pendingWithdrawalShares2 pending user withdrawal shares for the after the next one
*/
function getUpdatedUser(address[] memory vaultStrategies)
external
returns (
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
uint256
)
{
(uint256 totalUnderlying, , , , , ) = getUpdatedVault(vaultStrategies);
_redeemUser();
User storage user = users[msg.sender];
uint256 userTotalUnderlying;
if (totalShares > 0 && user.shares > 0) {
userTotalUnderlying = (totalUnderlying * user.shares) / totalShares;
}
IndexAction storage indexAction1 = userIndexAction[msg.sender][userLastInteractions[msg.sender].index1];
IndexAction storage indexAction2 = userIndexAction[msg.sender][userLastInteractions[msg.sender].index2];
return (
user.shares,
user.activeDeposit, // amount of user deposited underlying token
user.owed, // underlying token claimable amount
user.withdrawnDeposits, // underlying token withdrawn amount
userTotalUnderlying,
indexAction1.depositAmount,
indexAction1.withdrawShares,
indexAction2.depositAmount,
indexAction2.withdrawShares
);
}
/**
* @notice Redeem vault strategy deposits and withdrawals after do hard work.
*
* Requirements:
*
* - the provided strategies must be valid
*
* @param vaultStrategies vault strategies
*/
function redeemVaultStrategies(address[] memory vaultStrategies)
external
verifyStrategies(vaultStrategies)
redeemVaultStrategiesModifier(vaultStrategies)
{}
/**
* @notice Redeem vault strategy deposits and withdrawals after do hard work.
* @dev Intended to be called as a static call for view purposes
*
* Requirements:
*
* - the provided strategies must be valid
*
* @param vaultStrategies vault strategies
*
* @return totalUnderlying total vault underlying
* @return totalShares total vault shares
* @return pendingDeposit1 pending vault deposit for the next index
* @return pendingWithdrawalShares1 pending vault withdrawal shares for the next index
* @return pendingDeposit2 pending vault deposit for the index after the next one
* @return pendingWithdrawalShares2 pending vault withdrawal shares for the after the next one
*/
function getUpdatedVault(address[] memory vaultStrategies)
public
verifyStrategies(vaultStrategies)
redeemVaultStrategiesModifier(vaultStrategies)
returns (
uint256,
uint256,
uint256,
uint256,
uint256,
uint256
)
{
uint256 totalUnderlying = 0;
for (uint256 i; i < vaultStrategies.length; i++) {
totalUnderlying += spool.getUnderlying(vaultStrategies[i]);
}
IndexAction storage indexAction1 = vaultIndexAction[lastIndexInteracted.index1];
IndexAction storage indexAction2 = vaultIndexAction[lastIndexInteracted.index2];
return (
totalUnderlying,
totalShares,
indexAction1.depositAmount,
indexAction1.withdrawShares,
indexAction2.depositAmount,
indexAction2.withdrawShares
);
}
/**
* @notice Redeem user deposits and withdrawals
*
* @dev Can only redeem user up to last index vault has redeemed
*/
function redeemUser()
external
{
_redeemUser();
}
/* ========== STRATEGY REMOVED ========== */
/**
* @notice Notify a vault a strategy was removed from the Spool system
* @dev
* This can be called by anyone after a strategy has been removed from the system.
* After the removal of the strategy that the vault contains, all actions
* calling central Spool contract will revert. This function must be called,
* to remove the strategy from the vault and update the strategy hash according
* to the new strategy array.
*
* Requirements:
*
* - The Spool system must finish reallocation if it's in progress
* - the provided strategies must be valid
* - The strategy must belong to this vault
* - The strategy must be removed from the system
*
* @param vaultStrategies Array of current vault strategies (including the removed one)
* @param i Index of the removed strategy in the `vaultStrategies`
*/
function notifyStrategyRemoved(
address[] memory vaultStrategies,
uint256 i
)
external
reallocationFinished
verifyStrategies(vaultStrategies)
hasStrategies(vaultStrategies)
redeemVaultStrategiesModifier(vaultStrategies)
{
require(
i < vaultStrategies.length &&
!controller.validStrategy(vaultStrategies[i]),
"BSTR"
);
uint256 lastElement = vaultStrategies.length - 1;
address[] memory newStrategies = new address[](lastElement);
if (lastElement > 0) {
for (uint256 j; j < lastElement; j++) {
newStrategies[j] = vaultStrategies[j];
}
if (i < lastElement) {
newStrategies[i] = vaultStrategies[lastElement];
}
uint256 _proportions = proportions;
uint256 proportionsLeft = FULL_PERCENT - _proportions.get14BitUintByIndex(i);
if (lastElement > 1 && proportionsLeft > 0) {
if (i == lastElement) {
_proportions = _proportions.reset14BitUintByIndex(i);
} else {
uint256 lastProportion = _proportions.get14BitUintByIndex(lastElement);
_proportions = _proportions.reset14BitUintByIndex(i);
_proportions = _proportions.set14BitUintByIndex(i, lastProportion);
}
uint256 newProportions;
uint256 lastNewElement = lastElement - 1;
uint256 newProportionsLeft = FULL_PERCENT;
for (uint256 j; j < lastNewElement; j++) {
uint256 propJ = _proportions.get14BitUintByIndex(j);
propJ = (propJ * FULL_PERCENT) / proportionsLeft;
newProportions = newProportions.set14BitUintByIndex(j, propJ);
newProportionsLeft -= propJ;
}
newProportions = newProportions.set14BitUintByIndex(lastNewElement, newProportionsLeft);
proportions = newProportions;
} else {
proportions = FULL_PERCENT;
}
} else {
proportions = 0;
}
_updateStrategiesHash(newStrategies);
emit StrategyRemoved(i, vaultStrategies[i]);
}
/* ========== PRIVATE FUNCTIONS ========== */
/**
* @notice Throws if given array of strategies is empty
*/
function _hasStrategies(address[] memory vaultStrategies) private pure {
require(vaultStrategies.length > 0, "NST");
}
/* ========== MODIFIERS ========== */
/**
* @notice Throws if given array of strategies is empty
*/
modifier hasStrategies(address[] memory vaultStrategies) {
_hasStrategies(vaultStrategies);
_;
}
/**
* @notice Revert if reallocation is not finished for this vault
*/
modifier reallocationFinished() {
require(
!_isVaultReallocating() ||
reallocationIndex <= spool.getCompletedGlobalIndex(),
"RNF"
);
_;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @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 ReentrancyGuard {
// 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 = 0;
uint256 private constant _ENTERED = 1;
uint256 private _status;
constructor() {
_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() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Address.sol)
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 128 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../external/@openzeppelin/token/ERC20/IERC20.sol";
interface IController {
/* ========== FUNCTIONS ========== */
function strategies(uint256 i) external view returns (address);
function validStrategy(address strategy) external view returns (bool);
function validVault(address vault) external view returns (bool);
function getStrategiesCount() external view returns(uint8);
function supportedUnderlying(IERC20 underlying)
external
view
returns (bool);
function getAllStrategies() external view returns (address[] memory);
function verifyStrategies(address[] calldata _strategies) external view;
function transferToSpool(
address transferFrom,
uint256 amount
) external;
function checkPaused() external view;
/* ========== EVENTS ========== */
event EmergencyWithdrawStrategy(address indexed strategy);
event EmergencyRecipientUpdated(address indexed recipient);
event EmergencyWithdrawerUpdated(address indexed withdrawer, bool set);
event PauserUpdated(address indexed user, bool set);
event UnpauserUpdated(address indexed user, bool set);
event VaultCreated(address indexed vault, address underlying, address[] strategies, uint256[] proportions,
uint16 vaultFee, address riskProvider, int8 riskTolerance);
event StrategyAdded(address strategy);
event StrategyRemoved(address strategy);
event VaultInvalid(address vault);
event DisableStrategy(address strategy);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "./ISwapData.sol";
struct FastWithdrawParams {
bool doExecuteWithdraw;
uint256[][] slippages;
SwapData[][] swapData;
}
interface IFastWithdraw {
function transferShares(
address[] calldata vaultStrategies,
uint128[] calldata sharesWithdrawn,
uint256 proportionateDeposit,
address user,
FastWithdrawParams calldata fastWithdrawParams
) external;
/* ========== EVENTS ========== */
event StrategyWithdrawn(address indexed user, address indexed vault, address indexed strategy);
event UserSharesSaved(address indexed user, address indexed vault);
event FastWithdrawExecuted(address indexed user, address indexed vault, uint256 totalWithdrawn);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../external/@openzeppelin/token/ERC20/IERC20.sol";
interface IFeeHandler {
function payFees(
IERC20 underlying,
uint256 profit,
address riskProvider,
address vaultOwner,
uint16 vaultFee
) external returns (uint256 feesPaid);
function setRiskProviderFee(address riskProvider, uint16 fee) external;
/* ========== EVENTS ========== */
event FeesPaid(address indexed vault, uint profit, uint ecosystemCollected, uint treasuryCollected, uint riskProviderColected, uint vaultFeeCollected);
event RiskProviderFeeUpdated(address indexed riskProvider, uint indexed fee);
event EcosystemFeeUpdated(uint indexed fee);
event TreasuryFeeUpdated(uint indexed fee);
event EcosystemCollectorUpdated(address indexed collector);
event TreasuryCollectorUpdated(address indexed collector);
event FeeCollected(address indexed collector, IERC20 indexed underlying, uint amount);
event EcosystemFeeCollected(IERC20 indexed underlying, uint amount);
event TreasuryFeeCollected(IERC20 indexed underlying, uint amount);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "./spool/ISpoolExternal.sol";
import "./spool/ISpoolReallocation.sol";
import "./spool/ISpoolDoHardWork.sol";
import "./spool/ISpoolStrategy.sol";
import "./spool/ISpoolBase.sol";
/// @notice Utility Interface for central Spool implementation
interface ISpool is ISpoolExternal, ISpoolReallocation, ISpoolDoHardWork, ISpoolStrategy, ISpoolBase {}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
interface ISpoolOwner {
function isSpoolOwner(address user) external view returns(bool);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
/**
* @notice Strict holding information how to swap the asset
* @member slippage minumum output amount
* @member path swap path, first byte represents an action (e.g. Uniswap V2 custom swap), rest is swap specific path
*/
struct SwapData {
uint256 slippage; // min amount out
bytes path; // 1st byte is action, then path
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
interface ISpoolBase {
/* ========== FUNCTIONS ========== */
function getCompletedGlobalIndex() external view returns(uint24);
function getActiveGlobalIndex() external view returns(uint24);
function isMidReallocation() external view returns (bool);
/* ========== EVENTS ========== */
event ReallocationTableUpdated(
uint24 indexed index,
bytes32 reallocationTableHash
);
event ReallocationTableUpdatedWithTable(
uint24 indexed index,
bytes32 reallocationTableHash,
uint256[][] reallocationTable
);
event DoHardWorkCompleted(uint24 indexed index);
event SetAllocationProvider(address actor, bool isAllocationProvider);
event SetIsDoHardWorker(address actor, bool isDoHardWorker);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
interface ISpoolDoHardWork {
/* ========== EVENTS ========== */
event DoHardWorkStrategyCompleted(address indexed strat, uint256 indexed index);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../ISwapData.sol";
interface ISpoolExternal {
/* ========== FUNCTIONS ========== */
function deposit(address strategy, uint128 amount, uint256 index) external;
function withdraw(address strategy, uint256 vaultProportion, uint256 index) external;
function fastWithdrawStrat(address strat, address underlying, uint256 shares, uint256[] calldata slippages, SwapData[] calldata swapData) external returns(uint128);
function redeem(address strat, uint256 index) external returns (uint128, uint128);
function redeemUnderlying(uint128 amount) external;
function redeemReallocation(address[] calldata vaultStrategies, uint256 depositProportions, uint256 index) external;
function removeShares(address[] calldata vaultStrategies, uint256 vaultProportion) external returns(uint128[] memory);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
interface ISpoolReallocation {
event StartReallocation(uint24 indexed index);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
interface ISpoolStrategy {
/* ========== FUNCTIONS ========== */
function getUnderlying(address strat) external returns (uint128);
function getVaultTotalUnderlyingAtIndex(address strat, uint256 index) external view returns(uint128);
function addStrategy(address strat) external;
function disableStrategy(address strategy, bool skipDisable) external;
function runDisableStrategy(address strategy) external;
function emergencyWithdraw(
address strat,
address withdrawRecipient,
uint256[] calldata data
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../../external/@openzeppelin/token/ERC20/IERC20.sol";
interface IRewardDrip {
/* ========== STRUCTS ========== */
// The reward configuration struct, containing all the necessary data of a typical Synthetix StakingReward contract
struct RewardConfiguration {
uint32 rewardsDuration;
uint32 periodFinish;
uint192 rewardRate; // rewards per second multiplied by accuracy
uint32 lastUpdateTime;
uint224 rewardPerTokenStored;
mapping(address => uint256) userRewardPerTokenPaid;
mapping(address => uint256) rewards;
}
/* ========== FUNCTIONS ========== */
function getActiveRewards(address account) external;
function tokenBlacklist(IERC20 token) view external returns(bool);
/* ========== EVENTS ========== */
event RewardPaid(IERC20 token, address indexed user, uint256 reward);
event RewardAdded(IERC20 indexed token, uint256 amount, uint256 duration);
event RewardExtended(IERC20 indexed token, uint256 amount, uint256 leftover, uint256 duration, uint32 periodFinish);
event RewardRemoved(IERC20 indexed token);
event PeriodFinishUpdated(IERC20 indexed token, uint32 periodFinish);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "./IVaultDetails.sol";
interface IVaultBase {
/* ========== FUNCTIONS ========== */
function initialize(VaultInitializable calldata vaultInitializable) external;
/* ========== STRUCTS ========== */
struct User {
uint128 instantDeposit; // used for calculating rewards
uint128 activeDeposit; // users deposit after deposit process and claim
uint128 owed; // users owed underlying amount after withdraw has been processed and claimed
uint128 withdrawnDeposits; // users withdrawn deposit, used to calculate performance fees
uint128 shares; // users shares after deposit process and claim
}
/* ========== EVENTS ========== */
event Claimed(address indexed member, uint256 claimAmount);
event Deposit(address indexed member, uint256 indexed index, uint256 amount);
event Withdraw(address indexed member, uint256 indexed index, uint256 shares);
event WithdrawFast(address indexed member, uint256 shares);
event StrategyRemoved(uint256 i, address strategy);
event TransferVaultOwner(address owner);
event LowerVaultFee(uint16 fee);
event UpdateName(string name);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
struct VaultDetails {
address underlying;
address[] strategies;
uint256[] proportions;
address creator;
uint16 vaultFee;
address riskProvider;
int8 riskTolerance;
string name;
}
struct VaultInitializable {
string name;
address owner;
uint16 fee;
address[] strategies;
uint256[] proportions;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../../external/@openzeppelin/token/ERC20/IERC20.sol";
struct VaultImmutables {
IERC20 underlying;
address riskProvider;
int8 riskTolerance;
}
interface IVaultImmutable {
/* ========== FUNCTIONS ========== */
function underlying() external view returns (IERC20);
function riskProvider() external view returns (address);
function riskTolerance() external view returns (int8);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
interface IVaultIndexActions {
/* ========== STRUCTS ========== */
struct IndexAction {
uint128 depositAmount;
uint128 withdrawShares;
}
struct LastIndexInteracted {
uint128 index1;
uint128 index2;
}
struct Redeem {
uint128 depositShares;
uint128 withdrawnAmount;
}
/* ========== EVENTS ========== */
event VaultRedeem(uint indexed globalIndex);
event UserRedeem(address indexed member, uint indexed globalIndex);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
interface IVaultRestricted {
/* ========== FUNCTIONS ========== */
function reallocate(
address[] calldata vaultStrategies,
uint256 newVaultProportions,
uint256 finishedIndex,
uint24 activeIndex
) external returns (uint256[] memory, uint256);
function payFees(uint256 profit) external returns (uint256 feesPaid);
/* ========== EVENTS ========== */
event Reallocate(uint24 indexed index, uint256 newProportions);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
library Bitwise {
function get8BitUintByIndex(uint256 bitwiseData, uint256 i) internal pure returns(uint256) {
return (bitwiseData >> (8 * i)) & type(uint8).max;
}
// 14 bits is used for strategy proportions in a vault as FULL_PERCENT is 10_000
function get14BitUintByIndex(uint256 bitwiseData, uint256 i) internal pure returns(uint256) {
return (bitwiseData >> (14 * i)) & (16_383); // 16.383 is 2^14 - 1
}
function set14BitUintByIndex(uint256 bitwiseData, uint256 i, uint256 num14bit) internal pure returns(uint256) {
return bitwiseData + (num14bit << (14 * i));
}
function reset14BitUintByIndex(uint256 bitwiseData, uint256 i) internal pure returns(uint256) {
return bitwiseData & (~(16_383 << (14 * i)));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
/**
* @notice Library to provide utils for hashing and hash compatison of Spool related data
*/
library Hash {
function hashReallocationTable(uint256[][] memory reallocationTable) internal pure returns(bytes32) {
return keccak256(abi.encode(reallocationTable));
}
function hashStrategies(address[] memory strategies) internal pure returns(bytes32) {
return keccak256(abi.encodePacked(strategies));
}
function sameStrategies(address[] memory strategies1, address[] memory strategies2) internal pure returns(bool) {
return hashStrategies(strategies1) == hashStrategies(strategies2);
}
function sameStrategies(address[] memory strategies, bytes32 strategiesHash) internal pure returns(bool) {
return hashStrategies(strategies) == strategiesHash;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import "../external/@openzeppelin/utils/SafeCast.sol";
/**
* @notice A collection of custom math ustils used throughout the system
*/
library Math {
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? b : a;
}
function getProportion128(uint256 mul1, uint256 mul2, uint256 div) internal pure returns (uint128) {
return SafeCast.toUint128(((mul1 * mul2) / div));
}
function getProportion128Unchecked(uint256 mul1, uint256 mul2, uint256 div) internal pure returns (uint128) {
unchecked {
return uint128((mul1 * mul2) / div);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../external/@openzeppelin/token/ERC20/IERC20.sol";
/// @title Common Spool contracts constants
abstract contract BaseConstants {
/// @dev 2 digits precision
uint256 internal constant FULL_PERCENT = 100_00;
/// @dev Accuracy when doing shares arithmetics
uint256 internal constant ACCURACY = 10**30;
}
/// @title Contains USDC token related values
abstract contract USDC {
/// @notice USDC token contract address
IERC20 internal constant USDC_ADDRESS = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../interfaces/ISpoolOwner.sol";
/// @title Logic to help check whether the caller is the Spool owner
abstract contract SpoolOwnable {
/// @notice Contract that checks if address is Spool owner
ISpoolOwner internal immutable spoolOwner;
/**
* @notice Sets correct initial values
* @param _spoolOwner Spool owner contract address
*/
constructor(ISpoolOwner _spoolOwner) {
require(
address(_spoolOwner) != address(0),
"SpoolOwnable::constructor: Spool owner contract address cannot be 0"
);
spoolOwner = _spoolOwner;
}
/**
* @notice Checks if caller is Spool owner
* @return True if caller is Spool owner, false otherwise
*/
function isSpoolOwner() internal view returns(bool) {
return spoolOwner.isSpoolOwner(msg.sender);
}
/// @notice Checks and throws if caller is not Spool owner
function _onlyOwner() private view {
require(isSpoolOwner(), "SpoolOwnable::onlyOwner: Caller is not the Spool owner");
}
/// @notice Checks and throws if caller is not Spool owner
modifier onlyOwner() {
_onlyOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import "../interfaces/IController.sol";
/// @title Facilitates checking if the system is paused or not
abstract contract SpoolPausable {
/* ========== STATE VARIABLES ========== */
/// @notice The controller contract that is consulted for a strategy's and vault's validity
IController public immutable controller;
/**
* @notice Sets initial values
* @param _controller Controller contract address
*/
constructor(IController _controller) {
require(
address(_controller) != address(0),
"SpoolPausable::constructor: Controller contract address cannot be 0"
);
controller = _controller;
}
/* ========== MODIFIERS ========== */
/// @notice Throws if system is paused
modifier systemNotPaused() {
controller.checkPaused();
_;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../interfaces/vault/IRewardDrip.sol";
import "./VaultBase.sol";
import "../external/@openzeppelin/utils/SafeCast.sol";
import "../external/@openzeppelin/security/ReentrancyGuard.sol";
import "../libraries/Math.sol";
/**
* @notice Implementation of the {IRewardDrip} interface.
*
* @dev
* An adaptation of the Synthetix StakingRewards contract to support multiple tokens:
*
* https://github.com/Synthetixio/synthetix/blob/develop/contracts/StakingRewards.sol
*
* Instead of storing the values of the StakingRewards contract at the contract level,
* they are stored in a struct that is mapped to depending on the reward token instead.
*/
abstract contract RewardDrip is IRewardDrip, ReentrancyGuard, VaultBase {
using SafeERC20 for IERC20;
/* ========== CONSTANTS ========== */
/// @notice Multiplier used when dealing reward calculations
uint256 constant private REWARD_ACCURACY = 1e18;
/* ========== STATE VARIABLES ========== */
/// @notice All reward tokens supported by the contract
mapping(uint256 => IERC20) public rewardTokens;
/// @notice Vault reward token incentive configuration
mapping(IERC20 => RewardConfiguration) public rewardConfiguration;
/// @notice Blacklisted force-removed tokens
mapping(IERC20 => bool) public override tokenBlacklist;
/* ========== VIEWS ========== */
function lastTimeRewardApplicable(IERC20 token)
public
view
returns (uint32)
{
return uint32(Math.min(block.timestamp, rewardConfiguration[token].periodFinish));
}
function rewardPerToken(IERC20 token) public view returns (uint224) {
RewardConfiguration storage config = rewardConfiguration[token];
if (totalInstantDeposit == 0)
return config.rewardPerTokenStored;
uint256 timeDelta = lastTimeRewardApplicable(token) - config.lastUpdateTime;
if (timeDelta == 0)
return config.rewardPerTokenStored;
return
SafeCast.toUint224(
config.rewardPerTokenStored +
((timeDelta
* config.rewardRate)
/ totalInstantDeposit)
);
}
function earned(IERC20 token, address account)
public
view
returns (uint256)
{
RewardConfiguration storage config = rewardConfiguration[token];
uint256 userShares = users[account].instantDeposit;
if (userShares == 0)
return config.rewards[account];
uint256 userRewardPerTokenPaid = config.userRewardPerTokenPaid[account];
return
((userShares *
(rewardPerToken(token) - userRewardPerTokenPaid))
/ REWARD_ACCURACY)
+ config.rewards[account];
}
function getRewardForDuration(IERC20 token)
external
view
returns (uint256)
{
RewardConfiguration storage config = rewardConfiguration[token];
return uint256(config.rewardRate) * config.rewardsDuration;
}
/* ========== MUTATIVE FUNCTIONS ========== */
function getRewards(IERC20[] memory tokens) external nonReentrant {
for (uint256 i; i < tokens.length; i++) {
_getReward(tokens[i], msg.sender);
}
}
function getActiveRewards(address account) external override onlyController nonReentrant {
uint256 _rewardTokensCount = rewardTokensCount;
for (uint256 i; i < _rewardTokensCount; i++) {
_getReward(rewardTokens[i], account);
}
}
function _getReward(IERC20 token, address account)
internal
updateReward(token, account)
{
RewardConfiguration storage config = rewardConfiguration[token];
require(
config.rewardsDuration != 0,
"BTK"
);
uint256 reward = config.rewards[account];
if (reward > 0) {
config.rewards[account] = 0;
token.safeTransfer(account, reward);
emit RewardPaid(token, account, reward);
}
}
/* ========== RESTRICTED FUNCTIONS ========== */
/**
* @notice Allows a new token to be added to the reward system
*
* @dev
* Emits an {TokenAdded} event indicating the newly added reward token
* and configuration
*
* Requirements:
*
* - the caller must be the reward distributor
* - the reward duration must be non-zero
* - the token must not have already been added
*
*/
function addToken(
IERC20 token,
uint32 rewardsDuration,
uint256 reward
) external onlyVaultOwnerOrSpoolOwner exceptUnderlying(token) {
RewardConfiguration storage config = rewardConfiguration[token];
require(!tokenBlacklist[token], "TOBL");
require(
rewardsDuration != 0 &&
config.lastUpdateTime == 0,
"BCFG"
);
require(
rewardTokensCount <= 5,
"TMAX"
);
rewardTokens[rewardTokensCount] = token;
rewardTokensCount++;
config.rewardsDuration = rewardsDuration;
if (reward > 0) {
_notifyRewardAmount(token, reward);
}
}
function notifyRewardAmount(IERC20 token, uint256 reward, uint32 rewardsDuration)
external
onlyVaultOwnerOrSpoolOwner
{
rewardConfiguration[token].rewardsDuration = rewardsDuration;
_notifyRewardAmount(token, reward);
}
function _notifyRewardAmount(IERC20 token, uint256 reward)
private
updateReward(token, address(0))
{
RewardConfiguration storage config = rewardConfiguration[token];
require(
config.rewardPerTokenStored + (reward * REWARD_ACCURACY) <= type(uint192).max,
"RTB"
);
token.safeTransferFrom(msg.sender, address(this), reward);
uint32 newPeriodFinish = uint32(block.timestamp) + config.rewardsDuration;
if (block.timestamp >= config.periodFinish) {
config.rewardRate = SafeCast.toUint192((reward * REWARD_ACCURACY) / config.rewardsDuration);
emit RewardAdded(token, reward, config.rewardsDuration);
} else {
// If extending or adding additional rewards,
// cannot set new finish time to be less than previously configured
require(config.periodFinish <= newPeriodFinish, "PFS");
uint256 remaining = config.periodFinish - block.timestamp;
uint256 leftover = remaining * config.rewardRate;
uint192 newRewardRate = SafeCast.toUint192((reward * REWARD_ACCURACY + leftover) / config.rewardsDuration);
require(
newRewardRate >= config.rewardRate,
"LRR"
);
config.rewardRate = newRewardRate;
emit RewardExtended(token, reward, leftover, config.rewardsDuration, newPeriodFinish);
}
config.lastUpdateTime = uint32(block.timestamp);
config.periodFinish = newPeriodFinish;
}
// End rewards emission earlier
function updatePeriodFinish(IERC20 token, uint32 timestamp)
external
onlyOwner
updateReward(token, address(0))
{
if (rewardConfiguration[token].lastUpdateTime > timestamp) {
rewardConfiguration[token].periodFinish = rewardConfiguration[token].lastUpdateTime;
} else {
rewardConfiguration[token].periodFinish = timestamp;
}
emit PeriodFinishUpdated(token, rewardConfiguration[token].periodFinish);
}
/**
* @notice Claim reward tokens
* @dev
* This is meant to be an emergency function to claim reward tokens.
* Users that have not claimed yet will not be able to claim as
* the rewards will be removed.
*
* Requirements:
*
* - the caller must be Spool DAO
* - cannot claim vault underlying token
* - cannot only execute if the reward finished
*
* @param token Token address to remove
* @param amount Amount of tokens to claim
*/
function claimFinishedRewards(IERC20 token, uint256 amount) external onlyOwner exceptUnderlying(token) onlyFinished(token) {
token.safeTransfer(msg.sender, amount);
}
/**
* @notice Force remove reward from vault rewards configuration.
* @dev This is meant to be an emergency function if a reward token breaks.
*
* Requirements:
*
* - the caller must be Spool DAO
*
* @param token Token address to remove
*/
function forceRemoveReward(IERC20 token) external onlyOwner {
tokenBlacklist[token] = true;
_removeReward(token);
delete rewardConfiguration[token];
}
/**
* @notice Remove reward from vault rewards configuration.
* @dev
* Used to sanitize vault and save on gas, after the reward has ended.
* Users will be able to claim rewards
*
* Requirements:
*
* - the caller must be the spool owner or Spool DAO
* - cannot claim vault underlying token
* - cannot only execute if the reward finished
*
* @param token Token address to remove
*/
function removeReward(IERC20 token)
external
onlyVaultOwnerOrSpoolOwner
onlyFinished(token)
updateReward(token, address(0))
{
_removeReward(token);
}
/* ========== PRIVATE FUNCTIONS ========== */
/**
* @notice Syncs rewards across all tokens of the system
*
* This function is meant to be invoked every time the instant deposit
* of a user changes.
*/
function _updateRewards(address account) private {
uint256 _rewardTokensCount = rewardTokensCount;
for (uint256 i; i < _rewardTokensCount; i++)
_updateReward(rewardTokens[i], account);
}
function _updateReward(IERC20 token, address account) private {
RewardConfiguration storage config = rewardConfiguration[token];
config.rewardPerTokenStored = rewardPerToken(token);
config.lastUpdateTime = lastTimeRewardApplicable(token);
if (account != address(0)) {
config.rewards[account] = earned(token, account);
config.userRewardPerTokenPaid[account] = config
.rewardPerTokenStored;
}
}
function _removeReward(IERC20 token) private {
uint256 _rewardTokensCount = rewardTokensCount;
for (uint256 i; i < _rewardTokensCount; i++) {
if (rewardTokens[i] == token) {
rewardTokens[i] = rewardTokens[_rewardTokensCount - 1];
delete rewardTokens[_rewardTokensCount - 1];
rewardTokensCount--;
emit RewardRemoved(token);
break;
}
}
}
function _exceptUnderlying(IERC20 token) private view {
require(
token != _underlying(),
"NUT"
);
}
function _onlyFinished(IERC20 token) private view {
require(
block.timestamp > rewardConfiguration[token].periodFinish,
"RNF"
);
}
/**
* @notice Ensures that the caller is the controller
*/
function _onlyController() private view {
require(
msg.sender == address(controller),
"OCTRL"
);
}
/* ========== MODIFIERS ========== */
modifier updateReward(IERC20 token, address account) {
_updateReward(token, account);
_;
}
modifier updateRewards() {
_updateRewards(msg.sender);
_;
}
modifier exceptUnderlying(IERC20 token) {
_exceptUnderlying(token);
_;
}
modifier onlyFinished(IERC20 token) {
_onlyFinished(token);
_;
}
/**
* @notice Throws if called by anyone else other than the controller
*/
modifier onlyController() {
_onlyController();
_;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
// libraries
import "../external/@openzeppelin/token/ERC20/utils/SafeERC20.sol";
import "../external/@openzeppelin/utils/SafeCast.sol";
import "../libraries/Bitwise.sol";
import "../libraries/Hash.sol";
// extends
import "../interfaces/vault/IVaultBase.sol";
import "./VaultImmutable.sol";
import "../shared/SpoolOwnable.sol";
import "../shared/Constants.sol";
// other imports
import "../interfaces/vault/IVaultDetails.sol";
import "../interfaces/ISpool.sol";
import "../interfaces/IController.sol";
import "../interfaces/IFastWithdraw.sol";
import "../interfaces/IFeeHandler.sol";
import "../shared/SpoolPausable.sol";
/**
* @notice Implementation of the {IVaultBase} interface.
*
* @dev
* Vault base holds vault state variables and provides some of the common vault functions.
*/
abstract contract VaultBase is IVaultBase, VaultImmutable, SpoolOwnable, SpoolPausable, BaseConstants {
using Bitwise for uint256;
using SafeERC20 for IERC20;
/* ========== STATE VARIABLES ========== */
/// @notice The central Spool contract
ISpool internal immutable spool;
/// @notice The fast withdraw contract
IFastWithdraw internal immutable fastWithdraw;
/// @notice The fee handler contract
IFeeHandler internal immutable feeHandler;
/// @notice Boolean signaling if the contract was initialized yet
bool private _initialized;
/// @notice The owner of the vault, also the vault fee recipient
address public vaultOwner;
/// @notice Vault owner fee
uint16 public vaultFee;
/// @notice The name of the vault
string public name;
/// @notice The total shares of a vault
uint128 public totalShares;
/// @notice Total instant deposit, used to calculate vault reward incentives
uint128 public totalInstantDeposit;
/// @notice The proportions of each strategy when depositing
/// @dev Proportions are 14bits each, and the add up to FULL_PERCENT (10.000)
uint256 public proportions;
/// @notice Proportions to deposit after reallocation withdraw amount is claimed
uint256 internal depositProportions;
/// @notice Hash of the strategies list
bytes32 public strategiesHash;
/// @notice Number of vault incentivized tokens
uint8 public rewardTokensCount;
/// @notice Data if vault and at what index vault is reallocating
uint24 public reallocationIndex;
/// @notice User vault state values
mapping(address => User) public users;
/* ========== CONSTRUCTOR ========== */
/**
* @notice Sets the initial immutable values of the contract common for all vaults.
*
* @dev
* All values have been sanitized by the controller contract, meaning
* that no additional checks need to be applied here.
*
* @param _spool the spool implemenation
* @param _controller the controller implementation
* @param _fastWithdraw fast withdraw implementation
* @param _feeHandler fee handler implementation
*/
constructor(
ISpool _spool,
IController _controller,
IFastWithdraw _fastWithdraw,
IFeeHandler _feeHandler
)
SpoolPausable(_controller)
{
require(address(_spool) != address(0), "VaultBase::constructor: Spool address cannot be 0");
require(address(_fastWithdraw) != address(0), "VaultBase::constructor: FastWithdraw address cannot be 0");
require(address(_feeHandler) != address(0), "VaultBase::constructor: Fee Handler address cannot be 0");
spool = _spool;
fastWithdraw = _fastWithdraw;
feeHandler = _feeHandler;
}
/* ========== INITIALIZE ========== */
/**
* @notice Initializes state of the vault at proxy creation.
* @dev Called only once by vault factory after deploying a vault proxy.
* All values have been sanitized by the controller contract, meaning
* that no additional checks need to be applied here.
*
* @param vaultInitializable initial vault specific variables
*/
function initialize(
VaultInitializable memory vaultInitializable
) external override initializer {
vaultOwner = vaultInitializable.owner;
vaultFee = vaultInitializable.fee;
name = vaultInitializable.name;
proportions = _mapProportionsArrayToBits(vaultInitializable.proportions);
_updateStrategiesHash(vaultInitializable.strategies);
}
/* ========== VIEW FUNCTIONS ========== */
/**
* @notice Calculate and return proportion of passed parameters of 128 bit size
* @dev Calculates the value using in 256 bit space, later casts back to 128 bit
* Requirements:
*
* - the result can't be bigger than maximum 128 bits value
*
* @param mul1 first multiplication value
* @param mul2 second multiplication value
* @param div result division value
*
* @return 128 bit proportion result
*/
function _getProportion128(uint128 mul1, uint128 mul2, uint128 div) internal pure returns (uint128) {
return SafeCast.toUint128((uint256(mul1) * mul2) / div);
}
/* ========== RESTRICTED FUNCTIONS ========== */
/**
* @notice Transfer vault owner to another address.
*
* @param _vaultOwner new vault owner address
*
* Requirements:
*
* - the caller can only be the vault owner or Spool DAO
*/
function transferVaultOwner(address _vaultOwner) external onlyVaultOwnerOrSpoolOwner {
vaultOwner = _vaultOwner;
emit TransferVaultOwner(_vaultOwner);
}
/**
* @notice Set lower vault fee.
*
* @param _vaultFee new vault fee
*
* Requirements:
*
* - the caller can only be the vault owner
* - new vault fee must be lower than before
*/
function lowerVaultFee(uint16 _vaultFee) external {
require(
msg.sender == vaultOwner &&
_vaultFee < vaultFee,
"FEE"
);
vaultFee = _vaultFee;
emit LowerVaultFee(_vaultFee);
}
/**
* @notice Update the name of the vault.
*
* @param _name new vault name
*
* Requirements:
*
* - the caller can only be the Spool DAO
*/
function updateName(string memory _name) external onlyOwner {
name = _name;
emit UpdateName(_name);
}
// =========== DEPOSIT HELPERS ============ //
/**
* @notice Update instant deposit user and vault amounts
*
* @param amount deposited amount
*/
function _addInstantDeposit(uint128 amount) internal {
users[msg.sender].instantDeposit += amount;
totalInstantDeposit += amount;
}
/**
* @notice Get strategy deposit amount for the strategy
* @param _proportions Vault strategy proportions (14bit each)
* @param i index to get the proportion
* @param amount Total deposit amount
* @return strategyDepositAmount
*/
function _getStrategyDepositAmount(
uint256 _proportions,
uint256 i,
uint256 amount
) internal pure returns (uint128) {
return SafeCast.toUint128((_proportions.get14BitUintByIndex(i) * amount) / FULL_PERCENT);
}
/**
* @notice Transfers deposited underlying asset amount from user to spool contract.
* @dev Transfer happens from the vault or controller, defined by the user
*
* @param amount deposited amount
* @param fromVault flag indicating wether the transfer is intiafed from the vault or controller
*/
function _transferDepositToSpool(uint128 amount, bool fromVault) internal {
if (fromVault) {
_underlying().safeTransferFrom(msg.sender, address(spool), amount);
} else {
controller.transferToSpool(msg.sender, amount);
}
}
/* ========== WITHDRAW HELPERS ========== */
/**
* @notice Calculates proportions of shares relative to the total shares
* @dev Value has accuracy of `ACCURACY` which is 10^30
*
* @param sharesToWithdraw amount of shares
*
* @return total vault shares proportion
*/
function _getVaultShareProportion(uint128 sharesToWithdraw) internal view returns(uint256) {
return (ACCURACY * sharesToWithdraw) / totalShares;
}
// =========== PERFORMANCE FEES ============ //
/**
* @notice Pay fees to fee handler contract and transfer fee amount.
*
* @param profit Total profit made by the users
* @return feeSize Fee amount calculated from profit
*/
function _payFeesAndTransfer(uint256 profit) internal returns (uint128 feeSize) {
feeSize = SafeCast.toUint128(_payFees(profit));
_underlying().safeTransfer(address(feeHandler), feeSize);
}
/**
* @notice Call fee handler contract to pay fees, without transfering assets
* @dev Fee handler updates the fee storage slots and returns
*
* @param profit Total profit made by the users
* @return Fee amount calculated from profit
*/
function _payFees(uint256 profit) internal returns (uint256) {
return feeHandler.payFees(
_underlying(),
profit,
_riskProvider(),
vaultOwner,
vaultFee
);
}
// =========== STRATEGIIES ============ //
/**
* @notice Map vault strategy proportions array in one uint256 word.
*
* @dev Proportions sum up to `FULL_PERCENT` (10_000).
* There is maximum of 18 elements, and each takes maximum of 14bits.
*
* @param _proportions Vault strategy proportions array
* @return Mapped propportion 256 bit word format
*/
function _mapProportionsArrayToBits(uint256[] memory _proportions) internal pure returns (uint256) {
uint256 proportions14bit;
for (uint256 i = 0; i < _proportions.length; i++) {
proportions14bit = proportions14bit.set14BitUintByIndex(i, _proportions[i]);
}
return proportions14bit;
}
/**
* @dev Store vault strategy addresses array hash in `strategiesHash` storage
* @param vaultStrategies Array of strategy addresses
*/
function _updateStrategiesHash(address[] memory vaultStrategies) internal {
strategiesHash = Hash.hashStrategies(vaultStrategies);
}
/**
* @dev verify vault strategy addresses array against storage `strategiesHash`
* @param vaultStrategies Array of strategies to verify
*/
function _verifyStrategies(address[] memory vaultStrategies) internal view {
require(Hash.sameStrategies(vaultStrategies, strategiesHash), "VSH");
}
/* ========== PRIVATE FUNCTIONS ========== */
/**
* @notice Verify the caller is The vault owner or Spool DAO
*
* @dev
* Only callable from onlyVaultOwnerOrSpoolOwner modifier.
*
* Requirements:
*
* - msg.sender is the vault owner or Spool DAO
*/
function _onlyVaultOwnerOrSpoolOwner() private view {
require(
msg.sender == vaultOwner || isSpoolOwner(),
"OOD"
);
}
/**
* @notice Verify the caller is the spool contact
*
* @dev
* Only callable from onlySpool modifier.
*
* Requirements:
*
* - msg.sender is central spool contract
*/
function _onlySpool() private view {
require(address(spool) == msg.sender, "OSP");
}
/**
* @notice Verify caller is the spool contact
*
* @dev
* Only callable from onlyFastWithdraw modifier.
*
* Requirements:
*
* - caller is fast withdraw contract
*/
function _onlyFastWithdraw() private view {
require(address(fastWithdraw) == msg.sender, "OFW");
}
/**
* @notice Dissallow action if Spool reallocation already started
*/
function _noMidReallocation() private view {
require(!spool.isMidReallocation(), "NMR");
}
/* ========== MODIFIERS ========== */
/**
* @notice Ensures caller is vault owner or spool owner.
*/
modifier onlyVaultOwnerOrSpoolOwner() {
_onlyVaultOwnerOrSpoolOwner();
_;
}
/**
* @notice Ensures caller is central spool contract
*/
modifier onlySpool() {
_onlySpool();
_;
}
/**
* @notice Ensures caller is fast withdraw contract
*/
modifier onlyFastWithdraw() {
_onlyFastWithdraw();
_;
}
/**
* @notice Verifies given array of strategy addresses
*/
modifier verifyStrategies(address[] memory vaultStrategies) {
_verifyStrategies(vaultStrategies);
_;
}
/**
* @notice Ensures the system is not mid reallocation
*/
modifier noMidReallocation() {
_noMidReallocation();
_;
}
/**
* @notice Ensures the vault has not been initialized before
*/
modifier initializer() {
require(!_initialized, "AINT");
_;
_initialized = true;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../interfaces/vault/IVaultImmutable.sol";
/**
* @notice This contracts calls vault proxy that stores following
* properties as immutables.
*/
abstract contract VaultImmutable {
/* ========== FUNCTIONS ========== */
/**
* @dev Returns the underlying vault token from proxy address
* @return Underlying token contract
*/
function _underlying() internal view returns (IERC20) {
return IVaultImmutable(address(this)).underlying();
}
/**
* @dev Returns vaults risk provider from proxy address
* @return Risk provider contract
*/
function _riskProvider() internal view returns (address) {
return IVaultImmutable(address(this)).riskProvider();
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../interfaces/vault/IVaultIndexActions.sol";
import "./RewardDrip.sol";
/**
* @notice VaultIndexActions extends VaultBase and holds the logic to process index related data and actions.
*
* @dev
* Index functions are executed when state changes are performed, to synchronize to vault with central Spool contract.
*
* Index actions include:
* - Redeem vault: claiming vault shares and withdrawn amount when DHW is complete
* - Redeem user: claiming user deposit shares and/or withdrawn amount after vault claim has been processed
*/
abstract contract VaultIndexActions is IVaultIndexActions, RewardDrip {
using SafeERC20 for IERC20;
using Bitwise for uint256;
/* ========== CONSTANTS ========== */
/// @notice Value to multiply new deposit recieved to get the share amount
uint128 private constant SHARES_MULTIPLIER = 10**6;
/// @notice number of locked shares when initial shares are added
/// @dev This is done to prevent rounding errors and share manipulation
uint128 private constant INITIAL_SHARES_LOCKED = 10**11;
/// @notice minimum shares size to avoid loss of share due to computation precision
/// @dev If total shares go unders this value, new deposit is multiplied by the `SHARES_MULTIPLIER` again
uint256 private constant MIN_SHARES_FOR_ACCURACY = INITIAL_SHARES_LOCKED * 10;
/* ========== STATE VARIABLES ========== */
/// @notice Holds up to 2 global indexes vault last interacted at and havent been redeemed yet
/// @dev Second index can only be the next index of the first one
/// Second index is used if the do-hard-work is executed in 2 transactions and actions are executed in between
LastIndexInteracted public lastIndexInteracted;
/// @notice Maps all vault actions to the corresponding global index
mapping(uint256 => IndexAction) public vaultIndexAction;
/// @notice Maps user actions to the corresponding global index
mapping(address => mapping(uint256 => IndexAction)) public userIndexAction;
/// @notice Holds up to 2 global indexes users last interacted with, and havent been redeemed yet
mapping(address => LastIndexInteracted) public userLastInteractions;
/// @notice Global index to deposit and withdraw vault redeem
mapping(uint256 => Redeem) public redeems;
// =========== VIEW FUNCTIONS ============ //
/**
* @notice Checks and sets the "is reallocating" flag for given index
* @param index Index to check
* @return isReallocating True if vault is reallocating at this `index`
*/
function _isVaultReallocatingAtIndex(uint256 index) internal view returns (bool isReallocating) {
if (index == reallocationIndex) {
isReallocating = true;
}
}
/**
* @notice Check if the vault is set to reallocate
* @dev True if in the current index or the next one
* @return isReallocating True if vault is set to reallocate
*/
function _isVaultReallocating() internal view returns (bool isReallocating) {
if (reallocationIndex > 0) {
isReallocating = true;
}
}
// =========== VAULT REDEEM ============ //
/**
* @notice Redeem vault strategies after do hard work (DHW) has been completed
*
* @dev
* This is only possible if all vault strategy DHWs have been executed, otherwise it's reverted.
* If the system is paused, function will revert - impacts vault functions deposit, withdraw, fastWithdraw,
* claim, reallocate.
* @param vaultStrategies strategies of this vault (verified internally)
*/
function _redeemVaultStrategies(address[] memory vaultStrategies) internal systemNotPaused {
LastIndexInteracted memory _lastIndexInteracted = lastIndexInteracted;
if (_lastIndexInteracted.index1 > 0) {
uint256 globalIndex1 = _lastIndexInteracted.index1;
uint256 completedGlobalIndex = spool.getCompletedGlobalIndex();
if (globalIndex1 <= completedGlobalIndex) {
// redeem interacted index 1
_redeemStrategiesIndex(globalIndex1, vaultStrategies);
_lastIndexInteracted.index1 = 0;
if (_lastIndexInteracted.index2 > 0) {
uint256 globalIndex2 = _lastIndexInteracted.index2;
if (globalIndex2 <= completedGlobalIndex) {
// redeem interacted index 2
_redeemStrategiesIndex(globalIndex2, vaultStrategies);
} else {
_lastIndexInteracted.index1 = _lastIndexInteracted.index2;
}
_lastIndexInteracted.index2 = 0;
}
lastIndexInteracted = _lastIndexInteracted;
}
}
}
/**
* @notice Redeem strategies for at index
* @dev Causes additional gas for first interaction after DHW index has been completed
* @param globalIndex Global index
* @param vaultStrategies Array of vault strategy addresses
*/
function _redeemStrategiesIndex(uint256 globalIndex, address[] memory vaultStrategies) private {
uint128 _totalShares = totalShares;
uint128 totalReceived = 0;
uint128 totalWithdrawn = 0;
uint128 totalUnderlyingAtIndex = 0;
// if vault was reallocating at index claim reallocation deposit
bool isReallocating = _isVaultReallocatingAtIndex(globalIndex);
if (isReallocating) {
spool.redeemReallocation(vaultStrategies, depositProportions, globalIndex);
// Reset reallocation index to 0
reallocationIndex = 0;
}
// go over strategies and redeem deposited shares and withdrawn amount
for (uint256 i = 0; i < vaultStrategies.length; i++) {
address strat = vaultStrategies[i];
(uint128 receivedTokens, uint128 withdrawnTokens) = spool.redeem(strat, globalIndex);
totalReceived += receivedTokens;
totalWithdrawn += withdrawnTokens;
totalUnderlyingAtIndex += spool.getVaultTotalUnderlyingAtIndex(strat, globalIndex);
}
// redeem underlying withdrawn token for all strategies at once
if (totalWithdrawn > 0) {
spool.redeemUnderlying(totalWithdrawn);
}
// substract withdrawn shares
_totalShares -= vaultIndexAction[globalIndex].withdrawShares;
// calculate new deposit shares
uint128 newShares = 0;
if (_totalShares <= MIN_SHARES_FOR_ACCURACY || totalUnderlyingAtIndex == 0) {
// Enforce minimum shares size to avoid loss of share due to computation precision
newShares = totalReceived * SHARES_MULTIPLIER;
if (_totalShares < INITIAL_SHARES_LOCKED) {
if (newShares + _totalShares >= INITIAL_SHARES_LOCKED) {
unchecked {
uint128 newLockedShares = INITIAL_SHARES_LOCKED - _totalShares;
_totalShares += newLockedShares;
newShares -= newLockedShares;
}
} else {
unchecked {
_totalShares += newShares;
}
newShares = 0;
}
}
} else {
if (totalReceived < totalUnderlyingAtIndex) {
unchecked {
newShares = _getProportion128(totalReceived, _totalShares, totalUnderlyingAtIndex - totalReceived);
}
} else {
newShares = _totalShares;
}
}
// add new deposit shares
totalShares = _totalShares + newShares;
redeems[globalIndex] = Redeem(newShares, totalWithdrawn);
emit VaultRedeem(globalIndex);
}
// =========== USER REDEEM ============ //
/**
* @notice Redeem user deposit shares and withdrawn amount
*
* @dev
* Check if vault has already claimed shares for itself
*/
function _redeemUser() internal {
LastIndexInteracted memory _lastIndexInteracted = lastIndexInteracted;
LastIndexInteracted memory userIndexInteracted = userLastInteractions[msg.sender];
// check if strategy for index has already been redeemed
if (userIndexInteracted.index1 > 0 &&
(_lastIndexInteracted.index1 == 0 || userIndexInteracted.index1 < _lastIndexInteracted.index1)) {
// redeem interacted index 1
_redeemUserAction(userIndexInteracted.index1, true);
userIndexInteracted.index1 = 0;
if (userIndexInteracted.index2 > 0) {
if (_lastIndexInteracted.index2 == 0 || userIndexInteracted.index2 < _lastIndexInteracted.index1) {
// redeem interacted index 2
_redeemUserAction(userIndexInteracted.index2, false);
} else {
userIndexInteracted.index1 = userIndexInteracted.index2;
}
userIndexInteracted.index2 = 0;
}
userLastInteractions[msg.sender] = userIndexInteracted;
}
}
/**
* @notice Redeem user action for the `index`
* @param index index aw which user performed the action
* @param isFirstIndex Is this the first user index
*/
function _redeemUserAction(uint256 index, bool isFirstIndex) private {
User storage user = users[msg.sender];
IndexAction storage userIndex = userIndexAction[msg.sender][index];
// redeem user withdrawn amount at index
uint128 userWithdrawalShares = userIndex.withdrawShares;
if (userWithdrawalShares > 0) {
// calculate user withdrawn amount
uint128 userWithdrawnAmount = _getProportion128(redeems[index].withdrawnAmount, userWithdrawalShares, vaultIndexAction[index].withdrawShares);
user.owed += userWithdrawnAmount;
// calculate proportionate deposit to pay for performance fees on claim
uint128 proportionateDeposit;
uint128 sharesAtWithdrawal = user.shares + userWithdrawalShares;
if (isFirstIndex) {
// if user has 2 withdraws pending sum shares from the pending one as well
sharesAtWithdrawal += userIndexAction[msg.sender][index + 1].withdrawShares;
}
// check if withdrawal of all user shares was performes (all shares at the index of the action)
if (sharesAtWithdrawal > userWithdrawalShares) {
uint128 userTotalDeposit = user.activeDeposit;
proportionateDeposit = _getProportion128(userTotalDeposit, userWithdrawalShares, sharesAtWithdrawal);
user.activeDeposit = userTotalDeposit - proportionateDeposit;
} else {
proportionateDeposit = user.activeDeposit;
user.activeDeposit = 0;
}
user.withdrawnDeposits += proportionateDeposit;
// set user withdraw shares for index to 0
userIndex.withdrawShares = 0;
}
// redeem user deposit shares at index
uint128 userDepositAmount = userIndex.depositAmount;
if (userDepositAmount > 0) {
// calculate new user deposit shares
uint128 newUserShares = _getProportion128(userDepositAmount, redeems[index].depositShares, vaultIndexAction[index].depositAmount);
user.shares += newUserShares;
user.activeDeposit += userDepositAmount;
// set user deposit amount for index to 0
userIndex.depositAmount = 0;
}
emit UserRedeem(msg.sender, index);
}
// =========== INDEX FUNCTIONS ============ //
/**
* @dev Saves vault last interacted global index
* @param globalIndex Global index
*/
function _updateInteractedIndex(uint24 globalIndex) internal {
_updateLastIndexInteracted(lastIndexInteracted, globalIndex);
}
/**
* @dev Saves last user interacted global index
* @param globalIndex Global index
*/
function _updateUserInteractedIndex(uint24 globalIndex) internal {
_updateLastIndexInteracted(userLastInteractions[msg.sender], globalIndex);
}
/**
* @dev Update last index with which the system interacted
* @param lit Last interacted idex of a user or a vault
* @param globalIndex Global index
*/
function _updateLastIndexInteracted(LastIndexInteracted storage lit, uint24 globalIndex) private {
if (lit.index1 > 0) {
if (lit.index1 < globalIndex) {
lit.index2 = globalIndex;
}
} else {
lit.index1 = globalIndex;
}
}
/**
* @dev Gets current active global index from spool
*/
function _getActiveGlobalIndex() internal view returns(uint24) {
return spool.getActiveGlobalIndex();
}
/* ========== PRIVATE FUNCTIONS ========== */
/**
* @dev Ensures the vault is not currently reallocating
*/
function _noReallocation() private view {
require(!_isVaultReallocating(), "NRED");
}
/* ========== MODIFIERS ========== */
/**
* @dev Redeem given array of vault strategies
*/
modifier redeemVaultStrategiesModifier(address[] memory vaultStrategies) {
_redeemVaultStrategies(vaultStrategies);
_;
}
/**
* @dev Redeem user
*/
modifier redeemUserModifier() {
_redeemUser();
_;
}
/**
* @dev Ensures the vault is not currently reallocating
*/
modifier noReallocation() {
_noReallocation();
_;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.11;
import "../interfaces/vault/IVaultRestricted.sol";
import "./VaultIndexActions.sol";
/**
* @notice Implementation of the {IVaultRestricted} interface.
*
* @dev
* VaultRestricted extends VaultIndexActions and exposes functions restricted for Spool specific contracts.
*
* Index functions are executed when state changes are performed, to synchronize to vault with central Spool contract
*
* Functions:
* - payFees, called by fast withdraw, when user decides to fast withdraw its shares
* - reallocate, called by spool, sets new strategy allocation values and calculates what
* strategies to withdraw from and deposit to, to achieve the desired allocation
*/
abstract contract VaultRestricted is IVaultRestricted, VaultIndexActions {
using Bitwise for uint256;
// =========== FAST WITHDRAW FEES ============ //
/**
* @notice Notifies fee handler of user realized profits to calculate and store the fee.
* @dev
* Called by fast withdraw contract.
* Fee handler updates the fee storage slots and returns calculated fee value
* Fast withdraw transfers the calculated fee to the fee handler after.
*
* Requirements:
*
* - Caller must be the fast withdraw contract
*
* @param profit Total profit made by the user
* @return Fee amount calculated from the profit
*/
function payFees(uint256 profit) external override onlyFastWithdraw returns (uint256) {
return _payFees(profit);
}
/* ========== SPOOL REALLOCATE ========== */
/**
* @notice Update vault strategy proportions and reallocate funds according to the new proportions.
*
* @dev
* Requirements:
*
* - the caller must be the Spool contract
* - reallocation must not be in progress
* - new vault proportions must add up to `FULL_PERCENT`
*
* @param vaultStrategies Vault strategy addresses
* @param newVaultProportions New vault proportions
* @param finishedIndex Completed global index
* @param activeIndex current active global index, that we're setting reallocate for
*
* @return withdrawProportionsArray array of shares to be withdrawn from each vault strategy, and be later deposited back to other vault strategies
* @return newDepositProportions proportions to be deposited to strategies from all withdrawn funds (written in a uint word, 14bits each) values add up to `FULL_PERCENT`
*
*/
function reallocate(
address[] memory vaultStrategies,
uint256 newVaultProportions,
uint256 finishedIndex,
uint24 activeIndex
)
external
override
onlySpool
verifyStrategies(vaultStrategies)
redeemVaultStrategiesModifier(vaultStrategies)
noReallocation
returns(uint256[] memory withdrawProportionsArray, uint256 newDepositProportions)
{
(withdrawProportionsArray, newDepositProportions) = _adjustAllocation(vaultStrategies, newVaultProportions, finishedIndex);
proportions = newVaultProportions;
reallocationIndex = activeIndex;
_updateInteractedIndex(activeIndex);
emit Reallocate(reallocationIndex, newVaultProportions);
}
/**
* @notice Set new vault strategy allocation and calculate how the funds should be spread
*
* @dev
* Requirements:
*
* - new proportions must add up to 100% (`FULL_PERCENT`)
* - vault must withdraw from at least one strategy
* - vault must deposit to at least one strategy
* - vault total underlying must be more than zero
*
* @param vaultStrategies Vault strategy addresses
* @param newVaultProportions New vault proportions
* @param finishedIndex Completed global index
*
* @return withdrawProportionsArray array of shares to be withdrawn from each vault strategy, and be later deposited back to other vault strategies
* @return newDepositProportions proportions to be deposited to strategies from all withdrawn funds (written in a uint word, 14bits each) values add up to `FULL_PERCENT`
*/
function _adjustAllocation(
address[] memory vaultStrategies,
uint256 newVaultProportions,
uint256 finishedIndex
)
private returns(uint256[] memory, uint256)
{
uint256[] memory depositProportionsArray = new uint256[](vaultStrategies.length);
uint256[] memory withdrawProportionsArray = new uint256[](vaultStrategies.length);
(uint256[] memory stratUnderlyings, uint256 vaultTotalUnderlying) = _getStratsAndVaultUnderlying(vaultStrategies, finishedIndex);
require(vaultTotalUnderlying > 0, "NUL");
uint256 totalProportion;
uint256 totalDepositProportion;
uint256 lastDepositIndex;
{
// flags to check if reallocation will withdraw and reposit
bool didWithdraw = false;
bool willDeposit = false;
for (uint256 i; i < vaultStrategies.length; i++) {
uint256 newStratProportion = Bitwise.get14BitUintByIndex(newVaultProportions, i);
totalProportion += newStratProportion;
uint256 stratProportion;
if (stratUnderlyings[i] > 0) {
stratProportion = (stratUnderlyings[i] * FULL_PERCENT) / vaultTotalUnderlying;
}
// if current proportion is more than new - withdraw
if (stratProportion > newStratProportion) {
uint256 withdrawalProportion = stratProportion - newStratProportion;
if (withdrawalProportion < 10) // NOTE: skip if diff is less than 0.1%
continue;
uint256 withdrawalShareProportion = (withdrawalProportion * ACCURACY) / stratProportion;
withdrawProportionsArray[i] = withdrawalShareProportion;
didWithdraw = true;
} else if (stratProportion < newStratProportion) {
// if less - prepare for deposit
uint256 depositProportion = newStratProportion - stratProportion;
if (depositProportion < 10) // NOTE: skip if diff is less than 0.1%
continue;
depositProportionsArray[i] = depositProportion;
totalDepositProportion += depositProportion;
lastDepositIndex = i;
willDeposit = true;
}
}
// check if sum of new propotions equals to full percent
require(
totalProportion == FULL_PERCENT,
"BPP"
);
// Check if withdraw happened and if deposit will, otherwise revert
require(didWithdraw && willDeposit, "NRD");
}
// normalize deposit proportions to FULL_PERCENT
uint256 newDepositProportions;
uint256 totalDepositProp;
for (uint256 i; i <= lastDepositIndex; i++) {
if (depositProportionsArray[i] > 0) {
uint256 proportion = (depositProportionsArray[i] * FULL_PERCENT) / totalDepositProportion;
newDepositProportions = newDepositProportions.set14BitUintByIndex(i, proportion);
totalDepositProp += proportion;
}
}
newDepositProportions = newDepositProportions.set14BitUintByIndex(lastDepositIndex, FULL_PERCENT - totalDepositProp);
// store reallocation deposit proportions
depositProportions = newDepositProportions;
return (withdrawProportionsArray, newDepositProportions);
}
/**
* @notice Get strategies and vault underlying
* @param vaultStrategies Array of vault strategy addresses
* @param index Get the underlying amounts at index
*/
function _getStratsAndVaultUnderlying(address[] memory vaultStrategies, uint256 index)
private
view
returns (uint256[] memory, uint256)
{
uint256[] memory stratUnderlyings = new uint256[](vaultStrategies.length);
uint256 vaultTotalUnderlying;
for (uint256 i; i < vaultStrategies.length; i++) {
uint256 stratUnderlying = spool.getVaultTotalUnderlyingAtIndex(vaultStrategies[i], index);
stratUnderlyings[i] = stratUnderlying;
vaultTotalUnderlying += stratUnderlying;
}
return (stratUnderlyings, vaultTotalUnderlying);
}
}{
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 148
},
"remappings": [],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract ISpool","name":"_spool","type":"address"},{"internalType":"contract IController","name":"_controller","type":"address"},{"internalType":"contract IFastWithdraw","name":"_fastWithdraw","type":"address"},{"internalType":"contract IFeeHandler","name":"_feeHandler","type":"address"},{"internalType":"contract ISpoolOwner","name":"_spoolOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimAmount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"fee","type":"uint16"}],"name":"LowerVaultFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint32","name":"periodFinish","type":"uint32"}],"name":"PeriodFinishUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"index","type":"uint24"},{"indexed":false,"internalType":"uint256","name":"newProportions","type":"uint256"}],"name":"Reallocate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leftover","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"periodFinish","type":"uint32"}],"name":"RewardExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"}],"name":"RewardRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"i","type":"uint256"},{"indexed":false,"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"TransferVaultOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"UpdateName","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":true,"internalType":"uint256","name":"globalIndex","type":"uint256"}],"name":"UserRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"globalIndex","type":"uint256"}],"name":"VaultRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"WithdrawFast","type":"event"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint32","name":"rewardsDuration","type":"uint32"},{"internalType":"uint256","name":"reward","type":"uint256"}],"name":"addToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"doRedeemVault","type":"bool"},{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"bool","name":"doRedeemUser","type":"bool"}],"name":"claim","outputs":[{"internalType":"uint128","name":"claimAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claimFinishedRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"contract IController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bool","name":"transferFromVault","type":"bool"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"forceRemoveReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getActiveRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"}],"name":"getRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"}],"name":"getUpdatedUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"}],"name":"getUpdatedVault","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint16","name":"fee","type":"uint16"},{"internalType":"address[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"proportions","type":"uint256[]"}],"internalType":"struct VaultInitializable","name":"vaultInitializable","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastIndexInteracted","outputs":[{"internalType":"uint128","name":"index1","type":"uint128"},{"internalType":"uint128","name":"index2","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_vaultFee","type":"uint16"}],"name":"lowerVaultFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint32","name":"rewardsDuration","type":"uint32"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint256","name":"i","type":"uint256"}],"name":"notifyStrategyRemoved","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"profit","type":"uint256"}],"name":"payFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proportions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint256","name":"newVaultProportions","type":"uint256"},{"internalType":"uint256","name":"finishedIndex","type":"uint256"},{"internalType":"uint24","name":"activeIndex","type":"uint24"}],"name":"reallocate","outputs":[{"internalType":"uint256[]","name":"withdrawProportionsArray","type":"uint256[]"},{"internalType":"uint256","name":"newDepositProportions","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reallocationIndex","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeemUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"}],"name":"redeemVaultAndUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"}],"name":"redeemVaultStrategies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"redeems","outputs":[{"internalType":"uint128","name":"depositShares","type":"uint128"},{"internalType":"uint128","name":"withdrawnAmount","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"removeReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"rewardConfiguration","outputs":[{"internalType":"uint32","name":"rewardsDuration","type":"uint32"},{"internalType":"uint32","name":"periodFinish","type":"uint32"},{"internalType":"uint192","name":"rewardRate","type":"uint192"},{"internalType":"uint32","name":"lastUpdateTime","type":"uint32"},{"internalType":"uint224","name":"rewardPerTokenStored","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"rewardPerToken","outputs":[{"internalType":"uint224","name":"","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardTokensCount","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategiesHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"tokenBlacklist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalInstantDeposit","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vaultOwner","type":"address"}],"name":"transferVaultOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"}],"name":"updateName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"name":"updatePeriodFinish","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userIndexAction","outputs":[{"internalType":"uint128","name":"depositAmount","type":"uint128"},{"internalType":"uint128","name":"withdrawShares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userLastInteractions","outputs":[{"internalType":"uint128","name":"index1","type":"uint128"},{"internalType":"uint128","name":"index2","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"users","outputs":[{"internalType":"uint128","name":"instantDeposit","type":"uint128"},{"internalType":"uint128","name":"activeDeposit","type":"uint128"},{"internalType":"uint128","name":"owed","type":"uint128"},{"internalType":"uint128","name":"withdrawnDeposits","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultFee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vaultIndexAction","outputs":[{"internalType":"uint128","name":"depositAmount","type":"uint128"},{"internalType":"uint128","name":"withdrawShares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint128","name":"sharesToWithdraw","type":"uint128"},{"internalType":"bool","name":"withdrawAll","type":"bool"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint128","name":"sharesToWithdraw","type":"uint128"},{"internalType":"bool","name":"withdrawAll","type":"bool"},{"components":[{"internalType":"bool","name":"doExecuteWithdraw","type":"bool"},{"internalType":"uint256[][]","name":"slippages","type":"uint256[][]"},{"components":[{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"bytes","name":"path","type":"bytes"}],"internalType":"struct SwapData[][]","name":"swapData","type":"tuple[][]"}],"internalType":"struct FastWithdrawParams","name":"fastWithdrawParams","type":"tuple"}],"name":"withdrawFast","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101206040523480156200001257600080fd5b506040516200643738038062006437833981016040819052620000359162000312565b600080558484848482856001600160a01b038116620000cd5760405162461bcd60e51b815260206004820152604360248201527f53706f6f6c4f776e61626c653a3a636f6e7374727563746f723a2053706f6f6c60448201527f206f776e657220636f6e747261637420616464726573732063616e6e6f74206260648201526206520360ec1b608482015260a4015b60405180910390fd5b6001600160a01b0390811660805281166200015d5760405162461bcd60e51b815260206004820152604360248201527f53706f6f6c5061757361626c653a3a636f6e7374727563746f723a20436f6e7460448201527f726f6c6c657220636f6e747261637420616464726573732063616e6e6f74206260648201526206520360ec1b608482015260a401620000c4565b6001600160a01b0390811660a0528416620001d55760405162461bcd60e51b815260206004820152603160248201527f5661756c74426173653a3a636f6e7374727563746f723a2053706f6f6c206164604482015270064726573732063616e6e6f74206265203607c1b6064820152608401620000c4565b6001600160a01b038216620002535760405162461bcd60e51b815260206004820152603860248201527f5661756c74426173653a3a636f6e7374727563746f723a20466173745769746860448201527f6472617720616464726573732063616e6e6f74206265203000000000000000006064820152608401620000c4565b6001600160a01b038116620002d15760405162461bcd60e51b815260206004820152603760248201527f5661756c74426173653a3a636f6e7374727563746f723a204665652048616e6460448201527f6c657220616464726573732063616e6e6f7420626520300000000000000000006064820152608401620000c4565b6001600160a01b0393841660c05290831660e052909116610100525062000392945050505050565b6001600160a01b03811681146200030f57600080fd5b50565b600080600080600060a086880312156200032b57600080fd5b85516200033881620002f9565b60208701519095506200034b81620002f9565b60408701519094506200035e81620002f9565b60608701519093506200037181620002f9565b60808701519092506200038481620002f9565b809150509295509295909350565b60805160a05160c05160e05161010051615fcd6200046a600039600081816127010152612992015260008181611f9101526129320152600081816115410152818161177101528181611ed70152818161247c01528181612f520152818161365301528181613720015281816137ed015281816138b301528181613a1201528181614051015281816141650152818161420b015281816142ce015281816143960152614b2801526000818161085e01528181611864015281816123c601528181612a760152613919015260006149b90152615fcd6000f3fe608060405234801561001057600080fd5b50600436106102955760003560e01c806375a5a10a11610167578063a4d5e67c116100ce578063d8149b9b11610087578063d8149b9b146107e7578063f1229777146107fa578063f5bdf6c714610825578063f77c479114610859578063fb6138ab14610880578063fd3c11a81461092357600080fd5b8063a4d5e67c14610703578063a87430ba14610716578063bcd110141461079e578063c52f6c62146107b1578063cfce5dd9146107cb578063d7b9d423146107d457600080fd5b80638206818511610120578063820681851461064557806384da92a7146106585780638501881e1461066b5780638ab936b81461069f5780639fa45102146106b2578063a481ea29146106e557600080fd5b806375a5a10a14610558578063780d9d2d1461059757806379ecae54146105b65780637ae79d75146105f65780637bb7bed11461060957806380aa8c0d1461063257600080fd5b80633b0b4f5b1161020b578063692a4a91116101c4578063692a4a91146104905780636af3badd146104b85780636b76f333146104cb5780636e783b981461051f5780636f9ef7481461053257806373c2ad9c1461054557600080fd5b80633b0b4f5b146103c257806341a275e4146103ca578063510ccb43146103dd57806354663290146103f0578063564356d814610447578063638634ee1461046857600080fd5b8063281b98491161025d578063281b9849146103585780632e0b00451461036d5780633084d7351461038057806335303b11146103935780633a98ef39146103a65780633aa3cd4a146103b957600080fd5b806301ac145b1461029a57806306fdde03146102c7578063084fd9b4146102dc5780631f52692b14610307578063211dc32d14610337575b600080fd5b6001546102af90600160a81b900461ffff1681565b60405161ffff90911681526020015b60405180910390f35b6102cf610936565b6040516102be9190614f19565b6102ef6102ea3660046150a3565b6109c4565b6040516001600160801b0390911681526020016102be565b60015461031f9061010090046001600160a01b031681565b6040516001600160a01b0390911681526020016102be565b61034a610345366004615106565b610b50565b6040519081526020016102be565b61036b61036636600461513f565b610c27565b005b61036b61037b366004615187565b610c48565b61034a61038e3660046151bc565b610d5f565b61036b6103a13660046151d5565b610d7a565b6003546102ef906001600160801b031681565b61034a60045481565b61036b610e29565b61036b6103d83660046151f2565b610e33565b61036b6103eb366004615230565b610e70565b6104036103fe36600461513f565b610f11565b60408051998a5260208a0198909852968801959095526060870193909352608086019190915260a085015260c084015260e0830152610100820152610120016102be565b61045a6104553660046152cf565b6111e2565b6040516102be929190615361565b61047b6104763660046151d5565b61128d565b60405163ffffffff90911681526020016102be565b6007546104a490610100900462ffffff1681565b60405162ffffff90911681526020016102be565b61036b6104c63660046151d5565b6112be565b6104ff6104d93660046151bc565b6010602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b039384168152929091166020830152016102be565b61036b61052d3660046151d5565b611312565b61036b61054036600461513f565b611377565b61036b610553366004615383565b61138b565b6104ff6105663660046153c1565b600e6020908152600092835260408084209091529082529020546001600160801b0380821691600160801b90041682565b6007546105a49060ff1681565b60405160ff90911681526020016102be565b6105c96105c436600461513f565b611515565b604080519687526020870195909552938501929092526060840152608083015260a082015260c0016102be565b61036b6106043660046153ff565b611679565b61031f6106173660046151bc565b6009602052600090815260409020546001600160a01b031681565b61036b6106403660046153c1565b61172a565b61036b61065336600461541a565b611760565b61036b6106663660046154d5565b611beb565b6104ff6106793660046151d5565b600f602052600090815260409020546001600160801b0380821691600160801b90041682565b61036b6106ad36600461551e565b611c36565b6106d56106c03660046151d5565b600b6020526000908152604090205460ff1681565b60405190151581526020016102be565b600c546104ff906001600160801b0380821691600160801b90041682565b61036b6107113660046151d5565b611dd9565b6107646107243660046151d5565b6008602052600090815260409020805460018201546002909201546001600160801b0380831693600160801b938490048216938183169391048216911685565b604080516001600160801b03968716815294861660208601529285169284019290925283166060830152909116608082015260a0016102be565b61034a6107ac3660046151d5565b611e01565b6003546102ef90600160801b90046001600160801b031681565b61034a60065481565b61036b6107e2366004615737565b611e43565b61036b6107f536600461588f565b612049565b61080d6108083660046151d5565b61210c565b6040516001600160e01b0390911681526020016102be565b6104ff6108333660046151bc565b600d602052600090815260409020546001600160801b0380821691600160801b90041682565b61031f7f000000000000000000000000000000000000000000000000000000000000000081565b6108da61088e3660046151d5565b600a602052600090815260409020805460019091015463ffffffff80831692600160201b808204831693600160401b9092046001600160c01b031692821691046001600160e01b031685565b6040805163ffffffff968716815294861660208601526001600160c01b03909316928401929092529290921660608201526001600160e01b03909116608082015260a0016102be565b61036b61093136600461551e565b612208565b6002805461094390615965565b80601f016020809104026020016040519081016040528092919081815260200182805461096f90615965565b80156109bc5780601f10610991576101008083540402835291602001916109bc565b820191906000526020600020905b81548152906001019060200180831161099f57829003601f168201915b505050505081565b33600090815260086020526040812084156109eb576109e284612383565b6109eb846123c4565b82156109f9576109f961258e565b60018101546001600160801b0316915081610a415760405162461bcd60e51b815260206004820152600360248201526204341360ec1b60448201526064015b60405180910390fd5b6001810180546001600160801b03191690819055600160801b90046001600160801b03908116908316811015610ab9576001820180546001600160801b031690556000610a8e82856159b0565b90506000610aa4826001600160801b03166126e7565b9050610ab081866159b0565b94505050610ae3565b610ac383826159b0565b6001830180546001600160801b03928316600160801b0292169190911790555b610b0933846001600160801b0316610af961272e565b6001600160a01b03169190612797565b6040516001600160801b038416815233907fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a9060200160405180910390a250509392505050565b6001600160a01b038083166000908152600a60209081526040808320938516835260089091528120549091906001600160801b031680610bae57506001600160a01b0383166000908152600390910160205260409020549050610c21565b6001600160a01b03841660009081526002830160209081526040808320546003860190925290912054670de0b6b3a764000082610bea8961210c565b6001600160e01b0316610bfd91906159d8565b610c0790856159ef565b610c119190615a0e565b610c1b9190615a30565b93505050505b92915050565b80610c3181612383565b81610c3b816123c4565b610c4361258e565b505050565b610c506127ed565b816000610c5d8282612860565b6001600160a01b0384166000908152600a602052604090206001015463ffffffff80851691161115610cc9576001600160a01b0384166000908152600a602052604090206001810154815467ffffffff00000000191663ffffffff909116600160201b02179055610cff565b6001600160a01b0384166000908152600a60205260409020805467ffffffff000000001916600160201b63ffffffff8616021790555b6001600160a01b0384166000818152600a6020908152604091829020549151600160201b90920463ffffffff1682527f21b2dd8950fc3a17e42d75bdfba3bf13f5a451f2d4b1dab7ab7f8d44f8a06926910160405180910390a250505050565b6000610d69612930565b610d728261298e565b90505b919050565b610d82612a6b565b60016000541415610dd55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a38565b6001600090815560075460ff16905b81811015610e2057600081815260096020526040902054610e0e906001600160a01b031684612acb565b80610e1881615a48565b915050610de4565b50506000805550565b610e3161258e565b565b610e3b612bbf565b6001600160a01b0383166000908152600a60205260409020805463ffffffff191663ffffffff8316179055610c438383612c12565b60016000541415610ec35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a38565b600160009081555b8151811015610f0957610ef7828281518110610ee957610ee9615a63565b602002602001015133612acb565b80610f0181615a48565b915050610ecb565b505060008055565b600080600080600080600080600080610f298b611515565b50505050509050610f3861258e565b3360009081526008602052604081206003549091906001600160801b031615801590610f70575060028201546001600160801b031615155b15610fa15760035460028301546001600160801b0391821691610f949116856159ef565b610f9e9190615a0e565b90505b6000600e6000336001600160a01b03166001600160a01b031681526020019081526020016000206000600f6000336001600160a01b03166001600160a01b0316815260200190815260200160002060000160009054906101000a90046001600160801b03166001600160801b0316815260200190815260200160002090506000600e6000336001600160a01b03166001600160a01b031681526020019081526020016000206000600f6000336001600160a01b03166001600160a01b0316815260200190815260200160002060000160109054906101000a90046001600160801b03166001600160801b0316815260200190815260200160002090508360020160009054906101000a90046001600160801b03168460000160109054906101000a90046001600160801b03168560010160009054906101000a90046001600160801b03168660010160109054906101000a90046001600160801b0316868660000160009054906101000a90046001600160801b03168760000160109054906101000a90046001600160801b03168760000160009054906101000a90046001600160801b03168860000160109054906101000a90046001600160801b0316886001600160801b03169850876001600160801b03169750866001600160801b03169650856001600160801b03169550836001600160801b03169350826001600160801b03169250816001600160801b03169150806001600160801b031690509d509d509d509d509d509d509d509d509d5050505050509193959799909294969850565b606060006111ee612f50565b856111f881612383565b86611202816123c4565b61120a612fae565b611215888888612fec565b60048990556007805463ffffff00191661010062ffffff8a1602179055909450925061124085613368565b60075460405188815261010090910462ffffff16907f5c09fbfaf801f04455c21a7b761e0ef929b1c114f8e95d5ecc5179e4117416dd9060200160405180910390a2505094509492505050565b6001600160a01b0381166000908152600a6020526040812054610d72904290600160201b900463ffffffff16613373565b6112c66127ed565b6001600160a01b0381166000908152600b60205260409020805460ff191660011790556112f281613388565b6001600160a01b03166000908152600a6020526040812081815560010155565b61131a612bbf565b60018054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527f595b89b61981754cfd748b6a577e0310ba8a098a483e3e3bac5feab923d7b3f4906020015b60405180910390a150565b8061138181612383565b81610c43816123c4565b611393612bbf565b8261139d816134ac565b6001600160a01b0384166000908152600a60209081526040808320600b9092529091205460ff16156113fa5760405162461bcd60e51b8152600401610a38906020808252600490820152631513d09360e21b604082015260600190565b63ffffffff8416158015906114175750600181015463ffffffff16155b61144c5760405162461bcd60e51b8152600401610a38906020808252600490820152634243464760e01b604082015260600190565b600754600560ff909116111561148d5760405162461bcd60e51b8152600401610a38906020808252600490820152630a89a82b60e31b604082015260600190565b6007805460ff908116600090815260096020526040812080546001600160a01b0319166001600160a01b038a16179055825490911691906114cd83615a79565b82546101009290920a60ff81810219909316919092169190910217905550805463ffffffff191663ffffffff8516178155821561150e5761150e8584612c12565b5050505050565b6000806000806000808661152881612383565b87611532816123c4565b6000805b8a5181101561161d577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634e89a7118c838151811061158057611580615a63565b60200260200101516040518263ffffffff1660e01b81526004016115b391906001600160a01b0391909116815260200190565b6020604051808303816000875af11580156115d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f69190615a99565b611609906001600160801b031683615a30565b91508061161581615a48565b915050611536565b50600c546001600160801b038082166000908152600d6020526040808220600160801b9485900484168352912060035491549054949e9183169d508083169c5083900482169a5083821699509190920490911695509350505050565b60015461010090046001600160a01b0316331480156116a8575060015461ffff600160a81b9091048116908216105b6116da5760405162461bcd60e51b815260206004820152600360248201526246454560e81b6044820152606401610a38565b6001805461ffff60a81b1916600160a81b61ffff8416908102919091179091556040519081527f11878e6c9d396e93aa9470747ab6ae3baa13958d1df62e803a0b223c652a564b9060200161136c565b6117326127ed565b8161173c816134ac565b82611746816134fb565b61175a6001600160a01b0385163385612797565b50505050565b611768613555565b158061180757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663194b42f86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f19190615ab6565b60075462ffffff91821661010090910490911611155b6118395760405162461bcd60e51b815260206004820152600360248201526229272360e91b6044820152606401610a38565b8161184381612383565b8261184d81613571565b83611857816123c4565b84518410801561191957507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663192434098686815181106118a3576118a3615a63565b60200260200101516040518263ffffffff1660e01b81526004016118d691906001600160a01b0391909116815260200190565b602060405180830381865afa1580156118f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119179190615ad3565b155b61194e5760405162461bcd60e51b8152600401610a38906020808252600490820152632129aa2960e11b604082015260600190565b60006001865161195e91906159d8565b90506000816001600160401b0381111561197a5761197a614f3a565b6040519080825280602002602001820160405280156119a3578160200160208202803683370190505b5090508115611b6f5760005b82811015611a0d578781815181106119c9576119c9615a63565b60200260200101518282815181106119e3576119e3615a63565b6001600160a01b039092166020928302919091019091015280611a0581615a48565b9150506119af565b5081861015611a6357868281518110611a2857611a28615a63565b6020026020010151818781518110611a4257611a42615a63565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6004546000611a7282896135a8565b611a7e906127106159d8565b9050600184118015611a905750600081115b15611b615783881415611aae57611aa782896135c4565b9150611ad7565b6000611aba83866135a8565b9050611ac6838a6135c4565b9250611ad3838a836135e1565b9250505b600080611ae56001876159d8565b905061271060005b82811015611b49576000611b0187836135a8565b905085611b10612710836159ef565b611b1a9190615a0e565b9050611b278583836135e1565b9450611b3381846159d8565b9250508080611b4190615a48565b915050611aed565b50611b558383836135e1565b60045550611b68915050565b6127106004555b5050611b75565b60006004555b611b7e816135fb565b7f7f705c9890e1e97666eb559f0069c51eb9c6d8e37d6628895f3a886cc57b572086888881518110611bb257611bb2615a63565b6020026020010151604051611bda9291909182526001600160a01b0316602082015260400190565b60405180910390a150505050505050565b611bf36127ed565b8051611c06906002906020840190614e31565b507fed41a19b397d8e610d8f9e6e52ab2bef7e51c9977a12cb779c7cd86747f8d5098160405161136c9190614f19565b82611c4081612383565b83611c4a81613571565b84611c54816123c4565b611c5c61258e565b611c653361360a565b6000856001600160801b031611611ca45760405162461bcd60e51b815260206004820152600360248201526204e44560ec1b6044820152606401610a38565b6000611cae61364f565b62ffffff81166000908152600d6020526040812080549293508892909190611ce09084906001600160801b0316615af0565b82546101009290920a6001600160801b03818102199093169183160217909155336000908152600e6020908152604080832062ffffff87168452909152812080548a94509092611d3291859116615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550611d6687878362ffffff166136d3565b611d6f81613368565b611d788161388e565b611d8286866138a7565b611d8b86613979565b6040516001600160801b038716815262ffffff82169033907f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159060200160405180910390a350505050505050565b611de1612bbf565b80611deb816134fb565b816000611df88282612860565b61175a84613388565b6001600160a01b0381166000908152600a602052604081208054611e3c9063ffffffff811690600160401b90046001600160c01b03166159ef565b9392505050565b611e4b613a10565b83611e5581612383565b84611e5f816123c4565b611e67612fae565b611e6f61258e565b611e783361360a565b611e828585613ac5565b94506000611e8f86613db5565b600380549192508791600090611eaf9084906001600160801b03166159b0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663db6b687189846040518363ffffffff1660e01b8152600401611f23929190615b54565b6000604051808303816000875af1158015611f42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f6a9190810190615b67565b90506000611f7788613dea565b60405163e5c8848560e01b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e5c8848590611fce908c908690869033908d90600401615ca1565b600060405180830381600087803b158015611fe857600080fd5b505af1158015611ffc573d6000803e3d6000fd5b50506040516001600160801b038b1681523392507ff81ecc4c10049fe8abde0a7afff608b6ac1d4cf5f6d58673e466a418b47dbf57915060200160405180910390a2505050505050505050565b60015460ff16156120855760405162461bcd60e51b8152600401610a38906020808252600490820152631052539560e21b604082015260600190565b60208082015160018054604085015161ffff16600160a81b0261ffff60a81b196001600160a01b039094166101000293909316610100600160b81b031990911617919091179055815180516120de926002920190614e31565b506120ec8160800151613f71565b60045560608101516120fd906135fb565b506001805460ff191681179055565b6001600160a01b0381166000908152600a60205260408120600354600160801b90046001600160801b03166121555760010154600160201b90046001600160e01b031692915050565b600181015460009063ffffffff1661216c8561128d565b6121769190615da0565b63ffffffff1690508061219e575060010154600160201b90046001600160e01b031692915050565b600354825461220091600160801b90046001600160801b0316906121d290600160401b90046001600160c01b0316846159ef565b6121dc9190615a0e565b60018401546121fb9190600160201b90046001600160e01b0316615a30565b613fc9565b949350505050565b8261221281612383565b8361221c816123c4565b612224612fae565b61222c61258e565b6122353361360a565b61223f8484613ac5565b9350600061224b61364f565b336000908152600e6020908152604080832062ffffff8516845290915290208054919250869160109061228f908490600160801b90046001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555084600d60008362ffffff16815260200190815260200160002060000160108282829054906101000a90046001600160801b03166122f09190615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061232486868362ffffff16614036565b61232d81613368565b6123368161388e565b6040516001600160801b038616815262ffffff82169033907ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b5689060200160405180910390a3505050505050565b61238f81600654614115565b6123c15760405162461bcd60e51b81526020600482015260036024820152620aca6960eb1b6044820152606401610a38565b50565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634c8900606040518163ffffffff1660e01b815260040160006040518083038186803b15801561241d57600080fd5b505afa158015612431573d6000803e3d6000fd5b505060408051808201909152600c546001600160801b03808216808452600160801b90920416602083015290925015905061258a57600081600001516001600160801b0316905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663194b42f86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124fc9190615ab6565b62ffffff16905080821161175a576125148285614129565b6000835260208301516001600160801b0316156125695760208301516001600160801b031681811161254f5761254a8186614129565b612560565b60208401516001600160801b031684525b50600060208401525b825160208401516001600160801b03908116600160801b02911617600c5550505b5050565b604080518082018252600c546001600160801b038082168352600160801b918290048116602080850191909152336000908152600f82528590208551808701909652548083168087529390049091169084015290919015801590612612575081516001600160801b031615806126125750815181516001600160801b039182169116105b1561258a57805161262d906001600160801b03166001614590565b6000815260208101516001600160801b0316156126b25760208201516001600160801b03161580612677575081600001516001600160801b031681602001516001600160801b0316105b156126995761269481602001516001600160801b03166000614590565b6126aa565b60208101516001600160801b031681525b600060208201525b336000908152600f602090815260409091208251918301516001600160801b03908116600160801b0292169190911790555050565b60006126fa6126f58361298e565b6148a6565b9050610d757f0000000000000000000000000000000000000000000000000000000000000000826001600160801b0316610af95b6000306001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561276e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615dbd565b905090565b610c438363a9059cbb60e01b84846040516024016127b6929190615dda565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526148cf565b6127f56149a1565b610e315760405162461bcd60e51b815260206004820152603660248201527f53706f6f6c4f776e61626c653a3a6f6e6c794f776e65723a2043616c6c65722060448201527534b9903737ba103a34329029b837b7b61037bbb732b960511b6064820152608401610a38565b6001600160a01b0382166000908152600a602052604090206128818361210c565b8160010160046101000a8154816001600160e01b0302191690836001600160e01b031602179055506128b28361128d565b60018201805463ffffffff191663ffffffff929092169190911790556001600160a01b03821615610c43576128e78383610b50565b6001600160a01b0392909216600090815260038201602090815260408083209490945560018301546002909301905291909120600160201b9091046001600160e01b0316905550565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610e315760405162461bcd60e51b81526020600482015260036024820152624f465760e81b6044820152606401610a38565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634b6229016129c761272e565b846129d0614a2c565b60015460405160e086901b6001600160e01b03191681526001600160a01b0394851660048201526024810193909352908316604483015261010081049092166064820152600160a81b90910461ffff16608482015260a4016020604051808303816000875af1158015612a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d729190615df3565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610e315760405162461bcd60e51b815260206004820152600560248201526413d0d5149360da1b6044820152606401610a38565b8181612ad78282612860565b6001600160a01b0384166000908152600a60205260409020805463ffffffff16612b295760405162461bcd60e51b815260206004820152600360248201526242544b60e81b6044820152606401610a38565b6001600160a01b03841660009081526003820160205260409020548015612bb7576001600160a01b038086166000908152600384016020526040812055612b739087168683612797565b846001600160a01b03167f540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e8783604051612bae929190615dda565b60405180910390a25b505050505050565b60015461010090046001600160a01b0316331480612be05750612be06149a1565b610e315760405162461bcd60e51b815260206004820152600360248201526213d3d160ea1b6044820152606401610a38565b816000612c1f8282612860565b6001600160a01b0384166000908152600a602052604090206001600160c01b03612c51670de0b6b3a7640000866159ef565b6001830154612c709190600160201b90046001600160e01b0316615a30565b1115612ca45760405162461bcd60e51b8152602060048201526003602482015262292a2160e91b6044820152606401610a38565b612cb96001600160a01b038616333087614a6c565b8054600090612cce9063ffffffff1642615e0c565b8254909150600160201b900463ffffffff164210612d8c578154612d139063ffffffff16612d04670de0b6b3a7640000886159ef565b612d0e9190615a0e565b614aa4565b82546001600160c01b0391909116600160401b026001600160401b038216811784556040805188815263ffffffff928316929093169190911760208301526001600160a01b038816917f6a6f77044107a33658235d41bedbbaf2fe9ccdceb313143c947a5e76e1ec8474910160405180910390a2612f12565b815463ffffffff808316600160201b909204161115612dd35760405162461bcd60e51b815260206004820152600360248201526250465360e81b6044820152606401610a38565b8154600090612df0904290600160201b900463ffffffff166159d8565b8354909150600090612e1290600160401b90046001600160c01b0316836159ef565b8454909150600090612e419063ffffffff1683612e37670de0b6b3a76400008c6159ef565b612d049190615a30565b85549091506001600160c01b03600160401b90910481169082161015612e8f5760405162461bcd60e51b815260206004820152600360248201526226292960e91b6044820152606401610a38565b84546001600160c01b038216600160401b026001600160401b03821681178755604080518b81526020810186905263ffffffff9283169383169390931790830152851660608201526001600160a01b038a16907e4f7db2945633cb30dd3539fd30caced03aa57f25ff7039750216e9321b94be9060800160405180910390a25050505b60018201805463ffffffff42811663ffffffff199092169190911790915582549116600160201b0267ffffffff000000001990911617905550505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610e315760405162461bcd60e51b815260206004820152600360248201526204f53560ec1b6044820152606401610a38565b612fb6613555565b15610e315760405162461bcd60e51b8152600401610a38906020808252600490820152631394915160e21b604082015260600190565b606060008085516001600160401b0381111561300a5761300a614f3a565b604051908082528060200260200182016040528015613033578160200160208202803683370190505b509050600086516001600160401b0381111561305157613051614f3a565b60405190808252806020026020018201604052801561307a578160200160208202803683370190505b50905060008061308a8988614acd565b91509150600081116130c45760405162461bcd60e51b815260206004820152600360248201526213955360ea1b6044820152606401610a38565b600080808080805b8e5181101561322d5760006130e18f836135a8565b90506130ed8188615a30565b96506000808a848151811061310457613104615a63565b6020026020010151111561314757886127108b858151811061312857613128615a63565b602002602001015161313a91906159ef565b6131449190615a0e565b90505b818111156131be57600061315b83836159d8565b9050600a81101561316e5750505061321b565b6000826131886c0c9f2c9cd04674edea40000000846159ef565b6131929190615a0e565b9050808d86815181106131a7576131a7615a63565b602002602001018181525050600196505050613218565b818110156132185760006131d282846159d8565b9050600a8110156131e55750505061321b565b808d85815181106131f8576131f8615a63565b602090810291909101015261320d8189615a30565b975083965060019450505b50505b8061322581615a48565b9150506130cc565b5061271085146132655760405162461bcd60e51b815260206004820152600360248201526204250560ec1b6044820152606401610a38565b81801561326f5750805b6132a15760405162461bcd60e51b815260206004820152600360248201526213949160ea1b6044820152606401610a38565b505060008060005b8381116133355760008a82815181106132c4576132c4615a63565b60200260200101511115613323576000856127108c84815181106132ea576132ea615a63565b60200260200101516132fc91906159ef565b6133069190615a0e565b90506133138483836135e1565b935061331f8184615a30565b9250505b8061332d81615a48565b9150506132a9565b5061334e83613346836127106159d8565b8491906135e1565b6005819055979e979d50969b505050505050505050505050565b6123c1600c82614c23565b60008183116133825782611e3c565b50919050565b60075460ff1660005b81811015610c43576000818152600960205260409020546001600160a01b038481169116141561349a57600960006133ca6001856159d8565b8152602080820192909252604090810160009081205484825260099384905291812080546001600160a01b0319166001600160a01b03909316929092179091556134156001856159d8565b81526020810191909152604001600090812080546001600160a01b03191690556007805460ff169161344683615e2b565b91906101000a81548160ff021916908360ff16021790555050826001600160a01b03167f755c47ac85b75fe2251607db5a480aac818b88bb535814bf1e3c4784ae4f6baa60405160405180910390a2505050565b806134a481615a48565b915050613391565b6134b461272e565b6001600160a01b0316816001600160a01b031614156123c15760405162461bcd60e51b815260206004820152600360248201526213955560ea1b6044820152606401610a38565b6001600160a01b0381166000908152600a6020526040902054600160201b900463ffffffff1642116123c15760405162461bcd60e51b815260206004820152600360248201526229272360e91b6044820152606401610a38565b600754600090610100900462ffffff161561356e575060015b90565b60008151116123c15760405162461bcd60e51b81526020600482015260036024820152621394d560ea1b6044820152606401610a38565b60006135b582600e6159ef565b83901c613fff16905092915050565b60006135d182600e6159ef565b613fff901b198316905092915050565b60006135ee83600e6159ef565b6122009083901b85615a30565b61360481614c83565b60065550565b60075460ff1660005b81811015610c435760008181526009602052604090205461363d906001600160a01b031684612860565b8061364781615a48565b915050613613565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166359b4ad5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615ab6565b825182906000906136e6906001906159d8565b60045490915060005b828110156137db57600061370d8383896001600160801b0316614cb3565b90506001600160801b038116156137c8577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166360fb0f5989848151811061375f5761375f615a63565b602002602001015183896040518463ffffffff1660e01b815260040161378793929190615e48565b600060405180830381600087803b1580156137a157600080fd5b505af11580156137b5573d6000803e3d6000fd5b5050505080856137c591906159b0565b94505b50806137d381615a48565b9150506136ef565b506001600160801b03831615612bb7577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166360fb0f5987848151811061382c5761382c615a63565b602002602001015185876040518463ffffffff1660e01b815260040161385493929190615e48565b600060405180830381600087803b15801561386e57600080fd5b505af1158015613882573d6000803e3d6000fd5b50505050505050505050565b336000908152600f602052604090206123c19082614c23565b80156138f55761258a337f0000000000000000000000000000000000000000000000000000000000000000846001600160801b03166138e461272e565b6001600160a01b0316929190614a6c565b6040516320bae68360e21b81523360048201526001600160801b03831660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382eb9a0c90604401600060405180830381600087803b15801561396557600080fd5b505af1158015612bb7573d6000803e3d6000fd5b33600090815260086020526040812080548392906139a19084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555080600360108282829054906101000a90046001600160801b03166139e99190615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636bbbfc626040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a929190615ad3565b15610e315760405162461bcd60e51b81526020600482015260036024820152622726a960e91b6044820152606401610a38565b33600090815260086020908152604080832060028101548154600f85528386208451808601909552546001600160801b03818116808752600160801b90920481169686019690965292949182169391169115613b9a57336000908152600e6020908152604080832084516001600160801b039081168552925290912054613b4d9116836159b0565b60208201519092506001600160801b031615613b9a57336000908152600e60209081526040808320848301516001600160801b039081168552925290912054613b979116836159b0565b91505b8580613bcb57506000836001600160801b0316118015613bcb5750866001600160801b0316836001600160801b0316145b15613c7f576002840180546001600160801b0319169055600380549397508793839190601090613c0c908490600160801b90046001600160801b03166159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550818460000160008282829054906101000a90046001600160801b0316613c5691906159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550613daa565b866001600160801b0316836001600160801b031610158015613caa57506000876001600160801b0316115b613cdc5760405162461bcd60e51b81526020600482015260036024820152620aea6960eb1b6044820152606401610a38565b6000613ce9838986614cda565b905080600360108282829054906101000a90046001600160801b0316613d0f91906159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550808560000160008282829054906101000a90046001600160801b0316613d5991906159b0565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508784038560020160006101000a8154816001600160801b0302191690836001600160801b03160217905550505b509495945050505050565b6003546000906001600160801b0390811690613de09084166c0c9f2c9cd04674edea400000006159ef565b610d729190615a0e565b336000908152600860209081526040808320600f83528184208251808401909352546001600160801b038082168452600160801b9091048116938301939093526002810154909284918291613e4191889116615af0565b83519091506001600160801b031615613ee357336000908152600e6020908152604080832086516001600160801b039081168552925290912054613e8e91600160801b9091041682615af0565b60208401519091506001600160801b031615613ee357336000908152600e60209081526040808320868301516001600160801b039081168552925290912054613ee091600160801b9091041682615af0565b90505b856001600160801b0316816001600160801b03161115613f44578354600160801b90046001600160801b0316613f1a818884614cda565b9250613f2683826159b0565b85546001600160801b03918216600160801b02911617855550613f5f565b83546001600160801b038082168655600160801b9091041691505b506001600160801b0316949350505050565b60008060005b8351811015613fc257613fae81858381518110613f9657613f96615a63565b6020026020010151846135e19092919063ffffffff16565b915080613fba81615a48565b915050613f77565b5092915050565b60006001600160e01b038211156140325760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610a38565b5090565b600061404183613db5565b905060005b845181101561150e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b5c5f67286838151811061409057614090615a63565b60209081029190910101516040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024810185905260448101869052606401600060405180830381600087803b1580156140ea57600080fd5b505af11580156140fe573d6000803e3d6000fd5b50505050808061410d90615a48565b915050614046565b60008161412184614c83565b149392505050565b6003546001600160801b0316600080808061414387614d07565b905080156141dc576005546040516317c78b0960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916317c78b099161419c918a918c90600401615e72565b600060405180830381600087803b1580156141b657600080fd5b505af11580156141ca573d6000803e3d6000fd5b50506007805463ffffff001916905550505b60005b86518110156143685760008782815181106141fc576141fc615a63565b602002602001015190506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631e9a6950848d6040518363ffffffff1660e01b8152600401614257929190615dda565b60408051808303816000875af1158015614275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142999190615e97565b90925090506142a88289615af0565b97506142b48188615af0565b60405163a2b0de8b60e01b81529097506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a2b0de8b906143059086908f90600401615dda565b602060405180830381865afa158015614322573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143469190615a99565b6143509087615af0565b9550505050808061436090615a48565b9150506141df565b506001600160801b038316156143fb576040516341c78ddd60e01b81526001600160801b03841660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906341c78ddd90602401600060405180830381600087803b1580156143e257600080fd5b505af11580156143f6573d6000803e3d6000fd5b505050505b6000878152600d602052604090205461442490600160801b90046001600160801b0316866159b0565b9450600061443864174876e800600a615ec6565b6001600160801b0316866001600160801b031611158061445f57506001600160801b038316155b156144ce57614471620f424086615ec6565b905064174876e8006001600160801b03871610156144c95764174876e8006144998783615af0565b6001600160801b0316106144c15764174876e8008690038601950164174876e7ff19016144ff565b949094019360005b6144ff565b826001600160801b0316856001600160801b031610156144fc576144f58587878603614cda565b90506144ff565b50845b6145098187615af0565b600380546001600160801b039283166001600160801b03199091161790556040805180820182528383168152868316602080830191825260008d81526010909152838120925191518516600160801b02919094161790555189917f32568d6390d10efe40ae666165d7e927786da1a0bf60ad350fbd637da51dee0791a25050505050505050565b336000908152600860209081526040808320600e83528184208685529092529091208054600160801b90046001600160801b0316801561478f57600085815260106020908152604080832054600d909252822054614606916001600160801b03600160801b918290048116928692900416614cda565b60018501805491925082916000906146289084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550600080838660020160009054906101000a90046001600160801b03166146729190615af0565b905086156146c357336000908152600e60205260408120906146958a6001615a30565b81526020810191909152604001600020546146c090600160801b90046001600160801b031682615af0565b90505b836001600160801b0316816001600160801b03161115614724578554600160801b90046001600160801b03166146fa818684614cda565b925061470683826159b0565b87546001600160801b03918216600160801b0291161787555061473f565b85546001600160801b038082168855600160801b9091041691505b818660010160108282829054906101000a90046001600160801b03166147659190615af0565b82546101009290920a6001600160801b038181021990931691831602179091558654168655505050505b81546001600160801b0316801561487157600086815260106020908152604080832054600d9092528220546147d29184916001600160801b039182169116614cda565b60028601805491925082916000906147f49084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550818560000160108282829054906101000a90046001600160801b031661483e9190615af0565b82546001600160801b039182166101009390930a9283029190920219909116179055505082546001600160801b03191683555b604051869033907f961e41c19e060d605836d2061ee68d182e66923d6c75ab7685e39f63e9f8684d90600090a3505050505050565b60006001600160801b038211156140325760405162461bcd60e51b8152600401610a3890615ef5565b6000614924826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d269092919063ffffffff16565b805190915015610c4357808060200190518101906149429190615ad3565b610c435760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a38565b604051634c24e8cd60e01b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690634c24e8cd90602401602060405180830381865afa158015614a08573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615ad3565b6000306001600160a01b031663bdff15156040518163ffffffff1660e01b8152600401602060405180830381865afa15801561276e573d6000803e3d6000fd5b6040516001600160a01b038085166024830152831660448201526064810182905261175a9085906323b872dd60e01b906084016127b6565b60006001600160c01b038211156140325760405162461bcd60e51b8152600401610a3890615ef5565b606060008084516001600160401b03811115614aeb57614aeb614f3a565b604051908082528060200260200182016040528015614b14578160200160208202803683370190505b5090506000805b8651811015614c175760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a2b0de8b898481518110614b6757614b67615a63565b6020026020010151896040518363ffffffff1660e01b8152600401614b8d929190615dda565b602060405180830381865afa158015614baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bce9190615a99565b6001600160801b0316905080848381518110614bec57614bec615a63565b6020908102919091010152614c018184615a30565b9250508080614c0f90615a48565b915050614b1b565b50909590945092505050565b81546001600160801b031615614c6a57815462ffffff82166001600160801b03909116101561258a5781546001600160801b031662ffffff91909116600160801b02179055565b81546001600160801b03191662ffffff82161782555050565b600081604051602001614c969190615f3c565b604051602081830303815290604052805190602001209050919050565b600061220061271083614cc687876135a8565b614cd091906159ef565b6126f59190615a0e565b6000612200826001600160801b0316846001600160801b0316866001600160801b0316614cd091906159ef565b600754600090610100900462ffffff16821415610d7557506001919050565b6060612200848460008585843b614d7f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a38565b600080866001600160a01b03168587604051614d9b9190615f7b565b60006040518083038185875af1925050503d8060008114614dd8576040519150601f19603f3d011682016040523d82523d6000602084013e614ddd565b606091505b5091509150614ded828286614df8565b979650505050505050565b60608315614e07575081611e3c565b825115614e175782518084602001fd5b8160405162461bcd60e51b8152600401610a389190614f19565b828054614e3d90615965565b90600052602060002090601f016020900481019282614e5f5760008555614ea5565b82601f10614e7857805160ff1916838001178555614ea5565b82800160010185558215614ea5579182015b82811115614ea5578251825591602001919060010190614e8a565b506140329291505b808211156140325760008155600101614ead565b60005b83811015614edc578181015183820152602001614ec4565b8381111561175a5750506000910152565b60008151808452614f05816020860160208601614ec1565b601f01601f19169290920160200192915050565b602081526000611e3c6020830184614eed565b80151581146123c157600080fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715614f7257614f72614f3a565b60405290565b604051606081016001600160401b0381118282101715614f7257614f72614f3a565b60405160a081016001600160401b0381118282101715614f7257614f72614f3a565b604051601f8201601f191681016001600160401b0381118282101715614fe457614fe4614f3a565b604052919050565b60006001600160401b0382111561500557615005614f3a565b5060051b60200190565b6001600160a01b03811681146123c157600080fd5b8035610d758161500f565b600082601f83011261504057600080fd5b8135602061505561505083614fec565b614fbc565b82815260059290921b8401810191818101908684111561507457600080fd5b8286015b8481101561509857803561508b8161500f565b8352918301918301615078565b509695505050505050565b6000806000606084860312156150b857600080fd5b83356150c381614f2c565b925060208401356001600160401b038111156150de57600080fd5b6150ea8682870161502f565b92505060408401356150fb81614f2c565b809150509250925092565b6000806040838503121561511957600080fd5b82356151248161500f565b915060208301356151348161500f565b809150509250929050565b60006020828403121561515157600080fd5b81356001600160401b0381111561516757600080fd5b6122008482850161502f565b803563ffffffff81168114610d7557600080fd5b6000806040838503121561519a57600080fd5b82356151a58161500f565b91506151b360208401615173565b90509250929050565b6000602082840312156151ce57600080fd5b5035919050565b6000602082840312156151e757600080fd5b8135611e3c8161500f565b60008060006060848603121561520757600080fd5b83356152128161500f565b92506020840135915061522760408501615173565b90509250925092565b6000602080838503121561524357600080fd5b82356001600160401b0381111561525957600080fd5b8301601f8101851361526a57600080fd5b803561527861505082614fec565b81815260059190911b8201830190838101908783111561529757600080fd5b928401925b82841015614ded5783356152af8161500f565b8252928401929084019061529c565b62ffffff811681146123c157600080fd5b600080600080608085870312156152e557600080fd5b84356001600160401b038111156152fb57600080fd5b6153078782880161502f565b94505060208501359250604085013591506060850135615326816152be565b939692955090935050565b600081518084526020808501945080840160005b83811015613daa57815187529582019590820190600101615345565b6040815260006153746040830185615331565b90508260208301529392505050565b60008060006060848603121561539857600080fd5b83356153a38161500f565b92506153b160208501615173565b9150604084013590509250925092565b600080604083850312156153d457600080fd5b82356153df8161500f565b946020939093013593505050565b803561ffff81168114610d7557600080fd5b60006020828403121561541157600080fd5b611e3c826153ed565b6000806040838503121561542d57600080fd5b82356001600160401b0381111561544357600080fd5b61544f8582860161502f565b95602094909401359450505050565b60006001600160401b0383111561547757615477614f3a565b61548a601f8401601f1916602001614fbc565b905082815283838301111561549e57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126154c657600080fd5b611e3c8383356020850161545e565b6000602082840312156154e757600080fd5b81356001600160401b038111156154fd57600080fd5b612200848285016154b5565b6001600160801b03811681146123c157600080fd5b60008060006060848603121561553357600080fd5b83356001600160401b0381111561554957600080fd5b6155558682870161502f565b935050602084013561556681615509565b915060408401356150fb81614f2c565b600082601f83011261558757600080fd5b8135602061559761505083614fec565b82815260059290921b840181019181810190868411156155b657600080fd5b8286015b8481101561509857803583529183019183016155ba565b600082601f8301126155e257600080fd5b6155ef6150508335614fec565b82358082526020808301929160051b85010185101561560d57600080fd5b602084015b6020853560051b86010181101561572e576001600160401b03808235111561563957600080fd5b8135860187603f82011261564c57600080fd5b61565c6150506020830135614fec565b602082810135808352908201919060051b83016040018a81111561567f57600080fd5b604084015b8181101561571757858135111561569a57600080fd5b803585016040818e03603f190112156156b257600080fd5b6156ba614f50565b6040820135815287606083013511156156d257600080fd5b6060820135820191508d605f8301126156ea57600080fd5b6156fc8e60408401356060850161545e565b60208201528086525050602084019350602081019050615684565b505086525050602093840193919091019050615612565b50949350505050565b6000806000806080858703121561574d57600080fd5b6001600160401b03808635111561576357600080fd5b615770878735880161502f565b945060208087013561578181615509565b9450604087013561579181614f2c565b93506060870135828111156157a557600080fd5b87016060818a0312156157b757600080fd5b6157bf614f78565b6157c98235614f2c565b8135815282820135848111156157de57600080fd5b8201601f81018b136157ef57600080fd5b80356157fd61505082614fec565b81815260059190911b8201850190858101908d83111561581c57600080fd5b8684015b8381101561585257888135111561583657600080fd5b6158458f898335880101615576565b8352918701918701615820565b50808786015250505050604082013592508383111561587057600080fd5b61587c8a8484016155d1565b6040820152969995985093965050505050565b6000602082840312156158a157600080fd5b81356001600160401b03808211156158b857600080fd5b9083019060a082860312156158cc57600080fd5b6158d4614f9a565b8235828111156158e357600080fd5b6158ef878286016154b5565b8252506158fe60208401615024565b602082015261590f604084016153ed565b604082015260608301358281111561592657600080fd5b6159328782860161502f565b60608301525060808301358281111561594a57600080fd5b61595687828601615576565b60808301525095945050505050565b600181811c9082168061597957607f821691505b6020821081141561338257634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001600160801b03838116908316818110156159d0576159d061599a565b039392505050565b6000828210156159ea576159ea61599a565b500390565b6000816000190483118215151615615a0957615a0961599a565b500290565b600082615a2b57634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115615a4357615a4361599a565b500190565b6000600019821415615a5c57615a5c61599a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b600060ff821660ff811415615a9057615a9061599a565b60010192915050565b600060208284031215615aab57600080fd5b8151611e3c81615509565b600060208284031215615ac857600080fd5b8151611e3c816152be565b600060208284031215615ae557600080fd5b8151611e3c81614f2c565b60006001600160801b03828116848216808303821115615b1257615b1261599a565b01949350505050565b600081518084526020808501945080840160005b83811015613daa5781516001600160a01b031687529582019590820190600101615b2f565b6040815260006153746040830185615b1b565b60006020808385031215615b7a57600080fd5b82516001600160401b03811115615b9057600080fd5b8301601f81018513615ba157600080fd5b8051615baf61505082614fec565b81815260059190911b82018301908381019087831115615bce57600080fd5b928401925b82841015614ded578351615be681615509565b82529284019290840190615bd3565b600081518084526020808501808196506005915083821b81018387016000805b87811015615c92578484038b5282518051808652908801908886019080891b87018a01855b82811015615c7c57888203601f190184528451805183528c015160408d8401819052615c6881850183614eed565b968e0196958e019593505050600101615c3a565b509d8a019d965050509287019250600101615c15565b50919998505050505050505050565b60a081526000615cb460a0830188615b1b565b82810360208481019190915287518083528882019282019060005b81811015615cf45784516001600160801b031683529383019391830191600101615ccf565b505087604086015260018060a01b038716606086015284810360808601526060810192508551151581528186015160608383015283815180865260808401915060808160051b8501019550848301925060005b81811015615d7557607f19858803018352615d63878551615331565b96509285019291850191600101615d47565b5050505060408601519150808303604082015250615d938282615bf5565b9998505050505050505050565b600063ffffffff838116908316818110156159d0576159d061599a565b600060208284031215615dcf57600080fd5b8151611e3c8161500f565b6001600160a01b03929092168252602082015260400190565b600060208284031215615e0557600080fd5b5051919050565b600063ffffffff808316818516808303821115615b1257615b1261599a565b600060ff821680615e3e57615e3e61599a565b6000190192915050565b6001600160a01b039390931683526001600160801b03919091166020830152604082015260600190565b606081526000615e856060830186615b1b565b60208301949094525060400152919050565b60008060408385031215615eaa57600080fd5b8251615eb581615509565b602084015190925061513481615509565b60006001600160801b0382811684821681151582840482111615615eec57615eec61599a565b02949350505050565b60208082526027908201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316040820152663238206269747360c81b606082015260800190565b815160009082906020808601845b83811015615f6f5781516001600160a01b031685529382019390820190600101615f4a565b50929695505050505050565b60008251615f8d818460208701614ec1565b919091019291505056fea26469706673582212207361c12125b82fd6bc8ae7a18788ffa318a06f25efb8bb8d0ec886b1b94a5f6464736f6c634300080b0033000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e720000000000000000000000004f03f70a99e5c3b49d733ddd7458f80fa9b5a5b5
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102955760003560e01c806375a5a10a11610167578063a4d5e67c116100ce578063d8149b9b11610087578063d8149b9b146107e7578063f1229777146107fa578063f5bdf6c714610825578063f77c479114610859578063fb6138ab14610880578063fd3c11a81461092357600080fd5b8063a4d5e67c14610703578063a87430ba14610716578063bcd110141461079e578063c52f6c62146107b1578063cfce5dd9146107cb578063d7b9d423146107d457600080fd5b80638206818511610120578063820681851461064557806384da92a7146106585780638501881e1461066b5780638ab936b81461069f5780639fa45102146106b2578063a481ea29146106e557600080fd5b806375a5a10a14610558578063780d9d2d1461059757806379ecae54146105b65780637ae79d75146105f65780637bb7bed11461060957806380aa8c0d1461063257600080fd5b80633b0b4f5b1161020b578063692a4a91116101c4578063692a4a91146104905780636af3badd146104b85780636b76f333146104cb5780636e783b981461051f5780636f9ef7481461053257806373c2ad9c1461054557600080fd5b80633b0b4f5b146103c257806341a275e4146103ca578063510ccb43146103dd57806354663290146103f0578063564356d814610447578063638634ee1461046857600080fd5b8063281b98491161025d578063281b9849146103585780632e0b00451461036d5780633084d7351461038057806335303b11146103935780633a98ef39146103a65780633aa3cd4a146103b957600080fd5b806301ac145b1461029a57806306fdde03146102c7578063084fd9b4146102dc5780631f52692b14610307578063211dc32d14610337575b600080fd5b6001546102af90600160a81b900461ffff1681565b60405161ffff90911681526020015b60405180910390f35b6102cf610936565b6040516102be9190614f19565b6102ef6102ea3660046150a3565b6109c4565b6040516001600160801b0390911681526020016102be565b60015461031f9061010090046001600160a01b031681565b6040516001600160a01b0390911681526020016102be565b61034a610345366004615106565b610b50565b6040519081526020016102be565b61036b61036636600461513f565b610c27565b005b61036b61037b366004615187565b610c48565b61034a61038e3660046151bc565b610d5f565b61036b6103a13660046151d5565b610d7a565b6003546102ef906001600160801b031681565b61034a60045481565b61036b610e29565b61036b6103d83660046151f2565b610e33565b61036b6103eb366004615230565b610e70565b6104036103fe36600461513f565b610f11565b60408051998a5260208a0198909852968801959095526060870193909352608086019190915260a085015260c084015260e0830152610100820152610120016102be565b61045a6104553660046152cf565b6111e2565b6040516102be929190615361565b61047b6104763660046151d5565b61128d565b60405163ffffffff90911681526020016102be565b6007546104a490610100900462ffffff1681565b60405162ffffff90911681526020016102be565b61036b6104c63660046151d5565b6112be565b6104ff6104d93660046151bc565b6010602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b039384168152929091166020830152016102be565b61036b61052d3660046151d5565b611312565b61036b61054036600461513f565b611377565b61036b610553366004615383565b61138b565b6104ff6105663660046153c1565b600e6020908152600092835260408084209091529082529020546001600160801b0380821691600160801b90041682565b6007546105a49060ff1681565b60405160ff90911681526020016102be565b6105c96105c436600461513f565b611515565b604080519687526020870195909552938501929092526060840152608083015260a082015260c0016102be565b61036b6106043660046153ff565b611679565b61031f6106173660046151bc565b6009602052600090815260409020546001600160a01b031681565b61036b6106403660046153c1565b61172a565b61036b61065336600461541a565b611760565b61036b6106663660046154d5565b611beb565b6104ff6106793660046151d5565b600f602052600090815260409020546001600160801b0380821691600160801b90041682565b61036b6106ad36600461551e565b611c36565b6106d56106c03660046151d5565b600b6020526000908152604090205460ff1681565b60405190151581526020016102be565b600c546104ff906001600160801b0380821691600160801b90041682565b61036b6107113660046151d5565b611dd9565b6107646107243660046151d5565b6008602052600090815260409020805460018201546002909201546001600160801b0380831693600160801b938490048216938183169391048216911685565b604080516001600160801b03968716815294861660208601529285169284019290925283166060830152909116608082015260a0016102be565b61034a6107ac3660046151d5565b611e01565b6003546102ef90600160801b90046001600160801b031681565b61034a60065481565b61036b6107e2366004615737565b611e43565b61036b6107f536600461588f565b612049565b61080d6108083660046151d5565b61210c565b6040516001600160e01b0390911681526020016102be565b6104ff6108333660046151bc565b600d602052600090815260409020546001600160801b0380821691600160801b90041682565b61031f7f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c81565b6108da61088e3660046151d5565b600a602052600090815260409020805460019091015463ffffffff80831692600160201b808204831693600160401b9092046001600160c01b031692821691046001600160e01b031685565b6040805163ffffffff968716815294861660208601526001600160c01b03909316928401929092529290921660608201526001600160e01b03909116608082015260a0016102be565b61036b61093136600461551e565b612208565b6002805461094390615965565b80601f016020809104026020016040519081016040528092919081815260200182805461096f90615965565b80156109bc5780601f10610991576101008083540402835291602001916109bc565b820191906000526020600020905b81548152906001019060200180831161099f57829003601f168201915b505050505081565b33600090815260086020526040812084156109eb576109e284612383565b6109eb846123c4565b82156109f9576109f961258e565b60018101546001600160801b0316915081610a415760405162461bcd60e51b815260206004820152600360248201526204341360ec1b60448201526064015b60405180910390fd5b6001810180546001600160801b03191690819055600160801b90046001600160801b03908116908316811015610ab9576001820180546001600160801b031690556000610a8e82856159b0565b90506000610aa4826001600160801b03166126e7565b9050610ab081866159b0565b94505050610ae3565b610ac383826159b0565b6001830180546001600160801b03928316600160801b0292169190911790555b610b0933846001600160801b0316610af961272e565b6001600160a01b03169190612797565b6040516001600160801b038416815233907fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a9060200160405180910390a250509392505050565b6001600160a01b038083166000908152600a60209081526040808320938516835260089091528120549091906001600160801b031680610bae57506001600160a01b0383166000908152600390910160205260409020549050610c21565b6001600160a01b03841660009081526002830160209081526040808320546003860190925290912054670de0b6b3a764000082610bea8961210c565b6001600160e01b0316610bfd91906159d8565b610c0790856159ef565b610c119190615a0e565b610c1b9190615a30565b93505050505b92915050565b80610c3181612383565b81610c3b816123c4565b610c4361258e565b505050565b610c506127ed565b816000610c5d8282612860565b6001600160a01b0384166000908152600a602052604090206001015463ffffffff80851691161115610cc9576001600160a01b0384166000908152600a602052604090206001810154815467ffffffff00000000191663ffffffff909116600160201b02179055610cff565b6001600160a01b0384166000908152600a60205260409020805467ffffffff000000001916600160201b63ffffffff8616021790555b6001600160a01b0384166000818152600a6020908152604091829020549151600160201b90920463ffffffff1682527f21b2dd8950fc3a17e42d75bdfba3bf13f5a451f2d4b1dab7ab7f8d44f8a06926910160405180910390a250505050565b6000610d69612930565b610d728261298e565b90505b919050565b610d82612a6b565b60016000541415610dd55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a38565b6001600090815560075460ff16905b81811015610e2057600081815260096020526040902054610e0e906001600160a01b031684612acb565b80610e1881615a48565b915050610de4565b50506000805550565b610e3161258e565b565b610e3b612bbf565b6001600160a01b0383166000908152600a60205260409020805463ffffffff191663ffffffff8316179055610c438383612c12565b60016000541415610ec35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a38565b600160009081555b8151811015610f0957610ef7828281518110610ee957610ee9615a63565b602002602001015133612acb565b80610f0181615a48565b915050610ecb565b505060008055565b600080600080600080600080600080610f298b611515565b50505050509050610f3861258e565b3360009081526008602052604081206003549091906001600160801b031615801590610f70575060028201546001600160801b031615155b15610fa15760035460028301546001600160801b0391821691610f949116856159ef565b610f9e9190615a0e565b90505b6000600e6000336001600160a01b03166001600160a01b031681526020019081526020016000206000600f6000336001600160a01b03166001600160a01b0316815260200190815260200160002060000160009054906101000a90046001600160801b03166001600160801b0316815260200190815260200160002090506000600e6000336001600160a01b03166001600160a01b031681526020019081526020016000206000600f6000336001600160a01b03166001600160a01b0316815260200190815260200160002060000160109054906101000a90046001600160801b03166001600160801b0316815260200190815260200160002090508360020160009054906101000a90046001600160801b03168460000160109054906101000a90046001600160801b03168560010160009054906101000a90046001600160801b03168660010160109054906101000a90046001600160801b0316868660000160009054906101000a90046001600160801b03168760000160109054906101000a90046001600160801b03168760000160009054906101000a90046001600160801b03168860000160109054906101000a90046001600160801b0316886001600160801b03169850876001600160801b03169750866001600160801b03169650856001600160801b03169550836001600160801b03169350826001600160801b03169250816001600160801b03169150806001600160801b031690509d509d509d509d509d509d509d509d509d5050505050509193959799909294969850565b606060006111ee612f50565b856111f881612383565b86611202816123c4565b61120a612fae565b611215888888612fec565b60048990556007805463ffffff00191661010062ffffff8a1602179055909450925061124085613368565b60075460405188815261010090910462ffffff16907f5c09fbfaf801f04455c21a7b761e0ef929b1c114f8e95d5ecc5179e4117416dd9060200160405180910390a2505094509492505050565b6001600160a01b0381166000908152600a6020526040812054610d72904290600160201b900463ffffffff16613373565b6112c66127ed565b6001600160a01b0381166000908152600b60205260409020805460ff191660011790556112f281613388565b6001600160a01b03166000908152600a6020526040812081815560010155565b61131a612bbf565b60018054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527f595b89b61981754cfd748b6a577e0310ba8a098a483e3e3bac5feab923d7b3f4906020015b60405180910390a150565b8061138181612383565b81610c43816123c4565b611393612bbf565b8261139d816134ac565b6001600160a01b0384166000908152600a60209081526040808320600b9092529091205460ff16156113fa5760405162461bcd60e51b8152600401610a38906020808252600490820152631513d09360e21b604082015260600190565b63ffffffff8416158015906114175750600181015463ffffffff16155b61144c5760405162461bcd60e51b8152600401610a38906020808252600490820152634243464760e01b604082015260600190565b600754600560ff909116111561148d5760405162461bcd60e51b8152600401610a38906020808252600490820152630a89a82b60e31b604082015260600190565b6007805460ff908116600090815260096020526040812080546001600160a01b0319166001600160a01b038a16179055825490911691906114cd83615a79565b82546101009290920a60ff81810219909316919092169190910217905550805463ffffffff191663ffffffff8516178155821561150e5761150e8584612c12565b5050505050565b6000806000806000808661152881612383565b87611532816123c4565b6000805b8a5181101561161d577f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b0316634e89a7118c838151811061158057611580615a63565b60200260200101516040518263ffffffff1660e01b81526004016115b391906001600160a01b0391909116815260200190565b6020604051808303816000875af11580156115d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f69190615a99565b611609906001600160801b031683615a30565b91508061161581615a48565b915050611536565b50600c546001600160801b038082166000908152600d6020526040808220600160801b9485900484168352912060035491549054949e9183169d508083169c5083900482169a5083821699509190920490911695509350505050565b60015461010090046001600160a01b0316331480156116a8575060015461ffff600160a81b9091048116908216105b6116da5760405162461bcd60e51b815260206004820152600360248201526246454560e81b6044820152606401610a38565b6001805461ffff60a81b1916600160a81b61ffff8416908102919091179091556040519081527f11878e6c9d396e93aa9470747ab6ae3baa13958d1df62e803a0b223c652a564b9060200161136c565b6117326127ed565b8161173c816134ac565b82611746816134fb565b61175a6001600160a01b0385163385612797565b50505050565b611768613555565b158061180757507f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663194b42f86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f19190615ab6565b60075462ffffff91821661010090910490911611155b6118395760405162461bcd60e51b815260206004820152600360248201526229272360e91b6044820152606401610a38565b8161184381612383565b8261184d81613571565b83611857816123c4565b84518410801561191957507f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c6001600160a01b031663192434098686815181106118a3576118a3615a63565b60200260200101516040518263ffffffff1660e01b81526004016118d691906001600160a01b0391909116815260200190565b602060405180830381865afa1580156118f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119179190615ad3565b155b61194e5760405162461bcd60e51b8152600401610a38906020808252600490820152632129aa2960e11b604082015260600190565b60006001865161195e91906159d8565b90506000816001600160401b0381111561197a5761197a614f3a565b6040519080825280602002602001820160405280156119a3578160200160208202803683370190505b5090508115611b6f5760005b82811015611a0d578781815181106119c9576119c9615a63565b60200260200101518282815181106119e3576119e3615a63565b6001600160a01b039092166020928302919091019091015280611a0581615a48565b9150506119af565b5081861015611a6357868281518110611a2857611a28615a63565b6020026020010151818781518110611a4257611a42615a63565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6004546000611a7282896135a8565b611a7e906127106159d8565b9050600184118015611a905750600081115b15611b615783881415611aae57611aa782896135c4565b9150611ad7565b6000611aba83866135a8565b9050611ac6838a6135c4565b9250611ad3838a836135e1565b9250505b600080611ae56001876159d8565b905061271060005b82811015611b49576000611b0187836135a8565b905085611b10612710836159ef565b611b1a9190615a0e565b9050611b278583836135e1565b9450611b3381846159d8565b9250508080611b4190615a48565b915050611aed565b50611b558383836135e1565b60045550611b68915050565b6127106004555b5050611b75565b60006004555b611b7e816135fb565b7f7f705c9890e1e97666eb559f0069c51eb9c6d8e37d6628895f3a886cc57b572086888881518110611bb257611bb2615a63565b6020026020010151604051611bda9291909182526001600160a01b0316602082015260400190565b60405180910390a150505050505050565b611bf36127ed565b8051611c06906002906020840190614e31565b507fed41a19b397d8e610d8f9e6e52ab2bef7e51c9977a12cb779c7cd86747f8d5098160405161136c9190614f19565b82611c4081612383565b83611c4a81613571565b84611c54816123c4565b611c5c61258e565b611c653361360a565b6000856001600160801b031611611ca45760405162461bcd60e51b815260206004820152600360248201526204e44560ec1b6044820152606401610a38565b6000611cae61364f565b62ffffff81166000908152600d6020526040812080549293508892909190611ce09084906001600160801b0316615af0565b82546101009290920a6001600160801b03818102199093169183160217909155336000908152600e6020908152604080832062ffffff87168452909152812080548a94509092611d3291859116615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550611d6687878362ffffff166136d3565b611d6f81613368565b611d788161388e565b611d8286866138a7565b611d8b86613979565b6040516001600160801b038716815262ffffff82169033907f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159060200160405180910390a350505050505050565b611de1612bbf565b80611deb816134fb565b816000611df88282612860565b61175a84613388565b6001600160a01b0381166000908152600a602052604081208054611e3c9063ffffffff811690600160401b90046001600160c01b03166159ef565b9392505050565b611e4b613a10565b83611e5581612383565b84611e5f816123c4565b611e67612fae565b611e6f61258e565b611e783361360a565b611e828585613ac5565b94506000611e8f86613db5565b600380549192508791600090611eaf9084906001600160801b03166159b0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555060007f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663db6b687189846040518363ffffffff1660e01b8152600401611f23929190615b54565b6000604051808303816000875af1158015611f42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f6a9190810190615b67565b90506000611f7788613dea565b60405163e5c8848560e01b81529091506001600160a01b037f0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a169063e5c8848590611fce908c908690869033908d90600401615ca1565b600060405180830381600087803b158015611fe857600080fd5b505af1158015611ffc573d6000803e3d6000fd5b50506040516001600160801b038b1681523392507ff81ecc4c10049fe8abde0a7afff608b6ac1d4cf5f6d58673e466a418b47dbf57915060200160405180910390a2505050505050505050565b60015460ff16156120855760405162461bcd60e51b8152600401610a38906020808252600490820152631052539560e21b604082015260600190565b60208082015160018054604085015161ffff16600160a81b0261ffff60a81b196001600160a01b039094166101000293909316610100600160b81b031990911617919091179055815180516120de926002920190614e31565b506120ec8160800151613f71565b60045560608101516120fd906135fb565b506001805460ff191681179055565b6001600160a01b0381166000908152600a60205260408120600354600160801b90046001600160801b03166121555760010154600160201b90046001600160e01b031692915050565b600181015460009063ffffffff1661216c8561128d565b6121769190615da0565b63ffffffff1690508061219e575060010154600160201b90046001600160e01b031692915050565b600354825461220091600160801b90046001600160801b0316906121d290600160401b90046001600160c01b0316846159ef565b6121dc9190615a0e565b60018401546121fb9190600160201b90046001600160e01b0316615a30565b613fc9565b949350505050565b8261221281612383565b8361221c816123c4565b612224612fae565b61222c61258e565b6122353361360a565b61223f8484613ac5565b9350600061224b61364f565b336000908152600e6020908152604080832062ffffff8516845290915290208054919250869160109061228f908490600160801b90046001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555084600d60008362ffffff16815260200190815260200160002060000160108282829054906101000a90046001600160801b03166122f09190615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061232486868362ffffff16614036565b61232d81613368565b6123368161388e565b6040516001600160801b038616815262ffffff82169033907ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b5689060200160405180910390a3505050505050565b61238f81600654614115565b6123c15760405162461bcd60e51b81526020600482015260036024820152620aca6960eb1b6044820152606401610a38565b50565b7f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c6001600160a01b0316634c8900606040518163ffffffff1660e01b815260040160006040518083038186803b15801561241d57600080fd5b505afa158015612431573d6000803e3d6000fd5b505060408051808201909152600c546001600160801b03808216808452600160801b90920416602083015290925015905061258a57600081600001516001600160801b0316905060007f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663194b42f86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124fc9190615ab6565b62ffffff16905080821161175a576125148285614129565b6000835260208301516001600160801b0316156125695760208301516001600160801b031681811161254f5761254a8186614129565b612560565b60208401516001600160801b031684525b50600060208401525b825160208401516001600160801b03908116600160801b02911617600c5550505b5050565b604080518082018252600c546001600160801b038082168352600160801b918290048116602080850191909152336000908152600f82528590208551808701909652548083168087529390049091169084015290919015801590612612575081516001600160801b031615806126125750815181516001600160801b039182169116105b1561258a57805161262d906001600160801b03166001614590565b6000815260208101516001600160801b0316156126b25760208201516001600160801b03161580612677575081600001516001600160801b031681602001516001600160801b0316105b156126995761269481602001516001600160801b03166000614590565b6126aa565b60208101516001600160801b031681525b600060208201525b336000908152600f602090815260409091208251918301516001600160801b03908116600160801b0292169190911790555050565b60006126fa6126f58361298e565b6148a6565b9050610d757f0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e72826001600160801b0316610af95b6000306001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561276e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615dbd565b905090565b610c438363a9059cbb60e01b84846040516024016127b6929190615dda565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526148cf565b6127f56149a1565b610e315760405162461bcd60e51b815260206004820152603660248201527f53706f6f6c4f776e61626c653a3a6f6e6c794f776e65723a2043616c6c65722060448201527534b9903737ba103a34329029b837b7b61037bbb732b960511b6064820152608401610a38565b6001600160a01b0382166000908152600a602052604090206128818361210c565b8160010160046101000a8154816001600160e01b0302191690836001600160e01b031602179055506128b28361128d565b60018201805463ffffffff191663ffffffff929092169190911790556001600160a01b03821615610c43576128e78383610b50565b6001600160a01b0392909216600090815260038201602090815260408083209490945560018301546002909301905291909120600160201b9091046001600160e01b0316905550565b7f0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a6001600160a01b03163314610e315760405162461bcd60e51b81526020600482015260036024820152624f465760e81b6044820152606401610a38565b60007f0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e726001600160a01b0316634b6229016129c761272e565b846129d0614a2c565b60015460405160e086901b6001600160e01b03191681526001600160a01b0394851660048201526024810193909352908316604483015261010081049092166064820152600160a81b90910461ffff16608482015260a4016020604051808303816000875af1158015612a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d729190615df3565b336001600160a01b037f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c1614610e315760405162461bcd60e51b815260206004820152600560248201526413d0d5149360da1b6044820152606401610a38565b8181612ad78282612860565b6001600160a01b0384166000908152600a60205260409020805463ffffffff16612b295760405162461bcd60e51b815260206004820152600360248201526242544b60e81b6044820152606401610a38565b6001600160a01b03841660009081526003820160205260409020548015612bb7576001600160a01b038086166000908152600384016020526040812055612b739087168683612797565b846001600160a01b03167f540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e8783604051612bae929190615dda565b60405180910390a25b505050505050565b60015461010090046001600160a01b0316331480612be05750612be06149a1565b610e315760405162461bcd60e51b815260206004820152600360248201526213d3d160ea1b6044820152606401610a38565b816000612c1f8282612860565b6001600160a01b0384166000908152600a602052604090206001600160c01b03612c51670de0b6b3a7640000866159ef565b6001830154612c709190600160201b90046001600160e01b0316615a30565b1115612ca45760405162461bcd60e51b8152602060048201526003602482015262292a2160e91b6044820152606401610a38565b612cb96001600160a01b038616333087614a6c565b8054600090612cce9063ffffffff1642615e0c565b8254909150600160201b900463ffffffff164210612d8c578154612d139063ffffffff16612d04670de0b6b3a7640000886159ef565b612d0e9190615a0e565b614aa4565b82546001600160c01b0391909116600160401b026001600160401b038216811784556040805188815263ffffffff928316929093169190911760208301526001600160a01b038816917f6a6f77044107a33658235d41bedbbaf2fe9ccdceb313143c947a5e76e1ec8474910160405180910390a2612f12565b815463ffffffff808316600160201b909204161115612dd35760405162461bcd60e51b815260206004820152600360248201526250465360e81b6044820152606401610a38565b8154600090612df0904290600160201b900463ffffffff166159d8565b8354909150600090612e1290600160401b90046001600160c01b0316836159ef565b8454909150600090612e419063ffffffff1683612e37670de0b6b3a76400008c6159ef565b612d049190615a30565b85549091506001600160c01b03600160401b90910481169082161015612e8f5760405162461bcd60e51b815260206004820152600360248201526226292960e91b6044820152606401610a38565b84546001600160c01b038216600160401b026001600160401b03821681178755604080518b81526020810186905263ffffffff9283169383169390931790830152851660608201526001600160a01b038a16907e4f7db2945633cb30dd3539fd30caced03aa57f25ff7039750216e9321b94be9060800160405180910390a25050505b60018201805463ffffffff42811663ffffffff199092169190911790915582549116600160201b0267ffffffff000000001990911617905550505050565b7f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b03163314610e315760405162461bcd60e51b815260206004820152600360248201526204f53560ec1b6044820152606401610a38565b612fb6613555565b15610e315760405162461bcd60e51b8152600401610a38906020808252600490820152631394915160e21b604082015260600190565b606060008085516001600160401b0381111561300a5761300a614f3a565b604051908082528060200260200182016040528015613033578160200160208202803683370190505b509050600086516001600160401b0381111561305157613051614f3a565b60405190808252806020026020018201604052801561307a578160200160208202803683370190505b50905060008061308a8988614acd565b91509150600081116130c45760405162461bcd60e51b815260206004820152600360248201526213955360ea1b6044820152606401610a38565b600080808080805b8e5181101561322d5760006130e18f836135a8565b90506130ed8188615a30565b96506000808a848151811061310457613104615a63565b6020026020010151111561314757886127108b858151811061312857613128615a63565b602002602001015161313a91906159ef565b6131449190615a0e565b90505b818111156131be57600061315b83836159d8565b9050600a81101561316e5750505061321b565b6000826131886c0c9f2c9cd04674edea40000000846159ef565b6131929190615a0e565b9050808d86815181106131a7576131a7615a63565b602002602001018181525050600196505050613218565b818110156132185760006131d282846159d8565b9050600a8110156131e55750505061321b565b808d85815181106131f8576131f8615a63565b602090810291909101015261320d8189615a30565b975083965060019450505b50505b8061322581615a48565b9150506130cc565b5061271085146132655760405162461bcd60e51b815260206004820152600360248201526204250560ec1b6044820152606401610a38565b81801561326f5750805b6132a15760405162461bcd60e51b815260206004820152600360248201526213949160ea1b6044820152606401610a38565b505060008060005b8381116133355760008a82815181106132c4576132c4615a63565b60200260200101511115613323576000856127108c84815181106132ea576132ea615a63565b60200260200101516132fc91906159ef565b6133069190615a0e565b90506133138483836135e1565b935061331f8184615a30565b9250505b8061332d81615a48565b9150506132a9565b5061334e83613346836127106159d8565b8491906135e1565b6005819055979e979d50969b505050505050505050505050565b6123c1600c82614c23565b60008183116133825782611e3c565b50919050565b60075460ff1660005b81811015610c43576000818152600960205260409020546001600160a01b038481169116141561349a57600960006133ca6001856159d8565b8152602080820192909252604090810160009081205484825260099384905291812080546001600160a01b0319166001600160a01b03909316929092179091556134156001856159d8565b81526020810191909152604001600090812080546001600160a01b03191690556007805460ff169161344683615e2b565b91906101000a81548160ff021916908360ff16021790555050826001600160a01b03167f755c47ac85b75fe2251607db5a480aac818b88bb535814bf1e3c4784ae4f6baa60405160405180910390a2505050565b806134a481615a48565b915050613391565b6134b461272e565b6001600160a01b0316816001600160a01b031614156123c15760405162461bcd60e51b815260206004820152600360248201526213955560ea1b6044820152606401610a38565b6001600160a01b0381166000908152600a6020526040902054600160201b900463ffffffff1642116123c15760405162461bcd60e51b815260206004820152600360248201526229272360e91b6044820152606401610a38565b600754600090610100900462ffffff161561356e575060015b90565b60008151116123c15760405162461bcd60e51b81526020600482015260036024820152621394d560ea1b6044820152606401610a38565b60006135b582600e6159ef565b83901c613fff16905092915050565b60006135d182600e6159ef565b613fff901b198316905092915050565b60006135ee83600e6159ef565b6122009083901b85615a30565b61360481614c83565b60065550565b60075460ff1660005b81811015610c435760008181526009602052604090205461363d906001600160a01b031684612860565b8061364781615a48565b915050613613565b60007f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b03166359b4ad5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615ab6565b825182906000906136e6906001906159d8565b60045490915060005b828110156137db57600061370d8383896001600160801b0316614cb3565b90506001600160801b038116156137c8577f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b03166360fb0f5989848151811061375f5761375f615a63565b602002602001015183896040518463ffffffff1660e01b815260040161378793929190615e48565b600060405180830381600087803b1580156137a157600080fd5b505af11580156137b5573d6000803e3d6000fd5b5050505080856137c591906159b0565b94505b50806137d381615a48565b9150506136ef565b506001600160801b03831615612bb7577f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b03166360fb0f5987848151811061382c5761382c615a63565b602002602001015185876040518463ffffffff1660e01b815260040161385493929190615e48565b600060405180830381600087803b15801561386e57600080fd5b505af1158015613882573d6000803e3d6000fd5b50505050505050505050565b336000908152600f602052604090206123c19082614c23565b80156138f55761258a337f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242846001600160801b03166138e461272e565b6001600160a01b0316929190614a6c565b6040516320bae68360e21b81523360048201526001600160801b03831660248201527f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c6001600160a01b0316906382eb9a0c90604401600060405180830381600087803b15801561396557600080fd5b505af1158015612bb7573d6000803e3d6000fd5b33600090815260086020526040812080548392906139a19084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555080600360108282829054906101000a90046001600160801b03166139e99190615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555050565b7f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b0316636bbbfc626040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a929190615ad3565b15610e315760405162461bcd60e51b81526020600482015260036024820152622726a960e91b6044820152606401610a38565b33600090815260086020908152604080832060028101548154600f85528386208451808601909552546001600160801b03818116808752600160801b90920481169686019690965292949182169391169115613b9a57336000908152600e6020908152604080832084516001600160801b039081168552925290912054613b4d9116836159b0565b60208201519092506001600160801b031615613b9a57336000908152600e60209081526040808320848301516001600160801b039081168552925290912054613b979116836159b0565b91505b8580613bcb57506000836001600160801b0316118015613bcb5750866001600160801b0316836001600160801b0316145b15613c7f576002840180546001600160801b0319169055600380549397508793839190601090613c0c908490600160801b90046001600160801b03166159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550818460000160008282829054906101000a90046001600160801b0316613c5691906159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550613daa565b866001600160801b0316836001600160801b031610158015613caa57506000876001600160801b0316115b613cdc5760405162461bcd60e51b81526020600482015260036024820152620aea6960eb1b6044820152606401610a38565b6000613ce9838986614cda565b905080600360108282829054906101000a90046001600160801b0316613d0f91906159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550808560000160008282829054906101000a90046001600160801b0316613d5991906159b0565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508784038560020160006101000a8154816001600160801b0302191690836001600160801b03160217905550505b509495945050505050565b6003546000906001600160801b0390811690613de09084166c0c9f2c9cd04674edea400000006159ef565b610d729190615a0e565b336000908152600860209081526040808320600f83528184208251808401909352546001600160801b038082168452600160801b9091048116938301939093526002810154909284918291613e4191889116615af0565b83519091506001600160801b031615613ee357336000908152600e6020908152604080832086516001600160801b039081168552925290912054613e8e91600160801b9091041682615af0565b60208401519091506001600160801b031615613ee357336000908152600e60209081526040808320868301516001600160801b039081168552925290912054613ee091600160801b9091041682615af0565b90505b856001600160801b0316816001600160801b03161115613f44578354600160801b90046001600160801b0316613f1a818884614cda565b9250613f2683826159b0565b85546001600160801b03918216600160801b02911617855550613f5f565b83546001600160801b038082168655600160801b9091041691505b506001600160801b0316949350505050565b60008060005b8351811015613fc257613fae81858381518110613f9657613f96615a63565b6020026020010151846135e19092919063ffffffff16565b915080613fba81615a48565b915050613f77565b5092915050565b60006001600160e01b038211156140325760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610a38565b5090565b600061404183613db5565b905060005b845181101561150e577f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663b5c5f67286838151811061409057614090615a63565b60209081029190910101516040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024810185905260448101869052606401600060405180830381600087803b1580156140ea57600080fd5b505af11580156140fe573d6000803e3d6000fd5b50505050808061410d90615a48565b915050614046565b60008161412184614c83565b149392505050565b6003546001600160801b0316600080808061414387614d07565b905080156141dc576005546040516317c78b0960e01b81526001600160a01b037f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e24216916317c78b099161419c918a918c90600401615e72565b600060405180830381600087803b1580156141b657600080fd5b505af11580156141ca573d6000803e3d6000fd5b50506007805463ffffff001916905550505b60005b86518110156143685760008782815181106141fc576141fc615a63565b602002602001015190506000807f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b0316631e9a6950848d6040518363ffffffff1660e01b8152600401614257929190615dda565b60408051808303816000875af1158015614275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142999190615e97565b90925090506142a88289615af0565b97506142b48188615af0565b60405163a2b0de8b60e01b81529097506001600160a01b037f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242169063a2b0de8b906143059086908f90600401615dda565b602060405180830381865afa158015614322573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143469190615a99565b6143509087615af0565b9550505050808061436090615a48565b9150506141df565b506001600160801b038316156143fb576040516341c78ddd60e01b81526001600160801b03841660048201527f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b0316906341c78ddd90602401600060405180830381600087803b1580156143e257600080fd5b505af11580156143f6573d6000803e3d6000fd5b505050505b6000878152600d602052604090205461442490600160801b90046001600160801b0316866159b0565b9450600061443864174876e800600a615ec6565b6001600160801b0316866001600160801b031611158061445f57506001600160801b038316155b156144ce57614471620f424086615ec6565b905064174876e8006001600160801b03871610156144c95764174876e8006144998783615af0565b6001600160801b0316106144c15764174876e8008690038601950164174876e7ff19016144ff565b949094019360005b6144ff565b826001600160801b0316856001600160801b031610156144fc576144f58587878603614cda565b90506144ff565b50845b6145098187615af0565b600380546001600160801b039283166001600160801b03199091161790556040805180820182528383168152868316602080830191825260008d81526010909152838120925191518516600160801b02919094161790555189917f32568d6390d10efe40ae666165d7e927786da1a0bf60ad350fbd637da51dee0791a25050505050505050565b336000908152600860209081526040808320600e83528184208685529092529091208054600160801b90046001600160801b0316801561478f57600085815260106020908152604080832054600d909252822054614606916001600160801b03600160801b918290048116928692900416614cda565b60018501805491925082916000906146289084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550600080838660020160009054906101000a90046001600160801b03166146729190615af0565b905086156146c357336000908152600e60205260408120906146958a6001615a30565b81526020810191909152604001600020546146c090600160801b90046001600160801b031682615af0565b90505b836001600160801b0316816001600160801b03161115614724578554600160801b90046001600160801b03166146fa818684614cda565b925061470683826159b0565b87546001600160801b03918216600160801b0291161787555061473f565b85546001600160801b038082168855600160801b9091041691505b818660010160108282829054906101000a90046001600160801b03166147659190615af0565b82546101009290920a6001600160801b038181021990931691831602179091558654168655505050505b81546001600160801b0316801561487157600086815260106020908152604080832054600d9092528220546147d29184916001600160801b039182169116614cda565b60028601805491925082916000906147f49084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550818560000160108282829054906101000a90046001600160801b031661483e9190615af0565b82546001600160801b039182166101009390930a9283029190920219909116179055505082546001600160801b03191683555b604051869033907f961e41c19e060d605836d2061ee68d182e66923d6c75ab7685e39f63e9f8684d90600090a3505050505050565b60006001600160801b038211156140325760405162461bcd60e51b8152600401610a3890615ef5565b6000614924826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d269092919063ffffffff16565b805190915015610c4357808060200190518101906149429190615ad3565b610c435760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a38565b604051634c24e8cd60e01b81523360048201526000907f0000000000000000000000004f03f70a99e5c3b49d733ddd7458f80fa9b5a5b56001600160a01b031690634c24e8cd90602401602060405180830381865afa158015614a08573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615ad3565b6000306001600160a01b031663bdff15156040518163ffffffff1660e01b8152600401602060405180830381865afa15801561276e573d6000803e3d6000fd5b6040516001600160a01b038085166024830152831660448201526064810182905261175a9085906323b872dd60e01b906084016127b6565b60006001600160c01b038211156140325760405162461bcd60e51b8152600401610a3890615ef5565b606060008084516001600160401b03811115614aeb57614aeb614f3a565b604051908082528060200260200182016040528015614b14578160200160208202803683370190505b5090506000805b8651811015614c175760007f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663a2b0de8b898481518110614b6757614b67615a63565b6020026020010151896040518363ffffffff1660e01b8152600401614b8d929190615dda565b602060405180830381865afa158015614baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bce9190615a99565b6001600160801b0316905080848381518110614bec57614bec615a63565b6020908102919091010152614c018184615a30565b9250508080614c0f90615a48565b915050614b1b565b50909590945092505050565b81546001600160801b031615614c6a57815462ffffff82166001600160801b03909116101561258a5781546001600160801b031662ffffff91909116600160801b02179055565b81546001600160801b03191662ffffff82161782555050565b600081604051602001614c969190615f3c565b604051602081830303815290604052805190602001209050919050565b600061220061271083614cc687876135a8565b614cd091906159ef565b6126f59190615a0e565b6000612200826001600160801b0316846001600160801b0316866001600160801b0316614cd091906159ef565b600754600090610100900462ffffff16821415610d7557506001919050565b6060612200848460008585843b614d7f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a38565b600080866001600160a01b03168587604051614d9b9190615f7b565b60006040518083038185875af1925050503d8060008114614dd8576040519150601f19603f3d011682016040523d82523d6000602084013e614ddd565b606091505b5091509150614ded828286614df8565b979650505050505050565b60608315614e07575081611e3c565b825115614e175782518084602001fd5b8160405162461bcd60e51b8152600401610a389190614f19565b828054614e3d90615965565b90600052602060002090601f016020900481019282614e5f5760008555614ea5565b82601f10614e7857805160ff1916838001178555614ea5565b82800160010185558215614ea5579182015b82811115614ea5578251825591602001919060010190614e8a565b506140329291505b808211156140325760008155600101614ead565b60005b83811015614edc578181015183820152602001614ec4565b8381111561175a5750506000910152565b60008151808452614f05816020860160208601614ec1565b601f01601f19169290920160200192915050565b602081526000611e3c6020830184614eed565b80151581146123c157600080fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715614f7257614f72614f3a565b60405290565b604051606081016001600160401b0381118282101715614f7257614f72614f3a565b60405160a081016001600160401b0381118282101715614f7257614f72614f3a565b604051601f8201601f191681016001600160401b0381118282101715614fe457614fe4614f3a565b604052919050565b60006001600160401b0382111561500557615005614f3a565b5060051b60200190565b6001600160a01b03811681146123c157600080fd5b8035610d758161500f565b600082601f83011261504057600080fd5b8135602061505561505083614fec565b614fbc565b82815260059290921b8401810191818101908684111561507457600080fd5b8286015b8481101561509857803561508b8161500f565b8352918301918301615078565b509695505050505050565b6000806000606084860312156150b857600080fd5b83356150c381614f2c565b925060208401356001600160401b038111156150de57600080fd5b6150ea8682870161502f565b92505060408401356150fb81614f2c565b809150509250925092565b6000806040838503121561511957600080fd5b82356151248161500f565b915060208301356151348161500f565b809150509250929050565b60006020828403121561515157600080fd5b81356001600160401b0381111561516757600080fd5b6122008482850161502f565b803563ffffffff81168114610d7557600080fd5b6000806040838503121561519a57600080fd5b82356151a58161500f565b91506151b360208401615173565b90509250929050565b6000602082840312156151ce57600080fd5b5035919050565b6000602082840312156151e757600080fd5b8135611e3c8161500f565b60008060006060848603121561520757600080fd5b83356152128161500f565b92506020840135915061522760408501615173565b90509250925092565b6000602080838503121561524357600080fd5b82356001600160401b0381111561525957600080fd5b8301601f8101851361526a57600080fd5b803561527861505082614fec565b81815260059190911b8201830190838101908783111561529757600080fd5b928401925b82841015614ded5783356152af8161500f565b8252928401929084019061529c565b62ffffff811681146123c157600080fd5b600080600080608085870312156152e557600080fd5b84356001600160401b038111156152fb57600080fd5b6153078782880161502f565b94505060208501359250604085013591506060850135615326816152be565b939692955090935050565b600081518084526020808501945080840160005b83811015613daa57815187529582019590820190600101615345565b6040815260006153746040830185615331565b90508260208301529392505050565b60008060006060848603121561539857600080fd5b83356153a38161500f565b92506153b160208501615173565b9150604084013590509250925092565b600080604083850312156153d457600080fd5b82356153df8161500f565b946020939093013593505050565b803561ffff81168114610d7557600080fd5b60006020828403121561541157600080fd5b611e3c826153ed565b6000806040838503121561542d57600080fd5b82356001600160401b0381111561544357600080fd5b61544f8582860161502f565b95602094909401359450505050565b60006001600160401b0383111561547757615477614f3a565b61548a601f8401601f1916602001614fbc565b905082815283838301111561549e57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126154c657600080fd5b611e3c8383356020850161545e565b6000602082840312156154e757600080fd5b81356001600160401b038111156154fd57600080fd5b612200848285016154b5565b6001600160801b03811681146123c157600080fd5b60008060006060848603121561553357600080fd5b83356001600160401b0381111561554957600080fd5b6155558682870161502f565b935050602084013561556681615509565b915060408401356150fb81614f2c565b600082601f83011261558757600080fd5b8135602061559761505083614fec565b82815260059290921b840181019181810190868411156155b657600080fd5b8286015b8481101561509857803583529183019183016155ba565b600082601f8301126155e257600080fd5b6155ef6150508335614fec565b82358082526020808301929160051b85010185101561560d57600080fd5b602084015b6020853560051b86010181101561572e576001600160401b03808235111561563957600080fd5b8135860187603f82011261564c57600080fd5b61565c6150506020830135614fec565b602082810135808352908201919060051b83016040018a81111561567f57600080fd5b604084015b8181101561571757858135111561569a57600080fd5b803585016040818e03603f190112156156b257600080fd5b6156ba614f50565b6040820135815287606083013511156156d257600080fd5b6060820135820191508d605f8301126156ea57600080fd5b6156fc8e60408401356060850161545e565b60208201528086525050602084019350602081019050615684565b505086525050602093840193919091019050615612565b50949350505050565b6000806000806080858703121561574d57600080fd5b6001600160401b03808635111561576357600080fd5b615770878735880161502f565b945060208087013561578181615509565b9450604087013561579181614f2c565b93506060870135828111156157a557600080fd5b87016060818a0312156157b757600080fd5b6157bf614f78565b6157c98235614f2c565b8135815282820135848111156157de57600080fd5b8201601f81018b136157ef57600080fd5b80356157fd61505082614fec565b81815260059190911b8201850190858101908d83111561581c57600080fd5b8684015b8381101561585257888135111561583657600080fd5b6158458f898335880101615576565b8352918701918701615820565b50808786015250505050604082013592508383111561587057600080fd5b61587c8a8484016155d1565b6040820152969995985093965050505050565b6000602082840312156158a157600080fd5b81356001600160401b03808211156158b857600080fd5b9083019060a082860312156158cc57600080fd5b6158d4614f9a565b8235828111156158e357600080fd5b6158ef878286016154b5565b8252506158fe60208401615024565b602082015261590f604084016153ed565b604082015260608301358281111561592657600080fd5b6159328782860161502f565b60608301525060808301358281111561594a57600080fd5b61595687828601615576565b60808301525095945050505050565b600181811c9082168061597957607f821691505b6020821081141561338257634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001600160801b03838116908316818110156159d0576159d061599a565b039392505050565b6000828210156159ea576159ea61599a565b500390565b6000816000190483118215151615615a0957615a0961599a565b500290565b600082615a2b57634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115615a4357615a4361599a565b500190565b6000600019821415615a5c57615a5c61599a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b600060ff821660ff811415615a9057615a9061599a565b60010192915050565b600060208284031215615aab57600080fd5b8151611e3c81615509565b600060208284031215615ac857600080fd5b8151611e3c816152be565b600060208284031215615ae557600080fd5b8151611e3c81614f2c565b60006001600160801b03828116848216808303821115615b1257615b1261599a565b01949350505050565b600081518084526020808501945080840160005b83811015613daa5781516001600160a01b031687529582019590820190600101615b2f565b6040815260006153746040830185615b1b565b60006020808385031215615b7a57600080fd5b82516001600160401b03811115615b9057600080fd5b8301601f81018513615ba157600080fd5b8051615baf61505082614fec565b81815260059190911b82018301908381019087831115615bce57600080fd5b928401925b82841015614ded578351615be681615509565b82529284019290840190615bd3565b600081518084526020808501808196506005915083821b81018387016000805b87811015615c92578484038b5282518051808652908801908886019080891b87018a01855b82811015615c7c57888203601f190184528451805183528c015160408d8401819052615c6881850183614eed565b968e0196958e019593505050600101615c3a565b509d8a019d965050509287019250600101615c15565b50919998505050505050505050565b60a081526000615cb460a0830188615b1b565b82810360208481019190915287518083528882019282019060005b81811015615cf45784516001600160801b031683529383019391830191600101615ccf565b505087604086015260018060a01b038716606086015284810360808601526060810192508551151581528186015160608383015283815180865260808401915060808160051b8501019550848301925060005b81811015615d7557607f19858803018352615d63878551615331565b96509285019291850191600101615d47565b5050505060408601519150808303604082015250615d938282615bf5565b9998505050505050505050565b600063ffffffff838116908316818110156159d0576159d061599a565b600060208284031215615dcf57600080fd5b8151611e3c8161500f565b6001600160a01b03929092168252602082015260400190565b600060208284031215615e0557600080fd5b5051919050565b600063ffffffff808316818516808303821115615b1257615b1261599a565b600060ff821680615e3e57615e3e61599a565b6000190192915050565b6001600160a01b039390931683526001600160801b03919091166020830152604082015260600190565b606081526000615e856060830186615b1b565b60208301949094525060400152919050565b60008060408385031215615eaa57600080fd5b8251615eb581615509565b602084015190925061513481615509565b60006001600160801b0382811684821681151582840482111615615eec57615eec61599a565b02949350505050565b60208082526027908201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316040820152663238206269747360c81b606082015260800190565b815160009082906020808601845b83811015615f6f5781516001600160a01b031685529382019390820190600101615f4a565b50929695505050505050565b60008251615f8d818460208701614ec1565b919091019291505056fea26469706673582212207361c12125b82fd6bc8ae7a18788ffa318a06f25efb8bb8d0ec886b1b94a5f6464736f6c634300080b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e720000000000000000000000004f03f70a99e5c3b49d733ddd7458f80fa9b5a5b5
-----Decoded View---------------
Arg [0] : _spool (address): 0xe140bB5F424A53e0687bfC10F6845a5672D7e242
Arg [1] : _controller (address): 0xdD4051c3571C143b989C3227E8eB50983974835C
Arg [2] : _fastWithdraw (address): 0x4788c0A425Abb27721e05A39C417b69cbA6B065a
Arg [3] : _feeHandler (address): 0x3d3d0A7876D18770a21a5Ea05FeF211EBa808E72
Arg [4] : _spoolOwner (address): 0x4f03f70A99E5c3b49d733dDd7458f80Fa9B5A5B5
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242
Arg [1] : 000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c
Arg [2] : 0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a
Arg [3] : 0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e72
Arg [4] : 0000000000000000000000004f03f70a99e5c3b49d733ddd7458f80fa9b5a5b5
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.