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:
GearboxV3Strategy
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 99999 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import "@openzeppelin/token/ERC20/IERC20.sol";
import "@openzeppelin/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/utils/math/Math.sol";
import "../external/interfaces/strategies/gearbox/v3/IFarmingPool.sol";
import "../external/interfaces/strategies/gearbox/v3/IPoolV3.sol";
import "../interfaces/ISwapper.sol";
import "../libraries/PackedRange.sol";
import "../strategies/Strategy.sol";
error GearboxV3BeforeDepositCheckFailed();
// One asset: WETH || USDC
// One reward: GEAR
// no slippages needed
// Description:
// This is a Gearbox V3 strategy. WETH or USDC is deposited to it's equivalent
// Gearbox V3 pool, where it is lent via Compound/Aave. We receive "diesel"
// tokens (dTokens) following deposit. These tokens accrue value automatically.
//
// The dTokens are then deposited into a Gearbox farming pool to receive extra
// rewards, in the form of GEAR. this process mints sdTokens, 1:1 with dTokens.
// Therefore, we consider dTokens and sdTokens to be equivalent in value.
//
// Liquidity availability on redeem is subject to Aave/Compound rules.
contract GearboxV3Strategy is Strategy {
using SafeERC20 for IERC20;
/// @notice Swapper implementation
ISwapper public immutable swapper;
/// @notice GEAR token
/// @dev Reward token when participating in the Gearbox V3 protocol.
IERC20 public gear;
/// @notice dToken implementation (staking token)
IPoolV3 public dToken;
/// @notice sdToken implementation (LP token)
IFarmingPool public sdToken;
/// @notice exchangeRate at the last DHW.
uint256 private _lastExchangeRate;
/// @notice precision for yield calculation
uint256 private _mantissa;
/// @notice maximum balance allowed of the staking token
uint256 private constant _MAX_BALANCE = 1e32;
constructor(IAssetGroupRegistry assetGroupRegistry_, ISpoolAccessControl accessControl_, ISwapper swapper_)
Strategy(assetGroupRegistry_, accessControl_, NULL_ASSET_GROUP_ID)
{
if (address(swapper_) == address(0)) revert ConfigurationAddressZero();
swapper = swapper_;
}
function initialize(string memory strategyName_, uint256 assetGroupId_, IFarmingPool sdToken_)
external
initializer
{
__Strategy_init(strategyName_, assetGroupId_);
sdToken = sdToken_;
dToken = IPoolV3(sdToken_.stakingToken());
gear = IERC20(sdToken_.rewardsToken());
address[] memory tokens = assets();
if (tokens.length != 1 || tokens[0] != dToken.underlyingToken()) {
revert InvalidAssetGroup(assetGroupId());
}
_mantissa = 10 ** (dToken.decimals() * 2);
_lastExchangeRate = (_mantissa * dToken.expectedLiquidity()) / dToken.totalSupply();
}
function assetRatio() external pure override returns (uint256[] memory) {
uint256[] memory _assetRatio = new uint256[](1);
_assetRatio[0] = 1;
return _assetRatio;
}
function getUnderlyingAssetAmounts() external view returns (uint256[] memory amounts) {
amounts = new uint256[](1);
amounts[0] = _getdTokenValue(sdToken.balanceOf(address(this)));
}
/**
* @notice Nothing to swap as it's only one asset.
*/
function _swapAssets(address[] memory, uint256[] memory, SwapInfo[] calldata) internal override {}
function _compound(address[] calldata tokens, SwapInfo[] calldata swapInfo, uint256[] calldata)
internal
override
returns (int256 compoundedYieldPercentage)
{
if (swapInfo.length > 0) {
uint256 gearBalance = _getGearboxReward();
if (gearBalance > 0) {
gear.safeTransfer(address(swapper), gearBalance);
address[] memory tokensIn = new address[](1);
tokensIn[0] = address(gear);
uint256 swappedAmount = swapper.swap(tokensIn, swapInfo, tokens, address(this))[0];
if (swappedAmount > 0) {
uint256 sdTokenBalanceBefore = sdToken.balanceOf(address(this));
_depositToProtocolInternal(IERC20(tokens[0]), swappedAmount);
compoundedYieldPercentage =
_calculateYieldPercentage(sdTokenBalanceBefore, sdToken.balanceOf(address(this)));
}
}
}
}
function _getYieldPercentage(int256) internal override returns (int256 baseYieldPercentage) {
uint256 exchangeRateCurrent = (_mantissa * dToken.expectedLiquidity()) / dToken.totalSupply();
baseYieldPercentage = _calculateYieldPercentage(_lastExchangeRate, exchangeRateCurrent);
_lastExchangeRate = exchangeRateCurrent;
}
function _depositToProtocol(address[] calldata tokens, uint256[] memory amounts, uint256[] calldata)
internal
override
{
_depositToProtocolInternal(IERC20(tokens[0]), amounts[0]);
}
/**
* @notice Withdraw lp tokens from the GearboxV3 market
*/
function _redeemFromProtocol(address[] calldata, uint256 ssts, uint256[] calldata) internal override {
uint256 dTokenWithdrawAmount = (sdToken.balanceOf(address(this)) * ssts) / totalSupply();
_redeemFromProtocolInternal(dTokenWithdrawAmount);
}
function _emergencyWithdrawImpl(uint256[] calldata, address recipient) internal override {
uint256 sdTokenBalance = sdToken.balanceOf(address(this));
_redeemFromProtocolInternal(sdTokenBalance);
address[] memory tokens = assets();
IERC20(tokens[0]).safeTransfer(recipient, IERC20(tokens[0]).balanceOf(address(this)));
}
function _getUsdWorth(uint256[] memory exchangeRates, IUsdPriceFeedManager priceFeedManager)
internal
view
override
returns (uint256 usdValue)
{
uint256 sdTokenBalance = sdToken.balanceOf(address(this));
if (sdTokenBalance > 0) {
uint256 tokenValue = _getdTokenValue(sdTokenBalance);
address[] memory assetGroup = _assetGroupRegistry.listAssetGroup(assetGroupId());
usdValue = priceFeedManager.assetToUsdCustomPrice(assetGroup[0], tokenValue, exchangeRates[0]);
}
}
/**
* @dev Get value of the desired dToken amount in the asset token amount
* @param dTokenAmount dToken amount
* @return tokenAmount value of `dTokenAmount` in asset tokens
*/
function _getdTokenValue(uint256 dTokenAmount) private view returns (uint256) {
if (dTokenAmount == 0) {
return 0;
}
return dToken.previewRedeem(dTokenAmount);
}
function beforeDepositCheck(uint256[] memory, uint256[] calldata) public view override {
if (sdToken.balanceOf(address(this)) > _MAX_BALANCE) {
revert GearboxV3BeforeDepositCheckFailed();
}
}
function beforeRedeemalCheck(uint256, uint256[] calldata) public view override {}
function _depositToProtocolInternal(IERC20 token, uint256 amount) internal {
if (amount > 0) {
_resetAndApprove(token, address(dToken), amount);
uint256 shares = dToken.deposit(amount, address(this));
_resetAndApprove(dToken, address(sdToken), shares);
sdToken.deposit(shares);
}
}
function _redeemFromProtocolInternal(uint256 shares) internal {
if (shares > 0) {
sdToken.withdraw(shares);
dToken.redeem(shares, address(this), address(this));
}
}
function _getProtocolRewardsInternal() internal virtual override returns (address[] memory, uint256[] memory) {
address[] memory tokens = new address[](1);
uint256[] memory amounts = new uint256[](1);
tokens[0] = address(gear);
amounts[0] = _getGearboxReward();
return (tokens, amounts);
}
function _getGearboxReward() internal returns (uint256) {
sdToken.claim();
return gear.balanceOf(address(this));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the 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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.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));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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 (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/token/ERC20/IERC20.sol";
interface IFarmingPool is IERC20 {
event DistributorChanged(address oldDistributor, address newDistributor);
event RewardUpdated(uint256 reward, uint256 duration);
// View functions
function distributor() external view returns (address);
function stakingToken() external view returns (address);
function rewardsToken() external view returns (address);
function farmed(address account) external view returns (uint256);
// User functions
function deposit(uint256 amount) external;
function withdraw(uint256 amount) external;
function claim() external;
function exit() external;
// Owner functions
function setDistributor(address distributor_) external;
// Distributor functions
function startFarming(uint256 amount, uint256 period) external;
function rescueFunds(IERC20 token, uint256 amount) external;
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
pragma abicoder v1;
import {IERC4626} from "@openzeppelin/interfaces/IERC4626.sol";
import {IERC20Permit} from "@openzeppelin/token/ERC20/extensions/IERC20Permit.sol";
/// @title IVersion
/// @dev Declares a version function which returns the contract's version
interface IVersion {
/// @dev Returns contract version
function version() external view returns (uint256);
}
interface IPoolV3Events {
/// @notice Emitted when depositing liquidity with referral code
event Refer(address indexed onBehalfOf, uint256 indexed referralCode, uint256 amount);
/// @notice Emitted when credit account borrows funds from the pool
event Borrow(address indexed creditManager, address indexed creditAccount, uint256 amount);
/// @notice Emitted when credit account's debt is repaid to the pool
event Repay(address indexed creditManager, uint256 borrowedAmount, uint256 profit, uint256 loss);
/// @notice Emitted when incurred loss can't be fully covered by burning treasury's shares
event IncurUncoveredLoss(address indexed creditManager, uint256 loss);
/// @notice Emitted when new interest rate model contract is set
event SetInterestRateModel(address indexed newInterestRateModel);
/// @notice Emitted when new pool quota keeper contract is set
event SetPoolQuotaKeeper(address indexed newPoolQuotaKeeper);
/// @notice Emitted when new total debt limit is set
event SetTotalDebtLimit(uint256 limit);
/// @notice Emitted when new credit manager is connected to the pool
event AddCreditManager(address indexed creditManager);
/// @notice Emitted when new debt limit is set for a credit manager
event SetCreditManagerDebtLimit(address indexed creditManager, uint256 newLimit);
/// @notice Emitted when new withdrawal fee is set
event SetWithdrawFee(uint256 fee);
}
/// @title Pool V3 interface
interface IPoolV3 is IVersion, IPoolV3Events, IERC4626, IERC20Permit {
function addressProvider() external view returns (address);
function underlyingToken() external view returns (address);
function treasury() external view returns (address);
function withdrawFee() external view returns (uint16);
function creditManagers() external view returns (address[] memory);
function availableLiquidity() external view returns (uint256);
function expectedLiquidity() external view returns (uint256);
function expectedLiquidityLU() external view returns (uint256);
// ---------------- //
// ERC-4626 LENDING //
// ---------------- //
function depositWithReferral(uint256 assets, address receiver, uint256 referralCode)
external
returns (uint256 shares);
function mintWithReferral(uint256 shares, address receiver, uint256 referralCode)
external
returns (uint256 assets);
// --------- //
// BORROWING //
// --------- //
function totalBorrowed() external view returns (uint256);
function totalDebtLimit() external view returns (uint256);
function creditManagerBorrowed(address creditManager) external view returns (uint256);
function creditManagerDebtLimit(address creditManager) external view returns (uint256);
function creditManagerBorrowable(address creditManager) external view returns (uint256 borrowable);
function lendCreditAccount(uint256 borrowedAmount, address creditAccount) external;
function repayCreditAccount(uint256 repaidAmount, uint256 profit, uint256 loss) external;
// ------------- //
// INTEREST RATE //
// ------------- //
function interestRateModel() external view returns (address);
function baseInterestRate() external view returns (uint256);
function supplyRate() external view returns (uint256);
function baseInterestIndex() external view returns (uint256);
function baseInterestIndexLU() external view returns (uint256);
function lastBaseInterestUpdate() external view returns (uint40);
// ------ //
// QUOTAS //
// ------ //
function poolQuotaKeeper() external view returns (address);
function quotaRevenue() external view returns (uint256);
function lastQuotaRevenueUpdate() external view returns (uint40);
function updateQuotaRevenue(int256 quotaRevenueDelta) external;
function setQuotaRevenue(uint256 newQuotaRevenue) external;
// ------------- //
// CONFIGURATION //
// ------------- //
function setInterestRateModel(address newInterestRateModel) external;
function setPoolQuotaKeeper(address newPoolQuotaKeeper) external;
function setTotalDebtLimit(uint256 newLimit) external;
function setCreditManagerDebtLimit(address creditManager, uint256 newLimit) external;
function setWithdrawFee(uint256 newWithdrawFee) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
/* ========== STRUCTS ========== */
/**
* @notice Information needed to make a swap of assets.
* @custom:member swapTarget Contract executing the swap.
* @custom:member token Token to be swapped.
* @custom:member swapCallData Calldata describing the swap itself.
*/
struct SwapInfo {
address swapTarget;
address token;
bytes swapCallData;
}
/* ========== ERRORS ========== */
/**
* @notice Used when trying to do a swap via an exchange that is not allowed to execute a swap.
* @param exchange Exchange used.
*/
error ExchangeNotAllowed(address exchange);
/**
* @notice Used when trying to execute a swap but are not authorized.
* @param caller Caller of the swap method.
*/
error NotSwapper(address caller);
/* ========== INTERFACES ========== */
interface ISwapper {
/* ========== EVENTS ========== */
/**
* @notice Emitted when the exchange allowlist is updated.
* @param exchange Exchange that was updated.
* @param isAllowed Whether the exchange is allowed to be used in a swap or not after the update.
*/
event ExchangeAllowlistUpdated(address indexed exchange, bool isAllowed);
event Swapped(
address indexed receiver, address[] tokensIn, address[] tokensOut, uint256[] amountsIn, uint256[] amountsOut
);
/* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */
/**
* @notice Performs a swap of tokens with external contracts.
* - deposit tokens into the swapper contract
* - swapper will swap tokens based on swap info provided
* - swapper will return unswapped tokens to the receiver
* @param tokensIn Addresses of tokens available for the swap.
* @param swapInfo Information needed to perform the swap.
* @param tokensOut Addresses of tokens to swap to.
* @param receiver Receiver of unswapped tokens.
* @return amountsOut Amounts of `tokensOut` sent from the swapper to the receiver.
*/
function swap(
address[] calldata tokensIn,
SwapInfo[] calldata swapInfo,
address[] calldata tokensOut,
address receiver
) external returns (uint256[] memory amountsOut);
/**
* @notice Updates list of exchanges that can be used in a swap.
* @dev Requirements:
* - can only be called by user granted ROLE_SPOOL_ADMIN
* - exchanges and allowed arrays need to be of same length
* @param exchanges Addresses of exchanges.
* @param allowed Whether an exchange is allowed to be used in a swap.
*/
function updateExchangeAllowlist(address[] calldata exchanges, bool[] calldata allowed) external;
/* ========== EXTERNAL VIEW FUNCTIONS ========== */
/**
* @notice Checks if an exchange is allowed to be used in a swap.
* @param exchange Exchange to check.
* @return isAllowed True if the exchange is allowed to be used in a swap, false otherwise.
*/
function isExchangeAllowed(address exchange) external view returns (bool isAllowed);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
library PackedRange {
uint256 constant BITS = 128;
uint256 constant MAX = (1 << BITS) - 1;
function isWithinRange(uint256 range, uint256 value) internal pure returns (bool) {
return !((value < (range & MAX)) || (value > (range >> BITS)));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import "@openzeppelin/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/utils/math/Math.sol";
import "@openzeppelin-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "../interfaces/Constants.sol";
import "../interfaces/IAssetGroupRegistry.sol";
import "../interfaces/IMasterWallet.sol";
import "../interfaces/IStrategy.sol";
import "../interfaces/IStrategyRegistry.sol";
import "../interfaces/CommonErrors.sol";
import "../interfaces/Constants.sol";
import "../access/SpoolAccessControllable.sol";
/**
* @notice Used when initial locked strategy shares are already minted and strategy usd value is zero.
*/
error StrategyWorthIsZero();
abstract contract Strategy is ERC20Upgradeable, SpoolAccessControllable, IStrategy {
using SafeERC20 for IERC20;
/* ========== STATE VARIABLES ========== */
IAssetGroupRegistry internal immutable _assetGroupRegistry;
/// @notice Name of the strategy
string private _strategyName;
/// @dev ID of the asset group used by the strategy.
uint256 private immutable _assetGroupId;
/// @dev ID of the asset group used by the strategy.
uint256 private _assetGroupIdStorage;
// Only one of the above can be set. Use the `assetGroupId` function to read
// the correct one.
constructor(IAssetGroupRegistry assetGroupRegistry_, ISpoolAccessControl accessControl_, uint256 assetGroupId_)
SpoolAccessControllable(accessControl_)
{
if (address(assetGroupRegistry_) == address(0)) {
revert ConfigurationAddressZero();
}
_assetGroupRegistry = assetGroupRegistry_;
_assetGroupId = assetGroupId_;
}
function __Strategy_init(string memory strategyName_, uint256 assetGroupId_) internal onlyInitializing {
if (bytes(strategyName_).length == 0) revert InvalidConfiguration();
// asset group ID needs to be set exactly once,
// either in constructor or initializer
if (_assetGroupId == NULL_ASSET_GROUP_ID) {
if (assetGroupId_ == NULL_ASSET_GROUP_ID) {
revert InvalidAssetGroupIdInitialization();
}
_assetGroupIdStorage = assetGroupId_;
} else {
if (assetGroupId_ != NULL_ASSET_GROUP_ID) {
revert InvalidAssetGroupIdInitialization();
}
}
_assetGroupRegistry.validateAssetGroup(assetGroupId());
_strategyName = strategyName_;
__ERC20_init("Strategy Share Token", "SST");
}
/* ========== EXTERNAL VIEW FUNCTIONS ========== */
function assetGroupId() public view returns (uint256) {
return _assetGroupId > 0 ? _assetGroupId : _assetGroupIdStorage;
}
function assets() public view returns (address[] memory) {
return _assetGroupRegistry.listAssetGroup(assetGroupId());
}
function assetRatio() external view virtual returns (uint256[] memory);
function strategyName() external view returns (string memory) {
return _strategyName;
}
function beforeDepositCheck(uint256[] memory amounts, uint256[] calldata slippages) public virtual;
function beforeRedeemalCheck(uint256 ssts, uint256[] calldata slippages) public virtual;
/* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */
function doHardWork(StrategyDhwParameterBag calldata dhwParams) external returns (DhwInfo memory dhwInfo) {
_checkRole(ROLE_STRATEGY_REGISTRY, msg.sender);
bool depositNeeded;
uint256[] memory assetsToDeposit = new uint256[](dhwParams.assetGroup.length);
unchecked {
for (uint256 i; i < dhwParams.assetGroup.length; ++i) {
assetsToDeposit[i] = IERC20(dhwParams.assetGroup[i]).balanceOf(address(this));
if (assetsToDeposit[i] > 0) {
depositNeeded = true;
}
}
}
beforeDepositCheck(assetsToDeposit, dhwParams.slippages);
beforeRedeemalCheck(dhwParams.withdrawnShares, dhwParams.slippages);
// usdWorth[0]: usd worth before deposit / withdrawal
// usdWorth[1]: usd worth after deposit / withdrawal
uint256[2] memory usdWorth;
// Compound and get USD value.
{
dhwInfo.yieldPercentage = _getYieldPercentage(dhwParams.baseYield);
int256 compoundYield = _compound(dhwParams.assetGroup, dhwParams.compoundSwapInfo, dhwParams.slippages);
dhwInfo.yieldPercentage += compoundYield + compoundYield * dhwInfo.yieldPercentage / YIELD_FULL_PERCENT_INT;
}
// collect fees, mint SVTs relative to the yield generated
_collectPlatformFees(dhwInfo.yieldPercentage, dhwParams.platformFees);
usdWorth[0] = _getUsdWorth(dhwParams.exchangeRates, dhwParams.priceFeedManager);
uint256 matchedShares;
uint256 depositShareEquivalent;
uint256 mintedShares;
uint256 withdrawnShares = dhwParams.withdrawnShares;
// Calculate deposit share equivalent.
if (depositNeeded) {
uint256 valueToDeposit = dhwParams.priceFeedManager.assetToUsdCustomPriceBulk(
dhwParams.assetGroup, assetsToDeposit, dhwParams.exchangeRates
);
if (totalSupply() < INITIAL_LOCKED_SHARES) {
depositShareEquivalent = INITIAL_SHARE_MULTIPLIER * valueToDeposit;
} else if (usdWorth[0] > 0) {
depositShareEquivalent = totalSupply() * valueToDeposit / usdWorth[0];
} else {
revert StrategyWorthIsZero();
}
// Match withdrawals and deposits by taking smaller value as matched shares.
if (depositShareEquivalent < withdrawnShares) {
matchedShares = depositShareEquivalent;
} else {
matchedShares = withdrawnShares;
}
}
uint256[] memory withdrawnAssets = new uint256[](dhwParams.assetGroup.length);
bool withdrawn;
if (depositShareEquivalent > withdrawnShares) {
// Deposit is needed.
// - match if needed
if (matchedShares > 0) {
unchecked {
for (uint256 i; i < dhwParams.assetGroup.length; ++i) {
withdrawnAssets[i] = assetsToDeposit[i] * matchedShares / depositShareEquivalent;
assetsToDeposit[i] -= withdrawnAssets[i];
}
}
withdrawn = true;
}
// - swap assets
uint256[] memory assetsIn = new uint256[](assetsToDeposit.length);
if (dhwParams.swapInfo.length > 0) {
_swapAssets(dhwParams.assetGroup, assetsToDeposit, dhwParams.swapInfo);
for (uint256 i; i < dhwParams.assetGroup.length; ++i) {
assetsIn[i] = assetsToDeposit[i];
assetsToDeposit[i] = IERC20(dhwParams.assetGroup[i]).balanceOf(address(this)) - withdrawnAssets[i];
}
} else {
for (uint256 i; i < dhwParams.assetGroup.length; ++i) {
assetsIn[i] = assetsToDeposit[i];
}
}
// - deposit assets into the protocol
_depositToProtocol(dhwParams.assetGroup, assetsToDeposit, dhwParams.slippages);
usdWorth[1] = _getUsdWorth(dhwParams.exchangeRates, dhwParams.priceFeedManager);
// - mint SSTs
mintedShares = _mintStrategyShares(usdWorth[0], usdWorth[1]);
emit Deposited(mintedShares, usdWorth[1] - usdWorth[0], assetsIn, assetsToDeposit);
mintedShares += matchedShares;
} else if (withdrawnShares > depositShareEquivalent) {
// Withdrawal is needed.
// - match if needed
if (matchedShares > 0) {
unchecked {
withdrawnShares -= matchedShares;
mintedShares = matchedShares;
}
}
// - redeem shares from protocol
_redeemFromProtocol(dhwParams.assetGroup, withdrawnShares, dhwParams.slippages);
_burn(address(this), withdrawnShares);
withdrawn = true;
// - figure out how much was withdrawn
usdWorth[1] = _getUsdWorth(dhwParams.exchangeRates, dhwParams.priceFeedManager);
unchecked {
for (uint256 i; i < dhwParams.assetGroup.length; ++i) {
withdrawnAssets[i] = IERC20(dhwParams.assetGroup[i]).balanceOf(address(this));
}
}
emit Withdrawn(withdrawnShares, usdWorth[1], withdrawnAssets);
} else {
// Neither withdrawal nor deposit is needed.
// - match if needed
if (matchedShares > 0) {
mintedShares = withdrawnShares;
unchecked {
for (uint256 i; i < dhwParams.assetGroup.length; ++i) {
withdrawnAssets[i] = assetsToDeposit[i];
}
}
withdrawn = true;
}
usdWorth[1] = usdWorth[0];
}
// Transfer withdrawn assets to master wallet if needed.
if (withdrawn) {
unchecked {
for (uint256 i; i < dhwParams.assetGroup.length; ++i) {
IERC20(dhwParams.assetGroup[i]).safeTransfer(dhwParams.masterWallet, withdrawnAssets[i]);
}
}
}
dhwInfo.sharesMinted = mintedShares;
dhwInfo.assetsWithdrawn = withdrawnAssets;
dhwInfo.valueAtDhw = usdWorth[1];
dhwInfo.totalSstsAtDhw = totalSupply();
}
function redeemFast(
uint256 shares,
address masterWallet,
address[] calldata assetGroup,
uint256[] calldata slippages
) external returns (uint256[] memory) {
if (
!_accessControl.hasRole(ROLE_SMART_VAULT_MANAGER, msg.sender)
&& !_accessControl.hasRole(ROLE_STRATEGY_REGISTRY, msg.sender)
) {
revert NotFastRedeemer(msg.sender);
}
return _redeemShares(shares, address(this), masterWallet, assetGroup, slippages);
}
function redeemShares(uint256 shares, address redeemer, address[] calldata assetGroup, uint256[] calldata slippages)
external
returns (uint256[] memory)
{
_checkRole(ROLE_STRATEGY_REGISTRY, msg.sender);
return _redeemShares(shares, redeemer, redeemer, assetGroup, slippages);
}
/// @dev is only called when reallocating
function depositFast(
address[] calldata assetGroup,
uint256[] calldata exchangeRates,
IUsdPriceFeedManager priceFeedManager,
uint256[] calldata slippages,
SwapInfo[] calldata swapInfo
) external onlyRole(ROLE_SMART_VAULT_MANAGER, msg.sender) returns (uint256) {
// get amount of assets available to deposit
uint256[] memory assetsToDeposit = new uint256[](assetGroup.length);
for (uint256 i; i < assetGroup.length; ++i) {
assetsToDeposit[i] = IERC20(assetGroup[i]).balanceOf(address(this));
}
// swap assets
_swapAssets(assetGroup, assetsToDeposit, swapInfo);
uint256[] memory assetsDeposited = new uint256[](assetGroup.length);
for (uint256 i; i < assetGroup.length; ++i) {
assetsDeposited[i] = IERC20(assetGroup[i]).balanceOf(address(this));
}
// deposit assets
uint256 usdWorth0 = _getUsdWorth(exchangeRates, priceFeedManager);
_depositToProtocol(assetGroup, assetsDeposited, slippages);
uint256 usdWorth1 = _getUsdWorth(exchangeRates, priceFeedManager);
// mint SSTs
uint256 sstsToMint = _mintStrategyShares(usdWorth0, usdWorth1);
emit Deposited(sstsToMint, usdWorth1 - usdWorth0, assetsToDeposit, assetsDeposited);
return sstsToMint;
}
function claimShares(address smartVault, uint256 amount) external onlyRole(ROLE_SMART_VAULT_MANAGER, msg.sender) {
_transfer(address(this), smartVault, amount);
}
function releaseShares(address smartVault, uint256 amount)
external
onlyRole(ROLE_SMART_VAULT_MANAGER, msg.sender)
{
_transfer(smartVault, address(this), amount);
}
function emergencyWithdraw(uint256[] calldata slippages, address recipient)
external
onlyRole(ROLE_STRATEGY_REGISTRY, msg.sender)
{
_emergencyWithdrawImpl(slippages, recipient);
}
function getProtocolRewards() external onlyViewExecution returns (address[] memory, uint256[] memory) {
return _getProtocolRewardsInternal();
}
function getUsdWorth(uint256[] memory exchangeRates, IUsdPriceFeedManager priceFeedManager)
external
onlyRole(ROLE_SMART_VAULT_MANAGER, msg.sender)
returns (uint256)
{
return _getUsdWorth(exchangeRates, priceFeedManager);
}
/* ========== PRIVATE/INTERNAL FUNCTIONS ========== */
function _mintStrategyShares(uint256 usdWorthBefore, uint256 usdWorthAfter)
private
returns (uint256 mintedShares)
{
uint256 totalSupply_ = totalSupply();
if (totalSupply_ < INITIAL_LOCKED_SHARES) {
// multiply with usd worth after deposit as there are no other owned shares
mintedShares = usdWorthAfter * INITIAL_SHARE_MULTIPLIER;
unchecked {
uint256 lockedSharesLeftToMint = INITIAL_LOCKED_SHARES - totalSupply_;
if (mintedShares < lockedSharesLeftToMint) {
lockedSharesLeftToMint = mintedShares;
}
mintedShares -= lockedSharesLeftToMint;
_mint(INITIAL_LOCKED_SHARES_ADDRESS, lockedSharesLeftToMint);
}
} else if (usdWorthBefore > 0) {
mintedShares = (usdWorthAfter - usdWorthBefore) * totalSupply_ / usdWorthBefore;
} else {
revert StrategyWorthIsZero();
}
_mint(address(this), mintedShares);
}
function _redeemShares(
uint256 shares,
address shareOwner,
address recipient,
address[] calldata assetGroup,
uint256[] calldata slippages
) internal virtual returns (uint256[] memory) {
// redeem shares from protocol
uint256[] memory assetsWithdrawn = _redeemFromProtocolAndReturnAssets(assetGroup, shares, slippages);
_burn(shareOwner, shares);
// transfer assets to recipient (master wallet in case of redeemFast)
unchecked {
for (uint256 i; i < assetGroup.length; ++i) {
IERC20(assetGroup[i]).safeTransfer(recipient, assetsWithdrawn[i]);
}
}
return assetsWithdrawn;
}
/**
* @notice Calculate and mint platform performance fees based on the yield generated.
* @param yieldPct Yield generated since previous DHW. Full percent is `YIELD_FULL_PERCENT`.
* @param platformFees Platform fees info, containing information of the sice and recipient of the fees (SSTs).
* @return sharesMinted Returns newly minted shares representing the platform performance fees.
*/
function _collectPlatformFees(int256 yieldPct, PlatformFees calldata platformFees)
internal
virtual
returns (uint256 sharesMinted)
{
if (yieldPct > 0) {
uint256 uint256YieldPct = uint256(yieldPct);
uint256 yieldPctUsersPlusOne = uint256YieldPct
* (FULL_PERCENT - platformFees.ecosystemFeePct - platformFees.treasuryFeePct)
+ FULL_PERCENT * YIELD_FULL_PERCENT;
uint256 totalSupplyTimesYieldPct = totalSupply() * uint256YieldPct;
// mint new ecosystem fee SSTs
uint256 newEcosystemFeeSsts = totalSupplyTimesYieldPct * platformFees.ecosystemFeePct / yieldPctUsersPlusOne;
_mint(platformFees.ecosystemFeeReceiver, newEcosystemFeeSsts);
// mint new treasury fee SSTs
uint256 newTreasuryFeeSsts = totalSupplyTimesYieldPct * platformFees.treasuryFeePct / yieldPctUsersPlusOne;
_mint(platformFees.treasuryFeeReceiver, newTreasuryFeeSsts);
unchecked {
sharesMinted = newEcosystemFeeSsts + newTreasuryFeeSsts;
}
emit PlatformFeesCollected(address(this), sharesMinted);
}
}
function _redeemFromProtocolAndReturnAssets(address[] calldata tokens, uint256 ssts, uint256[] calldata slippages)
internal
virtual
returns (uint256[] memory withdrawnAssets)
{
withdrawnAssets = new uint256[](tokens.length);
for (uint256 i; i < tokens.length; ++i) {
withdrawnAssets[i] = IERC20(tokens[i]).balanceOf(address(this));
}
_redeemFromProtocol(tokens, ssts, slippages);
for (uint256 i; i < tokens.length; ++i) {
withdrawnAssets[i] = IERC20(tokens[i]).balanceOf(address(this)) - withdrawnAssets[i];
}
}
function _calculateYieldPercentage(uint256 previousValue, uint256 currentValue)
internal
pure
returns (int256 yieldPercentage)
{
if (currentValue > previousValue) {
yieldPercentage = int256((currentValue - previousValue) * YIELD_FULL_PERCENT / previousValue);
} else if (previousValue > currentValue) {
yieldPercentage = -int256((previousValue - currentValue) * YIELD_FULL_PERCENT / previousValue);
}
}
function _resetAndApprove(IERC20 token, address spender, uint256 amount) internal {
_resetAllowance(token, spender);
token.safeApprove(spender, amount);
}
function _resetAllowance(IERC20 token, address spender) internal {
if (token.allowance(address(this), spender) > 0) {
token.safeApprove(spender, 0);
}
}
function _isViewExecution() internal view returns (bool) {
return tx.origin == address(0);
}
function _compound(address[] calldata tokens, SwapInfo[] calldata compoundSwapInfo, uint256[] calldata slippages)
internal
virtual
returns (int256 compoundYield);
function _getYieldPercentage(int256 manualYield) internal virtual returns (int256);
/**
* @dev Swaps assets.
* @param tokens Addresses of tokens to swap.
* @param toSwap Available amounts to swap.
* @param swapInfo Information on how to swap.
*/
function _swapAssets(address[] memory tokens, uint256[] memory toSwap, SwapInfo[] calldata swapInfo)
internal
virtual;
/**
* @dev Deposits assets into the underlying protocol.
* @param tokens Addresses of asset tokens.
* @param amounts Amounts to deposit.
* @param slippages Slippages to guard depositing.
*/
function _depositToProtocol(address[] calldata tokens, uint256[] memory amounts, uint256[] calldata slippages)
internal
virtual;
/**
* @dev Redeems shares from the undelying protocol.
* @param tokens Addresses of asset tokens.
* @param ssts Amount of strategy tokens to redeem.
* @param slippages Slippages to guard redeemal.
*/
function _redeemFromProtocol(address[] calldata tokens, uint256 ssts, uint256[] calldata slippages)
internal
virtual;
function _emergencyWithdrawImpl(uint256[] calldata slippages, address recipient) internal virtual;
function _getUsdWorth(uint256[] memory exchangeRates, IUsdPriceFeedManager priceFeedManager)
internal
virtual
returns (uint256);
/**
* @dev Gets protocol rewards.
* @return tokens Addresses of reward tokens.
* @return amounts Amount of each reward token.
*/
function _getProtocolRewardsInternal()
internal
virtual
returns (address[] memory tokens, uint256[] memory amounts);
/* ========== MODIFIERS ========== */
modifier onlyViewExecution() {
require(_isViewExecution());
_;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 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://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or 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 {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*
* _Available since v4.7._
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[45] private __gap;
}// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; /// @dev Number of seconds in an average year. uint256 constant SECONDS_IN_YEAR = 31_556_926; /// @dev Number of seconds in an average year. int256 constant SECONDS_IN_YEAR_INT = 31_556_926; /// @dev Represents 100%. uint256 constant FULL_PERCENT = 100_00; /// @dev Represents 100%. int256 constant FULL_PERCENT_INT = 100_00; /// @dev Represents 100% for yield. int256 constant YIELD_FULL_PERCENT_INT = 10 ** 12; /// @dev Represents 100% for yield. uint256 constant YIELD_FULL_PERCENT = uint256(YIELD_FULL_PERCENT_INT); /// @dev Maximal management fee that can be set on a smart vault. Expressed in terms of FULL_PERCENT. uint256 constant MANAGEMENT_FEE_MAX = 5_00; /// @dev Maximal deposit fee that can be set on a smart vault. Expressed in terms of FULL_PERCENT. uint256 constant DEPOSIT_FEE_MAX = 5_00; /// @dev Maximal smart vault performance fee that can be set on a smart vault. Expressed in terms of FULL_PERCENT. uint256 constant SV_PERFORMANCE_FEE_MAX = 20_00; /// @dev Maximal ecosystem fee that can be set on the system. Expressed in terms of FULL_PERCENT. uint256 constant ECOSYSTEM_FEE_MAX = 20_00; /// @dev Maximal treasury fee that can be set on the system. Expressed in terms of FULL_PERCENT. uint256 constant TREASURY_FEE_MAX = 10_00; /// @dev Maximal risk score a strategy can be assigned. uint8 constant MAX_RISK_SCORE = 10_0; /// @dev Minimal risk score a strategy can be assigned. uint8 constant MIN_RISK_SCORE = 1; /// @dev Maximal value for risk tolerance a smart vautl can have. int8 constant MAX_RISK_TOLERANCE = 10; /// @dev Minimal value for risk tolerance a smart vault can have. int8 constant MIN_RISK_TOLERANCE = -10; /// @dev If set as risk provider, system will return fixed risk score values address constant STATIC_RISK_PROVIDER = address(0xaaa); /// @dev Fixed values to use if risk provider is set to STATIC_RISK_PROVIDER uint8 constant STATIC_RISK_SCORE = 1; /// @dev Maximal value of deposit NFT ID. uint256 constant MAXIMAL_DEPOSIT_ID = 2 ** 255; /// @dev Maximal value of withdrawal NFT ID. uint256 constant MAXIMAL_WITHDRAWAL_ID = 2 ** 256 - 1; /// @dev How many shares will be minted with a NFT uint256 constant NFT_MINTED_SHARES = 10 ** 6; /// @dev Each smart vault can have up to STRATEGY_COUNT_CAP strategies. uint256 constant STRATEGY_COUNT_CAP = 16; /// @dev Maximal DHW base yield. Expressed in terms of FULL_PERCENT. uint256 constant MAX_DHW_BASE_YIELD_LIMIT = 10_00; /// @dev Smart vault and strategy share multiplier at first deposit. uint256 constant INITIAL_SHARE_MULTIPLIER = 1000; /// @dev Strategy initial locked shares. These shares will never be unlocked. uint256 constant INITIAL_LOCKED_SHARES = 10 ** 12; /// @dev Strategy initial locked shares address. address constant INITIAL_LOCKED_SHARES_ADDRESS = address(0xdead); /// @dev Maximum number of guards a smart vault can be configured with uint256 constant MAX_GUARD_COUNT = 10; /// @dev Maximum number of actions a smart vault can be configured with uint256 constant MAX_ACTION_COUNT = 10; /// @dev ID of null asset group. Should not be used by any strategy or smart vault. uint256 constant NULL_ASSET_GROUP_ID = 0;
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import "@openzeppelin/token/ERC20/IERC20.sol";
/* ========== ERRORS ========== */
/**
* @notice Used when invalid ID for asset group is provided.
* @param assetGroupId Invalid ID for asset group.
*/
error InvalidAssetGroup(uint256 assetGroupId);
/**
* @notice Used when no assets are provided for an asset group.
*/
error NoAssetsProvided();
/**
* @notice Used when token is not allowed to be used as an asset.
* @param token Address of the token that is not allowed.
*/
error TokenNotAllowed(address token);
/**
* @notice Used when asset group already exists.
* @param assetGroupId ID of the already existing asset group.
*/
error AssetGroupAlreadyExists(uint256 assetGroupId);
/**
* @notice Used when given array is unsorted.
*/
error UnsortedArray();
/* ========== INTERFACES ========== */
interface IAssetGroupRegistry {
/* ========== EVENTS ========== */
/**
* @notice Emitted when token is allowed to be used as an asset.
* @param token Address of newly allowed token.
*/
event TokenAllowed(address indexed token);
/**
* @notice Emitted when asset group is registered.
* @param assetGroupId ID of the newly registered asset group.
*/
event AssetGroupRegistered(uint256 indexed assetGroupId);
/* ========== VIEW FUNCTIONS ========== */
/**
* @notice Checks if token is allowed to be used as an asset.
* @param token Address of token to check.
* @return isAllowed True if token is allowed, false otherwise.
*/
function isTokenAllowed(address token) external view returns (bool isAllowed);
/**
* @notice Gets number of registered asset groups.
* @return count Number of registered asset groups.
*/
function numberOfAssetGroups() external view returns (uint256 count);
/**
* @notice Gets asset group by its ID.
* @dev Requirements:
* - must provide a valid ID for the asset group
* @return assets Array of assets in the asset group.
*/
function listAssetGroup(uint256 assetGroupId) external view returns (address[] memory assets);
/**
* @notice Gets asset group length.
* @dev Requirements:
* - must provide a valid ID for the asset group
* @return length
*/
function assetGroupLength(uint256 assetGroupId) external view returns (uint256 length);
/**
* @notice Validates that provided ID represents an asset group.
* @dev Function reverts when ID does not represent an asset group.
* @param assetGroupId ID to validate.
*/
function validateAssetGroup(uint256 assetGroupId) external view;
/**
* @notice Checks if asset group composed of assets already exists.
* Will revert if provided assets cannot form an asset group.
* @param assets Assets composing the asset group.
* @return Asset group ID if such asset group exists, 0 otherwise.
*/
function checkAssetGroupExists(address[] calldata assets) external view returns (uint256);
/* ========== MUTATIVE FUNCTIONS ========== */
/**
* @notice Allows a token to be used as an asset.
* @dev Requirements:
* - can only be called by the ROLE_SPOOL_ADMIN
* @param token Address of token to be allowed.
*/
function allowToken(address token) external;
/**
* @notice Allows tokens to be used as assets.
* @dev Requirements:
* - can only be called by the ROLE_SPOOL_ADMIN
* @param tokens Addresses of tokens to be allowed.
*/
function allowTokenBatch(address[] calldata tokens) external;
/**
* @notice Registers a new asset group.
* @dev Requirements:
* - must provide at least one asset
* - all assets must be allowed
* - assets must be sorted
* - such asset group should not exist yet
* - can only be called by the ROLE_SPOOL_ADMIN
* @param assets Array of assets in the asset group.
* @return id Sequential ID assigned to the asset group.
*/
function registerAssetGroup(address[] calldata assets) external returns (uint256 id);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import "@openzeppelin/token/ERC20/IERC20.sol";
interface IMasterWallet {
/**
* @notice Transfers amount of token to the recipient.
* @dev Requirements:
* - caller must have role ROLE_MASTER_WALLET_MANAGER
* @param token Token to transfer.
* @param recipient Target of the transfer.
* @param amount Amount to transfer.
*/
function transfer(IERC20 token, address recipient, uint256 amount) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import "@openzeppelin/token/ERC20/IERC20.sol";
import "@openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {PlatformFees} from "./IStrategyRegistry.sol";
import "./ISwapper.sol";
import "./IUsdPriceFeedManager.sol";
/**
* @notice Struct holding information how to swap the assets.
* @custom:member slippage minumum output amount
* @custom: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
}
/**
* @notice Parameters for calling do hard work on strategy.
* @custom:member swapInfo Information for swapping assets before depositing into the protocol.
* @custom:member swapInfo Information for swapping rewards before depositing them back into the protocol.
* @custom:member slippages Slippages used to constrain depositing and withdrawing from the protocol.
* @custom:member assetGroup Asset group of the strategy.
* @custom:member exchangeRates Exchange rates for assets.
* @custom:member withdrawnShares Strategy shares withdrawn by smart vault.
* @custom:member masterWallet Master wallet.
* @custom:member priceFeedManager Price feed manager.
* @custom:member baseYield Base yield value, manual input for specific strategies.
* @custom:member platformFees Platform fees info.
*/
struct StrategyDhwParameterBag {
SwapInfo[] swapInfo;
SwapInfo[] compoundSwapInfo;
uint256[] slippages;
address[] assetGroup;
uint256[] exchangeRates;
uint256 withdrawnShares;
address masterWallet;
IUsdPriceFeedManager priceFeedManager;
int256 baseYield;
PlatformFees platformFees;
}
/**
* @notice Information about results of the do hard work.
* @custom:member sharesMinted Amount of strategy shares minted.
* @custom:member assetsWithdrawn Amount of assets withdrawn.
* @custom:member yieldPercentage Yield percentage from the previous DHW.
* @custom:member valueAtDhw Value of the strategy at the end of DHW.
* @custom:member totalSstsAtDhw Total SSTs at the end of DHW.
*/
struct DhwInfo {
uint256 sharesMinted;
uint256[] assetsWithdrawn;
int256 yieldPercentage;
uint256 valueAtDhw;
uint256 totalSstsAtDhw;
}
/**
* @notice Used when ghost strategy is called.
*/
error IsGhostStrategy();
/**
* @notice Used when user is not allowed to redeem fast.
* @param user User that tried to redeem fast.
*/
error NotFastRedeemer(address user);
/**
* @notice Used when asset group ID is not correctly initialized.
*/
error InvalidAssetGroupIdInitialization();
interface IStrategy is IERC20Upgradeable {
/* ========== EVENTS ========== */
event Deposited(
uint256 mintedShares, uint256 usdWorthDeposited, uint256[] assetsBeforeSwap, uint256[] assetsDeposited
);
event Withdrawn(uint256 withdrawnShares, uint256 usdWorthWithdrawn, uint256[] withdrawnAssets);
event PlatformFeesCollected(address indexed strategy, uint256 sharesMinted);
event Slippages(bool isDeposit, uint256 slippage, bytes data);
event BeforeDepositCheckSlippages(uint256[] amounts);
event BeforeRedeemalCheckSlippages(uint256 ssts);
/* ========== VIEW FUNCTIONS ========== */
/**
* @notice Gets strategy name.
* @return name Name of the strategy.
*/
function strategyName() external view returns (string memory name);
/**
* @notice Gets required ratio between underlying assets.
* @return ratio Required asset ratio for the strategy.
*/
function assetRatio() external view returns (uint256[] memory ratio);
/**
* @notice Gets asset group used by the strategy.
* @return id ID of the asset group.
*/
function assetGroupId() external view returns (uint256 id);
/**
* @notice Gets underlying assets for the strategy.
* @return assets Addresses of the underlying assets.
*/
function assets() external view returns (address[] memory assets);
/**
* @notice Gets underlying asset amounts for the strategy.
* @return amounts Amounts of the underlying assets.
*/
function getUnderlyingAssetAmounts() external view returns (uint256[] memory amounts);
/* ========== MUTATIVE FUNCTIONS ========== */
/**
* @dev Performs slippages check before depositing.
* @param amounts Amounts to be deposited.
* @param slippages Slippages to check against.
*/
function beforeDepositCheck(uint256[] memory amounts, uint256[] calldata slippages) external;
/**
* @dev Performs slippages check before redeemal.
* @param ssts Amount of strategy tokens to be redeemed.
* @param slippages Slippages to check against.
*/
function beforeRedeemalCheck(uint256 ssts, uint256[] calldata slippages) external;
/**
* @notice Does hard work:
* - compounds rewards
* - deposits into the protocol
* - withdraws from the protocol
* @dev Requirements:
* - caller must have role ROLE_STRATEGY_REGISTRY
* @param dhwParams Parameters for the do hard work.
* @return info Information about do the performed hard work.
*/
function doHardWork(StrategyDhwParameterBag calldata dhwParams) external returns (DhwInfo memory info);
/**
* @notice Claims strategy shares after do-hard-work.
* @dev Requirements:
* - caller must have role ROLE_SMART_VAULT_MANAGER
* @param smartVault Smart vault claiming shares.
* @param amount Amount of strategy shares to claim.
*/
function claimShares(address smartVault, uint256 amount) external;
/**
* @notice Releases shares back to strategy.
* @dev Requirements:
* - caller must have role ROLE_SMART_VAULT_MANAGER
* @param smartVault Smart vault releasing shares.
* @param amount Amount of strategy shares to release.
*/
function releaseShares(address smartVault, uint256 amount) external;
/**
* @notice Instantly redeems strategy shares for assets.
* @dev Requirements:
* - caller must have either role ROLE_SMART_VAULT_MANAGER or role ROLE_STRATEGY_REGISTRY
* @param shares Amount of shares to redeem.
* @param masterWallet Address of the master wallet.
* @param assetGroup Asset group of the strategy.
* @param slippages Slippages to guard redeeming.
* @return assetsWithdrawn Amount of assets withdrawn.
*/
function redeemFast(
uint256 shares,
address masterWallet,
address[] calldata assetGroup,
uint256[] calldata slippages
) external returns (uint256[] memory assetsWithdrawn);
/**
* @notice Instantly redeems strategy shares for assets.
* @param shares Amount of shares to redeem.
* @param redeemer Address of he redeemer, owner of SSTs.
* @param assetGroup Asset group of the strategy.
* @param slippages Slippages to guard redeeming.
* @return assetsWithdrawn Amount of assets withdrawn.
*/
function redeemShares(uint256 shares, address redeemer, address[] calldata assetGroup, uint256[] calldata slippages)
external
returns (uint256[] memory assetsWithdrawn);
/**
* @notice Instantly deposits into the protocol.
* @dev Requirements:
* - caller must have role ROLE_SMART_VAULT_MANAGER
* @param assetGroup Asset group of the strategy.
* @param exchangeRates Asset to USD exchange rates.
* @param priceFeedManager Price feed manager contract.
* @param slippages Slippages to guard depositing.
* @param swapInfo Information for swapping assets before depositing into the protocol.
* @return sstsMinted Amount of SSTs minted.
*/
function depositFast(
address[] calldata assetGroup,
uint256[] calldata exchangeRates,
IUsdPriceFeedManager priceFeedManager,
uint256[] calldata slippages,
SwapInfo[] calldata swapInfo
) external returns (uint256 sstsMinted);
/**
* @notice Instantly withdraws assets, bypassing shares mechanism.
* Transfers withdrawn assets to the emergency withdrawal wallet.
* @dev Requirements:
* - caller must have role ROLE_STRATEGY_REGISTRY
* @param slippages Slippages to guard redeeming.
* @param recipient Recipient address
*/
function emergencyWithdraw(uint256[] calldata slippages, address recipient) external;
/**
* @notice Gets USD worth of the strategy.
* @dev Requirements:
* - caller must have role ROLE_SMART_VAULT_MANAGER
* @param exchangeRates Asset to USD exchange rates.
* @param priceFeedManager Price feed manager contract.
*/
function getUsdWorth(uint256[] memory exchangeRates, IUsdPriceFeedManager priceFeedManager)
external
returns (uint256 usdWorth);
/**
* @notice Gets protocol rewards.
* @dev Requirements:
* - can only be called in view-execution mode.
* @return tokens Addresses of reward tokens.
* @return amounts Amount of reward tokens available.
*/
function getProtocolRewards() external returns (address[] memory tokens, uint256[] memory amounts);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import "./ISwapper.sol";
import {DhwInfo} from "./IStrategy.sol";
import "../libraries/uint16a16Lib.sol";
/* ========== ERRORS ========== */
/**
* @notice Used when trying to register an already registered strategy.
* @param address_ Address of already registered strategy.
*/
error StrategyAlreadyRegistered(address address_);
/**
* @notice Used when DHW was not run yet for a strategy index.
* @param strategy Address of the strategy.
* @param strategyIndex Index of the strategy.
*/
error DhwNotRunYetForIndex(address strategy, uint256 strategyIndex);
/**
* @notice Used when provided token list is invalid.
*/
error InvalidTokenList();
/**
* @notice Used when ghost strategy is used.
*/
error GhostStrategyUsed();
/**
* @notice Used when syncing vault that is already fully synced.
*/
error NothingToSync();
/**
* @notice Used when system tries to configure a too large ecosystem fee.
* @param ecosystemFeePct Requested ecosystem fee.
*/
error EcosystemFeeTooLarge(uint256 ecosystemFeePct);
/**
* @notice Used when system tries to configure a too large treasury fee.
* @param treasuryFeePct Requested treasury fee.
*/
error TreasuryFeeTooLarge(uint256 treasuryFeePct);
/**
* @notice Used when user tries to re-add a strategy that was previously removed from the system.
* @param strategy Strategy address
*/
error StrategyPreviouslyRemoved(address strategy);
/**
* @notice Represents change of state for a strategy during a DHW.
* @custom:member exchangeRates Exchange rates between assets and USD.
* @custom:member assetsDeposited Amount of assets deposited into the strategy.
* @custom:member sharesMinted Amount of strategy shares minted.
* @custom:member totalSSTs Amount of strategy shares at the end of the DHW.
* @custom:member totalStrategyValue Total strategy value at the end of the DHW.
* @custom:member dhwYields DHW yield percentage from the previous DHW.
*/
struct StrategyAtIndex {
uint256[] exchangeRates;
uint256[] assetsDeposited;
uint256 sharesMinted;
uint256 totalSSTs;
uint256 totalStrategyValue;
int256 dhwYields;
}
/**
* @notice Parameters for calling do hard work.
* @custom:member strategies Strategies to do-hard-worked upon, grouped by their asset group.
* @custom:member swapInfo Information for swapping assets before depositing into protocol. SwapInfo[] per each strategy.
* @custom:member compoundSwapInfo Information for swapping rewards before depositing them back into the protocol. SwapInfo[] per each strategy.
* @custom:member strategySlippages Slippages used to constrain depositing into and withdrawing from the protocol. uint256[] per strategy.
* @custom:member baseYields Base yield percentage the strategy created in the DHW period (applicable only for some strategies).
* @custom:member tokens List of all asset tokens involved in the do hard work.
* @custom:member exchangeRateSlippages Slippages used to constrain exchange rates for asset tokens. uint256[2] for each token.
* @custom:member validUntil Sets the maximum timestamp the user is willing to wait to start executing 'do hard work'.
*/
struct DoHardWorkParameterBag {
address[][] strategies;
SwapInfo[][][] swapInfo;
SwapInfo[][][] compoundSwapInfo;
uint256[][][] strategySlippages;
int256[][] baseYields;
address[] tokens;
uint256[2][] exchangeRateSlippages;
uint256 validUntil;
}
/**
* @notice Parameters for calling redeem fast.
* @custom:member strategies Addresses of strategies.
* @custom:member strategyShares Amount of shares to redeem.
* @custom:member assetGroup Asset group of the smart vault.
* @custom:member slippages Slippages to guard withdrawal.
*/
struct RedeemFastParameterBag {
address[] strategies;
uint256[] strategyShares;
address[] assetGroup;
uint256[][] withdrawalSlippages;
}
/**
* @notice Group of platform fees.
* @custom:member ecosystemFeeReciever Receiver of the ecosystem fees.
* @custom:member ecosystemFeePct Ecosystem fees. Expressed in FULL_PERCENT.
* @custom:member treasuryFeeReciever Receiver of the treasury fees.
* @custom:member treasuryFeePct Treasury fees. Expressed in FULL_PERCENT.
*/
struct PlatformFees {
address ecosystemFeeReceiver;
uint96 ecosystemFeePct;
address treasuryFeeReceiver;
uint96 treasuryFeePct;
}
/* ========== INTERFACES ========== */
interface IStrategyRegistry {
/* ========== EXTERNAL VIEW FUNCTIONS ========== */
/**
* @notice Returns address of emergency withdrawal wallet.
* @return emergencyWithdrawalWallet Address of the emergency withdrawal wallet.
*/
function emergencyWithdrawalWallet() external view returns (address emergencyWithdrawalWallet);
/**
* @notice Returns current do-hard-work indexes for strategies.
* @param strategies Strategies.
* @return dhwIndexes Current do-hard-work indexes for strategies.
*/
function currentIndex(address[] calldata strategies) external view returns (uint256[] memory dhwIndexes);
/**
* @notice Returns current strategy APYs.
* @param strategies Strategies.
*/
function strategyAPYs(address[] calldata strategies) external view returns (int256[] memory apys);
/**
* @notice Returns assets deposited into a do-hard-work index for a strategy.
* @param strategy Strategy.
* @param dhwIndex Do-hard-work index.
* @return assets Assets deposited into the do-hard-work index for the strategy.
*/
function depositedAssets(address strategy, uint256 dhwIndex) external view returns (uint256[] memory assets);
/**
* @notice Returns shares redeemed in a do-hard-work index for a strategy.
* @param strategy Strategy.
* @param dhwIndex Do-hard-work index.
* @return shares Shares redeemed in a do-hard-work index for the strategy.
*/
function sharesRedeemed(address strategy, uint256 dhwIndex) external view returns (uint256 shares);
/**
* @notice Gets timestamps when do-hard-works were performed.
* @param strategies Strategies.
* @param dhwIndexes Do-hard-work indexes.
* @return timestamps Timestamp for each pair of strategies and do-hard-work indexes.
*/
function dhwTimestamps(address[] calldata strategies, uint16a16 dhwIndexes)
external
view
returns (uint256[] memory timestamps);
function getDhwYield(address[] calldata strategies, uint16a16 dhwIndexes)
external
view
returns (int256[] memory yields);
/**
* @notice Returns state of strategies at do-hard-work indexes.
* @param strategies Strategies.
* @param dhwIndexes Do-hard-work indexes.
* @return states State of each strategy at corresponding do-hard-work index.
*/
function strategyAtIndexBatch(address[] calldata strategies, uint16a16 dhwIndexes, uint256 assetGroupLength)
external
view
returns (StrategyAtIndex[] memory states);
/**
* @notice Gets required asset ratio for strategy at last DHW.
* @param strategy Address of the strategy.
* @return assetRatio Asset ratio.
*/
function assetRatioAtLastDhw(address strategy) external view returns (uint256[] memory assetRatio);
/**
* @notice Gets set platform fees.
* @return fees Set platform fees.
*/
function platformFees() external view returns (PlatformFees memory fees);
/* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */
/**
* @notice Registers a strategy into the system.
* @dev Requirements:
* - caller must have role ROLE_SPOOL_ADMIN
* @param strategy Address of strategy to register.
* @param apy Apy of the strategy at the time of the registration.
*/
function registerStrategy(address strategy, int256 apy) external;
/**
* @notice Removes strategy from the system.
* @dev Requirements:
* - caller must have role ROLE_SMART_VAULT_MANAGER
* @param strategy Strategy to remove.
*/
function removeStrategy(address strategy) external;
/**
* @notice Sets ecosystem fee.
* @dev Requirements:
* - caller must have role ROLE_SPOOL_ADMIN
* @param ecosystemFeePct Ecosystem fee to set. Expressed in terms of FULL_PERCENT.
*/
function setEcosystemFee(uint96 ecosystemFeePct) external;
/**
* @notice Sets receiver of the ecosystem fees.
* @dev Requirements:
* - caller must have role ROLE_SPOOL_ADMIN
* @param ecosystemFeeReceiver Receiver to set.
*/
function setEcosystemFeeReceiver(address ecosystemFeeReceiver) external;
/**
* @notice Sets treasury fee.
* @dev Requirements:
* - caller must have role ROLE_SPOOL_ADMIN
* @param treasuryFeePct Treasury fee to set. Expressed in terms of FULL_PERCENT.
*/
function setTreasuryFee(uint96 treasuryFeePct) external;
/**
* @notice Sets treasury fee receiver.
* @dev Requirements:
* - caller must have role ROLE_SPOOL_ADMIN
* @param treasuryFeeReceiver Receiver to set.
*/
function setTreasuryFeeReceiver(address treasuryFeeReceiver) external;
/**
* @notice Does hard work on multiple strategies.
* @dev Requirements:
* - caller must have role ROLE_DO_HARD_WORKER
* @param dhwParams Parameters for do hard work.
*/
function doHardWork(DoHardWorkParameterBag calldata dhwParams) external;
/**
* @notice Adds deposits to strategies to be processed at next do-hard-work.
* @dev Requirements:
* - caller must have role ROLE_SMART_VAULT_MANAGER
* @param strategies Strategies to which to add deposit.
* @param amounts Amounts of assets to add to each strategy.
* @return strategyIndexes Current do-hard-work indexes for the strategies.
*/
function addDeposits(address[] calldata strategies, uint256[][] calldata amounts)
external
returns (uint16a16 strategyIndexes);
/**
* @notice Adds withdrawals to strategies to be processed at next do-hard-work.
* @dev Requirements:
* - caller must have role ROLE_SMART_VAULT_MANAGER
* @param strategies Strategies to which to add withdrawal.
* @param strategyShares Amounts of strategy shares to add to each strategy.
* @return strategyIndexes Current do-hard-work indexes for the strategies.
*/
function addWithdrawals(address[] calldata strategies, uint256[] calldata strategyShares)
external
returns (uint16a16 strategyIndexes);
/**
* @notice Instantly redeems strategy shares for assets.
* @dev Requirements:
* - caller must have role ROLE_SMART_VAULT_MANAGER
* @param redeemFastParams Parameters for fast redeem.
* @return withdrawnAssets Amount of assets withdrawn.
*/
function redeemFast(RedeemFastParameterBag calldata redeemFastParams)
external
returns (uint256[] memory withdrawnAssets);
/**
* @notice Claims withdrawals from the strategies.
* @dev Requirements:
* - caller must have role ROLE_SMART_VAULT_MANAGER
* - DHWs must be run for withdrawal indexes.
* @param strategies Addresses if strategies from which to claim withdrawal.
* @param dhwIndexes Indexes of strategies when withdrawal was made.
* @param strategyShares Amount of strategy shares that was withdrawn.
* @return assetsWithdrawn Amount of assets withdrawn from strategies.
*/
function claimWithdrawals(address[] calldata strategies, uint16a16 dhwIndexes, uint256[] calldata strategyShares)
external
returns (uint256[] memory assetsWithdrawn);
/**
* @notice Redeems strategy shares.
* Used by recipients of platform fees.
* @param strategies Strategies from which to redeem.
* @param shares Amount of shares to redeem from each strategy.
* @param withdrawalSlippages Slippages to guard redeemal process.
*/
function redeemStrategyShares(
address[] calldata strategies,
uint256[] calldata shares,
uint256[][] calldata withdrawalSlippages
) external;
/**
* @notice Strategy was registered
* @param strategy Strategy address
*/
event StrategyRegistered(address indexed strategy);
/**
* @notice Strategy was removed
* @param strategy Strategy address
*/
event StrategyRemoved(address indexed strategy);
/**
* @notice Strategy DHW was executed
* @param strategy Strategy address
* @param dhwIndex DHW index
* @param dhwInfo DHW info
*/
event StrategyDhw(address indexed strategy, uint256 dhwIndex, DhwInfo dhwInfo);
/**
* @notice Ecosystem fee configuration was changed
* @param feePct Fee percentage value
*/
event EcosystemFeeSet(uint256 feePct);
/**
* @notice Ecosystem fee receiver was changed
* @param ecosystemFeeReceiver Receiver address
*/
event EcosystemFeeReceiverSet(address indexed ecosystemFeeReceiver);
/**
* @notice Treasury fee configuration was changed
* @param feePct Fee percentage value
*/
event TreasuryFeeSet(uint256 feePct);
/**
* @notice Treasury fee receiver was changed
* @param treasuryFeeReceiver Receiver address
*/
event TreasuryFeeReceiverSet(address indexed treasuryFeeReceiver);
/**
* @notice Emergency withdrawal wallet changed
* @param wallet Emergency withdrawal wallet address
*/
event EmergencyWithdrawalWalletSet(address indexed wallet);
/**
* @notice Strategy shares have been redeemed
* @param strategy Strategy address
* @param owner Address that owns the shares
* @param recipient Address that received the withdrawn funds
* @param shares Amount of shares that were redeemed
* @param assetsWithdrawn Amounts of withdrawn assets
*/
event StrategySharesRedeemed(
address indexed strategy,
address indexed owner,
address indexed recipient,
uint256 shares,
uint256[] assetsWithdrawn
);
/**
* @notice Strategy shares were fast redeemed
* @param strategy Strategy address
* @param shares Amount of shares redeemed
* @param assetsWithdrawn Amounts of withdrawn assets
*/
event StrategySharesFastRedeemed(address indexed strategy, uint256 shares, uint256[] assetsWithdrawn);
/**
* @notice Strategy APY value was updated
* @param strategy Strategy address
* @param apy New APY value
*/
event StrategyApyUpdated(address indexed strategy, int256 apy);
}
interface IEmergencyWithdrawal {
/**
* @notice Emitted when a strategy is emergency withdrawn from.
* @param strategy Strategy that was emergency withdrawn from.
*/
event StrategyEmergencyWithdrawn(address indexed strategy);
/**
* @notice Set a new address that will receive assets withdrawn if emergency withdrawal is executed.
* @dev Requirements:
* - caller must have role ROLE_SPOOL_ADMIN
* @param wallet Address to set as the emergency withdrawal wallet.
*/
function setEmergencyWithdrawalWallet(address wallet) external;
/**
* @notice Instantly withdraws assets from a strategy, bypassing shares mechanism.
* @dev Requirements:
* - caller must have role ROLE_EMERGENCY_WITHDRAWAL_EXECUTOR
* @param strategies Addresses of strategies.
* @param withdrawalSlippages Slippages to guard withdrawal.
* @param removeStrategies Whether to remove strategies from the system after withdrawal.
*/
function emergencyWithdraw(
address[] calldata strategies,
uint256[][] calldata withdrawalSlippages,
bool removeStrategies
) external;
}// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; /** * @notice Used when an array has invalid length. */ error InvalidArrayLength(); /** * @notice Used when group of smart vaults or strategies do not have same asset group. */ error NotSameAssetGroup(); /** * @notice Used when configuring an address with a zero address. */ error ConfigurationAddressZero(); /** * @notice Used when constructor or intializer parameters are invalid. */ error InvalidConfiguration(); /** * @notice Used when fetched exchange rate is out of slippage range. */ error ExchangeRateOutOfSlippages(); /** * @notice Used when an invalid strategy is provided. * @param address_ Address of the invalid strategy. */ error InvalidStrategy(address address_); /** * @notice Used when doing low-level call on an address that is not a contract. * @param address_ Address of the contract */ error AddressNotContract(address address_); /** * @notice Used when invoking an only view execution and tx.origin is not address zero. * @param address_ Address of the tx.origin */ error OnlyViewExecution(address address_);
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import "../interfaces/ISpoolAccessControl.sol";
import "../interfaces/CommonErrors.sol";
import "./Roles.sol";
/**
* @notice Account access role verification middleware
*/
abstract contract SpoolAccessControllable {
/* ========== CONSTANTS ========== */
/**
* @dev Spool access control manager.
*/
ISpoolAccessControl internal immutable _accessControl;
/* ========== CONSTRUCTOR ========== */
/**
* @param accessControl_ Spool access control manager.
*/
constructor(ISpoolAccessControl accessControl_) {
if (address(accessControl_) == address(0)) revert ConfigurationAddressZero();
_accessControl = accessControl_;
}
/* ========== INTERNAL FUNCTIONS ========== */
/**
* @dev Reverts if an account is missing a role.\
* @param role Role to check for.
* @param account Account to check.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!_accessControl.hasRole(role, account)) {
revert MissingRole(role, account);
}
}
/**
* @dev Revert if an account is missing a role for a smartVault.
* @param smartVault Address of the smart vault.
* @param role Role to check for.
* @param account Account to check.
*/
function _checkSmartVaultRole(address smartVault, bytes32 role, address account) internal view {
if (!_accessControl.hasSmartVaultRole(smartVault, role, account)) {
revert MissingRole(role, account);
}
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (_accessControl.paused()) {
revert SystemPaused();
}
}
function _checkNonReentrant() internal view {
_accessControl.checkNonReentrant();
}
function _nonReentrantBefore() internal {
_accessControl.nonReentrantBefore();
}
function _nonReentrantAfter() internal {
_accessControl.nonReentrantAfter();
}
/* ========== MODIFIERS ========== */
/**
* @notice Only allows accounts with granted role.
* @dev Reverts when the account fails check.
* @param role Role to check for.
* @param account Account to check.
*/
modifier onlyRole(bytes32 role, address account) {
_checkRole(role, account);
_;
}
/**
* @notice Only allows accounts with granted role for a smart vault.
* @dev Reverts when the account fails check.
* @param smartVault Address of the smart vault.
* @param role Role to check for.
* @param account Account to check.
*/
modifier onlySmartVaultRole(address smartVault, bytes32 role, address account) {
_checkSmartVaultRole(smartVault, role, account);
_;
}
/**
* @notice Only allows accounts that are Spool admins or admins of a smart vault.
* @dev Reverts when the account fails check.
* @param smartVault Address of the smart vault.
* @param account Account to check.
*/
modifier onlyAdminOrVaultAdmin(address smartVault, address account) {
_accessControl.checkIsAdminOrVaultAdmin(smartVault, account);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Prevents a contract from calling itself, or other contracts using this modifier.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
/**
* @dev Check if a system has already entered in the non-reentrant state.
*/
modifier checkNonReentrant() {
_checkNonReentrant();
_;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the 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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Internal function that returns the initialized version. Returns `_initialized`
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Internal function that returns the initialized version. Returns `_initializing`
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
/// @dev Number of decimals used for USD values.
uint256 constant USD_DECIMALS = 18;
/**
* @notice Emitted when asset is invalid.
* @param asset Invalid asset.
*/
error InvalidAsset(address asset);
/**
* @notice Emitted when price returned by price aggregator is negative or zero.
* @param price Actual price returned by price aggregator.
*/
error NonPositivePrice(int256 price);
/**
* @notice Emitted when pricing data returned by price aggregator is not from the current
* round or the round hasn't finished.
*/
error StalePriceData();
interface IUsdPriceFeedManager {
/**
* @notice Gets number of decimals for an asset.
* @param asset Address of the asset.
* @return assetDecimals Number of decimals for the asset.
*/
function assetDecimals(address asset) external view returns (uint256 assetDecimals);
/**
* @notice Gets number of decimals for USD.
* @return usdDecimals Number of decimals for USD.
*/
function usdDecimals() external view returns (uint256 usdDecimals);
/**
* @notice Calculates asset value in USD using current price.
* @param asset Address of asset.
* @param assetAmount Amount of asset in asset decimals.
* @return usdValue Value in USD in USD decimals.
*/
function assetToUsd(address asset, uint256 assetAmount) external view returns (uint256 usdValue);
/**
* @notice Calculates USD value in asset using current price.
* @param asset Address of asset.
* @param usdAmount Amount of USD in USD decimals.
* @return assetValue Value in asset in asset decimals.
*/
function usdToAsset(address asset, uint256 usdAmount) external view returns (uint256 assetValue);
/**
* @notice Calculates asset value in USD using provided price.
* @param asset Address of asset.
* @param assetAmount Amount of asset in asset decimals.
* @param price Price of asset in USD.
* @return usdValue Value in USD in USD decimals.
*/
function assetToUsdCustomPrice(address asset, uint256 assetAmount, uint256 price)
external
view
returns (uint256 usdValue);
/**
* @notice Calculates assets value in USD using provided prices.
* @param assets Addresses of assets.
* @param assetAmounts Amounts of assets in asset decimals.
* @param prices Prices of asset in USD.
* @return usdValue Value in USD in USD decimals.
*/
function assetToUsdCustomPriceBulk(
address[] calldata assets,
uint256[] calldata assetAmounts,
uint256[] calldata prices
) external view returns (uint256 usdValue);
/**
* @notice Calculates USD value in asset using provided price.
* @param asset Address of asset.
* @param usdAmount Amount of USD in USD decimals.
* @param price Price of asset in USD.
* @return assetValue Value in asset in asset decimals.
*/
function usdToAssetCustomPrice(address asset, uint256 usdAmount, uint256 price)
external
view
returns (uint256 assetValue);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
type uint16a16 is uint256;
/**
* @notice This library enables packing of sixteen uint16 elements into one uint256 word.
*/
library uint16a16Lib {
/// @notice Number of bits per stored element.
uint256 constant bits = 16;
/// @notice Maximal number of elements stored.
uint256 constant elements = 16;
// must ensure that bits * elements <= 256
/// @notice Range covered by stored element.
uint256 constant range = 1 << bits;
/// @notice Maximal value of stored element.
uint256 constant max = range - 1;
/**
* @notice Gets element from packed array.
* @param va Packed array.
* @param index Index of element to get.
* @return element Element of va stored in index index.
*/
function get(uint16a16 va, uint256 index) internal pure returns (uint256) {
require(index < elements);
return (uint16a16.unwrap(va) >> (bits * index)) & max;
}
/**
* @notice Sets element to packed array.
* @param va Packed array.
* @param index Index under which to store the element
* @param ev Element to store.
* @return va Packed array with stored element.
*/
function set(uint16a16 va, uint256 index, uint256 ev) internal pure returns (uint16a16) {
require(index < elements);
require(ev < range);
index *= bits;
return uint16a16.wrap((uint16a16.unwrap(va) & ~(max << index)) | (ev << index));
}
/**
* @notice Sets elements to packed array.
* Elements are stored continuously from index 0 onwards.
* @param va Packed array.
* @param ev Elements to store.
* @return va Packed array with stored elements.
*/
function set(uint16a16 va, uint256[] memory ev) internal pure returns (uint16a16) {
for (uint256 i; i < ev.length; ++i) {
va = set(va, i, ev[i]);
}
return va;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import "@openzeppelin-upgradeable/access/IAccessControlUpgradeable.sol";
/**
* @notice Used when an account is missing a required role.
* @param role Required role.
* @param account Account missing the required role.
*/
error MissingRole(bytes32 role, address account);
/**
* @notice Used when interacting with Spool when the system is paused.
*/
error SystemPaused();
/**
* @notice Used when setting smart vault owner
*/
error SmartVaultOwnerAlreadySet(address smartVault);
/**
* @notice Used when a contract tries to enter in a non-reentrant state.
*/
error ReentrantCall();
/**
* @notice Used when a contract tries to call in a non-reentrant function and doesn't have the correct role.
*/
error NoReentrantRole();
/**
* @notice thrown if unauthorized account tries to perform ownership transfer
*/
error OwnableUnauthorizedAccount(address account);
interface ISpoolAccessControl is IAccessControlUpgradeable {
/* ========== VIEW FUNCTIONS ========== */
/**
* @notice Gets owner of a smart vault.
* @param smartVault Smart vault.
* @return owner Owner of the smart vault.
*/
function smartVaultOwner(address smartVault) external view returns (address owner);
/**
* @notice Looks if an account has a role for a smart vault.
* @param smartVault Address of the smart vault.
* @param role Role to look for.
* @param account Account to check.
* @return hasRole True if account has the role for the smart vault, false otherwise.
*/
function hasSmartVaultRole(address smartVault, bytes32 role, address account)
external
view
returns (bool hasRole);
/**
* @notice Checks if an account is either Spool admin or admin for a smart vault.
* @dev The function reverts if account is neither.
* @param smartVault Address of the smart vault.
* @param account to check.
*/
function checkIsAdminOrVaultAdmin(address smartVault, address account) external view;
/**
* @notice Checks if system is paused or not.
* @return isPaused True if system is paused, false otherwise.
*/
function paused() external view returns (bool isPaused);
/* ========== MUTATIVE FUNCTIONS ========== */
/**
* @notice Pauses the whole system.
* @dev Requirements:
* - caller must have role ROLE_PAUSER
*/
function pause() external;
/**
* @notice Unpauses the whole system.
* @dev Requirements:
* - caller must have role ROLE_UNPAUSER
*/
function unpause() external;
/**
* @notice Grants role to an account for a smart vault.
* @dev Requirements:
* - caller must have either role ROLE_SPOOL_ADMIN or role ROLE_SMART_VAULT_ADMIN for the smart vault
* @param smartVault Address of the smart vault.
* @param role Role to grant.
* @param account Account to grant the role to.
*/
function grantSmartVaultRole(address smartVault, bytes32 role, address account) external;
/**
* @notice Revokes role from an account for a smart vault.
* @dev Requirements:
* - caller must have either role ROLE_SPOOL_ADMIN or role ROLE_SMART_VAULT_ADMIN for the smart vault
* @param smartVault Address of the smart vault.
* @param role Role to revoke.
* @param account Account to revoke the role from.
*/
function revokeSmartVaultRole(address smartVault, bytes32 role, address account) external;
/**
* @notice Renounce role for a smart vault.
* @param smartVault Address of the smart vault.
* @param role Role to renounce.
*/
function renounceSmartVaultRole(address smartVault, bytes32 role) external;
/**
* @notice Grant ownership to smart vault and assigns admin role.
* @dev Ownership can only be granted once and it should be done at vault creation time.
* @param smartVault Address of the smart vault.
* @param owner address to which grant ownership to
*/
function grantSmartVaultOwnership(address smartVault, address owner) external;
/**
* @notice Checks and reverts if a system has already entered in the non-reentrant state.
*/
function checkNonReentrant() external view;
/**
* @notice Sets the entered flag to true when entering for the first time.
* @dev Reverts if a system has already entered before.
*/
function nonReentrantBefore() external;
/**
* @notice Resets the entered flag after the call is finished.
*/
function nonReentrantAfter() external;
/**
* @notice Emitted when ownership of a smart vault is granted to an address
* @param smartVault Smart vault address
* @param address_ Address of the new smart vault owner
*/
event SmartVaultOwnershipGranted(address indexed smartVault, address indexed address_);
/**
* @notice Smart vault specific role was granted
* @param smartVault Smart vault address
* @param role Role ID
* @param account Account to which the role was granted
*/
event SmartVaultRoleGranted(address indexed smartVault, bytes32 indexed role, address indexed account);
/**
* @notice Smart vault specific role was revoked
* @param smartVault Smart vault address
* @param role Role ID
* @param account Account for which the role was revoked
*/
event SmartVaultRoleRevoked(address indexed smartVault, bytes32 indexed role, address indexed account);
/**
* @notice Smart vault specific role was renounced
* @param smartVault Smart vault address
* @param role Role ID
* @param account Account that renounced the role
*/
event SmartVaultRoleRenounced(address indexed smartVault, bytes32 indexed role, address indexed account);
/**
* @notice SmartVault owner initiated transfer
* @param previousOwner address
* @param newOwner address
*/
event SmartVaultOwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @notice Ownership transfer is finalized
* @param previousOwner address
* @param newOwner address
*/
event SmartVaultOwnershipTransferred(address indexed previousOwner, address indexed newOwner);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
/**
* @dev Grants permission to:
* - acts as a default admin for other roles,
* - can whitelist an action with action manager,
* - can manage asset group registry.
*
* Is granted to the deployer of the SpoolAccessControl contract.
*
* Equals to the DEFAULT_ADMIN_ROLE of the OpenZeppelin AccessControl.
*/
bytes32 constant ROLE_SPOOL_ADMIN = 0x00;
/**
* @dev Grants permission to integrate a new smart vault into the Spool ecosystem.
*
* Should be granted to smart vault factory contracts.
*/
bytes32 constant ROLE_SMART_VAULT_INTEGRATOR = keccak256("SMART_VAULT_INTEGRATOR");
/**
* @dev Grants permission to
* - manage rewards on smart vaults,
* - manage roles on smart vaults,
* - redeem for another user of a smart vault.
*/
bytes32 constant ROLE_SMART_VAULT_ADMIN = keccak256("SMART_VAULT_ADMIN");
/**
* @dev Grants permission to manage allowlists with AllowlistGuard for a smart vault.
*
* Should be granted to whoever is in charge of maintaining allowlists with AllowlistGuard for a smart vault.
*/
bytes32 constant ROLE_GUARD_ALLOWLIST_MANAGER = keccak256("GUARD_ALLOWLIST_MANAGER");
/**
* @dev Grants permission to manage assets on master wallet.
*
* Should be granted to:
* - the SmartVaultManager contract,
* - the StrategyRegistry contract,
* - the DepositManager contract,
* - the WithdrawalManager contract.
*/
bytes32 constant ROLE_MASTER_WALLET_MANAGER = keccak256("MASTER_WALLET_MANAGER");
/**
* @dev Marks a contract as a smart vault manager.
*
* Should be granted to:
* - the SmartVaultManager contract,
* - the DepositManager contract.
*/
bytes32 constant ROLE_SMART_VAULT_MANAGER = keccak256("SMART_VAULT_MANAGER");
/**
* @dev Marks a contract as a strategy registry.
*
* Should be granted to the StrategyRegistry contract.
*/
bytes32 constant ROLE_STRATEGY_REGISTRY = keccak256("STRATEGY_REGISTRY");
/**
* @dev Grants permission to act as a risk provider.
*
* Should be granted to whoever is allowed to provide risk scores.
*/
bytes32 constant ROLE_RISK_PROVIDER = keccak256("RISK_PROVIDER");
/**
* @dev Grants permission to act as an allocation provider.
*
* Should be granted to contracts that are allowed to calculate allocations.
*/
bytes32 constant ROLE_ALLOCATION_PROVIDER = keccak256("ALLOCATION_PROVIDER");
/**
* @dev Grants permission to pause the system.
*/
bytes32 constant ROLE_PAUSER = keccak256("SYSTEM_PAUSER");
/**
* @dev Grants permission to unpause the system.
*/
bytes32 constant ROLE_UNPAUSER = keccak256("SYSTEM_UNPAUSER");
/**
* @dev Grants permission to manage rewards payment pool.
*/
bytes32 constant ROLE_REWARD_POOL_ADMIN = keccak256("REWARD_POOL_ADMIN");
/**
* @dev Grants permission to reallocate smart vaults.
*/
bytes32 constant ROLE_REALLOCATOR = keccak256("REALLOCATOR");
/**
* @dev Grants permission to be used as a strategy.
*/
bytes32 constant ROLE_STRATEGY = keccak256("STRATEGY");
/**
* @dev Grants permission to manually set strategy apy.
*/
bytes32 constant ROLE_STRATEGY_APY_SETTER = keccak256("STRATEGY_APY_SETTER");
/**
* @dev Grants permission to manage role ROLE_STRATEGY.
*/
bytes32 constant ADMIN_ROLE_STRATEGY = keccak256("ADMIN_STRATEGY");
/**
* @dev Grants permission vault admins to allow redeem on behalf of other users.
*/
bytes32 constant ROLE_SMART_VAULT_ALLOW_REDEEM = keccak256("SMART_VAULT_ALLOW_REDEEM");
/**
* @dev Grants permission to manage role ROLE_SMART_VAULT_ALLOW_REDEEM.
*/
bytes32 constant ADMIN_ROLE_SMART_VAULT_ALLOW_REDEEM = keccak256("ADMIN_SMART_VAULT_ALLOW_REDEEM");
/**
* @dev Grants permission to run do hard work.
*/
bytes32 constant ROLE_DO_HARD_WORKER = keccak256("DO_HARD_WORKER");
/**
* @dev Grants permission to immediately withdraw assets in case of emergency.
*/
bytes32 constant ROLE_EMERGENCY_WITHDRAWAL_EXECUTOR = keccak256("EMERGENCY_WITHDRAWAL_EXECUTOR");
/**
* @dev Grants permission to swap with swapper.
*
* Should be granted to the DepositSwap contract.
*/
bytes32 constant ROLE_SWAPPER = keccak256("SWAPPER");// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 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://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or 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 {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControlUpgradeable {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"@openzeppelin/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@solmate/=lib/solmate/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"solmate/=lib/solmate/src/"
],
"optimizer": {
"enabled": true,
"runs": 99999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"libraries": {
"script/helper/ArraysHelper.sol": {
"ArraysHelper": "0x7dF191D01252D2C0BABf77c725c63D69fe6EAA1a"
},
"src/libraries/ArrayMapping.sol": {
"ArrayMappingUint256": "0xB6B08a9ed9282c695ddFbfA81d9ebF208b4c1De0"
},
"src/libraries/ReallocationLib.sol": {
"ReallocationLib": "0x49CE6640EB2D89F80395C1a3695cc3a01b5C7bc1"
},
"src/libraries/SpoolUtils.sol": {
"SpoolUtils": "0xCB3fECd1D5eabB1b18383604A28352c17Bdfef89"
},
"src/strategies/libraries/EthFrxEthAssetGroupAdapter.sol": {
"EthFrxEthAssetGroupAdapter": "0xE23681bAA697EF5054E2D8C397ae59145027625D"
},
"src/strategies/libraries/EthStEthAssetGroupAdapter.sol": {
"EthStEthAssetGroupAdapter": "0x1516eb3a54377168b6b184326200f4258C3529dF"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IAssetGroupRegistry","name":"assetGroupRegistry_","type":"address"},{"internalType":"contract ISpoolAccessControl","name":"accessControl_","type":"address"},{"internalType":"contract ISwapper","name":"swapper_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ConfigurationAddressZero","type":"error"},{"inputs":[],"name":"GearboxV3BeforeDepositCheckFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"assetGroupId","type":"uint256"}],"name":"InvalidAssetGroup","type":"error"},{"inputs":[],"name":"InvalidAssetGroupIdInitialization","type":"error"},{"inputs":[],"name":"InvalidConfiguration","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"MissingRole","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"NotFastRedeemer","type":"error"},{"inputs":[],"name":"StrategyWorthIsZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"BeforeDepositCheckSlippages","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ssts","type":"uint256"}],"name":"BeforeRedeemalCheckSlippages","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"mintedShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdWorthDeposited","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"assetsBeforeSwap","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"assetsDeposited","type":"uint256[]"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"sharesMinted","type":"uint256"}],"name":"PlatformFeesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isDeposit","type":"bool"},{"indexed":false,"internalType":"uint256","name":"slippage","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"Slippages","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdrawnShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdWorthWithdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"withdrawnAssets","type":"uint256[]"}],"name":"Withdrawn","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"assetGroupId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetRatio","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"assets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"beforeDepositCheck","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"beforeRedeemalCheck","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claimShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dToken","outputs":[{"internalType":"contract IPoolV3","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetGroup","type":"address[]"},{"internalType":"uint256[]","name":"exchangeRates","type":"uint256[]"},{"internalType":"contract IUsdPriceFeedManager","name":"priceFeedManager","type":"address"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"},{"components":[{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"swapCallData","type":"bytes"}],"internalType":"struct SwapInfo[]","name":"swapInfo","type":"tuple[]"}],"name":"depositFast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"swapCallData","type":"bytes"}],"internalType":"struct SwapInfo[]","name":"swapInfo","type":"tuple[]"},{"components":[{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"swapCallData","type":"bytes"}],"internalType":"struct SwapInfo[]","name":"compoundSwapInfo","type":"tuple[]"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"},{"internalType":"address[]","name":"assetGroup","type":"address[]"},{"internalType":"uint256[]","name":"exchangeRates","type":"uint256[]"},{"internalType":"uint256","name":"withdrawnShares","type":"uint256"},{"internalType":"address","name":"masterWallet","type":"address"},{"internalType":"contract IUsdPriceFeedManager","name":"priceFeedManager","type":"address"},{"internalType":"int256","name":"baseYield","type":"int256"},{"components":[{"internalType":"address","name":"ecosystemFeeReceiver","type":"address"},{"internalType":"uint96","name":"ecosystemFeePct","type":"uint96"},{"internalType":"address","name":"treasuryFeeReceiver","type":"address"},{"internalType":"uint96","name":"treasuryFeePct","type":"uint96"}],"internalType":"struct PlatformFees","name":"platformFees","type":"tuple"}],"internalType":"struct StrategyDhwParameterBag","name":"dhwParams","type":"tuple"}],"name":"doHardWork","outputs":[{"components":[{"internalType":"uint256","name":"sharesMinted","type":"uint256"},{"internalType":"uint256[]","name":"assetsWithdrawn","type":"uint256[]"},{"internalType":"int256","name":"yieldPercentage","type":"int256"},{"internalType":"uint256","name":"valueAtDhw","type":"uint256"},{"internalType":"uint256","name":"totalSstsAtDhw","type":"uint256"}],"internalType":"struct DhwInfo","name":"dhwInfo","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"slippages","type":"uint256[]"},{"internalType":"address","name":"recipient","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gear","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolRewards","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getUnderlyingAssetAmounts","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"exchangeRates","type":"uint256[]"},{"internalType":"contract IUsdPriceFeedManager","name":"priceFeedManager","type":"address"}],"name":"getUsdWorth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"strategyName_","type":"string"},{"internalType":"uint256","name":"assetGroupId_","type":"uint256"},{"internalType":"contract IFarmingPool","name":"sdToken_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"masterWallet","type":"address"},{"internalType":"address[]","name":"assetGroup","type":"address[]"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"}],"name":"redeemFast","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"redeemer","type":"address"},{"internalType":"address[]","name":"assetGroup","type":"address[]"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"}],"name":"redeemShares","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"releaseShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sdToken","outputs":[{"internalType":"contract IFarmingPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapper","outputs":[{"internalType":"contract ISwapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101006040523480156200001257600080fd5b5060405162005f1638038062005f168339810160408190526200003591620000f0565b82826000816001600160a01b038116620000625760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b039081166080528316620000905760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b0392831660a05260c052508116620000c25760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b031660e05250620001449050565b6001600160a01b0381168114620000ed57600080fd5b50565b6000806000606084860312156200010657600080fd5b83516200011381620000d7565b60208501519093506200012681620000d7565b60408501519092506200013981620000d7565b809150509250925092565b60805160a05160c05160e051615d60620001b6600039600081816102750152818161344201526134f4015260008181610e2401528181610e520152612ad4015260008181610d0f01528181612b7201526130200152600081816122500152818161232c015261293c0152615d606000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c806395d89b411161010f578063c1a0ff4c116100a2578063dd62ed3e11610071578063dd62ed3e14610497578063e173ad25146104dd578063ebbd6bc7146104e5578063fc195d8c146104f857600080fd5b8063c1a0ff4c14610449578063c1a7d80e14610451578063c8d31e7414610464578063d9d7858a1461047757600080fd5b8063a457c2d7116100de578063a457c2d7146103fb578063a63e8c4b1461040e578063a9059cbb14610416578063beef8a711461042957600080fd5b806395d89b41146103aa5780639b6aaa47146103b25780639f2fd759146103c8578063a3ea6c97146103e857600080fd5b8063395093511161018757806371a973051161015657806371a973051461034d578063776c23fb146103625780637817bf4a1461038257806384ba89e3146103a257600080fd5b806339509351146102de5780634f3bddeb146102f15780636c60d9e71461030457806370a082311461031757600080fd5b806323b872dd116101c357806323b872dd1461025d5780632b3297f9146102705780632ce5183f146102bc578063313ce567146102cf57600080fd5b806306fdde03146101f5578063095ea7b3146102135780631480fce31461023657806318160ddd1461024b575b600080fd5b6101fd61050b565b60405161020a9190614b64565b60405180910390f35b610226610221366004614bd7565b61059d565b604051901515815260200161020a565b610249610244366004614c4f565b505050565b005b6035545b60405190815260200161020a565b61022661026b366004614c9b565b6105b7565b6102977f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161020a565b6102496102ca366004614bd7565b6105db565b6040516012815260200161020a565b6102266102ec366004614bd7565b610618565b6102496102ff366004614d6a565b610664565b610249610312366004614e37565b610ccd565b61024f610325366004614e8e565b73ffffffffffffffffffffffffffffffffffffffff1660009081526033602052604090205490565b610355610d0b565b60405161020a9190614efc565b609a546102979073ffffffffffffffffffffffffffffffffffffffff1681565b610395610390366004614f0f565b610dd7565b60405161020a9190614fcb565b61024f610e1f565b6101fd610e74565b6103ba610e83565b60405161020a929190614fde565b6098546102979073ffffffffffffffffffffffffffffffffffffffff1681565b61024f6103f636600461500c565b610ea1565b610226610409366004614bd7565b61127d565b61039561134e565b610226610424366004614bd7565b611399565b61043c6104373660046150e7565b6113a7565b60405161020a9190615123565b610395611fb8565b61024961045f3660046151f7565b612098565b61024f610472366004615253565b612170565b6099546102979073ffffffffffffffffffffffffffffffffffffffff1681565b61024f6104a53660046152a5565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260346020908152604080832093909416825291909152205490565b6101fd6121b3565b6102496104f3366004614bd7565b6121c2565b610395610506366004614f0f565b6121f9565b60606036805461051a906152d3565b80601f0160208091040260200160405190810160405280929190818152602001828054610546906152d3565b80156105935780601f1061056857610100808354040283529160200191610593565b820191906000526020600020905b81548152906001019060200180831161057657829003601f168201915b5050505050905090565b6000336105ab8185856123f6565b60019150505b92915050565b6000336105c58582856125a9565b6105d085858561267a565b506001949350505050565b7f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa563361060782826128f0565b61061230858561267a565b50505050565b33600081815260346020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906105ab908290869061065f908790615355565b6123f6565b600054610100900460ff16158080156106845750600054600160ff909116105b8061069e5750303b15801561069e575060005460ff166001145b61072f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561078d57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6107978484612a00565b609a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416908117909155604080517f72f702f300000000000000000000000000000000000000000000000000000000815290516372f702f3916004808201926020929091908290030181865afa15801561082e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108529190615368565b609960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff1663d1af0c7d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109019190615368565b609880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790556000610950610d0b565b905080516001141580610a395750609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ef9190615368565b73ffffffffffffffffffffffffffffffffffffffff1681600081518110610a1857610a18615385565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614155b15610a7b57610a46610e1f565b6040517f4b1f57ad00000000000000000000000000000000000000000000000000000000815260040161072691815260200190565b609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ae8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0c91906153b4565b610b179060026153d7565b610b2290600a615513565b609c55609954604080517f18160ddd000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff909216916318160ddd916004808201926020929091908290030181865afa158015610b95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bb99190615522565b609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fe14112d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4a9190615522565b609c54610c57919061553b565b610c619190615581565b609b5550801561061257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050565b7fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded233610cf982826128f0565b610d04858585612c86565b5050505050565b60607f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663dd0fe31a610d51610e1f565b6040518263ffffffff1660e01b8152600401610d6f91815260200190565b600060405180830381865afa158015610d8c573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610dd29190810190615595565b905090565b6060610e037fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded2336128f0565b610e1287878888888888612e21565b90505b9695505050505050565b6000807f000000000000000000000000000000000000000000000000000000000000000011610e4f575060975490565b507f000000000000000000000000000000000000000000000000000000000000000090565b60606037805461051a906152d3565b6060803215610e9157600080fd5b610e99612e90565b915091509091565b60007f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5633610ecf82826128f0565b60008b67ffffffffffffffff811115610eea57610eea614cdc565b604051908082528060200260200182016040528015610f13578160200160208202803683370190505b50905060005b8c811015611005578d8d82818110610f3357610f33615385565b9050602002016020810190610f489190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610fb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fd89190615522565b828281518110610fea57610fea615385565b6020908102919091010152610ffe81615624565b9050610f19565b5061103f8d8d8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050505050565b60008c67ffffffffffffffff81111561105a5761105a614cdc565b604051908082528060200260200182016040528015611083578160200160208202803683370190505b50905060005b8d811015611175578e8e828181106110a3576110a3615385565b90506020020160208101906110b89190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611124573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111489190615522565b82828151811061115a5761115a615385565b602090810291909101015261116e81615624565b9050611089565b5060006111b68d8d808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508f9250612f70915050565b90506111c58f8f848d8d6131e1565b60006112128e8e80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508d612f70565b90506000611220838361322c565b90507fef3370e7b2b7c0f8f907344d48c31e679e6ff7ff055cad7846daf5ca2d5b30dc8161124e858561565c565b8787604051611260949392919061566f565b60405180910390a196505050505050509998505050505050505050565b33600081815260346020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015611341576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610726565b6105d082868684036123f6565b604080516001808252818301909252606091600091906020808301908036833701905050905060018160008151811061138957611389615385565b6020908102919091010152919050565b6000336105ab81858561267a565b6113d96040518060a0016040528060008152602001606081526020016000815260200160008152602001600081525090565b6114037fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded2336128f0565b60008061141360608501856156a0565b905067ffffffffffffffff81111561142d5761142d614cdc565b604051908082528060200260200182016040528015611456578160200160208202803683370190505b50905060005b61146960608601866156a0565b90508110156115815761147f60608601866156a0565b8281811061148f5761148f615385565b90506020020160208101906114a49190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611510573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115349190615522565b82828151811061154657611546615385565b602002602001018181525050600082828151811061156657611566615385565b6020026020010151111561157957600192505b60010161145c565b506115938161045f60408701876156a0565b6115a860a085013561024460408701876156a0565b6115b0614b22565b6115be8561010001356132b3565b604085015260006115f46115d560608801886156a0565b6115e260208a018a6156a0565b6115ef60408c018c6156a0565b613409565b905064e8d4a5100085604001518261160c9190615708565b6116169190615754565b61162090826157bc565b8560400181815161163191906157bc565b905250506040840151611648906101208701613754565b506116a161165960808701876156a0565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061169c92505050610100880160e08901614e8e565b612f70565b81526000808060a088013586156117f25760006116c56101008b0160e08c01614e8e565b73ffffffffffffffffffffffffffffffffffffffff1663a2d2657f6116ed60608d018d6156a0565b8a8e80608001906116fe91906156a0565b6040518663ffffffff1660e01b815260040161171e959493929190615827565b602060405180830381865afa15801561173b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175f9190615522565b905064e8d4a5100061177060355490565b101561178957611782816103e861553b565b93506117dc565b8551156117aa5785516035546117a090839061553b565b6117829190615581565b6040517f907d9a5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818410156117ec578394506117f0565b8194505b505b600061180160608b018b6156a0565b905067ffffffffffffffff81111561181b5761181b614cdc565b604051908082528060200260200182016040528015611844578160200160208202803683370190505b509050600082851115611c8f5785156119055760005b61186760608d018d6156a0565b90508110156118ff5785878a838151811061188457611884615385565b6020026020010151028161189a5761189a615552565b048382815181106118ad576118ad615385565b6020026020010181815250508281815181106118cb576118cb615385565b60200260200101518982815181106118e5576118e5615385565b60209081029190910101805191909103905260010161185a565b50600190505b6000885167ffffffffffffffff81111561192157611921614cdc565b60405190808252806020026020018201604052801561194a578160200160208202803683370190505b50905060006119598d806156a0565b90501115611b2f576119c261197160608e018e6156a0565b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508a8e806000019061061291906156a0565b60005b6119d260608e018e6156a0565b9050811015611b29578981815181106119ed576119ed615385565b6020026020010151828281518110611a0757611a07615385565b602002602001018181525050838181518110611a2557611a25615385565b60200260200101518d8060600190611a3d91906156a0565b83818110611a4d57611a4d615385565b9050602002016020810190611a629190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611ace573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af29190615522565b611afc919061565c565b8a8281518110611b0e57611b0e615385565b6020908102919091010152611b2281615624565b90506119c5565b50611b91565b60005b611b3f60608e018e6156a0565b9050811015611b8f57898181518110611b5a57611b5a615385565b6020026020010151828281518110611b7457611b74615385565b6020908102919091010152611b8881615624565b9050611b32565b505b611bb7611ba160608e018e6156a0565b8b8f8060400190611bb291906156a0565b6131e1565b611c1a611bc760808e018e6156a0565b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508d60e001602081019061169c9190614e8e565b602089018190528851611c2c9161322c565b885160208a01519196507fef3370e7b2b7c0f8f907344d48c31e679e6ff7ff055cad7846daf5ca2d5b30dc918791611c639161565c565b838c604051611c75949392919061566f565b60405180910390a1611c878786615355565b945050611ee6565b84831115611e77578515611ca65785830392508593505b611ccc611cb660608d018d6156a0565b858e8060400190611cc791906156a0565b6138ce565b611cd63084613992565b506001611d2c611ce960808d018d6156a0565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061169c925050506101008e0160e08f01614e8e565b602088015260005b611d4160608d018d6156a0565b9050811015611e3157611d5760608d018d6156a0565b82818110611d6757611d67615385565b9050602002016020810190611d7c9190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611de8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0c9190615522565b838281518110611e1e57611e1e615385565b6020908102919091010152600101611d34565b5060208701516040517f8b1b306a5c3a19617717b1f37f2b4bea82978af8db2e859975f665fc40bc74b391611e6a9186919086906158a1565b60405180910390a1611ee6565b8515611ede5782935060005b611e9060608d018d6156a0565b9050811015611ed857888181518110611eab57611eab615385565b6020026020010151838281518110611ec557611ec5615385565b6020908102919091010152600101611e83565b50600190505b865160208801525b8015611f8e5760005b611efc60608d018d6156a0565b9050811015611f8c57611f84611f1860e08e0160c08f01614e8e565b848381518110611f2a57611f2a615385565b60200260200101518e8060600190611f4291906156a0565b85818110611f5257611f52615385565b9050602002016020810190611f679190614e8e565b73ffffffffffffffffffffffffffffffffffffffff169190613b58565b600101611eef565b505b838a526020808b0183905287015160608b015260355460808b015250979998505050505050505050565b6040805160018082528183019092526060916020808301908036833701905050609a546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529192506120769173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa15801561204d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120719190615522565b613c2c565b8160008151811061208957612089615385565b60200260200101818152505090565b609a546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526d04ee2d6d415b85acef81000000009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612114573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121389190615522565b1115610244576040517f09a3940800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60007f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa563361219e82826128f0565b6121a88585612f70565b92505b505092915050565b60606096805461051a906152d3565b7f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa56336121ee82826128f0565b61061284308561267a565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5660048201523360248201526060907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa1580156122ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d091906158c0565b1580156123ae57506040517f91d148540000000000000000000000000000000000000000000000000000000081527fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded260048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa158015612388573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ac91906158c0565b155b156123e7576040517f7ab612a3000000000000000000000000000000000000000000000000000000008152336004820152602401610726565b610e1287308888888888612e21565b73ffffffffffffffffffffffffffffffffffffffff8316612498576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff821661253b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152603460209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610612578181101561266d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610726565b61061284848484036123f6565b73ffffffffffffffffffffffffffffffffffffffff831661271d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff82166127c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff831660009081526033602052604090205481811015612876576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff80851660008181526033602052604080822086860390559286168082529083902080548601905591517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906128e39086815260200190565b60405180910390a3610612565b6040517f91d148540000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d1485490604401602060405180830381865afa158015612983573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a791906158c0565b6129fc576040517f75000dc00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82166024820152604401610726565b5050565b600054610100900460ff16612a97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610726565b8151600003612ad2576040517fc52a9bd300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000612b385780612b2e576040517f36307dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6097819055612b70565b8015612b70576040517f36307dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663e065ce6e612bb4610e1f565b6040518263ffffffff1660e01b8152600401612bd291815260200190565b60006040518083038186803b158015612bea57600080fd5b505afa158015612bfe573d6000803e3d6000fd5b505050508160969081612c119190615928565b506129fc6040518060400160405280601481526020017f537472617465677920536861726520546f6b656e0000000000000000000000008152506040518060400160405280600381526020017f5353540000000000000000000000000000000000000000000000000000000000815250613cd1565b609a546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612cf5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d199190615522565b9050612d2481613d72565b6000612d2e610d0b565b9050610d048382600081518110612d4757612d47615385565b60209081029190910101516040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015612dbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de19190615522565b83600081518110612df457612df4615385565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16613b589092919063ffffffff16565b60606000612e3286868b8787613ea1565b9050612e3e888a613992565b60005b85811015612e8157612e7988838381518110612e5f57612e5f615385565b6020026020010151898985818110611f5257611f52615385565b600101612e41565b5090505b979650505050505050565b60408051600180825281830190925260609182916000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683375050609854845192935073ffffffffffffffffffffffffffffffffffffffff1691849150600090612f0757612f07615385565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050612f49614102565b81600081518110612f5c57612f5c615385565b602090810291909101015290939092509050565b609a546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600091829173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015612fe3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130079190615522565b905080156131da57600061301a82613c2c565b905060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663dd0fe31a613062610e1f565b6040518263ffffffff1660e01b815260040161308091815260200190565b600060405180830381865afa15801561309d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526130e39190810190615595565b90508473ffffffffffffffffffffffffffffffffffffffff16639115900c8260008151811061311457613114615385565b6020026020010151848960008151811061313057613130615385565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff909316600484015260248301919091526044820152606401602060405180830381865afa1580156131b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131d59190615522565b935050505b5092915050565b610d04858560008181106131f7576131f7615385565b905060200201602081019061320c9190614e8e565b8460008151811061321f5761321f615385565b6020026020010151614217565b60008061323860355490565b905064e8d4a51000811015613280576132536103e88461553b565b915064e8d4a51000819003808310156132695750815b808303925061327a61dead82614394565b506132a9565b83156117aa578381613292828661565c565b61329c919061553b565b6132a69190615581565b91505b6131da3083614394565b600080609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613323573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133479190615522565b609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fe14112d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d89190615522565b609c546133e5919061553b565b6133ef9190615581565b90506133fd609b5482614489565b609b9190915592915050565b60008315610e1557600061341b614102565b90508015613749576098546134679073ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000083613b58565b604080516001808252818301909252600091602080830190803683375050609854825192935073ffffffffffffffffffffffffffffffffffffffff16918391506000906134b6576134b6615385565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166367c15262838a8a8e8e306040518763ffffffff1660e01b815260040161355596959493929190615a8b565b6000604051808303816000875af1158015613574573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526135ba9190810190615c22565b6000815181106135cc576135cc615385565b60200260200101519050600081111561374657609a546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa15801561364e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136729190615522565b90506136a58b8b600081811061368a5761368a615385565b905060200201602081019061369f9190614e8e565b83614217565b609a546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015261374291839173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015613719573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373d9190615522565b614489565b9450505b50505b509695505050505050565b6000808313156105b15782600061377264e8d4a5100061271061553b565b6137826080860160608701615ca8565b6bffffffffffffffffffffffff166137a06040870160208801615ca8565b6137ba906bffffffffffffffffffffffff1661271061565c565b6137c4919061565c565b6137ce908461553b565b6137d89190615355565b90506000826137e660355490565b6137f0919061553b565b90506000826138056040880160208901615ca8565b61381d906bffffffffffffffffffffffff168461553b565b6138279190615581565b905061383f6138396020880188614e8e565b82614394565b6000836138526080890160608a01615ca8565b61386a906bffffffffffffffffffffffff168561553b565b6138749190615581565b90506138896138396060890160408a01614e8e565b604051828201808252965030907fd02a13a1b75d55c8b9ced16f3558e9004a0827559f52715b34bcc685d904c4599060200160405180910390a2505050505092915050565b60006138d960355490565b609a546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152869173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015613947573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061396b9190615522565b613975919061553b565b61397f9190615581565b905061398a81613d72565b505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff821660009081526033602052604090205481811015613aeb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff831660008181526033602090815260408083208686039055603580548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526102449084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526144fc565b600081600003613c3e57506000919050565b6099546040517f4cdad5060000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff90911690634cdad50690602401602060405180830381865afa158015613cad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b19190615522565b600054610100900460ff16613d68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610726565b6129fc8282614608565b8015613e9e57609a546040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d90602401600060405180830381600087803b158015613de457600080fd5b505af1158015613df8573d6000803e3d6000fd5b50506099546040517fba087652000000000000000000000000000000000000000000000000000000008152600481018590523060248201819052604482015273ffffffffffffffffffffffffffffffffffffffff909116925063ba08765291506064016020604051808303816000875af1158015613e7a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129fc9190615522565b50565b60608467ffffffffffffffff811115613ebc57613ebc614cdc565b604051908082528060200260200182016040528015613ee5578160200160208202803683370190505b50905060005b85811015613fd757868682818110613f0557613f05615385565b9050602002016020810190613f1a9190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613f86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613faa9190615522565b828281518110613fbc57613fbc615385565b6020908102919091010152613fd081615624565b9050613eeb565b50613fe586868686866138ce565b60005b858110156140f85781818151811061400257614002615385565b602002602001015187878381811061401c5761401c615385565b90506020020160208101906140319190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa15801561409d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140c19190615522565b6140cb919061565c565b8282815181106140dd576140dd615385565b60209081029190910101526140f181615624565b9050613fe8565b5095945050505050565b609a54604080517f4e71d92d000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff1691634e71d92d916004808301928692919082900301818387803b15801561416d57600080fd5b505af1158015614181573d6000803e3d6000fd5b50506098546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff90911692506370a082319150602401602060405180830381865afa1580156141f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd29190615522565b80156129fc5760995461424290839073ffffffffffffffffffffffffffffffffffffffff16836146b8565b6099546040517f6e553f650000000000000000000000000000000000000000000000000000000081526004810183905230602482015260009173ffffffffffffffffffffffffffffffffffffffff1690636e553f65906044016020604051808303816000875af11580156142ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142de9190615522565b609954609a5491925061430b9173ffffffffffffffffffffffffffffffffffffffff9182169116836146b8565b609a546040517fb6b55f250000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff9091169063b6b55f2590602401600060405180830381600087803b15801561437757600080fd5b505af115801561438b573d6000803e3d6000fd5b50505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216614411576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610726565b80603560008282546144239190615355565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152603360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6000828211156144bf578264e8d4a510006144a4828561565c565b6144ae919061553b565b6144b89190615581565b90506105b1565b818311156105b1578264e8d4a510006144d8848361565c565b6144e2919061553b565b6144ec9190615581565b6144f590615cd6565b9392505050565b600061455e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166146e39092919063ffffffff16565b805190915015610244578080602001905181019061457c91906158c0565b610244576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610726565b600054610100900460ff1661469f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610726565b60366146ab8382615928565b5060376102448282615928565b6146c283836146fa565b61024473ffffffffffffffffffffffffffffffffffffffff841683836147b8565b60606146f2848460008561493a565b949350505050565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff82811660248301526000919084169063dd62ed3e90604401602060405180830381865afa158015614770573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147949190615522565b11156129fc576129fc73ffffffffffffffffffffffffffffffffffffffff83168260005b80158061485857506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015614832573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148569190615522565b155b6148e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610726565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526102449084907f095ea7b30000000000000000000000000000000000000000000000000000000090606401613baa565b6060824710156149cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610726565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516149f59190615d0e565b60006040518083038185875af1925050503d8060008114614a32576040519150601f19603f3d011682016040523d82523d6000602084013e614a37565b606091505b5091509150612e858783838760608315614ad9578251600003614ad25773ffffffffffffffffffffffffffffffffffffffff85163b614ad2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610726565b50816146f2565b6146f28383815115614aee5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107269190614b64565b60405180604001604052806002906020820280368337509192915050565b60005b83811015614b5b578181015183820152602001614b43565b50506000910152565b6020815260008251806020840152614b83816040850160208701614b40565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b73ffffffffffffffffffffffffffffffffffffffff81168114613e9e57600080fd5b60008060408385031215614bea57600080fd5b8235614bf581614bb5565b946020939093013593505050565b60008083601f840112614c1557600080fd5b50813567ffffffffffffffff811115614c2d57600080fd5b6020830191508360208260051b8501011115614c4857600080fd5b9250929050565b600080600060408486031215614c6457600080fd5b83359250602084013567ffffffffffffffff811115614c8257600080fd5b614c8e86828701614c03565b9497909650939450505050565b600080600060608486031215614cb057600080fd5b8335614cbb81614bb5565b92506020840135614ccb81614bb5565b929592945050506040919091013590565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614d5257614d52614cdc565b604052919050565b8035614d6581614bb5565b919050565b600080600060608486031215614d7f57600080fd5b833567ffffffffffffffff80821115614d9757600080fd5b818601915086601f830112614dab57600080fd5b8135602082821115614dbf57614dbf614cdc565b614def817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601614d0b565b92508183528881838601011115614e0557600080fd5b8181850182850137600081838501015282965080880135955050505050614e2e60408501614d5a565b90509250925092565b600080600060408486031215614e4c57600080fd5b833567ffffffffffffffff811115614e6357600080fd5b614e6f86828701614c03565b9094509250506020840135614e8381614bb5565b809150509250925092565b600060208284031215614ea057600080fd5b81356144f581614bb5565b600081518084526020808501945080840160005b83811015614ef157815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614ebf565b509495945050505050565b6020815260006144f56020830184614eab565b60008060008060008060808789031215614f2857600080fd5b863595506020870135614f3a81614bb5565b9450604087013567ffffffffffffffff80821115614f5757600080fd5b614f638a838b01614c03565b90965094506060890135915080821115614f7c57600080fd5b50614f8989828a01614c03565b979a9699509497509295939492505050565b600081518084526020808501945080840160005b83811015614ef157815187529582019590820190600101614faf565b6020815260006144f56020830184614f9b565b604081526000614ff16040830185614eab565b82810360208401526150038185614f9b565b95945050505050565b600080600080600080600080600060a08a8c03121561502a57600080fd5b893567ffffffffffffffff8082111561504257600080fd5b61504e8d838e01614c03565b909b50995060208c013591508082111561506757600080fd5b6150738d838e01614c03565b909950975060408c0135915061508882614bb5565b90955060608b0135908082111561509e57600080fd5b6150aa8d838e01614c03565b909650945060808c01359150808211156150c357600080fd5b506150d08c828d01614c03565b915080935050809150509295985092959850929598565b6000602082840312156150f957600080fd5b813567ffffffffffffffff81111561511057600080fd5b82016101a081850312156144f557600080fd5b60208152815160208201526000602083015160a0604084015261514960c0840182614f9b565b90506040840151606084015260608401516080840152608084015160a08401528091505092915050565b600067ffffffffffffffff82111561518d5761518d614cdc565b5060051b60200190565b600082601f8301126151a857600080fd5b813560206151bd6151b883615173565b614d0b565b82815260059290921b840181019181810190868411156151dc57600080fd5b8286015b8481101561374957803583529183019183016151e0565b60008060006040848603121561520c57600080fd5b833567ffffffffffffffff8082111561522457600080fd5b61523087838801615197565b9450602086013591508082111561524657600080fd5b50614c8e86828701614c03565b6000806040838503121561526657600080fd5b823567ffffffffffffffff81111561527d57600080fd5b61528985828601615197565b925050602083013561529a81614bb5565b809150509250929050565b600080604083850312156152b857600080fd5b82356152c381614bb5565b9150602083013561529a81614bb5565b600181811c908216806152e757607f821691505b602082108103615320577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156105b1576105b1615326565b60006020828403121561537a57600080fd5b81516144f581614bb5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156153c657600080fd5b815160ff811681146144f557600080fd5b60ff81811683821602908116908181146131da576131da615326565b600181815b8085111561544c57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561543257615432615326565b8085161561543f57918102915b93841c93908002906153f8565b509250929050565b600082615463575060016105b1565b81615470575060006105b1565b81600181146154865760028114615490576154ac565b60019150506105b1565b60ff8411156154a1576154a1615326565b50506001821b6105b1565b5060208310610133831016604e8410600b84101617156154cf575081810a6105b1565b6154d983836153f3565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561550b5761550b615326565b029392505050565b60006144f560ff841683615454565b60006020828403121561553457600080fd5b5051919050565b80820281158282048414176105b1576105b1615326565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261559057615590615552565b500490565b600060208083850312156155a857600080fd5b825167ffffffffffffffff8111156155bf57600080fd5b8301601f810185136155d057600080fd5b80516155de6151b882615173565b81815260059190911b820183019083810190878311156155fd57600080fd5b928401925b82841015612e8557835161561581614bb5565b82529284019290840190615602565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361565557615655615326565b5060010190565b818103818111156105b1576105b1615326565b84815283602082015260806040820152600061568e6080830185614f9b565b8281036060840152612e858185614f9b565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156d557600080fd5b83018035915067ffffffffffffffff8211156156f057600080fd5b6020019150600581901b3603821315614c4857600080fd5b808202600082127f80000000000000000000000000000000000000000000000000000000000000008414161561574057615740615326565b81810583148215176105b1576105b1615326565b60008261576357615763615552565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156157b7576157b7615326565b500590565b80820182811260008312801582168215821617156121ab576121ab615326565b8183526000602080850194508260005b85811015614ef15781356157ff81614bb5565b73ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016157ec565b60608152600061583b6060830187896157dc565b828103602084015261584d8187614f9b565b905082810360408401528381527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84111561588757600080fd5b8360051b8086602084013701602001979650505050505050565b8381528260208201526060604082015260006150036060830184614f9b565b6000602082840312156158d257600080fd5b815180151581146144f557600080fd5b601f82111561024457600081815260208120601f850160051c810160208610156159095750805b601f850160051c820191505b8181101561398a57828155600101615915565b815167ffffffffffffffff81111561594257615942614cdc565b6159568161595084546152d3565b846158e2565b602080601f8311600181146159a957600084156159735750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561398a565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156159f6578886015182559484019460019091019084016159d7565b5085821015615a3257878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b608081526000615a9e6080830189614eab565b602083820381850152818883528183019050818960051b8401018a60005b8b811015615be5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301845281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18e3603018112615b1d57600080fd5b8d0160608135615b2c81614bb5565b73ffffffffffffffffffffffffffffffffffffffff90811686528288013590615b5482614bb5565b1685880152604082810135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1018112615b9057600080fd5b90920187810192903567ffffffffffffffff811115615bae57600080fd5b803603841315615bbd57600080fd5b8282880152615bcf8388018286615a42565b9789019796505050928601925050600101615abc565b50508581036040870152615bfa81898b6157dc565b945050505050612e85606083018473ffffffffffffffffffffffffffffffffffffffff169052565b60006020808385031215615c3557600080fd5b825167ffffffffffffffff811115615c4c57600080fd5b8301601f81018513615c5d57600080fd5b8051615c6b6151b882615173565b81815260059190911b82018301908381019087831115615c8a57600080fd5b928401925b82841015612e8557835182529284019290840190615c8f565b600060208284031215615cba57600080fd5b81356bffffffffffffffffffffffff811681146144f557600080fd5b60007f80000000000000000000000000000000000000000000000000000000000000008203615d0757615d07615326565b5060000390565b60008251615d20818460208701614b40565b919091019291505056fea2646970667358221220a177bd400ab19b1baed3c50c5e07e76be9c9b1bbae4a62635d155d3b9501aa6a64736f6c634300081100330000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1300000000000000000000000033e52c206d584550193e642c8982f2fff6339994
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101f05760003560e01c806395d89b411161010f578063c1a0ff4c116100a2578063dd62ed3e11610071578063dd62ed3e14610497578063e173ad25146104dd578063ebbd6bc7146104e5578063fc195d8c146104f857600080fd5b8063c1a0ff4c14610449578063c1a7d80e14610451578063c8d31e7414610464578063d9d7858a1461047757600080fd5b8063a457c2d7116100de578063a457c2d7146103fb578063a63e8c4b1461040e578063a9059cbb14610416578063beef8a711461042957600080fd5b806395d89b41146103aa5780639b6aaa47146103b25780639f2fd759146103c8578063a3ea6c97146103e857600080fd5b8063395093511161018757806371a973051161015657806371a973051461034d578063776c23fb146103625780637817bf4a1461038257806384ba89e3146103a257600080fd5b806339509351146102de5780634f3bddeb146102f15780636c60d9e71461030457806370a082311461031757600080fd5b806323b872dd116101c357806323b872dd1461025d5780632b3297f9146102705780632ce5183f146102bc578063313ce567146102cf57600080fd5b806306fdde03146101f5578063095ea7b3146102135780631480fce31461023657806318160ddd1461024b575b600080fd5b6101fd61050b565b60405161020a9190614b64565b60405180910390f35b610226610221366004614bd7565b61059d565b604051901515815260200161020a565b610249610244366004614c4f565b505050565b005b6035545b60405190815260200161020a565b61022661026b366004614c9b565b6105b7565b6102977f00000000000000000000000033e52c206d584550193e642c8982f2fff633999481565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161020a565b6102496102ca366004614bd7565b6105db565b6040516012815260200161020a565b6102266102ec366004614bd7565b610618565b6102496102ff366004614d6a565b610664565b610249610312366004614e37565b610ccd565b61024f610325366004614e8e565b73ffffffffffffffffffffffffffffffffffffffff1660009081526033602052604090205490565b610355610d0b565b60405161020a9190614efc565b609a546102979073ffffffffffffffffffffffffffffffffffffffff1681565b610395610390366004614f0f565b610dd7565b60405161020a9190614fcb565b61024f610e1f565b6101fd610e74565b6103ba610e83565b60405161020a929190614fde565b6098546102979073ffffffffffffffffffffffffffffffffffffffff1681565b61024f6103f636600461500c565b610ea1565b610226610409366004614bd7565b61127d565b61039561134e565b610226610424366004614bd7565b611399565b61043c6104373660046150e7565b6113a7565b60405161020a9190615123565b610395611fb8565b61024961045f3660046151f7565b612098565b61024f610472366004615253565b612170565b6099546102979073ffffffffffffffffffffffffffffffffffffffff1681565b61024f6104a53660046152a5565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260346020908152604080832093909416825291909152205490565b6101fd6121b3565b6102496104f3366004614bd7565b6121c2565b610395610506366004614f0f565b6121f9565b60606036805461051a906152d3565b80601f0160208091040260200160405190810160405280929190818152602001828054610546906152d3565b80156105935780601f1061056857610100808354040283529160200191610593565b820191906000526020600020905b81548152906001019060200180831161057657829003601f168201915b5050505050905090565b6000336105ab8185856123f6565b60019150505b92915050565b6000336105c58582856125a9565b6105d085858561267a565b506001949350505050565b7f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa563361060782826128f0565b61061230858561267a565b50505050565b33600081815260346020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906105ab908290869061065f908790615355565b6123f6565b600054610100900460ff16158080156106845750600054600160ff909116105b8061069e5750303b15801561069e575060005460ff166001145b61072f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561078d57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6107978484612a00565b609a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416908117909155604080517f72f702f300000000000000000000000000000000000000000000000000000000815290516372f702f3916004808201926020929091908290030181865afa15801561082e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108529190615368565b609960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff1663d1af0c7d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109019190615368565b609880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790556000610950610d0b565b905080516001141580610a395750609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ef9190615368565b73ffffffffffffffffffffffffffffffffffffffff1681600081518110610a1857610a18615385565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614155b15610a7b57610a46610e1f565b6040517f4b1f57ad00000000000000000000000000000000000000000000000000000000815260040161072691815260200190565b609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ae8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0c91906153b4565b610b179060026153d7565b610b2290600a615513565b609c55609954604080517f18160ddd000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff909216916318160ddd916004808201926020929091908290030181865afa158015610b95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bb99190615522565b609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fe14112d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4a9190615522565b609c54610c57919061553b565b610c619190615581565b609b5550801561061257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050565b7fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded233610cf982826128f0565b610d04858585612c86565b5050505050565b60607f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a73ffffffffffffffffffffffffffffffffffffffff1663dd0fe31a610d51610e1f565b6040518263ffffffff1660e01b8152600401610d6f91815260200190565b600060405180830381865afa158015610d8c573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610dd29190810190615595565b905090565b6060610e037fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded2336128f0565b610e1287878888888888612e21565b90505b9695505050505050565b6000807f000000000000000000000000000000000000000000000000000000000000000011610e4f575060975490565b507f000000000000000000000000000000000000000000000000000000000000000090565b60606037805461051a906152d3565b6060803215610e9157600080fd5b610e99612e90565b915091509091565b60007f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5633610ecf82826128f0565b60008b67ffffffffffffffff811115610eea57610eea614cdc565b604051908082528060200260200182016040528015610f13578160200160208202803683370190505b50905060005b8c811015611005578d8d82818110610f3357610f33615385565b9050602002016020810190610f489190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610fb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fd89190615522565b828281518110610fea57610fea615385565b6020908102919091010152610ffe81615624565b9050610f19565b5061103f8d8d8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050505050565b60008c67ffffffffffffffff81111561105a5761105a614cdc565b604051908082528060200260200182016040528015611083578160200160208202803683370190505b50905060005b8d811015611175578e8e828181106110a3576110a3615385565b90506020020160208101906110b89190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611124573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111489190615522565b82828151811061115a5761115a615385565b602090810291909101015261116e81615624565b9050611089565b5060006111b68d8d808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508f9250612f70915050565b90506111c58f8f848d8d6131e1565b60006112128e8e80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508d612f70565b90506000611220838361322c565b90507fef3370e7b2b7c0f8f907344d48c31e679e6ff7ff055cad7846daf5ca2d5b30dc8161124e858561565c565b8787604051611260949392919061566f565b60405180910390a196505050505050509998505050505050505050565b33600081815260346020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015611341576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610726565b6105d082868684036123f6565b604080516001808252818301909252606091600091906020808301908036833701905050905060018160008151811061138957611389615385565b6020908102919091010152919050565b6000336105ab81858561267a565b6113d96040518060a0016040528060008152602001606081526020016000815260200160008152602001600081525090565b6114037fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded2336128f0565b60008061141360608501856156a0565b905067ffffffffffffffff81111561142d5761142d614cdc565b604051908082528060200260200182016040528015611456578160200160208202803683370190505b50905060005b61146960608601866156a0565b90508110156115815761147f60608601866156a0565b8281811061148f5761148f615385565b90506020020160208101906114a49190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611510573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115349190615522565b82828151811061154657611546615385565b602002602001018181525050600082828151811061156657611566615385565b6020026020010151111561157957600192505b60010161145c565b506115938161045f60408701876156a0565b6115a860a085013561024460408701876156a0565b6115b0614b22565b6115be8561010001356132b3565b604085015260006115f46115d560608801886156a0565b6115e260208a018a6156a0565b6115ef60408c018c6156a0565b613409565b905064e8d4a5100085604001518261160c9190615708565b6116169190615754565b61162090826157bc565b8560400181815161163191906157bc565b905250506040840151611648906101208701613754565b506116a161165960808701876156a0565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061169c92505050610100880160e08901614e8e565b612f70565b81526000808060a088013586156117f25760006116c56101008b0160e08c01614e8e565b73ffffffffffffffffffffffffffffffffffffffff1663a2d2657f6116ed60608d018d6156a0565b8a8e80608001906116fe91906156a0565b6040518663ffffffff1660e01b815260040161171e959493929190615827565b602060405180830381865afa15801561173b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175f9190615522565b905064e8d4a5100061177060355490565b101561178957611782816103e861553b565b93506117dc565b8551156117aa5785516035546117a090839061553b565b6117829190615581565b6040517f907d9a5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818410156117ec578394506117f0565b8194505b505b600061180160608b018b6156a0565b905067ffffffffffffffff81111561181b5761181b614cdc565b604051908082528060200260200182016040528015611844578160200160208202803683370190505b509050600082851115611c8f5785156119055760005b61186760608d018d6156a0565b90508110156118ff5785878a838151811061188457611884615385565b6020026020010151028161189a5761189a615552565b048382815181106118ad576118ad615385565b6020026020010181815250508281815181106118cb576118cb615385565b60200260200101518982815181106118e5576118e5615385565b60209081029190910101805191909103905260010161185a565b50600190505b6000885167ffffffffffffffff81111561192157611921614cdc565b60405190808252806020026020018201604052801561194a578160200160208202803683370190505b50905060006119598d806156a0565b90501115611b2f576119c261197160608e018e6156a0565b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508a8e806000019061061291906156a0565b60005b6119d260608e018e6156a0565b9050811015611b29578981815181106119ed576119ed615385565b6020026020010151828281518110611a0757611a07615385565b602002602001018181525050838181518110611a2557611a25615385565b60200260200101518d8060600190611a3d91906156a0565b83818110611a4d57611a4d615385565b9050602002016020810190611a629190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611ace573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af29190615522565b611afc919061565c565b8a8281518110611b0e57611b0e615385565b6020908102919091010152611b2281615624565b90506119c5565b50611b91565b60005b611b3f60608e018e6156a0565b9050811015611b8f57898181518110611b5a57611b5a615385565b6020026020010151828281518110611b7457611b74615385565b6020908102919091010152611b8881615624565b9050611b32565b505b611bb7611ba160608e018e6156a0565b8b8f8060400190611bb291906156a0565b6131e1565b611c1a611bc760808e018e6156a0565b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508d60e001602081019061169c9190614e8e565b602089018190528851611c2c9161322c565b885160208a01519196507fef3370e7b2b7c0f8f907344d48c31e679e6ff7ff055cad7846daf5ca2d5b30dc918791611c639161565c565b838c604051611c75949392919061566f565b60405180910390a1611c878786615355565b945050611ee6565b84831115611e77578515611ca65785830392508593505b611ccc611cb660608d018d6156a0565b858e8060400190611cc791906156a0565b6138ce565b611cd63084613992565b506001611d2c611ce960808d018d6156a0565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061169c925050506101008e0160e08f01614e8e565b602088015260005b611d4160608d018d6156a0565b9050811015611e3157611d5760608d018d6156a0565b82818110611d6757611d67615385565b9050602002016020810190611d7c9190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611de8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0c9190615522565b838281518110611e1e57611e1e615385565b6020908102919091010152600101611d34565b5060208701516040517f8b1b306a5c3a19617717b1f37f2b4bea82978af8db2e859975f665fc40bc74b391611e6a9186919086906158a1565b60405180910390a1611ee6565b8515611ede5782935060005b611e9060608d018d6156a0565b9050811015611ed857888181518110611eab57611eab615385565b6020026020010151838281518110611ec557611ec5615385565b6020908102919091010152600101611e83565b50600190505b865160208801525b8015611f8e5760005b611efc60608d018d6156a0565b9050811015611f8c57611f84611f1860e08e0160c08f01614e8e565b848381518110611f2a57611f2a615385565b60200260200101518e8060600190611f4291906156a0565b85818110611f5257611f52615385565b9050602002016020810190611f679190614e8e565b73ffffffffffffffffffffffffffffffffffffffff169190613b58565b600101611eef565b505b838a526020808b0183905287015160608b015260355460808b015250979998505050505050505050565b6040805160018082528183019092526060916020808301908036833701905050609a546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529192506120769173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa15801561204d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120719190615522565b613c2c565b8160008151811061208957612089615385565b60200260200101818152505090565b609a546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526d04ee2d6d415b85acef81000000009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612114573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121389190615522565b1115610244576040517f09a3940800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60007f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa563361219e82826128f0565b6121a88585612f70565b92505b505092915050565b60606096805461051a906152d3565b7f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa56336121ee82826128f0565b61061284308561267a565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5660048201523360248201526060907f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1373ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa1580156122ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d091906158c0565b1580156123ae57506040517f91d148540000000000000000000000000000000000000000000000000000000081527fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded260048201523360248201527f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1373ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa158015612388573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ac91906158c0565b155b156123e7576040517f7ab612a3000000000000000000000000000000000000000000000000000000008152336004820152602401610726565b610e1287308888888888612e21565b73ffffffffffffffffffffffffffffffffffffffff8316612498576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff821661253b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152603460209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610612578181101561266d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610726565b61061284848484036123f6565b73ffffffffffffffffffffffffffffffffffffffff831661271d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff82166127c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff831660009081526033602052604090205481811015612876576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff80851660008181526033602052604080822086860390559286168082529083902080548601905591517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906128e39086815260200190565b60405180910390a3610612565b6040517f91d148540000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301527f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1316906391d1485490604401602060405180830381865afa158015612983573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a791906158c0565b6129fc576040517f75000dc00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82166024820152604401610726565b5050565b600054610100900460ff16612a97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610726565b8151600003612ad2576040517fc52a9bd300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000612b385780612b2e576040517f36307dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6097819055612b70565b8015612b70576040517f36307dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a73ffffffffffffffffffffffffffffffffffffffff1663e065ce6e612bb4610e1f565b6040518263ffffffff1660e01b8152600401612bd291815260200190565b60006040518083038186803b158015612bea57600080fd5b505afa158015612bfe573d6000803e3d6000fd5b505050508160969081612c119190615928565b506129fc6040518060400160405280601481526020017f537472617465677920536861726520546f6b656e0000000000000000000000008152506040518060400160405280600381526020017f5353540000000000000000000000000000000000000000000000000000000000815250613cd1565b609a546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612cf5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d199190615522565b9050612d2481613d72565b6000612d2e610d0b565b9050610d048382600081518110612d4757612d47615385565b60209081029190910101516040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015612dbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de19190615522565b83600081518110612df457612df4615385565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16613b589092919063ffffffff16565b60606000612e3286868b8787613ea1565b9050612e3e888a613992565b60005b85811015612e8157612e7988838381518110612e5f57612e5f615385565b6020026020010151898985818110611f5257611f52615385565b600101612e41565b5090505b979650505050505050565b60408051600180825281830190925260609182916000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683375050609854845192935073ffffffffffffffffffffffffffffffffffffffff1691849150600090612f0757612f07615385565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050612f49614102565b81600081518110612f5c57612f5c615385565b602090810291909101015290939092509050565b609a546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600091829173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015612fe3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130079190615522565b905080156131da57600061301a82613c2c565b905060007f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a73ffffffffffffffffffffffffffffffffffffffff1663dd0fe31a613062610e1f565b6040518263ffffffff1660e01b815260040161308091815260200190565b600060405180830381865afa15801561309d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526130e39190810190615595565b90508473ffffffffffffffffffffffffffffffffffffffff16639115900c8260008151811061311457613114615385565b6020026020010151848960008151811061313057613130615385565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff909316600484015260248301919091526044820152606401602060405180830381865afa1580156131b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131d59190615522565b935050505b5092915050565b610d04858560008181106131f7576131f7615385565b905060200201602081019061320c9190614e8e565b8460008151811061321f5761321f615385565b6020026020010151614217565b60008061323860355490565b905064e8d4a51000811015613280576132536103e88461553b565b915064e8d4a51000819003808310156132695750815b808303925061327a61dead82614394565b506132a9565b83156117aa578381613292828661565c565b61329c919061553b565b6132a69190615581565b91505b6131da3083614394565b600080609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613323573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133479190615522565b609960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fe14112d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d89190615522565b609c546133e5919061553b565b6133ef9190615581565b90506133fd609b5482614489565b609b9190915592915050565b60008315610e1557600061341b614102565b90508015613749576098546134679073ffffffffffffffffffffffffffffffffffffffff167f00000000000000000000000033e52c206d584550193e642c8982f2fff633999483613b58565b604080516001808252818301909252600091602080830190803683375050609854825192935073ffffffffffffffffffffffffffffffffffffffff16918391506000906134b6576134b6615385565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060007f00000000000000000000000033e52c206d584550193e642c8982f2fff633999473ffffffffffffffffffffffffffffffffffffffff166367c15262838a8a8e8e306040518763ffffffff1660e01b815260040161355596959493929190615a8b565b6000604051808303816000875af1158015613574573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526135ba9190810190615c22565b6000815181106135cc576135cc615385565b60200260200101519050600081111561374657609a546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa15801561364e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136729190615522565b90506136a58b8b600081811061368a5761368a615385565b905060200201602081019061369f9190614e8e565b83614217565b609a546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015261374291839173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015613719573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373d9190615522565b614489565b9450505b50505b509695505050505050565b6000808313156105b15782600061377264e8d4a5100061271061553b565b6137826080860160608701615ca8565b6bffffffffffffffffffffffff166137a06040870160208801615ca8565b6137ba906bffffffffffffffffffffffff1661271061565c565b6137c4919061565c565b6137ce908461553b565b6137d89190615355565b90506000826137e660355490565b6137f0919061553b565b90506000826138056040880160208901615ca8565b61381d906bffffffffffffffffffffffff168461553b565b6138279190615581565b905061383f6138396020880188614e8e565b82614394565b6000836138526080890160608a01615ca8565b61386a906bffffffffffffffffffffffff168561553b565b6138749190615581565b90506138896138396060890160408a01614e8e565b604051828201808252965030907fd02a13a1b75d55c8b9ced16f3558e9004a0827559f52715b34bcc685d904c4599060200160405180910390a2505050505092915050565b60006138d960355490565b609a546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152869173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015613947573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061396b9190615522565b613975919061553b565b61397f9190615581565b905061398a81613d72565b505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff821660009081526033602052604090205481811015613aeb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610726565b73ffffffffffffffffffffffffffffffffffffffff831660008181526033602090815260408083208686039055603580548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526102449084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526144fc565b600081600003613c3e57506000919050565b6099546040517f4cdad5060000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff90911690634cdad50690602401602060405180830381865afa158015613cad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b19190615522565b600054610100900460ff16613d68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610726565b6129fc8282614608565b8015613e9e57609a546040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d90602401600060405180830381600087803b158015613de457600080fd5b505af1158015613df8573d6000803e3d6000fd5b50506099546040517fba087652000000000000000000000000000000000000000000000000000000008152600481018590523060248201819052604482015273ffffffffffffffffffffffffffffffffffffffff909116925063ba08765291506064016020604051808303816000875af1158015613e7a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129fc9190615522565b50565b60608467ffffffffffffffff811115613ebc57613ebc614cdc565b604051908082528060200260200182016040528015613ee5578160200160208202803683370190505b50905060005b85811015613fd757868682818110613f0557613f05615385565b9050602002016020810190613f1a9190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613f86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613faa9190615522565b828281518110613fbc57613fbc615385565b6020908102919091010152613fd081615624565b9050613eeb565b50613fe586868686866138ce565b60005b858110156140f85781818151811061400257614002615385565b602002602001015187878381811061401c5761401c615385565b90506020020160208101906140319190614e8e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa15801561409d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140c19190615522565b6140cb919061565c565b8282815181106140dd576140dd615385565b60209081029190910101526140f181615624565b9050613fe8565b5095945050505050565b609a54604080517f4e71d92d000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff1691634e71d92d916004808301928692919082900301818387803b15801561416d57600080fd5b505af1158015614181573d6000803e3d6000fd5b50506098546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff90911692506370a082319150602401602060405180830381865afa1580156141f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd29190615522565b80156129fc5760995461424290839073ffffffffffffffffffffffffffffffffffffffff16836146b8565b6099546040517f6e553f650000000000000000000000000000000000000000000000000000000081526004810183905230602482015260009173ffffffffffffffffffffffffffffffffffffffff1690636e553f65906044016020604051808303816000875af11580156142ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142de9190615522565b609954609a5491925061430b9173ffffffffffffffffffffffffffffffffffffffff9182169116836146b8565b609a546040517fb6b55f250000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff9091169063b6b55f2590602401600060405180830381600087803b15801561437757600080fd5b505af115801561438b573d6000803e3d6000fd5b50505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216614411576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610726565b80603560008282546144239190615355565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152603360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6000828211156144bf578264e8d4a510006144a4828561565c565b6144ae919061553b565b6144b89190615581565b90506105b1565b818311156105b1578264e8d4a510006144d8848361565c565b6144e2919061553b565b6144ec9190615581565b6144f590615cd6565b9392505050565b600061455e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166146e39092919063ffffffff16565b805190915015610244578080602001905181019061457c91906158c0565b610244576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610726565b600054610100900460ff1661469f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610726565b60366146ab8382615928565b5060376102448282615928565b6146c283836146fa565b61024473ffffffffffffffffffffffffffffffffffffffff841683836147b8565b60606146f2848460008561493a565b949350505050565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff82811660248301526000919084169063dd62ed3e90604401602060405180830381865afa158015614770573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147949190615522565b11156129fc576129fc73ffffffffffffffffffffffffffffffffffffffff83168260005b80158061485857506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015614832573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148569190615522565b155b6148e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610726565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526102449084907f095ea7b30000000000000000000000000000000000000000000000000000000090606401613baa565b6060824710156149cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610726565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516149f59190615d0e565b60006040518083038185875af1925050503d8060008114614a32576040519150601f19603f3d011682016040523d82523d6000602084013e614a37565b606091505b5091509150612e858783838760608315614ad9578251600003614ad25773ffffffffffffffffffffffffffffffffffffffff85163b614ad2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610726565b50816146f2565b6146f28383815115614aee5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107269190614b64565b60405180604001604052806002906020820280368337509192915050565b60005b83811015614b5b578181015183820152602001614b43565b50506000910152565b6020815260008251806020840152614b83816040850160208701614b40565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b73ffffffffffffffffffffffffffffffffffffffff81168114613e9e57600080fd5b60008060408385031215614bea57600080fd5b8235614bf581614bb5565b946020939093013593505050565b60008083601f840112614c1557600080fd5b50813567ffffffffffffffff811115614c2d57600080fd5b6020830191508360208260051b8501011115614c4857600080fd5b9250929050565b600080600060408486031215614c6457600080fd5b83359250602084013567ffffffffffffffff811115614c8257600080fd5b614c8e86828701614c03565b9497909650939450505050565b600080600060608486031215614cb057600080fd5b8335614cbb81614bb5565b92506020840135614ccb81614bb5565b929592945050506040919091013590565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614d5257614d52614cdc565b604052919050565b8035614d6581614bb5565b919050565b600080600060608486031215614d7f57600080fd5b833567ffffffffffffffff80821115614d9757600080fd5b818601915086601f830112614dab57600080fd5b8135602082821115614dbf57614dbf614cdc565b614def817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601614d0b565b92508183528881838601011115614e0557600080fd5b8181850182850137600081838501015282965080880135955050505050614e2e60408501614d5a565b90509250925092565b600080600060408486031215614e4c57600080fd5b833567ffffffffffffffff811115614e6357600080fd5b614e6f86828701614c03565b9094509250506020840135614e8381614bb5565b809150509250925092565b600060208284031215614ea057600080fd5b81356144f581614bb5565b600081518084526020808501945080840160005b83811015614ef157815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614ebf565b509495945050505050565b6020815260006144f56020830184614eab565b60008060008060008060808789031215614f2857600080fd5b863595506020870135614f3a81614bb5565b9450604087013567ffffffffffffffff80821115614f5757600080fd5b614f638a838b01614c03565b90965094506060890135915080821115614f7c57600080fd5b50614f8989828a01614c03565b979a9699509497509295939492505050565b600081518084526020808501945080840160005b83811015614ef157815187529582019590820190600101614faf565b6020815260006144f56020830184614f9b565b604081526000614ff16040830185614eab565b82810360208401526150038185614f9b565b95945050505050565b600080600080600080600080600060a08a8c03121561502a57600080fd5b893567ffffffffffffffff8082111561504257600080fd5b61504e8d838e01614c03565b909b50995060208c013591508082111561506757600080fd5b6150738d838e01614c03565b909950975060408c0135915061508882614bb5565b90955060608b0135908082111561509e57600080fd5b6150aa8d838e01614c03565b909650945060808c01359150808211156150c357600080fd5b506150d08c828d01614c03565b915080935050809150509295985092959850929598565b6000602082840312156150f957600080fd5b813567ffffffffffffffff81111561511057600080fd5b82016101a081850312156144f557600080fd5b60208152815160208201526000602083015160a0604084015261514960c0840182614f9b565b90506040840151606084015260608401516080840152608084015160a08401528091505092915050565b600067ffffffffffffffff82111561518d5761518d614cdc565b5060051b60200190565b600082601f8301126151a857600080fd5b813560206151bd6151b883615173565b614d0b565b82815260059290921b840181019181810190868411156151dc57600080fd5b8286015b8481101561374957803583529183019183016151e0565b60008060006040848603121561520c57600080fd5b833567ffffffffffffffff8082111561522457600080fd5b61523087838801615197565b9450602086013591508082111561524657600080fd5b50614c8e86828701614c03565b6000806040838503121561526657600080fd5b823567ffffffffffffffff81111561527d57600080fd5b61528985828601615197565b925050602083013561529a81614bb5565b809150509250929050565b600080604083850312156152b857600080fd5b82356152c381614bb5565b9150602083013561529a81614bb5565b600181811c908216806152e757607f821691505b602082108103615320577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156105b1576105b1615326565b60006020828403121561537a57600080fd5b81516144f581614bb5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156153c657600080fd5b815160ff811681146144f557600080fd5b60ff81811683821602908116908181146131da576131da615326565b600181815b8085111561544c57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561543257615432615326565b8085161561543f57918102915b93841c93908002906153f8565b509250929050565b600082615463575060016105b1565b81615470575060006105b1565b81600181146154865760028114615490576154ac565b60019150506105b1565b60ff8411156154a1576154a1615326565b50506001821b6105b1565b5060208310610133831016604e8410600b84101617156154cf575081810a6105b1565b6154d983836153f3565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561550b5761550b615326565b029392505050565b60006144f560ff841683615454565b60006020828403121561553457600080fd5b5051919050565b80820281158282048414176105b1576105b1615326565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261559057615590615552565b500490565b600060208083850312156155a857600080fd5b825167ffffffffffffffff8111156155bf57600080fd5b8301601f810185136155d057600080fd5b80516155de6151b882615173565b81815260059190911b820183019083810190878311156155fd57600080fd5b928401925b82841015612e8557835161561581614bb5565b82529284019290840190615602565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361565557615655615326565b5060010190565b818103818111156105b1576105b1615326565b84815283602082015260806040820152600061568e6080830185614f9b565b8281036060840152612e858185614f9b565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156d557600080fd5b83018035915067ffffffffffffffff8211156156f057600080fd5b6020019150600581901b3603821315614c4857600080fd5b808202600082127f80000000000000000000000000000000000000000000000000000000000000008414161561574057615740615326565b81810583148215176105b1576105b1615326565b60008261576357615763615552565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156157b7576157b7615326565b500590565b80820182811260008312801582168215821617156121ab576121ab615326565b8183526000602080850194508260005b85811015614ef15781356157ff81614bb5565b73ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016157ec565b60608152600061583b6060830187896157dc565b828103602084015261584d8187614f9b565b905082810360408401528381527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84111561588757600080fd5b8360051b8086602084013701602001979650505050505050565b8381528260208201526060604082015260006150036060830184614f9b565b6000602082840312156158d257600080fd5b815180151581146144f557600080fd5b601f82111561024457600081815260208120601f850160051c810160208610156159095750805b601f850160051c820191505b8181101561398a57828155600101615915565b815167ffffffffffffffff81111561594257615942614cdc565b6159568161595084546152d3565b846158e2565b602080601f8311600181146159a957600084156159735750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561398a565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156159f6578886015182559484019460019091019084016159d7565b5085821015615a3257878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b608081526000615a9e6080830189614eab565b602083820381850152818883528183019050818960051b8401018a60005b8b811015615be5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301845281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18e3603018112615b1d57600080fd5b8d0160608135615b2c81614bb5565b73ffffffffffffffffffffffffffffffffffffffff90811686528288013590615b5482614bb5565b1685880152604082810135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1018112615b9057600080fd5b90920187810192903567ffffffffffffffff811115615bae57600080fd5b803603841315615bbd57600080fd5b8282880152615bcf8388018286615a42565b9789019796505050928601925050600101615abc565b50508581036040870152615bfa81898b6157dc565b945050505050612e85606083018473ffffffffffffffffffffffffffffffffffffffff169052565b60006020808385031215615c3557600080fd5b825167ffffffffffffffff811115615c4c57600080fd5b8301601f81018513615c5d57600080fd5b8051615c6b6151b882615173565b81815260059190911b82018301908381019087831115615c8a57600080fd5b928401925b82841015612e8557835182529284019290840190615c8f565b600060208284031215615cba57600080fd5b81356bffffffffffffffffffffffff811681146144f557600080fd5b60007f80000000000000000000000000000000000000000000000000000000000000008203615d0757615d07615326565b5060000390565b60008251615d20818460208701614b40565b919091019291505056fea2646970667358221220a177bd400ab19b1baed3c50c5e07e76be9c9b1bbae4a62635d155d3b9501aa6a64736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1300000000000000000000000033e52c206d584550193e642c8982f2fff6339994
-----Decoded View---------------
Arg [0] : assetGroupRegistry_ (address): 0x1Aa2a802BA25669531Ffd2b1fF8ae94f3D87f41A
Arg [1] : accessControl_ (address): 0x7b533e72E0cDC63AacD8cDB926AC402b846Fbd13
Arg [2] : swapper_ (address): 0x33E52c206d584550193E642C8982f2Fff6339994
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a
Arg [1] : 0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd13
Arg [2] : 00000000000000000000000033e52c206d584550193e642c8982f2fff6339994
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.