Source Code
Overview
S Balance
S Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 43001532 | 165 days ago | Contract Creation | 0 S |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
MetaVaultLib
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IMetaVault} from "../../interfaces/IMetaVault.sol";
import {IControllable} from "../../interfaces/IControllable.sol";
import {IStabilityVault} from "../../interfaces/IStabilityVault.sol";
import {IVault} from "../../interfaces/IVault.sol";
import {CommonLib} from "../libs/CommonLib.sol";
import {VaultTypeLib} from "../libs/VaultTypeLib.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IPriceReader} from "../../interfaces/IPriceReader.sol";
import {IPlatform} from "../../interfaces/IPlatform.sol";
import {IRecoveryToken} from "../../interfaces/IRecoveryToken.sol";
library MetaVaultLib {
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;
/// @notice Not perform operations with value less than threshold
uint public constant USD_THRESHOLD = 1e13;
/// @notice Vault can be removed if its TVL is less than this value
uint public constant USD_THRESHOLD_REMOVE_VAULT = USD_THRESHOLD * 1000; // 1 cent
//region --------------------------------- Data types
struct WithdrawUnderlyingLocals {
uint vaultSharePriceUsd;
uint totalSupply;
uint balance;
uint totalUnderlying;
uint totalAmounts;
address recoveryToken;
address[] assets;
uint[] minAmounts;
uint[] amountsOut;
}
//endregion --------------------------------- Data types
//region --------------------------------- Restricted actions
function addVault(
IMetaVault.MetaVaultStorage storage $,
address vault,
uint[] memory newTargetProportions
) external {
// check vault
uint len = $.vaults.length;
for (uint i; i < len; ++i) {
if ($.vaults[i] == vault) {
revert IMetaVault.IncorrectVault();
}
}
// check proportions
require(newTargetProportions.length == $.vaults.length + 1, IControllable.IncorrectArrayLength());
_checkProportions(newTargetProportions);
address[] memory vaultAssets = IStabilityVault(vault).assets();
if (isMultiVault($)) {
// check asset
require(vaultAssets.length == 1, IMetaVault.IncorrectVault());
require(vaultAssets[0] == $.assets.values()[0], IMetaVault.IncorrectVault());
} else {
// add assets
len = vaultAssets.length;
for (uint i; i < len; ++i) {
$.assets.add(vaultAssets[i]);
}
}
// add vault
$.vaults.push(vault);
$.targetProportions = newTargetProportions;
emit IMetaVault.AddVault(vault);
emit IMetaVault.TargetProportions(newTargetProportions);
}
function removeVault(IMetaVault.MetaVaultStorage storage $, address vault, uint usdThreshold_) external {
// ----------------------------- get vault index
address[] memory _vaults = $.vaults;
uint vaultIndex = type(uint).max;
uint len = _vaults.length;
for (uint i; i < len; ++i) {
if (_vaults[i] == vault) {
vaultIndex = i;
break;
}
}
require(vaultIndex != type(uint).max, IMetaVault.IncorrectVault());
// ----------------------------- The proportions of the vault should be zero
uint[] memory _targetProportions = $.targetProportions;
require(_targetProportions[vaultIndex] == 0, IMetaVault.IncorrectProportions());
// ----------------------------- Total deposited amount should be less then threshold
uint vaultUsdValue = _getVaultUsdAmount(vault);
require(vaultUsdValue < usdThreshold_, IMetaVault.UsdAmountLessThreshold(vaultUsdValue, usdThreshold_));
// ----------------------------- Remove vault
if (vaultIndex != len - 1) {
$.vaults[vaultIndex] = _vaults[len - 1];
$.targetProportions[vaultIndex] = _targetProportions[len - 1];
}
$.vaults.pop();
$.targetProportions.pop();
_targetProportions = $.targetProportions;
_checkProportions(_targetProportions);
emit IMetaVault.RemoveVault(vault);
emit IMetaVault.TargetProportions(_targetProportions);
}
function cachePrices(IMetaVault.MetaVaultStorage storage $, IPriceReader priceReader_, bool clear) external {
require(!isMultiVault($), IMetaVault.IncorrectVault());
if (clear) {
// clear exist cache
priceReader_.preCalculateVaultPriceTx(address(0));
} else {
address[] memory _vaults = $.vaults;
for (uint i; i < _vaults.length; ++i) {
address[] memory _subVaults = IMetaVault(_vaults[i]).vaults();
for (uint j; j < _subVaults.length; ++j) {
priceReader_.preCalculateVaultPriceTx(_subVaults[j]);
}
priceReader_.preCalculateVaultPriceTx(_subVaults[i]);
}
}
}
/// @notice Withdraw underlying from the vault in emergency mode, don't burn shares
/// @param platformCVault_ [Platform address, Address of the cVault to withdraw underlying from]
/// @param owners Owners of the shares to withdraw underlying for
/// @param amounts Amounts of meta-vault tokens to withdraw underlying for each owner
/// @param minUnderlyingOut Minimal amounts of underlying to receive for each owner
/// @return amountsOut Amounts of underlying withdrawn for each owner
/// @return recoveryAmountOut Amounts of recovery tokens minted for each owner
/// @return sharesToBurn Amounts of shares to burn for each owner
function withdrawUnderlyingEmergency(
IMetaVault.MetaVaultStorage storage $,
address[2] memory platformCVault_,
address[] memory owners,
uint[] memory amounts,
uint[] memory minUnderlyingOut,
bool[] memory pausedRecoveryTokens
) external returns (uint[] memory amountsOut, uint[] memory recoveryAmountOut, uint[] memory sharesToBurn) {
//slither-disable-next-line uninitialized-local
WithdrawUnderlyingLocals memory v;
// only broken vaults with not zero recovery token are allowed for emergency withdraw
v.recoveryToken = $.recoveryTokens[platformCVault_[1]];
require(v.recoveryToken != address(0), IMetaVault.RecoveryTokenNotSet(platformCVault_[1]));
address _targetVault = _getTargetVault($, platformCVault_[1]);
uint len = owners.length;
require(
len == amounts.length && len == minUnderlyingOut.length && len == pausedRecoveryTokens.length,
IControllable.IncorrectArrayLength()
);
v.totalSupply = totalSupply($, platformCVault_[0]);
amountsOut = new uint[](len);
sharesToBurn = new uint[](len);
{
uint _totalShares = $.totalShares;
for (uint i; i < len; ++i) {
v.balance = balanceOf($, owners[i], v.totalSupply);
if (amounts[i] == 0) {
amounts[i] = v.balance;
} else {
require(
amounts[i] <= v.balance, IERC20Errors.ERC20InsufficientBalance(owners[i], v.balance, amounts[i])
);
}
require(amounts[i] != 0, IControllable.IncorrectBalance());
v.totalAmounts += amounts[i];
sharesToBurn[i] = amountToShares(amounts[i], _totalShares, v.totalSupply);
require(sharesToBurn[i] != 0, IMetaVault.ZeroSharesToBurn(amounts[i]));
}
// total amount of meta-vault tokens to withdraw underlying for all owners
require(v.totalAmounts != 0, IControllable.IncorrectZeroArgument());
}
// don't check last block protection here, because it is an emergency withdraw
// _beforeDepositOrWithdraw($, owner);
// for simplicity we don't check that the target vault has required amount of meta-vault tokens
// assume that this check will be done in the target vault
(v.vaultSharePriceUsd,) = IStabilityVault(_targetVault).price();
v.assets = new address[](1);
v.assets[0] = IVault(platformCVault_[1]).strategy().underlying();
v.amountsOut = new uint[](1);
v.minAmounts = new uint[](1);
if (_targetVault == platformCVault_[1]) {
// withdraw underlying from the target cVault vault
v.amountsOut = IStabilityVault(_targetVault).withdrawAssets(
v.assets,
_getTargetVaultSharesToWithdraw($, platformCVault_[0], v.totalAmounts, v.vaultSharePriceUsd, true),
v.minAmounts,
address(this),
address(this)
);
v.totalUnderlying = v.amountsOut[0];
} else {
// withdraw underlying from the child meta-vault
v.totalUnderlying = IMetaVault(_targetVault).withdrawUnderlying(
platformCVault_[1],
_getTargetVaultSharesToWithdraw($, platformCVault_[0], v.totalAmounts, v.vaultSharePriceUsd, true),
0,
address(this),
address(this)
);
}
recoveryAmountOut = new uint[](len);
for (uint i; i < len; ++i) {
// assume that burn shares will be called outside, just report the amount of shares to burn
v.amountsOut[0] = Math.mulDiv(v.totalUnderlying, amounts[i], v.totalAmounts, Math.Rounding.Floor);
amountsOut[i] = v.amountsOut[0];
require(
v.amountsOut[0] >= minUnderlyingOut[i],
IStabilityVault.ExceedSlippage(v.amountsOut[0], minUnderlyingOut[i])
);
IERC20(IVault(platformCVault_[1]).strategy().underlying()).transfer(owners[i], amountsOut[i]);
emit IStabilityVault.WithdrawAssets(msg.sender, owners[i], v.assets, amounts[i], v.amountsOut);
// disable last block protection in the emergency
// $.lastTransferBlock[owners[i]] = block.number;
if (!$.lastBlockDefenseWhitelist[msg.sender]) {
// 1 meta-vault token => 1 recovery token
recoveryAmountOut[i] = amounts[i];
IRecoveryToken(v.recoveryToken).mint(owners[i], recoveryAmountOut[i]);
if (pausedRecoveryTokens[i]) {
IRecoveryToken(v.recoveryToken).setAddressPaused(owners[i], true);
}
} else {
// the caller is a wrapped/meta-vault
// it mints its own recovery tokens
}
}
// todo we can have not zero (dust) underlying balance in result .. do we need to do anything with it?
return (amountsOut, recoveryAmountOut, sharesToBurn);
}
//endregion --------------------------------- Restricted actions
//region --------------------------------- Actions
function rebalanceMultiVault(
IMetaVault.MetaVaultStorage storage $,
uint[] memory withdrawShares,
uint[] memory depositAmountsProportions
) external {
uint len = $.vaults.length;
address[] memory _assets = $.assets.values();
for (uint i; i < len; ++i) {
if (withdrawShares[i] != 0) {
IStabilityVault($.vaults[i]).withdrawAssets(_assets, withdrawShares[i], new uint[](1));
require(depositAmountsProportions[i] == 0, IMetaVault.IncorrectRebalanceArgs());
}
}
uint totalToDeposit = IERC20(_assets[0]).balanceOf(address(this));
for (uint i; i < len; ++i) {
address vault = $.vaults[i];
uint[] memory amountsMax = new uint[](1);
amountsMax[0] = depositAmountsProportions[i] * totalToDeposit / 1e18;
if (amountsMax[0] != 0) {
IERC20(_assets[0]).forceApprove(vault, amountsMax[0]);
IStabilityVault(vault).depositAssets(_assets, amountsMax, 0, address(this));
require(withdrawShares[i] == 0, IMetaVault.IncorrectRebalanceArgs());
}
}
}
function _withdrawUnderlying(
IMetaVault.MetaVaultStorage storage $,
address platform_,
address cVault_,
uint amount,
uint minUnderlyingOut,
address receiver,
address owner
) external returns (uint underlyingOut, uint sharesToBurn) {
//slither-disable-next-line uninitialized-local
WithdrawUnderlyingLocals memory v;
if (msg.sender != owner) {
_spendAllowanceOrBlock($, owner, msg.sender, amount);
}
require(amount != 0, IControllable.IncorrectZeroArgument());
address _targetVault = MetaVaultLib._getTargetVault($, cVault_);
v.totalSupply = totalSupply($, platform_);
{
uint balance = balanceOf($, owner, v.totalSupply);
require(amount <= balance, IERC20Errors.ERC20InsufficientBalance(owner, balance, amount));
}
sharesToBurn = amountToShares(amount, $.totalShares, v.totalSupply);
require(sharesToBurn != 0, IMetaVault.ZeroSharesToBurn(amount));
// ensure that the target vault has required amount of meta-vault tokens
{
uint maxAmount = maxWithdrawUnderlying($, platform_, cVault_, owner);
require(amount <= maxAmount, IMetaVault.TooHighAmount(amount, maxAmount));
}
(v.vaultSharePriceUsd,) = IStabilityVault(_targetVault).price();
v.assets = new address[](1);
v.assets[0] = IVault(cVault_).strategy().underlying();
v.minAmounts = new uint[](1);
v.minAmounts[0] = minUnderlyingOut;
if (_targetVault == cVault_) {
// withdraw underlying from the target cVault vault
v.amountsOut = IStabilityVault(_targetVault).withdrawAssets(
v.assets,
_getTargetVaultSharesToWithdraw($, platform_, amount, v.vaultSharePriceUsd, true),
v.minAmounts,
receiver,
address(this)
);
underlyingOut = v.amountsOut[0];
} else {
// withdraw underlying from the child meta-vault
underlyingOut = IMetaVault(_targetVault).withdrawUnderlying(
cVault_,
_getTargetVaultSharesToWithdraw($, platform_, amount, v.vaultSharePriceUsd, true),
minUnderlyingOut,
receiver,
address(this)
);
}
// burning and updating defense-last-block is implemented outside
if (!$.lastBlockDefenseWhitelist[msg.sender]) {
// the user is not metavault so recovery tokens should be minted for broken vaults
address recoveryToken = $.recoveryTokens[cVault_];
// broken vault has not empty recovery token
if (recoveryToken != address(0)) {
IRecoveryToken(recoveryToken).mint(receiver, amount);
}
}
emit IStabilityVault.WithdrawAssets(msg.sender, owner, v.assets, amount, v.amountsOut);
}
function _withdrawAssets(
IMetaVault.MetaVaultStorage storage $,
address platform_,
address targetVault_,
address[] memory assets_,
uint amount,
uint[] memory minAssetAmountsOut,
address receiver,
address owner
) external returns (uint[] memory amountsOut, uint sharesToBurn) {
// assume that last block defense is already checked in the caller
if (msg.sender != owner) {
_spendAllowanceOrBlock($, owner, msg.sender, amount);
}
// ensure that provided assets correspond to the target vault
// assume that user should call {assetsForWithdraw} before calling this function and get correct list of assets
checkProvidedAssets(assets_, targetVault_);
require(amount != 0, IControllable.IncorrectZeroArgument());
uint _totalSupply = totalSupply($, platform_);
{
uint balance = balanceOf($, owner, _totalSupply);
require(amount <= balance, IERC20Errors.ERC20InsufficientBalance(owner, balance, amount));
}
require(assets_.length == minAssetAmountsOut.length, IControllable.IncorrectArrayLength());
sharesToBurn = MetaVaultLib.amountToShares(amount, $.totalShares, _totalSupply);
require(sharesToBurn != 0, IMetaVault.ZeroSharesToBurn(amount));
if (MetaVaultLib.isMultiVault($)) {
// withdraw the amount from all sub-vaults starting from the target vault
amountsOut = _withdrawFromMultiVault($, platform_, $.vaults, assets_, amount, receiver, targetVault_);
// check slippage
for (uint j; j < assets_.length; ++j) {
require(
amountsOut[j] >= minAssetAmountsOut[j],
IStabilityVault.ExceedSlippage(amountsOut[j], minAssetAmountsOut[j])
);
}
} else {
// ensure that the target vault has required amount
uint vaultSharePriceUsd;
{
uint maxAmountToWithdrawFromVault;
(maxAmountToWithdrawFromVault, vaultSharePriceUsd) =
_maxAmountToWithdrawFromVault($, platform_, targetVault_);
require(
amount <= maxAmountToWithdrawFromVault,
IMetaVault.MaxAmountForWithdrawPerTxReached(amount, maxAmountToWithdrawFromVault)
);
}
// withdraw the amount from the target vault
amountsOut = IStabilityVault(targetVault_).withdrawAssets(
assets_,
_getTargetVaultSharesToWithdraw($, platform_, amount, vaultSharePriceUsd, true),
minAssetAmountsOut,
receiver,
address(this)
);
}
emit IStabilityVault.WithdrawAssets(msg.sender, owner, assets_, amount, amountsOut);
}
function transferFrom(
IMetaVault.MetaVaultStorage storage $,
address platform_,
address from,
address to,
uint amount
) external {
// assume that last block defense is already checked in the caller
require(to != address(0), IERC20Errors.ERC20InvalidReceiver(to));
_spendAllowanceOrBlock($, from, msg.sender, amount);
uint shareTransfer = MetaVaultLib.amountToShares(amount, $.totalShares, totalSupply($, platform_));
$.shareBalance[from] -= shareTransfer;
$.shareBalance[to] += shareTransfer;
// update() should be called in the caller
}
//endregion --------------------------------- Actions
//region --------------------------------- View functions
function currentProportions(IMetaVault.MetaVaultStorage storage $)
external
view
returns (uint[] memory proportions)
{
return _currentProportions($);
}
function vaultForDepositWithdraw(IMetaVault.MetaVaultStorage storage $)
external
view
returns (address targetForDeposit, address targetForWithdraw)
{
address[] memory _vaults = $.vaults;
if ($.totalShares == 0) {
return (_vaults[0], _vaults[0]);
}
uint len = _vaults.length;
uint[] memory _proportions = _currentProportions($);
uint[] memory _targetProportions = $.targetProportions;
uint lowProportionDiff;
uint highProportionDiff;
targetForDeposit = _vaults[0];
targetForWithdraw = _vaults[0];
for (uint i; i < len; ++i) {
if (_proportions[i] < _targetProportions[i]) {
uint diff = _targetProportions[i] - _proportions[i];
if (diff > lowProportionDiff) {
lowProportionDiff = diff;
targetForDeposit = _vaults[i];
}
} else if (_proportions[i] > _targetProportions[i] && _proportions[i] > 1e16) {
uint diff = _proportions[i] - _targetProportions[i];
if (diff > highProportionDiff) {
highProportionDiff = diff;
targetForWithdraw = _vaults[i];
}
}
}
}
function isMultiVault(IMetaVault.MetaVaultStorage storage $) internal view returns (bool) {
return CommonLib.eq($._type, VaultTypeLib.MULTIVAULT);
}
function amountToShares(uint amount, uint totalShares_, uint totalSupply_) internal pure returns (uint) {
return totalSupply_ == 0 ? 0 : Math.mulDiv(amount, totalShares_, totalSupply_, Math.Rounding.Floor);
}
function balanceOf(
IMetaVault.MetaVaultStorage storage $,
address account,
uint totalSupply_
) internal view returns (uint) {
uint _totalShares = $.totalShares;
return _totalShares == 0
? 0
: Math.mulDiv($.shareBalance[account], totalSupply_, _totalShares, Math.Rounding.Floor);
}
function price(
IMetaVault.MetaVaultStorage storage $,
address platform_
) public view returns (uint price_, bool trusted_) {
address _pegAsset = $.pegAsset;
return _pegAsset == address(0)
? (1e18, true)
: IPriceReader(IPlatform(platform_).priceReader()).getPrice(_pegAsset);
}
function tvl(
IMetaVault.MetaVaultStorage storage $,
address platform_
) public view returns (uint tvl_, bool trusted_) {
IPriceReader priceReader = IPriceReader(IPlatform(platform_).priceReader());
bool notSafePrice;
// get deposited TVL of used vaults
address[] memory _vaults = $.vaults;
uint len = _vaults.length;
for (uint i; i < len; ++i) {
(uint vaultSharePrice, bool safe) = priceReader.getVaultPrice(_vaults[i]);
if (!safe) {
notSafePrice = true;
}
uint vaultSharesBalance = IERC20(_vaults[i]).balanceOf(address(this));
tvl_ += Math.mulDiv(vaultSharePrice, vaultSharesBalance, 1e18, Math.Rounding.Floor);
}
// get TVL of assets on contract balance
address[] memory _assets = $.assets.values();
len = _assets.length;
uint[] memory assetsOnBalance = new uint[](len);
for (uint i; i < len; ++i) {
assetsOnBalance[i] = IERC20(_assets[i]).balanceOf(address(this));
}
(uint assetsTvlUsd,,, bool trustedAssetsPrices) = priceReader.getAssetsPrice(_assets, assetsOnBalance);
tvl_ += assetsTvlUsd;
if (!trustedAssetsPrices) {
notSafePrice = true;
}
trusted_ = !notSafePrice;
}
function totalSupply(IMetaVault.MetaVaultStorage storage $, address platform_) public view returns (uint) {
// totalSupply is balance of peg asset
(uint tvlUsd,) = tvl($, platform_);
(uint priceAsset,) = price($, platform_);
return Math.mulDiv(tvlUsd, 1e18, priceAsset, Math.Rounding.Floor);
}
function maxWithdrawUnderlying(
IMetaVault.MetaVaultStorage storage $,
address platform_,
address cVault_,
address account
) public view returns (uint amount) {
uint userBalance = balanceOf($, account, totalSupply($, platform_));
address _targetVault = _getTargetVault($, cVault_);
if (_targetVault == cVault_) {
(uint maxMetaVaultTokensToWithdraw,) = _maxAmountToWithdrawFromVaultForShares(
$, platform_, _targetVault, IStabilityVault(_targetVault).maxWithdraw(address(this), 1)
);
return Math.min(userBalance, maxMetaVaultTokensToWithdraw);
} else {
(uint maxMetaVaultTokensToWithdraw,) = _maxAmountToWithdrawFromVaultForShares(
$, platform_, _targetVault, IMetaVault(_targetVault).maxWithdrawUnderlying(cVault_, address(this))
);
return Math.min(userBalance, maxMetaVaultTokensToWithdraw);
}
}
function internalSharePrice(
IMetaVault.MetaVaultStorage storage $,
address platform_
) external view returns (uint sharePrice, int apr, uint storedSharePrice, uint storedTime) {
uint totalShares = $.totalShares;
if (totalShares != 0) {
sharePrice = Math.mulDiv(totalSupply($, platform_), 1e18, totalShares, Math.Rounding.Ceil);
storedSharePrice = $.storedSharePrice;
storedTime = $.storedTime;
if (storedTime != 0) {
apr = _computeApr(sharePrice, int(sharePrice) - int(storedSharePrice), block.timestamp - storedTime);
}
}
return (sharePrice, apr, storedSharePrice, storedTime);
}
function previewDepositAssets(
IMetaVault.MetaVaultStorage storage $,
address platform_,
address _targetVault,
address[] memory assets_,
uint[] memory amountsMax
) external view returns (uint[] memory amountsConsumed, uint sharesOut, uint valueOut) {
uint targetVaultSharesOut;
uint targetVaultStrategyValueOut;
(uint targetVaultSharePrice,) = IStabilityVault(_targetVault).price();
(amountsConsumed, targetVaultSharesOut, targetVaultStrategyValueOut) =
IStabilityVault(_targetVault).previewDepositAssets(assets_, amountsMax);
{
uint usdOut = Math.mulDiv(targetVaultSharePrice, targetVaultSharesOut, 1e18, Math.Rounding.Floor);
sharesOut = _usdAmountToMetaVaultBalance($, platform_, usdOut);
}
uint _totalShares = $.totalShares;
valueOut = _totalShares == 0
? sharesOut
: MetaVaultLib.amountToShares(sharesOut, $.totalShares, totalSupply($, platform_));
}
function maxWithdrawMultiVault(
IMetaVault.MetaVaultStorage storage $,
address platform_,
uint userBalance,
uint mode
) external view returns (uint amount) {
for (uint i; i < $.vaults.length; ++i) {
address _targetVault = $.vaults[i];
(uint maxMetaVaultTokensToWithdraw,) = MetaVaultLib._maxAmountToWithdrawFromVaultForShares(
$,
platform_,
_targetVault,
mode == 0
? IStabilityVault(_targetVault).maxWithdraw(address(this))
: IStabilityVault(_targetVault).maxWithdraw(address(this), mode)
);
amount += maxMetaVaultTokensToWithdraw;
if (userBalance < amount) return userBalance;
}
return amount;
}
function maxWithdrawMetaVault(
IMetaVault.MetaVaultStorage storage $,
address platform_,
uint userBalance,
address _targetVault,
uint mode
) external view returns (uint amount) {
// Use reverse logic of withdrawAssets() to calculate max amount of MetaVault balance that can be withdrawn
// The logic is the same as for {_maxAmountToWithdrawFromVault} but balance is taken for the given account
(uint maxMetaVaultTokensToWithdraw,) = MetaVaultLib._maxAmountToWithdrawFromVaultForShares(
$,
platform_,
_targetVault,
mode == 0
? IStabilityVault(_targetVault).maxWithdraw(address(this))
: IStabilityVault(_targetVault).maxWithdraw(address(this), mode)
);
return Math.min(userBalance, maxMetaVaultTokensToWithdraw);
}
function maxDepositMultiVault(
IMetaVault.MetaVaultStorage storage $,
address account
) external view returns (uint[] memory maxAmounts) {
// MultiVault supports depositing to all sub-vaults
// so we need to calculate summary max deposit amounts for all sub-vaults
// but result cannot exceed type(uint).max
for (uint i; i < $.vaults.length; ++i) {
address _targetVault = $.vaults[i];
if (i == 0) {
// lazy initialization of maxAmounts
maxAmounts = new uint[](IStabilityVault(_targetVault).assets().length);
}
uint[] memory _amounts = IStabilityVault(_targetVault).maxDeposit(account);
for (uint j; j < _amounts.length; ++j) {
if (maxAmounts[j] != type(uint).max) {
maxAmounts[j] = _amounts[j] == type(uint).max ? type(uint).max : maxAmounts[j] + _amounts[j];
}
}
}
return maxAmounts;
}
/// @notice Ensures that the assets array corresponds to the assets of the given vault.
/// For simplicity we assume that the assets cannot be reordered.
function checkProvidedAssets(address[] memory assets_, address vault) internal view {
address[] memory assetsToCheck = IStabilityVault(vault).assets();
if (assets_.length != assetsToCheck.length) {
revert IControllable.IncorrectArrayLength();
}
for (uint i; i < assets_.length; ++i) {
if (assets_[i] != assetsToCheck[i]) {
revert IControllable.IncorrectAssetsList(assets_, assetsToCheck);
}
}
}
//endregion --------------------------------- View functions
//region --------------------------------- Internal logic
function _currentProportions(IMetaVault.MetaVaultStorage storage $)
internal
view
returns (uint[] memory proportions)
{
address[] memory _vaults = $.vaults;
if ($.totalShares == 0) {
return $.targetProportions;
}
uint len = _vaults.length;
proportions = new uint[](len);
uint[] memory vaultUsdValue = new uint[](len);
uint totalDepositedTvl;
for (uint i; i < len; ++i) {
vaultUsdValue[i] = MetaVaultLib._getVaultUsdAmount(_vaults[i]);
totalDepositedTvl += vaultUsdValue[i];
}
for (uint i; i < len; ++i) {
proportions[i] =
totalDepositedTvl == 0 ? 0 : Math.mulDiv(vaultUsdValue[i], 1e18, totalDepositedTvl, Math.Rounding.Floor);
}
}
function _getVaultUsdAmount(address vault) internal view returns (uint) {
(uint vaultTvl,) = IStabilityVault(vault).tvl();
uint vaultSharesBalance = IERC20(vault).balanceOf(address(this));
uint vaultTotalSupply = IERC20(vault).totalSupply();
return
vaultTotalSupply == 0 ? 0 : Math.mulDiv(vaultSharesBalance, vaultTvl, vaultTotalSupply, Math.Rounding.Floor);
}
function _checkProportions(uint[] memory proportions_) internal pure {
uint len = proportions_.length;
uint total;
for (uint i; i < len; ++i) {
total += proportions_[i];
}
require(total == 1e18, IMetaVault.IncorrectProportions());
}
/// @notice Find meta vault that contains the given {cVault_}
/// @dev Assume here that there are max 2 meta vaults in the circle: meta vault - meta vault - c-vault
function _getTargetVault(
IMetaVault.MetaVaultStorage storage $,
address cVault_
) internal view returns (address targetVault) {
// check if cVault belongs to the current MetaVault
address[] memory _vaults = $.vaults;
for (uint i; i < _vaults.length; ++i) {
if (_vaults[i] == cVault_) {
return _vaults[i];
}
}
// check if cVault belongs to one of the sub-meta-vaults
for (uint i; i < _vaults.length; ++i) {
address[] memory subVaults = IMetaVault(_vaults[i]).vaults();
for (uint j; j < subVaults.length; ++j) {
if (subVaults[j] == cVault_) {
return _vaults[i]; // return the parent vault of the sub-vault
}
}
}
revert IMetaVault.VaultNotFound(cVault_);
}
/// @notice Get the target shares to withdraw from the vault for the given {amount}.
/// @param amount Amount of meta-vault tokens
/// @param vaultSharePriceUsd Price of the vault shares in USD
/// @param revertOnLessThanThreshold If true, reverts if the USD amount to withdraw is less than the threshold.
/// @return targetVaultSharesToWithdraw Amount of shares to withdraw from the vault
function _getTargetVaultSharesToWithdraw(
IMetaVault.MetaVaultStorage storage $,
address platform_,
uint amount,
uint vaultSharePriceUsd,
bool revertOnLessThanThreshold
) internal view returns (uint targetVaultSharesToWithdraw) {
uint usdToWithdraw = _metaVaultBalanceToUsdAmount($, platform_, amount);
if (usdToWithdraw > USD_THRESHOLD) {
return Math.mulDiv(usdToWithdraw, 1e18, vaultSharePriceUsd, Math.Rounding.Floor);
} else {
if (revertOnLessThanThreshold) {
revert IMetaVault.UsdAmountLessThreshold(usdToWithdraw, USD_THRESHOLD);
}
return 0;
}
}
function _metaVaultBalanceToUsdAmount(
IMetaVault.MetaVaultStorage storage $,
address platform_,
uint amount
) internal view returns (uint) {
(uint priceAsset,) = price($, platform_);
return Math.mulDiv(amount, priceAsset, 1e18, Math.Rounding.Ceil);
}
/// @dev Shared implementation for {maxWithdraw} and {maxWithdrawAmountTx}
/// @param vault Vault to withdraw
/// @param vaultSharesToWithdraw Amount of shares to withdraw from the {vault}
/// @return maxAmount Amount of meta-vault tokens to withdraw
/// @return vaultSharePrice Price of the {vault}
function _maxAmountToWithdrawFromVaultForShares(
IMetaVault.MetaVaultStorage storage $,
address platform_,
address vault,
uint vaultSharesToWithdraw
) internal view returns (uint maxAmount, uint vaultSharePrice) {
(vaultSharePrice,) = IStabilityVault(vault).price();
uint vaultUsd = Math.mulDiv(vaultSharePrice, vaultSharesToWithdraw, 1e18, Math.Rounding.Floor);
// Convert USD amount to MetaVault tokens
maxAmount = _usdAmountToMetaVaultBalance($, platform_, vaultUsd);
}
function _usdAmountToMetaVaultBalance(
IMetaVault.MetaVaultStorage storage $,
address platform_,
uint usdAmount
) internal view returns (uint) {
(uint priceAsset,) = price($, platform_);
return Math.mulDiv(usdAmount, 1e18, priceAsset, Math.Rounding.Floor);
}
/// @notice Withdraw the {amount} from multiple sub-vaults starting with the {targetVault_}.
/// @dev Slippage is checked outside this function.
/// @param amount Amount of meta-vault tokens to withdraw.
function _withdrawFromMultiVault(
IMetaVault.MetaVaultStorage storage $,
address platform_,
address[] memory vaults_,
address[] memory assets_,
uint amount,
address receiver,
address targetVault_
) internal returns (uint[] memory amountsOut) {
uint totalAmount = amount;
// ------------------- set target vault on the first position in vaults_
setTargetVaultFirst(targetVault_, vaults_);
// ------------------- withdraw from vaults until requested amount is withdrawn
uint len = vaults_.length;
amountsOut = new uint[](assets_.length);
for (uint i; i < len; ++i) {
(uint amountToWithdraw, uint targetVaultSharesToWithdraw) =
_getAmountToWithdrawFromVault($, platform_, vaults_[i], totalAmount, address(this));
if (targetVaultSharesToWithdraw != 0) {
uint[] memory _amountsOut = IStabilityVault(vaults_[i]).withdrawAssets(
assets_,
targetVaultSharesToWithdraw,
new uint[](assets_.length), // minAssetAmountsOut is checked outside
receiver,
address(this)
);
for (uint j; j < assets_.length; ++j) {
amountsOut[j] += _amountsOut[j];
}
totalAmount -= amountToWithdraw;
if (totalAmount == 0) break;
}
}
// ------------------- ensure that all requested amount is withdrawn
require(totalAmount == 0, IMetaVault.MaxAmountForWithdrawPerTxReached(amount, amount - totalAmount));
return amountsOut;
}
/// @notice Get the amount to withdraw from the vault and the target shares to withdraw.
/// The amount is limited by the max withdraw amount of the vault.
/// @param amount Amount of meta-vault tokens
/// @param vault Vault to withdraw from
/// @param owner Owner of the shares to withdraw
/// @return amountToWithdraw Amount of meta-vault tokens to withdraw
/// @return targetVaultSharesToWithdraw Amount of shares to withdraw from the vault
function _getAmountToWithdrawFromVault(
IMetaVault.MetaVaultStorage storage $,
address platform_,
address vault,
uint amount,
address owner
) internal view returns (uint amountToWithdraw, uint targetVaultSharesToWithdraw) {
(uint maxAmount, uint vaultSharePriceUsd) =
_maxAmountToWithdrawFromVaultForShares($, platform_, vault, IStabilityVault(vault).maxWithdraw(owner));
amountToWithdraw = Math.min(amount, maxAmount);
targetVaultSharesToWithdraw =
MetaVaultLib._getTargetVaultSharesToWithdraw($, platform_, amountToWithdraw, vaultSharePriceUsd, false);
}
/// @notice Get the maximum amount of meta-vault tokens that can be withdrawn from the vault
/// without taking into account maxWithdraw limit.
function _maxAmountToWithdrawFromVault(
IMetaVault.MetaVaultStorage storage $,
address platform_,
address vault
) internal view returns (uint maxAmount, uint vaultSharePrice) {
return _maxAmountToWithdrawFromVaultForShares($, platform_, vault, IERC20(vault).balanceOf(address(this)));
}
/// @notice Find target vault in {vaults} and move it on the first position.
function setTargetVaultFirst(
address targetVault,
address[] memory vaults_
) internal pure returns (address[] memory) {
uint len = vaults_.length;
for (uint i; i < len; ++i) {
if (vaults_[i] == targetVault) {
// first withdraw should be from the target vault
// the order of other vaults does not matter because the rebalancer is called often enough
(vaults_[0], vaults_[i]) = (vaults_[i], vaults_[0]);
break;
}
}
return vaults_;
}
function _computeApr(uint tvl_, int earned, uint duration) internal pure returns (int) {
return (tvl_ == 0 || duration == 0)
? int(0)
: earned * int(1e18) * 100_000 * int(365) / int(tvl_) / int(duration * 1e18 / 1 days);
}
function _spendAllowanceOrBlock(
IMetaVault.MetaVaultStorage storage $,
address owner,
address spender,
uint amount
) internal {
uint currentAllowance = $.allowance[owner][spender];
if (owner != msg.sender && currentAllowance != type(uint).max) {
require(
currentAllowance >= amount, IERC20Errors.ERC20InsufficientAllowance(spender, currentAllowance, amount)
);
$.allowance[owner][spender] = currentAllowance - amount;
}
}
//endregion --------------------------------- Internal logic
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
/// @custom:storage-location erc7201:openzeppelin.storage.ERC20
struct ERC20Storage {
mapping(address account => uint256) _balances;
mapping(address account => mapping(address spender => uint256)) _allowances;
uint256 _totalSupply;
string _name;
string _symbol;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
function _getERC20Storage() private pure returns (ERC20Storage storage $) {
assembly {
$.slot := ERC20StorageLocation
}
}
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
ERC20Storage storage $ = _getERC20Storage();
$._name = name_;
$._symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
ERC20Storage storage $ = _getERC20Storage();
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
$._totalSupply += value;
} else {
uint256 fromBalance = $._balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
$._balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
$._totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
$._balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
ERC20Storage storage $ = _getERC20Storage();
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
$._allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IStabilityVault} from "./IStabilityVault.sol";
interface IMetaVault is IStabilityVault {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATA TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @custom:storage-location erc7201:stability.MetaVault
struct MetaVaultStorage {
/// @dev Flash loan exploit protection.
/// Prevents manipulations with deposit/transfer and withdraw/deposit in short time.
mapping(address msgSender => uint blockNumber) lastTransferBlock;
/// @dev Immutable vault type ID
string _type;
/// @inheritdoc IMetaVault
address pegAsset;
/// @inheritdoc IMetaVault
EnumerableSet.AddressSet assets;
/// @inheritdoc IMetaVault
address[] vaults;
/// @inheritdoc IMetaVault
uint[] targetProportions;
/// @inheritdoc IERC20Metadata
string name;
/// @inheritdoc IERC20Metadata
string symbol;
/// @inheritdoc IERC20
mapping(address owner => mapping(address spender => uint allowance)) allowance;
/// @dev Total internal shares
uint totalShares;
/// @dev Internal user balances
mapping(address => uint) shareBalance;
/// @dev Stored share price to track MetaVault APR
uint storedSharePrice;
/// @dev Last time APR emitted
uint storedTime;
/// @inheritdoc IStabilityVault
bool lastBlockDefenseDisabled;
/// @dev Whitelist for addresses (strategies) that are able to temporarily disable last-block-defense
mapping(address owner => bool whitelisted) lastBlockDefenseWhitelist;
/// @dev Recovery tokens for broken c-vaults
mapping(address cVault => address recoveryToken) recoveryTokens;
}
/// @notice Types of last-block-defense disable modes
enum LastBlockDefenseDisableMode {
/// @notice Last-block-defense is enabled
ENABLED_0,
/// @notice Last-block-defense is disabled for the current tx, update and _beforeDepositOrWithdraw update maps
DISABLED_TX_UPDATE_MAPS_1,
/// @notice Last-block-defense is disabled for the current tx, do not update maps
DISABLE_TX_DONT_UPDATE_MAPS_2
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
event APR(uint sharePrice, int apr, uint lastStoredSharePrice, uint duration, uint tvl);
event Rebalance(uint[] withdrawShares, uint[] depositAmountsProportions, int cost);
event AddVault(address vault);
event TargetProportions(uint[] proportions);
event WhitelistChanged(address owner, bool whitelisted);
event RemoveVault(address vault);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error UsdAmountLessThreshold(uint amountUsd, uint threshold);
error MaxAmountForWithdrawPerTxReached(uint amount, uint maxAmount);
error ZeroSharesToBurn(uint amountToWithdraw);
error IncorrectProportions();
error IncorrectRebalanceArgs();
error IncorrectVault();
error NotWhitelisted();
error VaultNotFound(address vault);
error TooHighAmount(uint amount, uint maxAmount);
error RecoveryTokenNotSet(address cVault_);
//region --------------------------------------- View functions
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEW FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Not perform operations with value less than threshold
function USD_THRESHOLD() external view returns (uint);
/// @notice Vault price pegged currency
function pegAsset() external view returns (address);
/// @notice Used CVaults
function vaults() external view returns (address[] memory);
/// @notice Current proportions of value between vaults
/// @return Shares of 1e18
function currentProportions() external view returns (uint[] memory);
/// @notice Target proportions of value between vaults
/// @return Shares of 1e18
function targetProportions() external view returns (uint[] memory);
/// @notice Vault where to deposit first
function vaultForDeposit() external view returns (address);
/// @notice Assets for deposit
function assetsForDeposit() external view returns (address[] memory);
/// @notice Vault for withdraw first
function vaultForWithdraw() external view returns (address);
/// @notice Assets for withdraw
function assetsForWithdraw() external view returns (address[] memory);
/// @notice Maximum withdraw amount for next withdraw TX
function maxWithdrawAmountTx() external view returns (uint);
/// @notice Show internal share prices
/// @return sharePrice Current internal share price
/// @return apr Current APR
/// @return storedSharePrice Stored internal share price
/// @return storedTime Time when stored
function internalSharePrice()
external
view
returns (uint sharePrice, int apr, uint storedSharePrice, uint storedTime);
/// @notice True if the {addr} is in last-block-defense whitelist
function whitelisted(address addr) external view returns (bool);
/// @param cVault_ Address of the target cVault from which underlying will be withdrawn.
/// @param account Address of the account for which the maximum withdraw amount is calculated.
/// @return amount Maximum amount that can be withdrawn from the vault for the given account.
/// This is max amount that can be passed to `withdraw` function.
function maxWithdrawUnderlying(address cVault_, address account) external view returns (uint amount);
/// @notice Recovery token for the given cVault. Zero for not-broken vaults
function recoveryToken(address cVault_) external view returns (address);
//endregion --------------------------------------- View functions
//region --------------------------------------- Write functions
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Update stored internal share price and emit APR
/// @return sharePrice Current internal share price
/// @return apr APR for last period
/// @return lastStoredSharePrice Last stored internal share price
/// @return duration Duration of last tracked period
function emitAPR() external returns (uint sharePrice, int apr, uint lastStoredSharePrice, uint duration);
/// @notice MetaVault re-balancing
/// @param withdrawShares Shares to withdraw from vaults
/// @param depositAmountsProportions Proportions to deposit
/// @return proportions Result proportions
/// @return cost Re-balance cost in USD
function rebalance(
uint[] memory withdrawShares,
uint[] memory depositAmountsProportions
) external returns (uint[] memory proportions, int cost);
/// @notice Set new target proportions
function setTargetProportions(uint[] memory newTargetProportions) external;
/// @notice Add CVault to MetaVault
function addVault(address vault, uint[] memory newTargetProportions) external;
/// @notice Remove CVault from MetaVault
/// @dev The proportions of the vault should be zero, total deposited amount should be less then threshold
function removeVault(address vault) external;
/// @notice Init after deploy
function initialize(
address platform_,
string memory type_,
address pegAsset_,
string memory name_,
string memory symbol_,
address[] memory vaults_,
uint[] memory proportions_
) external;
/// @notice Add/remove {addr} to/from last-block-defense whitelist
function changeWhitelist(address addr, bool addToWhitelist) external;
/// @notice Allow whitelisted address to disable last-block-defense for the current block or enable it back
/// @param disableMode See {LastBlockDefenseDisableMode}
/// 0 - the defence enabled
/// 1 - the defence disabled in tx, maps are updated
function setLastBlockDefenseDisabledTx(uint disableMode) external;
/// @notice Allow to cache assets and vaults prices in the transient cache
/// @param clear True - clear the cache, false - prepare the cache
function cachePrices(bool clear) external;
/// @notice Withdraw underlying from the given cVault
/// @param cVault_ Address of the target cVault from which underlying will be withdrawn.
/// The cVault can belong to the MetaVault directly or belong to one of its sub-meta-vaults.
/// @param amount Amount of meta-vault tokens to be withdrawn
/// @param minUnderlyingOut Minimum amount of underlying to be received
/// @param receiver Address to receive underlying
/// @param owner Address of the owner of the meta-vault tokens
/// @return underlyingOut Amount of underlying received
function withdrawUnderlying(
address cVault_,
uint amount,
uint minUnderlyingOut,
address receiver,
address owner
) external returns (uint underlyingOut);
/// @notice Withdraw underlying from the broken cVaults, mint recovery tokens if the caller is not whitelisted
/// @custom:access Governance, multisig or whitelisted addresses (i.e. wrapped meta-vaults)
/// @param cVault_ Address of the target cVault from which underlying will be withdrawn.
/// @param owners Addresses of the owners of the meta-vault tokens
/// @param amounts Amounts of meta-vault tokens to be withdrawn for each owner (0 - withdraw all)
/// @param minUnderlyingOut Minimum amount of underlying to be received for each owner
/// @param pausedRecoveryTokens If true, the recovery token will be set on puase for the given owner after minting
/// @return amountOut Amounts of underlying received for each owner.
/// @return recoveryAmountOut Amounts of recovery tokens received for each owner.
function withdrawUnderlyingEmergency(
address cVault_,
address[] memory owners,
uint[] memory amounts,
uint[] memory minUnderlyingOut,
bool[] memory pausedRecoveryTokens
) external returns (uint[] memory amountOut, uint[] memory recoveryAmountOut);
/// @notice Set recovery token address for the given cVault
/// @custom:access Governance, multisig
function setRecoveryToken(address cVault_, address recoveryToken_) external;
//endregion --------------------------------------- Write functions
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @dev Base core interface implemented by most platform contracts.
/// Inherited contracts store an immutable Platform proxy address in the storage,
/// which provides authorization capabilities and infrastructure contract addresses.
/// @author Alien Deployer (https://github.com/a17)
/// @author JodsMigel (https://github.com/JodsMigel)
/// @author dvpublic (https://github.com/dvpublic)
interface IControllable {
//region ----- Custom Errors -----
error IncorrectZeroArgument();
error IncorrectMsgSender();
error NotGovernance();
error NotMultisig();
error NotGovernanceAndNotMultisig();
error NotOperator();
error NotFactory();
error NotPlatform();
error NotVault();
error IncorrectArrayLength();
error AlreadyExist();
error NotExist();
error NotTheOwner();
error ETHTransferFailed();
error IncorrectInitParams();
error InsufficientBalance();
error IncorrectBalance();
error IncorrectLtv(uint ltv);
error TooLowValue(uint value);
error IncorrectAssetsList(address[] assets_, address[] expectedAssets_);
//endregion -- Custom Errors -----
event ContractInitialized(address platform, uint ts, uint block);
/// @notice Stability Platform main contract address
function platform() external view returns (address);
/// @notice Version of contract implementation
/// @dev SemVer scheme MAJOR.MINOR.PATCH
//slither-disable-next-line naming-convention
function VERSION() external view returns (string memory);
/// @notice Block number when contract was initialized
function createdBlock() external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
/// @notice Base interface of Stability Vault
interface IStabilityVault is IERC20, IERC20Metadata {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error WaitAFewBlocks();
error ExceedSlippage(uint mintToUser, uint minToMint);
error ExceedMaxSupply(uint maxSupply);
error NotSupported();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
event DepositAssets(address indexed account, address[] assets, uint[] amounts, uint mintAmount);
event WithdrawAssets(
address indexed sender, address indexed owner, address[] assets, uint sharesAmount, uint[] amountsOut
);
event MaxSupply(uint maxShares);
event VaultName(string newName);
event VaultSymbol(string newSymbol);
event LastBlockDefenseDisabled(bool isDisabled);
//region --------------------------------------- View functions
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEW FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Underlying assets
function assets() external view returns (address[] memory);
/// @notice Immutable vault type ID
function vaultType() external view returns (string memory);
/// @dev Calculation of consumed amounts, shares amount and liquidity/underlying value for provided available amounts of strategy assets
/// @param assets_ Assets suitable for vault strategy. Can be strategy assets, underlying asset or specific set of assets depending on strategy logic.
/// @param amountsMax Available amounts of assets_ that user wants to invest in vault
/// @return amountsConsumed Amounts of strategy assets that can be deposited by providing amountsMax
/// @return sharesOut Amount of vault shares that will be minted
/// @return valueOut Liquidity value or underlying token amount that will be received by the strategy
function previewDepositAssets(
address[] memory assets_,
uint[] memory amountsMax
) external view returns (uint[] memory amountsConsumed, uint sharesOut, uint valueOut);
/// @dev USD price of share with 18 decimals.
/// Not trusted vault share price can be manipulated, used only OFF-CHAIN.
/// @return price_ Price of 1e18 shares with 18 decimals precision
/// @return trusted True means oracle price, false means AMM spot price
function price() external view returns (uint price_, bool trusted);
/// @dev USD price of assets managed by strategy with 18 decimals
/// Not trusted vault share price can be manipulated, used only OFF-CHAIN.
/// @return tvl_ Total USD value of final assets in vault
/// @return trusted True means TVL calculated based only on oracle prices, false means AMM spot price was used.
function tvl() external view returns (uint tvl_, bool trusted);
/// @dev Minimum 6 blocks between deposit and withdraw check disabled
function lastBlockDefenseDisabled() external view returns (bool);
/// @return amount Maximum amount that can be withdrawn from the vault for the given account.
/// This is max amount that can be passed to `withdraw` function.
/// The implementation should take into account IStrategy.maxWithdrawAssets
/// @dev It's alias of IStabilityVault.maxWithdraw(account, 0) for backwords compatibility.
function maxWithdraw(address account) external view returns (uint amount);
/// @return amount Maximum amount that can be withdrawn from the vault for the given account.
/// This is max amount that can be passed to `withdraw` function.
/// The implementation should take into account IStrategy.maxWithdrawAssets
/// @param mode 0 - Return amount that can be withdrawn in assets
/// 1 - Return amount that can be withdrawn in underlying
function maxWithdraw(address account, uint mode) external view returns (uint amount);
/// @return maxAmounts Maximum amounts that can be deposited to the vault for the given account.
/// This is max amounts of {assets} that can be passed to `depositAssets` function as {amountsMax}.
/// The implementation should take into account IStrategy.maxDepositAssets
/// Return type(uint).max if there is no limit for the asset.
function maxDeposit(address account) external view returns (uint[] memory maxAmounts);
//endregion --------------------------------------- View functions
//region --------------------------------------- Write functions
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deposit final assets (pool assets) to the strategy and minting of vault shares.
/// If the strategy interacts with a pool or farms through an underlying token, then it will be minted.
/// Emits a {DepositAssets} event with consumed amounts.
/// @param assets_ Assets suitable for the strategy. Can be strategy assets, underlying asset or specific set of assets depending on strategy logic.
/// @param amountsMax Available amounts of assets_ that user wants to invest in vault
/// @param minSharesOut Slippage tolerance. Minimal shares amount which must be received by user.
/// @param receiver Receiver of deposit. If receiver is zero address, receiver is msg.sender.
function depositAssets(
address[] memory assets_,
uint[] memory amountsMax,
uint minSharesOut,
address receiver
) external;
/// @dev Burning shares of vault and obtaining strategy assets.
/// @param assets_ Assets suitable for the strategy. Can be strategy assets, underlying asset or specific set of assets depending on strategy logic.
/// @param amountShares Shares amount for burning
/// @param minAssetAmountsOut Slippage tolerance. Minimal amounts of strategy assets that user must receive.
/// @return Amount of assets for withdraw. It's related to assets_ one-by-one.
function withdrawAssets(
address[] memory assets_,
uint amountShares,
uint[] memory minAssetAmountsOut
) external returns (uint[] memory);
/// @dev Burning shares of vault and obtaining strategy assets.
/// @param assets_ Assets suitable for the strategy. Can be strategy assets, underlying asset or specific set of assets depending on strategy logic.
/// @param amountShares Shares amount for burning
/// @param minAssetAmountsOut Slippage tolerance. Minimal amounts of strategy assets that user must receive.
/// @param receiver Receiver of assets
/// @param owner Owner of vault shares
/// @return Amount of assets for withdraw. It's related to assets_ one-by-one.
function withdrawAssets(
address[] memory assets_,
uint amountShares,
uint[] memory minAssetAmountsOut,
address receiver,
address owner
) external returns (uint[] memory);
/// @dev Changing ERC20 name of vault
function setName(string calldata newName) external;
/// @dev Changing ERC20 symbol of vault
function setSymbol(string calldata newSymbol) external;
/// @dev Enable or disable last block check
function setLastBlockDefenseDisabled(bool isDisabled) external;
//endregion --------------------------------------- Write functions
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IStrategy} from "./IStrategy.sol";
import {IStabilityVault} from "./IStabilityVault.sol";
/// @notice Vault core interface.
/// Derived implementations can be effective for building tokenized vaults with single or multiple underlying liquidity mining position.
/// Fungible, static non-fungible and actively re-balancing liquidity is supported, as well as single token liquidity provided to lending protocols.
/// Vaults can be used for active concentrated liquidity management and market making.
/// @author Jude (https://github.com/iammrjude)
/// @author JodsMigel (https://github.com/JodsMigel)
interface IVault is IERC165, IStabilityVault {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error NotEnoughBalanceToPay();
error FuseTrigger();
error ExceedSlippageExactAsset(address asset, uint mintToUser, uint minToMint);
error NotEnoughAmountToInitSupply(uint mintAmount, uint initialShares);
error StrategyZeroDeposit();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
event HardWorkGas(uint gasUsed, uint gasCost, bool compensated);
event DoHardWorkOnDepositChanged(bool oldValue, bool newValue);
event MintFees(
uint vaultManagerReceiverFee,
uint strategyLogicReceiverFee,
uint ecosystemRevenueReceiverFee,
uint multisigReceiverFee
);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATA TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @custom:storage-location erc7201:stability.VaultBase
struct VaultBaseStorage {
/// @dev Prevents manipulations with deposit and withdraw in short time.
/// For simplification we are setup new withdraw request on each deposit/transfer.
mapping(address msgSender => uint blockNumber) withdrawRequests;
/// @inheritdoc IVault
IStrategy strategy;
/// @inheritdoc IVault
uint maxSupply;
/// @inheritdoc IVault
uint tokenId;
/// @inheritdoc IVault
bool doHardWorkOnDeposit;
/// @dev Immutable vault type ID
string _type;
/// @dev Changed ERC20 name
string changedName;
/// @dev Changed ERC20 symbol
string changedSymbol;
/// @inheritdoc IStabilityVault
bool lastBlockDefenseDisabled;
}
/// @title Vault Initialization Data
/// @notice Data structure containing parameters for initializing a new vault.
/// @dev This struct is commonly used as a parameter for the `initialize` function in vault contracts.
/// @param platform Platform address providing access control, infrastructure addresses, fee settings, and upgrade capability.
/// @param strategy Immutable strategy proxy used by the vault.
/// @param name ERC20 name for the vault token.
/// @param symbol ERC20 symbol for the vault token.
/// @param tokenId NFT ID associated with the VaultManager.
/// @param vaultInitAddresses Array of addresses used during vault initialization.
/// @param vaultInitNums Array of uint values corresponding to initialization parameters.
struct VaultInitializationData {
address platform;
address strategy;
string name;
string symbol;
uint tokenId;
address[] vaultInitAddresses;
uint[] vaultInitNums;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEW FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @return uniqueInitAddresses Return required unique init addresses
/// @return uniqueInitNums Return required unique init nums
function getUniqueInitParamLength() external view returns (uint uniqueInitAddresses, uint uniqueInitNums);
/// @notice Vault type extra data
/// @return Vault type color, background color and other extra data
function extra() external view returns (bytes32);
/// @notice Immutable strategy proxy used by the vault
/// @return Linked strategy
function strategy() external view returns (IStrategy);
/// @notice Max supply of shares in the vault.
/// Since the starting share price is $1, this ceiling can be considered as an approximate TVL limit.
/// @return Max total supply of vault
function maxSupply() external view returns (uint);
/// @dev VaultManager token ID. This tokenId earn feeVaultManager provided by Platform.
function tokenId() external view returns (uint);
/// @dev Trigger doHardwork on invest action. Enabled by default.
function doHardWorkOnDeposit() external view returns (bool);
/// @notice All available data on the latest declared APR (annual percentage rate)
/// @return totalApr Total APR of investing money to vault. 18 decimals: 1e18 - +100% per year.
/// @return strategyApr Strategy investmnt APR declared on last HardWork.
/// @return assetsWithApr Assets with underlying APR
/// @return assetsAprs Underlying APR of asset
function getApr()
external
view
returns (uint totalApr, uint strategyApr, address[] memory assetsWithApr, uint[] memory assetsAprs);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Write version of previewDepositAssets
/// @param assets_ Assets suitable for vault strategy. Can be strategy assets, underlying asset or specific set of assets depending on strategy logic.
/// @param amountsMax Available amounts of assets_ that user wants to invest in vault
/// @return amountsConsumed Amounts of strategy assets that can be deposited by providing amountsMax
/// @return sharesOut Amount of vault shares that will be minted
/// @return valueOut Liquidity value or underlying token amount that will be received by the strategy
function previewDepositAssetsWrite(
address[] memory assets_,
uint[] memory amountsMax
) external returns (uint[] memory amountsConsumed, uint sharesOut, uint valueOut);
/// @dev Mint fee shares callback
/// @param revenueAssets Assets returned by _claimRevenue function that was earned during HardWork
/// @param revenueAmounts Assets amounts returned from _claimRevenue function that was earned during HardWork
/// Only strategy can call this
function hardWorkMintFeeCallback(address[] memory revenueAssets, uint[] memory revenueAmounts) external;
/// @dev Setting of vault capacity
/// @param maxShares If totalSupply() exceeds this value, deposits will not be possible
function setMaxSupply(uint maxShares) external;
/// @dev If activated will call doHardWork on strategy on some deposit actions
/// @param value HardWork on deposit is enabled
function setDoHardWorkOnDeposit(bool value) external;
/// @notice Initialization function for the vault.
/// @dev This function is usually called by the Factory during the creation of a new vault.
/// @param vaultInitializationData Data structure containing parameters for vault initialization.
function initialize(VaultInitializationData memory vaultInitializationData) external;
/// @dev Calling the strategy HardWork by operator with optional compensation for spent gas from the vault balance
function doHardWork() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "./ConstantsLib.sol";
library CommonLib {
function filterAddresses(
address[] memory addresses,
address addressToRemove
) external pure returns (address[] memory filteredAddresses) {
uint len = addresses.length;
uint newLen;
// nosemgrep
for (uint i; i < len; ++i) {
if (addresses[i] != addressToRemove) {
++newLen;
}
}
filteredAddresses = new address[](newLen);
uint k;
// nosemgrep
for (uint i; i < len; ++i) {
if (addresses[i] != addressToRemove) {
filteredAddresses[k] = addresses[i];
++k;
}
}
}
function formatUsdAmount(uint amount) external pure returns (string memory formattedPrice) {
uint dollars = amount / 10 ** 18;
string memory priceStr;
if (dollars >= 1000) {
uint kDollars = dollars / 1000;
uint kDollarsFraction = (dollars - kDollars * 1000) / 10;
string memory delimiter = ".";
if (kDollarsFraction < 10) {
delimiter = ".0";
}
priceStr = string.concat(Strings.toString(kDollars), delimiter, Strings.toString(kDollarsFraction), "k");
} else if (dollars >= 100) {
priceStr = Strings.toString(dollars);
} else {
uint dollarsFraction = (amount - dollars * 10 ** 18) / 10 ** 14;
if (dollarsFraction > 0) {
string memory dollarsFractionDelimiter = ".";
if (dollarsFraction < 10) {
dollarsFractionDelimiter = ".000";
} else if (dollarsFraction < 100) {
dollarsFractionDelimiter = ".00";
} else if (dollarsFraction < 1000) {
dollarsFractionDelimiter = ".0";
}
priceStr = string.concat(
Strings.toString(dollars), dollarsFractionDelimiter, Strings.toString(dollarsFraction)
);
} else {
priceStr = Strings.toString(dollars);
}
}
formattedPrice = string.concat("$", priceStr);
}
function formatApr(uint apr) external pure returns (string memory formattedApr) {
uint aprInt = apr * 100 / ConstantsLib.DENOMINATOR;
uint aprFraction = (apr - aprInt * ConstantsLib.DENOMINATOR / 100) / 10;
string memory delimiter = ".";
if (aprFraction < 10) {
delimiter = ".0";
}
formattedApr = string.concat(Strings.toString(aprInt), delimiter, Strings.toString(aprFraction), "%");
}
function formatAprInt(int apr) external pure returns (string memory formattedApr) {
int aprInt = apr * 100 / int(ConstantsLib.DENOMINATOR);
int aprFraction = (apr - aprInt * int(ConstantsLib.DENOMINATOR) / 100) / 10;
string memory delimiter = ".";
if (aprFraction < 10 || aprFraction > -10) {
delimiter = ".0";
}
formattedApr = string.concat(i2s2(aprInt), delimiter, i2s(aprFraction), "%");
}
function implodeSymbols(
address[] memory assets,
string memory delimiter
) external view returns (string memory outString) {
return implode(getSymbols(assets), delimiter);
}
function implode(string[] memory strings, string memory delimiter) public pure returns (string memory outString) {
uint len = strings.length;
if (len == 0) {
return "";
}
outString = strings[0];
// nosemgrep
for (uint i = 1; i < len; ++i) {
outString = string.concat(outString, delimiter, strings[i]);
}
return outString;
}
function getSymbols(address[] memory assets) public view returns (string[] memory symbols) {
uint len = assets.length;
symbols = new string[](len);
// nosemgrep
for (uint i; i < len; ++i) {
symbols[i] = IERC20Metadata(assets[i]).symbol();
}
}
function bytesToBytes32(bytes memory b) external pure returns (bytes32 out) {
// nosemgrep
for (uint i; i < b.length; ++i) {
out |= bytes32(b[i] & 0xFF) >> (i * 8);
}
// return out;
}
function bToHex(bytes memory buffer) external pure returns (string memory) {
// Fixed buffer size for hexadecimal convertion
bytes memory converted = new bytes(buffer.length * 2);
bytes memory _base = "0123456789abcdef";
uint baseLength = _base.length;
// nosemgrep
for (uint i; i < buffer.length; ++i) {
converted[i * 2] = _base[uint8(buffer[i]) / baseLength];
converted[i * 2 + 1] = _base[uint8(buffer[i]) % baseLength];
}
return string(abi.encodePacked(converted));
}
function shortId(string memory id) external pure returns (string memory) {
uint words = 1;
bytes memory idBytes = bytes(id);
uint idBytesLength = idBytes.length;
// nosemgrep
for (uint i; i < idBytesLength; ++i) {
if (keccak256(bytes(abi.encodePacked(idBytes[i]))) == keccak256(bytes(" "))) {
++words;
}
}
bytes memory _shortId = new bytes(words);
uint k = 1;
_shortId[0] = idBytes[0];
// nosemgrep
for (uint i = 1; i < idBytesLength; ++i) {
if (keccak256(bytes(abi.encodePacked(idBytes[i]))) == keccak256(bytes(" "))) {
if (keccak256(bytes(abi.encodePacked(idBytes[i + 1]))) == keccak256(bytes("0"))) {
_shortId[k] = idBytes[i + 3];
} else {
_shortId[k] = idBytes[i + 1];
}
++k;
}
}
return string(abi.encodePacked(_shortId));
}
function eq(string memory a, string memory b) external pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
function u2s(uint num) external pure returns (string memory) {
return Strings.toString(num);
}
function i2s(int num) public pure returns (string memory) {
return Strings.toString(num > 0 ? uint(num) : uint(-num));
}
function i2s2(int num) public pure returns (string memory) {
if (num >= 0) {
return Strings.toString(uint(num));
}
return string.concat("-", Strings.toString(uint(-num)));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
library VaultTypeLib {
string internal constant COMPOUNDING = "Compounding";
string internal constant REWARDING = "Rewarding";
string internal constant REWARDING_MANAGED = "Rewarding Managed";
string internal constant MULTIVAULT = "MultiVault";
string internal constant METAVAULT = "MetaVault";
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/// @dev Combining oracle and DeX spot prices
/// @author Alien Deployer (https://github.com/a17)
/// @author Jude (https://github.com/iammrjude)
/// @author JodsMigel (https://github.com/JodsMigel)
interface IPriceReader {
//region ----- Events -----
event AdapterAdded(address adapter);
event AdapterRemoved(address adapter);
event VaultWithSafeSharePriceAdded(address vault);
event VaultWithSafeSharePriceRemoved(address vault);
//endregion -- Events -----
//region --------------------------- Errors
error NotWhitelistedTransientCache();
//endregion --------------------------- Errors
/// @notice Price of asset
/// @dev Price of 1.0 amount of asset in USD
/// @param asset Address of asset
/// @return price USD price with 18 decimals
/// @return trusted Price from oracle
function getPrice(address asset) external view returns (uint price, bool trusted);
/// @notice Price of vault share
/// @dev Price of 1.0 amount of vault token
/// @param vault Address of vault
/// @return price USD price with 18 decimals
/// @return safe Safe to use this price on-chain
function getVaultPrice(address vault) external view returns (uint price, bool safe);
/// @notice Get USD price of specified assets and amounts
/// @param assets_ Addresses of assets
/// @param amounts_ Amount of asset. Index of asset same as in previous parameter.
/// @return total Total USD value with 18 decimals
/// @return assetAmountPrice USD price of asset amount. Index of assetAmountPrice same as in assets_ parameters.
/// @return assetPrice USD price of asset. Index of assetAmountPrice same as in assets_ parameters.
/// @return trusted True if only oracle prices was used for calculation.
function getAssetsPrice(
address[] memory assets_,
uint[] memory amounts_
) external view returns (uint total, uint[] memory assetAmountPrice, uint[] memory assetPrice, bool trusted);
/// @notice Get vaults that have organic safe share price that can be used on-chain
function vaultsWithSafeSharePrice() external view returns (address[] memory vaults);
/// @notice Add oracle adapter to PriceReader
/// Only operator (multisig is operator too) can add adapter
/// @param adapter_ Address of price oracle proxy
function addAdapter(address adapter_) external;
/// @notice Remove oracle adapter from PriceReader
/// Only operator (multisig is operator too) can add adapter
/// @param adapter_ Address of price oracle proxy
function removeAdapter(address adapter_) external;
/// @notice Add vaults that have organic safe share price that can be used on-chain
/// Only operator (multisig is operator too) can add adapter
/// @param vaults Addresses of vaults
function addSafeSharePrices(address[] memory vaults) external;
/// @notice Remove vaults that have organic safe share price that can be used on-chain
/// Only operator (multisig is operator too) can add adapter
/// @param vaults Addresses of vaults
function removeSafeSharePrices(address[] memory vaults) external;
/// @notice Check if the user is whitelisted for using transient cache
function whitelistTransientCache(address user_) external view returns (bool);
/// @notice Add user to whitelist of users allowed to use the transient cache
function changeWhitelistTransientCache(address user, bool add) external;
/// @notice Save asset price to transient cache
/// @param asset Pass 0 to clear the cache
function preCalculatePriceTx(address asset) external;
/// @notice Save vault price to transient cache
/// Second call with the same vault will be ignored and won't not change the price
/// @param vault Pass 0 to clear the cache
function preCalculateVaultPriceTx(address vault) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @notice Interface of the main contract and entry point to the platform.
/// @author Alien Deployer (https://github.com/a17)
/// @author Jude (https://github.com/iammrjude)
/// @author JodsMigel (https://github.com/JodsMigel)
interface IPlatform {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error AlreadyAnnounced();
error SameVersion();
error NoNewVersion();
error UpgradeTimerIsNotOver(uint TimerTimestamp);
error IncorrectFee(uint minFee, uint maxFee);
error NotEnoughAllowedBBToken();
error TokenAlreadyExistsInSet(address token);
error AggregatorNotExists(address dexAggRouter);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
event PlatformVersion(string version);
event UpgradeAnnounce(
string oldVersion, string newVersion, address[] proxies, address[] newImplementations, uint timelock
);
event CancelUpgrade(string oldVersion, string newVersion);
event ProxyUpgraded(
address indexed proxy, address implementation, string oldContractVersion, string newContractVersion
);
event Addresses(
address multisig_,
address factory_,
address priceReader_,
address swapper_,
address buildingPermitToken_,
address vaultManager_,
address strategyLogic_,
address aprOracle_,
address hardWorker,
address rebalancer,
address zap,
address bridge
);
event OperatorAdded(address operator);
event OperatorRemoved(address operator);
event FeesChanged(uint fee, uint feeShareVaultManager, uint feeShareStrategyLogic, uint feeShareEcosystem);
event MinInitialBoostChanged(uint minInitialBoostPerDay, uint minInitialBoostDuration);
event NewAmmAdapter(string id, address proxy);
event EcosystemRevenueReceiver(address receiver);
event SetAllowedBBTokenVaults(address bbToken, uint vaultsToBuild, bool firstSet);
event RemoveAllowedBBToken(address bbToken);
event AddAllowedBoostRewardToken(address token);
event RemoveAllowedBoostRewardToken(address token);
event AddDefaultBoostRewardToken(address token);
event RemoveDefaultBoostRewardToken(address token);
event AddBoostTokens(address[] allowedBoostRewardToken, address[] defaultBoostRewardToken);
event AllowedBBTokenVaultUsed(address bbToken, uint vaultToUse);
event AddDexAggregator(address router);
event RemoveDexAggregator(address router);
event MinTvlForFreeHardWorkChanged(uint oldValue, uint newValue);
event CustomVaultFee(address vault, uint platformFee);
event Rebalancer(address rebalancer_);
event Bridge(address bridge_);
event RevenueRouter(address revenueRouter_);
event MetaVaultFactory(address metaVaultFactory);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATA TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
struct PlatformUpgrade {
string newVersion;
address[] proxies;
address[] newImplementations;
}
struct PlatformSettings {
string networkName;
bytes32 networkExtra;
uint fee;
uint feeShareVaultManager;
uint feeShareStrategyLogic;
uint feeShareEcosystem;
uint minInitialBoostPerDay;
uint minInitialBoostDuration;
}
struct AmmAdapter {
string id;
address proxy;
}
struct SetupAddresses {
address factory;
address priceReader;
address swapper;
address buildingPermitToken;
address buildingPayPerVaultToken;
address vaultManager;
address strategyLogic;
address aprOracle;
address targetExchangeAsset;
address hardWorker;
address zap;
address revenueRouter;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEW FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Platform version in CalVer scheme: YY.MM.MINOR-tag. Updates on core contract upgrades.
function platformVersion() external view returns (string memory);
/// @notice Time delay for proxy upgrades of core contracts and changing important platform settings by multisig
//slither-disable-next-line naming-convention
function TIME_LOCK() external view returns (uint);
/// @notice DAO governance
function governance() external view returns (address);
/// @notice Core team multi signature wallet. Development and operations fund
function multisig() external view returns (address);
/// @notice This NFT allow user to build limited number of vaults per week
function buildingPermitToken() external view returns (address);
/// @notice This ERC20 token is used as payment token for vault building
function buildingPayPerVaultToken() external view returns (address);
/// @notice Receiver of ecosystem revenue
function ecosystemRevenueReceiver() external view returns (address);
/// @dev The best asset in a network for swaps between strategy assets and farms rewards assets
/// The target exchange asset is used for finding the best strategy's exchange asset.
/// Rhe fewer routes needed to swap to the target exchange asset, the better.
function targetExchangeAsset() external view returns (address);
/// @notice Platform factory assembling vaults. Stores settings, strategy logic, farms.
/// Provides the opportunity to upgrade vaults and strategies.
/// @return Address of Factory proxy
function factory() external view returns (address);
/// @notice The holders of these NFT receive a share of the vault revenue
/// @return Address of VaultManager proxy
function vaultManager() external view returns (address);
/// @notice The holders of these tokens receive a share of the revenue received in all vaults using this strategy logic.
function strategyLogic() external view returns (address);
/// @notice Combining oracle and DeX spot prices
/// @return Address of PriceReader proxy
function priceReader() external view returns (address);
/// @notice Providing underlying assets APRs on-chain
/// @return Address of AprOracle proxy
function aprOracle() external view returns (address);
/// @notice On-chain price quoter and swapper
/// @return Address of Swapper proxy
function swapper() external view returns (address);
/// @notice HardWork resolver and caller
/// @return Address of HardWorker proxy
function hardWorker() external view returns (address);
/// @notice Rebalance resolver
/// @return Address of Rebalancer proxy
function rebalancer() external view returns (address);
/// @notice ZAP feature
/// @return Address of Zap proxy
function zap() external view returns (address);
/// @notice Platform revenue distributor
/// @return Address of the revenue distributor proxy
function revenueRouter() external view returns (address);
/// @notice Factory of MetaVaults
/// @return Address of the MetaVault factory
function metaVaultFactory() external view returns (address);
/// @notice Name of current EVM network
function networkName() external view returns (string memory);
/// @notice Minimal initial boost rewards per day USD amount which needs to create rewarding vault
function minInitialBoostPerDay() external view returns (uint);
/// @notice Minimal boost rewards vesting duration for initial boost
function minInitialBoostDuration() external view returns (uint);
/// @notice This function provides the timestamp of the platform upgrade timelock.
/// @dev This function is an external view function, meaning it doesn't modify the state.
/// @return uint representing the timestamp of the platform upgrade timelock.
function platformUpgradeTimelock() external view returns (uint);
/// @dev Extra network data
/// @return 0-2 bytes - color
/// 3-5 bytes - background color
/// 6-31 bytes - free
function networkExtra() external view returns (bytes32);
/// @notice Pending platform upgrade data
function pendingPlatformUpgrade() external view returns (PlatformUpgrade memory);
/// @notice Get platform revenue fee settings
/// @return fee Revenue fee % (between MIN_FEE - MAX_FEE) with DENOMINATOR precision.
/// @return feeShareVaultManager Revenue fee share % of VaultManager tokenId owner
/// @return feeShareStrategyLogic Revenue fee share % of StrategyLogic tokenId owner
/// @return feeShareEcosystem Revenue fee share % of ecosystemFeeReceiver
function getFees()
external
view
returns (uint fee, uint feeShareVaultManager, uint feeShareStrategyLogic, uint feeShareEcosystem);
/// @notice Get custom vault platform fee
/// @return fee revenue fee % with DENOMINATOR precision
function getCustomVaultFee(address vault) external view returns (uint fee);
/// @notice Platform settings
function getPlatformSettings() external view returns (PlatformSettings memory);
/// @notice AMM adapters of the platform
function getAmmAdapters() external view returns (string[] memory id, address[] memory proxy);
/// @notice Get AMM adapter data by hash
/// @param ammAdapterIdHash Keccak256 hash of adapter ID string
/// @return ID string and proxy address of AMM adapter
function ammAdapter(bytes32 ammAdapterIdHash) external view returns (AmmAdapter memory);
/// @notice Allowed buy-back tokens for rewarding vaults
function allowedBBTokens() external view returns (address[] memory);
/// @notice Vaults building limit for buy-back token.
/// This limit decrements when a vault for BB-token is built.
/// @param token Allowed buy-back token
/// @return vaultsLimit Number of vaults that can be built for BB-token
function allowedBBTokenVaults(address token) external view returns (uint vaultsLimit);
/// @notice Vaults building limits for allowed buy-back tokens.
/// @return bbToken Allowed buy-back tokens
/// @return vaultsLimit Number of vaults that can be built for BB-tokens
function allowedBBTokenVaults() external view returns (address[] memory bbToken, uint[] memory vaultsLimit);
/// @notice Non-zero vaults building limits for allowed buy-back tokens.
/// @return bbToken Allowed buy-back tokens
/// @return vaultsLimit Number of vaults that can be built for BB-tokens
function allowedBBTokenVaultsFiltered()
external
view
returns (address[] memory bbToken, uint[] memory vaultsLimit);
/// @notice Check address for existance in operators list
/// @param operator Address
/// @return True if this address is Stability Operator
function isOperator(address operator) external view returns (bool);
/// @notice Tokens that can be used for boost rewards of rewarding vaults
/// @return Addresses of tokens
function allowedBoostRewardTokens() external view returns (address[] memory);
/// @notice Allowed boost reward tokens that used for unmanaged rewarding vaults creation
/// @return Addresses of tokens
function defaultBoostRewardTokens() external view returns (address[] memory);
/// @notice Allowed boost reward tokens that used for unmanaged rewarding vaults creation
/// @param addressToRemove This address will be removed from default boost reward tokens
/// @return Addresses of tokens
function defaultBoostRewardTokensFiltered(address addressToRemove) external view returns (address[] memory);
/// @notice Allowed DeX aggregators
/// @return Addresses of DeX aggregator rounters
function dexAggregators() external view returns (address[] memory);
/// @notice DeX aggregator router address is allowed to be used in the platform
/// @param dexAggRouter Address of DeX aggreagator router
/// @return Can be used
function isAllowedDexAggregatorRouter(address dexAggRouter) external view returns (bool);
/// @notice Show minimum TVL for compensate if vault has not enough ETH
/// @return Minimum TVL for compensate.
function minTvlForFreeHardWork() external view returns (uint);
/// @notice Front-end platform viewer
/// @return platformAddresses Platform core addresses
/// platformAddresses[0] factory
/// platformAddresses[1] vaultManager
/// platformAddresses[2] strategyLogic
/// platformAddresses[3] buildingPermitToken
/// platformAddresses[4] buildingPayPerVaultToken
/// platformAddresses[5] governance
/// platformAddresses[6] multisig
/// platformAddresses[7] zap
/// platformAddresses[8] bridge
/// @return bcAssets Blue chip token addresses
/// @return dexAggregators_ DeX aggregators allowed to be used entire the platform
/// @return vaultType Vault type ID strings
/// @return vaultExtra Vault color, background color and other extra data. Index of vault same as in previous array.
/// @return vaultBulldingPrice Price of creating new vault in buildingPayPerVaultToken. Index of vault same as in previous array.
/// @return strategyId Strategy logic ID strings
/// @return isFarmingStrategy True if strategy is farming strategy. Index of strategy same as in previous array.
/// @return strategyTokenURI StrategyLogic NFT tokenId metadata and on-chain image. Index of strategy same as in previous array.
/// @return strategyExtra Strategy color, background color and other extra data. Index of strategy same as in previous array.
function getData()
external
view
returns (
address[] memory platformAddresses,
address[] memory bcAssets,
address[] memory dexAggregators_,
string[] memory vaultType,
bytes32[] memory vaultExtra,
uint[] memory vaultBulldingPrice,
string[] memory strategyId,
bool[] memory isFarmingStrategy,
string[] memory strategyTokenURI,
bytes32[] memory strategyExtra
);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Add platform operator.
/// Only governance and multisig can add operator.
/// @param operator Address of new operator
function addOperator(address operator) external;
/// @notice Remove platform operator.
/// Only governance and multisig can remove operator.
/// @param operator Address of operator to remove
function removeOperator(address operator) external;
/// @notice Announce upgrade of platform proxies implementations
/// Only governance and multisig can announce platform upgrades.
/// @param newVersion New platform version. Version must be changed when upgrading.
/// @param proxies Addresses of core contract proxies
/// @param newImplementations New implementation for proxy. Index of proxy same as in previous array.
function announcePlatformUpgrade(
string memory newVersion,
address[] memory proxies,
address[] memory newImplementations
) external;
/// @notice Upgrade platform
/// Only operator (multisig is operator too) can ececute pending platform upgrade
function upgrade() external;
/// @notice Cancel pending platform upgrade
/// Only operator (multisig is operator too) can ececute pending platform upgrade
function cancelUpgrade() external;
/// @notice Register AMM adapter in platform
/// @param id AMM adapter ID string from AmmAdapterIdLib
/// @param proxy Address of AMM adapter proxy
function addAmmAdapter(string memory id, address proxy) external;
// todo Only governance and multisig can set allowed bb-token vaults building limit
/// @notice Set new vaults building limit for buy-back token
/// @param bbToken Address of allowed buy-back token
/// @param vaultsToBuild Number of vaults that can be built for BB-token
function setAllowedBBTokenVaults(address bbToken, uint vaultsToBuild) external;
// todo Only governance and multisig can add allowed boost reward token
/// @notice Add new allowed boost reward token
/// @param token Address of token
function addAllowedBoostRewardToken(address token) external;
// todo Only governance and multisig can remove allowed boost reward token
/// @notice Remove allowed boost reward token
/// @param token Address of allowed boost reward token
function removeAllowedBoostRewardToken(address token) external;
// todo Only governance and multisig can add default boost reward token
/// @notice Add default boost reward token
/// @param token Address of default boost reward token
function addDefaultBoostRewardToken(address token) external;
// todo Only governance and multisig can remove default boost reward token
/// @notice Remove default boost reward token
/// @param token Address of allowed boost reward token
function removeDefaultBoostRewardToken(address token) external;
// todo Only governance and multisig can add allowed boost reward token
// todo Only governance and multisig can add default boost reward token
/// @notice Add new allowed boost reward token
/// @notice Add default boost reward token
/// @param allowedBoostRewardToken Address of allowed boost reward token
/// @param defaultBoostRewardToken Address of default boost reward token
function addBoostTokens(
address[] memory allowedBoostRewardToken,
address[] memory defaultBoostRewardToken
) external;
/// @notice Decrease allowed BB-token vault building limit when vault is built
/// Only Factory can do it.
/// @param bbToken Address of allowed buy-back token
function useAllowedBBTokenVault(address bbToken) external;
/// @notice Allow DeX aggregator routers to be used in the platform
/// @param dexAggRouter Addresses of DeX aggreagator routers
function addDexAggregators(address[] memory dexAggRouter) external;
/// @notice Remove allowed DeX aggregator router from the platform
/// @param dexAggRouter Address of DeX aggreagator router
function removeDexAggregator(address dexAggRouter) external;
/// @notice Change initial boost rewards settings
/// @param minInitialBoostPerDay_ Minimal initial boost rewards per day USD amount which needs to create rewarding vault
/// @param minInitialBoostDuration_ Minimal boost rewards vesting duration for initial boost
function setInitialBoost(uint minInitialBoostPerDay_, uint minInitialBoostDuration_) external;
/// @notice Update new minimum TVL for compensate.
/// @param value New minimum TVL for compensate.
function setMinTvlForFreeHardWork(uint value) external;
/// @notice Set custom platform fee for vault
/// @param vault Vault address
/// @param platformFee Custom platform fee
function setCustomVaultFee(address vault, uint platformFee) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface IRecoveryToken {
error TransfersPausedForAccount(address account);
event AccountPaused(address indexed account, bool paused);
/// @dev Init
function initialize(address platform_, address target_) external;
/// @notice Address of target of recovery
function target() external view returns (address);
/// @notice Mint tokens by target
function mint(address account, uint amount) external;
/// @notice Pause transfers from address
function setAddressPaused(address account, bool paused) external;
/// @notice Bulk transfer by governance
function bulkTransferFrom(address from, address[] calldata to, uint[] calldata amounts) external;
/// @notice The account is paused for transfers
function paused(address account) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @dev Core interface of strategy logic
interface IStrategy is IERC165 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
event HardWork(
uint apr, uint compoundApr, uint earned, uint tvl, uint duration, uint sharePrice, uint[] assetPrices
);
event ExtractFees(
uint vaultManagerReceiverFee,
uint strategyLogicReceiverFee,
uint ecosystemRevenueReceiverFee,
uint multisigReceiverFee
);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error NotReadyForHardWork();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATA TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @custom:storage-location erc7201:stability.StrategyBase
struct StrategyBaseStorage {
/// @inheritdoc IStrategy
address vault;
/// @inheritdoc IStrategy
uint total;
/// @inheritdoc IStrategy
uint lastHardWork;
/// @inheritdoc IStrategy
uint lastApr;
/// @inheritdoc IStrategy
uint lastAprCompound;
/// @inheritdoc IStrategy
address[] _assets;
/// @inheritdoc IStrategy
address _underlying;
string _id;
uint _exchangeAssetIndex;
uint customPriceImpactTolerance;
/// @inheritdoc IStrategy
uint fuseOn;
}
enum FuseMode {
FUSE_OFF_0,
/// @notice Fuse mode is on (emergency stop was called).
/// All assets were transferred from the underlying pool to the strategy balance, no deposits are allowed.
FUSE_ON_1
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEW FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Strategy logic string ID
function strategyLogicId() external view returns (string memory);
/// @dev Extra data
/// @return 0-2 bytes - strategy color
/// 3-5 bytes - strategy background color
/// 6-31 bytes - free
function extra() external view returns (bytes32);
/// @dev Types of vault that supported by strategy implementation
/// @return types Vault type ID strings
function supportedVaultTypes() external view returns (string[] memory types);
/// @dev Linked vault address
function vault() external view returns (address);
/// @dev Final assets that strategy invests
function assets() external view returns (address[] memory);
/// @notice Final assets and amounts that strategy manages
function assetsAmounts() external view returns (address[] memory assets_, uint[] memory amounts_);
/// @notice Priced invested assets proportions
/// @return proportions Proportions of assets with 18 decimals. Min is 0, max is 1e18.
function getAssetsProportions() external view returns (uint[] memory proportions);
/// @notice Underlying token address
/// @dev Can be used for liquidity farming strategies where AMM has fungible liquidity token (Solidly forks, etc),
/// for concentrated liquidity tokenized vaults (Gamma, G-UNI etc) and for other needs.
/// @return Address of underlying token or zero address if no underlying in strategy
function underlying() external view returns (address);
/// @dev Balance of liquidity token or liquidity value
function total() external view returns (uint);
/// @dev Last HardWork time
/// @return Timestamp
function lastHardWork() external view returns (uint);
/// @dev Last APR of earned USD amount registered by HardWork
/// ONLY FOR OFF-CHAIN USE.
/// Not trusted asset price can be manipulated.
/// @return APR with 5 decimals. 100_000 - 100% APR, 9_955 - 9.96% APR.
function lastApr() external view returns (uint);
/// @dev Last APR of compounded assets registered by HardWork.
/// Can be used on-chain.
/// @return APR with 5 decimals. 100_000 - 100% APR, 9_955 - 9.96% APR.
function lastAprCompound() external view returns (uint);
/// @notice Calculation of consumed amounts and liquidity/underlying value for provided strategy assets and amounts.
/// @param assets_ Strategy assets or part of them, if necessary
/// @param amountsMax Amounts of specified assets available for investing
/// @return amountsConsumed Cosumed amounts of assets when investing
/// @return value Liquidity value or underlying token amount minted when investing
function previewDepositAssets(
address[] memory assets_,
uint[] memory amountsMax
) external view returns (uint[] memory amountsConsumed, uint value);
/// @notice Write version of previewDepositAssets
/// @param assets_ Strategy assets or part of them, if necessary
/// @param amountsMax Amounts of specified assets available for investing
/// @return amountsConsumed Cosumed amounts of assets when investing
/// @return value Liquidity value or underlying token amount minted when investing
function previewDepositAssetsWrite(
address[] memory assets_,
uint[] memory amountsMax
) external returns (uint[] memory amountsConsumed, uint value);
/// @notice All strategy revenue (pool fees, farm rewards etc) that not claimed by strategy yet
/// @return assets_ Revenue assets
/// @return amounts Amounts. Index of asset same as in previous array.
function getRevenue() external view returns (address[] memory assets_, uint[] memory amounts);
/// @notice Optional specific name of investing strategy, underyling type, setup variation etc
/// @return name Empty string or specific name
/// @return showInVaultSymbol Show specific in linked vault symbol
function getSpecificName() external view returns (string memory name, bool showInVaultSymbol);
/// @notice Variants pf strategy initializations with description of money making mechanic.
/// As example, if strategy need farm, then number of variations is number of available farms.
/// If CAMM strategy have set of available widths (tick ranges), then number of variations is number of available farms.
/// If both example conditions are met then total number or variations = total farms * total widths.
/// @param platform_ Need this param because method called when strategy implementation is not initialized
/// @return variants Descriptions of the strategy for making money
/// @return addresses Init strategy addresses. Indexes for each variants depends of copmpared arrays lengths.
/// @return nums Init strategy numbers. Indexes for each variants depends of copmpared arrays lengths.
/// @return ticks Init strategy ticks. Indexes for each variants depends of copmpared arrays lengths.
function initVariants(address platform_)
external
view
returns (string[] memory variants, address[] memory addresses, uint[] memory nums, int24[] memory ticks);
/// @notice How does the strategy make money?
/// @return Description in free form
function description() external view returns (string memory);
/// @notice Is HardWork on vault deposits can be enabled
function isHardWorkOnDepositAllowed() external view returns (bool);
/// @notice Is HardWork can be executed
function isReadyForHardWork() external view returns (bool);
/// @notice Strategy not need to process revenue on HardWorks
function autoCompoundingByUnderlyingProtocol() external view returns (bool);
/// @notice Custom price impact tolerance instead default need for specific cases where liquidity in pools is low
function customPriceImpactTolerance() external view returns (uint);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A single universal initializer for all strategy implementations.
/// @param addresses All addresses that strategy requires for initialization. Min array length is 2.
/// addresses[0]: platform (required)
/// addresses[1]: vault (required)
/// addresses[2]: initStrategyAddresses[0] (optional)
/// addresses[3]: initStrategyAddresses[1] (optional)
/// addresses[n]: initStrategyAddresses[n - 2] (optional)
/// @param nums All uint values that strategy requires for initialization. Min array length is 0.
/// @param ticks All int24 values that strategy requires for initialization. Min array length is 0.
function initialize(address[] memory addresses, uint[] memory nums, int24[] memory ticks) external;
/// @notice Invest strategy assets. Amounts of assets must be already on strategy contract balance.
/// Only vault can call this.
/// @param amounts Amounts of strategy assets
/// @return value Liquidity value or underlying token amount
function depositAssets(uint[] memory amounts) external returns (uint value);
/// @notice Invest underlying asset. Asset must be already on strategy contract balance.
/// Only vault can call this.
/// @param amount Amount of underlying asset to invest
/// @return amountsConsumed Consumed amounts of invested assets
function depositUnderlying(uint amount) external returns (uint[] memory amountsConsumed);
/// @dev For specified amount of shares and assets_, withdraw strategy assets from farm/pool/staking and send to receiver if possible
/// Only vault can call this.
/// @param assets_ Here we give the user a choice of assets to withdraw if strategy support it
/// @param value Part of strategy total value to withdraw
/// @param receiver User address
/// @return amountsOut Amounts of assets sent to user
function withdrawAssets(
address[] memory assets_,
uint value,
address receiver
) external returns (uint[] memory amountsOut);
/// @notice Wothdraw underlying invested and send to receiver
/// Only vault can call this.
/// @param amount Amount of underlying asset to withdraw
/// @param receiver User of vault which withdraw underlying from the vault
function withdrawUnderlying(uint amount, address receiver) external;
/// @dev For specified amount of shares, transfer strategy assets from contract balance and send to receiver if possible
/// This method is called by vault w/o underlying on triggered fuse mode.
/// Only vault can call this.
/// @param amount Amount of liquidity value that user withdraw
/// @param totalAmount Total amount of strategy liquidity
/// @param receiver User of vault which withdraw assets
/// @return amountsOut Amounts of strategy assets sent to user
function transferAssets(
uint amount,
uint totalAmount,
address receiver
) external returns (uint[] memory amountsOut);
/// @notice Execute HardWork
/// During HardWork strategy claiming revenue and processing it.
/// Only vault can call this.
function doHardWork() external;
/// @notice Emergency stop investing by strategy, withdraw liquidity without rewards.
/// This action triggers FUSE mode.
/// Only governance or multisig can call this.
function emergencyStopInvesting() external;
/// @notice Custom price impact tolerance instead default need for specific cases where low liquidity in pools
/// @param priceImpactTolerance Tolerance percent with 100_000 DENOMINATOR. 4_000 == 4%
function setCustomPriceImpactTolerance(uint priceImpactTolerance) external;
/// @notice Total amount of assets available in the lending protocol for withdraw
/// It's normal situation when user is not able to withdraw all
/// because there are not enough reserves available in the protocol right now
/// @dev This function is replaced by more flexible maxWithdrawAssets(uint mode) function.
function maxWithdrawAssets() external view returns (uint[] memory amounts);
/// @notice Total amount of assets available in the lending protocol for withdraw
/// It's normal situation when user is not able to withdraw all
/// because there are not enough reserves available in the protocol right now
/// @param mode 0 - Return amount that can be withdrawn in assets
/// 1 - Return amount that can be withdrawn in underlying
/// @return amounts Empty array (zero length) is returned if all available amount can be withdrawn
function maxWithdrawAssets(uint mode) external view returns (uint[] memory amounts);
/// @notice Underlying pool TVL in the terms of USD
function poolTvl() external view returns (uint tvlUsd);
/// @notice return FUSE_ON_1 if emergency was called and all actives were transferred to the vault
function fuseMode() external view returns (uint);
/// @notice Maximum amounts of assets that can be deposited into the strategy
/// @return amounts Empty array (zero length) is returned if there are no limits on deposits
function maxDepositAssets() external view returns (uint[] memory amounts);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
library ConstantsLib {
uint internal constant DENOMINATOR = 100_000;
address internal constant DEAD_ADDRESS = 0xdEad000000000000000000000000000000000000;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@solady/=lib/solady/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
"solady/=lib/solady/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {
"src/core/libs/MetaVaultLib.sol": {
"CommonLib": "0x72a237cb9caaf0716bb4f717e32e79ce6460e5d3"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"uint256","name":"mintToUser","type":"uint256"},{"internalType":"uint256","name":"minToMint","type":"uint256"}],"name":"ExceedSlippage","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"IncorrectArrayLength","type":"error"},{"inputs":[{"internalType":"address[]","name":"assets_","type":"address[]"},{"internalType":"address[]","name":"expectedAssets_","type":"address[]"}],"name":"IncorrectAssetsList","type":"error"},{"inputs":[],"name":"IncorrectBalance","type":"error"},{"inputs":[],"name":"IncorrectProportions","type":"error"},{"inputs":[],"name":"IncorrectRebalanceArgs","type":"error"},{"inputs":[],"name":"IncorrectVault","type":"error"},{"inputs":[],"name":"IncorrectZeroArgument","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"maxAmount","type":"uint256"}],"name":"MaxAmountForWithdrawPerTxReached","type":"error"},{"inputs":[{"internalType":"address","name":"cVault_","type":"address"}],"name":"RecoveryTokenNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"maxAmount","type":"uint256"}],"name":"TooHighAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountUsd","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"UsdAmountLessThreshold","type":"error"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"VaultNotFound","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountToWithdraw","type":"uint256"}],"name":"ZeroSharesToBurn","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vault","type":"address"}],"name":"AddVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vault","type":"address"}],"name":"RemoveVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"proportions","type":"uint256[]"}],"name":"TargetProportions","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address[]","name":"assets","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"sharesAmount","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"amountsOut","type":"uint256[]"}],"name":"WithdrawAssets","type":"event"},{"inputs":[],"name":"USD_THRESHOLD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USD_THRESHOLD_REMOVE_VAULT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6155a4610034600b8282823980515f1a607314602857634e487b7160e01b5f525f60045260245ffd5b305f52607381538281f3fe730000000000000000000000000000000000000000301460806040526004361061013d575f3560e01c8063a837007d116100bf578063c1a0a95211610084578063c1a0a9521461034a578063c88e167a14610357578063d11856461461036a578063db60f77a1461037d578063e8afe558146103b0578063ed0fde82146103cf575f5ffd5b8063a837007d146102d0578063af2b5a08146102e3578063b95bed4414610305578063bd85888514610324578063c0a1557614610337575f5ffd5b80637fbf0b14116101055780637fbf0b14146101f95780639448313d146102265780639aa8a9a31461024e5780639ebce86414610281578063a4b06c00146102af575f5ffd5b80630f4493bc146101415780631c6710ec146101625780632d609c861461019b578063541020e0146101bb5780636469a73c146101da575b5f5ffd5b81801561014c575f5ffd5b5061016061015b36600461483e565b6103d7565b005b81801561016d575f5ffd5b5061018161017c3660046148bd565b6107a4565b604080519283526020830191909152015b60405180910390f35b6101ae6101a9366004614933565b610cef565b604051610192919061499b565b8180156101c6575f5ffd5b506101606101d53660046149ad565b610f17565b8180156101e5575f5ffd5b506101606101f43660046149f8565b6111c9565b818015610204575f5ffd5b50610218610213366004614a9b565b61145a565b604051610192929190614b5b565b610239610234366004614933565b611789565b60408051928352901515602083015201610192565b61026161025c366004614933565b611887565b604080519485526020850193909352918301526060820152608001610192565b81801561028c575f5ffd5b506102a061029b366004614c55565b6118f9565b60405161019293929190614d23565b6102c26102bd366004614d5b565b612501565b604051908152602001610192565b6102c26102de366004614d95565b612632565b6102f66102f1366004614de5565b612707565b60405161019293929190614e75565b818015610310575f5ffd5b5061016061031f366004614e99565b612849565b6102c2610332366004614933565b612bcd565b6101ae610345366004614ece565b612c07565b6102c26509184e72a00081565b6102c2610365366004614ee5565b612c12565b610239610378366004614933565b612c97565b61039061038b366004614ece565b61306f565b604080516001600160a01b03938416815292909116602083015201610192565b8180156103bb575f5ffd5b506101606103ca366004614f33565b61334b565b6102c2613409565b60058301545f6103e96003860161341e565b90505f5b828110156105535784818151811061040757610407614f79565b60200260200101515f1461054b5785600501818154811061042a5761042a614f79565b905f5260205f20015f9054906101000a90046001600160a01b03166001600160a01b0316630b4881ef8387848151811061046657610466614f79565b602002602001015160016001600160401b038111156104875761048761476e565b6040519080825280602002602001820160405280156104b0578160200160208202803683370190505b506040518463ffffffff1660e01b81526004016104cf93929190614fc6565b5f604051808303815f875af11580156104ea573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610511919081019061504b565b5083818151811061052457610524614f79565b60200260200101515f1461054b57604051637b27a36360e01b815260040160405180910390fd5b6001016103ed565b505f815f8151811061056757610567614f79565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156105b5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105d9919061507c565b90505f5b8381101561079b575f8760050182815481106105fb576105fb614f79565b5f9182526020822001546040805160018082528183019092526001600160a01b039092169350816020016020820280368337019050509050670de0b6b3a76400008488858151811061064f5761064f614f79565b602002602001015161066191906150a7565b61066b91906150d2565b815f8151811061067d5761067d614f79565b602002602001018181525050805f8151811061069b5761069b614f79565b60200260200101515f14610791576106f982825f815181106106bf576106bf614f79565b6020026020010151875f815181106106d9576106d9614f79565b60200260200101516001600160a01b03166134319092919063ffffffff16565b60405163b256cd8b60e01b81526001600160a01b0383169063b256cd8b9061072b90889085905f9030906004016150e5565b5f604051808303815f87803b158015610742575f5ffd5b505af1158015610754573d5f5f3e3d5ffd5b5050505087838151811061076a5761076a614f79565b60200260200101515f1461079157604051637b27a36360e01b815260040160405180910390fd5b50506001016105dd565b50505050505050565b5f5f6107ae6146c2565b336001600160a01b038516146107ca576107ca8a85338a6134ea565b865f036107ea576040516371c42ac360e01b815260040160405180910390fd5b5f6107f58b8a61358e565b90506108018b8b612bcd565b602083018190525f90610817908d908890613778565b905085818a818111156108495760405163391434e360e21b81526004016108409392919061512b565b60405180910390fd5b50505050610860888c600a015484602001516137bb565b9250878361088457604051633d6b36df60e11b815260040161084091815260200190565b505f6108928c8c8c89612632565b90508881808211156108c0576040516380f7c38160e01b815260048101929092526024820152604401610840565b505050806001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016040805180830381865afa1580156108fe573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610922919061514c565b5082526040805160018082528183019092529060208083019080368337019050508260c00181905250886001600160a01b031663a8c62e766040518163ffffffff1660e01b8152600401602060405180830381865afa158015610987573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109ab9190615170565b6001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109e6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a0a9190615170565b8260c001515f81518110610a2057610a20614f79565b6001600160a01b039290921660209283029190910182015260408051600180825281830190925291828101908036833750505060e0830181905280518891905f90610a6d57610a6d614f79565b602002602001018181525050886001600160a01b0316816001600160a01b031603610b4757806001600160a01b0316632cbfd6a38360c00151610ab78e8e8d885f015160016137dc565b8560e001518a306040518663ffffffff1660e01b8152600401610ade95949392919061518b565b5f604051808303815f875af1158015610af9573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b20919081019061504b565b610100830181905280515f90610b3857610b38614f79565b60200260200101519350610bef565b806001600160a01b03166356035e5d8a610b688e8e8d885f015160016137dc565b6040516001600160e01b031960e085901b1681526001600160a01b0392831660048201526024810191909152604481018b9052908916606482015230608482015260a4016020604051808303815f875af1158015610bc8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bec919061507c565b93505b335f908152600f8c01602052604090205460ff16610c89576001600160a01b03808a165f90815260108d016020526040902054168015610c87576040516340c10f1960e01b81526001600160a01b038881166004830152602482018b90528216906340c10f19906044015f604051808303815f87803b158015610c70575f5ffd5b505af1158015610c82573d5f5f3e3d5ffd5b505050505b505b846001600160a01b0316336001600160a01b03167ff6f76fa9e624451c5234aa077063a68dad722c8790170478f0b28b3dc3722bcc8460c001518b866101000151604051610cd993929190614fc6565b60405180910390a3505097509795505050505050565b60605f5b6005840154811015610f0f575f846005018281548110610d1557610d15614f79565b5f9182526020822001546001600160a01b03169150829003610dd857806001600160a01b03166371a973056040518163ffffffff1660e01b81526004015f60405180830381865afa158015610d6c573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610d9391908101906151db565b516001600160401b03811115610dab57610dab61476e565b604051908082528060200260200182016040528015610dd4578160200160208202803683370190505b5092505b60405163402d267d60e01b81526001600160a01b0385811660048301525f919083169063402d267d906024015f60405180830381865afa158015610e1e573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610e45919081019061504b565b90505f5b8151811015610f01575f19858281518110610e6657610e66614f79565b602002602001015114610ef9575f19828281518110610e8757610e87614f79565b602002602001015114610ed757818181518110610ea657610ea6614f79565b6020026020010151858281518110610ec057610ec0614f79565b6020026020010151610ed2919061526a565b610eda565b5f195b858281518110610eec57610eec614f79565b6020026020010181815250505b600101610e49565b505050806001019050610cf3565b505b92915050565b60058301545f5b81811015610f7f57836001600160a01b0316856005018281548110610f4557610f45614f79565b5f918252602090912001546001600160a01b031603610f7757604051631765138960e21b815260040160405180910390fd5b600101610f1e565b506005840154610f9090600161526a565b825114610fb057604051630ef9926760e21b815260040160405180910390fd5b610fb98261384c565b5f836001600160a01b03166371a973056040518163ffffffff1660e01b81526004015f60405180830381865afa158015610ff5573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261101c91908101906151db565b9050611027856138b1565b156110c257805160011461104e57604051631765138960e21b815260040160405180910390fd5b61105a8560030161341e565b5f8151811061106b5761106b614f79565b60200260200101516001600160a01b0316815f8151811061108e5761108e614f79565b60200260200101516001600160a01b0316146110bd57604051631765138960e21b815260040160405180910390fd5b61110a565b805191505f5b82811015611108576110ff8282815181106110e5576110e5614f79565b60200260200101518760030161394c90919063ffffffff16565b506001016110c8565b505b6005850180546001810182555f9182526020918290200180546001600160a01b0319166001600160a01b038716179055835161114e91600688019190860190614711565b506040516001600160a01b03851681527fa6b2cf3908b4193cc3704faa803c2ad050d6045e7529d995a6884434f7b8a22a9060200160405180910390a17fc0700897ab0b41fe622f2a0d2936b8713c49ded52905e340845b77f0e27907da836040516111ba919061499b565b60405180910390a15050505050565b6111d2836138b1565b156111f057604051631765138960e21b815260040160405180910390fd5b801561124657604051636443de6360e11b81525f60048201526001600160a01b0383169063c887bcc6906024015f604051808303815f87803b158015611234575f5ffd5b505af115801561079b573d5f5f3e3d5ffd5b5f8360050180548060200260200160405190810160405280929190818152602001828054801561129d57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161127f575b505050505090505f5b8151811015611452575f8282815181106112c2576112c2614f79565b60200260200101516001600160a01b0316638220ef5b6040518163ffffffff1660e01b81526004015f60405180830381865afa158015611304573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261132b91908101906151db565b90505f5b81518110156113c457856001600160a01b031663c887bcc683838151811061135957611359614f79565b60200260200101516040518263ffffffff1660e01b815260040161138c91906001600160a01b0391909116815260200190565b5f604051808303815f87803b1580156113a3575f5ffd5b505af11580156113b5573d5f5f3e3d5ffd5b5050505080600101905061132f565b50846001600160a01b031663c887bcc68284815181106113e6576113e6614f79565b60200260200101516040518263ffffffff1660e01b815260040161141991906001600160a01b0391909116815260200190565b5f604051808303815f87803b158015611430575f5ffd5b505af1158015611442573d5f5f3e3d5ffd5b50505050508060010190506112a6565b50505b505050565b60605f336001600160a01b03841614611479576114798a8433896134ea565b6114838789613960565b855f036114a3576040516371c42ac360e01b815260040160405180910390fd5b5f6114ae8b8b612bcd565b90505f6114bc8c8684613778565b9050848189818111156114e55760405163391434e360e21b81526004016108409392919061512b565b50505050855188511461150b57604051630ef9926760e21b815260040160405180910390fd5b61151a878c600a0154836137bb565b9150868261153e57604051633d6b36df60e11b815260040161084091815260200190565b506115488b6138b1565b15611665576115b68b8b8d6005018054806020026020016040519081016040528092919081815260200182805480156115a857602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161158a575b50505050508b8b8a8f613a62565b92505f5b885181101561165f578681815181106115d5576115d5614f79565b60200260200101518482815181106115ef576115ef614f79565b6020026020010151101584828151811061160b5761160b614f79565b602002602001015188838151811061162557611625614f79565b6020026020010151909161165557604051637a672cf560e11b815260048101929092526024820152604401610840565b50506001016115ba565b5061172c565b5f5f6116728d8d8d613c8b565b925090508881808211156116a2576040516331c60a1d60e21b815260048101929092526024820152604401610840565b505050896001600160a01b0316632cbfd6a38a6116c38f8f8d8760016137dc565b8a8a306040518663ffffffff1660e01b81526004016116e695949392919061518b565b5f604051808303815f875af1158015611701573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611728919081019061504b565b9350505b836001600160a01b0316336001600160a01b03167ff6f76fa9e624451c5234aa077063a68dad722c8790170478f0b28b3dc3722bcc8a8a8760405161177393929190614fc6565b60405180910390a3509850989650505050505050565b60028201545f9081906001600160a01b0316801561186f57836001600160a01b03166349b5fdb46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117dd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118019190615170565b6040516341976e0960e01b81526001600160a01b03838116600483015291909116906341976e09906024016040805180830381865afa158015611846573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061186a919061514c565b61187b565b670de0b6b3a764000060015b92509250509250929050565b5f5f5f5f5f86600a01549050805f146118ef576118b86118a78888612bcd565b670de0b6b3a7640000836001613cd1565b945086600c0154925086600d01549150815f146118ef576118ec856118dd858261527d565b6118e7854261529c565b613d1e565b93505b5092959194509250565b60608060606119066146c2565b6020808a0180516001600160a01b039081165f90815260108e0190935260409092205490911660a0830181905290519061195f5760405163a97a454560e01b81526001600160a01b039091166004820152602401610840565b505f6119728b8b6001602002015161358e565b8951895191925090811480156119885750875181145b80156119945750865181145b6119b157604051630ef9926760e21b815260040160405180910390fd5b8a516119be908d90612bcd565b6020840152806001600160401b038111156119db576119db61476e565b604051908082528060200260200182016040528015611a04578160200160208202803683370190505b509550806001600160401b03811115611a1f57611a1f61476e565b604051908082528060200260200182016040528015611a48578160200160208202803683370190505b50600a8d01549094505f5b82811015611c6357611a838e8d8381518110611a7157611a71614f79565b60200260200101518760200151613778565b60408601528a518b9082908110611a9c57611a9c614f79565b60200260200101515f03611ad25784604001518b8281518110611ac157611ac1614f79565b602002602001018181525050611b53565b84604001518b8281518110611ae957611ae9614f79565b602002602001015111158c8281518110611b0557611b05614f79565b602002602001015186604001518d8481518110611b2457611b24614f79565b6020026020010151909192611b4f5760405163391434e360e21b81526004016108409392919061512b565b5050505b8a8181518110611b6557611b65614f79565b60200260200101515f03611b8b5760405162f6b81160e81b815260040160405180910390fd5b8a8181518110611b9d57611b9d614f79565b602002602001015185608001818151611bb6919061526a565b9052508a51611be4908c9083908110611bd157611bd1614f79565b60200260200101518387602001516137bb565b868281518110611bf657611bf6614f79565b602002602001018181525050858181518110611c1457611c14614f79565b60200260200101515f14158b8281518110611c3157611c31614f79565b602002602001015190611c5a57604051633d6b36df60e11b815260040161084091815260200190565b50600101611a53565b5083608001515f03611c88576040516371c42ac360e01b815260040160405180910390fd5b50816001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016040805180830381865afa158015611cc4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ce8919061514c565b50835260408051600180825281830190925290602080830190803683370190505060c08401528a600160200201516001600160a01b031663a8c62e766040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d51573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d759190615170565b6001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd49190615170565b8360c001515f81518110611dea57611dea614f79565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337505050610100840152604080516001808252818301909252906020808301908036833750505060e084015260208b01516001600160a01b0390811690831603611f245760c08301516001600160a01b03831690632cbfd6a390611e8d8f8f5f5b60200201516080890151895160016137dc565b8660e0015130306040518663ffffffff1660e01b8152600401611eb495949392919061518b565b5f604051808303815f875af1158015611ecf573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611ef6919081019061504b565b610100840181905280515f90611f0e57611f0e614f79565b6020026020010151836060018181525050611fcb565b6001600160a01b0382166356035e5d8c60016020020151611f468f8f5f611e7a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201525f60448201523060648201819052608482015260a4016020604051808303815f875af1158015611fa1573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fc5919061507c565b60608401525b806001600160401b03811115611fe357611fe361476e565b60405190808252806020026020018201604052801561200c578160200160208202803683370190505b5094505f5b818110156124f15761204684606001518b838151811061203357612033614f79565b602002602001015186608001515f613cd1565b8461010001515f8151811061205d5761205d614f79565b6020026020010181815250508361010001515f8151811061208057612080614f79565b602002602001015187828151811061209a5761209a614f79565b6020026020010181815250508881815181106120b8576120b8614f79565b60200260200101518461010001515f815181106120d7576120d7614f79565b602002602001015110158461010001515f815181106120f8576120f8614f79565b60200260200101518a838151811061211257612112614f79565b6020026020010151909161214257604051637a672cf560e11b815260048101929092526024820152604401610840565b508c9050600160200201516001600160a01b031663a8c62e766040518163ffffffff1660e01b8152600401602060405180830381865afa158015612188573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121ac9190615170565b6001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121e7573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061220b9190615170565b6001600160a01b031663a9059cbb8c838151811061222b5761222b614f79565b602002602001015189848151811061224557612245614f79565b60200260200101516040518363ffffffff1660e01b815260040161227e9291906001600160a01b03929092168252602082015260400190565b6020604051808303815f875af115801561229a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122be91906152af565b508a81815181106122d1576122d1614f79565b60200260200101516001600160a01b0316336001600160a01b03167ff6f76fa9e624451c5234aa077063a68dad722c8790170478f0b28b3dc3722bcc8660c001518d858151811061232457612324614f79565b602002602001015188610100015160405161234193929190614fc6565b60405180910390a3335f908152600f8e01602052604090205460ff166124e95789818151811061237357612373614f79565b602002602001015186828151811061238d5761238d614f79565b6020026020010181815250508360a001516001600160a01b03166340c10f198c83815181106123be576123be614f79565b60200260200101518884815181106123d8576123d8614f79565b60200260200101516040518363ffffffff1660e01b81526004016124119291906001600160a01b03929092168252602082015260400190565b5f604051808303815f87803b158015612428575f5ffd5b505af115801561243a573d5f5f3e3d5ffd5b5050505087818151811061245057612450614f79565b6020026020010151156124e9578360a001516001600160a01b031663637250d98c838151811061248257612482614f79565b60209081029190910101516040516001600160e01b031960e084901b1681526001600160a01b039091166004820152600160248201526044015f604051808303815f87803b1580156124d2575f5ffd5b505af11580156124e4573d5f5f3e3d5ffd5b505050505b600101612011565b5050505096509650969350505050565b5f5f5b6005860154811015612628575f86600501828154811061252657612526614f79565b5f9182526020822001546001600160a01b031691506125fc88888488156125ba576040516342db43ab60e11b8152306004820152602481018a90526001600160a01b038716906385b68756906044015b602060405180830381865afa158015612591573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125b5919061507c565b613d8f565b60405163ce96cb7760e01b81523060048201526001600160a01b0387169063ce96cb7790602401602060405180830381865afa158015612591573d5f5f3e3d5ffd5b509050612609818561526a565b93508386101561261e5785935050505061262a565b5050600101612504565b505b949350505050565b5f5f61264886846126438989612bcd565b613778565b90505f612655878661358e565b9050846001600160a01b0316816001600160a01b0316036126c6576040516342db43ab60e11b8152306004820152600160248201525f906126af908990899085906001600160a01b038216906385b6875690604401612576565b5090506126bc8382613e21565b935050505061262a565b604051633d523fc760e11b81526001600160a01b0386811660048301523060248301525f916126af918a918a91869190821690637aa47f8e90604401612576565b60605f5f5f5f5f886001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016040805180830381865afa158015612749573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061276d919061514c565b50604051635b002c2d60e11b81529091506001600160a01b038a169063b600585a9061279f908b908b906004016152ca565b5f60405180830381865afa1580156127b9573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526127e091908101906152ee565b919750935091505f6127fc8285670de0b6b3a764000084613cd1565b90506128098c8c83613e36565b600a8d01549096509050801561283657612831868d600a015461282c8f8f612bcd565b6137bb565b612838565b855b945050505050955095509592505050565b5f836005018054806020026020016040519081016040528092919081815260200182805480156128a057602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612882575b505083519394505f199392505f9150505b818110156128fb57856001600160a01b03168482815181106128d5576128d5614f79565b60200260200101516001600160a01b0316036128f3578092506128fb565b6001016128b1565b505f19820361291d57604051631765138960e21b815260040160405180910390fd5b5f8660060180548060200260200160405190810160405280929190818152602001828054801561296a57602002820191905f5260205f20905b815481526020019060010190808311612956575b5050505050905080838151811061298357612983614f79565b60200260200101515f146129aa5760405163e88dac0360e01b815260040160405180910390fd5b5f6129b487613e59565b905080868082106129e157604051637136b73760e01b815260048101929092526024820152604401610840565b506129ef905060018461529c565b8414612a9b5784612a0160018561529c565b81518110612a1157612a11614f79565b6020026020010151886005018581548110612a2e57612a2e614f79565b5f91825260209091200180546001600160a01b0319166001600160a01b039290921691909117905581612a6260018561529c565b81518110612a7257612a72614f79565b6020026020010151886006018581548110612a8f57612a8f614f79565b5f918252602090912001555b87600501805480612aae57612aae615339565b5f8281526020902081015f1990810180546001600160a01b031916905501905560068801805480612ae157612ae1615339565b600190038181905f5260205f20015f9055905587600601805480602002602001604051908101604052809291908181526020018280548015612b4057602002820191905f5260205f20905b815481526020019060010190808311612b2c575b50505050509150612b508261384c565b6040516001600160a01b03881681527f8732b7fa557b5e15c2a836202ae53096017ea83a5d5eb1d7e967b4fa188770359060200160405180910390a17fc0700897ab0b41fe622f2a0d2936b8713c49ded52905e340845b77f0e27907da82604051612bbb919061499b565b60405180910390a15050505050505050565b5f5f612bd98484612c97565b5090505f612be78585611789565b509050612bfe82670de0b6b3a7640000835f613cd1565b95945050505050565b6060610f1182613f9d565b5f80612c7f8787868615612c53576040516342db43ab60e11b8152306004820152602481018890526001600160a01b038916906385b6875690604401612576565b60405163ce96cb7760e01b81523060048201526001600160a01b0389169063ce96cb7790602401612576565b509050612c8c8582613e21565b979650505050505050565b5f5f5f836001600160a01b03166349b5fdb46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cd6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cfa9190615170565b90505f5f86600501805480602002602001604051908101604052809291908181526020018280548015612d5457602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612d36575b505083519394505f925050505b81811015612ec0575f5f866001600160a01b031663ec917bca868581518110612d8c57612d8c614f79565b60200260200101516040518263ffffffff1660e01b8152600401612dbf91906001600160a01b0391909116815260200190565b6040805180830381865afa158015612dd9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612dfd919061514c565b9150915080612e0b57600195505b5f858481518110612e1e57612e1e614f79565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015612e6c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e90919061507c565b9050612ea68382670de0b6b3a76400005f613cd1565b612eb0908b61526a565b9950505050806001019050612d61565b505f612ece8960030161341e565b9050805191505f826001600160401b03811115612eed57612eed61476e565b604051908082528060200260200182016040528015612f16578160200160208202803683370190505b5090505f5b83811015612fcc57828181518110612f3557612f35614f79565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015612f83573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fa7919061507c565b828281518110612fb957612fb9614f79565b6020908102919091010152600101612f1b565b505f5f876001600160a01b031663a36fba3285856040518363ffffffff1660e01b8152600401612ffd9291906152ca565b5f60405180830381865afa158015613017573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261303e919081019061534d565b935050509150818a613050919061526a565b99508061305c57600196505b8615985050505050505050509250929050565b5f5f5f836005018054806020026020016040519081016040528092919081815260200182805480156130c857602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116130aa575b5050505050905083600a01545f0361311857805f815181106130ec576130ec614f79565b6020026020010151815f8151811061310657613106614f79565b60200260200101519250925050915091565b80515f61312486613f9d565b90505f8660060180548060200260200160405190810160405280929190818152602001828054801561317357602002820191905f5260205f20905b81548152602001906001019080831161315f575b505050505090505f5f855f8151811061318e5761318e614f79565b60200260200101519750855f815181106131aa576131aa614f79565b602002602001015196505f5b8581101561333f578381815181106131d0576131d0614f79565b60200260200101518582815181106131ea576131ea614f79565b60200260200101511015613267575f85828151811061320b5761320b614f79565b602002602001015185838151811061322557613225614f79565b6020026020010151613237919061529c565b9050838111156132615780935087828151811061325657613256614f79565b602002602001015199505b50613337565b83818151811061327957613279614f79565b602002602001015185828151811061329357613293614f79565b60200260200101511180156132c75750662386f26fc100008582815181106132bd576132bd614f79565b6020026020010151115b15613337575f8482815181106132df576132df614f79565b60200260200101518683815181106132f9576132f9614f79565b602002602001015161330b919061529c565b9050828111156133355780925087828151811061332a5761332a614f79565b602002602001015198505b505b6001016131b6565b50505050505050915091565b816001600160a01b03811661337f5760405163ec442f0560e01b81526001600160a01b039091166004820152602401610840565b5061338c858433846134ea565b5f6133a08287600a015461282c8989612bcd565b6001600160a01b0385165f908152600b880160205260408120805492935083929091906133ce90849061529c565b90915550506001600160a01b0383165f908152600b87016020526040812080548392906133fc90849061526a565b9091555050505050505050565b61341b6509184e72a0006103e86150a7565b81565b60605f61342a836141da565b9392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526134828482614233565b6134e457604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526134da9085906142d0565b6134e484826142d0565b50505050565b6001600160a01b038084165f81815260098701602090815260408083209487168352939052919091205490331480159061352557505f198114155b15611452578281838082101561355157604051637dc7a0d960e11b81526004016108409392919061512b565b5050508181613560919061529c565b6001600160a01b038086165f9081526009880160209081526040808320938816835292905220555050505050565b5f5f836005018054806020026020016040519081016040528092919081815260200182805480156135e657602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116135c8575b505050505090505f5b815181101561365557836001600160a01b031682828151811061361457613614614f79565b60200260200101516001600160a01b03160361364d5781818151811061363c5761363c614f79565b602002602001015192505050610f11565b6001016135ef565b505f5b8151811015613753575f82828151811061367457613674614f79565b60200260200101516001600160a01b0316638220ef5b6040518163ffffffff1660e01b81526004015f60405180830381865afa1580156136b6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526136dd91908101906151db565b90505f5b815181101561374957856001600160a01b031682828151811061370657613706614f79565b60200260200101516001600160a01b0316036137415783838151811061372e5761372e614f79565b6020026020010151945050505050610f11565b6001016136e1565b5050600101613658565b5060405163c02219d960e01b81526001600160a01b0384166004820152602401610840565b600a8301545f9080156137b1576001600160a01b0384165f908152600b860160205260408120546137ac9185908490613cd1565b612bfe565b5f95945050505050565b5f81156137d3576137ce8484845f613cd1565b61262a565b5f949350505050565b5f5f6137e9878787614331565b90506509184e72a0008111156138155761380d81670de0b6b3a7640000865f613cd1565b915050612bfe565b821561384357604051637136b73760e01b8152600481018290526509184e72a0006024820152604401610840565b5f915050612bfe565b80515f805b828110156138885783818151811061386b5761386b614f79565b60200260200101518261387e919061526a565b9150600101613851565b5080670de0b6b3a7640000146114555760405163e88dac0360e01b815260040160405180910390fd5b604080518082018252600a815269135d5b1d1a55985d5b1d60b21b602082015290516310d24b2160e11b81525f917372a237cb9caaf0716bb4f717e32e79ce6460e5d3916321a496429161390d916001870191906004016153ef565b602060405180830381865af4158015613928573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f1191906152af565b5f61342a836001600160a01b038416614355565b5f816001600160a01b03166371a973056040518163ffffffff1660e01b81526004015f60405180830381865afa15801561399c573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526139c391908101906151db565b905080518351146139e757604051630ef9926760e21b815260040160405180910390fd5b5f5b83518110156134e457818181518110613a0457613a04614f79565b60200260200101516001600160a01b0316848281518110613a2757613a27614f79565b60200260200101516001600160a01b031614613a5a5783826040516309b2ee8f60e21b81526004016108409291906154a4565b6001016139e9565b606083613a6f83886143a1565b50865186516001600160401b03811115613a8b57613a8b61476e565b604051908082528060200260200182016040528015613ab4578160200160208202803683370190505b5092505f5b81811015613c46575f5f613ae98d8d8d8681518110613ada57613ada614f79565b60200260200101518830614471565b91509150805f14613c3c575f8b8481518110613b0757613b07614f79565b60200260200101516001600160a01b0316632cbfd6a38c848e516001600160401b03811115613b3857613b3861476e565b604051908082528060200260200182016040528015613b61578160200160208202803683370190505b508d306040518663ffffffff1660e01b8152600401613b8495949392919061518b565b5f604051808303815f875af1158015613b9f573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613bc6919081019061504b565b90505f5b8b51811015613c1e57818181518110613be557613be5614f79565b6020026020010151888281518110613bff57613bff614f79565b60200260200101818151613c13919061526a565b905250600101613bca565b50613c29838761529c565b9550855f03613c3a57505050613c46565b505b5050600101613ab9565b50811586613c54848261529c565b9091613c7c576040516331c60a1d60e21b815260048101929092526024820152604401610840565b50505050979650505050505050565b6040516370a0823160e01b81523060048201525f908190613cc5908690869086906001600160a01b038216906370a0823190602401612576565b91509150935093915050565b5f5f613cde8686866144e1565b9050613ce9836145a0565b8015613d0457505f8480613cff57613cff6150be565b868809115b15612bfe57613d1460018261526a565b9695505050505050565b5f831580613d2a575081155b6137d35762015180613d4483670de0b6b3a76400006150a7565b613d4e91906150d2565b8461016d613d64670de0b6b3a7640000876154c8565b613d7190620186a06154c8565b613d7b91906154c8565b613d8591906154f7565b6137ce91906154f7565b5f5f836001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016040805180830381865afa158015613dcc573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613df0919061514c565b5090505f613e088285670de0b6b3a764000084613cd1565b9050613e15878783613e36565b92505094509492505050565b5f818310613e2f578161342a565b5090919050565b5f5f613e428585611789565b509050612bfe83670de0b6b3a7640000835f613cd1565b5f5f826001600160a01b031663e5328e066040518163ffffffff1660e01b81526004016040805180830381865afa158015613e96573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613eba919061514c565b506040516370a0823160e01b81523060048201529091505f906001600160a01b038516906370a0823190602401602060405180830381865afa158015613f02573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f26919061507c565b90505f846001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613f65573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f89919061507c565b905080156137b1576137ac8284835f613cd1565b60605f82600501805480602002602001604051908101604052809291908181526020018280548015613ff657602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311613fd8575b5050505050905082600a01545f03614061578260060180548060200260200160405190810160405280929190818152602001828054801561405457602002820191905f5260205f20905b815481526020019060010190808311614040575b5050505050915050919050565b8051806001600160401b0381111561407b5761407b61476e565b6040519080825280602002602001820160405280156140a4578160200160208202803683370190505b5092505f816001600160401b038111156140c0576140c061476e565b6040519080825280602002602001820160405280156140e9578160200160208202803683370190505b5090505f5f5b838110156141665761411985828151811061410c5761410c614f79565b6020026020010151613e59565b83828151811061412b5761412b614f79565b60200260200101818152505082818151811061414957614149614f79565b60200260200101518261415c919061526a565b91506001016140ef565b505f5b838110156141d05781156141a9576141a483828151811061418c5761418c614f79565b6020026020010151670de0b6b3a7640000845f613cd1565b6141ab565b5f5b8682815181106141bd576141bd614f79565b6020908102919091010152600101614169565b5050505050919050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561422757602002820191905f5260205f20905b815481526020019060010190808311614213575b50505050509050919050565b5f5f5f846001600160a01b03168460405161424e9190615523565b5f604051808303815f865af19150503d805f8114614287576040519150601f19603f3d011682016040523d82523d5f602084013e61428c565b606091505b50915091508180156142b65750805115806142b65750808060200190518101906142b691906152af565b8015612bfe5750505050506001600160a01b03163b151590565b5f6142e46001600160a01b038416836145cc565b905080515f1415801561430857508080602001905181019061430691906152af565b155b1561145557604051635274afe760e01b81526001600160a01b0384166004820152602401610840565b5f5f61433d8585611789565b509050612bfe8382670de0b6b3a76400006001613cd1565b5f81815260018301602052604081205461439a57508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610f11565b505f610f11565b80516060905f5b8181101561446857846001600160a01b03168482815181106143cc576143cc614f79565b60200260200101516001600160a01b031603614460578381815181106143f4576143f4614f79565b6020026020010151845f8151811061440e5761440e614f79565b6020026020010151855f8151811061442857614428614f79565b6020026020010186848151811061444157614441614f79565b6001600160a01b03938416602091820292909201015291169052614468565b6001016143a8565b50919392505050565b5f5f5f5f6144b68989898a6001600160a01b031663ce96cb778a6040518263ffffffff1660e01b815260040161257691906001600160a01b0391909116815260200190565b915091506144c48683613e21565b93506144d3898986845f6137dc565b925050509550959350505050565b5f838302815f1985870982811083820303915050805f036145155783828161450b5761450b6150be565b049250505061342a565b8084116145355760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f60028260038111156145b5576145b5615539565b6145bf919061554d565b60ff166001149050919050565b606061342a83835f845f5f856001600160a01b031684866040516145f09190615523565b5f6040518083038185875af1925050503d805f811461462a576040519150601f19603f3d011682016040523d82523d5f602084013e61462f565b606091505b5091509150613d1486838360608261464f5761464a82614696565b61342a565b815115801561466657506001600160a01b0384163b155b1561468f57604051639996b31560e01b81526001600160a01b0385166004820152602401610840565b508061342a565b8051156146a65780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b50565b6040518061012001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f6001600160a01b031681526020016060815260200160608152602001606081525090565b828054828255905f5260205f2090810192821561474a579160200282015b8281111561474a57825182559160200191906001019061472f565b5061475692915061475a565b5090565b5b80821115614756575f815560010161475b565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b03811182821017156147aa576147aa61476e565b604052919050565b5f6001600160401b038211156147ca576147ca61476e565b5060051b60200190565b5f82601f8301126147e3575f5ffd5b81356147f66147f1826147b2565b614782565b8082825260208201915060208360051b860101925085831115614817575f5ffd5b602085015b8381101561483457803583526020928301920161481c565b5095945050505050565b5f5f5f60608486031215614850575f5ffd5b8335925060208401356001600160401b0381111561486c575f5ffd5b614878868287016147d4565b92505060408401356001600160401b03811115614893575f5ffd5b61489f868287016147d4565b9150509250925092565b6001600160a01b03811681146146bf575f5ffd5b5f5f5f5f5f5f5f60e0888a0312156148d3575f5ffd5b8735965060208801356148e5816148a9565b955060408801356148f5816148a9565b9450606088013593506080880135925060a0880135614913816148a9565b915060c0880135614923816148a9565b8091505092959891949750929550565b5f5f60408385031215614944575f5ffd5b823591506020830135614956816148a9565b809150509250929050565b5f8151808452602084019350602083015f5b82811015614991578151865260209586019590910190600101614973565b5093949350505050565b602081525f61342a6020830184614961565b5f5f5f606084860312156149bf575f5ffd5b8335925060208401356149d1816148a9565b915060408401356001600160401b03811115614893575f5ffd5b80151581146146bf575f5ffd5b5f5f5f60608486031215614a0a575f5ffd5b833592506020840135614a1c816148a9565b91506040840135614a2c816149eb565b809150509250925092565b5f82601f830112614a46575f5ffd5b8135614a546147f1826147b2565b8082825260208201915060208360051b860101925085831115614a75575f5ffd5b602085015b83811015614834578035614a8d816148a9565b835260209283019201614a7a565b5f5f5f5f5f5f5f5f610100898b031215614ab3575f5ffd5b883597506020890135614ac5816148a9565b96506040890135614ad5816148a9565b955060608901356001600160401b03811115614aef575f5ffd5b614afb8b828c01614a37565b9550506080890135935060a08901356001600160401b03811115614b1d575f5ffd5b614b298b828c016147d4565b93505060c0890135614b3a816148a9565b915060e0890135614b4a816148a9565b809150509295985092959890939650565b604081525f614b6d6040830185614961565b90508260208301529392505050565b5f82601f830112614b8b575f5ffd5b604080519081016001600160401b0381118282101715614bad57614bad61476e565b8060405250806040840185811115614bc3575f5ffd5b845b81811015614be6578035614bd8816148a9565b835260209283019201614bc5565b509195945050505050565b5f82601f830112614c00575f5ffd5b8135614c0e6147f1826147b2565b8082825260208201915060208360051b860101925085831115614c2f575f5ffd5b602085015b83811015614834578035614c47816149eb565b835260209283019201614c34565b5f5f5f5f5f5f60e08789031215614c6a575f5ffd5b86359550614c7b8860208901614b7c565b945060608701356001600160401b03811115614c95575f5ffd5b614ca189828a01614a37565b94505060808701356001600160401b03811115614cbc575f5ffd5b614cc889828a016147d4565b93505060a08701356001600160401b03811115614ce3575f5ffd5b614cef89828a016147d4565b92505060c08701356001600160401b03811115614d0a575f5ffd5b614d1689828a01614bf1565b9150509295509295509295565b606081525f614d356060830186614961565b8281036020840152614d478186614961565b90508281036040840152613d148185614961565b5f5f5f5f60808587031215614d6e575f5ffd5b843593506020850135614d80816148a9565b93969395505050506040820135916060013590565b5f5f5f5f60808587031215614da8575f5ffd5b843593506020850135614dba816148a9565b92506040850135614dca816148a9565b91506060850135614dda816148a9565b939692955090935050565b5f5f5f5f5f60a08688031215614df9575f5ffd5b853594506020860135614e0b816148a9565b93506040860135614e1b816148a9565b925060608601356001600160401b03811115614e35575f5ffd5b614e4188828901614a37565b92505060808601356001600160401b03811115614e5c575f5ffd5b614e68888289016147d4565b9150509295509295909350565b606081525f614e876060830186614961565b60208301949094525060400152919050565b5f5f5f60608486031215614eab575f5ffd5b833592506020840135614ebd816148a9565b929592945050506040919091013590565b5f60208284031215614ede575f5ffd5b5035919050565b5f5f5f5f5f60a08688031215614ef9575f5ffd5b853594506020860135614f0b816148a9565b9350604086013592506060860135614f22816148a9565b949793965091946080013592915050565b5f5f5f5f5f60a08688031215614f47575f5ffd5b853594506020860135614f59816148a9565b93506040860135614f69816148a9565b92506060860135614f22816148a9565b634e487b7160e01b5f52603260045260245ffd5b5f8151808452602084019350602083015f5b828110156149915781516001600160a01b0316865260209586019590910190600101614f9f565b606081525f614fd86060830186614f8d565b8460208401528281036040840152613d148185614961565b5f82601f830112614fff575f5ffd5b815161500d6147f1826147b2565b8082825260208201915060208360051b86010192508583111561502e575f5ffd5b602085015b83811015614834578051835260209283019201615033565b5f6020828403121561505b575f5ffd5b81516001600160401b03811115615070575f5ffd5b61262a84828501614ff0565b5f6020828403121561508c575f5ffd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610f1157610f11615093565b634e487b7160e01b5f52601260045260245ffd5b5f826150e0576150e06150be565b500490565b608081525f6150f76080830187614f8d565b82810360208401526151098187614961565b604084019590955250506001600160a01b039190911660609091015292915050565b6001600160a01b039390931683526020830191909152604082015260600190565b5f5f6040838503121561515d575f5ffd5b82516020840151909250614956816149eb565b5f60208284031215615180575f5ffd5b815161342a816148a9565b60a081525f61519d60a0830188614f8d565b86602084015282810360408401526151b58187614961565b6001600160a01b0395861660608501529390941660809092019190915250949350505050565b5f602082840312156151eb575f5ffd5b81516001600160401b03811115615200575f5ffd5b8201601f81018413615210575f5ffd5b805161521e6147f1826147b2565b8082825260208201915060208360051b85010192508683111561523f575f5ffd5b6020840193505b82841015613d14578351615259816148a9565b825260209384019390910190615246565b80820180821115610f1157610f11615093565b8181035f831280158383131683831282161715610f0f57610f0f615093565b81810381811115610f1157610f11615093565b5f602082840312156152bf575f5ffd5b815161342a816149eb565b604081525f6152dc6040830185614f8d565b8281036020840152612bfe8185614961565b5f5f5f60608486031215615300575f5ffd5b83516001600160401b03811115615315575f5ffd5b61532186828701614ff0565b60208601516040909601519097959650949350505050565b634e487b7160e01b5f52603160045260245ffd5b5f5f5f5f60808587031215615360575f5ffd5b845160208601519094506001600160401b0381111561537d575f5ffd5b61538987828801614ff0565b93505060408501516001600160401b038111156153a4575f5ffd5b6153b087828801614ff0565b9250506060850151614dda816149eb565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b604081525f5f84545f8160011c9050600182168061540e57607f821691505b60208210810361542c57634e487b7160e01b5f52602260045260245ffd5b604086018290526060860181801561544b57600181146154615761548d565b60ff198516825283151560051b8201955061548d565b5f8a8152602090205f5b858110156154875781548482015260019091019060200161546b565b83019650505b50505050508281036020840152612bfe81856153c1565b604081525f6154b66040830185614f8d565b8281036020840152612bfe8185614f8d565b8082025f8212600160ff1b841416156154e3576154e3615093565b8181058314821517610f1157610f11615093565b5f82615505576155056150be565b600160ff1b82145f198414161561551e5761551e615093565b500590565b5f82518060208501845e5f920191825250919050565b634e487b7160e01b5f52602160045260245ffd5b5f60ff83168061555f5761555f6150be565b8060ff8416069150509291505056fea264697066735822122023a85e2f3f562dd6e58ec4792ede531216bfc08908795de161c6fa3f0a158cba64736f6c634300081c0033
Deployed Bytecode
0x7364b7962e48359a6627f5ae542aa7db928ddd2d28301460806040526004361061013d575f3560e01c8063a837007d116100bf578063c1a0a95211610084578063c1a0a9521461034a578063c88e167a14610357578063d11856461461036a578063db60f77a1461037d578063e8afe558146103b0578063ed0fde82146103cf575f5ffd5b8063a837007d146102d0578063af2b5a08146102e3578063b95bed4414610305578063bd85888514610324578063c0a1557614610337575f5ffd5b80637fbf0b14116101055780637fbf0b14146101f95780639448313d146102265780639aa8a9a31461024e5780639ebce86414610281578063a4b06c00146102af575f5ffd5b80630f4493bc146101415780631c6710ec146101625780632d609c861461019b578063541020e0146101bb5780636469a73c146101da575b5f5ffd5b81801561014c575f5ffd5b5061016061015b36600461483e565b6103d7565b005b81801561016d575f5ffd5b5061018161017c3660046148bd565b6107a4565b604080519283526020830191909152015b60405180910390f35b6101ae6101a9366004614933565b610cef565b604051610192919061499b565b8180156101c6575f5ffd5b506101606101d53660046149ad565b610f17565b8180156101e5575f5ffd5b506101606101f43660046149f8565b6111c9565b818015610204575f5ffd5b50610218610213366004614a9b565b61145a565b604051610192929190614b5b565b610239610234366004614933565b611789565b60408051928352901515602083015201610192565b61026161025c366004614933565b611887565b604080519485526020850193909352918301526060820152608001610192565b81801561028c575f5ffd5b506102a061029b366004614c55565b6118f9565b60405161019293929190614d23565b6102c26102bd366004614d5b565b612501565b604051908152602001610192565b6102c26102de366004614d95565b612632565b6102f66102f1366004614de5565b612707565b60405161019293929190614e75565b818015610310575f5ffd5b5061016061031f366004614e99565b612849565b6102c2610332366004614933565b612bcd565b6101ae610345366004614ece565b612c07565b6102c26509184e72a00081565b6102c2610365366004614ee5565b612c12565b610239610378366004614933565b612c97565b61039061038b366004614ece565b61306f565b604080516001600160a01b03938416815292909116602083015201610192565b8180156103bb575f5ffd5b506101606103ca366004614f33565b61334b565b6102c2613409565b60058301545f6103e96003860161341e565b90505f5b828110156105535784818151811061040757610407614f79565b60200260200101515f1461054b5785600501818154811061042a5761042a614f79565b905f5260205f20015f9054906101000a90046001600160a01b03166001600160a01b0316630b4881ef8387848151811061046657610466614f79565b602002602001015160016001600160401b038111156104875761048761476e565b6040519080825280602002602001820160405280156104b0578160200160208202803683370190505b506040518463ffffffff1660e01b81526004016104cf93929190614fc6565b5f604051808303815f875af11580156104ea573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610511919081019061504b565b5083818151811061052457610524614f79565b60200260200101515f1461054b57604051637b27a36360e01b815260040160405180910390fd5b6001016103ed565b505f815f8151811061056757610567614f79565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156105b5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105d9919061507c565b90505f5b8381101561079b575f8760050182815481106105fb576105fb614f79565b5f9182526020822001546040805160018082528183019092526001600160a01b039092169350816020016020820280368337019050509050670de0b6b3a76400008488858151811061064f5761064f614f79565b602002602001015161066191906150a7565b61066b91906150d2565b815f8151811061067d5761067d614f79565b602002602001018181525050805f8151811061069b5761069b614f79565b60200260200101515f14610791576106f982825f815181106106bf576106bf614f79565b6020026020010151875f815181106106d9576106d9614f79565b60200260200101516001600160a01b03166134319092919063ffffffff16565b60405163b256cd8b60e01b81526001600160a01b0383169063b256cd8b9061072b90889085905f9030906004016150e5565b5f604051808303815f87803b158015610742575f5ffd5b505af1158015610754573d5f5f3e3d5ffd5b5050505087838151811061076a5761076a614f79565b60200260200101515f1461079157604051637b27a36360e01b815260040160405180910390fd5b50506001016105dd565b50505050505050565b5f5f6107ae6146c2565b336001600160a01b038516146107ca576107ca8a85338a6134ea565b865f036107ea576040516371c42ac360e01b815260040160405180910390fd5b5f6107f58b8a61358e565b90506108018b8b612bcd565b602083018190525f90610817908d908890613778565b905085818a818111156108495760405163391434e360e21b81526004016108409392919061512b565b60405180910390fd5b50505050610860888c600a015484602001516137bb565b9250878361088457604051633d6b36df60e11b815260040161084091815260200190565b505f6108928c8c8c89612632565b90508881808211156108c0576040516380f7c38160e01b815260048101929092526024820152604401610840565b505050806001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016040805180830381865afa1580156108fe573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610922919061514c565b5082526040805160018082528183019092529060208083019080368337019050508260c00181905250886001600160a01b031663a8c62e766040518163ffffffff1660e01b8152600401602060405180830381865afa158015610987573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109ab9190615170565b6001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109e6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a0a9190615170565b8260c001515f81518110610a2057610a20614f79565b6001600160a01b039290921660209283029190910182015260408051600180825281830190925291828101908036833750505060e0830181905280518891905f90610a6d57610a6d614f79565b602002602001018181525050886001600160a01b0316816001600160a01b031603610b4757806001600160a01b0316632cbfd6a38360c00151610ab78e8e8d885f015160016137dc565b8560e001518a306040518663ffffffff1660e01b8152600401610ade95949392919061518b565b5f604051808303815f875af1158015610af9573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b20919081019061504b565b610100830181905280515f90610b3857610b38614f79565b60200260200101519350610bef565b806001600160a01b03166356035e5d8a610b688e8e8d885f015160016137dc565b6040516001600160e01b031960e085901b1681526001600160a01b0392831660048201526024810191909152604481018b9052908916606482015230608482015260a4016020604051808303815f875af1158015610bc8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bec919061507c565b93505b335f908152600f8c01602052604090205460ff16610c89576001600160a01b03808a165f90815260108d016020526040902054168015610c87576040516340c10f1960e01b81526001600160a01b038881166004830152602482018b90528216906340c10f19906044015f604051808303815f87803b158015610c70575f5ffd5b505af1158015610c82573d5f5f3e3d5ffd5b505050505b505b846001600160a01b0316336001600160a01b03167ff6f76fa9e624451c5234aa077063a68dad722c8790170478f0b28b3dc3722bcc8460c001518b866101000151604051610cd993929190614fc6565b60405180910390a3505097509795505050505050565b60605f5b6005840154811015610f0f575f846005018281548110610d1557610d15614f79565b5f9182526020822001546001600160a01b03169150829003610dd857806001600160a01b03166371a973056040518163ffffffff1660e01b81526004015f60405180830381865afa158015610d6c573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610d9391908101906151db565b516001600160401b03811115610dab57610dab61476e565b604051908082528060200260200182016040528015610dd4578160200160208202803683370190505b5092505b60405163402d267d60e01b81526001600160a01b0385811660048301525f919083169063402d267d906024015f60405180830381865afa158015610e1e573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610e45919081019061504b565b90505f5b8151811015610f01575f19858281518110610e6657610e66614f79565b602002602001015114610ef9575f19828281518110610e8757610e87614f79565b602002602001015114610ed757818181518110610ea657610ea6614f79565b6020026020010151858281518110610ec057610ec0614f79565b6020026020010151610ed2919061526a565b610eda565b5f195b858281518110610eec57610eec614f79565b6020026020010181815250505b600101610e49565b505050806001019050610cf3565b505b92915050565b60058301545f5b81811015610f7f57836001600160a01b0316856005018281548110610f4557610f45614f79565b5f918252602090912001546001600160a01b031603610f7757604051631765138960e21b815260040160405180910390fd5b600101610f1e565b506005840154610f9090600161526a565b825114610fb057604051630ef9926760e21b815260040160405180910390fd5b610fb98261384c565b5f836001600160a01b03166371a973056040518163ffffffff1660e01b81526004015f60405180830381865afa158015610ff5573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261101c91908101906151db565b9050611027856138b1565b156110c257805160011461104e57604051631765138960e21b815260040160405180910390fd5b61105a8560030161341e565b5f8151811061106b5761106b614f79565b60200260200101516001600160a01b0316815f8151811061108e5761108e614f79565b60200260200101516001600160a01b0316146110bd57604051631765138960e21b815260040160405180910390fd5b61110a565b805191505f5b82811015611108576110ff8282815181106110e5576110e5614f79565b60200260200101518760030161394c90919063ffffffff16565b506001016110c8565b505b6005850180546001810182555f9182526020918290200180546001600160a01b0319166001600160a01b038716179055835161114e91600688019190860190614711565b506040516001600160a01b03851681527fa6b2cf3908b4193cc3704faa803c2ad050d6045e7529d995a6884434f7b8a22a9060200160405180910390a17fc0700897ab0b41fe622f2a0d2936b8713c49ded52905e340845b77f0e27907da836040516111ba919061499b565b60405180910390a15050505050565b6111d2836138b1565b156111f057604051631765138960e21b815260040160405180910390fd5b801561124657604051636443de6360e11b81525f60048201526001600160a01b0383169063c887bcc6906024015f604051808303815f87803b158015611234575f5ffd5b505af115801561079b573d5f5f3e3d5ffd5b5f8360050180548060200260200160405190810160405280929190818152602001828054801561129d57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161127f575b505050505090505f5b8151811015611452575f8282815181106112c2576112c2614f79565b60200260200101516001600160a01b0316638220ef5b6040518163ffffffff1660e01b81526004015f60405180830381865afa158015611304573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261132b91908101906151db565b90505f5b81518110156113c457856001600160a01b031663c887bcc683838151811061135957611359614f79565b60200260200101516040518263ffffffff1660e01b815260040161138c91906001600160a01b0391909116815260200190565b5f604051808303815f87803b1580156113a3575f5ffd5b505af11580156113b5573d5f5f3e3d5ffd5b5050505080600101905061132f565b50846001600160a01b031663c887bcc68284815181106113e6576113e6614f79565b60200260200101516040518263ffffffff1660e01b815260040161141991906001600160a01b0391909116815260200190565b5f604051808303815f87803b158015611430575f5ffd5b505af1158015611442573d5f5f3e3d5ffd5b50505050508060010190506112a6565b50505b505050565b60605f336001600160a01b03841614611479576114798a8433896134ea565b6114838789613960565b855f036114a3576040516371c42ac360e01b815260040160405180910390fd5b5f6114ae8b8b612bcd565b90505f6114bc8c8684613778565b9050848189818111156114e55760405163391434e360e21b81526004016108409392919061512b565b50505050855188511461150b57604051630ef9926760e21b815260040160405180910390fd5b61151a878c600a0154836137bb565b9150868261153e57604051633d6b36df60e11b815260040161084091815260200190565b506115488b6138b1565b15611665576115b68b8b8d6005018054806020026020016040519081016040528092919081815260200182805480156115a857602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161158a575b50505050508b8b8a8f613a62565b92505f5b885181101561165f578681815181106115d5576115d5614f79565b60200260200101518482815181106115ef576115ef614f79565b6020026020010151101584828151811061160b5761160b614f79565b602002602001015188838151811061162557611625614f79565b6020026020010151909161165557604051637a672cf560e11b815260048101929092526024820152604401610840565b50506001016115ba565b5061172c565b5f5f6116728d8d8d613c8b565b925090508881808211156116a2576040516331c60a1d60e21b815260048101929092526024820152604401610840565b505050896001600160a01b0316632cbfd6a38a6116c38f8f8d8760016137dc565b8a8a306040518663ffffffff1660e01b81526004016116e695949392919061518b565b5f604051808303815f875af1158015611701573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611728919081019061504b565b9350505b836001600160a01b0316336001600160a01b03167ff6f76fa9e624451c5234aa077063a68dad722c8790170478f0b28b3dc3722bcc8a8a8760405161177393929190614fc6565b60405180910390a3509850989650505050505050565b60028201545f9081906001600160a01b0316801561186f57836001600160a01b03166349b5fdb46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117dd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118019190615170565b6040516341976e0960e01b81526001600160a01b03838116600483015291909116906341976e09906024016040805180830381865afa158015611846573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061186a919061514c565b61187b565b670de0b6b3a764000060015b92509250509250929050565b5f5f5f5f5f86600a01549050805f146118ef576118b86118a78888612bcd565b670de0b6b3a7640000836001613cd1565b945086600c0154925086600d01549150815f146118ef576118ec856118dd858261527d565b6118e7854261529c565b613d1e565b93505b5092959194509250565b60608060606119066146c2565b6020808a0180516001600160a01b039081165f90815260108e0190935260409092205490911660a0830181905290519061195f5760405163a97a454560e01b81526001600160a01b039091166004820152602401610840565b505f6119728b8b6001602002015161358e565b8951895191925090811480156119885750875181145b80156119945750865181145b6119b157604051630ef9926760e21b815260040160405180910390fd5b8a516119be908d90612bcd565b6020840152806001600160401b038111156119db576119db61476e565b604051908082528060200260200182016040528015611a04578160200160208202803683370190505b509550806001600160401b03811115611a1f57611a1f61476e565b604051908082528060200260200182016040528015611a48578160200160208202803683370190505b50600a8d01549094505f5b82811015611c6357611a838e8d8381518110611a7157611a71614f79565b60200260200101518760200151613778565b60408601528a518b9082908110611a9c57611a9c614f79565b60200260200101515f03611ad25784604001518b8281518110611ac157611ac1614f79565b602002602001018181525050611b53565b84604001518b8281518110611ae957611ae9614f79565b602002602001015111158c8281518110611b0557611b05614f79565b602002602001015186604001518d8481518110611b2457611b24614f79565b6020026020010151909192611b4f5760405163391434e360e21b81526004016108409392919061512b565b5050505b8a8181518110611b6557611b65614f79565b60200260200101515f03611b8b5760405162f6b81160e81b815260040160405180910390fd5b8a8181518110611b9d57611b9d614f79565b602002602001015185608001818151611bb6919061526a565b9052508a51611be4908c9083908110611bd157611bd1614f79565b60200260200101518387602001516137bb565b868281518110611bf657611bf6614f79565b602002602001018181525050858181518110611c1457611c14614f79565b60200260200101515f14158b8281518110611c3157611c31614f79565b602002602001015190611c5a57604051633d6b36df60e11b815260040161084091815260200190565b50600101611a53565b5083608001515f03611c88576040516371c42ac360e01b815260040160405180910390fd5b50816001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016040805180830381865afa158015611cc4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ce8919061514c565b50835260408051600180825281830190925290602080830190803683370190505060c08401528a600160200201516001600160a01b031663a8c62e766040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d51573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d759190615170565b6001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd49190615170565b8360c001515f81518110611dea57611dea614f79565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337505050610100840152604080516001808252818301909252906020808301908036833750505060e084015260208b01516001600160a01b0390811690831603611f245760c08301516001600160a01b03831690632cbfd6a390611e8d8f8f5f5b60200201516080890151895160016137dc565b8660e0015130306040518663ffffffff1660e01b8152600401611eb495949392919061518b565b5f604051808303815f875af1158015611ecf573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611ef6919081019061504b565b610100840181905280515f90611f0e57611f0e614f79565b6020026020010151836060018181525050611fcb565b6001600160a01b0382166356035e5d8c60016020020151611f468f8f5f611e7a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201525f60448201523060648201819052608482015260a4016020604051808303815f875af1158015611fa1573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fc5919061507c565b60608401525b806001600160401b03811115611fe357611fe361476e565b60405190808252806020026020018201604052801561200c578160200160208202803683370190505b5094505f5b818110156124f15761204684606001518b838151811061203357612033614f79565b602002602001015186608001515f613cd1565b8461010001515f8151811061205d5761205d614f79565b6020026020010181815250508361010001515f8151811061208057612080614f79565b602002602001015187828151811061209a5761209a614f79565b6020026020010181815250508881815181106120b8576120b8614f79565b60200260200101518461010001515f815181106120d7576120d7614f79565b602002602001015110158461010001515f815181106120f8576120f8614f79565b60200260200101518a838151811061211257612112614f79565b6020026020010151909161214257604051637a672cf560e11b815260048101929092526024820152604401610840565b508c9050600160200201516001600160a01b031663a8c62e766040518163ffffffff1660e01b8152600401602060405180830381865afa158015612188573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121ac9190615170565b6001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121e7573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061220b9190615170565b6001600160a01b031663a9059cbb8c838151811061222b5761222b614f79565b602002602001015189848151811061224557612245614f79565b60200260200101516040518363ffffffff1660e01b815260040161227e9291906001600160a01b03929092168252602082015260400190565b6020604051808303815f875af115801561229a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122be91906152af565b508a81815181106122d1576122d1614f79565b60200260200101516001600160a01b0316336001600160a01b03167ff6f76fa9e624451c5234aa077063a68dad722c8790170478f0b28b3dc3722bcc8660c001518d858151811061232457612324614f79565b602002602001015188610100015160405161234193929190614fc6565b60405180910390a3335f908152600f8e01602052604090205460ff166124e95789818151811061237357612373614f79565b602002602001015186828151811061238d5761238d614f79565b6020026020010181815250508360a001516001600160a01b03166340c10f198c83815181106123be576123be614f79565b60200260200101518884815181106123d8576123d8614f79565b60200260200101516040518363ffffffff1660e01b81526004016124119291906001600160a01b03929092168252602082015260400190565b5f604051808303815f87803b158015612428575f5ffd5b505af115801561243a573d5f5f3e3d5ffd5b5050505087818151811061245057612450614f79565b6020026020010151156124e9578360a001516001600160a01b031663637250d98c838151811061248257612482614f79565b60209081029190910101516040516001600160e01b031960e084901b1681526001600160a01b039091166004820152600160248201526044015f604051808303815f87803b1580156124d2575f5ffd5b505af11580156124e4573d5f5f3e3d5ffd5b505050505b600101612011565b5050505096509650969350505050565b5f5f5b6005860154811015612628575f86600501828154811061252657612526614f79565b5f9182526020822001546001600160a01b031691506125fc88888488156125ba576040516342db43ab60e11b8152306004820152602481018a90526001600160a01b038716906385b68756906044015b602060405180830381865afa158015612591573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125b5919061507c565b613d8f565b60405163ce96cb7760e01b81523060048201526001600160a01b0387169063ce96cb7790602401602060405180830381865afa158015612591573d5f5f3e3d5ffd5b509050612609818561526a565b93508386101561261e5785935050505061262a565b5050600101612504565b505b949350505050565b5f5f61264886846126438989612bcd565b613778565b90505f612655878661358e565b9050846001600160a01b0316816001600160a01b0316036126c6576040516342db43ab60e11b8152306004820152600160248201525f906126af908990899085906001600160a01b038216906385b6875690604401612576565b5090506126bc8382613e21565b935050505061262a565b604051633d523fc760e11b81526001600160a01b0386811660048301523060248301525f916126af918a918a91869190821690637aa47f8e90604401612576565b60605f5f5f5f5f886001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016040805180830381865afa158015612749573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061276d919061514c565b50604051635b002c2d60e11b81529091506001600160a01b038a169063b600585a9061279f908b908b906004016152ca565b5f60405180830381865afa1580156127b9573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526127e091908101906152ee565b919750935091505f6127fc8285670de0b6b3a764000084613cd1565b90506128098c8c83613e36565b600a8d01549096509050801561283657612831868d600a015461282c8f8f612bcd565b6137bb565b612838565b855b945050505050955095509592505050565b5f836005018054806020026020016040519081016040528092919081815260200182805480156128a057602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612882575b505083519394505f199392505f9150505b818110156128fb57856001600160a01b03168482815181106128d5576128d5614f79565b60200260200101516001600160a01b0316036128f3578092506128fb565b6001016128b1565b505f19820361291d57604051631765138960e21b815260040160405180910390fd5b5f8660060180548060200260200160405190810160405280929190818152602001828054801561296a57602002820191905f5260205f20905b815481526020019060010190808311612956575b5050505050905080838151811061298357612983614f79565b60200260200101515f146129aa5760405163e88dac0360e01b815260040160405180910390fd5b5f6129b487613e59565b905080868082106129e157604051637136b73760e01b815260048101929092526024820152604401610840565b506129ef905060018461529c565b8414612a9b5784612a0160018561529c565b81518110612a1157612a11614f79565b6020026020010151886005018581548110612a2e57612a2e614f79565b5f91825260209091200180546001600160a01b0319166001600160a01b039290921691909117905581612a6260018561529c565b81518110612a7257612a72614f79565b6020026020010151886006018581548110612a8f57612a8f614f79565b5f918252602090912001555b87600501805480612aae57612aae615339565b5f8281526020902081015f1990810180546001600160a01b031916905501905560068801805480612ae157612ae1615339565b600190038181905f5260205f20015f9055905587600601805480602002602001604051908101604052809291908181526020018280548015612b4057602002820191905f5260205f20905b815481526020019060010190808311612b2c575b50505050509150612b508261384c565b6040516001600160a01b03881681527f8732b7fa557b5e15c2a836202ae53096017ea83a5d5eb1d7e967b4fa188770359060200160405180910390a17fc0700897ab0b41fe622f2a0d2936b8713c49ded52905e340845b77f0e27907da82604051612bbb919061499b565b60405180910390a15050505050505050565b5f5f612bd98484612c97565b5090505f612be78585611789565b509050612bfe82670de0b6b3a7640000835f613cd1565b95945050505050565b6060610f1182613f9d565b5f80612c7f8787868615612c53576040516342db43ab60e11b8152306004820152602481018890526001600160a01b038916906385b6875690604401612576565b60405163ce96cb7760e01b81523060048201526001600160a01b0389169063ce96cb7790602401612576565b509050612c8c8582613e21565b979650505050505050565b5f5f5f836001600160a01b03166349b5fdb46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cd6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cfa9190615170565b90505f5f86600501805480602002602001604051908101604052809291908181526020018280548015612d5457602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612d36575b505083519394505f925050505b81811015612ec0575f5f866001600160a01b031663ec917bca868581518110612d8c57612d8c614f79565b60200260200101516040518263ffffffff1660e01b8152600401612dbf91906001600160a01b0391909116815260200190565b6040805180830381865afa158015612dd9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612dfd919061514c565b9150915080612e0b57600195505b5f858481518110612e1e57612e1e614f79565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015612e6c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e90919061507c565b9050612ea68382670de0b6b3a76400005f613cd1565b612eb0908b61526a565b9950505050806001019050612d61565b505f612ece8960030161341e565b9050805191505f826001600160401b03811115612eed57612eed61476e565b604051908082528060200260200182016040528015612f16578160200160208202803683370190505b5090505f5b83811015612fcc57828181518110612f3557612f35614f79565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015612f83573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fa7919061507c565b828281518110612fb957612fb9614f79565b6020908102919091010152600101612f1b565b505f5f876001600160a01b031663a36fba3285856040518363ffffffff1660e01b8152600401612ffd9291906152ca565b5f60405180830381865afa158015613017573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261303e919081019061534d565b935050509150818a613050919061526a565b99508061305c57600196505b8615985050505050505050509250929050565b5f5f5f836005018054806020026020016040519081016040528092919081815260200182805480156130c857602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116130aa575b5050505050905083600a01545f0361311857805f815181106130ec576130ec614f79565b6020026020010151815f8151811061310657613106614f79565b60200260200101519250925050915091565b80515f61312486613f9d565b90505f8660060180548060200260200160405190810160405280929190818152602001828054801561317357602002820191905f5260205f20905b81548152602001906001019080831161315f575b505050505090505f5f855f8151811061318e5761318e614f79565b60200260200101519750855f815181106131aa576131aa614f79565b602002602001015196505f5b8581101561333f578381815181106131d0576131d0614f79565b60200260200101518582815181106131ea576131ea614f79565b60200260200101511015613267575f85828151811061320b5761320b614f79565b602002602001015185838151811061322557613225614f79565b6020026020010151613237919061529c565b9050838111156132615780935087828151811061325657613256614f79565b602002602001015199505b50613337565b83818151811061327957613279614f79565b602002602001015185828151811061329357613293614f79565b60200260200101511180156132c75750662386f26fc100008582815181106132bd576132bd614f79565b6020026020010151115b15613337575f8482815181106132df576132df614f79565b60200260200101518683815181106132f9576132f9614f79565b602002602001015161330b919061529c565b9050828111156133355780925087828151811061332a5761332a614f79565b602002602001015198505b505b6001016131b6565b50505050505050915091565b816001600160a01b03811661337f5760405163ec442f0560e01b81526001600160a01b039091166004820152602401610840565b5061338c858433846134ea565b5f6133a08287600a015461282c8989612bcd565b6001600160a01b0385165f908152600b880160205260408120805492935083929091906133ce90849061529c565b90915550506001600160a01b0383165f908152600b87016020526040812080548392906133fc90849061526a565b9091555050505050505050565b61341b6509184e72a0006103e86150a7565b81565b60605f61342a836141da565b9392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526134828482614233565b6134e457604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526134da9085906142d0565b6134e484826142d0565b50505050565b6001600160a01b038084165f81815260098701602090815260408083209487168352939052919091205490331480159061352557505f198114155b15611452578281838082101561355157604051637dc7a0d960e11b81526004016108409392919061512b565b5050508181613560919061529c565b6001600160a01b038086165f9081526009880160209081526040808320938816835292905220555050505050565b5f5f836005018054806020026020016040519081016040528092919081815260200182805480156135e657602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116135c8575b505050505090505f5b815181101561365557836001600160a01b031682828151811061361457613614614f79565b60200260200101516001600160a01b03160361364d5781818151811061363c5761363c614f79565b602002602001015192505050610f11565b6001016135ef565b505f5b8151811015613753575f82828151811061367457613674614f79565b60200260200101516001600160a01b0316638220ef5b6040518163ffffffff1660e01b81526004015f60405180830381865afa1580156136b6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526136dd91908101906151db565b90505f5b815181101561374957856001600160a01b031682828151811061370657613706614f79565b60200260200101516001600160a01b0316036137415783838151811061372e5761372e614f79565b6020026020010151945050505050610f11565b6001016136e1565b5050600101613658565b5060405163c02219d960e01b81526001600160a01b0384166004820152602401610840565b600a8301545f9080156137b1576001600160a01b0384165f908152600b860160205260408120546137ac9185908490613cd1565b612bfe565b5f95945050505050565b5f81156137d3576137ce8484845f613cd1565b61262a565b5f949350505050565b5f5f6137e9878787614331565b90506509184e72a0008111156138155761380d81670de0b6b3a7640000865f613cd1565b915050612bfe565b821561384357604051637136b73760e01b8152600481018290526509184e72a0006024820152604401610840565b5f915050612bfe565b80515f805b828110156138885783818151811061386b5761386b614f79565b60200260200101518261387e919061526a565b9150600101613851565b5080670de0b6b3a7640000146114555760405163e88dac0360e01b815260040160405180910390fd5b604080518082018252600a815269135d5b1d1a55985d5b1d60b21b602082015290516310d24b2160e11b81525f917372a237cb9caaf0716bb4f717e32e79ce6460e5d3916321a496429161390d916001870191906004016153ef565b602060405180830381865af4158015613928573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f1191906152af565b5f61342a836001600160a01b038416614355565b5f816001600160a01b03166371a973056040518163ffffffff1660e01b81526004015f60405180830381865afa15801561399c573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526139c391908101906151db565b905080518351146139e757604051630ef9926760e21b815260040160405180910390fd5b5f5b83518110156134e457818181518110613a0457613a04614f79565b60200260200101516001600160a01b0316848281518110613a2757613a27614f79565b60200260200101516001600160a01b031614613a5a5783826040516309b2ee8f60e21b81526004016108409291906154a4565b6001016139e9565b606083613a6f83886143a1565b50865186516001600160401b03811115613a8b57613a8b61476e565b604051908082528060200260200182016040528015613ab4578160200160208202803683370190505b5092505f5b81811015613c46575f5f613ae98d8d8d8681518110613ada57613ada614f79565b60200260200101518830614471565b91509150805f14613c3c575f8b8481518110613b0757613b07614f79565b60200260200101516001600160a01b0316632cbfd6a38c848e516001600160401b03811115613b3857613b3861476e565b604051908082528060200260200182016040528015613b61578160200160208202803683370190505b508d306040518663ffffffff1660e01b8152600401613b8495949392919061518b565b5f604051808303815f875af1158015613b9f573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613bc6919081019061504b565b90505f5b8b51811015613c1e57818181518110613be557613be5614f79565b6020026020010151888281518110613bff57613bff614f79565b60200260200101818151613c13919061526a565b905250600101613bca565b50613c29838761529c565b9550855f03613c3a57505050613c46565b505b5050600101613ab9565b50811586613c54848261529c565b9091613c7c576040516331c60a1d60e21b815260048101929092526024820152604401610840565b50505050979650505050505050565b6040516370a0823160e01b81523060048201525f908190613cc5908690869086906001600160a01b038216906370a0823190602401612576565b91509150935093915050565b5f5f613cde8686866144e1565b9050613ce9836145a0565b8015613d0457505f8480613cff57613cff6150be565b868809115b15612bfe57613d1460018261526a565b9695505050505050565b5f831580613d2a575081155b6137d35762015180613d4483670de0b6b3a76400006150a7565b613d4e91906150d2565b8461016d613d64670de0b6b3a7640000876154c8565b613d7190620186a06154c8565b613d7b91906154c8565b613d8591906154f7565b6137ce91906154f7565b5f5f836001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016040805180830381865afa158015613dcc573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613df0919061514c565b5090505f613e088285670de0b6b3a764000084613cd1565b9050613e15878783613e36565b92505094509492505050565b5f818310613e2f578161342a565b5090919050565b5f5f613e428585611789565b509050612bfe83670de0b6b3a7640000835f613cd1565b5f5f826001600160a01b031663e5328e066040518163ffffffff1660e01b81526004016040805180830381865afa158015613e96573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613eba919061514c565b506040516370a0823160e01b81523060048201529091505f906001600160a01b038516906370a0823190602401602060405180830381865afa158015613f02573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f26919061507c565b90505f846001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613f65573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f89919061507c565b905080156137b1576137ac8284835f613cd1565b60605f82600501805480602002602001604051908101604052809291908181526020018280548015613ff657602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311613fd8575b5050505050905082600a01545f03614061578260060180548060200260200160405190810160405280929190818152602001828054801561405457602002820191905f5260205f20905b815481526020019060010190808311614040575b5050505050915050919050565b8051806001600160401b0381111561407b5761407b61476e565b6040519080825280602002602001820160405280156140a4578160200160208202803683370190505b5092505f816001600160401b038111156140c0576140c061476e565b6040519080825280602002602001820160405280156140e9578160200160208202803683370190505b5090505f5f5b838110156141665761411985828151811061410c5761410c614f79565b6020026020010151613e59565b83828151811061412b5761412b614f79565b60200260200101818152505082818151811061414957614149614f79565b60200260200101518261415c919061526a565b91506001016140ef565b505f5b838110156141d05781156141a9576141a483828151811061418c5761418c614f79565b6020026020010151670de0b6b3a7640000845f613cd1565b6141ab565b5f5b8682815181106141bd576141bd614f79565b6020908102919091010152600101614169565b5050505050919050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561422757602002820191905f5260205f20905b815481526020019060010190808311614213575b50505050509050919050565b5f5f5f846001600160a01b03168460405161424e9190615523565b5f604051808303815f865af19150503d805f8114614287576040519150601f19603f3d011682016040523d82523d5f602084013e61428c565b606091505b50915091508180156142b65750805115806142b65750808060200190518101906142b691906152af565b8015612bfe5750505050506001600160a01b03163b151590565b5f6142e46001600160a01b038416836145cc565b905080515f1415801561430857508080602001905181019061430691906152af565b155b1561145557604051635274afe760e01b81526001600160a01b0384166004820152602401610840565b5f5f61433d8585611789565b509050612bfe8382670de0b6b3a76400006001613cd1565b5f81815260018301602052604081205461439a57508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610f11565b505f610f11565b80516060905f5b8181101561446857846001600160a01b03168482815181106143cc576143cc614f79565b60200260200101516001600160a01b031603614460578381815181106143f4576143f4614f79565b6020026020010151845f8151811061440e5761440e614f79565b6020026020010151855f8151811061442857614428614f79565b6020026020010186848151811061444157614441614f79565b6001600160a01b03938416602091820292909201015291169052614468565b6001016143a8565b50919392505050565b5f5f5f5f6144b68989898a6001600160a01b031663ce96cb778a6040518263ffffffff1660e01b815260040161257691906001600160a01b0391909116815260200190565b915091506144c48683613e21565b93506144d3898986845f6137dc565b925050509550959350505050565b5f838302815f1985870982811083820303915050805f036145155783828161450b5761450b6150be565b049250505061342a565b8084116145355760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f60028260038111156145b5576145b5615539565b6145bf919061554d565b60ff166001149050919050565b606061342a83835f845f5f856001600160a01b031684866040516145f09190615523565b5f6040518083038185875af1925050503d805f811461462a576040519150601f19603f3d011682016040523d82523d5f602084013e61462f565b606091505b5091509150613d1486838360608261464f5761464a82614696565b61342a565b815115801561466657506001600160a01b0384163b155b1561468f57604051639996b31560e01b81526001600160a01b0385166004820152602401610840565b508061342a565b8051156146a65780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b50565b6040518061012001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f6001600160a01b031681526020016060815260200160608152602001606081525090565b828054828255905f5260205f2090810192821561474a579160200282015b8281111561474a57825182559160200191906001019061472f565b5061475692915061475a565b5090565b5b80821115614756575f815560010161475b565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b03811182821017156147aa576147aa61476e565b604052919050565b5f6001600160401b038211156147ca576147ca61476e565b5060051b60200190565b5f82601f8301126147e3575f5ffd5b81356147f66147f1826147b2565b614782565b8082825260208201915060208360051b860101925085831115614817575f5ffd5b602085015b8381101561483457803583526020928301920161481c565b5095945050505050565b5f5f5f60608486031215614850575f5ffd5b8335925060208401356001600160401b0381111561486c575f5ffd5b614878868287016147d4565b92505060408401356001600160401b03811115614893575f5ffd5b61489f868287016147d4565b9150509250925092565b6001600160a01b03811681146146bf575f5ffd5b5f5f5f5f5f5f5f60e0888a0312156148d3575f5ffd5b8735965060208801356148e5816148a9565b955060408801356148f5816148a9565b9450606088013593506080880135925060a0880135614913816148a9565b915060c0880135614923816148a9565b8091505092959891949750929550565b5f5f60408385031215614944575f5ffd5b823591506020830135614956816148a9565b809150509250929050565b5f8151808452602084019350602083015f5b82811015614991578151865260209586019590910190600101614973565b5093949350505050565b602081525f61342a6020830184614961565b5f5f5f606084860312156149bf575f5ffd5b8335925060208401356149d1816148a9565b915060408401356001600160401b03811115614893575f5ffd5b80151581146146bf575f5ffd5b5f5f5f60608486031215614a0a575f5ffd5b833592506020840135614a1c816148a9565b91506040840135614a2c816149eb565b809150509250925092565b5f82601f830112614a46575f5ffd5b8135614a546147f1826147b2565b8082825260208201915060208360051b860101925085831115614a75575f5ffd5b602085015b83811015614834578035614a8d816148a9565b835260209283019201614a7a565b5f5f5f5f5f5f5f5f610100898b031215614ab3575f5ffd5b883597506020890135614ac5816148a9565b96506040890135614ad5816148a9565b955060608901356001600160401b03811115614aef575f5ffd5b614afb8b828c01614a37565b9550506080890135935060a08901356001600160401b03811115614b1d575f5ffd5b614b298b828c016147d4565b93505060c0890135614b3a816148a9565b915060e0890135614b4a816148a9565b809150509295985092959890939650565b604081525f614b6d6040830185614961565b90508260208301529392505050565b5f82601f830112614b8b575f5ffd5b604080519081016001600160401b0381118282101715614bad57614bad61476e565b8060405250806040840185811115614bc3575f5ffd5b845b81811015614be6578035614bd8816148a9565b835260209283019201614bc5565b509195945050505050565b5f82601f830112614c00575f5ffd5b8135614c0e6147f1826147b2565b8082825260208201915060208360051b860101925085831115614c2f575f5ffd5b602085015b83811015614834578035614c47816149eb565b835260209283019201614c34565b5f5f5f5f5f5f60e08789031215614c6a575f5ffd5b86359550614c7b8860208901614b7c565b945060608701356001600160401b03811115614c95575f5ffd5b614ca189828a01614a37565b94505060808701356001600160401b03811115614cbc575f5ffd5b614cc889828a016147d4565b93505060a08701356001600160401b03811115614ce3575f5ffd5b614cef89828a016147d4565b92505060c08701356001600160401b03811115614d0a575f5ffd5b614d1689828a01614bf1565b9150509295509295509295565b606081525f614d356060830186614961565b8281036020840152614d478186614961565b90508281036040840152613d148185614961565b5f5f5f5f60808587031215614d6e575f5ffd5b843593506020850135614d80816148a9565b93969395505050506040820135916060013590565b5f5f5f5f60808587031215614da8575f5ffd5b843593506020850135614dba816148a9565b92506040850135614dca816148a9565b91506060850135614dda816148a9565b939692955090935050565b5f5f5f5f5f60a08688031215614df9575f5ffd5b853594506020860135614e0b816148a9565b93506040860135614e1b816148a9565b925060608601356001600160401b03811115614e35575f5ffd5b614e4188828901614a37565b92505060808601356001600160401b03811115614e5c575f5ffd5b614e68888289016147d4565b9150509295509295909350565b606081525f614e876060830186614961565b60208301949094525060400152919050565b5f5f5f60608486031215614eab575f5ffd5b833592506020840135614ebd816148a9565b929592945050506040919091013590565b5f60208284031215614ede575f5ffd5b5035919050565b5f5f5f5f5f60a08688031215614ef9575f5ffd5b853594506020860135614f0b816148a9565b9350604086013592506060860135614f22816148a9565b949793965091946080013592915050565b5f5f5f5f5f60a08688031215614f47575f5ffd5b853594506020860135614f59816148a9565b93506040860135614f69816148a9565b92506060860135614f22816148a9565b634e487b7160e01b5f52603260045260245ffd5b5f8151808452602084019350602083015f5b828110156149915781516001600160a01b0316865260209586019590910190600101614f9f565b606081525f614fd86060830186614f8d565b8460208401528281036040840152613d148185614961565b5f82601f830112614fff575f5ffd5b815161500d6147f1826147b2565b8082825260208201915060208360051b86010192508583111561502e575f5ffd5b602085015b83811015614834578051835260209283019201615033565b5f6020828403121561505b575f5ffd5b81516001600160401b03811115615070575f5ffd5b61262a84828501614ff0565b5f6020828403121561508c575f5ffd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610f1157610f11615093565b634e487b7160e01b5f52601260045260245ffd5b5f826150e0576150e06150be565b500490565b608081525f6150f76080830187614f8d565b82810360208401526151098187614961565b604084019590955250506001600160a01b039190911660609091015292915050565b6001600160a01b039390931683526020830191909152604082015260600190565b5f5f6040838503121561515d575f5ffd5b82516020840151909250614956816149eb565b5f60208284031215615180575f5ffd5b815161342a816148a9565b60a081525f61519d60a0830188614f8d565b86602084015282810360408401526151b58187614961565b6001600160a01b0395861660608501529390941660809092019190915250949350505050565b5f602082840312156151eb575f5ffd5b81516001600160401b03811115615200575f5ffd5b8201601f81018413615210575f5ffd5b805161521e6147f1826147b2565b8082825260208201915060208360051b85010192508683111561523f575f5ffd5b6020840193505b82841015613d14578351615259816148a9565b825260209384019390910190615246565b80820180821115610f1157610f11615093565b8181035f831280158383131683831282161715610f0f57610f0f615093565b81810381811115610f1157610f11615093565b5f602082840312156152bf575f5ffd5b815161342a816149eb565b604081525f6152dc6040830185614f8d565b8281036020840152612bfe8185614961565b5f5f5f60608486031215615300575f5ffd5b83516001600160401b03811115615315575f5ffd5b61532186828701614ff0565b60208601516040909601519097959650949350505050565b634e487b7160e01b5f52603160045260245ffd5b5f5f5f5f60808587031215615360575f5ffd5b845160208601519094506001600160401b0381111561537d575f5ffd5b61538987828801614ff0565b93505060408501516001600160401b038111156153a4575f5ffd5b6153b087828801614ff0565b9250506060850151614dda816149eb565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b604081525f5f84545f8160011c9050600182168061540e57607f821691505b60208210810361542c57634e487b7160e01b5f52602260045260245ffd5b604086018290526060860181801561544b57600181146154615761548d565b60ff198516825283151560051b8201955061548d565b5f8a8152602090205f5b858110156154875781548482015260019091019060200161546b565b83019650505b50505050508281036020840152612bfe81856153c1565b604081525f6154b66040830185614f8d565b8281036020840152612bfe8185614f8d565b8082025f8212600160ff1b841416156154e3576154e3615093565b8181058314821517610f1157610f11615093565b5f82615505576155056150be565b600160ff1b82145f198414161561551e5761551e615093565b500590565b5f82518060208501845e5f920191825250919050565b634e487b7160e01b5f52602160045260245ffd5b5f60ff83168061555f5761555f6150be565b8060ff8416069150509291505056fea264697066735822122023a85e2f3f562dd6e58ec4792ede531216bfc08908795de161c6fa3f0a158cba64736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.